diff --git a/akka-http-core/src/main/scala/akka/http/engine/rendering/RenderSupport.scala b/akka-http-core/src/main/scala/akka/http/engine/rendering/RenderSupport.scala index 0ae8ecf063..ac87405f19 100644 --- a/akka-http-core/src/main/scala/akka/http/engine/rendering/RenderSupport.scala +++ b/akka-http-core/src/main/scala/akka/http/engine/rendering/RenderSupport.scala @@ -40,7 +40,7 @@ private object RenderSupport { val messageBytes = if (!skipEntity) Flow(messageStart).concat(entityBytes).toPublisher() else { - // FIXME: This should be fixed by a CancelledSink once #15903 is done. Currently this is needed for the tests + // FIXME: This should be fixed by a CancelledDrain once #15903 is done. Currently this is needed for the tests entityBytes.subscribe(cancelledSusbcriber) messageStart } diff --git a/akka-http-core/src/main/scala/akka/http/util/package.scala b/akka-http-core/src/main/scala/akka/http/util/package.scala index 733b22e192..90bd35c5e3 100644 --- a/akka-http-core/src/main/scala/akka/http/util/package.scala +++ b/akka-http-core/src/main/scala/akka/http/util/package.scala @@ -53,7 +53,7 @@ package object util { }) } - // FIXME: This should be fixed by a CancelledSink once #15903 is done. Currently this is needed for the tests + // FIXME: This should be fixed by a CancelledDrain once #15903 is done. Currently this is needed for the tests private[http] def cancelledSusbcriber[T]: Subscriber[T] = new Subscriber[T] { override def onSubscribe(s: Subscription): Unit = s.cancel() override def onError(t: Throwable): Unit = () diff --git a/akka-http-core/src/test/scala/io/akka/integrationtest/http/HttpModelIntegrationSpec.scala b/akka-http-core/src/test/scala/io/akka/integrationtest/http/HttpModelIntegrationSpec.scala index abeef6a1bc..f2a741ad8d 100644 --- a/akka-http-core/src/test/scala/io/akka/integrationtest/http/HttpModelIntegrationSpec.scala +++ b/akka-http-core/src/test/scala/io/akka/integrationtest/http/HttpModelIntegrationSpec.scala @@ -62,7 +62,7 @@ class HttpModelIntegrationSpec extends WordSpec with Matchers with BeforeAndAfte entity = HttpEntity.Default( contentType = ContentTypes.`application/json`, contentLength = 5, - FlowFrom(List(ByteString("hello"))).toPublisher())) + Source(List(ByteString("hello"))).toPublisher())) // Our library uses a simple model of headers: a Seq[(String, String)]. // The body is represented as an Array[Byte]. To get the headers in @@ -141,7 +141,7 @@ class HttpModelIntegrationSpec extends WordSpec with Matchers with BeforeAndAfte // convert the body into a Publisher[ByteString]. val byteStringBody = ByteString(byteArrayBody) - val publisherBody = FlowFrom(List(byteStringBody)).toPublisher() + val publisherBody = Source(List(byteStringBody)).toPublisher() // Finally we can create our HttpResponse. diff --git a/akka-stream-testkit/src/test/scala/akka/stream/testkit2/ChainSetup.scala b/akka-stream-testkit/src/test/scala/akka/stream/testkit2/ChainSetup.scala index aab561842f..0a640abc2e 100644 --- a/akka-stream-testkit/src/test/scala/akka/stream/testkit2/ChainSetup.scala +++ b/akka-stream-testkit/src/test/scala/akka/stream/testkit2/ChainSetup.scala @@ -6,23 +6,21 @@ import akka.stream.scaladsl2._ import akka.stream.testkit.StreamTestKit import org.reactivestreams.Publisher -import scala.annotation.unchecked.uncheckedVariance - class ChainSetup[In, Out]( - stream: ProcessorFlow[In, In] ⇒ ProcessorFlow[In, Out], + stream: Flow[In, In] ⇒ Flow[In, Out], val settings: MaterializerSettings, materializer: FlowMaterializer, - toPublisher: (FlowWithSource[In, Out], FlowMaterializer) ⇒ Publisher[Out])(implicit val system: ActorSystem) { + toPublisher: (Source[Out], FlowMaterializer) ⇒ Publisher[Out])(implicit val system: ActorSystem) { - def this(stream: ProcessorFlow[In, In] ⇒ ProcessorFlow[In, Out], settings: MaterializerSettings, toPublisher: (FlowWithSource[In, Out], FlowMaterializer) ⇒ Publisher[Out])(implicit system: ActorSystem) = + def this(stream: Flow[In, In] ⇒ Flow[In, Out], settings: MaterializerSettings, toPublisher: (Source[Out], FlowMaterializer) ⇒ Publisher[Out])(implicit system: ActorSystem) = this(stream, settings, FlowMaterializer(settings)(system), toPublisher)(system) - def this(stream: ProcessorFlow[In, In] ⇒ ProcessorFlow[In, Out], settings: MaterializerSettings, materializerCreator: (MaterializerSettings, ActorRefFactory) ⇒ FlowMaterializer, toPublisher: (FlowWithSource[In, Out], FlowMaterializer) ⇒ Publisher[Out])(implicit system: ActorSystem) = + def this(stream: Flow[In, In] ⇒ Flow[In, Out], settings: MaterializerSettings, materializerCreator: (MaterializerSettings, ActorRefFactory) ⇒ FlowMaterializer, toPublisher: (Source[Out], FlowMaterializer) ⇒ Publisher[Out])(implicit system: ActorSystem) = this(stream, settings, materializerCreator(settings, system), toPublisher)(system) val upstream = StreamTestKit.PublisherProbe[In]() val downstream = StreamTestKit.SubscriberProbe[Out]() - private val s = stream(FlowFrom[In]).withSource(PublisherSource(upstream)) + private val s = Source(upstream).connect(stream(Flow[In])) val publisher = toPublisher(s, materializer) val upstreamSubscription = upstream.expectSubscription() publisher.subscribe(downstream) diff --git a/akka-stream-testkit/src/test/scala/akka/stream/testkit2/ScriptedTest.scala b/akka-stream-testkit/src/test/scala/akka/stream/testkit2/ScriptedTest.scala index 48b4317154..468c0cf88e 100644 --- a/akka-stream-testkit/src/test/scala/akka/stream/testkit2/ScriptedTest.scala +++ b/akka-stream-testkit/src/test/scala/akka/stream/testkit2/ScriptedTest.scala @@ -5,7 +5,7 @@ package akka.stream.testkit2 import akka.actor.ActorSystem import akka.stream.MaterializerSettings -import akka.stream.scaladsl2.{ FlowMaterializer, FlowWithSource, ProcessorFlow } +import akka.stream.scaladsl2.{ FlowMaterializer, Source, Flow } import akka.stream.testkit.StreamTestKit._ import org.reactivestreams.Publisher import org.scalatest.Matchers @@ -18,7 +18,7 @@ trait ScriptedTest extends Matchers { class ScriptException(msg: String) extends RuntimeException(msg) - def toPublisher[In, Out]: (FlowWithSource[In, Out], FlowMaterializer) ⇒ Publisher[Out] = + def toPublisher[In, Out]: (Source[Out], FlowMaterializer) ⇒ Publisher[Out] = (f, m) ⇒ f.toPublisher()(m) object Script { @@ -81,7 +81,7 @@ trait ScriptedTest extends Matchers { } class ScriptRunner[In, Out]( - op: ProcessorFlow[In, In] ⇒ ProcessorFlow[In, Out], + op: Flow[In, In] ⇒ Flow[In, Out], settings: MaterializerSettings, script: Script[In, Out], maximumOverrun: Int, @@ -191,7 +191,7 @@ trait ScriptedTest extends Matchers { } def runScript[In, Out](script: Script[In, Out], settings: MaterializerSettings, maximumOverrun: Int = 3, maximumRequest: Int = 3, maximumBuffer: Int = 3)( - op: ProcessorFlow[In, In] ⇒ ProcessorFlow[In, Out])(implicit system: ActorSystem): Unit = { + op: Flow[In, In] ⇒ Flow[In, Out])(implicit system: ActorSystem): Unit = { new ScriptRunner(op, settings, script, maximumOverrun, maximumRequest, maximumBuffer).run() } diff --git a/akka-stream-testkit/src/test/scala/akka/stream/testkit2/TwoStreamsSetup.scala b/akka-stream-testkit/src/test/scala/akka/stream/testkit2/TwoStreamsSetup.scala index 73d0604eb9..07619b0714 100644 --- a/akka-stream-testkit/src/test/scala/akka/stream/testkit2/TwoStreamsSetup.scala +++ b/akka-stream-testkit/src/test/scala/akka/stream/testkit2/TwoStreamsSetup.scala @@ -30,8 +30,8 @@ abstract class TwoStreamsSetup extends AkkaSpec { import FlowGraphImplicits._ val left = operationUnderTestLeft() val right = operationUnderTestRight() - val x = FlowFrom(p1) ~> left ~> FlowFrom[Outputs] ~> SubscriberSink(subscriber) - FlowFrom(p2) ~> right + val x = Source(p1) ~> left ~> Flow[Outputs] ~> SubscriberDrain(subscriber) + Source(p2) ~> right }.run() subscriber @@ -41,7 +41,7 @@ abstract class TwoStreamsSetup extends AkkaSpec { def completedPublisher[T]: Publisher[T] = StreamTestKit.emptyPublisher[T] - def nonemptyPublisher[T](elems: Iterator[T]): Publisher[T] = FlowFrom(elems).toPublisher() + def nonemptyPublisher[T](elems: Iterator[T]): Publisher[T] = Source(elems).toPublisher() def soonToFailPublisher[T]: Publisher[T] = StreamTestKit.lazyErrorPublisher[T](TestException) diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowAppendSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowAppendSpec.scala index 4a467d02bf..fe47548fb5 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowAppendSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowAppendSpec.scala @@ -14,32 +14,28 @@ class FlowAppendSpec extends AkkaSpec with River { val settings = MaterializerSettings(system) implicit val materializer = FlowMaterializer(settings) - "ProcessorFlow" should { - "append ProcessorFlow" in riverOf[String] { subscriber ⇒ - FlowFrom[Int] - .append(otherFlow) - .withSource(IterableSource(elements)) - .publishTo(subscriber) + "Flow" should { + "append Flow" in riverOf[String] { subscriber ⇒ + val flow = Flow[Int].connect(otherFlow) + Source(elements).connect(flow).publishTo(subscriber) } - "append FlowWithSink" in riverOf[String] { subscriber ⇒ - FlowFrom[Int] - .append(otherFlow.withSink(SubscriberSink(subscriber))) - .withSource(IterableSource(elements)) - .run() + "append Sink" in riverOf[String] { subscriber ⇒ + val sink = Flow[Int].connect(otherFlow.connect(SubscriberDrain(subscriber))) + Source(elements).connect(sink).run() } } - "FlowWithSource" should { - "append ProcessorFlow" in riverOf[String] { subscriber ⇒ - FlowFrom(elements) - .append(otherFlow) + "Source" should { + "append Flow" in riverOf[String] { subscriber ⇒ + Source(elements) + .connect(otherFlow) .publishTo(subscriber) } - "append FlowWithSink" in riverOf[String] { subscriber ⇒ - FlowFrom(elements) - .append(otherFlow.withSink(SubscriberSink(subscriber))) + "append Sink" in riverOf[String] { subscriber ⇒ + Source(elements) + .connect(otherFlow.connect(SubscriberDrain(subscriber))) .run() } } @@ -49,7 +45,7 @@ class FlowAppendSpec extends AkkaSpec with River { trait River { self: Matchers ⇒ val elements = (1 to 10) - val otherFlow = FlowFrom[Int].map(_.toString) + val otherFlow = Flow[Int].map(_.toString) def riverOf[T](flowConstructor: Subscriber[T] ⇒ Unit)(implicit system: ActorSystem) = { val subscriber = StreamTestKit.SubscriberProbe[T]() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowBufferSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowBufferSpec.scala index b9d0df4cfe..025f3a761b 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowBufferSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowBufferSpec.scala @@ -20,24 +20,24 @@ class FlowBufferSpec extends AkkaSpec { "Buffer" must { "pass elements through normally in backpressured mode" in { - val futureSink = FutureSink[Seq[Int]] - val mf = FlowFrom((1 to 1000).iterator).buffer(100, overflowStrategy = OverflowStrategy.backpressure).grouped(1001). - withSink(futureSink).run() - val future = futureSink.future(mf) + val futureDrain = FutureDrain[Seq[Int]] + val mf = Source((1 to 1000).iterator).buffer(100, overflowStrategy = OverflowStrategy.backpressure).grouped(1001). + connect(futureDrain).run() + val future = futureDrain.future(mf) Await.result(future, 3.seconds) should be(1 to 1000) } "pass elements through normally in backpressured mode with buffer size one" in { - val futureSink = FutureSink[Seq[Int]] - val mf = FlowFrom((1 to 1000).iterator).buffer(1, overflowStrategy = OverflowStrategy.backpressure).grouped(1001). - withSink(futureSink).run() - val future = futureSink.future(mf) + val futureDrain = FutureDrain[Seq[Int]] + val mf = Source((1 to 1000).iterator).buffer(1, overflowStrategy = OverflowStrategy.backpressure).grouped(1001). + connect(futureDrain).run() + val future = futureDrain.future(mf) Await.result(future, 3.seconds) should be(1 to 1000) } "pass elements through a chain of backpressured buffers of different size" in { - val futureSink = FutureSink[Seq[Int]] - val mf = FlowFrom((1 to 1000).iterator) + val futureDrain = FutureDrain[Seq[Int]] + val mf = Source((1 to 1000).iterator) .buffer(1, overflowStrategy = OverflowStrategy.backpressure) .buffer(10, overflowStrategy = OverflowStrategy.backpressure) .buffer(256, overflowStrategy = OverflowStrategy.backpressure) @@ -45,8 +45,8 @@ class FlowBufferSpec extends AkkaSpec { .buffer(5, overflowStrategy = OverflowStrategy.backpressure) .buffer(128, overflowStrategy = OverflowStrategy.backpressure) .grouped(1001) - .withSink(futureSink).run() - val future = futureSink.future(mf) + .connect(futureDrain).run() + val future = futureDrain.future(mf) Await.result(future, 3.seconds) should be(1 to 1000) } @@ -54,7 +54,7 @@ class FlowBufferSpec extends AkkaSpec { val publisher = StreamTestKit.PublisherProbe[Int]() val subscriber = StreamTestKit.SubscriberProbe[Int]() - FlowFrom(publisher).buffer(100, overflowStrategy = OverflowStrategy.backpressure).publishTo(subscriber) + Source(publisher).buffer(100, overflowStrategy = OverflowStrategy.backpressure).publishTo(subscriber) val autoPublisher = new StreamTestKit.AutoPublisher(publisher) val sub = subscriber.expectSubscription() @@ -74,7 +74,7 @@ class FlowBufferSpec extends AkkaSpec { val publisher = StreamTestKit.PublisherProbe[Int]() val subscriber = StreamTestKit.SubscriberProbe[Int]() - FlowFrom(publisher).buffer(100, overflowStrategy = OverflowStrategy.dropHead).publishTo(subscriber) + Source(publisher).buffer(100, overflowStrategy = OverflowStrategy.dropHead).publishTo(subscriber) val autoPublisher = new StreamTestKit.AutoPublisher(publisher) val sub = subscriber.expectSubscription() @@ -102,7 +102,7 @@ class FlowBufferSpec extends AkkaSpec { val publisher = StreamTestKit.PublisherProbe[Int]() val subscriber = StreamTestKit.SubscriberProbe[Int]() - FlowFrom(publisher).buffer(100, overflowStrategy = OverflowStrategy.dropTail).publishTo(subscriber) + Source(publisher).buffer(100, overflowStrategy = OverflowStrategy.dropTail).publishTo(subscriber) val autoPublisher = new StreamTestKit.AutoPublisher(publisher) val sub = subscriber.expectSubscription() @@ -133,7 +133,7 @@ class FlowBufferSpec extends AkkaSpec { val publisher = StreamTestKit.PublisherProbe[Int] val subscriber = StreamTestKit.SubscriberProbe[Int]() - FlowFrom(publisher).buffer(100, overflowStrategy = OverflowStrategy.dropBuffer).publishTo(subscriber) + Source(publisher).buffer(100, overflowStrategy = OverflowStrategy.dropBuffer).publishTo(subscriber) val autoPublisher = new StreamTestKit.AutoPublisher(publisher) val sub = subscriber.expectSubscription() @@ -164,7 +164,7 @@ class FlowBufferSpec extends AkkaSpec { val publisher = StreamTestKit.PublisherProbe[Int] val subscriber = StreamTestKit.SubscriberProbe[Int]() - FlowFrom(publisher).buffer(1, overflowStrategy = strategy).publishTo(subscriber) + Source(publisher).buffer(1, overflowStrategy = strategy).publishTo(subscriber) val autoPublisher = new StreamTestKit.AutoPublisher(publisher) val sub = subscriber.expectSubscription() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowCompileSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowCompileSpec.scala index c94432a045..52c10258e4 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowCompileSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowCompileSpec.scala @@ -5,143 +5,103 @@ package akka.stream.scaladsl2 import akka.stream.MaterializerSettings import akka.stream.testkit.AkkaSpec - import scala.collection.immutable.Seq import scala.concurrent.Future class FlowCompileSpec extends AkkaSpec { - val intSeq = IterableSource(Seq(1, 2, 3)) - val strSeq = IterableSource(Seq("a", "b", "c")) + val intSeq = IterableTap(Seq(1, 2, 3)) + val strSeq = IterableTap(Seq("a", "b", "c")) import scala.concurrent.ExecutionContext.Implicits.global - val intFut = FutureSource(Future { 3 }) + val intFut = FutureTap(Future { 3 }) implicit val materializer = FlowMaterializer(MaterializerSettings(system)) - "ProcessorFlow" should { - "go through all states" in { - val f: ProcessorFlow[Int, Int] = FlowFrom[Int] - .withSource(intSeq) - .withSink(PublisherSink[Int]) - .withoutSource - .withoutSink - } + "Flow" should { "should not run" in { - val open: ProcessorFlow[Int, Int] = FlowFrom[Int] + val open: Flow[Int, Int] = Flow[Int] "open.run()" shouldNot compile } - "accept IterableSource" in { - val f: FlowWithSource[Int, Int] = FlowFrom[Int].withSource(intSeq) + "accept Iterable" in { + val f: Source[Int] = intSeq.connect(Flow[Int]) } - "accept FutureSource" in { - val f: FlowWithSource[Int, Int] = FlowFrom[Int].withSource(intFut) + "accept Future" in { + val f: Source[Int] = intFut.connect(Flow[Int]) } - "append ProcessorFlow" in { - val open1: ProcessorFlow[Int, String] = FlowFrom[Int].map(_.toString) - val open2: ProcessorFlow[String, Int] = FlowFrom[String].map(_.hashCode) - val open3: ProcessorFlow[Int, Int] = open1.append(open2) + "append Flow" in { + val open1: Flow[Int, String] = Flow[Int].map(_.toString) + val open2: Flow[String, Int] = Flow[String].map(_.hashCode) + val open3: Flow[Int, Int] = open1.connect(open2) "open3.run()" shouldNot compile - val closedSource: FlowWithSource[Int, Int] = open3.withSource(intSeq) + val closedSource: Source[Int] = intSeq.connect(open3) "closedSource.run()" shouldNot compile - val closedSink: FlowWithSink[Int, Int] = open3.withSink(PublisherSink[Int]) + val closedSink: Sink[Int] = open3.connect(PublisherDrain[Int]) "closedSink.run()" shouldNot compile - closedSource.withSink(PublisherSink[Int]).run() - closedSink.withSource(intSeq).run() + closedSource.connect(PublisherDrain[Int]).run() + intSeq.connect(closedSink).run() } - "prepend ProcessorFlow" in { - val open1: ProcessorFlow[Int, String] = FlowFrom[Int].map(_.toString) - val open2: ProcessorFlow[String, Int] = FlowFrom[String].map(_.hashCode) - val open3: ProcessorFlow[String, String] = open1.prepend(open2) - "open3.run()" shouldNot compile - - val closedSource: FlowWithSource[String, String] = open3.withSource(strSeq) - "closedSource.run()" shouldNot compile - - val closedSink: FlowWithSink[String, String] = open3.withSink(PublisherSink[String]) - "closedSink.run()" shouldNot compile - - closedSource.withSink(PublisherSink[String]).run - closedSink.withSource(strSeq).run - } - "append FlowWithSink" in { - val open: ProcessorFlow[Int, String] = FlowFrom[Int].map(_.toString) - val closedSink: FlowWithSink[String, Int] = FlowFrom[String].map(_.hashCode).withSink(PublisherSink[Int]) - val appended: FlowWithSink[Int, Int] = open.append(closedSink) + "append Sink" in { + val open: Flow[Int, String] = Flow[Int].map(_.toString) + val closedDrain: Sink[String] = Flow[String].map(_.hashCode).connect(PublisherDrain[Int]) + val appended: Sink[Int] = open.connect(closedDrain) "appended.run()" shouldNot compile - "appended.toFuture" shouldNot compile - appended.withSource(intSeq).run + "appended.connect(FutureDrain[Int])" shouldNot compile + intSeq.connect(appended).run } - "prepend FlowWithSource" in { - val open: ProcessorFlow[Int, String] = FlowFrom[Int].map(_.toString) - val closedSource: FlowWithSource[String, Int] = FlowFrom[String].map(_.hashCode).withSource(strSeq) - val prepended: FlowWithSource[String, String] = open.prepend(closedSource) - "prepended.run()" shouldNot compile - "prepended.withSource(strSeq)" shouldNot compile - prepended.withSink(PublisherSink[String]).run + "be appended to Source" in { + val open: Flow[Int, String] = Flow[Int].map(_.toString) + val closedTap: Source[Int] = strSeq.connect(Flow[String].map(_.hashCode)) + val closedSource: Source[String] = closedTap.connect(open) + "closedSource.run()" shouldNot compile + "strSeq.connect(closedSource)" shouldNot compile + closedSource.connect(PublisherDrain[String]).run } } - "FlowWithSink" should { - val openSource: FlowWithSink[Int, String] = - FlowFrom[Int].map(_.toString).withSink(PublisherSink[String]) + "Sink" should { + val openSource: Sink[Int] = + Flow[Int].map(_.toString).connect(PublisherDrain[String]) "accept Source" in { - openSource.withSource(intSeq) - } - "drop Sink" in { - openSource.withoutSink - } - "not drop Source" in { - "openSource.withoutSource" shouldNot compile + intSeq.connect(openSource) } "not accept Sink" in { - "openSource.ToFuture" shouldNot compile + "openSource.connect(FutureDrain[String])" shouldNot compile } "not run()" in { "openSource.run()" shouldNot compile } } - "FlowWithSource" should { - val openSink: FlowWithSource[Int, String] = - FlowFrom(Seq(1, 2, 3)).map(_.toString) + "Source" should { + val openSource: Source[String] = + Source(Seq(1, 2, 3)).map(_.toString) "accept Sink" in { - openSink.withSink(PublisherSink[String]) + openSource.connect(PublisherDrain[String]) } - "drop Source" in { - openSink.withoutSource - } - "not drop Sink" in { - "openSink.withoutSink" shouldNot compile - } - "not accept Source" in { - "openSink.withSource(intSeq)" shouldNot compile + "not be accepted by Source" in { + "openSource.connect(intSeq)" shouldNot compile } "not run()" in { - "openSink.run()" shouldNot compile + "openSource.run()" shouldNot compile } } "RunnableFlow" should { - val closed: RunnableFlow[Int, String] = - FlowFrom(Seq(1, 2, 3)).map(_.toString).withSink(PublisherSink[String]) + FutureDrain[String] + val closed: RunnableFlow = + Source(Seq(1, 2, 3)).map(_.toString).connect(PublisherDrain[String]) "run" in { closed.run() } - "drop Source" in { - closed.withoutSource - } - "drop Sink" in { - closed.withoutSink - } - "not accept Source" in { - "closed.withSource(intSeq)" shouldNot compile + "not be accepted by Source" in { + "intSeq.connect(closed)" shouldNot compile } + "not accept Sink" in { - "closed.ToFuture" shouldNot compile + "closed.connect(FutureDrain[String])" shouldNot compile } } - } diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowConcatAllSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowConcatAllSpec.scala index 85d2065fff..dac8263499 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowConcatAllSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowConcatAllSpec.scala @@ -23,13 +23,13 @@ class FlowConcatAllSpec extends AkkaSpec { val testException = new Exception("test") with NoStackTrace "work in the happy case" in { - val s1 = FlowFrom((1 to 2).iterator) - val s2 = FlowFrom(List.empty[Int]) - val s3 = FlowFrom(List(3)) - val s4 = FlowFrom((4 to 6).iterator) - val s5 = FlowFrom((7 to 10).iterator) + val s1 = Source((1 to 2).iterator) + val s2 = Source(List.empty[Int]) + val s3 = Source(List(3)) + val s4 = Source((4 to 6).iterator) + val s5 = Source((7 to 10).iterator) - val main = FlowFrom(List(s1, s2, s3, s4, s5)) + val main = Source(List(s1, s2, s3, s4, s5)) val subscriber = StreamTestKit.SubscriberProbe[Int]() main.flatten(FlattenStrategy.concat).publishTo(subscriber) @@ -42,7 +42,7 @@ class FlowConcatAllSpec extends AkkaSpec { "work together with SplitWhen" in { val subscriber = StreamTestKit.SubscriberProbe[Int]() - FlowFrom((1 to 10).iterator).splitWhen(_ % 2 == 0).flatten(FlattenStrategy.concat).publishTo(subscriber) + Source((1 to 10).iterator).splitWhen(_ % 2 == 0).flatten(FlattenStrategy.concat).publishTo(subscriber) val subscription = subscriber.expectSubscription() subscription.request(10) subscriber.probe.receiveN(10) should be((1 to 10).map(StreamTestKit.OnNext(_))) @@ -51,16 +51,16 @@ class FlowConcatAllSpec extends AkkaSpec { } "on onError on master stream cancel the current open substream and signal error" in { - val publisher = StreamTestKit.PublisherProbe[FlowWithSource[Int, Int]]() + val publisher = StreamTestKit.PublisherProbe[Source[Int]]() val subscriber = StreamTestKit.SubscriberProbe[Int]() - FlowFrom(publisher).flatten(FlattenStrategy.concat).publishTo(subscriber) + Source(publisher).flatten(FlattenStrategy.concat).publishTo(subscriber) val upstream = publisher.expectSubscription() val downstream = subscriber.expectSubscription() downstream.request(1000) val substreamPublisher = StreamTestKit.PublisherProbe[Int]() - val substreamFlow = FlowFrom(substreamPublisher) + val substreamFlow = Source(substreamPublisher) upstream.expectRequest() upstream.sendNext(substreamFlow) val subUpstream = substreamPublisher.expectSubscription() @@ -71,16 +71,16 @@ class FlowConcatAllSpec extends AkkaSpec { } "on onError on open substream, cancel the master stream and signal error " in { - val publisher = StreamTestKit.PublisherProbe[FlowWithSource[Int, Int]]() + val publisher = StreamTestKit.PublisherProbe[Source[Int]]() val subscriber = StreamTestKit.SubscriberProbe[Int]() - FlowFrom(publisher).flatten(FlattenStrategy.concat).publishTo(subscriber) + Source(publisher).flatten(FlattenStrategy.concat).publishTo(subscriber) val upstream = publisher.expectSubscription() val downstream = subscriber.expectSubscription() downstream.request(1000) val substreamPublisher = StreamTestKit.PublisherProbe[Int]() - val substreamFlow = FlowFrom(substreamPublisher) + val substreamFlow = Source(substreamPublisher) upstream.expectRequest() upstream.sendNext(substreamFlow) val subUpstream = substreamPublisher.expectSubscription() @@ -91,16 +91,16 @@ class FlowConcatAllSpec extends AkkaSpec { } "on cancellation cancel the current open substream and the master stream" in { - val publisher = StreamTestKit.PublisherProbe[FlowWithSource[Int, Int]]() + val publisher = StreamTestKit.PublisherProbe[Source[Int]]() val subscriber = StreamTestKit.SubscriberProbe[Int]() - FlowFrom(publisher).flatten(FlattenStrategy.concat).publishTo(subscriber) + Source(publisher).flatten(FlattenStrategy.concat).publishTo(subscriber) val upstream = publisher.expectSubscription() val downstream = subscriber.expectSubscription() downstream.request(1000) val substreamPublisher = StreamTestKit.PublisherProbe[Int]() - val substreamFlow = FlowFrom(substreamPublisher) + val substreamFlow = Source(substreamPublisher) upstream.expectRequest() upstream.sendNext(substreamFlow) val subUpstream = substreamPublisher.expectSubscription() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowConflateSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowConflateSpec.scala index 4750813e84..c8b817153c 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowConflateSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowConflateSpec.scala @@ -23,7 +23,7 @@ class FlowConflateSpec extends AkkaSpec { val publisher = StreamTestKit.PublisherProbe[Int]() val subscriber = StreamTestKit.SubscriberProbe[Int]() - FlowFrom(publisher).conflate[Int](seed = i ⇒ i, aggregate = (sum, i) ⇒ sum + i).publishTo(subscriber) + Source(publisher).conflate[Int](seed = i ⇒ i, aggregate = (sum, i) ⇒ sum + i).publishTo(subscriber) val autoPublisher = new StreamTestKit.AutoPublisher(publisher) val sub = subscriber.expectSubscription() @@ -41,7 +41,7 @@ class FlowConflateSpec extends AkkaSpec { val publisher = StreamTestKit.PublisherProbe[Int]() val subscriber = StreamTestKit.SubscriberProbe[Int]() - FlowFrom(publisher).conflate[Int](seed = i ⇒ i, aggregate = (sum, i) ⇒ sum + i).publishTo(subscriber) + Source(publisher).conflate[Int](seed = i ⇒ i, aggregate = (sum, i) ⇒ sum + i).publishTo(subscriber) val autoPublisher = new StreamTestKit.AutoPublisher(publisher) val sub = subscriber.expectSubscription() @@ -56,13 +56,13 @@ class FlowConflateSpec extends AkkaSpec { } "work on a variable rate chain" in { - val foldSink = FoldSink[Int, Int](0)(_ + _) - val mf = FlowFrom((1 to 1000).iterator) + val foldDrain = FoldDrain[Int, Int](0)(_ + _) + val mf = Source((1 to 1000).iterator) .conflate[Int](seed = i ⇒ i, aggregate = (sum, i) ⇒ sum + i) .map { i ⇒ if (ThreadLocalRandom.current().nextBoolean()) Thread.sleep(10); i } - .withSink(foldSink) + .connect(foldDrain) .run() - val future = foldSink.future(mf) + val future = foldDrain.future(mf) Await.result(future, 10.seconds) should be(500500) } @@ -70,7 +70,7 @@ class FlowConflateSpec extends AkkaSpec { val publisher = StreamTestKit.PublisherProbe[Int]() val subscriber = StreamTestKit.SubscriberProbe[Int]() - FlowFrom(publisher).conflate[Int](seed = i ⇒ i, aggregate = (sum, i) ⇒ sum + i).publishTo(subscriber) + Source(publisher).conflate[Int](seed = i ⇒ i, aggregate = (sum, i) ⇒ sum + i).publishTo(subscriber) val autoPublisher = new StreamTestKit.AutoPublisher(publisher) val sub = subscriber.expectSubscription() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowDispatcherSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowDispatcherSpec.scala index 731390ea81..9373e8b08e 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowDispatcherSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowDispatcherSpec.scala @@ -14,7 +14,7 @@ class FlowDispatcherSpec extends AkkaSpec { "Flow with dispatcher setting" must { "use the specified dispatcher" in { val probe = TestProbe() - val p = FlowFrom(List(1, 2, 3)).map(i ⇒ + val p = Source(List(1, 2, 3)).map(i ⇒ { probe.ref ! Thread.currentThread().getName(); i }). consume() probe.receiveN(3) foreach { diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowDropSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowDropSpec.scala index ed7e2a6d7e..1d9740ec34 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowDropSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowDropSpec.scala @@ -29,7 +29,7 @@ class FlowDropSpec extends AkkaSpec with ScriptedTest { "not drop anything for negative n" in { val probe = StreamTestKit.SubscriberProbe[Int]() - FlowFrom(List(1, 2, 3)).drop(-1).publishTo(probe) + Source(List(1, 2, 3)).drop(-1).publishTo(probe) probe.expectSubscription().request(10) probe.expectNext(1) probe.expectNext(2) diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowDropWithinSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowDropWithinSpec.scala index 5a9fb633e3..4c46440d38 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowDropWithinSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowDropWithinSpec.scala @@ -18,7 +18,7 @@ class FlowDropWithinSpec extends AkkaSpec { val input = Iterator.from(1) val p = StreamTestKit.PublisherProbe[Int]() val c = StreamTestKit.SubscriberProbe[Int]() - FlowFrom(p).dropWithin(1.second).publishTo(c) + Source(p).dropWithin(1.second).publishTo(c) val pSub = p.expectSubscription val cSub = c.expectSubscription cSub.request(100) diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowExpandSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowExpandSpec.scala index b1199c4b00..d775c47638 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowExpandSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowExpandSpec.scala @@ -24,7 +24,7 @@ class FlowExpandSpec extends AkkaSpec { val subscriber = StreamTestKit.SubscriberProbe[Int]() // Simply repeat the last element as an extrapolation step - FlowFrom(publisher).expand[Int, Int](seed = i ⇒ i, extrapolate = i ⇒ (i, i)).publishTo(subscriber) + Source(publisher).expand[Int, Int](seed = i ⇒ i, extrapolate = i ⇒ (i, i)).publishTo(subscriber) val autoPublisher = new StreamTestKit.AutoPublisher(publisher) val sub = subscriber.expectSubscription() @@ -44,7 +44,7 @@ class FlowExpandSpec extends AkkaSpec { val subscriber = StreamTestKit.SubscriberProbe[Int]() // Simply repeat the last element as an extrapolation step - FlowFrom(publisher).expand[Int, Int](seed = i ⇒ i, extrapolate = i ⇒ (i, i)).publishTo(subscriber) + Source(publisher).expand[Int, Int](seed = i ⇒ i, extrapolate = i ⇒ (i, i)).publishTo(subscriber) val autoPublisher = new StreamTestKit.AutoPublisher(publisher) val sub = subscriber.expectSubscription() @@ -64,13 +64,13 @@ class FlowExpandSpec extends AkkaSpec { } "work on a variable rate chain" in { - val foldSink = FoldSink[Set[Int], Int](Set.empty[Int])(_ + _) - val mf = FlowFrom((1 to 100).iterator) + val foldDrain = FoldDrain[Set[Int], Int](Set.empty[Int])(_ + _) + val mf = Source((1 to 100).iterator) .map { i ⇒ if (ThreadLocalRandom.current().nextBoolean()) Thread.sleep(10); i } .expand[Int, Int](seed = i ⇒ i, extrapolate = i ⇒ (i, i)) - .withSink(foldSink) + .connect(foldDrain) .run() - val future = foldSink.future(mf) + val future = foldDrain.future(mf) Await.result(future, 10.seconds) should be(Set.empty[Int] ++ (1 to 100)) } @@ -79,7 +79,7 @@ class FlowExpandSpec extends AkkaSpec { val publisher = StreamTestKit.PublisherProbe[Int]() val subscriber = StreamTestKit.SubscriberProbe[Int]() - FlowFrom(publisher).expand[Int, Int](seed = i ⇒ i, extrapolate = i ⇒ (i, i)).publishTo(subscriber) + Source(publisher).expand[Int, Int](seed = i ⇒ i, extrapolate = i ⇒ (i, i)).publishTo(subscriber) val autoPublisher = new StreamTestKit.AutoPublisher(publisher) val sub = subscriber.expectSubscription() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowFilterSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowFilterSpec.scala index f35e466ebe..f5ee770eb0 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowFilterSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowFilterSpec.scala @@ -29,7 +29,7 @@ class FlowFilterSpec extends AkkaSpec with ScriptedTest { implicit val materializer = FlowMaterializer(settings) val probe = StreamTestKit.SubscriberProbe[Int]() - FlowFrom(Iterator.fill(1000)(0) ++ List(1)).filter(_ != 0). + Source(Iterator.fill(1000)(0) ++ List(1)).filter(_ != 0). toPublisher().subscribe(probe) val subscription = probe.expectSubscription() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowFoldSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowFoldSpec.scala index 231a318640..2ee2b2b9eb 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowFoldSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowFoldSpec.scala @@ -15,17 +15,17 @@ class FlowFoldSpec extends AkkaSpec with DefaultTimeout { "fold" in { val input = 1 to 100 - val foldSink = FoldSink[Int, Int](0)(_ + _) - val mf = FlowFrom(input).withSink(foldSink).run() - val future = foldSink.future(mf) + val foldDrain = FoldDrain[Int, Int](0)(_ + _) + val mf = Source(input).connect(foldDrain).run() + val future = foldDrain.future(mf) val expected = input.fold(0)(_ + _) Await.result(future, timeout.duration) should be(expected) } "propagate an error" in { val error = new Exception with NoStackTrace - val foldSink = FoldSink[Unit, Unit](())((_, _) ⇒ ()) - val mf = FlowFrom[Unit](() ⇒ throw error).withSink(foldSink).run() + val foldSink = FoldDrain[Unit, Unit](())((_, _) ⇒ ()) + val mf = Source[Unit](() ⇒ throw error).connect(foldSink).run() val future = foldSink.future(mf) the[Exception] thrownBy Await.result(future, timeout.duration) should be(error) } diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowForeachSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowForeachSpec.scala index 6829abd061..aee60f2922 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowForeachSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowForeachSpec.scala @@ -16,9 +16,9 @@ class FlowForeachSpec extends AkkaSpec { "A Foreach" must { "call the procedure for each element" in { - val foreachSink = ForeachSink[Int](testActor ! _) - val mf = FlowFrom(1 to 3).withSink(foreachSink).run() - foreachSink.future(mf).onSuccess { + val foreachDrain = ForeachDrain[Int](testActor ! _) + val mf = Source(1 to 3).connect(foreachDrain).run() + foreachDrain.future(mf).onSuccess { case _ ⇒ testActor ! "done" } expectMsg(1) @@ -28,9 +28,9 @@ class FlowForeachSpec extends AkkaSpec { } "complete the future for an empty stream" in { - val foreachSink = ForeachSink[Int](testActor ! _) - val mf = FlowFrom(Nil).withSink(foreachSink).run() - foreachSink.future(mf).onSuccess { + val foreachDrain = ForeachDrain[Int](testActor ! _) + val mf = Source(Nil).connect(foreachDrain).run() + foreachDrain.future(mf).onSuccess { case _ ⇒ testActor ! "done" } expectMsg("done") @@ -38,9 +38,9 @@ class FlowForeachSpec extends AkkaSpec { "yield the first error" in { val p = StreamTestKit.PublisherProbe[Int]() - val foreachSink = ForeachSink[Int](testActor ! _) - val mf = FlowFrom(p).withSink(foreachSink).run() - foreachSink.future(mf).onFailure { + val foreachDrain = ForeachDrain[Int](testActor ! _) + val mf = Source(p).connect(foreachDrain).run() + foreachDrain.future(mf).onFailure { case ex ⇒ testActor ! ex } val proc = p.expectSubscription diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowFromFutureSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowFromFutureSpec.scala index 89daaf48d2..fab000f6bd 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowFromFutureSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowFromFutureSpec.scala @@ -17,7 +17,7 @@ class FlowFromFutureSpec extends AkkaSpec { "A Flow based on a Future" must { "produce one element from already successful Future" in { - val p = FlowFrom(Future.successful(1)).toPublisher() + val p = Source(Future.successful(1)).toPublisher() val c = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c) val sub = c.expectSubscription() @@ -29,7 +29,7 @@ class FlowFromFutureSpec extends AkkaSpec { "produce error from already failed Future" in { val ex = new RuntimeException("test") with NoStackTrace - val p = FlowFrom(Future.failed[Int](ex)).toPublisher() + val p = Source(Future.failed[Int](ex)).toPublisher() val c = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c) c.expectError(ex) @@ -37,7 +37,7 @@ class FlowFromFutureSpec extends AkkaSpec { "produce one element when Future is completed" in { val promise = Promise[Int]() - val p = FlowFrom(promise.future).toPublisher() + val p = Source(promise.future).toPublisher() val c = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c) val sub = c.expectSubscription() @@ -51,7 +51,7 @@ class FlowFromFutureSpec extends AkkaSpec { "produce one element when Future is completed but not before request" in { val promise = Promise[Int]() - val p = FlowFrom(promise.future).toPublisher() + val p = Source(promise.future).toPublisher() val c = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c) val sub = c.expectSubscription() @@ -64,7 +64,7 @@ class FlowFromFutureSpec extends AkkaSpec { "produce elements with multiple subscribers" in { val promise = Promise[Int]() - val p = FlowFrom(promise.future).toPublisher() + val p = Source(promise.future).toPublisher() val c1 = StreamTestKit.SubscriberProbe[Int]() val c2 = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c1) @@ -82,7 +82,7 @@ class FlowFromFutureSpec extends AkkaSpec { "produce elements to later subscriber" in { val promise = Promise[Int]() - val p = FlowFrom(promise.future).toPublisher() + val p = Source(promise.future).toPublisher() val keepAlive = StreamTestKit.SubscriberProbe[Int]() val c1 = StreamTestKit.SubscriberProbe[Int]() val c2 = StreamTestKit.SubscriberProbe[Int]() @@ -103,7 +103,7 @@ class FlowFromFutureSpec extends AkkaSpec { "allow cancel before receiving element" in { val promise = Promise[Int]() - val p = FlowFrom(promise.future).toPublisher() + val p = Source(promise.future).toPublisher() val keepAlive = StreamTestKit.SubscriberProbe[Int]() val c = StreamTestKit.SubscriberProbe[Int]() p.subscribe(keepAlive) diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowGraphCompileSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowGraphCompileSpec.scala index 2fb187acdc..802752b841 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowGraphCompileSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowGraphCompileSpec.scala @@ -25,17 +25,17 @@ class FlowGraphCompileSpec extends AkkaSpec { } } - val f1 = FlowFrom[String].transform("f1", op[String, String]) - val f2 = FlowFrom[String].transform("f2", op[String, String]) - val f3 = FlowFrom[String].transform("f3", op[String, String]) - val f4 = FlowFrom[String].transform("f4", op[String, String]) - val f5 = FlowFrom[String].transform("f5", op[String, String]) - val f6 = FlowFrom[String].transform("f6", op[String, String]) + val f1 = Flow[String].transform("f1", op[String, String]) + val f2 = Flow[String].transform("f2", op[String, String]) + val f3 = Flow[String].transform("f3", op[String, String]) + val f4 = Flow[String].transform("f4", op[String, String]) + val f5 = Flow[String].transform("f5", op[String, String]) + val f6 = Flow[String].transform("f6", op[String, String]) - val in1 = IterableSource(List("a", "b", "c")) - val in2 = IterableSource(List("d", "e", "f")) - val out1 = PublisherSink[String] - val out2 = FutureSink[String] + val in1 = IterableTap(List("a", "b", "c")) + val in2 = IterableTap(List("d", "e", "f")) + val out1 = PublisherDrain[String] + val out2 = FutureDrain[String] "FlowGraph" should { "build simple merge" in { @@ -97,7 +97,7 @@ class FlowGraphCompileSpec extends AkkaSpec { val merge = Merge[String] val bcast1 = Broadcast[String] val bcast2 = Broadcast[String] - val feedbackLoopBuffer = FlowFrom[String].buffer(10, OverflowStrategy.dropBuffer) + val feedbackLoopBuffer = Flow[String].buffer(10, OverflowStrategy.dropBuffer) b. addEdge(in1, f1, merge). addEdge(merge, f2, bcast1). @@ -116,7 +116,7 @@ class FlowGraphCompileSpec extends AkkaSpec { val merge = Merge[String] val bcast1 = Broadcast[String] val bcast2 = Broadcast[String] - val feedbackLoopBuffer = FlowFrom[String].buffer(10, OverflowStrategy.dropBuffer) + val feedbackLoopBuffer = Flow[String].buffer(10, OverflowStrategy.dropBuffer) import FlowGraphImplicits._ in1 ~> f1 ~> merge ~> f2 ~> bcast1 ~> f3 ~> out1 bcast1 ~> feedbackLoopBuffer ~> bcast2 ~> f5 ~> merge @@ -145,13 +145,13 @@ class FlowGraphCompileSpec extends AkkaSpec { val m9 = Merge[String] val m10 = Merge[String] val m11 = Merge[String] - val in3 = IterableSource(List("b")) - val in5 = IterableSource(List("b")) - val in7 = IterableSource(List("a")) - val out2 = PublisherSink[String] - val out9 = PublisherSink[String] - val out10 = PublisherSink[String] - def f(s: String) = FlowFrom[String].transform(s, op[String, String]) + val in3 = IterableTap(List("b")) + val in5 = IterableTap(List("b")) + val in7 = IterableTap(List("a")) + val out2 = PublisherDrain[String] + val out9 = PublisherDrain[String] + val out10 = PublisherDrain[String] + def f(s: String) = Flow[String].transform(s, op[String, String]) import FlowGraphImplicits._ in7 ~> f("a") ~> b7 ~> f("b") ~> m11 ~> f("c") ~> b11 ~> f("d") ~> out2 @@ -164,53 +164,53 @@ class FlowGraphCompileSpec extends AkkaSpec { }.run() } - "attachSource and attachSink" in { + "attachTap and attachDrain" in { val mg = FlowGraph { b ⇒ val merge = Merge[String] - val undefinedSrc1 = UndefinedSource[String] - val undefinedSrc2 = UndefinedSource[String] - val undefinedSink1 = UndefinedSink[String] + val undefinedSrc1 = UndefinedTap[String] + val undefinedSrc2 = UndefinedTap[String] + val undefinedDrain1 = UndefinedDrain[String] b. addEdge(undefinedSrc1, f1, merge). - addEdge(UndefinedSource[String]("src2"), f2, merge). - addEdge(merge, f3, undefinedSink1) + addEdge(UndefinedTap[String]("src2"), f2, merge). + addEdge(merge, f3, undefinedDrain1) - b.attachSource(undefinedSrc1, in1) - b.attachSource(UndefinedSource[String]("src2"), in2) - b.attachSink(undefinedSink1, out1) + b.attachTap(undefinedSrc1, in1) + b.attachTap(UndefinedTap[String]("src2"), in2) + b.attachDrain(undefinedDrain1, out1) }.run() out1.publisher(mg) should not be (null) } "build partial flow graphs" in { - val undefinedSrc1 = UndefinedSource[String] - val undefinedSrc2 = UndefinedSource[String] - val undefinedSink1 = UndefinedSink[String] + val undefinedSrc1 = UndefinedTap[String] + val undefinedSrc2 = UndefinedTap[String] + val undefinedDrain1 = UndefinedDrain[String] val bcast = Broadcast[String] val partial1 = PartialFlowGraph { implicit b ⇒ import FlowGraphImplicits._ val merge = Merge[String] - undefinedSrc1 ~> f1 ~> merge ~> f2 ~> bcast ~> f3 ~> undefinedSink1 + undefinedSrc1 ~> f1 ~> merge ~> f2 ~> bcast ~> f3 ~> undefinedDrain1 undefinedSrc2 ~> f4 ~> merge } - partial1.undefinedSources should be(Set(undefinedSrc1, undefinedSrc2)) - partial1.undefinedSinks should be(Set(undefinedSink1)) + partial1.undefinedTaps should be(Set(undefinedSrc1, undefinedSrc2)) + partial1.undefinedDrains should be(Set(undefinedDrain1)) val partial2 = PartialFlowGraph(partial1) { implicit b ⇒ import FlowGraphImplicits._ - b.attachSource(undefinedSrc1, in1) - b.attachSource(undefinedSrc2, in2) - bcast ~> f5 ~> UndefinedSink[String]("sink2") + b.attachTap(undefinedSrc1, in1) + b.attachTap(undefinedSrc2, in2) + bcast ~> f5 ~> UndefinedDrain[String]("drain2") } - partial2.undefinedSources should be(Set.empty) - partial2.undefinedSinks should be(Set(undefinedSink1, UndefinedSink[String]("sink2"))) + partial2.undefinedTaps should be(Set.empty) + partial2.undefinedDrains should be(Set(undefinedDrain1, UndefinedDrain[String]("drain2"))) FlowGraph(partial2) { implicit b ⇒ - b.attachSink(undefinedSink1, out1) - b.attachSink(UndefinedSink[String]("sink2"), out2) + b.attachDrain(undefinedDrain1, out1) + b.attachDrain(UndefinedDrain[String]("drain2"), out2) }.run() } @@ -225,22 +225,13 @@ class FlowGraphCompileSpec extends AkkaSpec { }.run() } - "use FlowWithSource and FlowWithSink" in { - FlowGraph { implicit b ⇒ - val bcast = Broadcast[String] - import FlowGraphImplicits._ - f1.withSource(in1) ~> bcast ~> f2.withSink(out1) - bcast ~> f3.withSink(out2) - }.run() - } - "chain input and output ports" in { FlowGraph { implicit b ⇒ val zip = Zip[Int, String] - val out = PublisherSink[(Int, String)] + val out = PublisherDrain[(Int, String)] import FlowGraphImplicits._ - FlowFrom(List(1, 2, 3)) ~> zip.left ~> out - FlowFrom(List("a", "b", "c")) ~> zip.right + Source(List(1, 2, 3)) ~> zip.left ~> out + Source(List("a", "b", "c")) ~> zip.right }.run() } @@ -248,10 +239,10 @@ class FlowGraphCompileSpec extends AkkaSpec { FlowGraph { implicit b ⇒ val zip = Zip[Int, String] val unzip = Unzip[Int, String] - val out = PublisherSink[(Int, String)] + val out = PublisherDrain[(Int, String)] import FlowGraphImplicits._ - FlowFrom(List(1 -> "a", 2 -> "b", 3 -> "c")) ~> unzip.in - unzip.left ~> FlowFrom[Int].map(_ * 2) ~> zip.left + Source(List(1 -> "a", 2 -> "b", 3 -> "c")) ~> unzip.in + unzip.left ~> Flow[Int].map(_ * 2) ~> zip.left unzip.right ~> zip.right zip.out ~> out }.run() @@ -262,15 +253,15 @@ class FlowGraphCompileSpec extends AkkaSpec { FlowGraph { implicit b ⇒ val zip = Zip[Int, String] val unzip = Unzip[Int, String] - val wrongOut = PublisherSink[(Int, Int)] - val whatever = PublisherSink[Any] + val wrongOut = PublisherDrain[(Int, Int)] + val whatever = PublisherDrain[Any] import FlowGraphImplicits._ - "FlowFrom(List(1, 2, 3)) ~> zip.left ~> wrongOut" shouldNot compile - """FlowFrom(List("a", "b", "c")) ~> zip.left""" shouldNot compile - """FlowFrom(List("a", "b", "c")) ~> zip.out""" shouldNot compile + "Flow(List(1, 2, 3)) ~> zip.left ~> wrongOut" shouldNot compile + """Flow(List("a", "b", "c")) ~> zip.left""" shouldNot compile + """Flow(List("a", "b", "c")) ~> zip.out""" shouldNot compile "zip.left ~> zip.right" shouldNot compile - "FlowFrom(List(1, 2, 3)) ~> zip.left ~> wrongOut" shouldNot compile - """FlowFrom(List(1 -> "a", 2 -> "b", 3 -> "c")) ~> unzip.in ~> whatever""" shouldNot compile + "Flow(List(1, 2, 3)) ~> zip.left ~> wrongOut" shouldNot compile + """Flow(List(1 -> "a", 2 -> "b", 3 -> "c")) ~> unzip.in ~> whatever""" shouldNot compile } }.getMessage should include("empty") } @@ -298,50 +289,50 @@ class FlowGraphCompileSpec extends AkkaSpec { } "build with variance" in { - val out = SubscriberSink(SubscriberProbe[Fruit]()) + val out = SubscriberDrain(SubscriberProbe[Fruit]()) FlowGraph { b ⇒ val merge = Merge[Fruit] b. - addEdge(FlowFrom[Fruit](() ⇒ Some(new Apple)), merge). - addEdge(FlowFrom[Apple](() ⇒ Some(new Apple)), merge). - addEdge(merge, FlowFrom[Fruit].map(identity), out) + addEdge(Source[Fruit](() ⇒ Some(new Apple)), merge). + addEdge(Source[Apple](() ⇒ Some(new Apple)), merge). + addEdge(merge, Flow[Fruit].map(identity), out) } } "build with implicits and variance" in { PartialFlowGraph { implicit b ⇒ - val inA = PublisherSource(PublisherProbe[Fruit]()) - val inB = PublisherSource(PublisherProbe[Apple]()) - val outA = SubscriberSink(SubscriberProbe[Fruit]()) - val outB = SubscriberSink(SubscriberProbe[Fruit]()) + val inA = PublisherTap(PublisherProbe[Fruit]()) + val inB = PublisherTap(PublisherProbe[Apple]()) + val outA = SubscriberDrain(SubscriberProbe[Fruit]()) + val outB = SubscriberDrain(SubscriberProbe[Fruit]()) val merge = Merge[Fruit] val unzip = Unzip[Int, String] - val whatever = PublisherSink[Any] + val whatever = PublisherDrain[Any] import FlowGraphImplicits._ - FlowFrom[Fruit](() ⇒ Some(new Apple)) ~> merge - FlowFrom[Apple](() ⇒ Some(new Apple)) ~> merge + Source[Fruit](() ⇒ Some(new Apple)) ~> merge + Source[Apple](() ⇒ Some(new Apple)) ~> merge inA ~> merge inB ~> merge - inA ~> FlowFrom[Fruit].map(identity) ~> merge - inB ~> FlowFrom[Apple].map(identity) ~> merge - UndefinedSource[Apple] ~> merge - UndefinedSource[Apple] ~> FlowFrom[Fruit].map(identity) ~> merge - UndefinedSource[Apple] ~> FlowFrom[Apple].map(identity) ~> merge - merge ~> FlowFrom[Fruit].map(identity) ~> outA + inA ~> Flow[Fruit].map(identity) ~> merge + inB ~> Flow[Apple].map(identity) ~> merge + UndefinedTap[Apple] ~> merge + UndefinedTap[Apple] ~> Flow[Fruit].map(identity) ~> merge + UndefinedTap[Apple] ~> Flow[Apple].map(identity) ~> merge + merge ~> Flow[Fruit].map(identity) ~> outA - FlowFrom[Apple](() ⇒ Some(new Apple)) ~> Broadcast[Apple] ~> merge - FlowFrom[Apple](() ⇒ Some(new Apple)) ~> Broadcast[Apple] ~> outB - FlowFrom[Apple](() ⇒ Some(new Apple)) ~> Broadcast[Apple] ~> UndefinedSink[Fruit] + Source[Apple](() ⇒ Some(new Apple)) ~> Broadcast[Apple] ~> merge + Source[Apple](() ⇒ Some(new Apple)) ~> Broadcast[Apple] ~> outB + Source[Apple](() ⇒ Some(new Apple)) ~> Broadcast[Apple] ~> UndefinedDrain[Fruit] inB ~> Broadcast[Apple] ~> merge - FlowFrom(List(1 -> "a", 2 -> "b", 3 -> "c")) ~> unzip.in + Source(List(1 -> "a", 2 -> "b", 3 -> "c")) ~> unzip.in unzip.right ~> whatever - unzip.left ~> UndefinedSink[Any] + unzip.left ~> UndefinedDrain[Any] - "UndefinedSource[Fruit] ~> FlowFrom[Apple].map(identity) ~> merge" shouldNot compile - "UndefinedSource[Fruit] ~> Broadcast[Apple]" shouldNot compile + "UndefinedTap[Fruit] ~> Flow[Apple].map(identity) ~> merge" shouldNot compile + "UndefinedTap[Fruit] ~> Broadcast[Apple]" shouldNot compile "merge ~> Broadcast[Apple]" shouldNot compile - "merge ~> FlowFrom[Fruit].map(identity) ~> Broadcast[Apple]" shouldNot compile + "merge ~> Flow[Fruit].map(identity) ~> Broadcast[Apple]" shouldNot compile "inB ~> merge ~> Broadcast[Apple]" shouldNot compile "inA ~> Broadcast[Apple]" shouldNot compile } diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowGroupBySpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowGroupBySpec.scala index ab00a75ac3..e87391ec8c 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowGroupBySpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowGroupBySpec.scala @@ -32,19 +32,19 @@ class FlowGroupBySpec extends AkkaSpec { } class SubstreamsSupport(groupCount: Int = 2, elementCount: Int = 6) { - val source = FlowFrom((1 to elementCount).iterator).toPublisher() - val groupStream = FlowFrom(source).groupBy(_ % groupCount).toPublisher() - val masterSubscriber = StreamTestKit.SubscriberProbe[(Int, FlowWithSource[Int, Int])]() + val tap = Source((1 to elementCount).iterator).toPublisher() + val groupStream = Source(tap).groupBy(_ % groupCount).toPublisher() + val masterSubscriber = StreamTestKit.SubscriberProbe[(Int, Source[Int])]() groupStream.subscribe(masterSubscriber) val masterSubscription = masterSubscriber.expectSubscription() - def getSubFlow(expectedKey: Int): FlowWithSource[Int, Int] = { + def getSubFlow(expectedKey: Int): Source[Int] = { masterSubscription.request(1) expectSubFlow(expectedKey: Int) } - def expectSubFlow(expectedKey: Int): FlowWithSource[Int, Int] = { + def expectSubFlow(expectedKey: Int): Source[Int] = { val (key, substream) = masterSubscriber.expectNext() key should be(expectedKey) substream @@ -110,8 +110,8 @@ class FlowGroupBySpec extends AkkaSpec { "accept cancellation of master stream when not consumed anything" in { val publisherProbeProbe = StreamTestKit.PublisherProbe[Int]() - val publisher = FlowFrom(publisherProbeProbe).groupBy(_ % 2).toPublisher() - val subscriber = StreamTestKit.SubscriberProbe[(Int, FlowWithSource[Int, Int])]() + val publisher = Source(publisherProbeProbe).groupBy(_ % 2).toPublisher() + val subscriber = StreamTestKit.SubscriberProbe[(Int, Source[Int])]() publisher.subscribe(subscriber) val upstreamSubscription = publisherProbeProbe.expectSubscription() @@ -141,8 +141,8 @@ class FlowGroupBySpec extends AkkaSpec { } "work with empty input stream" in { - val publisher = FlowFrom(List.empty[Int]).groupBy(_ % 2).toPublisher() - val subscriber = StreamTestKit.SubscriberProbe[(Int, FlowWithSource[Int, Int])]() + val publisher = Source(List.empty[Int]).groupBy(_ % 2).toPublisher() + val subscriber = StreamTestKit.SubscriberProbe[(Int, Source[Int])]() publisher.subscribe(subscriber) subscriber.expectCompletedOrSubscriptionFollowedByComplete() @@ -150,8 +150,8 @@ class FlowGroupBySpec extends AkkaSpec { "abort on onError from upstream" in { val publisherProbeProbe = StreamTestKit.PublisherProbe[Int]() - val publisher = FlowFrom(publisherProbeProbe).groupBy(_ % 2).toPublisher() - val subscriber = StreamTestKit.SubscriberProbe[(Int, FlowWithSource[Int, Int])]() + val publisher = Source(publisherProbeProbe).groupBy(_ % 2).toPublisher() + val subscriber = StreamTestKit.SubscriberProbe[(Int, Source[Int])]() publisher.subscribe(subscriber) val upstreamSubscription = publisherProbeProbe.expectSubscription() @@ -167,8 +167,8 @@ class FlowGroupBySpec extends AkkaSpec { "abort on onError from upstream when substreams are running" in { val publisherProbeProbe = StreamTestKit.PublisherProbe[Int]() - val publisher = FlowFrom(publisherProbeProbe).groupBy(_ % 2).toPublisher() - val subscriber = StreamTestKit.SubscriberProbe[(Int, FlowWithSource[Int, Int])]() + val publisher = Source(publisherProbeProbe).groupBy(_ % 2).toPublisher() + val subscriber = StreamTestKit.SubscriberProbe[(Int, Source[Int])]() publisher.subscribe(subscriber) val upstreamSubscription = publisherProbeProbe.expectSubscription() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowGroupedWithinSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowGroupedWithinSpec.scala index d5f0797ad0..cfa4917821 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowGroupedWithinSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowGroupedWithinSpec.scala @@ -24,7 +24,7 @@ class FlowGroupedWithinSpec extends AkkaSpec with ScriptedTest { val input = Iterator.from(1) val p = StreamTestKit.PublisherProbe[Int]() val c = StreamTestKit.SubscriberProbe[immutable.Seq[Int]]() - FlowFrom(p).groupedWithin(1000, 1.second).publishTo(c) + Source(p).groupedWithin(1000, 1.second).publishTo(c) val pSub = p.expectSubscription val cSub = c.expectSubscription cSub.request(100) @@ -49,7 +49,7 @@ class FlowGroupedWithinSpec extends AkkaSpec with ScriptedTest { "deliver bufferd elements onComplete before the timeout" in { val c = StreamTestKit.SubscriberProbe[immutable.Seq[Int]]() - FlowFrom(1 to 3).groupedWithin(1000, 10.second).publishTo(c) + Source(1 to 3).groupedWithin(1000, 10.second).publishTo(c) val cSub = c.expectSubscription cSub.request(100) c.expectNext((1 to 3).toList) @@ -61,7 +61,7 @@ class FlowGroupedWithinSpec extends AkkaSpec with ScriptedTest { val input = Iterator.from(1) val p = StreamTestKit.PublisherProbe[Int]() val c = StreamTestKit.SubscriberProbe[immutable.Seq[Int]]() - FlowFrom(p).groupedWithin(1000, 1.second).publishTo(c) + Source(p).groupedWithin(1000, 1.second).publishTo(c) val pSub = p.expectSubscription val cSub = c.expectSubscription cSub.request(1) @@ -81,7 +81,7 @@ class FlowGroupedWithinSpec extends AkkaSpec with ScriptedTest { "drop empty groups" in { val p = StreamTestKit.PublisherProbe[Int]() val c = StreamTestKit.SubscriberProbe[immutable.Seq[Int]]() - FlowFrom(p).groupedWithin(1000, 500.millis).publishTo(c) + Source(p).groupedWithin(1000, 500.millis).publishTo(c) val pSub = p.expectSubscription val cSub = c.expectSubscription cSub.request(2) @@ -103,7 +103,7 @@ class FlowGroupedWithinSpec extends AkkaSpec with ScriptedTest { val input = Iterator.from(1) val p = StreamTestKit.PublisherProbe[Int]() val c = StreamTestKit.SubscriberProbe[immutable.Seq[Int]]() - FlowFrom(p).groupedWithin(3, 2.second).publishTo(c) + Source(p).groupedWithin(3, 2.second).publishTo(c) val pSub = p.expectSubscription val cSub = c.expectSubscription cSub.request(4) diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowIterableSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowIterableSpec.scala index ed6a56d670..e2614eb429 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowIterableSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowIterableSpec.scala @@ -18,7 +18,7 @@ class FlowIterableSpec extends AkkaSpec { "A Flow based on an iterable" must { "produce elements" in { - val p = FlowFrom(List(1, 2, 3)).toPublisher() + val p = Source(List(1, 2, 3)).toPublisher() val c = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c) val sub = c.expectSubscription() @@ -32,7 +32,7 @@ class FlowIterableSpec extends AkkaSpec { } "complete empty" in { - val p = FlowFrom(List.empty[Int]).toPublisher() + val p = Source(List.empty[Int]).toPublisher() val c = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c) c.expectComplete() @@ -44,7 +44,7 @@ class FlowIterableSpec extends AkkaSpec { } "produce elements with multiple subscribers" in { - val p = FlowFrom(List(1, 2, 3)).toPublisher() + val p = Source(List(1, 2, 3)).toPublisher() val c1 = StreamTestKit.SubscriberProbe[Int]() val c2 = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c1) @@ -68,7 +68,7 @@ class FlowIterableSpec extends AkkaSpec { } "produce elements to later subscriber" in { - val p = FlowFrom(List(1, 2, 3)).toPublisher() + val p = Source(List(1, 2, 3)).toPublisher() val c1 = StreamTestKit.SubscriberProbe[Int]() val c2 = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c1) @@ -94,7 +94,7 @@ class FlowIterableSpec extends AkkaSpec { } "produce elements with one transformation step" in { - val p = FlowFrom(List(1, 2, 3)).map(_ * 2).toPublisher() + val p = Source(List(1, 2, 3)).map(_ * 2).toPublisher() val c = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c) val sub = c.expectSubscription() @@ -106,7 +106,7 @@ class FlowIterableSpec extends AkkaSpec { } "produce elements with two transformation steps" in { - val p = FlowFrom(List(1, 2, 3, 4)).filter(_ % 2 == 0).map(_ * 2).toPublisher() + val p = Source(List(1, 2, 3, 4)).filter(_ % 2 == 0).map(_ * 2).toPublisher() val c = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c) val sub = c.expectSubscription() @@ -118,7 +118,7 @@ class FlowIterableSpec extends AkkaSpec { "allow cancel before receiving all elements" in { val count = 100000 - val p = FlowFrom(1 to count).toPublisher() + val p = Source(1 to count).toPublisher() val c = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c) val sub = c.expectSubscription() @@ -134,19 +134,19 @@ class FlowIterableSpec extends AkkaSpec { } "have value equality of publisher" in { - val p1 = FlowFrom(List(1, 2, 3)).toPublisher() - val p2 = FlowFrom(List(1, 2, 3)).toPublisher() + val p1 = Source(List(1, 2, 3)).toPublisher() + val p2 = Source(List(1, 2, 3)).toPublisher() p1 should be(p2) p2 should be(p1) - val p3 = FlowFrom(List(1, 2, 3, 4)).toPublisher() + val p3 = Source(List(1, 2, 3, 4)).toPublisher() p1 should not be (p3) p3 should not be (p1) - val p4 = FlowFrom(Vector.empty[String]).toPublisher() - val p5 = FlowFrom(Set.empty[String]).toPublisher() + val p4 = Source(Vector.empty[String]).toPublisher() + val p5 = Source(Set.empty[String]).toPublisher() p1 should not be (p4) p4 should be(p5) p5 should be(p4) - val p6 = FlowFrom(List(1, 2, 3).iterator).toPublisher() + val p6 = Source(List(1, 2, 3).iterator).toPublisher() p1 should not be (p6) p6 should not be (p1) } diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowIteratorSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowIteratorSpec.scala index c9b9d4837a..d3b39db7bb 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowIteratorSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowIteratorSpec.scala @@ -22,7 +22,7 @@ class FlowIteratorSpec extends AkkaSpec { "A Flow based on an iterator" must { "produce elements" in { - val p = FlowFrom(List(1, 2, 3).iterator).toPublisher() + val p = Source(List(1, 2, 3).iterator).toPublisher() val c = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c) val sub = c.expectSubscription() @@ -36,7 +36,7 @@ class FlowIteratorSpec extends AkkaSpec { } "complete empty" in { - val p = FlowFrom(List.empty[Int].iterator).toPublisher() + val p = Source(List.empty[Int].iterator).toPublisher() val c = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c) c.expectComplete() @@ -48,7 +48,7 @@ class FlowIteratorSpec extends AkkaSpec { } "produce elements with multiple subscribers" in { - val p = FlowFrom(List(1, 2, 3).iterator).toPublisher() + val p = Source(List(1, 2, 3).iterator).toPublisher() val c1 = StreamTestKit.SubscriberProbe[Int]() val c2 = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c1) @@ -72,7 +72,7 @@ class FlowIteratorSpec extends AkkaSpec { } "produce elements to later subscriber" in { - val p = FlowFrom(List(1, 2, 3).iterator).toPublisher() + val p = Source(List(1, 2, 3).iterator).toPublisher() val c1 = StreamTestKit.SubscriberProbe[Int]() val c2 = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c1) @@ -95,7 +95,7 @@ class FlowIteratorSpec extends AkkaSpec { } "produce elements with one transformation step" in { - val p = FlowFrom(List(1, 2, 3).iterator).map(_ * 2).toPublisher() + val p = Source(List(1, 2, 3).iterator).map(_ * 2).toPublisher() val c = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c) val sub = c.expectSubscription() @@ -107,7 +107,7 @@ class FlowIteratorSpec extends AkkaSpec { } "produce elements with two transformation steps" in { - val p = FlowFrom(List(1, 2, 3, 4).iterator).filter(_ % 2 == 0).map(_ * 2).toPublisher() + val p = Source(List(1, 2, 3, 4).iterator).filter(_ % 2 == 0).map(_ * 2).toPublisher() val c = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c) val sub = c.expectSubscription() @@ -119,7 +119,7 @@ class FlowIteratorSpec extends AkkaSpec { "allow cancel before receiving all elements" in { val count = 100000 - val p = FlowFrom((1 to count).iterator).toPublisher() + val p = Source((1 to count).iterator).toPublisher() val c = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c) val sub = c.expectSubscription() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowMapAsyncSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowMapAsyncSpec.scala index e95fb96006..0148119ec5 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowMapAsyncSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowMapAsyncSpec.scala @@ -23,7 +23,7 @@ class FlowMapAsyncSpec extends AkkaSpec { "produce future elements" in { val c = StreamTestKit.SubscriberProbe[Int]() implicit val ec = system.dispatcher - val p = FlowFrom(1 to 3).mapAsync(n ⇒ Future(n)).publishTo(c) + val p = Source(1 to 3).mapAsync(n ⇒ Future(n)).publishTo(c) val sub = c.expectSubscription() sub.request(2) c.expectNext(1) @@ -37,7 +37,7 @@ class FlowMapAsyncSpec extends AkkaSpec { "produce future elements in order" in { val c = StreamTestKit.SubscriberProbe[Int]() implicit val ec = system.dispatcher - val p = FlowFrom(1 to 50).mapAsync(n ⇒ Future { + val p = Source(1 to 50).mapAsync(n ⇒ Future { Thread.sleep(ThreadLocalRandom.current().nextInt(1, 10)) n }).publishTo(c) @@ -51,7 +51,7 @@ class FlowMapAsyncSpec extends AkkaSpec { val probe = TestProbe() val c = StreamTestKit.SubscriberProbe[Int]() implicit val ec = system.dispatcher - val p = FlowFrom(1 to 20).mapAsync(n ⇒ Future { + val p = Source(1 to 20).mapAsync(n ⇒ Future { probe.ref ! n n }).publishTo(c) @@ -76,7 +76,7 @@ class FlowMapAsyncSpec extends AkkaSpec { val latch = TestLatch(1) val c = StreamTestKit.SubscriberProbe[Int]() implicit val ec = system.dispatcher - val p = FlowFrom(1 to 5).mapAsync(n ⇒ Future { + val p = Source(1 to 5).mapAsync(n ⇒ Future { if (n == 3) throw new RuntimeException("err1") with NoStackTrace else { Await.ready(latch, 10.seconds) @@ -93,7 +93,7 @@ class FlowMapAsyncSpec extends AkkaSpec { val latch = TestLatch(1) val c = StreamTestKit.SubscriberProbe[Int]() implicit val ec = system.dispatcher - val p = FlowFrom(1 to 5).mapAsync(n ⇒ + val p = Source(1 to 5).mapAsync(n ⇒ if (n == 3) throw new RuntimeException("err2") with NoStackTrace else { Future { diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowMapAsyncUnorderedSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowMapAsyncUnorderedSpec.scala index d408511901..1595502fdb 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowMapAsyncUnorderedSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowMapAsyncUnorderedSpec.scala @@ -23,7 +23,7 @@ class FlowMapAsyncUnorderedSpec extends AkkaSpec { val c = StreamTestKit.SubscriberProbe[Int]() implicit val ec = system.dispatcher val latch = (1 to 4).map(_ -> TestLatch(1)).toMap - val p = FlowFrom(1 to 4).mapAsyncUnordered(n ⇒ Future { + val p = Source(1 to 4).mapAsyncUnordered(n ⇒ Future { Await.ready(latch(n), 5.seconds) n }).publishTo(c) @@ -44,7 +44,7 @@ class FlowMapAsyncUnorderedSpec extends AkkaSpec { val probe = TestProbe() val c = StreamTestKit.SubscriberProbe[Int]() implicit val ec = system.dispatcher - val p = FlowFrom(1 to 20).mapAsyncUnordered(n ⇒ Future { + val p = Source(1 to 20).mapAsyncUnordered(n ⇒ Future { probe.ref ! n n }).publishTo(c) @@ -70,7 +70,7 @@ class FlowMapAsyncUnorderedSpec extends AkkaSpec { val latch = TestLatch(1) val c = StreamTestKit.SubscriberProbe[Int]() implicit val ec = system.dispatcher - val p = FlowFrom(1 to 5).mapAsyncUnordered(n ⇒ Future { + val p = Source(1 to 5).mapAsyncUnordered(n ⇒ Future { if (n == 3) throw new RuntimeException("err1") with NoStackTrace else { Await.ready(latch, 10.seconds) @@ -87,7 +87,7 @@ class FlowMapAsyncUnorderedSpec extends AkkaSpec { val latch = TestLatch(1) val c = StreamTestKit.SubscriberProbe[Int]() implicit val ec = system.dispatcher - val p = FlowFrom(1 to 5).mapAsync(n ⇒ + val p = Source(1 to 5).mapAsync(n ⇒ if (n == 3) throw new RuntimeException("err2") with NoStackTrace else { Future { diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowMapSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowMapSpec.scala index 1953589947..35c4a90832 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowMapSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowMapSpec.scala @@ -26,7 +26,7 @@ class FlowMapSpec extends AkkaSpec with ScriptedTest { "not blow up with high request counts" in { val probe = StreamTestKit.SubscriberProbe[Int]() - FlowFrom(List(1).iterator). + Source(List(1).iterator). map(_ + 1).map(_ + 1).map(_ + 1).map(_ + 1).map(_ + 1). toPublisher().subscribe(probe) diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowOnCompleteSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowOnCompleteSpec.scala index 9dc933fc93..842d808c72 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowOnCompleteSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowOnCompleteSpec.scala @@ -27,7 +27,7 @@ class FlowOnCompleteSpec extends AkkaSpec with ScriptedTest { "invoke callback on normal completion" in { val onCompleteProbe = TestProbe() val p = StreamTestKit.PublisherProbe[Int]() - FlowFrom(p).withSink(OnCompleteSink(onCompleteProbe.ref ! _)).run() + Source(p).connect(OnCompleteDrain[Int](onCompleteProbe.ref ! _)).run() val proc = p.expectSubscription proc.expectRequest() proc.sendNext(42) @@ -39,7 +39,7 @@ class FlowOnCompleteSpec extends AkkaSpec with ScriptedTest { "yield the first error" in { val onCompleteProbe = TestProbe() val p = StreamTestKit.PublisherProbe[Int]() - FlowFrom(p).withSink(OnCompleteSink(onCompleteProbe.ref ! _)).run() + Source(p).connect(OnCompleteDrain[Int](onCompleteProbe.ref ! _)).run() val proc = p.expectSubscription proc.expectRequest() val ex = new RuntimeException("ex") with NoStackTrace @@ -51,7 +51,7 @@ class FlowOnCompleteSpec extends AkkaSpec with ScriptedTest { "invoke callback for an empty stream" in { val onCompleteProbe = TestProbe() val p = StreamTestKit.PublisherProbe[Int]() - FlowFrom(p).withSink(OnCompleteSink(onCompleteProbe.ref ! _)).run() + Source(p).connect(OnCompleteDrain[Int](onCompleteProbe.ref ! _)).run() val proc = p.expectSubscription proc.expectRequest() proc.sendComplete() @@ -63,14 +63,14 @@ class FlowOnCompleteSpec extends AkkaSpec with ScriptedTest { val onCompleteProbe = TestProbe() val p = StreamTestKit.PublisherProbe[Int]() import system.dispatcher // for the Future.onComplete - val foreachSink = ForeachSink[Int] { + val foreachDrain = ForeachDrain[Int] { x ⇒ onCompleteProbe.ref ! ("foreach-" + x) } - val mf = FlowFrom(p).map { x ⇒ + val mf = Source(p).map { x ⇒ onCompleteProbe.ref ! ("map-" + x) x - }.withSink(foreachSink).run() - foreachSink.future(mf) onComplete { onCompleteProbe.ref ! _ } + }.connect(foreachDrain).run() + foreachDrain.future(mf) onComplete { onCompleteProbe.ref ! _ } val proc = p.expectSubscription proc.expectRequest() proc.sendNext(42) diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowPrefixAndTailSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowPrefixAndTailSpec.scala index e75555fe76..c48d607fd0 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowPrefixAndTailSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowPrefixAndTailSpec.scala @@ -25,12 +25,12 @@ class FlowPrefixAndTailSpec extends AkkaSpec { val testException = new Exception("test") with NoStackTrace - def newFutureSink = FutureSink[(immutable.Seq[Int], FlowWithSource[Int, Int])] + def newFutureDrain = FutureDrain[(immutable.Seq[Int], Source[Int])] "work on empty input" in { - val futureSink = newFutureSink - val mf = FlowFrom(Nil).prefixAndTail(10).withSink(futureSink).run() - val fut = futureSink.future(mf) + val futureDrain = newFutureDrain + val mf = Source(Nil).prefixAndTail(10).connect(futureDrain).run() + val fut = futureDrain.future(mf) val (prefix, tailFlow) = Await.result(fut, 3.seconds) prefix should be(Nil) val tailSubscriber = SubscriberProbe[Int] @@ -39,9 +39,9 @@ class FlowPrefixAndTailSpec extends AkkaSpec { } "work on short input" in { - val futureSink = newFutureSink - val mf = FlowFrom(List(1, 2, 3)).prefixAndTail(10).withSink(futureSink).run() - val fut = futureSink.future(mf) + val futureDrain = newFutureDrain + val mf = Source(List(1, 2, 3)).prefixAndTail(10).connect(futureDrain).run() + val fut = futureDrain.future(mf) val (prefix, tailFlow) = Await.result(fut, 3.seconds) prefix should be(List(1, 2, 3)) val tailSubscriber = SubscriberProbe[Int] @@ -50,48 +50,48 @@ class FlowPrefixAndTailSpec extends AkkaSpec { } "work on longer inputs" in { - val futureSink = newFutureSink - val mf = FlowFrom((1 to 10).iterator).prefixAndTail(5).withSink(futureSink).run() - val fut = futureSink.future(mf) + val futureDrain = newFutureDrain + val mf = Source((1 to 10).iterator).prefixAndTail(5).connect(futureDrain).run() + val fut = futureDrain.future(mf) val (takes, tail) = Await.result(fut, 3.seconds) takes should be(1 to 5) - val futureSink2 = FutureSink[immutable.Seq[Int]] - val mf2 = tail.grouped(6).withSink(futureSink2).run() - val fut2 = futureSink2.future(mf2) + val futureDrain2 = FutureDrain[immutable.Seq[Int]] + val mf2 = tail.grouped(6).connect(futureDrain2).run() + val fut2 = futureDrain2.future(mf2) Await.result(fut2, 3.seconds) should be(6 to 10) } "handle zero take count" in { - val futureSink = newFutureSink - val mf = FlowFrom((1 to 10).iterator).prefixAndTail(0).withSink(futureSink).run() - val fut = futureSink.future(mf) + val futureDrain = newFutureDrain + val mf = Source((1 to 10).iterator).prefixAndTail(0).connect(futureDrain).run() + val fut = futureDrain.future(mf) val (takes, tail) = Await.result(fut, 3.seconds) takes should be(Nil) - val futureSink2 = FutureSink[immutable.Seq[Int]] - val mf2 = tail.grouped(11).withSink(futureSink2).run() - val fut2 = futureSink2.future(mf2) + val futureDrain2 = FutureDrain[immutable.Seq[Int]] + val mf2 = tail.grouped(11).connect(futureDrain2).run() + val fut2 = futureDrain2.future(mf2) Await.result(fut2, 3.seconds) should be(1 to 10) } "handle negative take count" in { - val futureSink = newFutureSink - val mf = FlowFrom((1 to 10).iterator).prefixAndTail(-1).withSink(futureSink).run() - val fut = futureSink.future(mf) + val futureDrain = newFutureDrain + val mf = Source((1 to 10).iterator).prefixAndTail(-1).connect(futureDrain).run() + val fut = futureDrain.future(mf) val (takes, tail) = Await.result(fut, 3.seconds) takes should be(Nil) - val futureSink2 = FutureSink[immutable.Seq[Int]] - val mf2 = tail.grouped(11).withSink(futureSink2).run() - val fut2 = futureSink2.future(mf2) + val futureDrain2 = FutureDrain[immutable.Seq[Int]] + val mf2 = tail.grouped(11).connect(futureDrain2).run() + val fut2 = futureDrain2.future(mf2) Await.result(fut2, 3.seconds) should be(1 to 10) } "work if size of take is equal to stream size" in { - val futureSink = newFutureSink - val mf = FlowFrom((1 to 10).iterator).prefixAndTail(10).withSink(futureSink).run() - val fut = futureSink.future(mf) + val futureDrain = newFutureDrain + val mf = Source((1 to 10).iterator).prefixAndTail(10).connect(futureDrain).run() + val fut = futureDrain.future(mf) val (takes, tail) = Await.result(fut, 3.seconds) takes should be(1 to 10) @@ -102,9 +102,9 @@ class FlowPrefixAndTailSpec extends AkkaSpec { "handle onError when no substream open" in { val publisher = StreamTestKit.PublisherProbe[Int]() - val subscriber = StreamTestKit.SubscriberProbe[(immutable.Seq[Int], FlowWithSource[Int, Int])]() + val subscriber = StreamTestKit.SubscriberProbe[(immutable.Seq[Int], Source[Int])]() - FlowFrom(publisher).prefixAndTail(3).publishTo(subscriber) + Source(publisher).prefixAndTail(3).publishTo(subscriber) val upstream = publisher.expectSubscription() val downstream = subscriber.expectSubscription() @@ -120,9 +120,9 @@ class FlowPrefixAndTailSpec extends AkkaSpec { "handle onError when substream is open" in { val publisher = StreamTestKit.PublisherProbe[Int]() - val subscriber = StreamTestKit.SubscriberProbe[(immutable.Seq[Int], FlowWithSource[Int, Int])]() + val subscriber = StreamTestKit.SubscriberProbe[(immutable.Seq[Int], Source[Int])]() - FlowFrom(publisher).prefixAndTail(1).publishTo(subscriber) + Source(publisher).prefixAndTail(1).publishTo(subscriber) val upstream = publisher.expectSubscription() val downstream = subscriber.expectSubscription() @@ -147,9 +147,9 @@ class FlowPrefixAndTailSpec extends AkkaSpec { "handle master stream cancellation" in { val publisher = StreamTestKit.PublisherProbe[Int]() - val subscriber = StreamTestKit.SubscriberProbe[(immutable.Seq[Int], FlowWithSource[Int, Int])]() + val subscriber = StreamTestKit.SubscriberProbe[(immutable.Seq[Int], Source[Int])]() - FlowFrom(publisher).prefixAndTail(3).publishTo(subscriber) + Source(publisher).prefixAndTail(3).publishTo(subscriber) val upstream = publisher.expectSubscription() val downstream = subscriber.expectSubscription() @@ -165,9 +165,9 @@ class FlowPrefixAndTailSpec extends AkkaSpec { "handle substream cancellation" in { val publisher = StreamTestKit.PublisherProbe[Int]() - val subscriber = StreamTestKit.SubscriberProbe[(immutable.Seq[Int], FlowWithSource[Int, Int])]() + val subscriber = StreamTestKit.SubscriberProbe[(immutable.Seq[Int], Source[Int])]() - FlowFrom(publisher).prefixAndTail(1).publishTo(subscriber) + Source(publisher).prefixAndTail(1).publishTo(subscriber) val upstream = publisher.expectSubscription() val downstream = subscriber.expectSubscription() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowPrependSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowPrependSpec.scala deleted file mode 100644 index 7cb25dbf2a..0000000000 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowPrependSpec.scala +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (C) 2009-2014 Typesafe Inc. - */ -package akka.stream.scaladsl2 - -import akka.stream.MaterializerSettings -import akka.stream.testkit.AkkaSpec - -class FlowPrependSpec extends AkkaSpec with River { - - val settings = MaterializerSettings(system) - implicit val materializer = FlowMaterializer(settings) - - "ProcessorFlow" should { - "prepend ProcessorFlow" in riverOf[String] { subscriber ⇒ - FlowFrom[String] - .prepend(otherFlow) - .withSource(IterableSource(elements)) - .publishTo(subscriber) - } - - "prepend FlowWithSource" in riverOf[String] { subscriber ⇒ - FlowFrom[String] - .prepend(otherFlow.withSource(IterableSource(elements))) - .publishTo(subscriber) - } - } - - "FlowWithSink" should { - "prepend ProcessorFlow" in riverOf[String] { subscriber ⇒ - FlowFrom[String] - .withSink(SubscriberSink(subscriber)) - .prepend(otherFlow) - .withSource(IterableSource(elements)) - .run() - } - - "prepend FlowWithSource" in riverOf[String] { subscriber ⇒ - FlowFrom[String] - .withSink(SubscriberSink(subscriber)) - .prepend(otherFlow.withSource(IterableSource(elements))) - .run() - } - } - -} diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowPublishToSubscriberSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowPublishToSubscriberSpec.scala index 45c4e5fe12..445272c946 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowPublishToSubscriberSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowPublishToSubscriberSpec.scala @@ -15,11 +15,11 @@ class FlowPublishToSubscriberSpec extends AkkaSpec { implicit val materializer = FlowMaterializer(settings) - "A Flow with SubscriberSink" must { + "A Flow with SubscriberDrain" must { "publish elements to the subscriber" in { val c = StreamTestKit.SubscriberProbe[Int]() - FlowFrom(List(1, 2, 3)).withSink(SubscriberSink(c)).run() + Source(List(1, 2, 3)).connect(SubscriberDrain(c)).run() val s = c.expectSubscription() s.request(3) c.expectNext(1) diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowSpec.scala index 372fb137c3..9990ece187 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowSpec.scala @@ -79,19 +79,19 @@ class FlowSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.debug.rece implicit val mat = FlowMaterializer(settings) - val identity: ProcessorFlow[Any, Any] ⇒ ProcessorFlow[Any, Any] = in ⇒ in.map(e ⇒ e) - val identity2: ProcessorFlow[Any, Any] ⇒ ProcessorFlow[Any, Any] = in ⇒ identity(in) + val identity: Flow[Any, Any] ⇒ Flow[Any, Any] = in ⇒ in.map(e ⇒ e) + val identity2: Flow[Any, Any] ⇒ Flow[Any, Any] = in ⇒ identity(in) - val toPublisher: (FlowWithSource[Any, Any], FlowMaterializer) ⇒ Publisher[Any] = + val toPublisher: (Source[Any], FlowMaterializer) ⇒ Publisher[Any] = (f, m) ⇒ f.toPublisher()(m) - def toFanoutPublisher[In, Out](initialBufferSize: Int, maximumBufferSize: Int): (FlowWithSource[In, Out], FlowMaterializer) ⇒ Publisher[Out] = + def toFanoutPublisher[In, Out](initialBufferSize: Int, maximumBufferSize: Int): (Source[Out], FlowMaterializer) ⇒ Publisher[Out] = (f, m) ⇒ f.toFanoutPublisher(initialBufferSize, maximumBufferSize)(m) - def materializeIntoSubscriberAndPublisher[In, Out](processorFlow: ProcessorFlow[In, Out]): (Subscriber[In], Publisher[Out]) = { - val source = SubscriberSource[In] - val sink = PublisherSink[Out] - val mf = processorFlow.withSource(source).withSink(sink).run() - (source.subscriber(mf), sink.publisher(mf)) + def materializeIntoSubscriberAndPublisher[In, Out](flow: Flow[In, Out]): (Subscriber[In], Publisher[Out]) = { + val tap = SubscriberTap[In] + val drain = PublisherDrain[Out] + val mf = tap.connect(flow).connect(drain).run() + (tap.subscriber(mf), drain.publisher(mf)) } "A Flow" must { @@ -168,14 +168,14 @@ class FlowSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.debug.rece } "materialize into Publisher/Subscriber" in { - val processorFlow = FlowFrom[String] - val (flowIn: Subscriber[String], flowOut: Publisher[String]) = materializeIntoSubscriberAndPublisher(processorFlow) + val flow = Flow[String] + val (flowIn: Subscriber[String], flowOut: Publisher[String]) = materializeIntoSubscriberAndPublisher(flow) val c1 = StreamTestKit.SubscriberProbe[String]() flowOut.subscribe(c1) - val source: Publisher[String] = FlowFrom(List("1", "2", "3")).toPublisher() - source.subscribe(flowIn) + val tap: Publisher[String] = Source(List("1", "2", "3")).toPublisher() + tap.subscribe(flowIn) val sub1 = c1.expectSubscription sub1.request(3) @@ -186,8 +186,8 @@ class FlowSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.debug.rece } "materialize into Publisher/Subscriber and transformation processor" in { - val processorFlow = FlowFrom[Int].map((i: Int) ⇒ i.toString) - val (flowIn: Subscriber[Int], flowOut: Publisher[String]) = materializeIntoSubscriberAndPublisher(processorFlow) + val flow = Flow[Int].map((i: Int) ⇒ i.toString) + val (flowIn: Subscriber[Int], flowOut: Publisher[String]) = materializeIntoSubscriberAndPublisher(flow) val c1 = StreamTestKit.SubscriberProbe[String]() flowOut.subscribe(c1) @@ -195,8 +195,8 @@ class FlowSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.debug.rece sub1.request(3) c1.expectNoMsg(200.millis) - val source: Publisher[Int] = FlowFrom(List(1, 2, 3)).toPublisher() - source.subscribe(flowIn) + val tap: Publisher[Int] = Source(List(1, 2, 3)).toPublisher() + tap.subscribe(flowIn) c1.expectNext("1") c1.expectNext("2") @@ -205,8 +205,8 @@ class FlowSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.debug.rece } "materialize into Publisher/Subscriber and multiple transformation processors" in { - val processorFlow = FlowFrom[Int].map(_.toString).map("elem-" + _) - val (flowIn, flowOut) = materializeIntoSubscriberAndPublisher(processorFlow) + val flow = Flow[Int].map(_.toString).map("elem-" + _) + val (flowIn, flowOut) = materializeIntoSubscriberAndPublisher(flow) val c1 = StreamTestKit.SubscriberProbe[String]() flowOut.subscribe(c1) @@ -214,8 +214,8 @@ class FlowSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.debug.rece sub1.request(3) c1.expectNoMsg(200.millis) - val source: Publisher[Int] = FlowFrom(List(1, 2, 3)).toPublisher() - source.subscribe(flowIn) + val tap: Publisher[Int] = Source(List(1, 2, 3)).toPublisher() + tap.subscribe(flowIn) c1.expectNext("elem-1") c1.expectNext("elem-2") @@ -224,11 +224,11 @@ class FlowSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.debug.rece } "subscribe Subscriber" in { - val processorFlow: ProcessorFlow[String, String] = FlowFrom[String] + val flow: Flow[String, String] = Flow[String] val c1 = StreamTestKit.SubscriberProbe[String]() - val flow: FlowWithSink[String, String] = processorFlow.withSink(SubscriberSink(c1)) - val source: Publisher[String] = FlowFrom(List("1", "2", "3")).toPublisher() - flow.withSource(PublisherSource(source)).run() + val sink: Sink[String] = flow.connect(SubscriberDrain(c1)) + val publisher: Publisher[String] = Source(List("1", "2", "3")).toPublisher() + Source(publisher).connect(sink).run() val sub1 = c1.expectSubscription sub1.request(3) @@ -239,10 +239,10 @@ class FlowSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.debug.rece } "perform transformation operation" in { - val processorFlow = FlowFrom[Int].map(i ⇒ { testActor ! i.toString; i.toString }) + val flow = Flow[Int].map(i ⇒ { testActor ! i.toString; i.toString }) - val source = FlowFrom(List(1, 2, 3)).toPublisher() - processorFlow.withSource(PublisherSource(source)).consume() + val publisher = Source(List(1, 2, 3)).toPublisher() + Source(publisher).connect(flow).consume() expectMsg("1") expectMsg("2") @@ -250,11 +250,11 @@ class FlowSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.debug.rece } "perform transformation operation and subscribe Subscriber" in { - val processorFlow = FlowFrom[Int].map(_.toString) + val flow = Flow[Int].map(_.toString) val c1 = StreamTestKit.SubscriberProbe[String]() - val flow: FlowWithSink[Int, String] = processorFlow.withSink(SubscriberSink(c1)) - val source: Publisher[Int] = FlowFrom(List(1, 2, 3)).toPublisher() - flow.withSource(PublisherSource(source)).run() + val sink: Sink[Int] = flow.connect(SubscriberDrain(c1)) + val publisher: Publisher[Int] = Source(List(1, 2, 3)).toPublisher() + Source(publisher).connect(sink).run() val sub1 = c1.expectSubscription sub1.request(3) @@ -265,7 +265,7 @@ class FlowSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.debug.rece } "be materializable several times with fanout publisher" in { - val flow = FlowFrom(List(1, 2, 3)).map(_.toString) + val flow = Source(List(1, 2, 3)).map(_.toString) val p1 = flow.toFanoutPublisher(2, 2) val p2 = flow.toFanoutPublisher(2, 2) val s1 = StreamTestKit.SubscriberProbe[String]() @@ -298,14 +298,14 @@ class FlowSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.debug.rece } "be covariant" in { - val f1: FlowWithSource[Fruit, Fruit] = FlowFrom[Fruit](() ⇒ Some(new Apple)) - val p1: Publisher[Fruit] = FlowFrom[Fruit](() ⇒ Some(new Apple)).toPublisher() - val f2: FlowWithSource[Fruit, FlowWithSource[Fruit, Fruit]] = FlowFrom[Fruit](() ⇒ Some(new Apple)).splitWhen(_ ⇒ true) - val f3: FlowWithSource[Fruit, (Boolean, FlowWithSource[Fruit, Fruit])] = FlowFrom[Fruit](() ⇒ Some(new Apple)).groupBy(_ ⇒ true) - val f4: FlowWithSource[Fruit, (immutable.Seq[Fruit], FlowWithSource[Fruit, Fruit])] = FlowFrom[Fruit](() ⇒ Some(new Apple)).prefixAndTail(1) - val d1: ProcessorFlow[String, FlowWithSource[Fruit, Fruit]] = FlowFrom[String].map(_ ⇒ new Apple).splitWhen(_ ⇒ true) - val d2: ProcessorFlow[String, (Boolean, FlowWithSource[Fruit, Fruit])] = FlowFrom[String].map(_ ⇒ new Apple).groupBy(_ ⇒ true) - val d3: ProcessorFlow[String, (immutable.Seq[Apple], FlowWithSource[Fruit, Fruit])] = FlowFrom[String].map(_ ⇒ new Apple).prefixAndTail(1) + val f1: Source[Fruit] = Source[Fruit](() ⇒ Some(new Apple)) + val p1: Publisher[Fruit] = Source[Fruit](() ⇒ Some(new Apple)).toPublisher() + val f2: Source[Source[Fruit]] = Source[Fruit](() ⇒ Some(new Apple)).splitWhen(_ ⇒ true) + val f3: Source[(Boolean, Source[Fruit])] = Source[Fruit](() ⇒ Some(new Apple)).groupBy(_ ⇒ true) + val f4: Source[(immutable.Seq[Fruit], Source[Fruit])] = Source[Fruit](() ⇒ Some(new Apple)).prefixAndTail(1) + val d1: Flow[String, Source[Fruit]] = Flow[String].map(_ ⇒ new Apple).splitWhen(_ ⇒ true) + val d2: Flow[String, (Boolean, Source[Fruit])] = Flow[String].map(_ ⇒ new Apple).groupBy(_ ⇒ true) + val d3: Flow[String, (immutable.Seq[Apple], Source[Fruit])] = Flow[String].map(_ ⇒ new Apple).prefixAndTail(1) } } diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowSplitWhenSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowSplitWhenSpec.scala index 009815601d..5e279d7aef 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowSplitWhenSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowSplitWhenSpec.scala @@ -31,19 +31,19 @@ class FlowSplitWhenSpec extends AkkaSpec { } class SubstreamsSupport(splitWhen: Int = 3, elementCount: Int = 6) { - val source = FlowFrom((1 to elementCount).iterator) - val groupStream = source.splitWhen(_ == splitWhen).toPublisher() - val masterSubscriber = StreamTestKit.SubscriberProbe[FlowWithSource[Int, Int]]() + val tap = Source((1 to elementCount).iterator) + val groupStream = tap.splitWhen(_ == splitWhen).toPublisher() + val masterSubscriber = StreamTestKit.SubscriberProbe[Source[Int]]() groupStream.subscribe(masterSubscriber) val masterSubscription = masterSubscriber.expectSubscription() - def getSubFlow(): FlowWithSource[Int, Int] = { + def getSubFlow(): Source[Int] = { masterSubscription.request(1) expectSubPublisher() } - def expectSubPublisher(): FlowWithSource[Int, Int] = { + def expectSubPublisher(): Source[Int] = { val substream = masterSubscriber.expectNext() substream } diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowTakeSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowTakeSpec.scala index fd4ab09f7d..4fe953c96c 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowTakeSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowTakeSpec.scala @@ -34,7 +34,7 @@ class FlowTakeSpec extends AkkaSpec with ScriptedTest { "not take anything for negative n" in { val probe = StreamTestKit.SubscriberProbe[Int]() - FlowFrom(List(1, 2, 3)).take(-1).publishTo(probe) + Source(List(1, 2, 3)).take(-1).publishTo(probe) probe.expectSubscription().request(10) probe.expectComplete() } diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowTakeWithinSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowTakeWithinSpec.scala index 8c09df30b2..198ac1c090 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowTakeWithinSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowTakeWithinSpec.scala @@ -18,7 +18,7 @@ class FlowTakeWithinSpec extends AkkaSpec { val input = Iterator.from(1) val p = StreamTestKit.PublisherProbe[Int]() val c = StreamTestKit.SubscriberProbe[Int]() - FlowFrom(p).takeWithin(1.second).publishTo(c) + Source(p).takeWithin(1.second).publishTo(c) val pSub = p.expectSubscription() val cSub = c.expectSubscription() cSub.request(100) @@ -38,7 +38,7 @@ class FlowTakeWithinSpec extends AkkaSpec { "deliver bufferd elements onComplete before the timeout" in { val c = StreamTestKit.SubscriberProbe[Int]() - FlowFrom(1 to 3).takeWithin(1.second).publishTo(c) + Source(1 to 3).takeWithin(1.second).publishTo(c) val cSub = c.expectSubscription() c.expectNoMsg(200.millis) cSub.request(100) diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowThunkSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowThunkSpec.scala index daca41400f..1e048d32c1 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowThunkSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowThunkSpec.scala @@ -18,7 +18,7 @@ class FlowThunkSpec extends AkkaSpec { "produce elements" in { val iter = List(1, 2, 3).iterator - val p = FlowFrom(() ⇒ if (iter.hasNext) Some(iter.next()) else None).map(_ + 10).toPublisher() + val p = Source(() ⇒ if (iter.hasNext) Some(iter.next()) else None).map(_ + 10).toPublisher() val c = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c) val sub = c.expectSubscription() @@ -32,7 +32,7 @@ class FlowThunkSpec extends AkkaSpec { } "complete empty" in { - val p = FlowFrom(() ⇒ None).toPublisher() + val p = Source(() ⇒ None).toPublisher() val c = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c) val sub = c.expectSubscription() @@ -44,7 +44,7 @@ class FlowThunkSpec extends AkkaSpec { "allow cancel before receiving all elements" in { val count = 100000 val iter = (1 to count).iterator - val p = FlowFrom(() ⇒ if (iter.hasNext) Some(iter.next()) else None).toPublisher() + val p = Source(() ⇒ if (iter.hasNext) Some(iter.next()) else None).toPublisher() val c = StreamTestKit.SubscriberProbe[Int]() p.subscribe(c) val sub = c.expectSubscription() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowTimerTransformerSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowTimerTransformerSpec.scala index 7302698df9..71b786a859 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowTimerTransformerSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowTimerTransformerSpec.scala @@ -16,7 +16,7 @@ class FlowTimerTransformerSpec extends AkkaSpec { "A Flow with TimerTransformer operations" must { "produce scheduled ticks as expected" in { val p = StreamTestKit.PublisherProbe[Int]() - val p2 = FlowFrom(p). + val p2 = Source(p). timerTransform("timer", () ⇒ new TimerTransformer[Int, Int] { schedulePeriodically("tick", 100.millis) var tickCount = 0 @@ -41,7 +41,7 @@ class FlowTimerTransformerSpec extends AkkaSpec { "schedule ticks when last transformation step (consume)" in { val p = StreamTestKit.PublisherProbe[Int]() - val p2 = FlowFrom(p). + val p2 = Source(p). timerTransform("timer", () ⇒ new TimerTransformer[Int, Int] { schedulePeriodically("tick", 100.millis) var tickCount = 0 @@ -65,7 +65,7 @@ class FlowTimerTransformerSpec extends AkkaSpec { "propagate error if onTimer throws an exception" in { val exception = new Exception("Expected exception to the rule") with NoStackTrace val p = StreamTestKit.PublisherProbe[Int]() - val p2 = FlowFrom(p). + val p2 = Source(p). timerTransform("timer", () ⇒ new TimerTransformer[Int, Int] { scheduleOnce("tick", 100.millis) diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowToFutureSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowToFutureSpec.scala index b8cfe4034f..768a5b01bc 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowToFutureSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowToFutureSpec.scala @@ -23,8 +23,8 @@ class FlowToFutureSpec extends AkkaSpec with ScriptedTest { "yield the first value" in { val p = StreamTestKit.PublisherProbe[Int]() - val f = FutureSink[Int] - val m = FlowFrom(p).withSink(f).run() + val f = FutureDrain[Int] + val m = Source(p).connect(f).run() val proc = p.expectSubscription proc.expectRequest() proc.sendNext(42) @@ -34,9 +34,9 @@ class FlowToFutureSpec extends AkkaSpec with ScriptedTest { "yield the first value when actively constructing" in { val p = StreamTestKit.PublisherProbe[Int]() - val f = FutureSink[Int] - val s = SubscriberSource[Int] - val m = FlowFrom[Int].withSource(s).withSink(f).run() + val f = FutureDrain[Int] + val s = SubscriberTap[Int] + val m = s.connect(f).run() p.subscribe(s.subscriber(m)) val proc = p.expectSubscription proc.expectRequest() @@ -47,8 +47,8 @@ class FlowToFutureSpec extends AkkaSpec with ScriptedTest { "yield the first error" in { val p = StreamTestKit.PublisherProbe[Int]() - val f = FutureSink[Int] - val m = FlowFrom(p).withSink(f).run() + val f = FutureDrain[Int] + val m = Source(p).connect(f).run() val proc = p.expectSubscription proc.expectRequest() val ex = new RuntimeException("ex") @@ -60,8 +60,8 @@ class FlowToFutureSpec extends AkkaSpec with ScriptedTest { "yield NoSuchElementExcption for empty stream" in { val p = StreamTestKit.PublisherProbe[Int]() - val f = FutureSink[Int] - val m = FlowFrom(p).withSink(f).run() + val f = FutureDrain[Int] + val m = Source(p).connect(f).run() val proc = p.expectSubscription proc.expectRequest() proc.sendComplete() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowTransformRecoverSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowTransformRecoverSpec.scala index d1c67dd1f1..853d2c0112 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowTransformRecoverSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowTransformRecoverSpec.scala @@ -40,8 +40,8 @@ class FlowTransformRecoverSpec extends AkkaSpec { "A Flow with transformRecover operations" must { "produce one-to-one transformation as expected" in { - val p = FlowFrom(List(1, 2, 3).iterator).toPublisher() - val p2 = FlowFrom(p). + val p = Source(List(1, 2, 3).iterator).toPublisher() + val p2 = Source(p). transform("transform", () ⇒ new Transformer[Int, Int] { var tot = 0 override def onNext(elem: Int) = { @@ -68,8 +68,8 @@ class FlowTransformRecoverSpec extends AkkaSpec { } "produce one-to-several transformation as expected" in { - val p = FlowFrom(List(1, 2, 3).iterator).toPublisher() - val p2 = FlowFrom(p). + val p = Source(List(1, 2, 3).iterator).toPublisher() + val p2 = Source(p). transform("transform", () ⇒ new Transformer[Int, Int] { var tot = 0 override def onNext(elem: Int) = { @@ -99,8 +99,8 @@ class FlowTransformRecoverSpec extends AkkaSpec { } "produce dropping transformation as expected" in { - val p = FlowFrom(List(1, 2, 3, 4).iterator).toPublisher() - val p2 = FlowFrom(p). + val p = Source(List(1, 2, 3, 4).iterator).toPublisher() + val p2 = Source(p). transform("transform", () ⇒ new Transformer[Int, Int] { var tot = 0 override def onNext(elem: Int) = { @@ -127,8 +127,8 @@ class FlowTransformRecoverSpec extends AkkaSpec { } "produce multi-step transformation as expected" in { - val p = FlowFrom(List("a", "bc", "def").iterator).toPublisher() - val p2 = FlowFrom(p). + val p = Source(List("a", "bc", "def").iterator).toPublisher() + val p2 = Source(p). transform("transform", () ⇒ new TryRecoveryTransformer[String, Int] { var concat = "" override def onNext(element: Try[String]) = { @@ -170,8 +170,8 @@ class FlowTransformRecoverSpec extends AkkaSpec { } "invoke onComplete when done" in { - val p = FlowFrom(List("a").iterator).toPublisher() - val p2 = FlowFrom(p). + val p = Source(List("a").iterator).toPublisher() + val p2 = Source(p). transform("transform", () ⇒ new TryRecoveryTransformer[String, String] { var s = "" override def onNext(element: Try[String]) = { @@ -191,7 +191,7 @@ class FlowTransformRecoverSpec extends AkkaSpec { "allow cancellation using isComplete" in { val p = StreamTestKit.PublisherProbe[Int]() - val p2 = FlowFrom(p). + val p2 = Source(p). transform("transform", () ⇒ new TryRecoveryTransformer[Int, Int] { var s = "" override def onNext(element: Try[Int]) = { @@ -215,7 +215,7 @@ class FlowTransformRecoverSpec extends AkkaSpec { "call onComplete after isComplete signaled completion" in { val p = StreamTestKit.PublisherProbe[Int]() - val p2 = FlowFrom(p). + val p2 = Source(p). transform("transform", () ⇒ new TryRecoveryTransformer[Int, Int] { var s = "" override def onNext(element: Try[Int]) = { @@ -240,8 +240,8 @@ class FlowTransformRecoverSpec extends AkkaSpec { } "report error when exception is thrown" in { - val p = FlowFrom(List(1, 2, 3).iterator).toPublisher() - val p2 = FlowFrom(p). + val p = Source(List(1, 2, 3).iterator).toPublisher() + val p2 = Source(p). transform("transform", () ⇒ new Transformer[Int, Int] { override def onNext(elem: Int) = { if (elem == 2) throw new IllegalArgumentException("two not allowed") @@ -266,7 +266,7 @@ class FlowTransformRecoverSpec extends AkkaSpec { "report error after emitted elements" in { EventFilter[IllegalArgumentException]("two not allowed") intercept { - val p2 = FlowFrom(List(1, 2, 3).iterator). + val p2 = Source(List(1, 2, 3).iterator). mapConcat { elem ⇒ if (elem == 2) throw new IllegalArgumentException("two not allowed") else (1 to 5).map(elem * 100 + _) @@ -315,7 +315,7 @@ class FlowTransformRecoverSpec extends AkkaSpec { "transform errors in sequence with normal messages" in { val p = StreamTestKit.PublisherProbe[Int]() - val p2 = FlowFrom(p). + val p2 = Source(p). transform("transform", () ⇒ new Transformer[Int, String] { var s = "" override def onNext(element: Int) = { @@ -348,7 +348,7 @@ class FlowTransformRecoverSpec extends AkkaSpec { "forward errors when received and thrown" in { val p = StreamTestKit.PublisherProbe[Int]() - val p2 = FlowFrom(p). + val p2 = Source(p). transform("transform", () ⇒ new Transformer[Int, Int] { override def onNext(in: Int) = List(in) override def onError(e: Throwable) = throw e @@ -366,8 +366,8 @@ class FlowTransformRecoverSpec extends AkkaSpec { } "support cancel as expected" in { - val p = FlowFrom(List(1, 2, 3).iterator).toPublisher() - val p2 = FlowFrom(p). + val p = Source(List(1, 2, 3).iterator).toPublisher() + val p2 = Source(p). transform("transform", () ⇒ new Transformer[Int, Int] { override def onNext(elem: Int) = List(elem, elem) override def onError(e: Throwable) = List(-1) diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowTransformSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowTransformSpec.scala index ef9ecab700..293839d6cc 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowTransformSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/FlowTransformSpec.scala @@ -23,8 +23,8 @@ class FlowTransformSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.d "A Flow with transform operations" must { "produce one-to-one transformation as expected" in { - val p = FlowFrom(List(1, 2, 3)).toPublisher() - val p2 = FlowFrom(p). + val p = Source(List(1, 2, 3)).toPublisher() + val p2 = Source(p). transform("transform", () ⇒ new Transformer[Int, Int] { var tot = 0 override def onNext(elem: Int) = { @@ -46,8 +46,8 @@ class FlowTransformSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.d } "produce one-to-several transformation as expected" in { - val p = FlowFrom(List(1, 2, 3)).toPublisher() - val p2 = FlowFrom(p). + val p = Source(List(1, 2, 3)).toPublisher() + val p2 = Source(p). transform("transform", () ⇒ new Transformer[Int, Int] { var tot = 0 override def onNext(elem: Int) = { @@ -72,8 +72,8 @@ class FlowTransformSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.d } "produce dropping transformation as expected" in { - val p = FlowFrom(List(1, 2, 3, 4)).toPublisher() - val p2 = FlowFrom(p). + val p = Source(List(1, 2, 3, 4)).toPublisher() + val p2 = Source(p). transform("transform", () ⇒ new Transformer[Int, Int] { var tot = 0 override def onNext(elem: Int) = { @@ -99,8 +99,8 @@ class FlowTransformSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.d } "produce multi-step transformation as expected" in { - val p = FlowFrom(List("a", "bc", "def")).toPublisher() - val p2 = FlowFrom(p). + val p = Source(List("a", "bc", "def")).toPublisher() + val p2 = Source(p). transform("transform", () ⇒ new Transformer[String, Int] { var concat = "" override def onNext(elem: String) = { @@ -138,8 +138,8 @@ class FlowTransformSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.d } "invoke onComplete when done" in { - val p = FlowFrom(List("a")).toPublisher() - val p2 = FlowFrom(p). + val p = Source(List("a")).toPublisher() + val p2 = Source(p). transform("transform", () ⇒ new Transformer[String, String] { var s = "" override def onNext(element: String) = { @@ -159,8 +159,8 @@ class FlowTransformSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.d "invoke cleanup when done" in { val cleanupProbe = TestProbe() - val p = FlowFrom(List("a")).toPublisher() - val p2 = FlowFrom(p). + val p = Source(List("a")).toPublisher() + val p2 = Source(p). transform("transform", () ⇒ new Transformer[String, String] { var s = "" override def onNext(element: String) = { @@ -182,8 +182,8 @@ class FlowTransformSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.d "invoke cleanup when done consume" in { val cleanupProbe = TestProbe() - val p = FlowFrom(List("a")).toPublisher() - FlowFrom(p). + val p = Source(List("a")).toPublisher() + Source(p). transform("transform", () ⇒ new Transformer[String, String] { var s = "x" override def onNext(element: String) = { @@ -192,14 +192,14 @@ class FlowTransformSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.d } override def cleanup() = cleanupProbe.ref ! s }). - withSink(BlackholeSink).run() + connect(BlackholeDrain).run() cleanupProbe.expectMsg("a") } "invoke cleanup when done after error" in { val cleanupProbe = TestProbe() - val p = FlowFrom(List("a", "b", "c")).toPublisher() - val p2 = FlowFrom(p). + val p = Source(List("a", "b", "c")).toPublisher() + val p2 = Source(p). transform("transform", () ⇒ new Transformer[String, String] { var s = "" override def onNext(in: String) = { @@ -227,7 +227,7 @@ class FlowTransformSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.d "allow cancellation using isComplete" in { val p = StreamTestKit.PublisherProbe[Int]() - val p2 = FlowFrom(p). + val p2 = Source(p). transform("transform", () ⇒ new Transformer[Int, Int] { var s = "" override def onNext(element: Int) = { @@ -252,7 +252,7 @@ class FlowTransformSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.d "call onComplete after isComplete signaled completion" in { val cleanupProbe = TestProbe() val p = StreamTestKit.PublisherProbe[Int]() - val p2 = FlowFrom(p). + val p2 = Source(p). transform("transform", () ⇒ new Transformer[Int, Int] { var s = "" override def onNext(element: Int) = { @@ -279,8 +279,8 @@ class FlowTransformSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.d } "report error when exception is thrown" in { - val p = FlowFrom(List(1, 2, 3)).toPublisher() - val p2 = FlowFrom(p). + val p = Source(List(1, 2, 3)).toPublisher() + val p2 = Source(p). transform("transform", () ⇒ new Transformer[Int, Int] { override def onNext(elem: Int) = { if (elem == 2) { @@ -304,8 +304,8 @@ class FlowTransformSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.d } "support cancel as expected" in { - val p = FlowFrom(List(1, 2, 3)).toPublisher() - val p2 = FlowFrom(p). + val p = Source(List(1, 2, 3)).toPublisher() + val p2 = Source(p). transform("transform", () ⇒ new Transformer[Int, Int] { override def onNext(elem: Int) = List(elem, elem) }). @@ -323,8 +323,8 @@ class FlowTransformSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.d } "support producing elements from empty inputs" in { - val p = FlowFrom(List.empty[Int]).toPublisher() - val p2 = FlowFrom(p). + val p = Source(List.empty[Int]).toPublisher() + val p2 = Source(p). transform("transform", () ⇒ new Transformer[Int, Int] { override def onNext(elem: Int) = Nil override def onTermination(e: Option[Throwable]) = List(1, 2, 3) @@ -343,7 +343,7 @@ class FlowTransformSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.d "support converting onComplete into onError" in { val subscriber = StreamTestKit.SubscriberProbe[Int]() - FlowFrom(List(5, 1, 2, 3)).transform("transform", () ⇒ new Transformer[Int, Int] { + Source(List(5, 1, 2, 3)).transform("transform", () ⇒ new Transformer[Int, Int] { var expectedNumberOfElements: Option[Int] = None var count = 0 override def onNext(elem: Int) = @@ -375,7 +375,7 @@ class FlowTransformSpec extends AkkaSpec(ConfigFactory.parseString("akka.actor.d } "be safe to reuse" in { - val flow = FlowFrom(1 to 3).transform("transform", () ⇒ + val flow = Source(1 to 3).transform("transform", () ⇒ new Transformer[Int, Int] { var count = 0 diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphBroadcastSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphBroadcastSpec.scala index 94b4e66af0..b987793436 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphBroadcastSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphBroadcastSpec.scala @@ -22,9 +22,9 @@ class GraphBroadcastSpec extends AkkaSpec { FlowGraph { implicit b ⇒ val bcast = Broadcast[Int]("broadcast") - FlowFrom(List(1, 2, 3)) ~> bcast - bcast ~> FlowFrom[Int].buffer(16, OverflowStrategy.backpressure) ~> SubscriberSink(c1) - bcast ~> FlowFrom[Int].buffer(16, OverflowStrategy.backpressure) ~> SubscriberSink(c2) + Source(List(1, 2, 3)) ~> bcast + bcast ~> Flow[Int].buffer(16, OverflowStrategy.backpressure) ~> SubscriberDrain(c1) + bcast ~> Flow[Int].buffer(16, OverflowStrategy.backpressure) ~> SubscriberDrain(c2) }.run() val sub1 = c1.expectSubscription() @@ -46,27 +46,27 @@ class GraphBroadcastSpec extends AkkaSpec { } "work with n-way broadcast" in { - val f1 = FutureSink[Seq[Int]] - val f2 = FutureSink[Seq[Int]] - val f3 = FutureSink[Seq[Int]] - val f4 = FutureSink[Seq[Int]] - val f5 = FutureSink[Seq[Int]] + val f1 = FutureDrain[Seq[Int]] + val f2 = FutureDrain[Seq[Int]] + val f3 = FutureDrain[Seq[Int]] + val f4 = FutureDrain[Seq[Int]] + val f5 = FutureDrain[Seq[Int]] val g = FlowGraph { implicit b ⇒ val bcast = Broadcast[Int]("broadcast") - FlowFrom(List(1, 2, 3)) ~> bcast - bcast ~> FlowFrom[Int].grouped(5) ~> f1 - bcast ~> FlowFrom[Int].grouped(5) ~> f2 - bcast ~> FlowFrom[Int].grouped(5) ~> f3 - bcast ~> FlowFrom[Int].grouped(5) ~> f4 - bcast ~> FlowFrom[Int].grouped(5) ~> f5 + Source(List(1, 2, 3)) ~> bcast + bcast ~> Flow[Int].grouped(5) ~> f1 + bcast ~> Flow[Int].grouped(5) ~> f2 + bcast ~> Flow[Int].grouped(5) ~> f3 + bcast ~> Flow[Int].grouped(5) ~> f4 + bcast ~> Flow[Int].grouped(5) ~> f5 }.run() - Await.result(g.getSinkFor(f1), 3.seconds) should be(List(1, 2, 3)) - Await.result(g.getSinkFor(f2), 3.seconds) should be(List(1, 2, 3)) - Await.result(g.getSinkFor(f3), 3.seconds) should be(List(1, 2, 3)) - Await.result(g.getSinkFor(f4), 3.seconds) should be(List(1, 2, 3)) - Await.result(g.getSinkFor(f5), 3.seconds) should be(List(1, 2, 3)) + Await.result(g.getDrainFor(f1), 3.seconds) should be(List(1, 2, 3)) + Await.result(g.getDrainFor(f2), 3.seconds) should be(List(1, 2, 3)) + Await.result(g.getDrainFor(f3), 3.seconds) should be(List(1, 2, 3)) + Await.result(g.getDrainFor(f4), 3.seconds) should be(List(1, 2, 3)) + Await.result(g.getDrainFor(f5), 3.seconds) should be(List(1, 2, 3)) } "produce to other even though downstream cancels" in { @@ -75,9 +75,9 @@ class GraphBroadcastSpec extends AkkaSpec { FlowGraph { implicit b ⇒ val bcast = Broadcast[Int]("broadcast") - FlowFrom(List(1, 2, 3)) ~> bcast - bcast ~> FlowFrom[Int] ~> SubscriberSink(c1) - bcast ~> FlowFrom[Int] ~> SubscriberSink(c2) + Source(List(1, 2, 3)) ~> bcast + bcast ~> Flow[Int] ~> SubscriberDrain(c1) + bcast ~> Flow[Int] ~> SubscriberDrain(c2) }.run() val sub1 = c1.expectSubscription() @@ -96,9 +96,9 @@ class GraphBroadcastSpec extends AkkaSpec { FlowGraph { implicit b ⇒ val bcast = Broadcast[Int]("broadcast") - FlowFrom(List(1, 2, 3)) ~> bcast - bcast ~> FlowFrom[Int] ~> SubscriberSink(c1) - bcast ~> FlowFrom[Int] ~> SubscriberSink(c2) + Source(List(1, 2, 3)) ~> bcast + bcast ~> Flow[Int] ~> SubscriberDrain(c1) + bcast ~> Flow[Int] ~> SubscriberDrain(c2) }.run() val sub1 = c1.expectSubscription() @@ -118,9 +118,9 @@ class GraphBroadcastSpec extends AkkaSpec { FlowGraph { implicit b ⇒ val bcast = Broadcast[Int]("broadcast") - FlowFrom(p1.getPublisher) ~> bcast - bcast ~> FlowFrom[Int] ~> SubscriberSink(c1) - bcast ~> FlowFrom[Int] ~> SubscriberSink(c2) + Source(p1.getPublisher) ~> bcast + bcast ~> Flow[Int] ~> SubscriberDrain(c1) + bcast ~> Flow[Int] ~> SubscriberDrain(c2) }.run() val bsub = p1.expectSubscription() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphConcatSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphConcatSpec.scala index 4198941e1a..3a891c1a40 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphConcatSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphConcatSpec.scala @@ -26,13 +26,13 @@ class GraphConcatSpec extends TwoStreamsSetup { val concat1 = Concat[Int]("concat1") val concat2 = Concat[Int]("concat2") - FlowFrom(List.empty[Int].iterator) ~> concat1.first - FlowFrom((1 to 4).iterator) ~> concat1.second + Source(List.empty[Int].iterator) ~> concat1.first + Source((1 to 4).iterator) ~> concat1.second concat1.out ~> concat2.first - FlowFrom((5 to 10).iterator) ~> concat2.second + Source((5 to 10).iterator) ~> concat2.second - concat2.out ~> SubscriberSink(probe) + concat2.out ~> SubscriberDrain(probe) }.run() val subscription = probe.expectSubscription() @@ -109,9 +109,9 @@ class GraphConcatSpec extends TwoStreamsSetup { FlowGraph { implicit b ⇒ val concat = Concat[Int] - FlowFrom(List(1, 2, 3)) ~> concat.first - FlowFrom(promise.future) ~> concat.second - concat.out ~> SubscriberSink(subscriber) + Source(List(1, 2, 3)) ~> concat.first + Source(promise.future) ~> concat.second + concat.out ~> SubscriberDrain(subscriber) }.run() val subscription = subscriber.expectSubscription() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphMergeSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphMergeSpec.scala index d6e0621972..565015a5f8 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphMergeSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphMergeSpec.scala @@ -19,9 +19,9 @@ class GraphMergeSpec extends TwoStreamsSetup { "work in the happy case" in { // Different input sizes (4 and 6) - val source1 = FlowFrom((0 to 3).iterator) - val source2 = FlowFrom((4 to 9).iterator) - val source3 = FlowFrom(List.empty[Int].iterator) + val tap1 = Source((0 to 3).iterator) + val tap2 = Source((4 to 9).iterator) + val tap3 = Source(List.empty[Int].iterator) val probe = StreamTestKit.SubscriberProbe[Int]() FlowGraph { implicit b ⇒ @@ -29,9 +29,9 @@ class GraphMergeSpec extends TwoStreamsSetup { val m2 = Merge[Int]("m2") val m3 = Merge[Int]("m3") - source1 ~> m1 ~> FlowFrom[Int].map(_ * 2) ~> m2 ~> FlowFrom[Int].map(_ / 2).map(_ + 1) ~> SubscriberSink(probe) - source2 ~> m1 - source3 ~> m2 + tap1 ~> m1 ~> Flow[Int].map(_ * 2) ~> m2 ~> Flow[Int].map(_ / 2).map(_ + 1) ~> SubscriberDrain(probe) + tap2 ~> m1 + tap3 ~> m2 }.run() @@ -48,24 +48,24 @@ class GraphMergeSpec extends TwoStreamsSetup { } "work with n-way merge" in { - val source1 = FlowFrom(List(1)) - val source2 = FlowFrom(List(2)) - val source3 = FlowFrom(List(3)) - val source4 = FlowFrom(List(4)) - val source5 = FlowFrom(List(5)) - val source6 = FlowFrom(List.empty[Int]) + val tap1 = Source(List(1)) + val tap2 = Source(List(2)) + val tap3 = Source(List(3)) + val tap4 = Source(List(4)) + val tap5 = Source(List(5)) + val tap6 = Source(List.empty[Int]) val probe = StreamTestKit.SubscriberProbe[Int]() FlowGraph { implicit b ⇒ val merge = Merge[Int]("merge") - source1 ~> merge ~> FlowFrom[Int] ~> SubscriberSink(probe) - source2 ~> merge - source3 ~> merge - source4 ~> merge - source5 ~> merge - source6 ~> merge + tap1 ~> merge ~> Flow[Int] ~> SubscriberDrain(probe) + tap2 ~> merge + tap3 ~> merge + tap4 ~> merge + tap5 ~> merge + tap6 ~> merge }.run() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphOpsIntegrationSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphOpsIntegrationSpec.scala index 7c27cfc2c3..712ddbd60f 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphOpsIntegrationSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphOpsIntegrationSpec.scala @@ -19,26 +19,26 @@ class GraphOpsIntegrationSpec extends AkkaSpec { "FlowGraphs" must { "support broadcast - merge layouts" in { - val resultFuture = FutureSink[Seq[Int]] + val resultFuture = FutureDrain[Seq[Int]] val g = FlowGraph { implicit b ⇒ val bcast = Broadcast[Int]("broadcast") val merge = Merge[Int]("merge") - FlowFrom(List(1, 2, 3)) ~> bcast + Source(List(1, 2, 3)) ~> bcast bcast ~> merge - bcast ~> FlowFrom[Int].map(_ + 3) ~> merge - merge ~> FlowFrom[Int].grouped(10) ~> resultFuture + bcast ~> Flow[Int].map(_ + 3) ~> merge + merge ~> Flow[Int].grouped(10) ~> resultFuture }.run() - Await.result(g.getSinkFor(resultFuture), 3.seconds).sorted should be(List(1, 2, 3, 4, 5, 6)) + Await.result(g.getDrainFor(resultFuture), 3.seconds).sorted should be(List(1, 2, 3, 4, 5, 6)) } "support wikipedia Topological_sorting 2" in { // see https://en.wikipedia.org/wiki/Topological_sorting#mediaviewer/File:Directed_acyclic_graph.png - val resultFuture2 = FutureSink[Seq[Int]] - val resultFuture9 = FutureSink[Seq[Int]] - val resultFuture10 = FutureSink[Seq[Int]] + val resultFuture2 = FutureDrain[Seq[Int]] + val resultFuture9 = FutureDrain[Seq[Int]] + val resultFuture10 = FutureDrain[Seq[Int]] val g = FlowGraph { implicit b ⇒ val b3 = Broadcast[Int]("b3") @@ -48,9 +48,9 @@ class GraphOpsIntegrationSpec extends AkkaSpec { val m9 = Merge[Int]("m9") val m10 = Merge[Int]("m10") val m11 = Merge[Int]("m11") - val in3 = IterableSource(List(3)) - val in5 = IterableSource(List(5)) - val in7 = IterableSource(List(7)) + val in3 = IterableTap(List(3)) + val in5 = IterableTap(List(5)) + val in7 = IterableTap(List(7)) // First layer in7 ~> b7 @@ -65,24 +65,39 @@ class GraphOpsIntegrationSpec extends AkkaSpec { // Second layer m11 ~> b11 - b11 ~> FlowFrom[Int].grouped(1000) ~> resultFuture2 // Vertex 2 is omitted since it has only one in and out + b11 ~> Flow[Int].grouped(1000) ~> resultFuture2 // Vertex 2 is omitted since it has only one in and out b11 ~> m9 b11 ~> m10 m8 ~> m9 // Third layer - m9 ~> FlowFrom[Int].grouped(1000) ~> resultFuture9 - m10 ~> FlowFrom[Int].grouped(1000) ~> resultFuture10 + m9 ~> Flow[Int].grouped(1000) ~> resultFuture9 + m10 ~> Flow[Int].grouped(1000) ~> resultFuture10 }.run() - Await.result(g.getSinkFor(resultFuture2), 3.seconds).sorted should be(List(5, 7)) - Await.result(g.getSinkFor(resultFuture9), 3.seconds).sorted should be(List(3, 5, 7, 7)) - Await.result(g.getSinkFor(resultFuture10), 3.seconds).sorted should be(List(3, 5, 7)) + Await.result(g.getDrainFor(resultFuture2), 3.seconds).sorted should be(List(5, 7)) + Await.result(g.getDrainFor(resultFuture9), 3.seconds).sorted should be(List(3, 5, 7, 7)) + Await.result(g.getDrainFor(resultFuture10), 3.seconds).sorted should be(List(3, 5, 7)) } + "allow adding of flows to sources and sinks to flows" in { + val resultFuture = FutureDrain[Seq[Int]] + + val g = FlowGraph { implicit b ⇒ + val bcast = Broadcast[Int]("broadcast") + val merge = Merge[Int]("merge") + + Source(List(1, 2, 3)) ~> Flow[Int].map(_ * 2) ~> bcast + bcast ~> merge + bcast ~> Flow[Int].map(_ + 3) ~> merge + merge ~> Flow[Int].grouped(10).connect(resultFuture) + }.run() + + Await.result(g.getDrainFor(resultFuture), 3.seconds) should contain theSameElementsAs (Seq(2, 4, 6, 5, 7, 9)) + } } } diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphUnzipSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphUnzipSpec.scala index 1ec023eb74..5b732d8ada 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphUnzipSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphUnzipSpec.scala @@ -22,9 +22,9 @@ class GraphUnzipSpec extends AkkaSpec { FlowGraph { implicit b ⇒ val unzip = Unzip[Int, String]("unzip") - FlowFrom(List(1 -> "a", 2 -> "b", 3 -> "c")) ~> unzip.in - unzip.right ~> FlowFrom[String].buffer(16, OverflowStrategy.backpressure) ~> SubscriberSink(c2) - unzip.left ~> FlowFrom[Int].buffer(16, OverflowStrategy.backpressure).map(_ * 2) ~> SubscriberSink(c1) + Source(List(1 -> "a", 2 -> "b", 3 -> "c")) ~> unzip.in + unzip.right ~> Flow[String].buffer(16, OverflowStrategy.backpressure) ~> SubscriberDrain(c2) + unzip.left ~> Flow[Int].buffer(16, OverflowStrategy.backpressure).map(_ * 2) ~> SubscriberDrain(c1) }.run() val sub1 = c1.expectSubscription() @@ -51,9 +51,9 @@ class GraphUnzipSpec extends AkkaSpec { FlowGraph { implicit b ⇒ val unzip = Unzip[Int, String]("unzip") - FlowFrom(List(1 -> "a", 2 -> "b", 3 -> "c")) ~> unzip.in - unzip.left ~> SubscriberSink(c1) - unzip.right ~> SubscriberSink(c2) + Source(List(1 -> "a", 2 -> "b", 3 -> "c")) ~> unzip.in + unzip.left ~> SubscriberDrain(c1) + unzip.right ~> SubscriberDrain(c2) }.run() val sub1 = c1.expectSubscription() @@ -72,9 +72,9 @@ class GraphUnzipSpec extends AkkaSpec { FlowGraph { implicit b ⇒ val unzip = Unzip[Int, String]("unzip") - FlowFrom(List(1 -> "a", 2 -> "b", 3 -> "c")) ~> unzip.in - unzip.left ~> SubscriberSink(c1) - unzip.right ~> SubscriberSink(c2) + Source(List(1 -> "a", 2 -> "b", 3 -> "c")) ~> unzip.in + unzip.left ~> SubscriberDrain(c1) + unzip.right ~> SubscriberDrain(c2) }.run() val sub1 = c1.expectSubscription() @@ -94,9 +94,9 @@ class GraphUnzipSpec extends AkkaSpec { FlowGraph { implicit b ⇒ val unzip = Unzip[Int, String]("unzip") - FlowFrom(p1.getPublisher) ~> unzip.in - unzip.left ~> SubscriberSink(c1) - unzip.right ~> SubscriberSink(c2) + Source(p1.getPublisher) ~> unzip.in + unzip.left ~> SubscriberDrain(c1) + unzip.right ~> SubscriberDrain(c2) }.run() val p1Sub = p1.expectSubscription() @@ -122,10 +122,10 @@ class GraphUnzipSpec extends AkkaSpec { val zip = Zip[Int, String] val unzip = Unzip[Int, String] import FlowGraphImplicits._ - FlowFrom(List(1 -> "a", 2 -> "b", 3 -> "c")) ~> unzip.in + Source(List(1 -> "a", 2 -> "b", 3 -> "c")) ~> unzip.in unzip.left ~> zip.left unzip.right ~> zip.right - zip.out ~> SubscriberSink(c1) + zip.out ~> SubscriberDrain(c1) }.run() val sub1 = c1.expectSubscription() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphZipSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphZipSpec.scala index d313dcd7fc..3a584a5b07 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphZipSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/GraphZipSpec.scala @@ -22,10 +22,10 @@ class GraphZipSpec extends TwoStreamsSetup { FlowGraph { implicit b ⇒ val zip = Zip[Int, String] - FlowFrom(1 to 4) ~> zip.left - FlowFrom(List("A", "B", "C", "D", "E", "F")) ~> zip.right + Source(1 to 4) ~> zip.left + Source(List("A", "B", "C", "D", "E", "F")) ~> zip.right - zip.out ~> SubscriberSink(probe) + zip.out ~> SubscriberDrain(probe) }.run() val subscription = probe.expectSubscription() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/ImplicitFlowMaterializerSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/ImplicitFlowMaterializerSpec.scala index 3752bbc0cd..5495d7fc65 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/ImplicitFlowMaterializerSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/ImplicitFlowMaterializerSpec.scala @@ -16,15 +16,15 @@ object ImplicitFlowMaterializerSpec { .withInputBuffer(initialSize = 2, maxSize = 16) .withFanOutBuffer(initialSize = 1, maxSize = 16) - val flow = FlowFrom(input).map(_.toUpperCase()) + val flow = Source(input).map(_.toUpperCase()) def receive = { case "run" ⇒ // run takes an implicit FlowMaterializer parameter, which is provided by ImplicitFlowMaterializer import context.dispatcher - val foldSink = FoldSink[String, String]("")(_ + _) - val mf = flow.withSink(foldSink).run() - foldSink.future(mf) pipeTo sender() + val foldDrain = FoldDrain[String, String]("")(_ + _) + val mf = flow.connect(foldDrain).run() + foldDrain.future(mf) pipeTo sender() } } } diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/TickPublisherSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/TickPublisherSpec.scala index a7fbe39f94..3fde6450f9 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/TickPublisherSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl2/TickPublisherSpec.scala @@ -17,7 +17,7 @@ class TickPublisherSpec extends AkkaSpec { "produce ticks" in { val tickGen = Iterator from 1 val c = StreamTestKit.SubscriberProbe[String]() - FlowFrom(1.second, 500.millis, () ⇒ "tick-" + tickGen.next()).publishTo(c) + Source(1.second, 500.millis, () ⇒ "tick-" + tickGen.next()).publishTo(c) val sub = c.expectSubscription() sub.request(3) c.expectNoMsg(600.millis) @@ -33,7 +33,7 @@ class TickPublisherSpec extends AkkaSpec { "drop ticks when not requested" in { val tickGen = Iterator from 1 val c = StreamTestKit.SubscriberProbe[String]() - FlowFrom(1.second, 1.second, () ⇒ "tick-" + tickGen.next()).publishTo(c) + Source(1.second, 1.second, () ⇒ "tick-" + tickGen.next()).publishTo(c) val sub = c.expectSubscription() sub.request(2) c.expectNext("tick-1") @@ -50,7 +50,7 @@ class TickPublisherSpec extends AkkaSpec { "produce ticks with multiple subscribers" in { val tickGen = Iterator from 1 - val p = FlowFrom(1.second, 1.second, () ⇒ "tick-" + tickGen.next()).toPublisher() + val p = Source(1.second, 1.second, () ⇒ "tick-" + tickGen.next()).toPublisher() val c1 = StreamTestKit.SubscriberProbe[String]() val c2 = StreamTestKit.SubscriberProbe[String]() p.subscribe(c1) @@ -74,7 +74,7 @@ class TickPublisherSpec extends AkkaSpec { "signal onError when tick closure throws" in { val c = StreamTestKit.SubscriberProbe[String]() - FlowFrom(1.second, 1.second, () ⇒ throw new RuntimeException("tick err") with NoStackTrace).publishTo(c) + Source[String](1.second, 1.second, () ⇒ throw new RuntimeException("tick err") with NoStackTrace).publishTo(c) val sub = c.expectSubscription() sub.request(3) c.expectError.getMessage should be("tick err") @@ -83,8 +83,8 @@ class TickPublisherSpec extends AkkaSpec { // FIXME enable this test again when zip is back "be usable with zip for a simple form of rate limiting" ignore { // val c = StreamTestKit.SubscriberProbe[Int]() - // val rate = FlowFrom(1.second, 1.second, () ⇒ "tick").toPublisher() - // FlowFrom(1 to 100).zip(rate).map { case (n, _) ⇒ n }.publishTo(c) + // val rate = Source(1.second, 1.second, () ⇒ "tick").toPublisher() + // Source(1 to 100).zip(rate).map { case (n, _) ⇒ n }.publishTo(c) // val sub = c.expectSubscription() // sub.request(1000) // c.expectNext(1) diff --git a/akka-stream/src/main/scala/akka/stream/impl2/ActorBasedFlowMaterializer.scala b/akka-stream/src/main/scala/akka/stream/impl2/ActorBasedFlowMaterializer.scala index e6124681b6..4744cf82c8 100644 --- a/akka-stream/src/main/scala/akka/stream/impl2/ActorBasedFlowMaterializer.scala +++ b/akka-stream/src/main/scala/akka/stream/impl2/ActorBasedFlowMaterializer.scala @@ -132,57 +132,57 @@ case class ActorBasedFlowMaterializer(override val settings: MaterializerSetting } // Ops come in reverse order - override def materialize[In, Out](source: Source[In], sink: Sink[Out], ops: List[Ast.AstNode]): MaterializedFlow = { + override def materialize[In, Out](tap: Tap[In], drain: Drain[Out], ops: List[Ast.AstNode]): MaterializedPipe = { val flowName = createFlowName() - def attachSink(pub: Publisher[Out]) = sink match { - case s: SimpleSink[Out] ⇒ s.attach(pub, this, flowName) - case s: SinkWithKey[Out, _] ⇒ s.attach(pub, this, flowName) - case _ ⇒ throw new MaterializationException("unknown Sink type " + sink.getClass) + def attachDrain(pub: Publisher[Out]) = drain match { + case s: SimpleDrain[Out] ⇒ s.attach(pub, this, flowName) + case s: DrainWithKey[Out, _] ⇒ s.attach(pub, this, flowName) + case _ ⇒ throw new MaterializationException("unknown Drain type " + drain.getClass) } - def attachSource(sub: Subscriber[In]) = source match { - case s: SimpleSource[In] ⇒ s.attach(sub, this, flowName) - case s: SourceWithKey[In, _] ⇒ s.attach(sub, this, flowName) - case _ ⇒ throw new MaterializationException("unknown Source type " + sink.getClass) + def attachTap(sub: Subscriber[In]) = tap match { + case s: SimpleTap[In] ⇒ s.attach(sub, this, flowName) + case s: TapWithKey[In, _] ⇒ s.attach(sub, this, flowName) + case _ ⇒ throw new MaterializationException("unknown Tap type " + drain.getClass) } - def createSink() = sink.asInstanceOf[Sink[In]] match { - case s: SimpleSink[In] ⇒ s.create(this, flowName) -> (()) - case s: SinkWithKey[In, _] ⇒ s.create(this, flowName) - case _ ⇒ throw new MaterializationException("unknown Sink type " + sink.getClass) + def createDrain() = drain.asInstanceOf[Drain[In]] match { + case s: SimpleDrain[In] ⇒ s.create(this, flowName) -> (()) + case s: DrainWithKey[In, _] ⇒ s.create(this, flowName) + case _ ⇒ throw new MaterializationException("unknown Drain type " + drain.getClass) } - def createSource() = source.asInstanceOf[Source[Out]] match { - case s: SimpleSource[Out] ⇒ s.create(this, flowName) -> (()) - case s: SourceWithKey[Out, _] ⇒ s.create(this, flowName) - case _ ⇒ throw new MaterializationException("unknown Source type " + sink.getClass) + def createTap() = tap.asInstanceOf[Tap[Out]] match { + case s: SimpleTap[Out] ⇒ s.create(this, flowName) -> (()) + case s: TapWithKey[Out, _] ⇒ s.create(this, flowName) + case _ ⇒ throw new MaterializationException("unknown Tap type " + drain.getClass) } def isActive(s: AnyRef) = s match { - case source: SimpleSource[_] ⇒ source.isActive - case source: SourceWithKey[_, _] ⇒ source.isActive - case sink: SimpleSink[_] ⇒ sink.isActive - case sink: SinkWithKey[_, _] ⇒ sink.isActive - case _: Source[_] ⇒ throw new MaterializationException("unknown Source type " + sink.getClass) - case _: Sink[_] ⇒ throw new MaterializationException("unknown Sink type " + sink.getClass) + case tap: SimpleTap[_] ⇒ tap.isActive + case tap: TapWithKey[_, _] ⇒ tap.isActive + case drain: SimpleDrain[_] ⇒ drain.isActive + case drain: DrainWithKey[_, _] ⇒ drain.isActive + case _: Tap[_] ⇒ throw new MaterializationException("unknown Tap type " + drain.getClass) + case _: Drain[_] ⇒ throw new MaterializationException("unknown Drain type " + drain.getClass) } - val (sourceValue, sinkValue) = + val (tapValue, drainValue) = if (ops.isEmpty) { - if (isActive(sink)) { - val (sub, value) = createSink() - (attachSource(sub), value) - } else if (isActive(source)) { - val (pub, value) = createSource() - (value, attachSink(pub)) + if (isActive(drain)) { + val (sub, value) = createDrain() + (attachTap(sub), value) + } else if (isActive(tap)) { + val (pub, value) = createTap() + (value, attachDrain(pub)) } else { val id: Processor[In, Out] = processorForNode(identityTransform, flowName, 1).asInstanceOf[Processor[In, Out]] - (attachSource(id), attachSink(id)) + (attachTap(id), attachDrain(id)) } } else { val opsSize = ops.size val last = processorForNode(ops.head, flowName, opsSize).asInstanceOf[Processor[Any, Out]] val first = processorChain(last, ops.tail, flowName, opsSize - 1).asInstanceOf[Processor[In, Any]] - (attachSource(first), attachSink(last)) + (attachTap(first), attachDrain(last)) } - new MaterializedFlow(source, sourceValue, sink, sinkValue) + new MaterializedPipe(tap, tapValue, drain, drainValue) } private val identityTransform = Ast.Transform("identity", () ⇒ diff --git a/akka-stream/src/main/scala/akka/stream/impl2/ConcatAllImpl.scala b/akka-stream/src/main/scala/akka/stream/impl2/ConcatAllImpl.scala index f493951d09..1db54f09fd 100644 --- a/akka-stream/src/main/scala/akka/stream/impl2/ConcatAllImpl.scala +++ b/akka-stream/src/main/scala/akka/stream/impl2/ConcatAllImpl.scala @@ -5,7 +5,7 @@ package akka.stream.impl2 import akka.stream.impl.TransferPhase import akka.stream.impl.MultiStreamInputProcessor -import akka.stream.scaladsl2.FlowWithSource +import akka.stream.scaladsl2.Source import akka.stream.scaladsl2.FlowMaterializer /** @@ -17,7 +17,7 @@ private[akka] class ConcatAllImpl(materializer: FlowMaterializer) import MultiStreamInputProcessor._ val takeNextSubstream = TransferPhase(primaryInputs.NeedsInput && primaryOutputs.NeedsDemand) { () ⇒ - val flow = primaryInputs.dequeueInputElement().asInstanceOf[FlowWithSource[Any, Any]] + val flow = primaryInputs.dequeueInputElement().asInstanceOf[Source[Any]] val publisher = flow.toPublisher()(materializer) // FIXME we can pass the flow to createSubstreamInput (but avoiding copy impl now) val inputs = createAndSubscribeSubstreamInput(publisher) diff --git a/akka-stream/src/main/scala/akka/stream/impl2/GroupByProcessorImpl.scala b/akka-stream/src/main/scala/akka/stream/impl2/GroupByProcessorImpl.scala index 6a9cbd9d8f..a37c76d06f 100644 --- a/akka-stream/src/main/scala/akka/stream/impl2/GroupByProcessorImpl.scala +++ b/akka-stream/src/main/scala/akka/stream/impl2/GroupByProcessorImpl.scala @@ -5,7 +5,7 @@ package akka.stream.impl2 import akka.stream.MaterializerSettings import akka.stream.impl.TransferPhase -import akka.stream.scaladsl2.FlowFrom +import akka.stream.scaladsl2.Source import akka.stream.impl.MultiStreamOutputProcessor /** @@ -45,7 +45,7 @@ private[akka] class GroupByProcessorImpl(settings: MaterializerSettings, val key nextPhase(waitNext) } else { val substreamOutput = createSubstreamOutput() - val substreamFlow = FlowFrom(substreamOutput) // substreamOutput is a Publisher + val substreamFlow = Source(substreamOutput) // substreamOutput is a Publisher primaryOutputs.enqueueOutputElement((key, substreamFlow)) keyToSubstreamOutput(key) = substreamOutput nextPhase(dispatchToSubstream(elem, substreamOutput)) diff --git a/akka-stream/src/main/scala/akka/stream/impl2/PrefixAndTailImpl.scala b/akka-stream/src/main/scala/akka/stream/impl2/PrefixAndTailImpl.scala index 55de49ad50..6d8d6fb3d1 100644 --- a/akka-stream/src/main/scala/akka/stream/impl2/PrefixAndTailImpl.scala +++ b/akka-stream/src/main/scala/akka/stream/impl2/PrefixAndTailImpl.scala @@ -8,7 +8,7 @@ import scala.collection.immutable import akka.stream.impl.TransferPhase import akka.stream.impl.EmptyPublisher import akka.stream.impl.MultiStreamOutputProcessor -import akka.stream.scaladsl2.FlowFrom +import akka.stream.scaladsl2.Source /** * INTERNAL API @@ -44,13 +44,13 @@ private[akka] class PrefixAndTailImpl(_settings: MaterializerSettings, val takeM } def emitEmptyTail(): Unit = { - primaryOutputs.enqueueOutputElement((taken, FlowFrom(EmptyPublisher[Any]))) + primaryOutputs.enqueueOutputElement((taken, Source(EmptyPublisher[Any]))) nextPhase(completedPhase) } def emitNonEmptyTail(): Unit = { val substreamOutput = createSubstreamOutput() - val substreamFlow = FlowFrom(substreamOutput) // substreamOutput is a Publisher + val substreamFlow = Source(substreamOutput) // substreamOutput is a Publisher primaryOutputs.enqueueOutputElement((taken, substreamFlow)) primaryOutputs.complete() nextPhase(streamTailPhase(substreamOutput)) diff --git a/akka-stream/src/main/scala/akka/stream/impl2/SplitWhenProcessorImpl.scala b/akka-stream/src/main/scala/akka/stream/impl2/SplitWhenProcessorImpl.scala index 39e5681152..bdceaaf2c2 100644 --- a/akka-stream/src/main/scala/akka/stream/impl2/SplitWhenProcessorImpl.scala +++ b/akka-stream/src/main/scala/akka/stream/impl2/SplitWhenProcessorImpl.scala @@ -6,7 +6,7 @@ package akka.stream.impl2 import akka.stream.MaterializerSettings import akka.stream.impl.TransferPhase import akka.stream.impl.MultiStreamOutputProcessor -import akka.stream.scaladsl2.FlowFrom +import akka.stream.scaladsl2.Source /** * INTERNAL API @@ -24,7 +24,7 @@ private[akka] class SplitWhenProcessorImpl(_settings: MaterializerSettings, val def openSubstream(elem: Any): TransferPhase = TransferPhase(primaryOutputs.NeedsDemand) { () ⇒ val substreamOutput = createSubstreamOutput() - val substreamFlow = FlowFrom(substreamOutput) // substreamOutput is a Publisher + val substreamFlow = Source(substreamOutput) // substreamOutput is a Publisher primaryOutputs.enqueueOutputElement(substreamFlow) currentSubstream = substreamOutput nextPhase(serveSubstreamFirst(currentSubstream, elem)) diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl2/Drain.scala b/akka-stream/src/main/scala/akka/stream/scaladsl2/Drain.scala new file mode 100644 index 0000000000..778313436c --- /dev/null +++ b/akka-stream/src/main/scala/akka/stream/scaladsl2/Drain.scala @@ -0,0 +1,276 @@ +/** + * Copyright (C) 2014 Typesafe Inc. + */ +package akka.stream.scaladsl2 + +import akka.actor.Props + +import scala.collection.immutable +import scala.annotation.unchecked.uncheckedVariance +import scala.concurrent.{ Future, Promise } +import scala.util.{ Failure, Success, Try } +import org.reactivestreams.{ Publisher, Subscriber, Subscription } +import akka.stream.Transformer +import akka.stream.impl.{ FanoutProcessorImpl, BlackholeSubscriber } +import akka.stream.impl2.{ ActorProcessorFactory, ActorBasedFlowMaterializer } +import java.util.concurrent.atomic.AtomicReference + +/** + * This trait is a marker for a pluggable stream drain. Concrete instances should + * implement [[DrainWithKey]] or [[SimpleDrain]], otherwise a custom [[FlowMaterializer]] + * will have to be used to be able to attach them. + * + * All Drains defined in this package rely upon an [[akka.stream.impl2.ActorBasedFlowMaterializer]] being + * made available to them in order to use the attach method. Other + * FlowMaterializers can be used but must then implement the functionality of these + * Drain nodes themselves (or construct an ActorBasedFlowMaterializer). + */ +trait Drain[-In] extends Sink[In] + +/** + * A drain that does not need to create a user-accessible object during materialization. + */ +trait SimpleDrain[-In] extends Drain[In] with DrainOps[In] { + /** + * Attach this drain to the given [[org.reactivestreams.Publisher]]. Using the given + * [[FlowMaterializer]] is completely optional, especially if this drain belongs to + * a different Reactive Streams implementation. It is the responsibility of the + * caller to provide a suitable FlowMaterializer that can be used for running + * Flows if necessary. + * + * @param flowPublisher the Publisher to consume elements from + * @param materializer a FlowMaterializer that may be used for creating flows + * @param flowName the name of the current flow, which should be used in log statements or error messages + */ + def attach(flowPublisher: Publisher[In @uncheckedVariance], materializer: ActorBasedFlowMaterializer, flowName: String): Unit + /** + * This method is only used for Drains that return true from [[#isActive]], which then must + * implement it. + */ + def create(materializer: ActorBasedFlowMaterializer, flowName: String): Subscriber[In] @uncheckedVariance = + throw new UnsupportedOperationException(s"forgot to implement create() for $getClass that says isActive==true") + /** + * This method indicates whether this Drain can create a Subscriber instead of being + * attached to a Publisher. This is only used if the Flow does not contain any + * operations. + */ + def isActive: Boolean = false + +} + +/** + * A drain that will create an object during materialization that the user will need + * to retrieve in order to access aspects of this drain (could be a completion Future + * or a cancellation handle, etc.) + */ +trait DrainWithKey[-In, T] extends DrainOps[In] { + /** + * Attach this drain to the given [[org.reactivestreams.Publisher]]. Using the given + * [[FlowMaterializer]] is completely optional, especially if this drain belongs to + * a different Reactive Streams implementation. It is the responsibility of the + * caller to provide a suitable FlowMaterializer that can be used for running + * Flows if necessary. + * + * @param flowPublisher the Publisher to consume elements from + * @param materializer a FlowMaterializer that may be used for creating flows + * @param flowName the name of the current flow, which should be used in log statements or error messages + */ + def attach(flowPublisher: Publisher[In @uncheckedVariance], materializer: ActorBasedFlowMaterializer, flowName: String): T + /** + * This method is only used for Drains that return true from [[#isActive]], which then must + * implement it. + */ + def create(materializer: ActorBasedFlowMaterializer, flowName: String): (Subscriber[In] @uncheckedVariance, T) = + throw new UnsupportedOperationException(s"forgot to implement create() for $getClass that says isActive==true") + /** + * This method indicates whether this Drain can create a Subscriber instead of being + * attached to a Publisher. This is only used if the Flow does not contain any + * operations. + */ + def isActive: Boolean = false + + // these are unique keys, case class equality would break them + final override def equals(other: Any): Boolean = super.equals(other) + final override def hashCode: Int = super.hashCode +} + +/** + * Holds the downstream-most [[org.reactivestreams.Publisher]] interface of the materialized flow. + * The stream will not have any subscribers attached at this point, which means that after prefetching + * elements to fill the internal buffers it will assert back-pressure until + * a subscriber connects and creates demand for elements to be emitted. + */ +object PublisherDrain { + private val instance = new PublisherDrain[Nothing] + def apply[T]: PublisherDrain[T] = instance.asInstanceOf[PublisherDrain[T]] + def withFanout[T](initialBufferSize: Int, maximumBufferSize: Int): FanoutPublisherDrain[T] = + new FanoutPublisherDrain[T](initialBufferSize, maximumBufferSize) +} + +class PublisherDrain[In] extends DrainWithKey[In, Publisher[In]] { + def attach(flowPublisher: Publisher[In], materializer: ActorBasedFlowMaterializer, flowName: String): Publisher[In] = flowPublisher + def publisher(m: MaterializedDrain): Publisher[In] = m.getDrainFor(this) + + override def toString: String = "PublisherDrain" +} + +class FanoutPublisherDrain[In](initialBufferSize: Int, maximumBufferSize: Int) extends DrainWithKey[In, Publisher[In]] { + def publisher(m: MaterializedDrain): Publisher[In] = m.getDrainFor(this) + override def attach(flowPublisher: Publisher[In], materializer: ActorBasedFlowMaterializer, flowName: String): Publisher[In] = { + val fanoutActor = materializer.actorOf( + Props(new FanoutProcessorImpl(materializer.settings, initialBufferSize, maximumBufferSize)), s"$flowName-fanoutPublisher") + val fanoutProcessor = ActorProcessorFactory[In, In](fanoutActor) + flowPublisher.subscribe(fanoutProcessor) + fanoutProcessor + } + + override def toString: String = "Fanout" +} + +object FutureDrain { + def apply[T]: FutureDrain[T] = new FutureDrain[T] +} + +/** + * Holds a [[scala.concurrent.Future]] that will be fulfilled with the first + * thing that is signaled to this stream, which can be either an element (after + * which the upstream subscription is canceled), an error condition (putting + * the Future into the corresponding failed state) or the end-of-stream + * (failing the Future with a NoSuchElementException). + */ +class FutureDrain[In] extends DrainWithKey[In, Future[In]] { + def attach(flowPublisher: Publisher[In], materializer: ActorBasedFlowMaterializer, flowName: String): Future[In] = { + val (sub, f) = create(materializer, flowName) + flowPublisher.subscribe(sub) + f + } + override def isActive = true + override def create(materializer: ActorBasedFlowMaterializer, flowName: String): (Subscriber[In], Future[In]) = { + val p = Promise[In]() + val sub = new Subscriber[In] { // TODO #15804 verify this using the RS TCK + private val sub = new AtomicReference[Subscription] + override def onSubscribe(s: Subscription): Unit = + if (!sub.compareAndSet(null, s)) s.cancel() + else s.request(1) + override def onNext(t: In): Unit = { p.trySuccess(t); sub.get.cancel() } + override def onError(t: Throwable): Unit = p.tryFailure(t) + override def onComplete(): Unit = p.tryFailure(new NoSuchElementException("empty stream")) + } + (sub, p.future) + } + + def future(m: MaterializedDrain): Future[In] = m.getDrainFor(this) + + override def toString: String = "FutureDrain" +} + +/** + * Attaches a subscriber to this stream which will just discard all received + * elements. + */ +final case object BlackholeDrain extends SimpleDrain[Any] { + override def attach(flowPublisher: Publisher[Any], materializer: ActorBasedFlowMaterializer, flowName: String): Unit = + flowPublisher.subscribe(create(materializer, flowName)) + override def isActive: Boolean = true + override def create(materializer: ActorBasedFlowMaterializer, flowName: String): Subscriber[Any] = + new BlackholeSubscriber[Any](materializer.settings.maxInputBufferSize) +} + +/** + * Attaches a subscriber to this stream. + */ +final case class SubscriberDrain[In](subscriber: Subscriber[In]) extends SimpleDrain[In] { + override def attach(flowPublisher: Publisher[In], materializer: ActorBasedFlowMaterializer, flowName: String): Unit = + flowPublisher.subscribe(subscriber) + override def isActive: Boolean = true + override def create(materializer: ActorBasedFlowMaterializer, flowName: String): Subscriber[In] = subscriber +} + +object OnCompleteDrain { + private val SuccessUnit = Success[Unit](()) +} + +/** + * When the flow is completed, either through an error or normal + * completion, apply the provided function with [[scala.util.Success]] + * or [[scala.util.Failure]]. + */ +final case class OnCompleteDrain[In](callback: Try[Unit] ⇒ Unit) extends SimpleDrain[In] { + override def attach(flowPublisher: Publisher[In], materializer: ActorBasedFlowMaterializer, flowName: String): Unit = + Source(flowPublisher).transform("onCompleteDrain", () ⇒ new Transformer[In, Unit] { + override def onNext(in: In) = Nil + override def onError(e: Throwable) = () + override def onTermination(e: Option[Throwable]) = { + e match { + case None ⇒ callback(OnCompleteDrain.SuccessUnit) + case Some(e) ⇒ callback(Failure(e)) + } + Nil + } + }).consume()(materializer.withNamePrefix(flowName)) +} + +/** + * Invoke the given procedure for each received element. The drain holds a [[scala.concurrent.Future]] + * that will be completed with `Success` when reaching the normal end of the stream, or completed + * with `Failure` if there is an error is signaled in the stream. + */ +final case class ForeachDrain[In](f: In ⇒ Unit) extends DrainWithKey[In, Future[Unit]] { + override def attach(flowPublisher: Publisher[In], materializer: ActorBasedFlowMaterializer, flowName: String): Future[Unit] = { + val promise = Promise[Unit]() + Source(flowPublisher).transform("foreach", () ⇒ new Transformer[In, Unit] { + override def onNext(in: In) = { f(in); Nil } + override def onError(cause: Throwable): Unit = () + override def onTermination(e: Option[Throwable]) = { + e match { + case None ⇒ promise.success(()) + case Some(e) ⇒ promise.failure(e) + } + Nil + } + }).consume()(materializer.withNamePrefix(flowName)) + promise.future + } + def future(m: MaterializedDrain): Future[Unit] = m.getDrainFor(this) +} + +/** + * Invoke the given function for every received element, giving it its previous + * output (or the given `zero` value) and the element as input. The drain holds a + * [[scala.concurrent.Future]] that will be completed with value of the final + * function evaluation when the input stream ends, or completed with `Failure` + * if there is an error is signaled in the stream. + */ +final case class FoldDrain[U, In](zero: U)(f: (U, In) ⇒ U) extends DrainWithKey[In, Future[U]] { + override def attach(flowPublisher: Publisher[In], materializer: ActorBasedFlowMaterializer, flowName: String): Future[U] = { + val promise = Promise[U]() + + Source(flowPublisher).transform("fold", () ⇒ new Transformer[In, U] { + var state: U = zero + override def onNext(in: In): immutable.Seq[U] = { state = f(state, in); Nil } + override def onError(cause: Throwable) = () + override def onTermination(e: Option[Throwable]) = { + e match { + case None ⇒ promise.success(state) + case Some(e) ⇒ promise.failure(e) + } + Nil + } + }).consume()(materializer.withNamePrefix(flowName)) + + promise.future + } + def future(m: MaterializedDrain): Future[U] = m.getDrainFor(this) +} + +trait MaterializedDrain { + /** + * Do not call directly. Use accessor method in the concrete `Drain`, e.g. [[PublisherDrain#publisher]]. + */ + def getDrainFor[T](drainKey: DrainWithKey[_, T]): T +} + +trait DrainOps[-In] extends Drain[In] { + override def toSubscriber()(implicit materializer: FlowMaterializer): Subscriber[In @uncheckedVariance] = + Flow[In].connect(this).toSubscriber() +} diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl2/FlattenStrategy.scala b/akka-stream/src/main/scala/akka/stream/scaladsl2/FlattenStrategy.scala index 5140c5d580..876ae8b2ab 100644 --- a/akka-stream/src/main/scala/akka/stream/scaladsl2/FlattenStrategy.scala +++ b/akka-stream/src/main/scala/akka/stream/scaladsl2/FlattenStrategy.scala @@ -17,7 +17,7 @@ object FlattenStrategy { * emitting its elements directly to the output until it completes and then taking the next stream. This has the * consequence that if one of the input stream is infinite, no other streams after that will be consumed from. */ - def concat[T]: FlattenStrategy[FlowWithSource[_, T], T] = Concat[T]() + def concat[T]: FlattenStrategy[Source[T], T] = Concat[T]() - private[akka] case class Concat[T]() extends FlattenStrategy[FlowWithSource[_, T], T] -} \ No newline at end of file + private[akka] case class Concat[T]() extends FlattenStrategy[Source[T], T] +} diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl2/Flow.scala b/akka-stream/src/main/scala/akka/stream/scaladsl2/Flow.scala index 8cf2a6237f..341cdb0336 100644 --- a/akka-stream/src/main/scala/akka/stream/scaladsl2/Flow.scala +++ b/akka-stream/src/main/scala/akka/stream/scaladsl2/Flow.scala @@ -3,69 +3,55 @@ */ package akka.stream.scaladsl2 +import akka.stream.{ TimerTransformer, Transformer, OverflowStrategy } import scala.collection.immutable -import scala.collection.immutable -import akka.stream.impl2.Ast._ -import org.reactivestreams._ -import scala.annotation.unchecked.uncheckedVariance -import scala.concurrent.Future -import scala.language.higherKinds -import akka.stream.Transformer import scala.concurrent.duration.FiniteDuration -import scala.concurrent.duration.Duration -import akka.util.Collections.EmptyImmutableSeq -import akka.stream.TimerTransformer -import akka.stream.OverflowStrategy +import scala.concurrent.Future + +import scala.language.higherKinds /** - * This is the interface from which all concrete Flows inherit. No generic - * operations are presented because the concrete type of Flow (i.e. whether - * it has a [[Source]] or a [[Sink]]) determines what is available. + * A `Flow` is a set of stream processing steps that has one open input and one open output. */ -sealed trait Flow +trait Flow[-In, +Out] extends FlowOps[Out] { + override type Repr[+O] <: Flow[In, O] -object FlowOps { - private case object TakeWithinTimerKey - private case object DropWithinTimerKey - private case object GroupedWithinTimerKey + /** + * Transform this flow by appending the given processing steps. + */ + def connect[T](flow: Flow[Out, T]): Flow[In, T] - private val takeCompletedTransformer: Transformer[Any, Any] = new Transformer[Any, Any] { - override def onNext(elem: Any) = Nil - override def isComplete = true - } + /** + * Connect this flow to a sink, concatenating the processing steps of both. + */ + def connect(sink: Sink[Out]): Sink[In] +} - private val identityTransformer: Transformer[Any, Any] = new Transformer[Any, Any] { - override def onNext(elem: Any) = List(elem) - } +object Flow { + /** + * Helper to create `Flow` without a [[Source]] or a [[Sink]]. + * Example usage: `Flow[Int]` + */ + def apply[T]: Flow[T, T] = Pipe.empty[T] } /** - * Scala API: Operations offered by flows with a free output side: the DSL flows left-to-right only. + * Scala API: Operations offered by Flows and Sources with a free output side: the DSL flows left-to-right only. */ -trait FlowOps[-In, +Out] { - import FlowOps._ - type Repr[-I, +O] <: FlowOps[I, O] - - // Storing ops in reverse order - protected def andThen[U](op: AstNode): Repr[In, U] +trait FlowOps[+Out] { + type Repr[+O] /** * Transform this stream by applying the given function to each of the elements * as they pass through this processing step. */ - def map[T](f: Out ⇒ T): Repr[In, T] = - transform("map", () ⇒ new Transformer[Out, T] { - override def onNext(in: Out) = List(f(in)) - }) + def map[T](f: Out ⇒ T): Repr[T] /** * Transform each input element into a sequence of output elements that is * then flattened into the output stream. */ - def mapConcat[T](f: Out ⇒ immutable.Seq[T]): Repr[In, T] = - transform("mapConcat", () ⇒ new Transformer[Out, T] { - override def onNext(in: Out) = f(in) - }) + def mapConcat[T](f: Out ⇒ immutable.Seq[T]): Repr[T] /** * Transform this stream by applying the given function to each of the elements @@ -76,8 +62,7 @@ trait FlowOps[-In, +Out] { * * @see [[#mapAsyncUnordered]] */ - def mapAsync[T](f: Out ⇒ Future[T]): Repr[In, T] = - andThen(MapAsync(f.asInstanceOf[Any ⇒ Future[Any]])) + def mapAsync[T](f: Out ⇒ Future[T]): Repr[T] /** * Transform this stream by applying the given function to each of the elements @@ -89,26 +74,19 @@ trait FlowOps[-In, +Out] { * * @see [[#mapAsync]] */ - def mapAsyncUnordered[T](f: Out ⇒ Future[T]): Repr[In, T] = - andThen(MapAsyncUnordered(f.asInstanceOf[Any ⇒ Future[Any]])) + def mapAsyncUnordered[T](f: Out ⇒ Future[T]): Repr[T] /** * Only pass on those elements that satisfy the given predicate. */ - def filter(p: Out ⇒ Boolean): Repr[In, Out] = - transform("filter", () ⇒ new Transformer[Out, Out] { - override def onNext(in: Out) = if (p(in)) List(in) else Nil - }) + def filter(p: Out ⇒ Boolean): Repr[Out] /** * Transform this stream by applying the given partial function to each of the elements * on which the function is defined as they pass through this processing step. * Non-matching elements are filtered out. */ - def collect[T](pf: PartialFunction[Out, T]): Repr[In, T] = - transform("collect", () ⇒ new Transformer[Out, T] { - override def onNext(in: Out) = if (pf.isDefinedAt(in)) List(pf(in)) else Nil - }) + def collect[T](pf: PartialFunction[Out, T]): Repr[T] /** * Chunk up this stream into groups of the given size, with the last group @@ -116,22 +94,7 @@ trait FlowOps[-In, +Out] { * * `n` must be positive, otherwise IllegalArgumentException is thrown. */ - def grouped(n: Int): Repr[In, immutable.Seq[Out]] = { - require(n > 0, "n must be greater than 0") - transform("grouped", () ⇒ new Transformer[Out, immutable.Seq[Out]] { - var buf: Vector[Out] = Vector.empty - override def onNext(in: Out) = { - buf :+= in - if (buf.size == n) { - val group = buf - buf = Vector.empty - List(group) - } else - Nil - } - override def onTermination(e: Option[Throwable]) = if (buf.isEmpty) Nil else List(buf) - }) - } + def grouped(n: Int): Repr[immutable.Seq[Out]] /** * Chunk up this stream into groups of elements received within a time window, @@ -143,72 +106,18 @@ trait FlowOps[-In, +Out] { * `n` must be positive, and `d` must be greater than 0 seconds, otherwise * IllegalArgumentException is thrown. */ - def groupedWithin(n: Int, d: FiniteDuration): Repr[In, immutable.Seq[Out]] = { - require(n > 0, "n must be greater than 0") - require(d > Duration.Zero) - timerTransform("groupedWithin", () ⇒ new TimerTransformer[Out, immutable.Seq[Out]] { - schedulePeriodically(GroupedWithinTimerKey, d) - var buf: Vector[Out] = Vector.empty - - override def onNext(in: Out) = { - buf :+= in - if (buf.size == n) { - // start new time window - schedulePeriodically(GroupedWithinTimerKey, d) - emitGroup() - } else Nil - } - override def onTermination(e: Option[Throwable]) = if (buf.isEmpty) Nil else List(buf) - override def onTimer(timerKey: Any) = emitGroup() - private def emitGroup(): immutable.Seq[immutable.Seq[Out]] = - if (buf.isEmpty) EmptyImmutableSeq - else { - val group = buf - buf = Vector.empty - List(group) - } - }) - } + def groupedWithin(n: Int, d: FiniteDuration): Repr[immutable.Seq[Out]] /** * Discard the given number of elements at the beginning of the stream. * No elements will be dropped if `n` is zero or negative. */ - def drop(n: Int): Repr[In, Out] = - transform("drop", () ⇒ new Transformer[Out, Out] { - var delegate: Transformer[Out, Out] = - if (n <= 0) identityTransformer.asInstanceOf[Transformer[Out, Out]] - else new Transformer[Out, Out] { - var c = n - override def onNext(in: Out) = { - c -= 1 - if (c == 0) - delegate = identityTransformer.asInstanceOf[Transformer[Out, Out]] - Nil - } - } - - override def onNext(in: Out) = delegate.onNext(in) - }) + def drop(n: Int): Repr[Out] /** * Discard the elements received within the given duration at beginning of the stream. */ - def dropWithin(d: FiniteDuration): Repr[In, Out] = - timerTransform("dropWithin", () ⇒ new TimerTransformer[Out, Out] { - scheduleOnce(DropWithinTimerKey, d) - - var delegate: Transformer[Out, Out] = - new Transformer[Out, Out] { - override def onNext(in: Out) = Nil - } - - override def onNext(in: Out) = delegate.onNext(in) - override def onTimer(timerKey: Any) = { - delegate = identityTransformer.asInstanceOf[Transformer[Out, Out]] - Nil - } - }) + def dropWithin(d: FiniteDuration): Repr[Out] /** * Terminate processing (and cancel the upstream publisher) after the given @@ -219,23 +128,7 @@ trait FlowOps[-In, +Out] { * The stream will be completed without producing any elements if `n` is zero * or negative. */ - def take(n: Int): Repr[In, Out] = - transform("take", () ⇒ new Transformer[Out, Out] { - var delegate: Transformer[Out, Out] = - if (n <= 0) takeCompletedTransformer.asInstanceOf[Transformer[Out, Out]] - else new Transformer[Out, Out] { - var c = n - override def onNext(in: Out) = { - c -= 1 - if (c == 0) - delegate = takeCompletedTransformer.asInstanceOf[Transformer[Out, Out]] - List(in) - } - } - - override def onNext(in: Out) = delegate.onNext(in) - override def isComplete = delegate.isComplete - }) + def take(n: Int): Repr[Out] /** * Terminate processing (and cancel the upstream publisher) after the given @@ -246,19 +139,7 @@ trait FlowOps[-In, +Out] { * Note that this can be combined with [[#take]] to limit the number of elements * within the duration. */ - def takeWithin(d: FiniteDuration): Repr[In, Out] = - timerTransform("takeWithin", () ⇒ new TimerTransformer[Out, Out] { - scheduleOnce(TakeWithinTimerKey, d) - - var delegate: Transformer[Out, Out] = identityTransformer.asInstanceOf[Transformer[Out, Out]] - - override def onNext(in: Out) = delegate.onNext(in) - override def isComplete = delegate.isComplete - override def onTimer(timerKey: Any) = { - delegate = takeCompletedTransformer.asInstanceOf[Transformer[Out, Out]] - Nil - } - }) + def takeWithin(d: FiniteDuration): Repr[Out] /** * Allows a faster upstream to progress independently of a slower subscriber by conflating elements into a summary @@ -271,8 +152,7 @@ trait FlowOps[-In, +Out] { * @param seed Provides the first state for a conflated value using the first unconsumed element as a start * @param aggregate Takes the currently aggregated value and the current pending element to produce a new aggregate */ - def conflate[S](seed: Out ⇒ S, aggregate: (S, Out) ⇒ S): Repr[In, S] = - andThen(Conflate(seed.asInstanceOf[Any ⇒ Any], aggregate.asInstanceOf[(Any, Any) ⇒ Any])) + def conflate[S](seed: Out ⇒ S, aggregate: (S, Out) ⇒ S): Repr[S] /** * Allows a faster downstream to progress independently of a slower publisher by extrapolating elements from an older @@ -287,8 +167,7 @@ trait FlowOps[-In, +Out] { * @param extrapolate Takes the current extrapolation state to produce an output element and the next extrapolation * state. */ - def expand[S, U](seed: Out ⇒ S, extrapolate: S ⇒ (U, S)): Repr[In, U] = - andThen(Expand(seed.asInstanceOf[Any ⇒ Any], extrapolate.asInstanceOf[Any ⇒ (Any, Any)])) + def expand[S, U](seed: Out ⇒ S, extrapolate: S ⇒ (U, S)): Repr[U] /** * Adds a fixed size buffer in the flow that allows to store elements from a faster upstream until it becomes full. @@ -298,10 +177,7 @@ trait FlowOps[-In, +Out] { * @param size The size of the buffer in element count * @param overflowStrategy Strategy that is used when incoming elements cannot fit inside the buffer */ - def buffer(size: Int, overflowStrategy: OverflowStrategy): Repr[In, Out] = { - require(size > 0, s"Buffer size must be larger than zero but was [$size]") - andThen(Buffer(size, overflowStrategy)) - } + def buffer(size: Int, overflowStrategy: OverflowStrategy): Repr[Out] /** * Generic transformation of a stream: for each element the [[akka.stream.Transformer#onNext]] @@ -325,17 +201,14 @@ trait FlowOps[-In, +Out] { * * Note that you can use [[#timerTransform]] if you need support for scheduled events in the transformer. */ - def transform[T](name: String, mkTransformer: () ⇒ Transformer[Out, T]): Repr[In, T] = { - andThen(Transform(name, mkTransformer.asInstanceOf[() ⇒ Transformer[Any, Any]])) - } + def transform[T](name: String, mkTransformer: () ⇒ Transformer[Out, T]): Repr[T] /** * Takes up to `n` elements from the stream and returns a pair containing a strict sequence of the taken element * and a stream representing the remaining elements. If ''n'' is zero or negative, then this will return a pair * of an empty collection and a stream containing the whole upstream unchanged. */ - def prefixAndTail[U >: Out](n: Int): Repr[In, (immutable.Seq[Out], FlowWithSource[U, U])] = - andThen(PrefixAndTail(n)) + def prefixAndTail[U >: Out](n: Int): Repr[(immutable.Seq[Out], Source[U])] /** * This operation demultiplexes the incoming stream into separate output @@ -348,8 +221,7 @@ trait FlowOps[-In, +Out] { * care to unblock (or cancel) all of the produced streams even if you want * to consume only one of them. */ - def groupBy[K, U >: Out](f: Out ⇒ K): Repr[In, (K, FlowWithSource[U, U])] = - andThen(GroupBy(f.asInstanceOf[Any ⇒ Any])) + def groupBy[K, U >: Out](f: Out ⇒ K): Repr[(K, Source[U])] /** * This operation applies the given predicate to all incoming elements and @@ -364,17 +236,13 @@ trait FlowOps[-In, +Out] { * true, false, false // elements go into third substream * }}} */ - def splitWhen[U >: Out](p: Out ⇒ Boolean): Repr[In, FlowWithSource[U, U]] = - andThen(SplitWhen(p.asInstanceOf[Any ⇒ Boolean])) + def splitWhen[U >: Out](p: Out ⇒ Boolean): Repr[Source[U]] /** * Transforms a stream of streams into a contiguous stream of elements using the provided flattening strategy. - * This operation can be used on a stream of element type [[StreamWithSource]]. + * This operation can be used on a stream of element type [[Source]]. */ - def flatten[U](strategy: FlattenStrategy[Out, U]): Repr[In, U] = strategy match { - case _: FlattenStrategy.Concat[Out] ⇒ andThen(ConcatAll) - case _ ⇒ throw new IllegalArgumentException(s"Unsupported flattening strategy [${strategy.getClass.getSimpleName}]") - } + def flatten[U](strategy: FlattenStrategy[Out, U]): Repr[U] /** * Transformation of a stream, with additional support for scheduled events. @@ -400,122 +268,19 @@ trait FlowOps[-In, +Out] { * * Note that you can use [[#transform]] if you just need to transform elements time plays no role in the transformation. */ - def timerTransform[U](name: String, mkTransformer: () ⇒ TimerTransformer[Out, U]): Repr[In, U] = - andThen(TimerTransform(name, mkTransformer.asInstanceOf[() ⇒ TimerTransformer[Any, Any]])) -} - -object ProcessorFlow { - private val emptyInstance = ProcessorFlow[Any, Any](ops = Nil) - def empty[T]: ProcessorFlow[T, T] = emptyInstance.asInstanceOf[ProcessorFlow[T, T]] -} - -/** - * Flow without attached input and without attached output, can be used as a `Processor`. - */ -final case class ProcessorFlow[-In, +Out](ops: List[AstNode]) extends FlowOps[In, Out] { - override type Repr[-I, +O] = ProcessorFlow[I, O] - - override protected def andThen[U](op: AstNode): Repr[In, U] = this.copy(ops = op :: ops) - - def withSink(out: Sink[Out]): FlowWithSink[In, Out] = FlowWithSink(out, ops) - def withSource(in: Source[In]): FlowWithSource[In, Out] = FlowWithSource(in, ops) - - def prepend[T](f: ProcessorFlow[T, In]): ProcessorFlow[T, Out] = ProcessorFlow(ops ::: f.ops) - def prepend[T](f: FlowWithSource[T, In]): FlowWithSource[T, Out] = f.append(this) - - def append[T](f: ProcessorFlow[Out, T]): ProcessorFlow[In, T] = ProcessorFlow(f.ops ++: ops) - def append[T](f: FlowWithSink[Out, T]): FlowWithSink[In, T] = f.prepend(this) -} - -/** - * Flow with attached output, can be used as a `Subscriber`. - */ -final case class FlowWithSink[-In, +Out](private[scaladsl2] val output: Sink[Out @uncheckedVariance], ops: List[AstNode]) { - - def withSource(in: Source[In]): RunnableFlow[In, Out] = RunnableFlow(in, output, ops) - def withoutSink: ProcessorFlow[In, Out] = ProcessorFlow(ops) - - def prepend[T](f: ProcessorFlow[T, In]): FlowWithSink[T, Out] = FlowWithSink(output, ops ::: f.ops) - def prepend[T](f: FlowWithSource[T, In]): RunnableFlow[T, Out] = RunnableFlow(f.input, output, ops ::: f.ops) - - def toSubscriber()(implicit materializer: FlowMaterializer): Subscriber[In @uncheckedVariance] = { - val subIn = SubscriberSource[In]() - val mf = withSource(subIn).run() - subIn.subscriber(mf) - } -} - -/** - * Flow with attached input, can be used as a `Publisher`. - */ -final case class FlowWithSource[-In, +Out](private[scaladsl2] val input: Source[In @uncheckedVariance], ops: List[AstNode]) extends FlowOps[In, Out] { - override type Repr[-I, +O] = FlowWithSource[I, O] - - override protected def andThen[U](op: AstNode): Repr[In, U] = this.copy(ops = op :: ops) - - def withSink(out: Sink[Out]): RunnableFlow[In, Out] = RunnableFlow(input, out, ops) - def withoutSource: ProcessorFlow[In, Out] = ProcessorFlow(ops) - - def append[T](f: ProcessorFlow[Out, T]): FlowWithSource[In, T] = FlowWithSource(input, f.ops ++: ops) - def append[T](f: FlowWithSink[Out, T]): RunnableFlow[In, T] = RunnableFlow(input, f.output, f.ops ++: ops) - - def toPublisher()(implicit materializer: FlowMaterializer): Publisher[Out @uncheckedVariance] = { - val pubOut = PublisherSink[Out] - val mf = withSink(pubOut).run() - pubOut.publisher(mf) - } - - def toFanoutPublisher(initialBufferSize: Int, maximumBufferSize: Int)(implicit materializer: FlowMaterializer): Publisher[Out @uncheckedVariance] = { - val pubOut = PublisherSink.withFanout[Out](initialBufferSize, maximumBufferSize) - val mf = withSink(pubOut).run() - pubOut.publisher(mf) - } - - def publishTo(subscriber: Subscriber[Out @uncheckedVariance])(implicit materializer: FlowMaterializer): Unit = - toPublisher().subscribe(subscriber) - - def consume()(implicit materializer: FlowMaterializer): Unit = - withSink(BlackholeSink).run() - + def timerTransform[U](name: String, mkTransformer: () ⇒ TimerTransformer[Out, U]): Repr[U] } /** * Flow with attached input and output, can be executed. */ -final case class RunnableFlow[-In, +Out](private[scaladsl2] val input: Source[In @uncheckedVariance], - private[scaladsl2] val output: Sink[Out @uncheckedVariance], ops: List[AstNode]) extends Flow { - def withoutSink: FlowWithSource[In, Out] = FlowWithSource(input, ops) - def withoutSource: FlowWithSink[In, Out] = FlowWithSink(output, ops) - - def run()(implicit materializer: FlowMaterializer): MaterializedFlow = - materializer.materialize(input, output, ops) +trait RunnableFlow { + def run()(implicit materializer: FlowMaterializer): MaterializedFlow } /** * Returned by [[RunnableFlow#run]] and can be used as parameter to the - * accessor method to retrieve the materialized `Source` or `Sink`, e.g. - * [[SubscriberSource#subscriber]] or [[PublisherSink#publisher]]. + * accessor method to retrieve the materialized `Tap` or `Drain`, e.g. + * [[SubscriberTap#subscriber]] or [[PublisherDrain#publisher]]. */ -class MaterializedFlow(sourceKey: AnyRef, matSource: Any, sinkKey: AnyRef, matSink: Any) extends MaterializedSource with MaterializedSink { - /** - * Do not call directly. Use accessor method in the concrete `Source`, e.g. [[SubscriberSource#subscriber]]. - */ - override def getSourceFor[T](key: SourceWithKey[_, T]): T = - if (key == sourceKey) matSource.asInstanceOf[T] - else throw new IllegalArgumentException(s"Source key [$key] doesn't match the source [$sourceKey] of this flow") - - /** - * Do not call directly. Use accessor method in the concrete `Sink`, e.g. [[PublisherSink#publisher]]. - */ - def getSinkFor[T](key: SinkWithKey[_, T]): T = - if (key == sinkKey) matSink.asInstanceOf[T] - else throw new IllegalArgumentException(s"Sink key [$key] doesn't match the sink [$sinkKey] of this flow") -} - -trait MaterializedSource { - def getSourceFor[T](sourceKey: SourceWithKey[_, T]): T -} - -trait MaterializedSink { - def getSinkFor[T](sinkKey: SinkWithKey[_, T]): T -} +trait MaterializedFlow extends MaterializedTap with MaterializedDrain diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl2/FlowGraph.scala b/akka-stream/src/main/scala/akka/stream/scaladsl2/FlowGraph.scala index 7b2641142c..367e19baa4 100644 --- a/akka-stream/src/main/scala/akka/stream/scaladsl2/FlowGraph.scala +++ b/akka-stream/src/main/scala/akka/stream/scaladsl2/FlowGraph.scala @@ -4,11 +4,10 @@ package akka.stream.scaladsl2 import scala.language.existentials -import scalax.collection.edge.LkDiEdge +import scalax.collection.edge.{ LkBase, LkDiEdge } import scalax.collection.mutable.Graph import scalax.collection.immutable.{ Graph ⇒ ImmutableGraph } import org.reactivestreams.Subscriber -import akka.stream.impl.BlackholeSubscriber import org.reactivestreams.Publisher import akka.stream.impl2.Ast @@ -73,8 +72,8 @@ object Merge { * Merge several streams, taking elements as they arrive from input streams * (picking randomly when several have elements ready). * - * When building the [[FlowGraph]] you must connect one or more input flows/sources - * and one output flow/sink to the `Merge` vertex. + * When building the [[FlowGraph]] you must connect one or more input pipes/taps + * and one output pipe/drain to the `Merge` vertex. */ final class Merge[T](override val name: Option[String]) extends FlowGraphInternal.InternalVertex with Junction[T] { override private[akka] val vertex = this @@ -259,82 +258,83 @@ final class Concat[T](override val name: Option[String]) extends FlowGraphIntern override private[akka] def astNode = Ast.Concat } -object UndefinedSink { +object UndefinedDrain { /** - * Create a new anonymous `UndefinedSink` vertex with the specified input type. - * Note that a `UndefinedSink` instance can only be used at one place (one vertex) + * Create a new anonymous `UndefinedDrain` vertex with the specified input type. + * Note that a `UndefinedDrain` instance can only be used at one place (one vertex) * in the `FlowGraph`. This method creates a new instance every time it * is called and those instances are not `equal`. */ - def apply[T]: UndefinedSink[T] = new UndefinedSink[T](None) + def apply[T]: UndefinedDrain[T] = new UndefinedDrain[T](None) /** - * Create a named `UndefinedSink` vertex with the specified input type. - * Note that a `UndefinedSink` with a specific name can only be used at one place (one vertex) + * Create a named `UndefinedDrain` vertex with the specified input type. + * Note that a `UndefinedDrain` with a specific name can only be used at one place (one vertex) * in the `FlowGraph`. Calling this method several times with the same name * returns instances that are `equal`. */ - def apply[T](name: String): UndefinedSink[T] = new UndefinedSink[T](Some(name)) + def apply[T](name: String): UndefinedDrain[T] = new UndefinedDrain[T](Some(name)) } /** - * It is possible to define a [[PartialFlowGraph]] with output flows that are not connected - * yet by using this placeholder instead of the real [[Sink]]. Later the placeholder can - * be replaced with [[FlowGraphBuilder#attachSink]]. + * It is possible to define a [[PartialFlowGraph]] with output pipes that are not connected + * yet by using this placeholder instead of the real [[Drain]]. Later the placeholder can + * be replaced with [[FlowGraphBuilder#attachDrain]]. */ -final class UndefinedSink[-T](override val name: Option[String]) extends FlowGraphInternal.InternalVertex { +final class UndefinedDrain[-T](override val name: Option[String]) extends FlowGraphInternal.InternalVertex { override def minimumInputCount: Int = 1 override def maximumInputCount: Int = 1 override def minimumOutputCount: Int = 0 override def maximumOutputCount: Int = 0 - override private[akka] def astNode = throw new UnsupportedOperationException("Undefined sinks cannot be materialized") + override private[akka] def astNode = throw new UnsupportedOperationException("Undefined drains cannot be materialized") } -object UndefinedSource { +object UndefinedTap { /** - * Create a new anonymous `UndefinedSource` vertex with the specified input type. - * Note that a `UndefinedSource` instance can only be used at one place (one vertex) + * Create a new anonymous `UndefinedTap` vertex with the specified input type. + * Note that a `UndefinedTap` instance can only be used at one place (one vertex) * in the `FlowGraph`. This method creates a new instance every time it * is called and those instances are not `equal`. */ - def apply[T]: UndefinedSource[T] = new UndefinedSource[T](None) + def apply[T]: UndefinedTap[T] = new UndefinedTap[T](None) /** - * Create a named `UndefinedSource` vertex with the specified output type. - * Note that a `UndefinedSource` with a specific name can only be used at one place (one vertex) + * Create a named `UndefinedTap` vertex with the specified output type. + * Note that a `UndefinedTap` with a specific name can only be used at one place (one vertex) * in the `FlowGraph`. Calling this method several times with the same name * returns instances that are `equal`. */ - def apply[T](name: String): UndefinedSource[T] = new UndefinedSource[T](Some(name)) + def apply[T](name: String): UndefinedTap[T] = new UndefinedTap[T](Some(name)) } /** - * It is possible to define a [[PartialFlowGraph]] with input flows that are not connected - * yet by using this placeholder instead of the real [[Source]]. Later the placeholder can - * be replaced with [[FlowGraphBuilder#attachSource]]. + * It is possible to define a [[PartialFlowGraph]] with input pipes that are not connected + * yet by using this placeholder instead of the real [[Tap]]. Later the placeholder can + * be replaced with [[FlowGraphBuilder#attachTap]]. */ -final class UndefinedSource[+T](override val name: Option[String]) extends FlowGraphInternal.InternalVertex { +final class UndefinedTap[+T](override val name: Option[String]) extends FlowGraphInternal.InternalVertex { override def minimumInputCount: Int = 0 override def maximumInputCount: Int = 0 override def minimumOutputCount: Int = 1 override def maximumOutputCount: Int = 1 - override private[akka] def astNode = throw new UnsupportedOperationException("Undefined sources cannot be materialized") + override private[akka] def astNode = throw new UnsupportedOperationException("Undefined taps cannot be materialized") } /** * INTERNAL API */ private[akka] object FlowGraphInternal { + val OnlyPipesErrorMessage = "Only pipes are supported currently!" def UnlabeledPort = -1 sealed trait Vertex - case class SourceVertex(source: Source[_]) extends Vertex { - override def toString = source.toString + case class TapVertex(tap: Tap[_]) extends Vertex { + override def toString = tap.toString // these are unique keys, case class equality would break them final override def equals(other: Any): Boolean = super.equals(other) final override def hashCode: Int = super.hashCode } - case class SinkVertex(sink: Sink[_]) extends Vertex { - override def toString = sink.toString + case class DrainVertex(drain: Drain[_]) extends Vertex { + override def toString = drain.toString // these are unique keys, case class equality would break them final override def equals(other: Any): Boolean = super.equals(other) final override def hashCode: Int = super.hashCode @@ -370,107 +370,159 @@ private[akka] object FlowGraphInternal { // flow not part of equals/hashCode case class EdgeLabel(qualifier: Int)( - val flow: ProcessorFlow[Any, Any], + val pipe: Pipe[Any, Nothing], val inputPort: Int, val outputPort: Int) { - override def toString: String = flow.toString + override def toString: String = pipe.toString } + type EdgeType[T] = LkDiEdge[T] { type L1 = EdgeLabel } } /** * Builder of [[FlowGraph]] and [[PartialFlowGraph]]. * Syntactic sugar is provided by [[FlowGraphImplicits]]. */ -class FlowGraphBuilder private (graph: Graph[FlowGraphInternal.Vertex, LkDiEdge]) { +class FlowGraphBuilder private (graph: Graph[FlowGraphInternal.Vertex, FlowGraphInternal.EdgeType]) { import FlowGraphInternal._ - private[akka] def this() = this(Graph.empty[FlowGraphInternal.Vertex, LkDiEdge]) + private[akka] def this() = this(Graph.empty[FlowGraphInternal.Vertex, FlowGraphInternal.EdgeType]) - private[akka] def this(immutableGraph: ImmutableGraph[FlowGraphInternal.Vertex, LkDiEdge]) = - this(Graph.from(edges = immutableGraph.edges.map(e ⇒ LkDiEdge(e.from.value, e.to.value)(e.label)).toIterable)) + private[akka] def this(immutableGraph: ImmutableGraph[FlowGraphInternal.Vertex, FlowGraphInternal.EdgeType]) = + this({ + val edges: Iterable[FlowGraphInternal.EdgeType[FlowGraphInternal.Vertex]] = + immutableGraph.edges.map(e ⇒ LkDiEdge(e.from.value, e.to.value)(e.label)) + Graph.from(edges = edges) + }) - private implicit val edgeFactory = scalax.collection.edge.LkDiEdge + private implicit val edgeFactory = scalax.collection.edge.LkDiEdge.asInstanceOf[LkBase.LkEdgeCompanion[EdgeType]] var edgeQualifier = graph.edges.size private var cyclesAllowed = false - def addEdge[In, Out](source: Source[In], flow: ProcessorFlow[In, Out], sink: JunctionInPort[Out]): this.type = { - val sourceVertex = SourceVertex(source) - checkAddSourceSinkPrecondition(sourceVertex) - checkJunctionInPortPrecondition(sink) - addGraphEdge(sourceVertex, sink.vertex, flow, inputPort = sink.port, outputPort = UnlabeledPort) + private def addTapPipeEdge[In, Out](tap: Tap[In], pipe: Pipe[In, Out], drain: JunctionInPort[Out]): this.type = { + val tapVertex = TapVertex(tap) + checkAddTapDrainPrecondition(tapVertex) + checkJunctionInPortPrecondition(drain) + addGraphEdge(tapVertex, drain.vertex, pipe, inputPort = drain.port, outputPort = UnlabeledPort) this } - def addEdge[In, Out](source: UndefinedSource[In], flow: ProcessorFlow[In, Out], sink: JunctionInPort[Out]): this.type = { - checkAddSourceSinkPrecondition(source) - checkJunctionInPortPrecondition(sink) - addGraphEdge(source, sink.vertex, flow, inputPort = sink.port, outputPort = UnlabeledPort) + private def addPipeDrainEdge[In, Out](tap: JunctionOutPort[In], pipe: Pipe[In, Out], drain: Drain[Out]): this.type = { + val drainVertex = DrainVertex(drain) + checkAddTapDrainPrecondition(drainVertex) + checkJunctionOutPortPrecondition(tap) + addGraphEdge(tap.vertex, drainVertex, pipe, inputPort = UnlabeledPort, outputPort = tap.port) this } - def addEdge[In, Out](source: JunctionOutPort[In], flow: ProcessorFlow[In, Out], sink: Sink[Out]): this.type = { - val sinkVertex = SinkVertex(sink) - checkAddSourceSinkPrecondition(sinkVertex) - checkJunctionOutPortPrecondition(source) - addGraphEdge(source.vertex, sinkVertex, flow, inputPort = UnlabeledPort, outputPort = source.port) - this - } - - def addEdge[In, Out](source: JunctionOutPort[In], flow: ProcessorFlow[In, Out], sink: UndefinedSink[Out]): this.type = { - checkAddSourceSinkPrecondition(sink) - checkJunctionOutPortPrecondition(source) - addGraphEdge(source.vertex, sink, flow, inputPort = UnlabeledPort, outputPort = source.port) - this - } - - def addEdge[In, Out](source: JunctionOutPort[In], flow: ProcessorFlow[In, Out], sink: JunctionInPort[Out]): this.type = { - checkJunctionOutPortPrecondition(source) - checkJunctionInPortPrecondition(sink) - addGraphEdge(source.vertex, sink.vertex, flow, inputPort = sink.port, outputPort = source.port) - this - } - - def addEdge[In, Out](flow: FlowWithSource[In, Out], sink: JunctionInPort[Out]): this.type = { - addEdge(flow.input, flow.withoutSource, sink) - this - } - - def addEdge[In, Out](source: JunctionOutPort[In], flow: FlowWithSink[In, Out]): this.type = { - addEdge(source, flow.withoutSink, flow.output) - this - } - - private def addGraphEdge[In, Out](from: Vertex, to: Vertex, flow: ProcessorFlow[In, Out], inputPort: Int, outputPort: Int): Unit = { - if (edgeQualifier == Int.MaxValue) throw new IllegalArgumentException(s"Too many edges") - val label = EdgeLabel(edgeQualifier)(flow.asInstanceOf[ProcessorFlow[Any, Any]], inputPort, outputPort) - graph.addLEdge(from, to)(label) - edgeQualifier += 1 - } - - def attachSink[Out](token: UndefinedSink[Out], sink: Sink[Out]): this.type = { - graph.find(token) match { - case Some(existing) ⇒ - require(existing.value.isInstanceOf[UndefinedSink[_]], s"Flow already attached to a sink [${existing.value}]") - val edge = existing.incoming.head - graph.remove(existing) - graph.addLEdge(edge.from.value, SinkVertex(sink))(edge.label) - case None ⇒ throw new IllegalArgumentException(s"No matching UndefinedSink [${token}]") + def addEdge[In, Out](tap: UndefinedTap[In], flow: Flow[In, Out], drain: JunctionInPort[Out]): this.type = { + checkAddTapDrainPrecondition(tap) + checkJunctionInPortPrecondition(drain) + flow match { + case pipe: Pipe[In, Out] ⇒ + addGraphEdge(tap, drain.vertex, pipe, inputPort = drain.port, outputPort = UnlabeledPort) + case _ ⇒ + throw new IllegalArgumentException(OnlyPipesErrorMessage) } this } - def attachSource[In](token: UndefinedSource[In], source: Source[In]): this.type = { + def addEdge[In, Out](tap: JunctionOutPort[In], flow: Flow[In, Out], drain: UndefinedDrain[Out]): this.type = { + checkAddTapDrainPrecondition(drain) + checkJunctionOutPortPrecondition(tap) + flow match { + case pipe: Pipe[In, Out] ⇒ + addGraphEdge(tap.vertex, drain, pipe, inputPort = UnlabeledPort, outputPort = tap.port) + case _ ⇒ + throw new IllegalArgumentException(OnlyPipesErrorMessage) + } + this + } + + def addEdge[In, Out](tap: JunctionOutPort[In], flow: Flow[In, Out], drain: JunctionInPort[Out]): this.type = { + checkJunctionOutPortPrecondition(tap) + checkJunctionInPortPrecondition(drain) + flow match { + case pipe: Pipe[In, Out] ⇒ + addGraphEdge(tap.vertex, drain.vertex, pipe, inputPort = drain.port, outputPort = tap.port) + case _ ⇒ + throw new IllegalArgumentException(OnlyPipesErrorMessage) + } + this + } + + def addEdge[In, Out](source: Source[In], flow: Flow[In, Out], drain: JunctionInPort[Out]): this.type = { + (source, flow) match { + case (tap: Tap[In], pipe: Pipe[In, Out]) ⇒ + addTapPipeEdge(tap, pipe, drain) + case (spipe: SourcePipe[In], pipe: Pipe[In, Out]) ⇒ + addEdge(spipe.connect(pipe), drain) + case _ ⇒ + throw new IllegalArgumentException(OnlyPipesErrorMessage) + } + this + } + + def addEdge[Out](source: Source[Out], drain: JunctionInPort[Out]): this.type = { + source match { + case tap: Tap[Out] ⇒ + addTapPipeEdge(tap, Pipe.empty[Out], drain) + case pipe: SourcePipe[Out] ⇒ + addTapPipeEdge(pipe.input, Pipe(pipe.ops), drain) + case _ ⇒ + throw new IllegalArgumentException(OnlyPipesErrorMessage) + } + this + } + + def addEdge[In, Out](tap: JunctionOutPort[In], sink: Sink[In]): this.type = { + sink match { + case drain: Drain[In] ⇒ addPipeDrainEdge(tap, Pipe.empty[In], drain) + case pipe: SinkPipe[In] ⇒ addPipeDrainEdge(tap, Pipe(pipe.ops), pipe.output) + case _ ⇒ throw new IllegalArgumentException(OnlyPipesErrorMessage) + } + this + } + + def addEdge[In, Out](tap: JunctionOutPort[In], flow: Flow[In, Out], sink: Sink[Out]): this.type = { + (flow, sink) match { + case (pipe: Pipe[In, Out], drain: Drain[Out]) ⇒ + addPipeDrainEdge(tap, pipe, drain) + case (pipe: Pipe[In, Out], spipe: SinkPipe[Out]) ⇒ + addEdge(tap, pipe.connect(spipe)) + case _ ⇒ throw new IllegalArgumentException(OnlyPipesErrorMessage) + } + this + } + + private def addGraphEdge[In, Out](from: Vertex, to: Vertex, pipe: Pipe[In, Out], inputPort: Int, outputPort: Int): Unit = { + if (edgeQualifier == Int.MaxValue) throw new IllegalArgumentException(s"Too many edges") + val label = EdgeLabel(edgeQualifier)(pipe.asInstanceOf[Pipe[Any, Nothing]], inputPort, outputPort) + graph.addLEdge(from, to)(label) + edgeQualifier += 1 + } + + def attachDrain[Out](token: UndefinedDrain[Out], drain: Drain[Out]): this.type = { + graph.find(token) match { + case Some(existing) ⇒ + val edge = existing.incoming.head + graph.remove(existing) + graph.addLEdge(edge.from.value, DrainVertex(drain))(edge.label) + case None ⇒ throw new IllegalArgumentException(s"No matching UndefinedDrain [${token}]") + } + this + } + + def attachTap[In](token: UndefinedTap[In], tap: Tap[In]): this.type = { graph.find(token) match { case Some(existing) ⇒ - require(existing.value.isInstanceOf[UndefinedSource[_]], s"Flow already attached to a source [${existing.value}]") val edge = existing.outgoing.head graph.remove(existing) - graph.addLEdge(SourceVertex(source), edge.to.value)(edge.label) - case None ⇒ throw new IllegalArgumentException(s"No matching UndefinedSource [${token}]") + graph.addLEdge(TapVertex(tap), edge.to.value)(edge.label) + case None ⇒ throw new IllegalArgumentException(s"No matching UndefinedTap [${token}]") } this } @@ -485,7 +537,7 @@ class FlowGraphBuilder private (graph: Graph[FlowGraphInternal.Vertex, LkDiEdge] cyclesAllowed = true } - private def checkAddSourceSinkPrecondition(node: Vertex): Unit = + private def checkAddTapDrainPrecondition(node: Vertex): Unit = require(graph.find(node) == None, s"[$node] instance is already used in this flow graph") private def checkJunctionInPortPrecondition(junction: JunctionInPort[_]): Unit = { @@ -534,8 +586,10 @@ class FlowGraphBuilder private (graph: Graph[FlowGraphInternal.Vertex, LkDiEdge] } //convert it to an immutable.Graph - private def immutableGraph(): ImmutableGraph[Vertex, LkDiEdge] = - ImmutableGraph.from(edges = graph.edges.map(e ⇒ LkDiEdge(e.from.value, e.to.value)(e.label)).toIterable) + private def immutableGraph(): ImmutableGraph[Vertex, FlowGraphInternal.EdgeType] = { + val edges = graph.edges.map(e ⇒ LkDiEdge(e.from.value, e.to.value)(e.label)) + ImmutableGraph.from(edges = edges: Iterable[FlowGraphInternal.EdgeType[FlowGraphInternal.Vertex]]) + } private def checkPartialBuildPreconditions(): Unit = { if (!cyclesAllowed) graph.findCycle match { @@ -545,18 +599,18 @@ class FlowGraphBuilder private (graph: Graph[FlowGraphInternal.Vertex, LkDiEdge] } private def checkBuildPreconditions(): Unit = { - val undefinedSourcesSinks = graph.nodes.filter { + val undefinedTapsDrains = graph.nodes.filter { _.value match { - case _: UndefinedSource[_] | _: UndefinedSink[_] ⇒ true - case x ⇒ false + case _: UndefinedTap[_] | _: UndefinedDrain[_] ⇒ true + case x ⇒ false } } - if (undefinedSourcesSinks.nonEmpty) { - val formatted = undefinedSourcesSinks.map(n ⇒ n.value match { - case u: UndefinedSource[_] ⇒ s"$u -> ${n.outgoing.head.label} -> ${n.outgoing.head.to}" - case u: UndefinedSink[_] ⇒ s"${n.incoming.head.from} -> ${n.incoming.head.label} -> $u" + if (undefinedTapsDrains.nonEmpty) { + val formatted = undefinedTapsDrains.map(n ⇒ n.value match { + case u: UndefinedTap[_] ⇒ s"$u -> ${n.outgoing.head.label} -> ${n.outgoing.head.to}" + case u: UndefinedDrain[_] ⇒ s"${n.incoming.head.from} -> ${n.incoming.head.label} -> $u" }) - throw new IllegalArgumentException("Undefined sources or sinks: " + formatted.mkString(", ")) + throw new IllegalArgumentException("Undefined taps or drains: " + formatted.mkString(", ")) } graph.nodes.foreach { node ⇒ @@ -580,9 +634,9 @@ class FlowGraphBuilder private (graph: Graph[FlowGraphInternal.Vertex, LkDiEdge] require(graph.nonEmpty, "Graph must not be empty") require(graph.exists(graph having ((node = { n ⇒ n.isLeaf && n.diSuccessors.isEmpty }))), - "Graph must have at least one sink") + "Graph must have at least one drain") require(graph.exists(graph having ((node = { n ⇒ n.isLeaf && n.diPredecessors.isEmpty }))), - "Graph must have at least one source") + "Graph must have at least one tap") require(graph.isConnected, "Graph must be connected") } @@ -600,12 +654,12 @@ object FlowGraph { * Build a [[FlowGraph]] from scratch. */ def apply(block: FlowGraphBuilder ⇒ Unit): FlowGraph = - apply(ImmutableGraph.empty[FlowGraphInternal.Vertex, LkDiEdge])(block) + apply(ImmutableGraph.empty[FlowGraphInternal.Vertex, FlowGraphInternal.EdgeType])(block) /** * Continue building a [[FlowGraph]] from an existing `PartialFlowGraph`. - * For example you can attach undefined sources and sinks with - * [[FlowGraphBuilder#attachSource]] and [[FlowGraphBuilder#attachSink]] + * For example you can attach undefined taps and drains with + * [[FlowGraphBuilder#attachTap]] and [[FlowGraphBuilder#attachDrain]] */ def apply(partialFlowGraph: PartialFlowGraph)(block: FlowGraphBuilder ⇒ Unit): FlowGraph = apply(partialFlowGraph.graph)(block) @@ -617,7 +671,7 @@ object FlowGraph { def apply(flowGraph: FlowGraph)(block: FlowGraphBuilder ⇒ Unit): FlowGraph = apply(flowGraph.graph)(block) - private def apply(graph: ImmutableGraph[FlowGraphInternal.Vertex, LkDiEdge])(block: FlowGraphBuilder ⇒ Unit): FlowGraph = { + private def apply(graph: ImmutableGraph[FlowGraphInternal.Vertex, FlowGraphInternal.EdgeType])(block: FlowGraphBuilder ⇒ Unit): FlowGraph = { val builder = new FlowGraphBuilder(graph) block(builder) builder.build() @@ -630,23 +684,23 @@ object FlowGraph { * Build a `FlowGraph` by starting with one of the `apply` methods in * in [[FlowGraph$ companion object]]. Syntactic sugar is provided by [[FlowGraphImplicits]]. */ -class FlowGraph private[akka] (private[akka] val graph: ImmutableGraph[FlowGraphInternal.Vertex, LkDiEdge]) { +class FlowGraph private[akka] (private[akka] val graph: ImmutableGraph[FlowGraphInternal.Vertex, FlowGraphInternal.EdgeType]) { import FlowGraphInternal._ /** - * Materialize the `FlowGraph` and attach all sinks and sources. + * Materialize the `FlowGraph` and attach all drains and taps. */ - def run()(implicit materializer: FlowMaterializer): MaterializedFlowGraph = { + def run()(implicit materializer: FlowMaterializer): MaterializedPipeGraph = { import scalax.collection.GraphTraversal._ - // start with sinks + // start with drains val startingNodes = graph.nodes.filter(n ⇒ n.isLeaf && n.diSuccessors.isEmpty) case class Memo(visited: Set[graph.EdgeT] = Set.empty, downstreamSubscriber: Map[graph.EdgeT, Subscriber[Any]] = Map.empty, upstreamPublishers: Map[graph.EdgeT, Publisher[Any]] = Map.empty, - sources: Map[SourceVertex, FlowWithSink[Any, Any]] = Map.empty, - materializedSinks: Map[SinkWithKey[_, _], Any] = Map.empty) + taps: Map[TapVertex, SinkPipe[Any]] = Map.empty, + materializedDrains: Map[DrainWithKey[_, _], Any] = Map.empty) val result = startingNodes.foldLeft(Memo()) { case (memo, start) ⇒ @@ -658,38 +712,38 @@ class FlowGraph private[akka] (private[akka] val graph: ImmutableGraph[FlowGraph if (memo.visited(edge)) { memo } else { - val flow = edge.label.asInstanceOf[EdgeLabel].flow + val pipe = edge.label.pipe - // returns the materialized sink, if any - def connectToDownstream(publisher: Publisher[Any]): Option[(SinkWithKey[_, _], Any)] = { - val f = flow.withSource(PublisherSource(publisher)) + // returns the materialized drain, if any + def connectToDownstream(publisher: Publisher[Any]): Option[(DrainWithKey[_, _], Any)] = { + val f = pipe.withTap(PublisherTap(publisher)) edge.to.value match { - case SinkVertex(sink: SinkWithKey[_, _]) ⇒ - val mf = f.withSink(sink.asInstanceOf[Sink[Any]]).run() - Some(sink -> mf.getSinkFor(sink)) - case SinkVertex(sink) ⇒ - f.withSink(sink.asInstanceOf[Sink[Any]]).run() + case DrainVertex(drain: DrainWithKey[_, _]) ⇒ + val mf = f.withDrain(drain).run() + Some(drain -> mf.getDrainFor(drain)) + case DrainVertex(drain) ⇒ + f.withDrain(drain).run() None case _ ⇒ - f.withSink(SubscriberSink(memo.downstreamSubscriber(edge))).run() + f.withDrain(SubscriberDrain(memo.downstreamSubscriber(edge))).run() None } } edge.from.value match { - case src: SourceVertex ⇒ - val f = flow.withSink(SubscriberSink(memo.downstreamSubscriber(edge))) - // connect the source with the flow later + case src: TapVertex ⇒ + val f = pipe.withDrain(SubscriberDrain(memo.downstreamSubscriber(edge))) + // connect the tap with the pipe later memo.copy(visited = memo.visited + edge, - sources = memo.sources.updated(src, f)) + taps = memo.taps.updated(src, f)) case v: InternalVertex ⇒ if (memo.upstreamPublishers.contains(edge)) { // vertex already materialized - val materializedSink = connectToDownstream(memo.upstreamPublishers(edge)) + val materializedDrain = connectToDownstream(memo.upstreamPublishers(edge)) memo.copy( visited = memo.visited + edge, - materializedSinks = memo.materializedSinks ++ materializedSink) + materializedDrains = memo.materializedDrains ++ materializedDrain) } else { val op = v.astNode @@ -697,16 +751,16 @@ class FlowGraph private[akka] (private[akka] val graph: ImmutableGraph[FlowGraph materializer.materializeJunction[Any, Any](op, edge.from.inDegree, edge.from.outDegree) // TODO: Check for gaps in port numbers val edgeSubscribers = - edge.from.incoming.toSeq.sortBy(_.label.asInstanceOf[EdgeLabel].inputPort).zip(subscribers) + edge.from.incoming.toSeq.sortBy(_.label.inputPort).zip(subscribers) val edgePublishers = - edge.from.outgoing.toSeq.sortBy(_.label.asInstanceOf[EdgeLabel].outputPort).zip(publishers).toMap + edge.from.outgoing.toSeq.sortBy(_.label.outputPort).zip(publishers).toMap val publisher = edgePublishers(edge) - val materializedSink = connectToDownstream(publisher) + val materializedDrain = connectToDownstream(publisher) memo.copy( visited = memo.visited + edge, downstreamSubscriber = memo.downstreamSubscriber ++ edgeSubscribers, upstreamPublishers = memo.upstreamPublishers ++ edgePublishers, - materializedSinks = memo.materializedSinks ++ materializedSink) + materializedDrains = memo.materializedDrains ++ materializedDrain) } } @@ -716,17 +770,17 @@ class FlowGraph private[akka] (private[akka] val graph: ImmutableGraph[FlowGraph } - // connect all input sources as the last thing - val materializedSources = result.sources.foldLeft(Map.empty[SourceWithKey[_, _], Any]) { - case (acc, (SourceVertex(src), flow)) ⇒ - val mf = flow.withSource(src).run() + // connect all input taps as the last thing + val materializedTaps = result.taps.foldLeft(Map.empty[TapWithKey[_, _], Any]) { + case (acc, (TapVertex(src), pipe)) ⇒ + val mf = pipe.withTap(src).run() src match { - case srcKey: SourceWithKey[_, _] ⇒ acc.updated(srcKey, mf.getSourceFor(srcKey)) - case _ ⇒ acc + case srcKey: TapWithKey[_, _] ⇒ acc.updated(srcKey, mf.getTapFor(srcKey)) + case _ ⇒ acc } } - new MaterializedFlowGraph(materializedSources, result.materializedSinks) + new MaterializedPipeGraph(materializedTaps, result.materializedDrains) } } @@ -742,7 +796,7 @@ object PartialFlowGraph { * Build a [[PartialFlowGraph]] from scratch. */ def apply(block: FlowGraphBuilder ⇒ Unit): PartialFlowGraph = - apply(ImmutableGraph.empty[FlowGraphInternal.Vertex, LkDiEdge])(block) + apply(ImmutableGraph.empty[FlowGraphInternal.Vertex, FlowGraphInternal.EdgeType])(block) /** * Continue building a [[PartialFlowGraph]] from an existing `PartialFlowGraph`. @@ -756,7 +810,7 @@ object PartialFlowGraph { def apply(flowGraph: FlowGraph)(block: FlowGraphBuilder ⇒ Unit): PartialFlowGraph = apply(flowGraph.graph)(block) - private def apply(graph: ImmutableGraph[FlowGraphInternal.Vertex, LkDiEdge])(block: FlowGraphBuilder ⇒ Unit): PartialFlowGraph = { + private def apply(graph: ImmutableGraph[FlowGraphInternal.Vertex, FlowGraphInternal.EdgeType])(block: FlowGraphBuilder ⇒ Unit): PartialFlowGraph = { val builder = new FlowGraphBuilder(graph) block(builder) builder.partialBuild() @@ -765,53 +819,53 @@ object PartialFlowGraph { } /** - * `PartialFlowGraph` may have sources and sinks that are not attached, and it can therefore not + * `PartialFlowGraph` may have taps and drains that are not attached, and it can therefore not * be `run` until those are attached. * * Build a `PartialFlowGraph` by starting with one of the `apply` methods in * in [[FlowGraph$ companion object]]. Syntactic sugar is provided by [[FlowGraphImplicits]]. */ -class PartialFlowGraph private[akka] (private[akka] val graph: ImmutableGraph[FlowGraphInternal.Vertex, LkDiEdge]) { +class PartialFlowGraph private[akka] (private[akka] val graph: ImmutableGraph[FlowGraphInternal.Vertex, FlowGraphInternal.EdgeType]) { import FlowGraphInternal._ - def undefinedSources: Set[UndefinedSource[_]] = - graph.nodes.collect { - case n if n.value.isInstanceOf[UndefinedSource[_]] ⇒ n.value.asInstanceOf[UndefinedSource[_]] - }(collection.breakOut) + def undefinedTaps: Set[UndefinedTap[_]] = + graph.nodes.iterator.map(_.value).collect { + case n: UndefinedTap[_] ⇒ n + }.toSet - def undefinedSinks: Set[UndefinedSink[_]] = - graph.nodes.collect { - case n if n.value.isInstanceOf[UndefinedSink[_]] ⇒ n.value.asInstanceOf[UndefinedSink[_]] - }(collection.breakOut) + def undefinedDrains: Set[UndefinedDrain[_]] = + graph.nodes.iterator.map(_.value).collect { + case n: UndefinedDrain[_] ⇒ n + }.toSet } /** * Returned by [[FlowGraph#run]] and can be used as parameter to the - * accessor method to retrieve the materialized `Source` or `Sink`, e.g. - * [[SubscriberSource#subscriber]] or [[PublisherSink#publisher]]. + * accessor method to retrieve the materialized `Tap` or `Drain`, e.g. + * [[SubscriberTap#subscriber]] or [[PublisherDrain#publisher]]. */ -class MaterializedFlowGraph(materializedSources: Map[SourceWithKey[_, _], Any], materializedSinks: Map[SinkWithKey[_, _], Any]) - extends MaterializedSource with MaterializedSink { +class MaterializedPipeGraph(materializedTaps: Map[TapWithKey[_, _], Any], materializedDrains: Map[DrainWithKey[_, _], Any]) + extends MaterializedTap with MaterializedDrain { /** - * Do not call directly. Use accessor method in the concrete `Source`, e.g. [[SubscriberSource#subscriber]]. + * Do not call directly. Use accessor method in the concrete `Tap`, e.g. [[SubscriberTap#subscriber]]. */ - override def getSourceFor[T](key: SourceWithKey[_, T]): T = - materializedSources.get(key) match { - case Some(matSource) ⇒ matSource.asInstanceOf[T] + override def getTapFor[T](key: TapWithKey[_, T]): T = + materializedTaps.get(key) match { + case Some(matTap) ⇒ matTap.asInstanceOf[T] case None ⇒ - throw new IllegalArgumentException(s"Source key [$key] doesn't exist in this flow graph") + throw new IllegalArgumentException(s"Tap key [$key] doesn't exist in this flow graph") } /** - * Do not call directly. Use accessor method in the concrete `Sink`, e.g. [[PublisherSink#publisher]]. + * Do not call directly. Use accessor method in the concrete `Drain`, e.g. [[PublisherDrain#publisher]]. */ - def getSinkFor[T](key: SinkWithKey[_, T]): T = - materializedSinks.get(key) match { - case Some(matSink) ⇒ matSink.asInstanceOf[T] + def getDrainFor[T](key: DrainWithKey[_, T]): T = + materializedDrains.get(key) match { + case Some(matDrain) ⇒ matDrain.asInstanceOf[T] case None ⇒ - throw new IllegalArgumentException(s"Sink key [$key] doesn't exist in this flow graph") + throw new IllegalArgumentException(s"Drain key [$key] doesn't exist in this flow graph") } } @@ -819,82 +873,70 @@ class MaterializedFlowGraph(materializedSources: Map[SourceWithKey[_, _], Any], * Implicit conversions that provides syntactic sugar for building flow graphs. */ object FlowGraphImplicits { - implicit class SourceOps[In](val source: Source[In]) extends AnyVal { - def ~>[Out](flow: ProcessorFlow[In, Out])(implicit builder: FlowGraphBuilder): SourceNextStep[In, Out] = { - new SourceNextStep(source, flow, builder) - } - def ~>(sink: JunctionInPort[In])(implicit builder: FlowGraphBuilder): JunctionOutPort[sink.NextT] = { - builder.addEdge(source, ProcessorFlow.empty[In], sink) - sink.next - } - } - - class SourceNextStep[In, Out](source: Source[In], flow: ProcessorFlow[In, Out], builder: FlowGraphBuilder) { - def ~>(sink: JunctionInPort[Out]): JunctionOutPort[sink.NextT] = { - builder.addEdge(source, flow, sink) - sink.next + class SourceNextStep[In, Out](source: Source[In], flow: Flow[In, Out], builder: FlowGraphBuilder) { + def ~>(drain: JunctionInPort[Out]): JunctionOutPort[drain.NextT] = { + builder.addEdge(source, flow, drain) + drain.next } } implicit class JunctionOps[In](val junction: JunctionOutPort[In]) extends AnyVal { - def ~>[Out](flow: ProcessorFlow[In, Out])(implicit builder: FlowGraphBuilder): JunctionNextStep[In, Out] = { + def ~>[Out](flow: Flow[In, Out])(implicit builder: FlowGraphBuilder): JunctionNextStep[In, Out] = new JunctionNextStep(junction, flow, builder) + + def ~>(drain: UndefinedDrain[In])(implicit builder: FlowGraphBuilder): Unit = + builder.addEdge(junction, Pipe.empty[In], drain) + + def ~>(drain: JunctionInPort[In])(implicit builder: FlowGraphBuilder): JunctionOutPort[drain.NextT] = { + builder.addEdge(junction, Pipe.empty[In], drain) + drain.next } - def ~>(sink: Sink[In])(implicit builder: FlowGraphBuilder): Unit = - builder.addEdge(junction, ProcessorFlow.empty[In], sink) - - def ~>(sink: UndefinedSink[In])(implicit builder: FlowGraphBuilder): Unit = - builder.addEdge(junction, ProcessorFlow.empty[In], sink) - - def ~>(sink: JunctionInPort[In])(implicit builder: FlowGraphBuilder): JunctionOutPort[sink.NextT] = { - builder.addEdge(junction, ProcessorFlow.empty[In], sink) - sink.next - } - - def ~>(flow: FlowWithSink[In, _])(implicit builder: FlowGraphBuilder): Unit = - builder.addEdge(junction, flow) + def ~>(sink: Sink[In])(implicit builder: FlowGraphBuilder): Unit = builder.addEdge(junction, sink) } - class JunctionNextStep[In, Out](junction: JunctionOutPort[In], flow: ProcessorFlow[In, Out], builder: FlowGraphBuilder) { - def ~>(sink: JunctionInPort[Out]): JunctionOutPort[sink.NextT] = { - builder.addEdge(junction, flow, sink) - sink.next + class JunctionNextStep[In, Out](junction: JunctionOutPort[In], flow: Flow[In, Out], builder: FlowGraphBuilder) { + def ~>(drain: JunctionInPort[Out]): JunctionOutPort[drain.NextT] = { + builder.addEdge(junction, flow, drain) + drain.next } def ~>(sink: Sink[Out]): Unit = { builder.addEdge(junction, flow, sink) } - def ~>(sink: UndefinedSink[Out]): Unit = { - builder.addEdge(junction, flow, sink) + def ~>(drain: UndefinedDrain[Out]): Unit = { + builder.addEdge(junction, flow, drain) } } - implicit class FlowWithSourceOps[In, Out](val flow: FlowWithSource[In, Out]) extends AnyVal { - def ~>(sink: JunctionInPort[Out])(implicit builder: FlowGraphBuilder): JunctionOutPort[sink.NextT] = { - builder.addEdge(flow, sink) - sink.next + implicit class SourceOps[Out](val source: Source[Out]) extends AnyVal { + + def ~>[O](flow: Flow[Out, O])(implicit builder: FlowGraphBuilder): SourceNextStep[Out, O] = + new SourceNextStep(source, flow, builder) + + def ~>(drain: JunctionInPort[Out])(implicit builder: FlowGraphBuilder): JunctionOutPort[drain.NextT] = { + builder.addEdge(source, drain) + drain.next } } - implicit class UndefinedSourceOps[In](val source: UndefinedSource[In]) extends AnyVal { - def ~>[Out](flow: ProcessorFlow[In, Out])(implicit builder: FlowGraphBuilder): UndefinedSourceNextStep[In, Out] = { - new UndefinedSourceNextStep(source, flow, builder) - } + implicit class UndefinedTapOps[In](val tap: UndefinedTap[In]) extends AnyVal { + def ~>[Out](flow: Flow[In, Out])(implicit builder: FlowGraphBuilder): UndefinedTapNextStep[In, Out] = + new UndefinedTapNextStep(tap, flow, builder) - def ~>(sink: JunctionInPort[In])(implicit builder: FlowGraphBuilder): JunctionOutPort[sink.NextT] = { - builder.addEdge(source, ProcessorFlow.empty[In], sink) - sink.next + def ~>(drain: JunctionInPort[In])(implicit builder: FlowGraphBuilder): JunctionOutPort[drain.NextT] = { + builder.addEdge(tap, Pipe.empty[In], drain) + drain.next } } - class UndefinedSourceNextStep[In, Out](source: UndefinedSource[In], flow: ProcessorFlow[In, Out], builder: FlowGraphBuilder) { - def ~>(sink: JunctionInPort[Out]): JunctionOutPort[sink.NextT] = { - builder.addEdge(source, flow, sink) - sink.next + class UndefinedTapNextStep[In, Out](tap: UndefinedTap[In], flow: Flow[In, Out], builder: FlowGraphBuilder) { + def ~>(drain: JunctionInPort[Out]): JunctionOutPort[drain.NextT] = { + builder.addEdge(tap, flow, drain) + drain.next } } diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl2/FlowMaterializer.scala b/akka-stream/src/main/scala/akka/stream/scaladsl2/FlowMaterializer.scala index 013501cbff..97802b49cb 100644 --- a/akka-stream/src/main/scala/akka/stream/scaladsl2/FlowMaterializer.scala +++ b/akka-stream/src/main/scala/akka/stream/scaladsl2/FlowMaterializer.scala @@ -138,7 +138,7 @@ abstract class FlowMaterializer(val settings: MaterializerSettings) { * stream. The result can be highly implementation specific, ranging from * local actor chains to remote-deployed processing networks. */ - def materialize[In, Out](source: Source[In], sink: Sink[Out], ops: List[Ast.AstNode]): MaterializedFlow + def materialize[In, Out](tap: Tap[In], drain: Drain[Out], ops: List[Ast.AstNode]): MaterializedPipe /** * Create publishers and subscribers for fan-in and fan-out operations. diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl2/Pipe.scala b/akka-stream/src/main/scala/akka/stream/scaladsl2/Pipe.scala new file mode 100644 index 0000000000..9e1504c36a --- /dev/null +++ b/akka-stream/src/main/scala/akka/stream/scaladsl2/Pipe.scala @@ -0,0 +1,330 @@ +/** + * Copyright (C) 2014 Typesafe Inc. + */ +package akka.stream.scaladsl2 + +import scala.collection.immutable +import akka.stream.impl2.Ast._ +import org.reactivestreams._ +import scala.concurrent.Future +import akka.stream.Transformer +import scala.concurrent.duration.FiniteDuration +import scala.concurrent.duration.Duration +import akka.util.Collections.EmptyImmutableSeq +import akka.stream.TimerTransformer +import akka.stream.OverflowStrategy + +import scala.annotation.unchecked.uncheckedVariance +import scala.language.higherKinds +import scala.language.existentials + +private[scaladsl2] object PipeOps { + private case object TakeWithinTimerKey + private case object DropWithinTimerKey + private case object GroupedWithinTimerKey + + private val takeCompletedTransformer: Transformer[Any, Any] = new Transformer[Any, Any] { + override def onNext(elem: Any) = Nil + override def isComplete = true + } + + private val identityTransformer: Transformer[Any, Any] = new Transformer[Any, Any] { + override def onNext(elem: Any) = List(elem) + } +} + +/** + * Scala API: Operations offered by flows with a free output side: the DSL flows left-to-right only. + */ +private[scaladsl2] trait PipeOps[+Out] extends FlowOps[Out] { + import PipeOps._ + type Repr[+O] + + // Storing ops in reverse order + protected def andThen[U](op: AstNode): Repr[U] + + override def map[T](f: Out ⇒ T): Repr[T] = + transform("map", () ⇒ new Transformer[Out, T] { + override def onNext(in: Out) = List(f(in)) + }) + + override def mapConcat[T](f: Out ⇒ immutable.Seq[T]): Repr[T] = + transform("mapConcat", () ⇒ new Transformer[Out, T] { + override def onNext(in: Out) = f(in) + }) + + override def mapAsync[T](f: Out ⇒ Future[T]): Repr[T] = + andThen(MapAsync(f.asInstanceOf[Any ⇒ Future[Any]])) + + override def mapAsyncUnordered[T](f: Out ⇒ Future[T]): Repr[T] = + andThen(MapAsyncUnordered(f.asInstanceOf[Any ⇒ Future[Any]])) + + override def filter(p: Out ⇒ Boolean): Repr[Out] = + transform("filter", () ⇒ new Transformer[Out, Out] { + override def onNext(in: Out) = if (p(in)) List(in) else Nil + }) + + override def collect[T](pf: PartialFunction[Out, T]): Repr[T] = + transform("collect", () ⇒ new Transformer[Out, T] { + override def onNext(in: Out) = if (pf.isDefinedAt(in)) List(pf(in)) else Nil + }) + + override def grouped(n: Int): Repr[immutable.Seq[Out]] = { + require(n > 0, "n must be greater than 0") + transform("grouped", () ⇒ new Transformer[Out, immutable.Seq[Out]] { + var buf: Vector[Out] = Vector.empty + override def onNext(in: Out) = { + buf :+= in + if (buf.size == n) { + val group = buf + buf = Vector.empty + List(group) + } else + Nil + } + override def onTermination(e: Option[Throwable]) = if (buf.isEmpty) Nil else List(buf) + }) + } + + override def groupedWithin(n: Int, d: FiniteDuration): Repr[immutable.Seq[Out]] = { + require(n > 0, "n must be greater than 0") + require(d > Duration.Zero) + timerTransform("groupedWithin", () ⇒ new TimerTransformer[Out, immutable.Seq[Out]] { + schedulePeriodically(GroupedWithinTimerKey, d) + var buf: Vector[Out] = Vector.empty + + override def onNext(in: Out) = { + buf :+= in + if (buf.size == n) { + // start new time window + schedulePeriodically(GroupedWithinTimerKey, d) + emitGroup() + } else Nil + } + override def onTermination(e: Option[Throwable]) = if (buf.isEmpty) Nil else List(buf) + override def onTimer(timerKey: Any) = emitGroup() + private def emitGroup(): immutable.Seq[immutable.Seq[Out]] = + if (buf.isEmpty) EmptyImmutableSeq + else { + val group = buf + buf = Vector.empty + List(group) + } + }) + } + + override def drop(n: Int): Repr[Out] = + transform("drop", () ⇒ new Transformer[Out, Out] { + var delegate: Transformer[Out, Out] = + if (n <= 0) identityTransformer.asInstanceOf[Transformer[Out, Out]] + else new Transformer[Out, Out] { + var c = n + override def onNext(in: Out) = { + c -= 1 + if (c == 0) + delegate = identityTransformer.asInstanceOf[Transformer[Out, Out]] + Nil + } + } + + override def onNext(in: Out) = delegate.onNext(in) + }) + + override def dropWithin(d: FiniteDuration): Repr[Out] = + timerTransform("dropWithin", () ⇒ new TimerTransformer[Out, Out] { + scheduleOnce(DropWithinTimerKey, d) + + var delegate: Transformer[Out, Out] = + new Transformer[Out, Out] { + override def onNext(in: Out) = Nil + } + + override def onNext(in: Out) = delegate.onNext(in) + override def onTimer(timerKey: Any) = { + delegate = identityTransformer.asInstanceOf[Transformer[Out, Out]] + Nil + } + }) + + override def take(n: Int): Repr[Out] = + transform("take", () ⇒ new Transformer[Out, Out] { + var delegate: Transformer[Out, Out] = + if (n <= 0) takeCompletedTransformer.asInstanceOf[Transformer[Out, Out]] + else new Transformer[Out, Out] { + var c = n + override def onNext(in: Out) = { + c -= 1 + if (c == 0) + delegate = takeCompletedTransformer.asInstanceOf[Transformer[Out, Out]] + List(in) + } + } + + override def onNext(in: Out) = delegate.onNext(in) + override def isComplete = delegate.isComplete + }) + + override def takeWithin(d: FiniteDuration): Repr[Out] = + timerTransform("takeWithin", () ⇒ new TimerTransformer[Out, Out] { + scheduleOnce(TakeWithinTimerKey, d) + + var delegate: Transformer[Out, Out] = identityTransformer.asInstanceOf[Transformer[Out, Out]] + + override def onNext(in: Out) = delegate.onNext(in) + override def isComplete = delegate.isComplete + override def onTimer(timerKey: Any) = { + delegate = takeCompletedTransformer.asInstanceOf[Transformer[Out, Out]] + Nil + } + }) + + override def conflate[S](seed: Out ⇒ S, aggregate: (S, Out) ⇒ S): Repr[S] = + andThen(Conflate(seed.asInstanceOf[Any ⇒ Any], aggregate.asInstanceOf[(Any, Any) ⇒ Any])) + + override def expand[S, U](seed: Out ⇒ S, extrapolate: S ⇒ (U, S)): Repr[U] = + andThen(Expand(seed.asInstanceOf[Any ⇒ Any], extrapolate.asInstanceOf[Any ⇒ (Any, Any)])) + + override def buffer(size: Int, overflowStrategy: OverflowStrategy): Repr[Out] = { + require(size > 0, s"Buffer size must be larger than zero but was [$size]") + andThen(Buffer(size, overflowStrategy)) + } + + override def transform[T](name: String, mkTransformer: () ⇒ Transformer[Out, T]): Repr[T] = { + andThen(Transform(name, mkTransformer.asInstanceOf[() ⇒ Transformer[Any, Any]])) + } + + override def prefixAndTail[U >: Out](n: Int): Repr[(immutable.Seq[Out], Source[U])] = + andThen(PrefixAndTail(n)) + + override def groupBy[K, U >: Out](f: Out ⇒ K): Repr[(K, Source[U])] = + andThen(GroupBy(f.asInstanceOf[Any ⇒ Any])) + + override def splitWhen[U >: Out](p: Out ⇒ Boolean): Repr[Source[U]] = + andThen(SplitWhen(p.asInstanceOf[Any ⇒ Boolean])) + + override def flatten[U](strategy: FlattenStrategy[Out, U]): Repr[U] = strategy match { + case _: FlattenStrategy.Concat[Out] ⇒ andThen(ConcatAll) + case _ ⇒ throw new IllegalArgumentException(s"Unsupported flattening strategy [${strategy.getClass.getSimpleName}]") + } + + override def timerTransform[U](name: String, mkTransformer: () ⇒ TimerTransformer[Out, U]): Repr[U] = + andThen(TimerTransform(name, mkTransformer.asInstanceOf[() ⇒ TimerTransformer[Any, Any]])) +} + +private[scaladsl2] object Pipe { + private val emptyInstance = Pipe[Any, Any](ops = Nil) + def empty[T]: Pipe[T, T] = emptyInstance.asInstanceOf[Pipe[T, T]] + + val OnlyPipesErrorMessage = "Only pipes are supported currently!" +} + +/** + * Flow with one open input and one open output.. + */ +private[scaladsl2] final case class Pipe[-In, +Out](ops: List[AstNode]) extends Flow[In, Out] with PipeOps[Out] { + override type Repr[+O] = Pipe[In @uncheckedVariance, O] + + override protected def andThen[U](op: AstNode): Repr[U] = this.copy(ops = op :: ops) + + def withDrain(out: Drain[Out]): SinkPipe[In] = SinkPipe(out, ops) + + def withTap(in: Tap[In]): SourcePipe[Out] = SourcePipe(in, ops) + + override def connect[T](flow: Flow[Out, T]): Flow[In, T] = flow match { + case p: Pipe[T, In] ⇒ Pipe(p.ops ++: ops) + case _ ⇒ throw new IllegalArgumentException(Pipe.OnlyPipesErrorMessage) + } + + override def connect(sink: Sink[Out]): Sink[In] = sink match { + case sp: SinkPipe[Out] ⇒ sp.prependPipe(this) + case d: Drain[Out] ⇒ this.withDrain(d) + case _ ⇒ throw new IllegalArgumentException(Pipe.OnlyPipesErrorMessage) + } +} + +/** + * Pipe with open input and attached output. Can be used as a `Subscriber`. + */ +private[scaladsl2] final case class SinkPipe[-In](output: Drain[_], ops: List[AstNode]) extends Sink[In] { + + def withTap(in: Tap[In]): RunnablePipe = RunnablePipe(in, output, ops) + + def prependPipe[T](pipe: Pipe[T, In]): SinkPipe[T] = SinkPipe(output, ops ::: pipe.ops) + + override def toSubscriber()(implicit materializer: FlowMaterializer): Subscriber[In @uncheckedVariance] = { + val subIn = SubscriberTap[In]() + val mf = withTap(subIn).run() + subIn.subscriber(mf) + } +} + +/** + * Pipe with open output and attached input. Can be used as a `Publisher`. + */ +private[scaladsl2] final case class SourcePipe[+Out](input: Tap[_], ops: List[AstNode]) extends Source[Out] with PipeOps[Out] { + override type Repr[+O] = SourcePipe[O] + + override protected def andThen[U](op: AstNode): Repr[U] = SourcePipe(input, op :: ops) + + def withDrain(out: Drain[Out]): RunnablePipe = RunnablePipe(input, out, ops) + + def appendPipe[T](pipe: Pipe[Out, T]): SourcePipe[T] = SourcePipe(input, pipe.ops ++: ops) + + override def connect[T](flow: Flow[Out, T]): Source[T] = flow match { + case p: Pipe[Out, T] ⇒ appendPipe(p) + case _ ⇒ throw new IllegalArgumentException(Pipe.OnlyPipesErrorMessage) + } + + override def connect(sink: Sink[Out]): RunnableFlow = sink match { + case sp: SinkPipe[Out] ⇒ RunnablePipe(input, sp.output, sp.ops ++: ops) + case d: Drain[Out] ⇒ this.withDrain(d) + case _ ⇒ throw new IllegalArgumentException(Pipe.OnlyPipesErrorMessage) + } + + override def toPublisher()(implicit materializer: FlowMaterializer): Publisher[Out @uncheckedVariance] = { + val pubOut = PublisherDrain[Out] + val mf = withDrain(pubOut).run() + pubOut.publisher(mf) + } + + override def toFanoutPublisher(initialBufferSize: Int, maximumBufferSize: Int)(implicit materializer: FlowMaterializer): Publisher[Out @uncheckedVariance] = { + val pubOut = PublisherDrain.withFanout[Out](initialBufferSize, maximumBufferSize) + val mf = withDrain(pubOut).run() + pubOut.publisher(mf) + } + + override def publishTo(subscriber: Subscriber[Out @uncheckedVariance])(implicit materializer: FlowMaterializer): Unit = + toPublisher().subscribe(subscriber) + + override def consume()(implicit materializer: FlowMaterializer): Unit = + withDrain(BlackholeDrain).run() +} + +/** + * Pipe with attached input and output, can be executed. + */ +private[scaladsl2] final case class RunnablePipe(input: Tap[_], output: Drain[_], ops: List[AstNode]) extends RunnableFlow { + def run()(implicit materializer: FlowMaterializer): MaterializedPipe = + materializer.materialize(input, output, ops) +} + +/** + * Returned by [[RunnablePipe#run]] and can be used as parameter to the + * accessor method to retrieve the materialized `Tap` or `Drain`, e.g. + * [[SubscriberTap#subscriber]] or [[PublisherDrain#publisher]]. + */ +private[stream] class MaterializedPipe(tapKey: AnyRef, matTap: Any, drainKey: AnyRef, matDrain: Any) extends MaterializedFlow { + /** + * Do not call directly. Use accessor method in the concrete `Tap`, e.g. [[SubscriberTap#subscriber]]. + */ + override def getTapFor[T](key: TapWithKey[_, T]): T = + if (key == tapKey) matTap.asInstanceOf[T] + else throw new IllegalArgumentException(s"Tap key [$key] doesn't match the tap [$tapKey] of this flow") + + /** + * Do not call directly. Use accessor method in the concrete `Drain`, e.g. [[PublisherDrain#publisher]]. + */ + def getDrainFor[T](key: DrainWithKey[_, T]): T = + if (key == drainKey) matDrain.asInstanceOf[T] + else throw new IllegalArgumentException(s"Drain key [$key] doesn't match the drain [$drainKey] of this flow") +} diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl2/Sink.scala b/akka-stream/src/main/scala/akka/stream/scaladsl2/Sink.scala index e9947493f9..16fa3d1d60 100644 --- a/akka-stream/src/main/scala/akka/stream/scaladsl2/Sink.scala +++ b/akka-stream/src/main/scala/akka/stream/scaladsl2/Sink.scala @@ -3,263 +3,15 @@ */ package akka.stream.scaladsl2 -import akka.actor.Props +import org.reactivestreams.Subscriber -import scala.collection.immutable +import scala.language.implicitConversions import scala.annotation.unchecked.uncheckedVariance -import scala.concurrent.{ Future, Promise } -import scala.util.{ Failure, Success, Try } -import org.reactivestreams.{ Publisher, Subscriber, Subscription } -import akka.stream.Transformer -import akka.stream.impl.{ FanoutProcessorImpl, BlackholeSubscriber } -import akka.stream.impl2.{ ActorProcessorFactory, ActorBasedFlowMaterializer } -import java.util.concurrent.atomic.AtomicReference /** - * This trait is a marker for a pluggable stream sink. Concrete instances should - * implement [[SinkWithKey]] or [[SimpleSink]], otherwise a custom [[FlowMaterializer]] - * will have to be used to be able to attach them. - * - * All Sinks defined in this package rely upon an [[akka.stream.impl2.ActorBasedFlowMaterializer]] being - * made available to them in order to use the attach method. Other - * FlowMaterializers can be used but must then implement the functionality of these - * Sink nodes themselves (or construct an ActorBasedFlowMaterializer). + * A `Sink` is a set of stream processing steps that has one open input and an attached output. + * Can be used as a `Subscriber` */ -trait Sink[-Out] - -/** - * A sink that does not need to create a user-accessible object during materialization. - */ -trait SimpleSink[-Out] extends Sink[Out] { - /** - * Attach this sink to the given [[org.reactivestreams.Publisher]]. Using the given - * [[FlowMaterializer]] is completely optional, especially if this sink belongs to - * a different Reactive Streams implementation. It is the responsibility of the - * caller to provide a suitable FlowMaterializer that can be used for running - * Flows if necessary. - * - * @param flowPublisher the Publisher to consume elements from - * @param materializer a FlowMaterializer that may be used for creating flows - * @param flowName the name of the current flow, which should be used in log statements or error messages - */ - def attach(flowPublisher: Publisher[Out @uncheckedVariance], materializer: ActorBasedFlowMaterializer, flowName: String): Unit - /** - * This method is only used for Sinks that return true from [[#isActive]], which then must - * implement it. - */ - def create(materializer: ActorBasedFlowMaterializer, flowName: String): Subscriber[Out] @uncheckedVariance = - throw new UnsupportedOperationException(s"forgot to implement create() for $getClass that says isActive==true") - /** - * This method indicates whether this Sink can create a Subscriber instead of being - * attached to a Publisher. This is only used if the Flow does not contain any - * operations. - */ - def isActive: Boolean = false - +trait Sink[-In] { + def toSubscriber()(implicit materializer: FlowMaterializer): Subscriber[In @uncheckedVariance] } - -/** - * A sink that will create an object during materialization that the user will need - * to retrieve in order to access aspects of this sink (could be a completion Future - * or a cancellation handle, etc.) - */ -trait SinkWithKey[-Out, T] extends Sink[Out] { - /** - * Attach this sink to the given [[org.reactivestreams.Publisher]]. Using the given - * [[FlowMaterializer]] is completely optional, especially if this sink belongs to - * a different Reactive Streams implementation. It is the responsibility of the - * caller to provide a suitable FlowMaterializer that can be used for running - * Flows if necessary. - * - * @param flowPublisher the Publisher to consume elements from - * @param materializer a FlowMaterializer that may be used for creating flows - * @param flowName the name of the current flow, which should be used in log statements or error messages - */ - def attach(flowPublisher: Publisher[Out @uncheckedVariance], materializer: ActorBasedFlowMaterializer, flowName: String): T - /** - * This method is only used for Sinks that return true from [[#isActive]], which then must - * implement it. - */ - def create(materializer: ActorBasedFlowMaterializer, flowName: String): (Subscriber[Out] @uncheckedVariance, T) = - throw new UnsupportedOperationException(s"forgot to implement create() for $getClass that says isActive==true") - /** - * This method indicates whether this Sink can create a Subscriber instead of being - * attached to a Publisher. This is only used if the Flow does not contain any - * operations. - */ - def isActive: Boolean = false - - // these are unique keys, case class equality would break them - final override def equals(other: Any): Boolean = super.equals(other) - final override def hashCode: Int = super.hashCode -} - -/** - * Holds the downstream-most [[org.reactivestreams.Publisher]] interface of the materialized flow. - * The stream will not have any subscribers attached at this point, which means that after prefetching - * elements to fill the internal buffers it will assert back-pressure until - * a subscriber connects and creates demand for elements to be emitted. - */ -object PublisherSink { - private val instance = new PublisherSink[Nothing] - def apply[T]: PublisherSink[T] = instance.asInstanceOf[PublisherSink[T]] - def withFanout[T](initialBufferSize: Int, maximumBufferSize: Int): FanoutPublisherSink[T] = - new FanoutPublisherSink[T](initialBufferSize, maximumBufferSize) -} - -class PublisherSink[Out] extends SinkWithKey[Out, Publisher[Out]] { - def attach(flowPublisher: Publisher[Out], materializer: ActorBasedFlowMaterializer, flowName: String): Publisher[Out] = flowPublisher - def publisher(m: MaterializedSink): Publisher[Out] = m.getSinkFor(this) - - override def toString: String = "PublisherSink" -} - -class FanoutPublisherSink[Out](initialBufferSize: Int, maximumBufferSize: Int) extends SinkWithKey[Out, Publisher[Out]] { - def publisher(m: MaterializedSink): Publisher[Out] = m.getSinkFor(this) - override def attach(flowPublisher: Publisher[Out], materializer: ActorBasedFlowMaterializer, flowName: String): Publisher[Out] = { - val fanoutActor = materializer.actorOf( - Props(new FanoutProcessorImpl(materializer.settings, initialBufferSize, maximumBufferSize)), s"$flowName-fanoutPublisher") - val fanoutProcessor = ActorProcessorFactory[Out, Out](fanoutActor) - flowPublisher.subscribe(fanoutProcessor) - fanoutProcessor - } - - override def toString: String = "Fanout" -} - -object FutureSink { - def apply[T]: FutureSink[T] = new FutureSink[T] -} - -/** - * Holds a [[scala.concurrent.Future]] that will be fulfilled with the first - * thing that is signaled to this stream, which can be either an element (after - * which the upstream subscription is canceled), an error condition (putting - * the Future into the corresponding failed state) or the end-of-stream - * (failing the Future with a NoSuchElementException). - */ -class FutureSink[Out] extends SinkWithKey[Out, Future[Out]] { - def attach(flowPublisher: Publisher[Out], materializer: ActorBasedFlowMaterializer, flowName: String): Future[Out] = { - val (sub, f) = create(materializer, flowName) - flowPublisher.subscribe(sub) - f - } - override def isActive = true - override def create(materializer: ActorBasedFlowMaterializer, flowName: String): (Subscriber[Out], Future[Out]) = { - val p = Promise[Out]() - val sub = new Subscriber[Out] { // TODO #15804 verify this using the RS TCK - private val sub = new AtomicReference[Subscription] - override def onSubscribe(s: Subscription): Unit = - if (!sub.compareAndSet(null, s)) s.cancel() - else s.request(1) - override def onNext(t: Out): Unit = { p.trySuccess(t); sub.get.cancel() } - override def onError(t: Throwable): Unit = p.tryFailure(t) - override def onComplete(): Unit = p.tryFailure(new NoSuchElementException("empty stream")) - } - (sub, p.future) - } - - def future(m: MaterializedSink): Future[Out] = m.getSinkFor(this) - - override def toString: String = "FutureSink" -} - -/** - * Attaches a subscriber to this stream which will just discard all received - * elements. - */ -final case object BlackholeSink extends SimpleSink[Any] { - override def attach(flowPublisher: Publisher[Any], materializer: ActorBasedFlowMaterializer, flowName: String): Unit = - flowPublisher.subscribe(create(materializer, flowName)) - override def isActive: Boolean = true - override def create(materializer: ActorBasedFlowMaterializer, flowName: String): Subscriber[Any] = - new BlackholeSubscriber[Any](materializer.settings.maxInputBufferSize) -} - -/** - * Attaches a subscriber to this stream. - */ -final case class SubscriberSink[Out](subscriber: Subscriber[Out]) extends SimpleSink[Out] { - override def attach(flowPublisher: Publisher[Out], materializer: ActorBasedFlowMaterializer, flowName: String): Unit = - flowPublisher.subscribe(subscriber) - override def isActive: Boolean = true - override def create(materializer: ActorBasedFlowMaterializer, flowName: String): Subscriber[Out] = subscriber -} - -object OnCompleteSink { - private val SuccessUnit = Success[Unit](()) -} - -/** - * When the flow is completed, either through an error or normal - * completion, apply the provided function with [[scala.util.Success]] - * or [[scala.util.Failure]]. - */ -final case class OnCompleteSink[Out](callback: Try[Unit] ⇒ Unit) extends SimpleSink[Out] { - override def attach(flowPublisher: Publisher[Out], materializer: ActorBasedFlowMaterializer, flowName: String): Unit = - FlowFrom(flowPublisher).transform("onCompleteSink", () ⇒ new Transformer[Out, Unit] { - override def onNext(in: Out) = Nil - override def onError(e: Throwable) = () - override def onTermination(e: Option[Throwable]) = { - e match { - case None ⇒ callback(OnCompleteSink.SuccessUnit) - case Some(e) ⇒ callback(Failure(e)) - } - Nil - } - }).consume()(materializer.withNamePrefix(flowName)) -} - -/** - * Invoke the given procedure for each received element. The sink holds a [[scala.concurrent.Future]] - * that will be completed with `Success` when reaching the normal end of the stream, or completed - * with `Failure` if there is an error is signaled in the stream. - */ -final case class ForeachSink[Out](f: Out ⇒ Unit) extends SinkWithKey[Out, Future[Unit]] { - override def attach(flowPublisher: Publisher[Out], materializer: ActorBasedFlowMaterializer, flowName: String): Future[Unit] = { - val promise = Promise[Unit]() - FlowFrom(flowPublisher).transform("foreach", () ⇒ new Transformer[Out, Unit] { - override def onNext(in: Out) = { f(in); Nil } - override def onError(cause: Throwable): Unit = () - override def onTermination(e: Option[Throwable]) = { - e match { - case None ⇒ promise.success(()) - case Some(e) ⇒ promise.failure(e) - } - Nil - } - }).consume()(materializer.withNamePrefix(flowName)) - promise.future - } - def future(m: MaterializedSink): Future[Unit] = m.getSinkFor(this) -} - -/** - * Invoke the given function for every received element, giving it its previous - * output (or the given `zero` value) and the element as input. The sink holds a - * [[scala.concurrent.Future]] that will be completed with value of the final - * function evaluation when the input stream ends, or completed with `Failure` - * if there is an error is signaled in the stream. - */ -final case class FoldSink[U, Out](zero: U)(f: (U, Out) ⇒ U) extends SinkWithKey[Out, Future[U]] { - override def attach(flowPublisher: Publisher[Out], materializer: ActorBasedFlowMaterializer, flowName: String): Future[U] = { - val promise = Promise[U]() - - FlowFrom(flowPublisher).transform("fold", () ⇒ new Transformer[Out, U] { - var state: U = zero - override def onNext(in: Out): immutable.Seq[U] = { state = f(state, in); Nil } - override def onError(cause: Throwable) = () - override def onTermination(e: Option[Throwable]) = { - e match { - case None ⇒ promise.success(state) - case Some(e) ⇒ promise.failure(e) - } - Nil - } - }).consume()(materializer.withNamePrefix(flowName)) - - promise.future - } - def future(m: MaterializedSink): Future[U] = m.getSinkFor(this) -} - diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl2/Source.scala b/akka-stream/src/main/scala/akka/stream/scaladsl2/Source.scala index 656b0de1a2..2d8575d223 100644 --- a/akka-stream/src/main/scala/akka/stream/scaladsl2/Source.scala +++ b/akka-stream/src/main/scala/akka/stream/scaladsl2/Source.scala @@ -3,71 +3,90 @@ */ package akka.stream.scaladsl2 +import org.reactivestreams.{ Subscriber, Publisher } + import scala.annotation.unchecked.uncheckedVariance import scala.collection.immutable import scala.concurrent.Future import scala.concurrent.duration.FiniteDuration -import scala.util.{ Failure, Success } -import org.reactivestreams.{ Publisher, Subscriber } +import scala.language.higherKinds +import scala.language.implicitConversions -import akka.stream.impl.{ ActorPublisher, EmptyPublisher, ErrorPublisher, FuturePublisher, IterablePublisher, IteratorPublisher, SimpleCallbackPublisher, TickPublisher, Stop } -import akka.stream.impl2.ActorBasedFlowMaterializer +/** + * A `Source` is a set of stream processing steps that has one open output and an attached input. + * Can be used as a `Publisher` + */ +trait Source[+Out] extends FlowOps[Out] { + override type Repr[+O] <: Source[O] -object FlowFrom { /** - * Helper to create `Flow` without [[Source]]. - * Example usage: `FlowFrom[Int]` + * Transform this source by appending the given processing stages. */ - def apply[T]: ProcessorFlow[T, T] = ProcessorFlow.empty[T] + def connect[T](flow: Flow[Out, T]): Source[T] /** - * Helper to create `Flow` with [[Source]] from `Publisher`. + * Connect this source to a sink, concatenating the processing steps of both. + */ + def connect(sink: Sink[Out]): RunnableFlow + + def toPublisher()(implicit materializer: FlowMaterializer): Publisher[Out @uncheckedVariance] + + def toFanoutPublisher(initialBufferSize: Int, maximumBufferSize: Int)(implicit materializer: FlowMaterializer): Publisher[Out @uncheckedVariance] + + def publishTo(subscriber: Subscriber[Out @uncheckedVariance])(implicit materializer: FlowMaterializer) + + def consume()(implicit materializer: FlowMaterializer): Unit +} + +object Source { + /** + * Helper to create [[Source]] from `Publisher`. * * Construct a transformation starting with given publisher. The transformation steps * are executed by a series of [[org.reactivestreams.Processor]] instances * that mediate the flow of elements downstream and the propagation of * back-pressure upstream. */ - def apply[T](publisher: Publisher[T]): FlowWithSource[T, T] = FlowFrom[T].withSource(PublisherSource(publisher)) + def apply[T](publisher: Publisher[T]): Tap[T] = PublisherTap(publisher) /** - * Helper to create `Flow` with [[Source]] from `Iterator`. - * Example usage: `FlowFrom(Seq(1,2,3).iterator)` + * Helper to create [[Source]] from `Iterator`. + * Example usage: `Source(Seq(1,2,3).iterator)` * - * Start a new `Flow` from the given Iterator. The produced stream of elements + * Start a new `Source` from the given Iterator. The produced stream of elements * will continue until the iterator runs empty or fails during evaluation of * the `next()` method. Elements are pulled out of the iterator * in accordance with the demand coming from the downstream transformation * steps. */ - def apply[T](iterator: Iterator[T]): FlowWithSource[T, T] = FlowFrom[T].withSource(IteratorSource(iterator)) + def apply[T](iterator: Iterator[T]): Tap[T] = IteratorTap(iterator) /** - * Helper to create `Flow` with [[Source]] from `Iterable`. - * Example usage: `FlowFrom(Seq(1,2,3))` + * Helper to create [[Source]] from `Iterable`. + * Example usage: `Source(Seq(1,2,3))` * - * Starts a new `Flow` from the given `Iterable`. This is like starting from an + * Starts a new `Source` from the given `Iterable`. This is like starting from an * Iterator, but every Subscriber directly attached to the Publisher of this * stream will see an individual flow of elements (always starting from the * beginning) regardless of when they subscribed. */ - def apply[T](iterable: immutable.Iterable[T]): FlowWithSource[T, T] = FlowFrom[T].withSource(IterableSource(iterable)) + def apply[T](iterable: immutable.Iterable[T]): Tap[T] = IterableTap(iterable) /** * Define the sequence of elements to be produced by the given closure. * The stream ends normally when evaluation of the closure returns a `None`. * The stream ends exceptionally when an exception is thrown from the closure. */ - def apply[T](f: () ⇒ Option[T]): FlowWithSource[T, T] = FlowFrom[T].withSource(ThunkSource(f)) + def apply[T](f: () ⇒ Option[T]): Tap[T] = ThunkTap(f) /** - * Start a new `Flow` from the given `Future`. The stream will consist of + * Start a new `Source` from the given `Future`. The stream will consist of * one element when the `Future` is completed with a successful value, which * may happen before or after materializing the `Flow`. * The stream terminates with an error if the `Future` is completed with a failure. */ - def apply[T](future: Future[T]): FlowWithSource[T, T] = FlowFrom[T].withSource(FutureSource(future)) + def apply[T](future: Future[T]): Tap[T] = FutureTap(future) /** * Elements are produced from the tick closure periodically with the specified interval. @@ -76,200 +95,6 @@ object FlowFrom { * element is produced it will not receive that tick element later. It will * receive new tick elements as soon as it has requested more elements. */ - def apply[T](initialDelay: FiniteDuration, interval: FiniteDuration, tick: () ⇒ T): FlowWithSource[T, T] = - FlowFrom[T].withSource(TickSource(initialDelay, interval, tick)) - + def apply[T](initialDelay: FiniteDuration, interval: FiniteDuration, tick: () ⇒ T): Tap[T] = + TickTap(initialDelay, interval, tick) } - -/** - * This trait is a marker for a pluggable stream source. Concrete instances should - * implement [[SourceWithKey]] or [[SimpleSource]], otherwise a custom [[FlowMaterializer]] - * will have to be used to be able to attach them. - * - * All Sources defined in this package rely upon an ActorBasedFlowMaterializer being - * made available to them in order to use the attach method. Other - * FlowMaterializers can be used but must then implement the functionality of these - * Source nodes themselves (or construct an ActorBasedFlowMaterializer). - */ -trait Source[+In] - -/** - * A source that does not need to create a user-accessible object during materialization. - */ -trait SimpleSource[+In] extends Source[In] { - /** - * Attach this source to the given [[org.reactivestreams.Subscriber]]. Using the given - * [[FlowMaterializer]] is completely optional, especially if this source belongs to - * a different Reactive Streams implementation. It is the responsibility of the - * caller to provide a suitable FlowMaterializer that can be used for running - * Flows if necessary. - * - * @param flowSubscriber the Subscriber to produce elements to - * @param materializer a FlowMaterializer that may be used for creating flows - * @param flowName the name of the current flow, which should be used in log statements or error messages - */ - def attach(flowSubscriber: Subscriber[In] @uncheckedVariance, materializer: ActorBasedFlowMaterializer, flowName: String): Unit - /** - * This method is only used for Sources that return true from [[#isActive]], which then must - * implement it. - */ - def create(materializer: ActorBasedFlowMaterializer, flowName: String): Publisher[In] @uncheckedVariance = - throw new UnsupportedOperationException(s"forgot to implement create() for $getClass that says isActive==true") - /** - * This method indicates whether this Source can create a Publisher instead of being - * attached to a Subscriber. This is only used if the Flow does not contain any - * operations. - */ - def isActive: Boolean = false - - // these are unique keys, case class equality would break them - final override def equals(other: Any): Boolean = super.equals(other) - final override def hashCode: Int = super.hashCode -} - -/** - * A source that will create an object during materialization that the user will need - * to retrieve in order to access aspects of this source (could be a Subscriber, a - * Future/Promise, etc.). - */ -trait SourceWithKey[+In, T] extends Source[In] { - /** - * Attach this source to the given [[org.reactivestreams.Subscriber]]. Using the given - * [[FlowMaterializer]] is completely optional, especially if this source belongs to - * a different Reactive Streams implementation. It is the responsibility of the - * caller to provide a suitable FlowMaterializer that can be used for running - * Flows if necessary. - * - * @param flowSubscriber the Subscriber to produce elements to - * @param materializer a FlowMaterializer that may be used for creating flows - * @param flowName the name of the current flow, which should be used in log statements or error messages - */ - def attach(flowSubscriber: Subscriber[In] @uncheckedVariance, materializer: ActorBasedFlowMaterializer, flowName: String): T - /** - * This method is only used for Sources that return true from [[#isActive]], which then must - * implement it. - */ - def create(materializer: ActorBasedFlowMaterializer, flowName: String): (Publisher[In] @uncheckedVariance, T) = - throw new UnsupportedOperationException(s"forgot to implement create() for $getClass that says isActive==true") - /** - * This method indicates whether this Source can create a Publisher instead of being - * attached to a Subscriber. This is only used if the Flow does not contain any - * operations. - */ - def isActive: Boolean = false - - // these are unique keys, case class equality would break them - final override def equals(other: Any): Boolean = super.equals(other) - final override def hashCode: Int = super.hashCode -} - -/** - * Holds a `Subscriber` representing the input side of the flow. - * The `Subscriber` can later be connected to an upstream `Publisher`. - */ -final case class SubscriberSource[In]() extends SourceWithKey[In, Subscriber[In]] { - override def attach(flowSubscriber: Subscriber[In], materializer: ActorBasedFlowMaterializer, flowName: String): Subscriber[In] = - flowSubscriber - - def subscriber(m: MaterializedSource): Subscriber[In] = m.getSourceFor(this) -} - -/** - * Construct a transformation starting with given publisher. The transformation steps - * are executed by a series of [[org.reactivestreams.Processor]] instances - * that mediate the flow of elements downstream and the propagation of - * back-pressure upstream. - */ -final case class PublisherSource[In](p: Publisher[In]) extends SimpleSource[In] { - override def attach(flowSubscriber: Subscriber[In], materializer: ActorBasedFlowMaterializer, flowName: String): Unit = - p.subscribe(flowSubscriber) - override def isActive: Boolean = true - override def create(materializer: ActorBasedFlowMaterializer, flowName: String): Publisher[In] = p -} - -/** - * Start a new `Flow` from the given Iterator. The produced stream of elements - * will continue until the iterator runs empty or fails during evaluation of - * the `next()` method. Elements are pulled out of the iterator - * in accordance with the demand coming from the downstream transformation - * steps. - */ -final case class IteratorSource[In](iterator: Iterator[In]) extends SimpleSource[In] { - override def attach(flowSubscriber: Subscriber[In], materializer: ActorBasedFlowMaterializer, flowName: String): Unit = - create(materializer, flowName).subscribe(flowSubscriber) - override def isActive: Boolean = true - override def create(materializer: ActorBasedFlowMaterializer, flowName: String): Publisher[In] = - if (iterator.isEmpty) EmptyPublisher[In] - else ActorPublisher[In](materializer.actorOf(IteratorPublisher.props(iterator, materializer.settings), - name = s"$flowName-0-iterator")) -} - -/** - * Starts a new `Flow` from the given `Iterable`. This is like starting from an - * Iterator, but every Subscriber directly attached to the Publisher of this - * stream will see an individual flow of elements (always starting from the - * beginning) regardless of when they subscribed. - */ -final case class IterableSource[In](iterable: immutable.Iterable[In]) extends SimpleSource[In] { - override def attach(flowSubscriber: Subscriber[In], materializer: ActorBasedFlowMaterializer, flowName: String): Unit = - create(materializer, flowName).subscribe(flowSubscriber) - override def isActive: Boolean = true - override def create(materializer: ActorBasedFlowMaterializer, flowName: String): Publisher[In] = - if (iterable.isEmpty) EmptyPublisher[In] - else ActorPublisher[In](materializer.actorOf(IterablePublisher.props(iterable, materializer.settings), - name = s"$flowName-0-iterable"), Some(iterable)) -} - -/** - * Define the sequence of elements to be produced by the given closure. - * The stream ends normally when evaluation of the closure returns a `None`. - * The stream ends exceptionally when an exception is thrown from the closure. - */ -final case class ThunkSource[In](f: () ⇒ Option[In]) extends SimpleSource[In] { - override def attach(flowSubscriber: Subscriber[In], materializer: ActorBasedFlowMaterializer, flowName: String): Unit = - create(materializer, flowName).subscribe(flowSubscriber) - override def isActive: Boolean = true - override def create(materializer: ActorBasedFlowMaterializer, flowName: String): Publisher[In] = - ActorPublisher[In](materializer.actorOf(SimpleCallbackPublisher.props(materializer.settings, - () ⇒ f().getOrElse(throw Stop)), name = s"$flowName-0-thunk")) -} - -/** - * Start a new `Flow` from the given `Future`. The stream will consist of - * one element when the `Future` is completed with a successful value, which - * may happen before or after materializing the `Flow`. - * The stream terminates with an error if the `Future` is completed with a failure. - */ -final case class FutureSource[In](future: Future[In]) extends SimpleSource[In] { - override def attach(flowSubscriber: Subscriber[In], materializer: ActorBasedFlowMaterializer, flowName: String): Unit = - create(materializer, flowName).subscribe(flowSubscriber) - override def isActive: Boolean = true - override def create(materializer: ActorBasedFlowMaterializer, flowName: String): Publisher[In] = - future.value match { - case Some(Success(element)) ⇒ - ActorPublisher[In](materializer.actorOf(IterablePublisher.props(List(element), materializer.settings), - name = s"$flowName-0-future"), Some(future)) - case Some(Failure(t)) ⇒ - ErrorPublisher(t).asInstanceOf[Publisher[In]] - case None ⇒ - ActorPublisher[In](materializer.actorOf(FuturePublisher.props(future, materializer.settings), - name = s"$flowName-0-future"), Some(future)) - } -} - -/** - * Elements are produced from the tick closure periodically with the specified interval. - * The tick element will be delivered to downstream consumers that has requested any elements. - * If a consumer has not requested any elements at the point in time when the tick - * element is produced it will not receive that tick element later. It will - * receive new tick elements as soon as it has requested more elements. - */ -final case class TickSource[In](initialDelay: FiniteDuration, interval: FiniteDuration, tick: () ⇒ In) extends SimpleSource[In] { - override def attach(flowSubscriber: Subscriber[In], materializer: ActorBasedFlowMaterializer, flowName: String): Unit = - create(materializer, flowName).subscribe(flowSubscriber) - override def isActive: Boolean = true - override def create(materializer: ActorBasedFlowMaterializer, flowName: String): Publisher[In] = - ActorPublisher[In](materializer.actorOf(TickPublisher.props(initialDelay, interval, tick, materializer.settings), - name = s"$flowName-0-tick")) -} - diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl2/Tap.scala b/akka-stream/src/main/scala/akka/stream/scaladsl2/Tap.scala new file mode 100644 index 0000000000..9d8354099e --- /dev/null +++ b/akka-stream/src/main/scala/akka/stream/scaladsl2/Tap.scala @@ -0,0 +1,279 @@ +/** + * Copyright (C) 2014 Typesafe Inc. + */ +package akka.stream.scaladsl2 + +import akka.stream.impl._ +import akka.stream.impl2.ActorBasedFlowMaterializer +import akka.stream.{ Transformer, OverflowStrategy, TimerTransformer } +import org.reactivestreams.{ Publisher, Subscriber } +import scala.collection.immutable +import scala.concurrent.duration.FiniteDuration +import scala.concurrent.Future +import scala.util.{ Failure, Success } + +import scala.annotation.unchecked.uncheckedVariance + +/** + * This trait is a marker for a pluggable stream tap. Concrete instances should + * implement [[TapWithKey]] or [[SimpleTap]], otherwise a custom [[FlowMaterializer]] + * will have to be used to be able to attach them. + * + * All Taps defined in this package rely upon an ActorBasedFlowMaterializer being + * made available to them in order to use the attach method. Other + * FlowMaterializers can be used but must then implement the functionality of these + * Tap nodes themselves (or construct an ActorBasedFlowMaterializer). + */ +trait Tap[+Out] extends Source[Out] + +/** + * A tap that does not need to create a user-accessible object during materialization. + */ +trait SimpleTap[+Out] extends Tap[Out] with TapOps[Out] { + /** + * Attach this tap to the given [[org.reactivestreams.Subscriber]]. Using the given + * [[FlowMaterializer]] is completely optional, especially if this tap belongs to + * a different Reactive Streams implementation. It is the responsibility of the + * caller to provide a suitable FlowMaterializer that can be used for running + * Flows if necessary. + * + * @param flowSubscriber the Subscriber to produce elements to + * @param materializer a FlowMaterializer that may be used for creating flows + * @param flowName the name of the current flow, which should be used in log statements or error messages + */ + def attach(flowSubscriber: Subscriber[Out] @uncheckedVariance, materializer: ActorBasedFlowMaterializer, flowName: String): Unit + /** + * This method is only used for Taps that return true from [[#isActive]], which then must + * implement it. + */ + def create(materializer: ActorBasedFlowMaterializer, flowName: String): Publisher[Out] @uncheckedVariance = + throw new UnsupportedOperationException(s"forgot to implement create() for $getClass that says isActive==true") + /** + * This method indicates whether this Tap can create a Publisher instead of being + * attached to a Subscriber. This is only used if the Flow does not contain any + * operations. + */ + def isActive: Boolean = false + + // these are unique keys, case class equality would break them + final override def equals(other: Any): Boolean = super.equals(other) + final override def hashCode: Int = super.hashCode +} + +/** + * A tap that will create an object during materialization that the user will need + * to retrieve in order to access aspects of this tap (could be a Subscriber, a + * Future/Promise, etc.). + */ +trait TapWithKey[+Out, T] extends TapOps[Out] { + /** + * Attach this tap to the given [[org.reactivestreams.Subscriber]]. Using the given + * [[FlowMaterializer]] is completely optional, especially if this tap belongs to + * a different Reactive Streams implementation. It is the responsibility of the + * caller to provide a suitable FlowMaterializer that can be used for running + * Flows if necessary. + * + * @param flowSubscriber the Subscriber to produce elements to + * @param materializer a FlowMaterializer that may be used for creating flows + * @param flowName the name of the current flow, which should be used in log statements or error messages + */ + def attach(flowSubscriber: Subscriber[Out] @uncheckedVariance, materializer: ActorBasedFlowMaterializer, flowName: String): T + /** + * This method is only used for Taps that return true from [[#isActive]], which then must + * implement it. + */ + def create(materializer: ActorBasedFlowMaterializer, flowName: String): (Publisher[Out] @uncheckedVariance, T) = + throw new UnsupportedOperationException(s"forgot to implement create() for $getClass that says isActive==true") + /** + * This method indicates whether this Tap can create a Publisher instead of being + * attached to a Subscriber. This is only used if the Flow does not contain any + * operations. + */ + def isActive: Boolean = false + + // these are unique keys, case class equality would break them + final override def equals(other: Any): Boolean = super.equals(other) + final override def hashCode: Int = super.hashCode +} + +/** + * Holds a `Subscriber` representing the input side of the flow. + * The `Subscriber` can later be connected to an upstream `Publisher`. + */ +final case class SubscriberTap[Out]() extends TapWithKey[Out, Subscriber[Out]] { + override def attach(flowSubscriber: Subscriber[Out], materializer: ActorBasedFlowMaterializer, flowName: String): Subscriber[Out] = + flowSubscriber + + def subscriber(m: MaterializedTap): Subscriber[Out] = m.getTapFor(this) +} + +/** + * Construct a transformation starting with given publisher. The transformation steps + * are executed by a series of [[org.reactivestreams.Processor]] instances + * that mediate the flow of elements downstream and the propagation of + * back-pressure upstream. + */ +final case class PublisherTap[Out](p: Publisher[Out]) extends SimpleTap[Out] { + override def attach(flowSubscriber: Subscriber[Out], materializer: ActorBasedFlowMaterializer, flowName: String): Unit = + p.subscribe(flowSubscriber) + override def isActive: Boolean = true + override def create(materializer: ActorBasedFlowMaterializer, flowName: String): Publisher[Out] = p +} + +/** + * Start a new `Source` from the given Iterator. The produced stream of elements + * will continue until the iterator runs empty or fails during evaluation of + * the `next()` method. Elements are pulled out of the iterator + * in accordance with the demand coming from the downstream transformation + * steps. + */ +final case class IteratorTap[Out](iterator: Iterator[Out]) extends SimpleTap[Out] { + override def attach(flowSubscriber: Subscriber[Out], materializer: ActorBasedFlowMaterializer, flowName: String): Unit = + create(materializer, flowName).subscribe(flowSubscriber) + override def isActive: Boolean = true + override def create(materializer: ActorBasedFlowMaterializer, flowName: String): Publisher[Out] = + if (iterator.isEmpty) EmptyPublisher[Out] + else ActorPublisher[Out](materializer.actorOf(IteratorPublisher.props(iterator, materializer.settings), + name = s"$flowName-0-iterator")) +} + +/** + * Starts a new `Source` from the given `Iterable`. This is like starting from an + * Iterator, but every Subscriber directly attached to the Publisher of this + * stream will see an individual flow of elements (always starting from the + * beginning) regardless of when they subscribed. + */ +final case class IterableTap[Out](iterable: immutable.Iterable[Out]) extends SimpleTap[Out] { + override def attach(flowSubscriber: Subscriber[Out], materializer: ActorBasedFlowMaterializer, flowName: String): Unit = + create(materializer, flowName).subscribe(flowSubscriber) + override def isActive: Boolean = true + override def create(materializer: ActorBasedFlowMaterializer, flowName: String): Publisher[Out] = + if (iterable.isEmpty) EmptyPublisher[Out] + else ActorPublisher[Out](materializer.actorOf(IterablePublisher.props(iterable, materializer.settings), + name = s"$flowName-0-iterable"), Some(iterable)) +} + +/** + * Define the sequence of elements to be produced by the given closure. + * The stream ends normally when evaluation of the closure returns a `None`. + * The stream ends exceptionally when an exception is thrown from the closure. + */ +final case class ThunkTap[Out](f: () ⇒ Option[Out]) extends SimpleTap[Out] { + override def attach(flowSubscriber: Subscriber[Out], materializer: ActorBasedFlowMaterializer, flowName: String): Unit = + create(materializer, flowName).subscribe(flowSubscriber) + override def isActive: Boolean = true + override def create(materializer: ActorBasedFlowMaterializer, flowName: String): Publisher[Out] = + ActorPublisher[Out](materializer.actorOf(SimpleCallbackPublisher.props(materializer.settings, + () ⇒ f().getOrElse(throw Stop)), name = s"$flowName-0-thunk")) +} + +/** + * Start a new `Source` from the given `Future`. The stream will consist of + * one element when the `Future` is completed with a successful value, which + * may happen before or after materializing the `Flow`. + * The stream terminates with an error if the `Future` is completed with a failure. + */ +final case class FutureTap[Out](future: Future[Out]) extends SimpleTap[Out] { + override def attach(flowSubscriber: Subscriber[Out], materializer: ActorBasedFlowMaterializer, flowName: String): Unit = + create(materializer, flowName).subscribe(flowSubscriber) + override def isActive: Boolean = true + override def create(materializer: ActorBasedFlowMaterializer, flowName: String): Publisher[Out] = + future.value match { + case Some(Success(element)) ⇒ + ActorPublisher[Out](materializer.actorOf(IterablePublisher.props(List(element), materializer.settings), + name = s"$flowName-0-future"), Some(future)) + case Some(Failure(t)) ⇒ + ErrorPublisher(t).asInstanceOf[Publisher[Out]] + case None ⇒ + ActorPublisher[Out](materializer.actorOf(FuturePublisher.props(future, materializer.settings), + name = s"$flowName-0-future"), Some(future)) + } +} + +/** + * Elements are produced from the tick closure periodically with the specified interval. + * The tick element will be delivered to downstream consumers that has requested any elements. + * If a consumer has not requested any elements at the point in time when the tick + * element is produced it will not receive that tick element later. It will + * receive new tick elements as soon as it has requested more elements. + */ +final case class TickTap[Out](initialDelay: FiniteDuration, interval: FiniteDuration, tick: () ⇒ Out) extends SimpleTap[Out] { + override def attach(flowSubscriber: Subscriber[Out], materializer: ActorBasedFlowMaterializer, flowName: String): Unit = + create(materializer, flowName).subscribe(flowSubscriber) + override def isActive: Boolean = true + override def create(materializer: ActorBasedFlowMaterializer, flowName: String): Publisher[Out] = + ActorPublisher[Out](materializer.actorOf(TickPublisher.props(initialDelay, interval, tick, materializer.settings), + name = s"$flowName-0-tick")) +} + +trait MaterializedTap { + /** + * Do not call directly. Use accessor method in the concrete `Tap`, e.g. [[SubscriberTap#subscriber]]. + */ + def getTapFor[T](tapKey: TapWithKey[_, T]): T +} + +trait TapOps[+Out] extends Tap[Out] { + override type Repr[+O] = SourcePipe[O] + + private def sourcePipe = Pipe.empty[Out].withTap(this) + + override def connect[T](flow: Flow[Out, T]): Source[T] = sourcePipe.connect(flow) + + override def connect(sink: Sink[Out]): RunnableFlow = sourcePipe.connect(sink) + + override def toPublisher()(implicit materializer: FlowMaterializer): Publisher[Out @uncheckedVariance] = + sourcePipe.toPublisher()(materializer) + + override def toFanoutPublisher(initialBufferSize: Int, maximumBufferSize: Int)(implicit materializer: FlowMaterializer): Publisher[Out @uncheckedVariance] = + sourcePipe.toFanoutPublisher(initialBufferSize, maximumBufferSize)(materializer) + + override def publishTo(subscriber: Subscriber[Out @uncheckedVariance])(implicit materializer: FlowMaterializer): Unit = + sourcePipe.publishTo(subscriber)(materializer) + + override def consume()(implicit materializer: FlowMaterializer): Unit = + sourcePipe.consume() + + override def map[T](f: Out ⇒ T): Repr[T] = sourcePipe.map(f) + + override def mapConcat[T](f: Out ⇒ immutable.Seq[T]): Repr[T] = sourcePipe.mapConcat(f) + + override def mapAsync[T](f: Out ⇒ Future[T]): Repr[T] = sourcePipe.mapAsync(f) + + override def mapAsyncUnordered[T](f: Out ⇒ Future[T]): Repr[T] = sourcePipe.mapAsyncUnordered(f) + + override def filter(p: Out ⇒ Boolean): Repr[Out] = sourcePipe.filter(p) + + override def collect[T](pf: PartialFunction[Out, T]): Repr[T] = sourcePipe.collect(pf) + + override def grouped(n: Int): Repr[immutable.Seq[Out]] = sourcePipe.grouped(n) + + override def groupedWithin(n: Int, d: FiniteDuration): Repr[immutable.Seq[Out]] = sourcePipe.groupedWithin(n, d) + + override def drop(n: Int): Repr[Out] = sourcePipe.drop(n) + + override def dropWithin(d: FiniteDuration): Repr[Out] = sourcePipe.dropWithin(d) + + override def take(n: Int): Repr[Out] = sourcePipe.take(n) + + override def takeWithin(d: FiniteDuration): Repr[Out] = sourcePipe.takeWithin(d) + + override def conflate[S](seed: Out ⇒ S, aggregate: (S, Out) ⇒ S): Repr[S] = sourcePipe.conflate(seed, aggregate) + + override def expand[S, U](seed: Out ⇒ S, extrapolate: S ⇒ (U, S)): Repr[U] = sourcePipe.expand(seed, extrapolate) + + override def buffer(size: Int, overflowStrategy: OverflowStrategy): Repr[Out] = sourcePipe.buffer(size, overflowStrategy) + + override def transform[T](name: String, mkTransformer: () ⇒ Transformer[Out, T]): Repr[T] = sourcePipe.transform(name, mkTransformer) + + override def prefixAndTail[U >: Out](n: Int): Repr[(immutable.Seq[Out], Source[U])] = sourcePipe.prefixAndTail(n) + + override def groupBy[K, U >: Out](f: Out ⇒ K): Repr[(K, Source[U])] = sourcePipe.groupBy(f) + + override def splitWhen[U >: Out](p: Out ⇒ Boolean): Repr[Source[U]] = sourcePipe.splitWhen(p) + + override def flatten[U](strategy: FlattenStrategy[Out, U]): Repr[U] = sourcePipe.flatten(strategy) + + override def timerTransform[U](name: String, mkTransformer: () ⇒ TimerTransformer[Out, U]): Repr[U] = + sourcePipe.timerTransform(name, mkTransformer) +} diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl2/package.scala b/akka-stream/src/main/scala/akka/stream/scaladsl2/package.scala index b849164f1c..a9c37956e6 100644 --- a/akka-stream/src/main/scala/akka/stream/scaladsl2/package.scala +++ b/akka-stream/src/main/scala/akka/stream/scaladsl2/package.scala @@ -5,29 +5,29 @@ package akka.stream /** * Scala API: The flow DSL allows the formulation of stream transformations based on some - * input. The starting point is called [[Source]] and can be a collection, an iterator, + * input. The starting point is called [[Tap]] and can be a collection, an iterator, * a block of code which is evaluated repeatedly or a [[org.reactivestreams.Publisher]]. - * A flow with an attached `Source` is a [[FlowWithSource]] and is constructed - * with the `apply` methods in [[FlowFrom]]. + * A flow with an attached input and open output, including `Tap` is a [[Source]], and is + * constructed with the `apply` methods in [[Source]]. * - * A flow may also be defined without an attached input `Source` and that is then - * a [[ProcessorFlow]]. The `Source` can be attached later with [[ProcessorFlow#withSource]] - * and it becomes a [[FlowWithSource]]. + * A flow may also be defined without an attached input or output and that is then + * a [[Flow]]. The `Flow` can be connected to the `Source` later by using [[Source#connect]] with + * the flow as argument, and it remains a [[Source]]. * - * Transformations can appended to `FlowWithSource` and `ProcessorFlow` with the operations + * Transformations can be appended to `Source` and `Flow` with the operations * defined in [[FlowOps]]. Each DSL element produces a new flow that can be further transformed, * building up a description of the complete transformation pipeline. * - * The output of the flow can be attached to a [[Sink]] with [[FlowWithSource#withSink]] - * and if it also has an attached `Source` it becomes a [[RunnableFlow]]. In order to execute - * this pipeline the flow must be materialized by calling [[RunnableFlow#run]] on it. + * The termination point of a flow is called [[Drain]] and can for example be a `Future` or + * [[org.reactivestreams.Subscriber]]. A flow with an attached output and open input, + * including `Drain`, is a [[Sink]]. * - * You may also first attach the `Sink` to a `ProcessorFlow` with [[ProcessorFlow#withSink]] - * and then it becomes a [[FlowWithSink]] and then attach the `Source` to make - * it runnable. + * If a flow has both an attached input and an attached output it becomes a [[RunnableFlow]]. + * In order to execute this pipeline the flow must be materialized by calling [[RunnableFlow#run]] on it. * - * Flows can be wired together before they are materialized by appending or prepending them, or - * connecting them into a [[FlowGraph]] with fan-in and fan-out elements. + * You can create your `Source`, `Flow` and `Sink` in any order and then wire them together before + * they are materialized by connecting them using [[Flow#connect]], or connecting them into a + * [[FlowGraph]] with fan-in and fan-out elements. * * See Reactive Streams for * details on [[org.reactivestreams.Publisher]] and [[org.reactivestreams.Subscriber]].