From 3bc02fc694c7e7cdedaabed8788631364f3fe816 Mon Sep 17 00:00:00 2001 From: Roland Kuhn Date: Thu, 19 Feb 2015 21:18:24 +0100 Subject: [PATCH 1/4] IT COMPILES (well, no tests) --- .../akka/stream/testkit/TwoStreamsSetup.scala | 17 +- .../stream/actor/ActorPublisherSpec.scala | 10 +- .../stream/scaladsl/GraphPartialSpec.scala | 17 + .../akka/stream/FanInShape.scala.template | 33 + .../akka/stream/FanOutShape.scala.template | 33 + .../stream/impl/GenJunctions.scala.template | 33 +- .../akka/stream/impl/ZipWith.scala.template | 6 +- .../stream/javadsl/FlowCreate.scala.template | 28 + .../stream/javadsl/GraphCreate.scala.template | 37 + .../stream/javadsl/SinkCreate.scala.template | 32 + .../javadsl/SourceCreate.scala.template | 24 + .../stream/javadsl/ZipWith.scala.template | 60 +- .../javadsl/japi/Functions.scala.template | 26 + .../stream/scaladsl/FanInPorts.scala.template | 34 - .../scaladsl/FanOutPorts.scala.template | 34 - .../stream/scaladsl/FlowApply.scala.template | 17 +- .../scaladsl/FlowGraphApply.scala.template | 96 --- .../stream/scaladsl/GraphApply.scala.template | 92 +++ .../stream/scaladsl/SinkApply.scala.template | 35 +- .../scaladsl/SourceApply.scala.template | 17 +- .../stream/scaladsl/ZipWith.scala.template | 36 - .../scaladsl/ZipWithApply.scala.template | 12 +- .../src/main/scala/akka/stream/Graph.scala | 23 + .../src/main/scala/akka/stream/Shape.scala | 107 +++ .../impl/ActorBasedFlowMaterializer.scala | 37 +- .../main/scala/akka/stream/impl/FanIn.scala | 4 +- .../main/scala/akka/stream/impl/FanOut.scala | 4 +- .../akka/stream/impl/FlexiMergeImpl.scala | 25 +- .../akka/stream/impl/FlexiRouteImpl.scala | 27 +- .../main/scala/akka/stream/impl/Flows.scala | 11 +- .../scala/akka/stream/impl/Junctions.scala | 130 ++-- .../main/scala/akka/stream/impl/Sinks.scala | 13 +- .../main/scala/akka/stream/impl/Sources.scala | 17 +- .../scala/akka/stream/impl/StreamLayout.scala | 33 +- .../akka/stream/javadsl/FlattenStrategy.scala | 4 +- .../akka/stream/javadsl/FlexiMerge.scala | 177 ++--- .../akka/stream/javadsl/FlexiRoute.scala | 193 ++---- .../main/scala/akka/stream/javadsl/Flow.scala | 159 ++--- .../scala/akka/stream/javadsl/FlowGraph.scala | 631 ------------------ .../scala/akka/stream/javadsl/Graph.scala | 286 ++++++++ .../akka/stream/javadsl/MaterializedMap.scala | 75 --- .../main/scala/akka/stream/javadsl/Sink.scala | 84 +-- .../scala/akka/stream/javadsl/Source.scala | 136 ++-- .../scala/akka/stream/javadsl/StreamTcp.scala | 62 +- .../akka/stream/scaladsl/FlexiMerge.scala | 53 +- .../akka/stream/scaladsl/FlexiRoute.scala | 46 +- .../scala/akka/stream/scaladsl/Flow.scala | 43 +- .../akka/stream/scaladsl/FlowGraph.scala | 374 ----------- .../scala/akka/stream/scaladsl/Graph.scala | 269 ++++++++ .../scala/akka/stream/scaladsl/Graphs.scala | 65 -- .../akka/stream/scaladsl/JavaConverters.scala | 44 +- ...rializable.scala => Materialization.scala} | 12 - .../scala/akka/stream/scaladsl/Sink.scala | 14 +- .../scala/akka/stream/scaladsl/Source.scala | 37 +- 54 files changed, 1526 insertions(+), 2398 deletions(-) create mode 100644 akka-stream/src/main/boilerplate/akka/stream/FanInShape.scala.template create mode 100644 akka-stream/src/main/boilerplate/akka/stream/FanOutShape.scala.template create mode 100644 akka-stream/src/main/boilerplate/akka/stream/javadsl/FlowCreate.scala.template create mode 100644 akka-stream/src/main/boilerplate/akka/stream/javadsl/GraphCreate.scala.template create mode 100644 akka-stream/src/main/boilerplate/akka/stream/javadsl/SinkCreate.scala.template create mode 100644 akka-stream/src/main/boilerplate/akka/stream/javadsl/SourceCreate.scala.template create mode 100644 akka-stream/src/main/boilerplate/akka/stream/javadsl/japi/Functions.scala.template delete mode 100644 akka-stream/src/main/boilerplate/akka/stream/scaladsl/FanInPorts.scala.template delete mode 100644 akka-stream/src/main/boilerplate/akka/stream/scaladsl/FanOutPorts.scala.template delete mode 100644 akka-stream/src/main/boilerplate/akka/stream/scaladsl/FlowGraphApply.scala.template create mode 100644 akka-stream/src/main/boilerplate/akka/stream/scaladsl/GraphApply.scala.template delete mode 100644 akka-stream/src/main/boilerplate/akka/stream/scaladsl/ZipWith.scala.template create mode 100644 akka-stream/src/main/scala/akka/stream/Graph.scala create mode 100644 akka-stream/src/main/scala/akka/stream/Shape.scala delete mode 100644 akka-stream/src/main/scala/akka/stream/javadsl/FlowGraph.scala create mode 100644 akka-stream/src/main/scala/akka/stream/javadsl/Graph.scala delete mode 100644 akka-stream/src/main/scala/akka/stream/javadsl/MaterializedMap.scala delete mode 100644 akka-stream/src/main/scala/akka/stream/scaladsl/FlowGraph.scala create mode 100644 akka-stream/src/main/scala/akka/stream/scaladsl/Graph.scala delete mode 100644 akka-stream/src/main/scala/akka/stream/scaladsl/Graphs.scala rename akka-stream/src/main/scala/akka/stream/scaladsl/{Materializable.scala => Materialization.scala} (73%) diff --git a/akka-stream-testkit/src/test/scala/akka/stream/testkit/TwoStreamsSetup.scala b/akka-stream-testkit/src/test/scala/akka/stream/testkit/TwoStreamsSetup.scala index 04e715051ec..17c789657b9 100644 --- a/akka-stream-testkit/src/test/scala/akka/stream/testkit/TwoStreamsSetup.scala +++ b/akka-stream-testkit/src/test/scala/akka/stream/testkit/TwoStreamsSetup.scala @@ -1,7 +1,6 @@ package akka.stream.testkit -import akka.stream.scaladsl.FlowGraph.FlowGraphBuilder -import akka.stream.{ FlowMaterializer, MaterializerSettings } +import akka.stream.{ FlowMaterializer, MaterializerSettings, Inlet, Outlet } import akka.stream.scaladsl._ import org.reactivestreams.Publisher import scala.collection.immutable @@ -18,18 +17,18 @@ abstract class TwoStreamsSetup extends AkkaSpec { type Outputs - abstract class Fixture(b: FlowGraphBuilder) { - def left: Graphs.InPort[Int] - def right: Graphs.InPort[Int] - def out: Graphs.OutPort[Outputs] + abstract class Fixture(b: Graph.Builder) { + def left: Inlet[Int] + def right: Inlet[Int] + def out: Outlet[Outputs] } - def fixture(b: FlowGraphBuilder): Fixture + def fixture(b: Graph.Builder): Fixture def setup(p1: Publisher[Int], p2: Publisher[Int]) = { val subscriber = StreamTestKit.SubscriberProbe[Outputs]() - FlowGraph() { implicit b ⇒ - import FlowGraph.Implicits._ + Graph.closed() { implicit b ⇒ + import Graph.Implicits._ val f = fixture(b) Source(p1) ~> f.left diff --git a/akka-stream-tests/src/test/scala/akka/stream/actor/ActorPublisherSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/actor/ActorPublisherSpec.scala index 4eac7897eb5..6fd62a6f3ef 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/actor/ActorPublisherSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/actor/ActorPublisherSpec.scala @@ -6,12 +6,8 @@ package akka.stream.actor import akka.actor.ActorRef import akka.actor.PoisonPill import akka.actor.Props -import akka.stream.scaladsl.Broadcast -import akka.stream.scaladsl.FlowGraph -import akka.stream.scaladsl.Merge +import akka.stream.scaladsl._ import akka.stream.FlowMaterializer -import akka.stream.scaladsl.Sink -import akka.stream.scaladsl.Source import akka.stream.testkit.AkkaSpec import akka.stream.testkit.StreamTestKit import akka.testkit.EventFilter @@ -290,9 +286,9 @@ class ActorPublisherSpec extends AkkaSpec with ImplicitSender { val sink1 = Sink(ActorSubscriber[String](system.actorOf(receiverProps(probe1.ref)))) val sink2 = Sink[String](receiverProps(probe2.ref)) - val senderRef2 = FlowGraph(Source[Int](senderProps)) { implicit b ⇒ + val senderRef2 = Graph.closed(Source[Int](senderProps)) { implicit b ⇒ source2 ⇒ - import FlowGraph.Implicits._ + import Graph.Implicits._ val merge = Merge[Int](2) val bcast = Broadcast[String](2) diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphPartialSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphPartialSpec.scala index 8c7435e7531..0f0d109e64a 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphPartialSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphPartialSpec.scala @@ -93,6 +93,23 @@ class GraphPartialSpec extends AkkaSpec { Await.result(sub2._1, 3.seconds) should be(12) Await.result(sub2._2, 3.seconds) should be(24) } + + "be able to expose the ports of imported graphs" in { + val p = FlowGraph.partial(Flow[Int].map(_ + 1)) { implicit b ⇒ + flow ⇒ + FlowPorts(flow.inlet, flow.outlet) + } + + val fut = FlowGraph(Sink.head[Int], p)(Keep.left) { implicit b ⇒ + (sink, flow) ⇒ + import FlowGraph.Implicits._ + Source.single(0) ~> flow.inlet + flow.outlet ~> sink.inlet + }.run() + + Await.result(fut, 3.seconds) should be(0) + + } } } diff --git a/akka-stream/src/main/boilerplate/akka/stream/FanInShape.scala.template b/akka-stream/src/main/boilerplate/akka/stream/FanInShape.scala.template new file mode 100644 index 00000000000..73b3ebc9f01 --- /dev/null +++ b/akka-stream/src/main/boilerplate/akka/stream/FanInShape.scala.template @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2014-2015 Typesafe Inc. + */ +package akka.stream + +import scala.collection.immutable + +abstract class FanInShape[O] extends Shape { + final val out = new Outlet[O]("out") + + final override def outlets: immutable.Seq[Outlet[_]] = out :: Nil + final override def inlets: immutable.Seq[Inlet[_]] = _inlets + + private var _inlets: List[Inlet[_]] = Nil + protected def newInlet[T](name: String): Inlet[T] = { + val p = new Inlet[T](name) + _inlets ::= p + p + } +} + +class UniformFanInShape[T, O](n: Int) extends FanInShape[O] { + val in: Array[Inlet[T]] = Array.tabulate(n)(i => newInlet[T](s"in$i")) + override def deepCopy(): UniformFanInShape[T, O] = new UniformFanInShape(n) +} + +[2..#class FanInShape1[[#T0#], O] extends FanInShape[O] { + [#val in0 = newInlet[T0]("in0")# + ] + override def deepCopy(): FanInShape1[[#T0#], O] = new FanInShape1 +}# + +] diff --git a/akka-stream/src/main/boilerplate/akka/stream/FanOutShape.scala.template b/akka-stream/src/main/boilerplate/akka/stream/FanOutShape.scala.template new file mode 100644 index 00000000000..cd2dfd94e91 --- /dev/null +++ b/akka-stream/src/main/boilerplate/akka/stream/FanOutShape.scala.template @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2014-2015 Typesafe Inc. + */ +package akka.stream + +import scala.collection.immutable + +abstract class FanOutShape[I] extends Shape { + final val in = new Inlet[I]("out") + + final override def outlets: immutable.Seq[Outlet[_]] = _outlets + final override def inlets: immutable.Seq[Inlet[_]] = in :: Nil + + private var _outlets: List[Outlet[_]] = Nil + protected def newOutlet[T](name: String): Outlet[T] = { + val p = new Outlet[T](name) + _outlets ::= p + p + } +} + +class UniformFanOutShape[I, O](n: Int) extends FanOutShape[I] { + val out: Array[Outlet[O]] = Array.tabulate(n)(i => newOutlet[O](s"out$i")) + override def deepCopy(): UniformFanOutShape[I, O] = new UniformFanOutShape(n) +} + +[2..#class FanOutShape1[I, [#O0#]] extends FanOutShape[I] { + [#val out0 = newOutlet[O0]("out0")# + ] + override def deepCopy(): FanOutShape1[I, [#O0#]] = new FanOutShape1 +}# + +] diff --git a/akka-stream/src/main/boilerplate/akka/stream/impl/GenJunctions.scala.template b/akka-stream/src/main/boilerplate/akka/stream/impl/GenJunctions.scala.template index a565db8bc50..37c8c73d204 100644 --- a/akka-stream/src/main/boilerplate/akka/stream/impl/GenJunctions.scala.template +++ b/akka-stream/src/main/boilerplate/akka/stream/impl/GenJunctions.scala.template @@ -4,10 +4,10 @@ package akka.stream.impl import akka.actor.Props -import akka.stream.MaterializerSettings +import akka.stream._ import akka.stream.impl.Junctions.FaninModule -import akka.stream.impl.StreamLayout.{ Mapping, OutPort, InPort, Module } -import akka.stream.scaladsl.{ Graphs, OperationAttributes } +import akka.stream.impl.StreamLayout.{ Mapping, Module } +import akka.stream.scaladsl.OperationAttributes import akka.stream.scaladsl.OperationAttributes._ /** Boilerplate generated Junctions */ @@ -16,38 +16,19 @@ object GenJunctions { sealed trait ZipWithModule { /** Allows hiding the boilerplate Props creation from the materializer */ def props(settings: MaterializerSettings): Props - - /** - * In addition to the Set based view onto the ports in this module, - * we must maintain an ordered collection for materialization purposes. - */ - def ins: Vector[InPort] } - [2..19# + [2..20# final case class ZipWith1Module[[#A1#], B]( - [#in1: Graphs.InPort[A1],# - ] - out: Graphs.OutPort[B], + shape: FanInShape1[[#A1#], B], f: ([#A1#]) ⇒ B, override val attributes: OperationAttributes = name("zipWith1")) extends FaninModule with ZipWithModule { - override val ins: Vector[InPort] = Vector([#in1#]) - - override val inPorts: Set[InPort] = Set([#in1#]) - override val outPorts: Set[OutPort] = Set(out) - override def withAttributes(attr: OperationAttributes): Module = copy(attributes = attr) override def carbonCopy: () ⇒ Mapping = () ⇒ { - val newZip = ZipWith1Module( - [#new Graphs.InPort[A1](in1.toString),# - ] - new Graphs.OutPort[B](out.toString), - f, - attributes) - - Mapping(newZip, Map([#in1 -> newZip.in1#]), Map(out -> newZip.out)) + val newZip = ZipWith1Module(shape.deepCopy(), f, attributes) + Mapping(newZip, shape.inlets.zip(newZip.shape.inlets).toMap, Map(shape.out -> newZip.shape.out)) } override def props(settings: MaterializerSettings): Props = diff --git a/akka-stream/src/main/boilerplate/akka/stream/impl/ZipWith.scala.template b/akka-stream/src/main/boilerplate/akka/stream/impl/ZipWith.scala.template index 1c321c9c2d3..ca0d318e05b 100644 --- a/akka-stream/src/main/boilerplate/akka/stream/impl/ZipWith.scala.template +++ b/akka-stream/src/main/boilerplate/akka/stream/impl/ZipWith.scala.template @@ -5,12 +5,10 @@ package akka.stream.impl import scala.collection.immutable import akka.actor.Props -import akka.stream.MaterializerSettings +import akka.stream.{ MaterializerSettings, Shape, Inlet, Outlet } import akka.stream.impl.GenJunctions._ -import akka.stream.scaladsl.FlowGraph.FlowGraphBuilder -import akka.stream.scaladsl.Graphs.{ InPort, OutPort, Ports } -[2..19#/** INTERNAL API */ +[2..20#/** INTERNAL API */ private[akka] final class Zip1With(_settings: MaterializerSettings, f: Function1[[#Any#], Any]) extends FanIn(_settings, inputCount = 1) { diff --git a/akka-stream/src/main/boilerplate/akka/stream/javadsl/FlowCreate.scala.template b/akka-stream/src/main/boilerplate/akka/stream/javadsl/FlowCreate.scala.template new file mode 100644 index 00000000000..26c0aab98ad --- /dev/null +++ b/akka-stream/src/main/boilerplate/akka/stream/javadsl/FlowCreate.scala.template @@ -0,0 +1,28 @@ +/** + * Copyright (C) 2015 Typesafe Inc. + */ +package akka.stream.javadsl + +import akka.stream.scaladsl +import akka.stream.{ Inlet, Outlet, Shape, Graph } +import akka.stream.scaladsl.JavaConverters._ +import akka.japi.Pair + +trait FlowCreate { + + import language.implicitConversions + private implicit def p[A, B](pair: Pair[A, B]): (A, B) = pair.first -> pair.second + + def create[I, O](block: japi.Function[Graph.Builder, Inlet[I] Pair Outlet[O]]): Flow[I, O, Unit] = + new Flow(scaladsl.Flow() { b ⇒ block.apply(b.asJava) }) + + def create[I, O, S <: Shape, M](g1: Graph[S, M], block: japi.Function2[Graph.Builder, S, Inlet[I] Pair Outlet[O]]): Flow[I, O, M] = + new Flow(scaladsl.Flow(g1) { b ⇒ s => block.apply(b.asJava, s) }) + + [3..21#def create[I, O, [#S1 <: Shape#], [#M1#], M]([#g1: Graph[S1, M1]#], combineMat: japi.Function1[[#M1#], M], + block: japi.Function2[Graph.Builder, [#S1#], Inlet[I] Pair Outlet[O]]): Flow[I, O, M] = + new Flow(scaladsl.Flow([#g1#])(combineMat.apply _) { b => ([#s1#]) => block.apply(b.asJava, [#s1#]) })# + + ] + +} diff --git a/akka-stream/src/main/boilerplate/akka/stream/javadsl/GraphCreate.scala.template b/akka-stream/src/main/boilerplate/akka/stream/javadsl/GraphCreate.scala.template new file mode 100644 index 00000000000..b95df4fe9c8 --- /dev/null +++ b/akka-stream/src/main/boilerplate/akka/stream/javadsl/GraphCreate.scala.template @@ -0,0 +1,37 @@ +/** + * Copyright (C) 2015 Typesafe Inc. + */ +package akka.stream.javadsl + +import akka.stream.scaladsl +import akka.stream.{ Inlet, Shape, Graph } +import akka.stream.scaladsl.JavaConverters._ + +trait GraphCreate { + + import language.implicitConversions + private implicit def r[M](run: scaladsl.RunnableFlow[M]): RunnableFlow[M] = new RunnableFlowAdapter(run) + + def closed(block: japi.Procedure[Graph.Builder]): RunnableFlow[Unit] = + scaladsl.Graph.closed() { b ⇒ block.apply(b.asJava) } + + def partial[S <: Shape](block: japi.Function[Graph.Builder, S]): Graph[S, Unit] = + scaladsl.Graph.partial() { b ⇒ block.apply(b.asJava) } + + def closed[S1 <: Shape, M](g1: Graph[S1, M], block: japi.Procedure2[Graph.Builder, S1]): RunnableFlow[M] = + scaladsl.Graph.closed(g1) { b ⇒ s => block.apply(b.asJava, s) } + + def partial[S1 <: Shape, S <: Shape, M](g1: Graph[S1, M], block: japi.Function2[Graph.Builder, S1, S]): Graph[S, M] = + scaladsl.Graph.partial(g1) { b ⇒ s => block.apply(b.asJava, s) } + + [3..21#def closed[[#S1 <: Shape#], [#M1#], M]([#g1: Graph[S1, M1]#], combineMat: japi.Function1[[#M1#], M], + block: japi.Procedure2[Graph.Builder, [#S1#]]): RunnableFlow[M] = + scaladsl.Graph.closed([#g1#])(combineMat.apply _) { b => ([#s1#]) => block.apply(b.asJava, [#s1#]) } + + def partial[[#S1 <: Shape#], S <: Shape, [#M1#], M]([#g1: Graph[S1, M1]#], combineMat: japi.Function1[[#M1#], M], + block: japi.Function2[Graph.Builder, [#S1#], S]): Graph[S, M] = + scaladsl.Graph.partial([#g1#])(combineMat.apply _) { b => ([#s1#]) => block.apply(b.asJava, [#s1#]) }# + + ] + +} diff --git a/akka-stream/src/main/boilerplate/akka/stream/javadsl/SinkCreate.scala.template b/akka-stream/src/main/boilerplate/akka/stream/javadsl/SinkCreate.scala.template new file mode 100644 index 00000000000..8fc4717476b --- /dev/null +++ b/akka-stream/src/main/boilerplate/akka/stream/javadsl/SinkCreate.scala.template @@ -0,0 +1,32 @@ +/** + * Copyright (C) 2015 Typesafe Inc. + */ +package akka.stream.javadsl + +import akka.stream.scaladsl +import akka.stream.{ Inlet, Shape, Graph } +import akka.stream.scaladsl.JavaConverters._ + +trait SinkCreate { + + /** + * Creates a `Sink` by using a FlowGraphBuilder from this [[PartialFlowGraph]] on a block that expects + * a [[FlowGraphBuilder]] and returns the `UndefinedSource`. + */ + def create[T](block: japi.Function[Graph.Builder, Inlet[T]]): Sink[T, Unit] = + new Sink(scaladsl.Sink() { b ⇒ block.apply(b.asJava) }) + + /** + * Creates a `Sink` by using a FlowGraphBuilder from this [[PartialFlowGraph]] on a block that expects + * a [[FlowGraphBuilder]] and returns the `UndefinedSource`. + */ + def create[T, S <: Shape, M](g1: Graph[S, M], block: japi.Function2[Graph.Builder, S, Inlet[T]]): Sink[T, M] = + new Sink(scaladsl.Sink(g1) { b ⇒ s => block.apply(b.asJava, s) }) + + [3..21#def create[T, [#S1 <: Shape#], [#M1#], M]([#g1: Graph[S1, M1]#], combineMat: japi.Function1[[#M1#], M], + block: japi.Function2[Graph.Builder, [#S1#], Inlet[T]]): Sink[T, M] = + new Sink(scaladsl.Sink([#g1#])(combineMat.apply _) { b => ([#s1#]) => block.apply(b.asJava, [#s1#]) })# + + ] + +} diff --git a/akka-stream/src/main/boilerplate/akka/stream/javadsl/SourceCreate.scala.template b/akka-stream/src/main/boilerplate/akka/stream/javadsl/SourceCreate.scala.template new file mode 100644 index 00000000000..7d637b10b8f --- /dev/null +++ b/akka-stream/src/main/boilerplate/akka/stream/javadsl/SourceCreate.scala.template @@ -0,0 +1,24 @@ +/** + * Copyright (C) 2015 Typesafe Inc. + */ +package akka.stream.javadsl + +import akka.stream.scaladsl +import akka.stream.{ Outlet, Shape, Graph } +import akka.stream.scaladsl.JavaConverters._ + +trait SourceCreate { + + def create[T](block: japi.Function[Graph.Builder, Outlet[T]]): Source[T, Unit] = + new Source(scaladsl.Source() { b ⇒ block.apply(b.asJava) }) + + def create[T, S <: Shape, M](g1: Graph[S, M], block: japi.Function2[Graph.Builder, S, Outlet[T]]): Source[T, M] = + new Source(scaladsl.Source(g1) { b ⇒ s => block.apply(b.asJava, s) }) + + [3..21#def create[T, [#S1 <: Shape#], [#M1#], M]([#g1: Graph[S1, M1]#], combineMat: japi.Function1[[#M1#], M], + block: japi.Function2[Graph.Builder, [#S1#], Outlet[T]]): Source[T, M] = + new Source(scaladsl.Source([#g1#])(combineMat.apply _) { b => ([#s1#]) => block.apply(b.asJava, [#s1#]) })# + + ] + +} diff --git a/akka-stream/src/main/boilerplate/akka/stream/javadsl/ZipWith.scala.template b/akka-stream/src/main/boilerplate/akka/stream/javadsl/ZipWith.scala.template index b38b6629839..614eec09910 100644 --- a/akka-stream/src/main/boilerplate/akka/stream/javadsl/ZipWith.scala.template +++ b/akka-stream/src/main/boilerplate/akka/stream/javadsl/ZipWith.scala.template @@ -3,8 +3,8 @@ */ package akka.stream.javadsl +import akka.stream._ import akka.stream.scaladsl -import akka.stream.javadsl.japi object ZipWith { @@ -14,60 +14,14 @@ object ZipWith { * @param f zipping-function from the input values to the output value * @param attributes optional attributes for this vertex */ - def create[A, B, Out](f: japi.Function2[A, B, Out], attributes: OperationAttributes): Zip2With[A, B, Out] = - new Zip2With(new scaladsl.Zip2With[A, B, Out](f.apply _, attributes.asScala)) + def create[A, B, Out](f: japi.Function2[A, B, Out]): Graph[FanInShape2[A, B, Out], Unit] = + scaladsl.ZipWith(f.apply _) - /** - * Create a new `ZipWith` vertex with the specified input types and zipping-function `f`. - * - * @param f zipping-function from the input values to the output value - * Creates a new named `ZipWith` vertex with the specified input types and zipping-function `f`. - * Note that a `ZipWith` 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 create[A, B, Out](f: japi.Function2[A, B, Out]): Zip2With[A, B, Out] = - create(f, OperationAttributes.none) - - - [3..#/** Create a new `ZipWith` specialized for 1 input streams. */ - def create[[#T1#], Out](f: japi.Function[Zip1WithInputs[[#T1#]], Out]) = - new Zip1With(new scaladsl.Zip1With[[#T1#], Out](([#t1#]) ⇒ f.apply(new Zip1WithInputs[[#T1#]]([#t1#])), scaladsl.OperationAttributes.none))# - ] - - // CLASS BOILERPLATE - sealed trait ZipWithInputs - [2..#final class Zip1WithInputs[[#T1#]]([#val t1: T1#]) extends ZipWithInputs# + [3..20#/** Create a new `ZipWith` specialized for 1 input streams. */ + def create[[#T1#], Out](f: japi.Function1[[#T1#], Out]): Graph[FanInShape1[[#T1#], Out], Unit] = + scaladsl.ZipWith(f.apply _)# + ] - [#final class Input1[T1, Out] private[akka] (val asScala: scaladsl.ZipWith.Input1[T1, Out]) extends JunctionInPort[T1]# - ] - - final class Left[A, B, Out](override val asScala: scaladsl.ZipWith.Left[A, B, Out]) extends JunctionInPort[A] - final class Right[A, B, Out](override val asScala: scaladsl.ZipWith.Right[A, B, Out]) extends JunctionInPort[B] - final class Out[Out](override val asScala: scaladsl.ZipWith.Out[Out]) extends JunctionOutPort[Out] } - -/** - * Takes two streams and outputs an output stream formed from the two input streams - * by combining corresponding elements in pairs. If one of the two streams is - * longer than the other, its remaining elements are ignored. - */ -final class Zip2With[A, B, Out] private[akka] (val asScala: scaladsl.Zip2With[A, B, Out]) { - val left = new ZipWith.Left[A, B, Out](asScala.left) - val right = new ZipWith.Right[A, B, Out](asScala.right) - val out = new ZipWith.Out[Out](asScala.out) -} - -[3..#/** - * Takes multiple streams and outputs an output stream formed from the two input streams - * by combining corresponding elements in pairs. If one of the two streams is - * longer than the other, its remaining elements are ignored. - */ -final class Zip1With[[#T1#], Out] private[akka] (val asScala: scaladsl.Zip1With[[#T1#], Out]) { - val out = new ZipWith.Out[Out](asScala.out) - [#val input1 = new ZipWith.Input1[T1, Out](asScala.input1)# - ] -}# -] \ No newline at end of file diff --git a/akka-stream/src/main/boilerplate/akka/stream/javadsl/japi/Functions.scala.template b/akka-stream/src/main/boilerplate/akka/stream/javadsl/japi/Functions.scala.template new file mode 100644 index 00000000000..c29ebc72924 --- /dev/null +++ b/akka-stream/src/main/boilerplate/akka/stream/javadsl/japi/Functions.scala.template @@ -0,0 +1,26 @@ +/** + * Copyright (C) 2015 Typesafe Inc. + */ +package akka.stream.javadsl.japi + +[3..22#/** + * A Function interface. Used to create 1-arg first-class-functions is Java. + */ +@SerialVersionUID(##1L) +trait Function1[[#T1#], R] { + @throws(classOf[Exception]) + def apply([#arg1: T1#]): R +}# + +] + +[2..#/** + * A Consumer interface. Used to create 1-arg consumers in Java. + */ +@SerialVersionUID(1L) +trait Procedure1[[#T1#]] { + @throws(classOf[Exception]) + def apply([#arg1: T1#]): Unit +}# + +] diff --git a/akka-stream/src/main/boilerplate/akka/stream/scaladsl/FanInPorts.scala.template b/akka-stream/src/main/boilerplate/akka/stream/scaladsl/FanInPorts.scala.template deleted file mode 100644 index 46732a4e677..00000000000 --- a/akka-stream/src/main/boilerplate/akka/stream/scaladsl/FanInPorts.scala.template +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (C) 2014-2015 Typesafe Inc. - */ -package akka.stream.scaladsl - -import akka.stream.scaladsl.Graphs.{ Graph, Ports, InPort, OutPort } -import scala.collection.immutable - -abstract class FanInPorts[O] extends Ports { - final val out = new OutPort[O]("out") - - final override def outlets: immutable.Seq[OutPort[_]] = out :: Nil - final override def inlets: immutable.Seq[InPort[_]] = _inlets - - private var _inlets: List[InPort[_]] = Nil - protected def port[T](name: String): InPort[T] = { - val p = new InPort[T](name) - _inlets ::= p - p - } -} - -class UniformFanIn[T, O](n: Int) extends FanInPorts[O] { - val in: Array[InPort[T]] = Array.tabulate(n)(i => port[T](s"in$i")) - override def deepCopy() = new UniformFanIn(n) -} - -[2..#class FanIn1[[#T0#], O] extends FanInPorts[O] { - [#val in0 = port[T0]("in0")# - ] - override def deepCopy() = new FanIn1 -}# - -] diff --git a/akka-stream/src/main/boilerplate/akka/stream/scaladsl/FanOutPorts.scala.template b/akka-stream/src/main/boilerplate/akka/stream/scaladsl/FanOutPorts.scala.template deleted file mode 100644 index 034fa375512..00000000000 --- a/akka-stream/src/main/boilerplate/akka/stream/scaladsl/FanOutPorts.scala.template +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (C) 2014-2015 Typesafe Inc. - */ -package akka.stream.scaladsl - -import akka.stream.scaladsl.Graphs.{ Graph, Ports, InPort, OutPort } -import scala.collection.immutable - -abstract class FanOutPorts[I] extends Ports { - final val in = new InPort[I]("out") - - final override def outlets: immutable.Seq[OutPort[_]] = _outlets - final override def inlets: immutable.Seq[InPort[_]] = in :: Nil - - private var _outlets: List[OutPort[_]] = Nil - protected def port[T](name: String): OutPort[T] = { - val p = new OutPort[T](name) - _outlets ::= p - p - } -} - -class UniformFanOut[I, O](n: Int) extends FanOutPorts[I] { - val out: Array[OutPort[O]] = Array.tabulate(n)(i => port[O](s"out$i")) - override def deepCopy() = new UniformFanOut(n) -} - -[2..#class FanOut1[I, [#O0#]] extends FanOutPorts[I] { - [#val out0 = port[O0]("out0")# - ] - override def deepCopy() = new FanOut1 -}# - -] diff --git a/akka-stream/src/main/boilerplate/akka/stream/scaladsl/FlowApply.scala.template b/akka-stream/src/main/boilerplate/akka/stream/scaladsl/FlowApply.scala.template index 55cb0180726..fef7dd0864e 100644 --- a/akka-stream/src/main/boilerplate/akka/stream/scaladsl/FlowApply.scala.template +++ b/akka-stream/src/main/boilerplate/akka/stream/scaladsl/FlowApply.scala.template @@ -3,27 +3,26 @@ */ package akka.stream.scaladsl -import akka.stream.scaladsl.FlowGraph.FlowGraphBuilder -import akka.stream.scaladsl.Graphs.{ Graph, Ports, InPort, OutPort } +import akka.stream.{ Shape, Inlet, Outlet, Graph } trait FlowApply { - def apply[I, O]()(block: FlowGraphBuilder ⇒ (Graphs.InPort[I], Graphs.OutPort[O])): Flow[I, O, Unit] = { - val builder = new FlowGraphBuilder + def apply[I, O]()(block: Graph.Builder ⇒ (Inlet[I], Outlet[O])): Flow[I, O, Unit] = { + val builder = new Graph.Builder val (inlet, outlet) = block(builder) builder.buildFlow(inlet, outlet) } - def apply[I, O, Mat](g1: Graph[Ports, Mat])(buildBlock: FlowGraphBuilder => (g1.Ports) ⇒ (Graphs.InPort[I], Graphs.OutPort[O])): Flow[I, O, Mat] = { - val builder = new FlowGraphBuilder + def apply[I, O, Mat](g1: Graph[Shape, Mat])(buildBlock: Graph.Builder => (g1.Shape) ⇒ (Inlet[I], Outlet[O])): Flow[I, O, Mat] = { + val builder = new Graph.Builder val p = builder.importGraph(g1, Keep.right) val (inlet, outlet) = buildBlock(builder)(p) builder.buildFlow(inlet, outlet) } - [2..#def apply[I, O, [#M1#], Mat]([#g1: Graph[Ports, M1]#])(combineMat: ([#M1#]) => Mat)( - buildBlock: FlowGraphBuilder => ([#g1.Ports#]) ⇒ (Graphs.InPort[I], Graphs.OutPort[O])): Flow[I, O, Mat] = { - val builder = new FlowGraphBuilder + [2..#def apply[I, O, [#M1#], Mat]([#g1: Graph[Shape, M1]#])(combineMat: ([#M1#]) => Mat)( + buildBlock: Graph.Builder => ([#g1.Shape#]) ⇒ (Inlet[I], Outlet[O])): Flow[I, O, Mat] = { + val builder = new Graph.Builder val curried = combineMat.curried val p##1 = builder.importGraph(g##1, (_: Any, m##1: M##1) ⇒ curried(m##1)) [2..#val p1 = builder.importGraph(g1, (f: M1 ⇒ Any, m1: M1) ⇒ f(m1))# diff --git a/akka-stream/src/main/boilerplate/akka/stream/scaladsl/FlowGraphApply.scala.template b/akka-stream/src/main/boilerplate/akka/stream/scaladsl/FlowGraphApply.scala.template deleted file mode 100644 index 219bcf780a9..00000000000 --- a/akka-stream/src/main/boilerplate/akka/stream/scaladsl/FlowGraphApply.scala.template +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright (C) 2014-2015 Typesafe Inc. - */ -package akka.stream.scaladsl - -import akka.stream.scaladsl.FlowGraph.FlowGraphBuilder -import akka.stream.scaladsl.Graphs.{ Graph, Ports } -import akka.stream.impl.StreamLayout.Module - -trait FlowGraphApply { - - def apply()(buildBlock: (FlowGraphBuilder) ⇒ Unit): RunnableFlow[Unit] = { - val builder = new FlowGraphBuilder - buildBlock(builder) - builder.buildRunnable() - } - - def apply[Mat](g1: Graph[Ports, Mat])(buildBlock: FlowGraphBuilder ⇒ (g1.Ports) ⇒ Unit): RunnableFlow[Mat] = { - val builder = new FlowGraphBuilder - val p1 = builder.importGraph(g1, Keep.right) - buildBlock(builder)(p1) - builder.buildRunnable() - } - - private def checkPartialPorts[P <: Ports](p: P, mod: Module): Unit = { - if (p.inlets.toSet != mod.inPorts) - throw new IllegalStateException("The input ports in the returned Ports instance must correspond to the unconnected ports") - if (p.outlets.toSet != mod.outPorts) - throw new IllegalStateException("The output ports in the returned Ports instance must correspond to the unconnected ports") - } - - def partial[P <: Ports](buildBlock: FlowGraphBuilder ⇒ P): Graph[P, Unit] = { - val builder = new FlowGraphBuilder - val p = buildBlock(builder) - val mod = builder.module.wrap() - - checkPartialPorts(p, mod) - - new Graph[P, Unit] { - override type MaterializedType = Unit - override def ports: P = p - override private[stream] def module: Module = mod - } - } - - def partial[P <: Ports, Mat](g1: Graph[Ports, Mat])(buildBlock: FlowGraphBuilder ⇒ (g1.Ports) ⇒ P): Graph[P, Mat] = { - val builder = new FlowGraphBuilder - val p1 = builder.importGraph(g1, Keep.right) - val p = buildBlock(builder)(p1) - val mod = builder.module.wrap() - - checkPartialPorts(p, mod) - - new Graph[P, Mat] { - override type MaterializedType = Mat - override def ports: P = p - override private[stream] def module: Module = mod - } - } - - - - [2..#def apply[Mat, [#M1#]]([#g1: Graph[Ports, M1]#])(combineMat: ([#M1#]) ⇒ Mat)(buildBlock: FlowGraphBuilder ⇒ ([#g1.Ports#]) ⇒ Unit): RunnableFlow[Mat] = { - val builder = new FlowGraphBuilder - val curried = combineMat.curried - val p##1 = builder.importGraph(g##1, (_: Any, m##1: M##1) ⇒ curried(m##1)) - [2..#val p1 = builder.importGraph(g1, (f: M1 ⇒ Any, m1: M1) ⇒ f(m1))# - ] - buildBlock(builder)([#p1#]) - builder.buildRunnable() - }# - - ] - - [2..#def partial[P <: Ports, Mat, [#M1#]]([#g1: Graph[Ports, M1]#])(combineMat: ([#M1#]) ⇒ Mat)(buildBlock: FlowGraphBuilder ⇒ ([#g1.Ports#]) ⇒ P): Graph[P, Mat] = { - val builder = new FlowGraphBuilder - val curried = combineMat.curried - val p##1 = builder.importGraph(g##1, (_: Any, m##1: M##1) ⇒ curried(m##1)) - [2..#val p1 = builder.importGraph(g1, (f: M1 ⇒ Any, m1: M1) ⇒ f(m1))# - ] - val p = buildBlock(builder)([#p1#]) - val mod = builder.module.wrap() - - checkPartialPorts(p, mod) - - new Graph[P, Mat] { - override type MaterializedType = Mat - override def ports: P = p - override private[stream] def module: Module = mod - } - }# - - ] - - -} diff --git a/akka-stream/src/main/boilerplate/akka/stream/scaladsl/GraphApply.scala.template b/akka-stream/src/main/boilerplate/akka/stream/scaladsl/GraphApply.scala.template new file mode 100644 index 00000000000..5d5aa0cef26 --- /dev/null +++ b/akka-stream/src/main/boilerplate/akka/stream/scaladsl/GraphApply.scala.template @@ -0,0 +1,92 @@ +/** + * Copyright (C) 2014-2015 Typesafe Inc. + */ +package akka.stream.scaladsl + +import akka.stream.impl.StreamLayout.Module +import akka.stream.{ Graph, Shape } + +trait GraphApply { + + def closed()(buildBlock: (Graph.Builder) ⇒ Unit): RunnableFlow[Unit] = { + val builder = new Graph.Builder + buildBlock(builder) + builder.buildRunnable() + } + + def closed[Mat](g1: Graph[Shape, Mat])(buildBlock: Graph.Builder ⇒ (g1.Shape) ⇒ Unit): RunnableFlow[Mat] = { + val builder = new Graph.Builder + val p1 = builder.importGraph(g1, Keep.right) + buildBlock(builder)(p1) + builder.buildRunnable() + } + + private def checkPartialPorts[S <: Shape](s: S, mod: Module): Unit = { + if (s.inlets.toSet != mod.inPorts) + throw new IllegalStateException("The input ports in the returned Ports instance must correspond to the unconnected ports") + if (s.outlets.toSet != mod.outPorts) + throw new IllegalStateException("The output ports in the returned Ports instance must correspond to the unconnected ports") + } + + def partial[S <: Shape]()(buildBlock: Graph.Builder ⇒ S): Graph[S, Unit] = { + val builder = new Graph.Builder + val s = buildBlock(builder) + val mod = builder.module.wrap() + + checkPartialPorts(s, mod) + + new Graph[S, Unit] { + override def shape: S = s + override private[stream] def module: Module = mod + } + } + + def partial[S <: Shape, Mat](g1: Graph[Shape, Mat])(buildBlock: Graph.Builder ⇒ (g1.Shape) ⇒ S): Graph[S, Mat] = { + val builder = new Graph.Builder + val s1 = builder.importGraph(g1, Keep.right) + val s = buildBlock(builder)(s1) + val mod = builder.module.wrap() + + checkPartialPorts(s, mod) + + new Graph[S, Mat] { + override def shape: S = s + override private[stream] def module: Module = mod + } + } + + + + [2..#def closed[Mat, [#M1#]]([#g1: Graph[Shape, M1]#])(combineMat: ([#M1#]) ⇒ Mat)(buildBlock: Graph.Builder ⇒ ([#g1.Shape#]) ⇒ Unit): RunnableFlow[Mat] = { + val builder = new Graph.Builder + val curried = combineMat.curried + val s##1 = builder.importGraph(g##1, (_: Any, m##1: M##1) ⇒ curried(m##1)) + [2..#val s1 = builder.importGraph(g1, (f: M1 ⇒ Any, m1: M1) ⇒ f(m1))# + ] + buildBlock(builder)([#s1#]) + builder.buildRunnable() + }# + + ] + + [2..#def partial[S <: Shape, Mat, [#M1#]]([#g1: Graph[Shape, M1]#])(combineMat: ([#M1#]) ⇒ Mat)(buildBlock: Graph.Builder ⇒ ([#g1.Shape#]) ⇒ S): Graph[S, Mat] = { + val builder = new Graph.Builder + val curried = combineMat.curried + val s##1 = builder.importGraph(g##1, (_: Any, m##1: M##1) ⇒ curried(m##1)) + [2..#val s1 = builder.importGraph(g1, (f: M1 ⇒ Any, m1: M1) ⇒ f(m1))# + ] + val s = buildBlock(builder)([#s1#]) + val mod = builder.module.wrap() + + checkPartialPorts(s, mod) + + new Graph[S, Mat] { + override def shape: S = s + override private[stream] def module: Module = mod + } + }# + + ] + + +} diff --git a/akka-stream/src/main/boilerplate/akka/stream/scaladsl/SinkApply.scala.template b/akka-stream/src/main/boilerplate/akka/stream/scaladsl/SinkApply.scala.template index 17898f8df54..179aa6fa633 100644 --- a/akka-stream/src/main/boilerplate/akka/stream/scaladsl/SinkApply.scala.template +++ b/akka-stream/src/main/boilerplate/akka/stream/scaladsl/SinkApply.scala.template @@ -3,33 +3,32 @@ */ package akka.stream.scaladsl -import akka.stream.scaladsl.FlowGraph.FlowGraphBuilder -import akka.stream.scaladsl.Graphs.{ Graph, Ports, InPort } +import akka.stream.{ Inlet, Graph, Shape } trait SinkApply { - def apply[In]()(buildBlock: FlowGraphBuilder => InPort[In]): Sink[In, Unit] = { - val builder = new FlowGraphBuilder - val port = buildBlock(builder) - builder.buildSink(port) + def apply[In]()(buildBlock: Graph.Builder => Inlet[In]): Sink[In, Unit] = { + val builder = new Graph.Builder + val inlet = buildBlock(builder) + builder.buildSink(inlet) } - def apply[In, Mat](g1: Graph[Ports, Mat])(buildBlock: FlowGraphBuilder => (g1.Ports) => InPort[In]): Sink[In, Mat] = { - val builder = new FlowGraphBuilder - val p = builder.importGraph(g1, Keep.right) - val port = buildBlock(builder)(p) - builder.buildSink(port) + def apply[In, Mat](g1: Graph[Shape, Mat])(buildBlock: Graph.Builder => (g1.Shape) => Inlet[In]): Sink[In, Mat] = { + val builder = new Graph.Builder + val s = builder.importGraph(g1, Keep.right) + val inlet = buildBlock(builder)(s) + builder.buildSink(inlet) } - [2..#def apply[In, [#M1#], Mat]([#g1: Graph[Ports, M1]#])(combineMat: ([#M1#]) ⇒ Mat)( - buildBlock: FlowGraphBuilder ⇒ ([#g1.Ports#]) ⇒ InPort[In]): Sink[In, Mat] = { - val builder = new FlowGraphBuilder + [2..#def apply[In, [#M1#], Mat]([#g1: Graph[Shape, M1]#])(combineMat: ([#M1#]) ⇒ Mat)( + buildBlock: Graph.Builder ⇒ ([#g1.Shape#]) ⇒ Inlet[In]): Sink[In, Mat] = { + val builder = new Graph.Builder val curried = combineMat.curried - val p##1 = builder.importGraph(g##1, (_: Any, m##1: M##1) ⇒ curried(m##1)) - [2..#val p1 = builder.importGraph(g1, (f: M1 ⇒ Any, m1: M1) ⇒ f(m1))# + val s##1 = builder.importGraph(g##1, (_: Any, m##1: M##1) ⇒ curried(m##1)) + [2..#val s1 = builder.importGraph(g1, (f: M1 ⇒ Any, m1: M1) ⇒ f(m1))# ] - val port = buildBlock(builder)([#p1#]) - builder.buildSink(port) + val inlet = buildBlock(builder)([#s1#]) + builder.buildSink(inlet) }# ] diff --git a/akka-stream/src/main/boilerplate/akka/stream/scaladsl/SourceApply.scala.template b/akka-stream/src/main/boilerplate/akka/stream/scaladsl/SourceApply.scala.template index d853c5f0ed1..2a4af1b13c8 100644 --- a/akka-stream/src/main/boilerplate/akka/stream/scaladsl/SourceApply.scala.template +++ b/akka-stream/src/main/boilerplate/akka/stream/scaladsl/SourceApply.scala.template @@ -3,27 +3,26 @@ */ package akka.stream.scaladsl -import akka.stream.scaladsl.FlowGraph.FlowGraphBuilder -import akka.stream.scaladsl.Graphs.{ Graph, Ports, OutPort } +import akka.stream.{ Outlet, Shape, Graph } trait SourceApply { - def apply[Out]()(buildBlock: FlowGraphBuilder => OutPort[Out]): Source[Out, Unit] = { - val builder = new FlowGraphBuilder + def apply[Out]()(buildBlock: Graph.Builder => Outlet[Out]): Source[Out, Unit] = { + val builder = new Graph.Builder val port = buildBlock(builder) builder.buildSource(port) } - def apply[Out, Mat](g1: Graph[Ports, Mat])(buildBlock: FlowGraphBuilder => (g1.Ports) => OutPort[Out]): Source[Out, Mat] = { - val builder = new FlowGraphBuilder + def apply[Out, Mat](g1: Graph[Shape, Mat])(buildBlock: Graph.Builder => (g1.Shape) => Outlet[Out]): Source[Out, Mat] = { + val builder = new Graph.Builder val p = builder.importGraph(g1, Keep.right) val port = buildBlock(builder)(p) builder.buildSource(port) } - [2..#def apply[Out, [#M1#], Mat]([#g1: Graph[Ports, M1]#])(combineMat: ([#M1#]) ⇒ Mat)( - buildBlock: FlowGraphBuilder ⇒ ([#g1.Ports#]) ⇒ OutPort[Out]): Source[Out, Mat] = { - val builder = new FlowGraphBuilder + [2..#def apply[Out, [#M1#], Mat]([#g1: Graph[Shape, M1]#])(combineMat: ([#M1#]) ⇒ Mat)( + buildBlock: Graph.Builder ⇒ ([#g1.Shape#]) ⇒ Outlet[Out]): Source[Out, Mat] = { + val builder = new Graph.Builder val curried = combineMat.curried val p##1 = builder.importGraph(g##1, (_: Any, m##1: M##1) ⇒ curried(m##1)) [2..#val p1 = builder.importGraph(g1, (f: M1 ⇒ Any, m1: M1) ⇒ f(m1))# diff --git a/akka-stream/src/main/boilerplate/akka/stream/scaladsl/ZipWith.scala.template b/akka-stream/src/main/boilerplate/akka/stream/scaladsl/ZipWith.scala.template deleted file mode 100644 index d47294b413c..00000000000 --- a/akka-stream/src/main/boilerplate/akka/stream/scaladsl/ZipWith.scala.template +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (C) 2014-2015 Typesafe Inc. - */ -package akka.stream.scaladsl - -import scala.collection.immutable -import akka.actor.Props -import akka.stream.MaterializerSettings -import akka.stream.impl.GenJunctions._ -import akka.stream.scaladsl.FlowGraph.FlowGraphBuilder -import akka.stream.scaladsl.Graphs.{ InPort, OutPort, Ports } - -[2..19#object ZipWith1 { - - final case class ZipWith1Ports[[#A1#], O]( - [#in1: InPort[A1], # - ]out: OutPort[O]) extends Ports { - override val inlets: immutable.Seq[InPort[_]] = List([#in1#]) - override val outlets: immutable.Seq[OutPort[_]] = List(out) - - override def deepCopy(): ZipWith1Ports[[#A1#], O] = - ZipWith1Ports( - [#new InPort(in1.toString), # - ]new OutPort(out.toString)) - } - - def apply[[#A1#], O](zipper: ([#A1#]) ⇒ O)(implicit b: FlowGraphBuilder): ZipWith1Ports[[#A1#], O] = { - val zipWithModule = new ZipWith1Module( - [#new InPort[A1]("ZipWith1.in1"),# - ]new OutPort[O]("ZipWith1.out"), - zipper) - b.addModule(zipWithModule) - ZipWith1Ports([#zipWithModule.in1#], zipWithModule.out) - } -}# -] diff --git a/akka-stream/src/main/boilerplate/akka/stream/scaladsl/ZipWithApply.scala.template b/akka-stream/src/main/boilerplate/akka/stream/scaladsl/ZipWithApply.scala.template index 4b3faf53902..9edc68595c0 100644 --- a/akka-stream/src/main/boilerplate/akka/stream/scaladsl/ZipWithApply.scala.template +++ b/akka-stream/src/main/boilerplate/akka/stream/scaladsl/ZipWithApply.scala.template @@ -3,13 +3,17 @@ */ package akka.stream.scaladsl -import akka.stream.scaladsl.FlowGraph.FlowGraphBuilder -import akka.stream.scaladsl.Graphs.{ Graph, Ports } +import akka.stream.impl.GenJunctions._ +import akka.stream._ trait ZipWithApply { - [2..19#def apply[[#A1#], O](zipper: ([#A1#]) ⇒ O)(implicit b: FlowGraphBuilder) = - ZipWith1(zipper)# + [2..20#def apply[[#A1#], O](zipper: ([#A1#]) ⇒ O): Graph[FanInShape1[[#A1#], O], Unit] = + new Graph[FanInShape1[[#A1#], O], Unit] { + val shape = new FanInShape1[[#A1#], O] + val module = new ZipWith1Module(shape, zipper, OperationAttributes.name("ZipWith1")) + } + # ] diff --git a/akka-stream/src/main/scala/akka/stream/Graph.scala b/akka-stream/src/main/scala/akka/stream/Graph.scala new file mode 100644 index 00000000000..62f34c632e7 --- /dev/null +++ b/akka-stream/src/main/scala/akka/stream/Graph.scala @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2015 Typesafe Inc. + */ +package akka.stream + +import akka.stream.impl.StreamLayout + +trait Graph[+S <: Shape, +M] { + /** + * Type-level accessor for the shape parameter of this graph. + */ + type Shape = S + /** + * The shape of a graph is all that is externally visible: its inlets and outlets. + */ + def shape: S + /** + * INTERNAL API. + * + * Every materializable element must be backed by a stream layout module + */ + private[stream] def module: StreamLayout.Module +} diff --git a/akka-stream/src/main/scala/akka/stream/Shape.scala b/akka-stream/src/main/scala/akka/stream/Shape.scala new file mode 100644 index 00000000000..30ad86e7be9 --- /dev/null +++ b/akka-stream/src/main/scala/akka/stream/Shape.scala @@ -0,0 +1,107 @@ +/** + * Copyright (C) 2015 Typesafe Inc. + */ +package akka.stream + +import scala.collection.immutable +import scala.collection.JavaConverters._ + +sealed abstract class InPort +sealed abstract class OutPort + +final class Inlet[-T](override val toString: String) extends InPort +final class Outlet[+T](override val toString: String) extends OutPort + +abstract class Shape { + /** + * Scala API: get a list of all input ports + */ + def inlets: immutable.Seq[Inlet[_]] + + /** + * Scala API: get a list of all output ports + */ + def outlets: immutable.Seq[Outlet[_]] + + /** + * Create a copy of this Ports object, returning the same type as the + * original; this constraint can unfortunately not be expressed in the + * type system. + */ + def deepCopy(): Shape + + /** + * Java API: get a list of all input ports + */ + def getInlets: java.util.List[Inlet[_]] = inlets.asJava + + /** + * Java API: get a list of all output ports + */ + def getOutlets: java.util.List[Outlet[_]] = outlets.asJava +} + +/** + * Java API for creating custom Shape types. + */ +abstract class AbstractShape extends Shape { + def allInlets: java.util.List[Inlet[_]] + def allOutlets: java.util.List[Outlet[_]] + + final override lazy val inlets = allInlets.asScala.toList + final override lazy val outlets = allOutlets.asScala.toList + + final override def getInlets = allInlets + final override def getOutlets = allOutlets +} + +object EmptyShape extends Shape { + override val inlets = Nil + override val outlets = Nil + override def deepCopy() = this + + /** + * Java API: obtain EmptyShape instance + */ + def getInstance: Shape = this +} + +case class AmorphousShape(inlets: immutable.Seq[Inlet[_]], outlets: immutable.Seq[Outlet[_]]) extends Shape { + override def deepCopy() = AmorphousShape( + inlets.map(i => new Inlet[Any](i.toString)), + outlets.map(o => new Outlet[Any](o.toString)) + ) +} + +final case class SourceShape[+T](outlet: Outlet[T]) extends Shape { + override val inlets: immutable.Seq[Inlet[_]] = Nil + override val outlets: immutable.Seq[Outlet[_]] = List(outlet) + + override def deepCopy(): SourceShape[T] = SourceShape(new Outlet(outlet.toString)) +} + +final case class FlowShape[-I, +O](inlet: Inlet[I], outlet: Outlet[O]) extends Shape { + override val inlets: immutable.Seq[Inlet[_]] = List(inlet) + override val outlets: immutable.Seq[Outlet[_]] = List(outlet) + + override def deepCopy(): FlowShape[I, O] = FlowShape(new Inlet(inlet.toString), new Outlet(outlet.toString)) +} + +final case class SinkShape[-T](inlet: Inlet[T]) extends Shape { + override val inlets: immutable.Seq[Inlet[_]] = List(inlet) + override val outlets: immutable.Seq[Outlet[_]] = Nil + + override def deepCopy(): SinkShape[T] = SinkShape(new Inlet(inlet.toString)) +} + +/** + * In1 => Out1 + * Out2 <= In2 + */ +final case class BidiShape[-In1, +Out1, -In2, +Out2](in1: Inlet[In1], out1: Outlet[Out1], in2: Inlet[In2], out2: Outlet[Out2]) extends Shape { + override val inlets: immutable.Seq[Inlet[_]] = List(in1, in2) + override val outlets: immutable.Seq[Outlet[_]] = List(out1, out2) + + override def deepCopy(): BidiShape[In1, Out1, In2, Out2] = + BidiShape(new Inlet(in1.toString), new Outlet(out1.toString), new Inlet(in2.toString), new Outlet(out2.toString)) +} diff --git a/akka-stream/src/main/scala/akka/stream/impl/ActorBasedFlowMaterializer.scala b/akka-stream/src/main/scala/akka/stream/impl/ActorBasedFlowMaterializer.scala index cc5d9482aca..baf6ac129e9 100644 --- a/akka-stream/src/main/scala/akka/stream/impl/ActorBasedFlowMaterializer.scala +++ b/akka-stream/src/main/scala/akka/stream/impl/ActorBasedFlowMaterializer.scala @@ -14,7 +14,7 @@ import akka.stream.impl.Junctions._ import akka.stream.impl.StreamLayout.Module import akka.stream.impl.fusing.ActorInterpreter import akka.stream.scaladsl._ -import akka.stream.{ FlowMaterializer, MaterializerSettings } +import akka.stream.{ FlowMaterializer, MaterializerSettings, InPort } import org.reactivestreams._ import scala.concurrent.{ Await, ExecutionContext } @@ -96,22 +96,23 @@ case class ActorBasedFlowMaterializer(override val settings: MaterializerSetting op match { case fanin: FaninModule ⇒ val (props, inputs, output) = fanin match { - case MergeModule(ins, out, _) ⇒ - (FairMerge.props(effectiveAttributes.settings(settings), ins.size), ins, out) + case MergeModule(shape, _) ⇒ + (FairMerge.props(effectiveAttributes.settings(settings), shape.in.size), shape.in.toSeq, shape.out) case f: FlexiMergeModule[t, p] ⇒ - val flexi = f.flexi(f.ports) - (FlexiMerge.props(effectiveAttributes.settings(settings), f.ports, flexi), f.ports.inlets, f.ports.outlets.head) + val flexi = f.flexi(f.shape) + (FlexiMerge.props(effectiveAttributes.settings(settings), f.shape, flexi), f.shape.inlets, f.shape.outlets.head) // TODO each materialization needs its own logic - case MergePreferredModule(preferred, ins, out, _) ⇒ - (UnfairMerge.props(effectiveAttributes.settings(settings), ins.size + 1), preferred +: ins, out) + case MergePreferredModule(shape, _) ⇒ + (UnfairMerge.props(effectiveAttributes.settings(settings), shape.inlets.size), shape.inlets, shape.out) - case ConcatModule(first, second, out, _) ⇒ - (Concat.props(effectiveAttributes.settings(settings)), List(first, second), out) + case ConcatModule(shape, _) ⇒ + require(shape.in.size == 2, "currently only supporting concatenation of exactly two inputs") // FIXME + (Concat.props(effectiveAttributes.settings(settings)), shape.in.toSeq, shape.out) case zip: ZipWithModule ⇒ - (zip.props(effectiveAttributes.settings(settings)), zip.ins, zip.outPorts.head) + (zip.props(effectiveAttributes.settings(settings)), zip.shape.inlets, zip.outPorts.head) } val impl = actorOf(props, stageName(effectiveAttributes), effectiveAttributes.settings(settings).dispatcher) val publisher = new ActorPublisher[Any](impl) @@ -124,14 +125,14 @@ case class ActorBasedFlowMaterializer(override val settings: MaterializerSetting case fanout: FanoutModule ⇒ val (props, in, outs) = fanout match { case r: FlexiRouteModule[t, p] ⇒ - val flexi = r.flexi(r.ports) - (FlexiRoute.props(effectiveAttributes.settings(settings), r.ports, flexi), r.ports.inlets.head, r.ports.outlets) - case BroadcastModule(in, outs, _) ⇒ - (Broadcast.props(effectiveAttributes.settings(settings), outs.size), in, outs) - case BalanceModule(in, outs, waitForDownstreams, _) ⇒ - (Balance.props(effectiveAttributes.settings(settings), outs.size, waitForDownstreams), in, outs) - case UnzipModule(in, left, right, _) ⇒ - (Unzip.props(effectiveAttributes.settings(settings)), in, List(left, right)) + val flexi = r.flexi(r.shape) + (FlexiRoute.props(effectiveAttributes.settings(settings), r.shape, flexi), r.shape.inlets.head: InPort, r.shape.outlets) + case BroadcastModule(shape, _) ⇒ + (Broadcast.props(effectiveAttributes.settings(settings), shape.out.size), shape.in, shape.out.toSeq) + case BalanceModule(shape, waitForDownstreams, _) ⇒ + (Balance.props(effectiveAttributes.settings(settings), shape.out.size, waitForDownstreams), shape.in, shape.out.toSeq) + case UnzipModule(shape, _) ⇒ + (Unzip.props(effectiveAttributes.settings(settings)), shape.in, shape.outlets) } val impl = actorOf(props, stageName(effectiveAttributes), effectiveAttributes.settings(settings).dispatcher) val publishers = Vector.tabulate(outs.size)(id ⇒ new ActorPublisher[Any](impl) { // FIXME switch to List.tabulate for inputCount < 8? diff --git a/akka-stream/src/main/scala/akka/stream/impl/FanIn.scala b/akka-stream/src/main/scala/akka/stream/impl/FanIn.scala index 0bb234d33e5..1d147e148d0 100644 --- a/akka-stream/src/main/scala/akka/stream/impl/FanIn.scala +++ b/akka-stream/src/main/scala/akka/stream/impl/FanIn.scala @@ -8,7 +8,7 @@ import akka.actor.Props import akka.stream.MaterializerSettings import akka.stream.actor.{ ActorSubscriberMessage, ActorSubscriber } import akka.stream.scaladsl.FlexiMerge.MergeLogic -import akka.stream.scaladsl.Graphs.{ InPort, Ports } +import akka.stream.{ InPort, Shape } import org.reactivestreams.{ Subscription, Subscriber } import akka.actor.DeadLetterSuppression @@ -288,7 +288,7 @@ private[akka] final class UnfairMerge(_settings: MaterializerSettings, * INTERNAL API */ private[akka] object FlexiMerge { - def props[T, P <: Ports](settings: MaterializerSettings, ports: P, mergeLogic: MergeLogic[T]): Props = + def props[T, S <: Shape](settings: MaterializerSettings, ports: S, mergeLogic: MergeLogic[T]): Props = Props(new FlexiMergeImpl(settings, ports, mergeLogic)) } diff --git a/akka-stream/src/main/scala/akka/stream/impl/FanOut.scala b/akka-stream/src/main/scala/akka/stream/impl/FanOut.scala index 37a5c927e4e..2f1028e9678 100644 --- a/akka-stream/src/main/scala/akka/stream/impl/FanOut.scala +++ b/akka-stream/src/main/scala/akka/stream/impl/FanOut.scala @@ -4,7 +4,7 @@ package akka.stream.impl import akka.stream.scaladsl.FlexiRoute.RouteLogic -import akka.stream.scaladsl.Graphs.Ports +import akka.stream.Shape import scala.collection.immutable import akka.actor.Actor @@ -352,6 +352,6 @@ private[akka] class Unzip(_settings: MaterializerSettings) extends FanOut(_setti * INTERNAL API */ private[akka] object FlexiRoute { - def props[T, P <: Ports](settings: MaterializerSettings, ports: P, routeLogic: RouteLogic[T]): Props = + def props[T, S <: Shape](settings: MaterializerSettings, ports: S, routeLogic: RouteLogic[T]): Props = Props(new FlexiRouteImpl(settings, ports, routeLogic)) } diff --git a/akka-stream/src/main/scala/akka/stream/impl/FlexiMergeImpl.scala b/akka-stream/src/main/scala/akka/stream/impl/FlexiMergeImpl.scala index 099cc46f804..6ced6619c75 100644 --- a/akka-stream/src/main/scala/akka/stream/impl/FlexiMergeImpl.scala +++ b/akka-stream/src/main/scala/akka/stream/impl/FlexiMergeImpl.scala @@ -4,7 +4,7 @@ package akka.stream.impl import akka.stream.scaladsl.FlexiMerge.{ Read, ReadAll, ReadAny, ReadPreferred } -import akka.stream.scaladsl.Graphs.{ Ports, InPort } +import akka.stream.{ Shape, InPort } import akka.stream.{ MaterializerSettings, scaladsl } import scala.collection.breakOut @@ -14,17 +14,16 @@ import scala.util.control.NonFatal /** * INTERNAL API */ -private[akka] class FlexiMergeImpl[T, P <: Ports]( +private[akka] class FlexiMergeImpl[T, S <: Shape]( _settings: MaterializerSettings, - ports: P, - mergeLogic: scaladsl.FlexiMerge.MergeLogic[T]) extends FanIn(_settings, ports.inlets.size) { + shape: S, + mergeLogic: scaladsl.FlexiMerge.MergeLogic[T]) extends FanIn(_settings, shape.inlets.size) { private type StateT = mergeLogic.State[_] private type CompletionT = mergeLogic.CompletionHandling - private type InP = StreamLayout.InPort - val inputMapping: Array[InP] = ports.inlets.toArray - val indexOf: Map[InP, Int] = ports.inlets.zipWithIndex.toMap + val inputMapping: Array[InPort] = shape.inlets.toArray + val indexOf: Map[InPort, Int] = shape.inlets.zipWithIndex.toMap private var behavior: StateT = _ private def anyBehavior = behavior.asInstanceOf[mergeLogic.State[Any]] @@ -60,14 +59,14 @@ private[akka] class FlexiMergeImpl[T, P <: Ports]( override def error(cause: Throwable): Unit = fail(cause) - override def cancel(input: InP): Unit = inputBunch.cancel(indexOf(input)) + override def cancel(input: InPort): Unit = inputBunch.cancel(indexOf(input)) override def changeCompletionHandling(newCompletion: CompletionT): Unit = FlexiMergeImpl.this.changeCompletionHandling(newCompletion) } - private def markInputs(inputs: Array[InP]): Unit = { + private def markInputs(inputs: Array[InPort]): Unit = { inputBunch.unmarkAllInputs() var i = 0 while (i < inputs.length) { @@ -78,7 +77,7 @@ private[akka] class FlexiMergeImpl[T, P <: Ports]( } } - private def include(port: InP): Boolean = include(indexOf(port)) + private def include(port: InPort): Boolean = include(indexOf(port)) private def include(portIndex: Int): Boolean = portIndex >= 0 && portIndex < inputCount && !inputBunch.isCancelled(portIndex) && !inputBunch.isDepleted(portIndex) @@ -149,7 +148,7 @@ private[akka] class FlexiMergeImpl[T, P <: Ports]( }) - private def triggerCompletionAfterRead(inputs: Seq[InP]): Unit = { + private def triggerCompletionAfterRead(inputs: Seq[InPort]): Unit = { var j = 0 while (j < inputs.length) { triggerCompletionAfterRead(inputs(j)) @@ -157,11 +156,11 @@ private[akka] class FlexiMergeImpl[T, P <: Ports]( } } - private def triggerCompletionAfterRead(inputHandle: InP): Unit = + private def triggerCompletionAfterRead(inputHandle: InPort): Unit = if (inputBunch.isDepleted(indexOf(inputHandle))) triggerCompletion(inputHandle) - private def triggerCompletion(in: InP): Unit = + private def triggerCompletion(in: InPort): Unit = changeBehavior( try completion.onComplete(ctx, in) catch { diff --git a/akka-stream/src/main/scala/akka/stream/impl/FlexiRouteImpl.scala b/akka-stream/src/main/scala/akka/stream/impl/FlexiRouteImpl.scala index 28eb14acf66..617d3df37b3 100644 --- a/akka-stream/src/main/scala/akka/stream/impl/FlexiRouteImpl.scala +++ b/akka-stream/src/main/scala/akka/stream/impl/FlexiRouteImpl.scala @@ -5,26 +5,25 @@ package akka.stream.impl import akka.stream.{ scaladsl, MaterializerSettings } import akka.stream.impl.FanOut.OutputBunch -import akka.stream.scaladsl.Graphs.Ports +import akka.stream.{ Shape, OutPort } import scala.util.control.NonFatal /** * INTERNAL API */ -private[akka] class FlexiRouteImpl[T, P <: Ports](_settings: MaterializerSettings, - ports: P, +private[akka] class FlexiRouteImpl[T, S <: Shape](_settings: MaterializerSettings, + shape: S, routeLogic: scaladsl.FlexiRoute.RouteLogic[T]) - extends FanOut(_settings, ports.outlets.size) { + extends FanOut(_settings, shape.outlets.size) { import akka.stream.scaladsl.FlexiRoute._ - private type OutP = StreamLayout.OutPort - private type StateT = routeLogic.State[Any] + private type StateT = routeLogic.State[AnyRef] private type CompletionT = routeLogic.CompletionHandling - val outputMapping: Array[OutP] = ports.outlets.toArray - val indexOf: Map[OutP, Int] = ports.outlets.zipWithIndex.toMap + val outputMapping: Array[OutPort] = shape.outlets.toArray + val indexOf: Map[OutPort, Int] = shape.outlets.zipWithIndex.toMap private var behavior: StateT = _ private var completion: CompletionT = _ @@ -50,11 +49,11 @@ private[akka] class FlexiRouteImpl[T, P <: Ports](_settings: MaterializerSetting } } - private val ctx: routeLogic.RouteLogicContext[Any] = new routeLogic.RouteLogicContext[Any] { - override def isDemandAvailable(output: OutP): Boolean = + private val ctx: routeLogic.RouteLogicContext[AnyRef] = new routeLogic.RouteLogicContext[AnyRef] { + override def isDemandAvailable(output: OutPort): Boolean = (indexOf(output) < outputCount) && outputBunch.isPending(indexOf(output)) - override def emit(output: OutP, elem: Any): Unit = { + override def emit(output: OutPort, elem: AnyRef): Unit = { val idx = indexOf(output) require(outputBunch.isPending(idx), s"emit to [$output] not allowed when no demand available") outputBunch.enqueue(idx, elem) @@ -66,12 +65,12 @@ private[akka] class FlexiRouteImpl[T, P <: Ports](_settings: MaterializerSetting context.stop(self) } - override def complete(output: OutP): Unit = + override def complete(output: OutPort): Unit = outputBunch.complete(indexOf(output)) override def error(cause: Throwable): Unit = fail(cause) - override def error(output: OutP, cause: Throwable): Unit = + override def error(output: OutPort, cause: Throwable): Unit = outputBunch.error(indexOf(output), cause) override def changeCompletionHandling(newCompletion: CompletionT): Unit = @@ -79,7 +78,7 @@ private[akka] class FlexiRouteImpl[T, P <: Ports](_settings: MaterializerSetting } - private def markOutputs(outputs: Array[OutP]): Unit = { + private def markOutputs(outputs: Array[OutPort]): Unit = { outputBunch.unmarkAllOutputs() var i = 0 while (i < outputs.length) { diff --git a/akka-stream/src/main/scala/akka/stream/impl/Flows.scala b/akka-stream/src/main/scala/akka/stream/impl/Flows.scala index d4c1e8f244a..4a976e7f48d 100644 --- a/akka-stream/src/main/scala/akka/stream/impl/Flows.scala +++ b/akka-stream/src/main/scala/akka/stream/impl/Flows.scala @@ -3,16 +3,15 @@ */ package akka.stream.impl -import akka.stream.scaladsl.Graphs +import akka.stream._ trait FlowModule[In, Out, Mat] extends StreamLayout.Module { override def subModules = Set.empty override def downstreams = Map.empty override def upstreams = Map.empty - val inPort = new Graphs.InPort[In]("Flow.in") - val outPort = new Graphs.OutPort[Out]("Flow.out") - override val inPorts: Set[StreamLayout.InPort] = Set(inPort) - override val outPorts: Set[StreamLayout.OutPort] = Set(outPort) - + + val inPort = new Inlet[In]("Flow.in") + val outPort = new Outlet[Out]("Flow.out") + override val shape = new FlowShape(inPort, outPort) } diff --git a/akka-stream/src/main/scala/akka/stream/impl/Junctions.scala b/akka-stream/src/main/scala/akka/stream/impl/Junctions.scala index e29af70ad5a..84892de05e9 100644 --- a/akka-stream/src/main/scala/akka/stream/impl/Junctions.scala +++ b/akka-stream/src/main/scala/akka/stream/impl/Junctions.scala @@ -1,9 +1,14 @@ package akka.stream.impl -import akka.stream.impl.StreamLayout.{ Mapping, OutPort, InPort, Module } +import akka.stream.impl.StreamLayout.{ Mapping, Module } import akka.stream.scaladsl.FlexiRoute.RouteLogic -import akka.stream.scaladsl.{ Graphs, OperationAttributes } +import akka.stream.scaladsl.OperationAttributes +import akka.stream.{ Inlet, Outlet, Shape, InPort, OutPort } import akka.stream.scaladsl.FlexiMerge.MergeLogic +import akka.stream.UniformFanInShape +import akka.stream.UniformFanOutShape +import akka.stream.FanOutShape2 +import akka.stream.scaladsl.MergePreferred object Junctions { @@ -22,166 +27,107 @@ object Junctions { private[akka] trait FanoutModule extends JunctionModule final case class MergeModule[T]( - ins: Vector[Graphs.InPort[T]], - out: Graphs.OutPort[T], + shape: UniformFanInShape[T, T], override val attributes: OperationAttributes = name("merge")) extends FaninModule { - override val inPorts: Set[InPort] = ins.toSet - override val outPorts: Set[OutPort] = Set(out) - override def withAttributes(attr: OperationAttributes): Module = copy(attributes = attr) override def carbonCopy: () ⇒ Mapping = () ⇒ { - val newMerge = MergeModule( - ins.map(i ⇒ new Graphs.InPort[Any](i.toString)), - new Graphs.OutPort[Any](out.toString), - attributes) - - Mapping(newMerge, ins.zip(newMerge.ins).toMap, Map(out -> newMerge.out)) + val newMerge = MergeModule(shape.deepCopy(), attributes) + Mapping(newMerge, shape.in.zip(newMerge.shape.in).toMap, Map(shape.out -> newMerge.shape.out)) } } final case class BroadcastModule[T]( - in: Graphs.InPort[T], - outs: Vector[Graphs.OutPort[T]], + shape: UniformFanOutShape[T, T], override val attributes: OperationAttributes = name("broadcast")) extends FanoutModule { - override val inPorts: Set[InPort] = Set(in) - override val outPorts: Set[OutPort] = outs.toSet - override def withAttributes(attr: OperationAttributes): Module = copy(attributes = attr) override def carbonCopy: () ⇒ Mapping = () ⇒ { - val newMerge = BroadcastModule( - new Graphs.InPort[Any](in.toString), - outs.map(o ⇒ new Graphs.OutPort[Any](o.toString)), - attributes) - - Mapping(newMerge, Map(in -> newMerge.in), outs.zip(newMerge.outs).toMap) + val newMerge = BroadcastModule(shape.deepCopy(), attributes) + Mapping(newMerge, Map(shape.in -> newMerge.shape.in), shape.out.zip(newMerge.shape.out).toMap) } } final case class MergePreferredModule[T]( - preferred: Graphs.InPort[T], - ins: Vector[Graphs.InPort[T]], - out: Graphs.OutPort[T], + shape: MergePreferred.MergePreferredShape[T], override val attributes: OperationAttributes = name("preferred")) extends FaninModule { - override val inPorts: Set[InPort] = ins.toSet + preferred - override val outPorts: Set[OutPort] = Set(out) override def withAttributes(attr: OperationAttributes): Module = copy(attributes = attr) override def carbonCopy: () ⇒ Mapping = () ⇒ { - val newMerge = MergePreferredModule( - new Graphs.InPort[Any](preferred.toString), - ins.map(i ⇒ new Graphs.InPort[Any](i.toString)), - new Graphs.OutPort[Any](out.toString), - attributes) - - Mapping(newMerge, (ins.zip(newMerge.ins) :+ (preferred -> newMerge.preferred)).toMap, Map(out -> newMerge.out)) + val newMerge = MergePreferredModule(shape.deepCopy(), attributes) + Mapping(newMerge, shape.inlets.zip(newMerge.shape.inlets).toMap, Map(shape.out -> newMerge.shape.out)) } } - final case class FlexiMergeModule[T, P <: Graphs.Ports]( - ports: P, - flexi: P ⇒ MergeLogic[T], + final case class FlexiMergeModule[T, S <: Shape]( + shape: S, + flexi: S ⇒ MergeLogic[T], override val attributes: OperationAttributes = name("flexiMerge")) extends FaninModule { - require(ports.outlets.size == 1, "FlexiMerge can have only one output port") - - override val inPorts: Set[InPort] = ports.inlets.toSet - override val outPorts: Set[OutPort] = ports.outlets.toSet + require(shape.outlets.size == 1, "FlexiMerge can have only one output port") override def withAttributes(attributes: OperationAttributes): Module = copy(attributes = attributes) override def carbonCopy: () ⇒ Mapping = () ⇒ { - val newModule = new FlexiMergeModule(ports.deepCopy().asInstanceOf[P], flexi, attributes) - Mapping(newModule, Map(ports.inlets.zip(newModule.ports.inlets): _*), Map(ports.outlets.head → newModule.ports.outlets.head)) + val newModule = new FlexiMergeModule(shape.deepCopy().asInstanceOf[S], flexi, attributes) + Mapping(newModule, Map(shape.inlets.zip(newModule.shape.inlets): _*), Map(shape.outlets.head → newModule.shape.outlets.head)) } } - final case class FlexiRouteModule[T, P <: Graphs.Ports]( - ports: P, - flexi: P ⇒ RouteLogic[T], + final case class FlexiRouteModule[T, S <: Shape]( + shape: S, + flexi: S ⇒ RouteLogic[T], override val attributes: OperationAttributes = name("flexiRoute")) extends FanoutModule { - require(ports.inlets.size == 1, "FlexiRoute can have only one input port") - - override val inPorts: Set[InPort] = ports.inlets.toSet - override val outPorts: Set[OutPort] = ports.outlets.toSet + require(shape.inlets.size == 1, "FlexiRoute can have only one input port") override def withAttributes(attributes: OperationAttributes): Module = copy(attributes = attributes) override def carbonCopy: () ⇒ Mapping = () ⇒ { - val newModule = new FlexiRouteModule(ports.deepCopy().asInstanceOf[P], flexi, attributes) - Mapping(newModule, Map(ports.inlets.zip(newModule.ports.inlets): _*), Map(ports.outlets.head → newModule.ports.outlets.head)) + val newModule = new FlexiRouteModule(shape.deepCopy().asInstanceOf[S], flexi, attributes) + Mapping(newModule, Map(shape.inlets.zip(newModule.shape.inlets): _*), Map(shape.outlets.head → newModule.shape.outlets.head)) } } final case class BalanceModule[T]( - in: Graphs.InPort[T], - outs: Vector[Graphs.OutPort[T]], + shape: UniformFanOutShape[T, T], waitForAllDownstreams: Boolean, override val attributes: OperationAttributes = name("broadcast")) extends FanoutModule { - override val inPorts: Set[InPort] = Set(in) - override val outPorts: Set[OutPort] = outs.toSet - override def withAttributes(attr: OperationAttributes): Module = copy(attributes = attr) override def carbonCopy: () ⇒ Mapping = () ⇒ { - val newMerge = BalanceModule( - new Graphs.InPort[Any](in.toString), - outs.map(o ⇒ new Graphs.OutPort[Any](o.toString)), - waitForAllDownstreams, - attributes) - - Mapping(newMerge, Map(in -> newMerge.in), outs.zip(newMerge.outs).toMap) + val newMerge = BalanceModule(shape.deepCopy(), waitForAllDownstreams, attributes) + Mapping(newMerge, Map(shape.in -> newMerge.shape.in), shape.out.zip(newMerge.shape.out).toMap) } } final case class UnzipModule[A, B]( - in: Graphs.InPort[(A, B)], - left: Graphs.OutPort[A], - right: Graphs.OutPort[B], + shape: FanOutShape2[(A, B), A, B], override val attributes: OperationAttributes = name("unzip")) extends FanoutModule { - override val inPorts: Set[InPort] = Set(in) - override val outPorts: Set[OutPort] = Set(left, right) - override def withAttributes(attr: OperationAttributes): Module = copy(attributes = attr) override def carbonCopy: () ⇒ Mapping = () ⇒ { - val newZip2 = UnzipModule( - new Graphs.InPort[(A, B)](in.toString), - new Graphs.OutPort[A](left.toString), - new Graphs.OutPort[B](right.toString), - attributes) - - Mapping(newZip2, Map(in -> newZip2.in), Map(right -> newZip2.right, left -> newZip2.right)) + val newZip2 = UnzipModule(shape.deepCopy(), attributes) + Mapping(newZip2, Map(shape.in -> newZip2.shape.in), Map(shape.out0 -> newZip2.shape.out0, shape.out1 -> newZip2.shape.out1)) } } - final case class ConcatModule[A1, A2, B]( - first: Graphs.InPort[A1], - second: Graphs.InPort[A2], - out: Graphs.OutPort[B], + final case class ConcatModule[T]( + shape: UniformFanInShape[T, T], override val attributes: OperationAttributes = name("concat")) extends FaninModule { - override val inPorts: Set[InPort] = Set(first, second) - override val outPorts: Set[OutPort] = Set(out) - override def withAttributes(attr: OperationAttributes): Module = copy(attributes = attr) override def carbonCopy: () ⇒ Mapping = () ⇒ { - val newZip2 = ConcatModule( - new Graphs.InPort[A1](first.toString), - new Graphs.InPort[A1](second.toString), - new Graphs.OutPort[B](out.toString), - attributes) + val newZip2 = ConcatModule(shape.deepCopy(), attributes) - Mapping(newZip2, Map(first -> newZip2.first, second -> newZip2.second), Map(out -> newZip2.out)) + Mapping(newZip2, Map(shape.in(0) -> newZip2.shape.in(0), shape.in(1) -> newZip2.shape.in(1)), Map(shape.out -> newZip2.shape.out)) } } diff --git a/akka-stream/src/main/scala/akka/stream/impl/Sinks.scala b/akka-stream/src/main/scala/akka/stream/impl/Sinks.scala index 4d1590ad569..9089713ca23 100644 --- a/akka-stream/src/main/scala/akka/stream/impl/Sinks.scala +++ b/akka-stream/src/main/scala/akka/stream/impl/Sinks.scala @@ -4,17 +4,17 @@ package akka.stream.impl import java.util.concurrent.atomic.AtomicReference - import akka.actor.{ ActorRef, Props } -import akka.stream.impl.StreamLayout.{ Mapping, Module, OutPort, InPort } +import akka.stream.impl.StreamLayout.{ Mapping, Module } import akka.stream.scaladsl.OperationAttributes._ -import akka.stream.scaladsl.{ Graphs, Sink, OperationAttributes, Source } +import akka.stream.scaladsl.{ Sink, OperationAttributes, Source } import akka.stream.stage._ +import akka.stream.{ Inlet, Outlet, InPort, OutPort } import org.reactivestreams.{ Processor, Publisher, Subscriber, Subscription } - import scala.annotation.unchecked.uncheckedVariance import scala.concurrent.{ Future, Promise } import scala.util.{ Failure, Success, Try } +import akka.stream.SinkShape trait SinkModule[-In, Mat] extends StreamLayout.Module { @@ -34,9 +34,8 @@ trait SinkModule[-In, Mat] extends StreamLayout.Module { override def downstreams: Map[OutPort, InPort] = Map.empty override def upstreams: Map[InPort, OutPort] = Map.empty - val inPort: Graphs.InPort[In] = new Graphs.InPort[In]("Sink.in") - override def inPorts: Set[InPort] = Set(inPort) - override def outPorts: Set[OutPort] = Set.empty + val inPort: Inlet[In] = new Inlet[In]("Sink.in") + override val shape = new SinkShape[In](inPort) protected def newInstance: SinkModule[In, Mat] override def carbonCopy: () ⇒ Mapping = () ⇒ { diff --git a/akka-stream/src/main/scala/akka/stream/impl/Sources.scala b/akka-stream/src/main/scala/akka/stream/impl/Sources.scala index 4d1172ca176..5dca396e5f0 100644 --- a/akka-stream/src/main/scala/akka/stream/impl/Sources.scala +++ b/akka-stream/src/main/scala/akka/stream/impl/Sources.scala @@ -6,7 +6,7 @@ package akka.stream.impl import java.util.concurrent.atomic.AtomicBoolean import akka.actor.{ PoisonPill, Cancellable, Props, ActorRef } import akka.stream.FlowMaterializer -import akka.stream.scaladsl.{ Graphs, OperationAttributes } +import akka.stream.scaladsl.OperationAttributes import org.reactivestreams._ import scala.annotation.unchecked.uncheckedVariance import scala.annotation.tailrec @@ -16,18 +16,17 @@ import scala.concurrent.duration.FiniteDuration import scala.util.control.NonFatal import scala.util.{ Success, Failure } import akka.stream.impl.StreamLayout.{ Module, Mapping } +import akka.stream.{ Inlet, Outlet, InPort, OutPort } +import akka.stream.SourceShape trait SourceModule[+Out, +Mat] extends Module { - - private type IP = StreamLayout.InPort - private type OP = StreamLayout.OutPort + + val outPort: Outlet[Out] = new Outlet[Out]("Source.out") + override val shape = new SourceShape[Out](outPort) override def subModules: Set[Module] = Set.empty - override def upstreams: Map[IP, OP] = Map.empty - override def downstreams: Map[OP, IP] = Map.empty - override def inPorts: Set[IP] = Set.empty - val outPort: Graphs.OutPort[Out] = new Graphs.OutPort[Out]("Source.out") - override val outPorts: Set[OP] = Set(outPort) + override def upstreams: Map[InPort, OutPort] = Map.empty + override def downstreams: Map[OutPort, InPort] = Map.empty /** * This method is only used for Sources that return true from [[#isActive]], which then must diff --git a/akka-stream/src/main/scala/akka/stream/impl/StreamLayout.scala b/akka-stream/src/main/scala/akka/stream/impl/StreamLayout.scala index b828128bf3f..05356a85419 100644 --- a/akka-stream/src/main/scala/akka/stream/impl/StreamLayout.scala +++ b/akka-stream/src/main/scala/akka/stream/impl/StreamLayout.scala @@ -3,18 +3,15 @@ */ package akka.stream.impl -import akka.stream.scaladsl.OperationAttributes +import akka.stream.scaladsl.{ Keep, OperationAttributes } +import akka.stream.{ InPort, OutPort, Shape, EmptyShape, AmorphousShape } import org.reactivestreams.{ Subscription, Publisher, Subscriber } -import akka.stream.scaladsl.Keep /** * INTERNAL API */ private[akka] object StreamLayout { - class InPort - class OutPort - // TODO: Materialization order // TODO: Special case linear composites // TODO: Cycles @@ -28,8 +25,10 @@ private[akka] object StreamLayout { case class Mapping(module: Module, inPorts: Map[InPort, InPort], outPorts: Map[OutPort, OutPort]) trait Module { - def inPorts: Set[InPort] - def outPorts: Set[OutPort] + def shape: Shape + + final val inPorts: Set[InPort] = shape.inlets.toSet + final val outPorts: Set[OutPort] = shape.outlets.toSet def isRunnable: Boolean = inPorts.isEmpty && outPorts.isEmpty def isSink: Boolean = (inPorts.size == 1) && outPorts.isEmpty @@ -42,8 +41,7 @@ private[akka] object StreamLayout { CompositeModule( subModules, - inPorts - to, - outPorts - from, + AmorphousShape(shape.inlets.filterNot(_ == to), shape.outlets.filterNot(_ == from)), downstreams.updated(from, to), upstreams.updated(to, from), materializedValueComputation, @@ -57,8 +55,7 @@ private[akka] object StreamLayout { def transformMaterializedValue(f: Any ⇒ Any): Module = { CompositeModule( subModules = this.subModules, - inPorts, - outPorts, + shape, downstreams, upstreams, Transform(f, this.materializedValueComputation), @@ -80,8 +77,7 @@ private[akka] object StreamLayout { CompositeModule( modules1 ++ modules2, - this.inPorts ++ that.inPorts, - this.outPorts ++ that.outPorts, + AmorphousShape(shape.inlets ++ that.shape.inlets, shape.outlets ++ that.shape.outlets), this.downstreams ++ that.downstreams, this.upstreams ++ that.upstreams, if (f eq Keep.left) materializedValueComputation @@ -98,8 +94,7 @@ private[akka] object StreamLayout { def wrap(): Module = { CompositeModule( subModules = Set(this), - inPorts, - outPorts, + shape, downstreams, upstreams, Atomic(this), @@ -127,10 +122,9 @@ private[akka] object StreamLayout { } object EmptyModule extends Module { - override def subModules: Set[Module] = Set.empty + override def shape = EmptyShape - override def inPorts: Set[InPort] = Set.empty - override def outPorts: Set[OutPort] = Set.empty + override def subModules: Set[Module] = Set.empty override def downstreams: Map[OutPort, InPort] = Map.empty override def upstreams: Map[InPort, OutPort] = Map.empty @@ -152,8 +146,7 @@ private[akka] object StreamLayout { final case class CompositeModule( subModules: Set[Module], - inPorts: Set[InPort], - outPorts: Set[OutPort], + shape: Shape, downstreams: Map[OutPort, InPort], upstreams: Map[InPort, OutPort], override val materializedValueComputation: MaterializedValueNode, diff --git a/akka-stream/src/main/scala/akka/stream/javadsl/FlattenStrategy.scala b/akka-stream/src/main/scala/akka/stream/javadsl/FlattenStrategy.scala index 69aa49d0079..64b906e03de 100644 --- a/akka-stream/src/main/scala/akka/stream/javadsl/FlattenStrategy.scala +++ b/akka-stream/src/main/scala/akka/stream/javadsl/FlattenStrategy.scala @@ -12,8 +12,8 @@ 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]: akka.stream.FlattenStrategy[javadsl.Source[T], T] = - akka.stream.FlattenStrategy.Concat[T]().asInstanceOf[akka.stream.FlattenStrategy[javadsl.Source[T], T]] + def concat[T]: akka.stream.FlattenStrategy[javadsl.Source[T, _], T] = + akka.stream.FlattenStrategy.Concat[T]().asInstanceOf[akka.stream.FlattenStrategy[javadsl.Source[T, _], T]] // TODO so in theory this should be safe, but let's rethink the design later } diff --git a/akka-stream/src/main/scala/akka/stream/javadsl/FlexiMerge.scala b/akka-stream/src/main/scala/akka/stream/javadsl/FlexiMerge.scala index aafe839b171..601ed3e065d 100644 --- a/akka-stream/src/main/scala/akka/stream/javadsl/FlexiMerge.scala +++ b/akka-stream/src/main/scala/akka/stream/javadsl/FlexiMerge.scala @@ -9,42 +9,10 @@ import akka.stream.scaladsl.FlexiMerge.ReadAllInputsBase import scala.collection.immutable import java.util.{ List ⇒ JList } import akka.japi.Util.immutableIndexedSeq -import akka.stream.impl.Ast.Defaults._ -import akka.stream.impl.FlexiMergeImpl.MergeLogicFactory +import akka.stream._ object FlexiMerge { - /** - * @see [[InputPort]] - */ - sealed trait InputHandle extends scaladsl.FlexiMerge.InputHandle - - /** - * An `InputPort` can be connected to a [[Source]] with the [[FlowGraphBuilder]]. - * The `InputPort` is also an [[InputHandle]], which is passed as parameter - * to [[State]] `onInput` when an input element has been read so that you - * can know exactly from which input the element was read. - */ - class InputPort[In, Out] private[akka] (val port: Int, parent: FlexiMerge[_, Out]) - extends JunctionInPort[In] with InputHandle { - - def handle: InputHandle = this - - override val asScala: scaladsl.JunctionInPort[In] = new scaladsl.JunctionInPort[In] { - override def port: Int = InputPort.this.port - override def vertex = parent.vertex - type NextT = Nothing - override def next = scaladsl.NoNext - } - - /** - * INTERNAL API - */ - override private[akka] def portIndex: Int = port - - override def toString: String = s"InputPort($port)" - } - sealed trait ReadCondition /** @@ -56,7 +24,7 @@ object FlexiMerge { * has been completed. `IllegalArgumentException` is thrown if * that is not obeyed. */ - class Read(val input: InputHandle) extends ReadCondition + class Read(val input: InPort) extends ReadCondition /** * Read condition for the [[State]] that will be @@ -66,7 +34,7 @@ object FlexiMerge { * Cancelled and completed inputs are not used, i.e. it is allowed * to specify them in the list of `inputs`. */ - class ReadAny(val inputs: JList[InputHandle]) extends ReadCondition + class ReadAny(val inputs: JList[InPort]) extends ReadCondition /** * Read condition for the [[FlexiMerge#State]] that will be @@ -78,7 +46,7 @@ object FlexiMerge { * Cancelled and completed inputs are not used, i.e. it is allowed * to specify them in the list of `inputs`. */ - class ReadPreferred(val preferred: InputHandle, val secondaries: JList[InputHandle]) extends ReadCondition + class ReadPreferred(val preferred: InPort, val secondaries: JList[InPort]) extends ReadCondition /** * Read condition for the [[FlexiMerge#State]] that will be @@ -91,17 +59,17 @@ object FlexiMerge { * the resulting [[ReadAllInputs]] will then not contain values for this element, which can be * handled via supplying a default value instead of the value from the (now cancelled) input. */ - class ReadAll(val inputs: JList[InputHandle]) extends ReadCondition + class ReadAll(val inputs: JList[InPort]) extends ReadCondition /** * Provides typesafe accessors to values from inputs supplied to [[ReadAll]]. */ - final class ReadAllInputs(map: immutable.Map[scaladsl.FlexiMerge.InputHandle, Any]) extends ReadAllInputsBase { + final class ReadAllInputs(map: immutable.Map[InPort, Any]) extends ReadAllInputsBase { /** Returns the value for the given [[InputPort]], or `null` if this input was cancelled. */ - def get[T](input: InputPort[T, _]): T = getOrDefault(input, null) + def get[T](input: Inlet[T]): T = getOrDefault(input, null) /** Returns the value for the given [[InputPort]], or `defaultValue`. */ - def getOrDefault[T, B >: T](input: InputPort[T, _], defaultValue: B): T = map.getOrElse(input, defaultValue).asInstanceOf[T] + def getOrDefault[T, B >: T](input: Inlet[T], defaultValue: B): T = map.getOrElse(input, defaultValue).asInstanceOf[T] } /** @@ -135,7 +103,7 @@ object FlexiMerge { /** * Cancel a specific upstream input stream. */ - def cancel(input: InputHandle): Unit + def cancel(input: InPort): Unit /** * Replace current [[CompletionHandling]]. @@ -157,8 +125,8 @@ object FlexiMerge { * or it can be swallowed to continue with remaining inputs. */ abstract class CompletionHandling[Out] { - def onComplete(ctx: MergeLogicContext[Out], input: InputHandle): State[_, Out] - def onError(ctx: MergeLogicContext[Out], input: InputHandle, cause: Throwable): State[_, Out] + def onComplete(ctx: MergeLogicContext[Out], input: InPort): State[Out] + def onError(ctx: MergeLogicContext[Out], input: InPort, cause: Throwable): State[Out] } /** @@ -170,8 +138,8 @@ object FlexiMerge { * The `onInput` method is called when an `element` was read from the `input`. * The method returns next behavior or [[MergeLogic#sameState]] to keep current behavior. */ - abstract class State[In, Out](val condition: ReadCondition) { - def onInput(ctx: MergeLogicContext[Out], input: InputHandle, element: In): State[_, Out] + abstract class State[Out](val condition: ReadCondition) { + def onInput(ctx: MergeLogicContext[Out], input: InPort, element: AnyRef): State[Out] } /** @@ -180,24 +148,23 @@ object FlexiMerge { * * Concrete instance is supposed to be created by implementing [[FlexiMerge#createMergeLogic]]. */ - abstract class MergeLogic[In, Out] { - def inputHandles(inputCount: Int): JList[InputHandle] - def initialState: State[In, Out] + abstract class MergeLogic[Out] { + def initialState: State[Out] def initialCompletionHandling: CompletionHandling[Out] = defaultCompletionHandling /** * Return this from [[State]] `onInput` to use same state for next element. */ - def sameState[A]: State[A, Out] = FlexiMerge.sameStateInstance.asInstanceOf[State[A, Out]] + def sameState: State[Out] = FlexiMerge.sameStateInstance.asInstanceOf[State[Out]] /** * Convenience to create a [[Read]] condition. */ - def read(input: InputHandle): Read = new Read(input) + def read(input: InPort): Read = new Read(input) /** * Convenience to create a [[ReadAny]] condition. */ - @varargs def readAny(inputs: InputHandle*): ReadAny = { + @varargs def readAny(inputs: InPort*): ReadAny = { import scala.collection.JavaConverters._ new ReadAny(inputs.asJava) } @@ -205,7 +172,7 @@ object FlexiMerge { /** * Convenience to create a [[ReadPreferred]] condition. */ - @varargs def readPreferred(preferred: InputHandle, secondaries: InputHandle*): ReadPreferred = { + @varargs def readPreferred(preferred: InPort, secondaries: InPort*): ReadPreferred = { import scala.collection.JavaConverters._ new ReadPreferred(preferred, secondaries.asJava) } @@ -213,7 +180,7 @@ object FlexiMerge { /** * Convenience to create a [[ReadAll]] condition. */ - @varargs def readAll(inputs: InputHandle*): ReadAll = { + @varargs def readAll(inputs: InPort*): ReadAll = { import scala.collection.JavaConverters._ new ReadAll(inputs.asJava) } @@ -222,11 +189,11 @@ object FlexiMerge { * Will continue to operate until a read becomes unsatisfiable, then it completes. * Errors are immediately propagated. */ - def defaultCompletionHandling[A]: CompletionHandling[Out] = + def defaultCompletionHandling: CompletionHandling[Out] = new CompletionHandling[Out] { - override def onComplete(ctx: MergeLogicContext[Out], input: InputHandle): State[A, Out] = + override def onComplete(ctx: MergeLogicContext[Out], input: InPort): State[Out] = sameState - override def onError(ctx: MergeLogicContext[Out], input: InputHandle, cause: Throwable): State[A, Out] = { + override def onError(ctx: MergeLogicContext[Out], input: InPort, cause: Throwable): State[Out] = { ctx.error(cause) sameState } @@ -236,21 +203,21 @@ object FlexiMerge { * Completes as soon as any input completes. * Errors are immediately propagated. */ - def eagerClose[A]: CompletionHandling[Out] = + def eagerClose: CompletionHandling[Out] = new CompletionHandling[Out] { - override def onComplete(ctx: MergeLogicContext[Out], input: InputHandle): State[A, Out] = { + override def onComplete(ctx: MergeLogicContext[Out], input: InPort): State[Out] = { ctx.complete() sameState } - override def onError(ctx: MergeLogicContext[Out], input: InputHandle, cause: Throwable): State[A, Out] = { + override def onError(ctx: MergeLogicContext[Out], input: InPort, cause: Throwable): State[Out] = { ctx.error(cause) sameState } } } - private val sameStateInstance = new State[Any, Any](new ReadAny(java.util.Collections.emptyList[InputHandle])) { - override def onInput(ctx: MergeLogicContext[Any], input: InputHandle, element: Any): State[Any, Any] = + private val sameStateInstance = new State[Any](new ReadAny(java.util.Collections.emptyList[InPort])) { + override def onInput(ctx: MergeLogicContext[Any], input: InPort, element: AnyRef): State[Any] = throw new UnsupportedOperationException("SameState.onInput should not be called") override def toString: String = "SameState" @@ -260,22 +227,20 @@ object FlexiMerge { * INTERNAL API */ private[akka] object Internal { - class MergeLogicWrapper[Out](delegate: MergeLogic[_, Out]) extends scaladsl.FlexiMerge.MergeLogic[Out] { - override def inputHandles(inputCount: Int): immutable.IndexedSeq[scaladsl.FlexiMerge.InputHandle] = - immutableIndexedSeq(delegate.inputHandles(inputCount)) + class MergeLogicWrapper[Out](delegate: MergeLogic[Out]) extends scaladsl.FlexiMerge.MergeLogic[Out] { - override def initialState: this.State[_] = wrapState(delegate.initialState) + override def initialState: State[_] = wrapState(delegate.initialState) override def initialCompletionHandling: this.CompletionHandling = wrapCompletionHandling(delegate.initialCompletionHandling) - private def wrapState[In](delegateState: FlexiMerge.State[In, Out]): State[In] = + private def wrapState(delegateState: FlexiMerge.State[Out]): State[_] = if (sameStateInstance == delegateState) SameState else State(convertReadCondition(delegateState.condition)) { (ctx, inputHandle, elem) ⇒ val newDelegateState = - delegateState.onInput(new MergeLogicContextWrapper(ctx), asJava(inputHandle), elem) + delegateState.onInput(new MergeLogicContextWrapper(ctx), inputHandle, elem) wrapState(newDelegateState) } @@ -284,36 +249,35 @@ object FlexiMerge { CompletionHandling( onComplete = (ctx, inputHandle) ⇒ { val newDelegateState = delegateCompletionHandling.onComplete( - new MergeLogicContextWrapper(ctx), asJava(inputHandle)) + new MergeLogicContextWrapper(ctx), inputHandle) wrapState(newDelegateState) }, onError = (ctx, inputHandle, cause) ⇒ { val newDelegateState = delegateCompletionHandling.onError( - new MergeLogicContextWrapper(ctx), asJava(inputHandle), cause) + new MergeLogicContextWrapper(ctx), inputHandle, cause) wrapState(newDelegateState) }) - private def asJava(inputHandle: scaladsl.FlexiMerge.InputHandle): InputHandle = - inputHandle.asInstanceOf[InputHandle] - class MergeLogicContextWrapper[In](delegate: MergeLogicContext) extends FlexiMerge.MergeLogicContext[Out] { override def isDemandAvailable: Boolean = delegate.isDemandAvailable override def emit(elem: Out): Unit = delegate.emit(elem) override def complete(): Unit = delegate.complete() override def error(cause: Throwable): Unit = delegate.error(cause) - override def cancel(input: InputHandle): Unit = delegate.cancel(input) + override def cancel(input: InPort): Unit = delegate.cancel(input) override def changeCompletionHandling(completion: FlexiMerge.CompletionHandling[Out]): Unit = delegate.changeCompletionHandling(wrapCompletionHandling(completion)) } } + + private def toAnyRefSeq(l: JList[InPort]) = immutableIndexedSeq(l).asInstanceOf[immutable.Seq[Inlet[AnyRef]]] - def convertReadCondition(condition: ReadCondition): scaladsl.FlexiMerge.ReadCondition = { + def convertReadCondition(condition: ReadCondition): scaladsl.FlexiMerge.ReadCondition[AnyRef] = { condition match { - case r: ReadAny ⇒ scaladsl.FlexiMerge.ReadAny(immutableIndexedSeq(r.inputs)) - case r: ReadPreferred ⇒ scaladsl.FlexiMerge.ReadPreferred(r.preferred, immutableIndexedSeq(r.secondaries)) - case r: Read ⇒ scaladsl.FlexiMerge.Read(r.input) - case r: ReadAll ⇒ scaladsl.FlexiMerge.ReadAll(new ReadAllInputs(_), immutableIndexedSeq(r.inputs): _*) + case r: ReadAny ⇒ scaladsl.FlexiMerge.ReadAny(toAnyRefSeq(r.inputs)) + case r: ReadPreferred ⇒ scaladsl.FlexiMerge.ReadPreferred(r.preferred.asInstanceOf[Inlet[AnyRef]], toAnyRefSeq(r.secondaries)) + case r: Read ⇒ scaladsl.FlexiMerge.Read(r.input.asInstanceOf[Inlet[AnyRef]]) + case r: ReadAll ⇒ scaladsl.FlexiMerge.ReadAll(new ReadAllInputs(_), toAnyRefSeq(r.inputs): _*).asInstanceOf[scaladsl.FlexiMerge.ReadCondition[AnyRef]] } } @@ -336,64 +300,13 @@ object FlexiMerge { * * @param attributes optional attributes for this vertex */ -abstract class FlexiMerge[In, Out](val attributes: OperationAttributes) { +abstract class FlexiMerge[S <: Shape](val attributes: OperationAttributes) { import FlexiMerge._ - import scaladsl.FlowGraphInternal - import akka.stream.impl.Ast def this() = this(OperationAttributes.none) - private var inputCount = 0 - - def createMergeLogic(): MergeLogic[In, Out] - - // hide the internal vertex things from subclass, and make it possible to create new instance - private class FlexiMergeVertex(override val attributes: scaladsl.OperationAttributes) extends FlowGraphInternal.InternalVertex { - override def minimumInputCount = 2 - override def maximumInputCount = inputCount - override def minimumOutputCount = 1 - override def maximumOutputCount = 1 - - override private[akka] val astNode = { - val factory = new MergeLogicFactory[Any] { - override def attributes: scaladsl.OperationAttributes = FlexiMergeVertex.this.attributes - override def createMergeLogic(): scaladsl.FlexiMerge.MergeLogic[Any] = - new Internal.MergeLogicWrapper(FlexiMerge.this.createMergeLogic().asInstanceOf[MergeLogic[Any, Any]]) - } - Ast.FlexiMergeNode(factory, flexiMerge and attributes) - } - - final override def newInstance() = new FlexiMergeVertex(attributes.withoutName) - } - - /** - * INTERNAL API - */ - private[akka] val vertex: FlowGraphInternal.InternalVertex = new FlexiMergeVertex(attributes.asScala) - - /** - * Output port of the `FlexiMerge` junction. A [[Sink]] can be connected to this output - * with the [[FlowGraphBuilder]]. - */ - val out: JunctionOutPort[Out] = new JunctionOutPort[Out] { - override val asScala: scaladsl.JunctionOutPort[Out] = new scaladsl.JunctionOutPort[Out] { - override def vertex: FlowGraphInternal.Vertex = FlexiMerge.this.vertex - } - } - - /** - * Concrete subclass is supposed to define one or more input ports and - * they are created by calling this method. Each [[FlexiMerge.InputPort]] can be - * connected to a [[Source]] with the [[FlowGraphBuilder]]. - * The `InputPort` is also an [[FlexiMerge.InputHandle]], which is passed as parameter - * to [[FlexiMerge#State]] `onInput` when an input element has been read so that you - * can know exactly from which input the element was read. - */ - protected final def createInputPort[T](): InputPort[T, Out] = { - val port = inputCount - inputCount += 1 - new InputPort(port, parent = this) - } + // FIXME can the type parameter make sense at all? + def createMergeLogic(): MergeLogic[_] override def toString = attributes.asScala.nameLifted match { case Some(n) ⇒ n diff --git a/akka-stream/src/main/scala/akka/stream/javadsl/FlexiRoute.scala b/akka-stream/src/main/scala/akka/stream/javadsl/FlexiRoute.scala index c08e1490f5e..54cde4ca1e6 100644 --- a/akka-stream/src/main/scala/akka/stream/javadsl/FlexiRoute.scala +++ b/akka-stream/src/main/scala/akka/stream/javadsl/FlexiRoute.scala @@ -8,39 +8,10 @@ import akka.stream.scaladsl import scala.collection.immutable import java.util.{ List ⇒ JList } import akka.japi.Util.immutableIndexedSeq -import akka.stream.impl.Ast.Defaults._ -import akka.stream.impl.FlexiRouteImpl.RouteLogicFactory +import akka.stream._ object FlexiRoute { - /** - * @see [[OutputPort]] - */ - sealed trait OutputHandle extends scaladsl.FlexiRoute.OutputHandle - - /** - * An `OutputPort` can be connected to a [[Sink]] with the [[FlowGraphBuilder]]. - * The `OutputPort` is also an [[OutputHandle]] which you use to define to which - * downstream output to emit an element. - */ - class OutputPort[In, Out] private[akka] (val port: Int, parent: FlexiRoute[In, _]) - extends JunctionOutPort[Out] with OutputHandle { - - def handle: OutputHandle = this - - override val asScala: scaladsl.JunctionOutPort[Out] = new scaladsl.JunctionOutPort[Out] { - override def port: Int = OutputPort.this.port - override def vertex = parent.vertex - } - - /** - * INTERNAL API - */ - override private[akka] def portIndex: Int = port - - override def toString: String = s"OutputPort($port)" - } - sealed trait DemandCondition /** @@ -52,7 +23,7 @@ object FlexiRoute { * has been completed. `IllegalArgumentException` is thrown if * that is not obeyed. */ - class DemandFrom(val output: OutputHandle) extends DemandCondition + class DemandFrom(val output: OutPort) extends DemandCondition /** * Demand condition for the [[State]] that will be @@ -62,7 +33,7 @@ object FlexiRoute { * Cancelled and completed inputs are not used, i.e. it is allowed * to specify them in the list of `outputs`. */ - class DemandFromAny(val outputs: JList[OutputHandle]) extends DemandCondition + class DemandFromAny(val outputs: JList[OutPort]) extends DemandCondition /** * Demand condition for the [[State]] that will be @@ -72,30 +43,30 @@ object FlexiRoute { * Cancelled and completed outputs are not used, i.e. it is allowed * to specify them in the list of `outputs`. */ - class DemandFromAll(val outputs: JList[OutputHandle]) extends DemandCondition + class DemandFromAll(val outputs: JList[OutPort]) extends DemandCondition /** * Context that is passed to the functions of [[State]] and [[CompletionHandling]]. * The context provides means for performing side effects, such as emitting elements * downstream. */ - trait RouteLogicContext[In, Out] { + trait RouteLogicContext { /** * @return `true` if at least one element has been requested by the given downstream (output). */ - def isDemandAvailable(output: OutputHandle): Boolean + def isDemandAvailable(output: OutPort): Boolean /** * Emit one element downstream. It is only allowed to `emit` when * [[#isDemandAvailable]] is `true` for the given `output`, otherwise * `IllegalArgumentException` is thrown. */ - def emit(output: OutputHandle, elem: Out): Unit + def emit(output: OutPort, elem: AnyRef): Unit /** * Complete the given downstream successfully. */ - def complete(output: OutputHandle): Unit + def complete(output: OutPort): Unit /** * Complete all downstreams successfully and cancel upstream. @@ -105,7 +76,7 @@ object FlexiRoute { /** * Complete the given downstream with failure. */ - def error(output: OutputHandle, cause: Throwable): Unit + def error(output: OutPort, cause: Throwable): Unit /** * Complete all downstreams with failure and cancel upstream. @@ -115,7 +86,7 @@ object FlexiRoute { /** * Replace current [[CompletionHandling]]. */ - def changeCompletionHandling(completion: CompletionHandling[In]): Unit + def changeCompletionHandling(completion: CompletionHandling): Unit } /** @@ -131,10 +102,10 @@ object FlexiRoute { * The `onCancel` method is called when a downstream output cancels. * It returns next behavior or [[#sameState]] to keep current behavior. */ - abstract class CompletionHandling[In] { - def onComplete(ctx: RouteLogicContext[In, Any]): Unit - def onError(ctx: RouteLogicContext[In, Any], cause: Throwable): Unit - def onCancel(ctx: RouteLogicContext[In, Any], output: OutputHandle): State[In, _] + abstract class CompletionHandling { + def onComplete(ctx: RouteLogicContext): Unit + def onError(ctx: RouteLogicContext, cause: Throwable): Unit + def onCancel(ctx: RouteLogicContext, output: OutPort): State } /** @@ -147,8 +118,8 @@ object FlexiRoute { * The `onInput` method is called when an `element` was read from upstream. * The function returns next behavior or [[#sameState]] to keep current behavior. */ - abstract class State[In, Out](val condition: DemandCondition) { - def onInput(ctx: RouteLogicContext[In, Out], preferredOutput: OutputHandle, element: In): State[In, _] + abstract class State(val condition: DemandCondition) { + def onInput(ctx: RouteLogicContext, preferredOutput: OutPort, element: AnyRef): State } /** @@ -158,20 +129,20 @@ object FlexiRoute { * * Concrete instance is supposed to be created by implementing [[FlexiRoute#createRouteLogic]]. */ - abstract class RouteLogic[In, Out] { - def outputHandles(outputCount: Int): JList[OutputHandle] - def initialState: State[In, Out] - def initialCompletionHandling: CompletionHandling[In] = defaultCompletionHandling + abstract class RouteLogic { + + def initialState: State + def initialCompletionHandling: CompletionHandling = defaultCompletionHandling /** * Return this from [[State]] `onInput` to use same state for next element. */ - def sameState[A]: State[In, A] = FlexiRoute.sameStateInstance.asInstanceOf[State[In, A]] + def sameState: State = FlexiRoute.sameStateInstance /** * Convenience to create a [[DemandFromAny]] condition. */ - @varargs def demandFromAny(outputs: OutputHandle*): DemandFromAny = { + @varargs def demandFromAny(outputs: OutPort*): DemandFromAny = { import scala.collection.JavaConverters._ new DemandFromAny(outputs.asJava) } @@ -179,7 +150,7 @@ object FlexiRoute { /** * Convenience to create a [[DemandFromAll]] condition. */ - @varargs def demandFromAll(outputs: OutputHandle*): DemandFromAll = { + @varargs def demandFromAll(outputs: OutPort*): DemandFromAll = { import scala.collection.JavaConverters._ new DemandFromAll(outputs.asJava) } @@ -187,17 +158,17 @@ object FlexiRoute { /** * Convenience to create a [[DemandFrom]] condition. */ - def demandFrom(output: OutputHandle): DemandFrom = new DemandFrom(output) + def demandFrom(output: OutPort): DemandFrom = new DemandFrom(output) /** * When an output cancels it continues with remaining outputs. * Error or completion from upstream are immediately propagated. */ - def defaultCompletionHandling: CompletionHandling[In] = - new CompletionHandling[In] { - override def onComplete(ctx: RouteLogicContext[In, Any]): Unit = () - override def onError(ctx: RouteLogicContext[In, Any], cause: Throwable): Unit = () - override def onCancel(ctx: RouteLogicContext[In, Any], output: OutputHandle): State[In, _] = + def defaultCompletionHandling: CompletionHandling = + new CompletionHandling { + override def onComplete(ctx: RouteLogicContext): Unit = () + override def onError(ctx: RouteLogicContext, cause: Throwable): Unit = () + override def onCancel(ctx: RouteLogicContext, output: OutPort): State = sameState } @@ -205,19 +176,19 @@ object FlexiRoute { * Completes as soon as any output cancels. * Error or completion from upstream are immediately propagated. */ - def eagerClose[A]: CompletionHandling[In] = - new CompletionHandling[In] { - override def onComplete(ctx: RouteLogicContext[In, Any]): Unit = () - override def onError(ctx: RouteLogicContext[In, Any], cause: Throwable): Unit = () - override def onCancel(ctx: RouteLogicContext[In, Any], output: OutputHandle): State[In, _] = { + def eagerClose[A]: CompletionHandling = + new CompletionHandling { + override def onComplete(ctx: RouteLogicContext): Unit = () + override def onError(ctx: RouteLogicContext, cause: Throwable): Unit = () + override def onCancel(ctx: RouteLogicContext, output: OutPort): State = { ctx.complete() sameState } } } - private val sameStateInstance = new State[Any, Any](new DemandFromAny(java.util.Collections.emptyList[OutputHandle])) { - override def onInput(ctx: RouteLogicContext[Any, Any], output: OutputHandle, element: Any): State[Any, Any] = + private val sameStateInstance = new State(new DemandFromAny(java.util.Collections.emptyList[OutPort])) { + override def onInput(ctx: RouteLogicContext, output: OutPort, element: AnyRef): State = throw new UnsupportedOperationException("SameState.onInput should not be called") override def toString: String = "SameState" @@ -227,27 +198,25 @@ object FlexiRoute { * INTERNAL API */ private[akka] object Internal { - class RouteLogicWrapper[In](delegate: RouteLogic[In, _]) extends scaladsl.FlexiRoute.RouteLogic[In] { - override def outputHandles(outputCount: Int): immutable.IndexedSeq[scaladsl.FlexiRoute.OutputHandle] = - immutableIndexedSeq(delegate.outputHandles(outputCount)) + class RouteLogicWrapper(delegate: RouteLogic) extends scaladsl.FlexiRoute.RouteLogic[AnyRef] { override def initialState: this.State[_] = wrapState(delegate.initialState) override def initialCompletionHandling: this.CompletionHandling = wrapCompletionHandling(delegate.initialCompletionHandling) - private def wrapState[Out](delegateState: FlexiRoute.State[In, Out]): State[Out] = + private def wrapState(delegateState: FlexiRoute.State): State[_] = if (sameStateInstance == delegateState) SameState else - State(convertDemandCondition(delegateState.condition)) { (ctx, outputHandle, elem) ⇒ + State[AnyRef](convertDemandCondition(delegateState.condition)) { (ctx, outputHandle, elem) ⇒ val newDelegateState = - delegateState.onInput(new RouteLogicContextWrapper(ctx), asJava(outputHandle), elem) + delegateState.onInput(new RouteLogicContextWrapper(ctx), outputHandle, elem) wrapState(newDelegateState) } private def wrapCompletionHandling[Out]( - delegateCompletionHandling: FlexiRoute.CompletionHandling[In]): CompletionHandling = + delegateCompletionHandling: FlexiRoute.CompletionHandling): CompletionHandling = CompletionHandling( onComplete = ctx ⇒ { delegateCompletionHandling.onComplete(new RouteLogicContextWrapper(ctx)) @@ -257,31 +226,30 @@ object FlexiRoute { }, onCancel = (ctx, outputHandle) ⇒ { val newDelegateState = delegateCompletionHandling.onCancel( - new RouteLogicContextWrapper(ctx), asJava(outputHandle)) + new RouteLogicContextWrapper(ctx), outputHandle) wrapState(newDelegateState) }) - private def asJava(outputHandle: scaladsl.FlexiRoute.OutputHandle): OutputHandle = - outputHandle.asInstanceOf[OutputHandle] - - class RouteLogicContextWrapper[Out](delegate: RouteLogicContext[Out]) extends FlexiRoute.RouteLogicContext[In, Out] { - override def isDemandAvailable(output: OutputHandle): Boolean = delegate.isDemandAvailable(output) - override def emit(output: OutputHandle, elem: Out): Unit = delegate.emit(output, elem) + class RouteLogicContextWrapper(delegate: RouteLogicContext[AnyRef]) extends FlexiRoute.RouteLogicContext { + override def isDemandAvailable(output: OutPort): Boolean = delegate.isDemandAvailable(output) + override def emit(output: OutPort, elem: AnyRef): Unit = delegate.emit(output, elem) override def complete(): Unit = delegate.complete() - override def complete(output: OutputHandle): Unit = delegate.complete(output) + override def complete(output: OutPort): Unit = delegate.complete(output) override def error(cause: Throwable): Unit = delegate.error(cause) - override def error(output: OutputHandle, cause: Throwable): Unit = delegate.error(output, cause) - override def changeCompletionHandling(completion: FlexiRoute.CompletionHandling[In]): Unit = + override def error(output: OutPort, cause: Throwable): Unit = delegate.error(output, cause) + override def changeCompletionHandling(completion: FlexiRoute.CompletionHandling): Unit = delegate.changeCompletionHandling(wrapCompletionHandling(completion)) } } + + private def toAnyRefSeq(l: JList[OutPort]) = immutableIndexedSeq(l).asInstanceOf[immutable.Seq[Outlet[AnyRef]]] def convertDemandCondition(condition: DemandCondition): scaladsl.FlexiRoute.DemandCondition = condition match { - case c: DemandFromAny ⇒ scaladsl.FlexiRoute.DemandFromAny(immutableIndexedSeq(c.outputs)) - case c: DemandFromAll ⇒ scaladsl.FlexiRoute.DemandFromAll(immutableIndexedSeq(c.outputs)) - case c: DemandFrom ⇒ scaladsl.FlexiRoute.DemandFrom(c.output) + case c: DemandFromAny ⇒ scaladsl.FlexiRoute.DemandFromAny(toAnyRefSeq(c.outputs)) + case c: DemandFromAll ⇒ scaladsl.FlexiRoute.DemandFromAll(toAnyRefSeq(c.outputs)) + case c: DemandFrom ⇒ scaladsl.FlexiRoute.DemandFrom(c.output.asInstanceOf[Outlet[AnyRef]]) } } @@ -305,67 +273,14 @@ object FlexiRoute { */ abstract class FlexiRoute[In, Out](val attributes: OperationAttributes) { import FlexiRoute._ - import scaladsl.FlowGraphInternal - import akka.stream.impl.Ast def this() = this(OperationAttributes.none) - private var outputCount = 0 - - // hide the internal vertex things from subclass, and make it possible to create new instance - private class RouteVertex(override val attributes: scaladsl.OperationAttributes) extends FlowGraphInternal.InternalVertex { - override def minimumInputCount = 1 - override def maximumInputCount = 1 - override def minimumOutputCount = 2 - override def maximumOutputCount = outputCount - - override private[akka] val astNode = { - val factory = new RouteLogicFactory[Any] { - override def attributes: scaladsl.OperationAttributes = RouteVertex.this.attributes - override def createRouteLogic(): scaladsl.FlexiRoute.RouteLogic[Any] = - new Internal.RouteLogicWrapper(FlexiRoute.this.createRouteLogic().asInstanceOf[RouteLogic[Any, Any]]) - } - Ast.FlexiRouteNode(factory, flexiRoute and attributes) - } - - final override def newInstance() = new RouteVertex(attributes.withoutName) - } - - /** - * INTERNAL API - */ - private[akka] val vertex: FlowGraphInternal.InternalVertex = new RouteVertex(attributes.asScala) - - /** - * Input port of the `FlexiRoute` junction. A [[Source]] can be connected to this output - * with the [[FlowGraphBuilder]]. - */ - val in: JunctionInPort[In] = new JunctionInPort[In] { - override val asScala: scaladsl.JunctionInPort[In] = new scaladsl.JunctionInPort[In] { - override def vertex = FlexiRoute.this.vertex - type NextT = Nothing - override def next = scaladsl.NoNext - } - } - - /** - * Concrete subclass is supposed to define one or more output ports and - * they are created by calling this method. Each [[FlexiRoute.OutputPort]] can be - * connected to a [[Sink]] with the [[FlowGraphBuilder]]. - * The `OutputPort` is also an [[FlexiRoute.OutputHandle]] which you use to define to which - * downstream output to emit an element. - */ - protected final def createOutputPort[T](): OutputPort[In, T] = { - val port = outputCount - outputCount += 1 - new OutputPort(port, parent = this) - } - /** * Create the stateful logic that will be used when reading input elements * and emitting output elements. Create a new instance every time. */ - def createRouteLogic(): RouteLogic[In, Out] + def createRouteLogic(): RouteLogic override def toString = attributes.asScala.nameLifted match { case Some(n) ⇒ n diff --git a/akka-stream/src/main/scala/akka/stream/javadsl/Flow.scala b/akka-stream/src/main/scala/akka/stream/javadsl/Flow.scala index b70884fc8da..66acbb6b46c 100644 --- a/akka-stream/src/main/scala/akka/stream/javadsl/Flow.scala +++ b/akka-stream/src/main/scala/akka/stream/javadsl/Flow.scala @@ -11,82 +11,52 @@ import scala.concurrent.Future import scala.concurrent.duration.FiniteDuration import akka.stream.stage.Stage -object Flow { +object Flow extends FlowCreate { import akka.stream.scaladsl.JavaConverters._ /** Adapt [[scaladsl.Flow]] for use within Java DSL */ - def adapt[I, O](flow: scaladsl.Flow[I, O]): javadsl.Flow[I, O] = + def adapt[I, O, M](flow: scaladsl.Flow[I, O, M]): javadsl.Flow[I, O, M] = new Flow(flow) /** Create a `Flow` which can process elements of type `T`. */ - def empty[T](): javadsl.Flow[T, T] = + def empty[T](): javadsl.Flow[T, T, Unit] = Flow.create() /** Create a `Flow` which can process elements of type `T`. */ - def create[T](): javadsl.Flow[T, T] = - Flow.adapt[T, T](scaladsl.Pipe.empty[T]) + def create[T](): javadsl.Flow[T, T, Unit] = + adapt(scaladsl.Flow[T]) /** Create a `Flow` which can process elements of type `T`. */ - def of[T](clazz: Class[T]): javadsl.Flow[T, T] = + def of[T](clazz: Class[T]): javadsl.Flow[T, T, Unit] = create[T]() - - /** - * Creates a `Flow` by using an empty [[FlowGraphBuilder]] on a block that expects a [[FlowGraphBuilder]] and - * returns the `UndefinedSource` and `UndefinedSink`. - */ - def create[I, O](block: japi.Function[FlowGraphBuilder, akka.japi.Pair[UndefinedSource[I], UndefinedSink[O]]]): Flow[I, O] = { - val sFlow = scaladsl.Flow() { b ⇒ - val pair = block.apply(b.asJava) - pair.first.asScala → pair.second.asScala - } - new javadsl.Flow[I, O](sFlow) - } - - /** - * Creates a `Flow` by using a [[FlowGraphBuilder]] from this [[PartialFlowGraph]] on a block that expects - * a [[FlowGraphBuilder]] and returns the `UndefinedSource` and `UndefinedSink`. - */ - def create[I, O](graph: PartialFlowGraph, block: japi.Function[javadsl.FlowGraphBuilder, akka.japi.Pair[UndefinedSource[I], UndefinedSink[O]]]): Flow[I, O] = { - val sFlow = scaladsl.Flow(graph.asScala) { b ⇒ - val pair = block.apply(b.asJava) - pair.first.asScala → pair.second.asScala - } - new Flow[I, O](sFlow) - } - - /** - * Create a flow from a seemingly disconnected Source and Sink pair. - */ - def create[I, O](sink: javadsl.Sink[I], source: javadsl.Source[O]): Flow[I, O] = - new Flow(scaladsl.Flow(sink.asScala, source.asScala)) - } /** Create a `Flow` which can process elements of type `T`. */ -class Flow[-In, +Out](delegate: scaladsl.Flow[In, Out]) { +class Flow[-In, +Out, +Mat](delegate: scaladsl.Flow[In, Out, Mat]) { import scala.collection.JavaConverters._ import akka.stream.scaladsl.JavaConverters._ /** Converts this Flow to it's Scala DSL counterpart */ - def asScala: scaladsl.Flow[In, Out] = delegate + def asScala: scaladsl.Flow[In, Out, Mat] = delegate /** * Transform this [[Flow]] by appending the given processing steps. */ - def via[T](flow: javadsl.Flow[Out, T]): javadsl.Flow[In, T] = + def via[T, M](flow: javadsl.Flow[Out, T, M]): javadsl.Flow[In, T, M] = new Flow(delegate.via(flow.asScala)) /** * Connect this [[Flow]] to a [[Sink]], concatenating the processing steps of both. */ - def to(sink: javadsl.Sink[Out]): javadsl.Sink[In] = + def to(sink: javadsl.Sink[Out, _]): javadsl.Sink[In, Mat] = new Sink(delegate.to(sink.asScala)) /** * Join this [[Flow]] to another [[Flow]], by cross connecting the inputs and outputs, creating a [[RunnableFlow]] */ - def join(flow: javadsl.Flow[Out, In]): javadsl.RunnableFlow = + // TODO shouldn’t this combine the materialized values? + def join[M](flow: javadsl.Flow[Out, In, M]): javadsl.RunnableFlow[M] = new RunnableFlowAdapter(delegate.join(flow.asScala)) /** @@ -98,51 +68,23 @@ class Flow[-In, +Out](delegate: scaladsl.Flow[In, Out]) { * @tparam T materialized type of given KeyedSource * @tparam U materialized type of given KeyedSink */ - def runWith[T, U](source: javadsl.KeyedSource[In, T], sink: javadsl.KeyedSink[Out, U], materializer: FlowMaterializer): akka.japi.Pair[T, U] = { + def runWith[T, U](source: javadsl.Source[In, T], sink: javadsl.Sink[Out, U], materializer: FlowMaterializer): akka.japi.Pair[T, U] = { val p = delegate.runWith(source.asScala, sink.asScala)(materializer) akka.japi.Pair(p._1.asInstanceOf[T], p._2.asInstanceOf[U]) } - /** - * Connect the `Source` to this `Flow` and then connect it to the `KeyedSink` and run it. - * - * The returned value will contain the materialized value of the `KeyedSink`, e.g. `Publisher` of a `Sink.publisher()`. - * - * @tparam T materialized type of given KeyedSink - */ - def runWith[T](source: javadsl.Source[In], sink: javadsl.KeyedSink[Out, T], materializer: FlowMaterializer): T = - delegate.runWith(source.asScala, sink.asScala)(materializer)._2.asInstanceOf[T] - - /** - * Connect the `KeyedSource` to this `Flow` and then connect it to the `Sink` and run it. - * - * The returned value will contain the materialized value of the `KeyedSource`, e.g. `Subscriber` of a `Source.from(publisher)`. - * - * @tparam T materialized type of given KeyedSource - */ - def runWith[T](source: javadsl.KeyedSource[In, T], sink: javadsl.Sink[Out], materializer: FlowMaterializer): T = - delegate.runWith(source.asScala, sink.asScala)(materializer)._1.asInstanceOf[T] - - /** - * Connect the `Source` to this `Flow` and then connect it to the `Sink` and run it. - * - * As both `Source` and `Sink` are "simple", no value is returned from this `runWith` overload. - */ - def runWith(source: javadsl.Source[In], sink: javadsl.Sink[Out], materializer: FlowMaterializer): Unit = - delegate.runWith(source.asScala, sink.asScala)(materializer) - /** * Transform this stream by applying the given function to each of the elements * as they pass through this processing step. */ - def map[T](f: japi.Function[Out, T]): javadsl.Flow[In, T] = + def map[T](f: japi.Function[Out, T]): javadsl.Flow[In, T, Mat] = new Flow(delegate.map(f.apply)) /** * Transform each input element into a sequence of output elements that is * then flattened into the output stream. */ - def mapConcat[T](f: japi.Function[Out, java.util.List[T]]): javadsl.Flow[In, T] = + def mapConcat[T](f: japi.Function[Out, java.util.List[T]]): javadsl.Flow[In, T, Mat] = new Flow(delegate.mapConcat(elem ⇒ Util.immutableSeq(f.apply(elem)))) /** @@ -154,7 +96,7 @@ class Flow[-In, +Out](delegate: scaladsl.Flow[In, Out]) { * * @see [[#mapAsyncUnordered]] */ - def mapAsync[T](f: japi.Function[Out, Future[T]]): javadsl.Flow[In, T] = + def mapAsync[T](f: japi.Function[Out, Future[T]]): javadsl.Flow[In, T, Mat] = new Flow(delegate.mapAsync(f.apply)) /** @@ -167,13 +109,13 @@ class Flow[-In, +Out](delegate: scaladsl.Flow[In, Out]) { * * @see [[#mapAsync]] */ - def mapAsyncUnordered[T](f: japi.Function[Out, Future[T]]): javadsl.Flow[In, T] = + def mapAsyncUnordered[T](f: japi.Function[Out, Future[T]]): javadsl.Flow[In, T, Mat] = new Flow(delegate.mapAsyncUnordered(f.apply)) /** * Only pass on those elements that satisfy the given predicate. */ - def filter(p: japi.Predicate[Out]): javadsl.Flow[In, Out] = + def filter(p: japi.Predicate[Out]): javadsl.Flow[In, Out, Mat] = new Flow(delegate.filter(p.test)) /** @@ -181,7 +123,7 @@ class Flow[-In, +Out](delegate: scaladsl.Flow[In, Out]) { * 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]): javadsl.Flow[In, T] = + def collect[T](pf: PartialFunction[Out, T]): javadsl.Flow[In, T, Mat] = new Flow(delegate.collect(pf)) /** @@ -190,7 +132,7 @@ class Flow[-In, +Out](delegate: scaladsl.Flow[In, Out]) { * * `n` must be positive, otherwise IllegalArgumentException is thrown. */ - def grouped(n: Int): javadsl.Flow[In, java.util.List[Out @uncheckedVariance]] = + def grouped(n: Int): javadsl.Flow[In, java.util.List[Out @uncheckedVariance], Mat] = new Flow(delegate.grouped(n).map(_.asJava)) // FIXME optimize to one step /** @@ -199,7 +141,7 @@ class Flow[-In, +Out](delegate: scaladsl.Flow[In, Out]) { * applies the current and next value to the given function `f`, * emitting the next current value. */ - def scan[T](zero: T)(f: japi.Function2[T, Out, T]): javadsl.Flow[In, T] = + def scan[T](zero: T)(f: japi.Function2[T, Out, T]): javadsl.Flow[In, T, Mat] = new Flow(delegate.scan(zero)(f.apply)) /** @@ -212,20 +154,20 @@ class Flow[-In, +Out](delegate: scaladsl.Flow[In, Out]) { * `n` must be positive, and `d` must be greater than 0 seconds, otherwise * IllegalArgumentException is thrown. */ - def groupedWithin(n: Int, d: FiniteDuration): javadsl.Flow[In, java.util.List[Out @uncheckedVariance]] = + def groupedWithin(n: Int, d: FiniteDuration): javadsl.Flow[In, java.util.List[Out @uncheckedVariance], Mat] = new Flow(delegate.groupedWithin(n, d).map(_.asJava)) // FIXME optimize to one step /** * 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): javadsl.Flow[In, Out] = + def drop(n: Int): javadsl.Flow[In, Out, Mat] = new Flow(delegate.drop(n)) /** * Discard the elements received within the given duration at beginning of the stream. */ - def dropWithin(d: FiniteDuration): javadsl.Flow[In, Out] = + def dropWithin(d: FiniteDuration): javadsl.Flow[In, Out, Mat] = new Flow(delegate.dropWithin(d)) /** @@ -237,7 +179,7 @@ class Flow[-In, +Out](delegate: scaladsl.Flow[In, Out]) { * The stream will be completed without producing any elements if `n` is zero * or negative. */ - def take(n: Int): javadsl.Flow[In, Out] = + def take(n: Int): javadsl.Flow[In, Out, Mat] = new Flow(delegate.take(n)) /** @@ -249,7 +191,7 @@ class Flow[-In, +Out](delegate: scaladsl.Flow[In, Out]) { * Note that this can be combined with [[#take]] to limit the number of elements * within the duration. */ - def takeWithin(d: FiniteDuration): javadsl.Flow[In, Out] = + def takeWithin(d: FiniteDuration): javadsl.Flow[In, Out, Mat] = new Flow(delegate.takeWithin(d)) /** @@ -263,7 +205,7 @@ class Flow[-In, +Out](delegate: scaladsl.Flow[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: japi.Function[Out, S], aggregate: japi.Function2[S, Out, S]): javadsl.Flow[In, S] = + def conflate[S](seed: japi.Function[Out, S], aggregate: japi.Function2[S, Out, S]): javadsl.Flow[In, S, Mat] = new Flow(delegate.conflate(seed.apply)(aggregate.apply)) /** @@ -279,7 +221,7 @@ class Flow[-In, +Out](delegate: scaladsl.Flow[In, Out]) { * @param extrapolate Takes the current extrapolation state to produce an output element and the next extrapolation * state. */ - def expand[S, U](seed: japi.Function[Out, S], extrapolate: japi.Function[S, akka.japi.Pair[U, S]]): javadsl.Flow[In, U] = + def expand[S, U](seed: japi.Function[Out, S], extrapolate: japi.Function[S, akka.japi.Pair[U, S]]): javadsl.Flow[In, U, Mat] = new Flow(delegate.expand(seed(_))(s ⇒ { val p = extrapolate(s) (p.first, p.second) @@ -293,7 +235,7 @@ class Flow[-In, +Out](delegate: scaladsl.Flow[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): javadsl.Flow[In, Out] = + def buffer(size: Int, overflowStrategy: OverflowStrategy): javadsl.Flow[In, Out, Mat] = new Flow(delegate.buffer(size, overflowStrategy)) /** @@ -301,7 +243,7 @@ class Flow[-In, +Out](delegate: scaladsl.Flow[In, Out]) { * This operator makes it possible to extend the `Flow` API when there is no specialized * operator that performs the transformation. */ - def transform[U](mkStage: japi.Creator[Stage[Out, U]]): javadsl.Flow[In, U] = + def transform[U](mkStage: japi.Creator[Stage[Out, U]]): javadsl.Flow[In, U, Mat] = new Flow(delegate.transform(() ⇒ mkStage.create())) /** @@ -309,7 +251,7 @@ class Flow[-In, +Out](delegate: scaladsl.Flow[In, Out]) { * 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(n: Int): javadsl.Flow[In, akka.japi.Pair[java.util.List[Out @uncheckedVariance], javadsl.Source[Out @uncheckedVariance]]] = + def prefixAndTail(n: Int): javadsl.Flow[In, akka.japi.Pair[java.util.List[Out @uncheckedVariance], javadsl.Source[Out @uncheckedVariance, Unit]], Mat] = new Flow(delegate.prefixAndTail(n).map { case (taken, tail) ⇒ akka.japi.Pair(taken.asJava, tail.asJava) }) /** @@ -323,7 +265,7 @@ class Flow[-In, +Out](delegate: scaladsl.Flow[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](f: japi.Function[Out, K]): javadsl.Flow[In, akka.japi.Pair[K, javadsl.Source[Out @uncheckedVariance]]] = + def groupBy[K](f: japi.Function[Out, K]): javadsl.Flow[In, akka.japi.Pair[K, javadsl.Source[Out @uncheckedVariance, Unit]], Mat] = new Flow(delegate.groupBy(f.apply).map { case (k, p) ⇒ akka.japi.Pair(k, p.asJava) }) // FIXME optimize to one step /** @@ -339,38 +281,30 @@ class Flow[-In, +Out](delegate: scaladsl.Flow[In, Out]) { * true, false, false // elements go into third substream * }}} */ - def splitWhen(p: japi.Predicate[Out]): javadsl.Flow[In, Source[Out]] = + def splitWhen(p: japi.Predicate[Out]): javadsl.Flow[In, Source[Out, Unit], Mat] = new Flow(delegate.splitWhen(p.test).map(_.asJava)) /** * 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 [[Source]]. */ - def flatten[U](strategy: akka.stream.FlattenStrategy[Out, U]): javadsl.Flow[In, U] = + def flatten[U](strategy: akka.stream.FlattenStrategy[Out, U]): javadsl.Flow[In, U, Mat] = new Flow(delegate.flatten(strategy)) /** * Returns a new `Flow` that concatenates a secondary `Source` to this flow so that, * the first element emitted by the given ("second") source is emitted after the last element of this Flow. */ - def concat(second: javadsl.Source[In]): javadsl.Flow[In, Out] = + def concat[M](second: javadsl.Source[Out @uncheckedVariance, M]): javadsl.Flow[In, Out, Unit] = new Flow(delegate.concat(second.asScala)) - /** - * Add a key that will have a value available after materialization. - * The key can only use other keys if they have been added to the flow - * before this key. - */ - def withKey[T](key: javadsl.Key[T]): Flow[In, Out] = - new Flow(delegate.withKey(key.asScala)) - /** * Applies given [[OperationAttributes]] to a given section. */ - def section[I <: In, O](attributes: OperationAttributes, section: japi.Function[javadsl.Flow[In, Out], javadsl.Flow[I, O]]): javadsl.Flow[I, O] = + def section[O, M](attributes: OperationAttributes, section: japi.Function[javadsl.Flow[Out, Out, Unit], javadsl.Flow[Out, O, M]] @uncheckedVariance): javadsl.Flow[In, O, M] = new Flow(delegate.section(attributes.asScala) { - val scalaToJava = (flow: scaladsl.Flow[In, Out]) ⇒ new javadsl.Flow[In, Out](flow) - val javaToScala = (flow: javadsl.Flow[I, O]) ⇒ flow.asScala + val scalaToJava = (flow: scaladsl.Flow[Out, Out, Unit]) ⇒ new javadsl.Flow(flow) + val javaToScala = (flow: javadsl.Flow[Out, O, M]) ⇒ flow.asScala scalaToJava andThen section.apply andThen javaToScala }) } @@ -380,23 +314,14 @@ class Flow[-In, +Out](delegate: scaladsl.Flow[In, Out]) { * * Flow with attached input and output, can be executed. */ -trait RunnableFlow { +trait RunnableFlow[+Mat] { /** * Run this flow and return the [[MaterializedMap]] containing the values for the [[KeyedMaterializable]] of the flow. */ - def run(materializer: FlowMaterializer): javadsl.MaterializedMap - - /** - * Run this flow and return the value of the [[KeyedMaterializable]]. - */ - def runWith[M](key: KeyedMaterializable[M], materializer: FlowMaterializer): M + def run(materializer: FlowMaterializer): Mat } /** INTERNAL API */ -private[akka] class RunnableFlowAdapter(runnable: scaladsl.RunnableFlow) extends RunnableFlow { - override def run(materializer: FlowMaterializer): MaterializedMap = - new MaterializedMap(runnable.run()(materializer)) - - def runWith[M](key: KeyedMaterializable[M], materializer: FlowMaterializer): M = - runnable.runWith(key.asScala)(materializer) +private[akka] class RunnableFlowAdapter[Mat](runnable: scaladsl.RunnableFlow[Mat]) extends RunnableFlow[Mat] { + override def run(materializer: FlowMaterializer): Mat = runnable.run()(materializer) } diff --git a/akka-stream/src/main/scala/akka/stream/javadsl/FlowGraph.scala b/akka-stream/src/main/scala/akka/stream/javadsl/FlowGraph.scala deleted file mode 100644 index 0bf1332d925..00000000000 --- a/akka-stream/src/main/scala/akka/stream/javadsl/FlowGraph.scala +++ /dev/null @@ -1,631 +0,0 @@ -/** - * Copyright (C) 2014 Typesafe Inc. - */ -package akka.stream.javadsl - -import akka.stream._ -import akka.stream.scaladsl - -trait JunctionInPort[-T] { - /** Convert this element to it's `scaladsl` equivalent. */ - def asScala: scaladsl.JunctionInPort[T] -} -trait JunctionOutPort[T] { - /** Convert this element to it's `scaladsl` equivalent. */ - def asScala: scaladsl.JunctionOutPort[T] -} -abstract class Junction[T] extends JunctionInPort[T] with JunctionOutPort[T] { - /** Convert this element to it's `scaladsl` equivalent. */ - def asScala: scaladsl.Junction[T] -} - -/** INTERNAL API */ -private object JunctionPortAdapter { - def apply[T](delegate: scaladsl.JunctionInPort[T]): javadsl.JunctionInPort[T] = - new JunctionInPort[T] { override def asScala: scaladsl.JunctionInPort[T] = delegate } - - def apply[T](delegate: scaladsl.JunctionOutPort[T]): javadsl.JunctionOutPort[T] = - new JunctionOutPort[T] { override def asScala: scaladsl.JunctionOutPort[T] = delegate } -} - -object Merge { - - /** - * Create a new `Merge` vertex with the specified output type and attributes. - * - * @param attributes optional attributes for this vertex - */ - def create[T](attributes: OperationAttributes): Merge[T] = - new Merge(new scaladsl.Merge[T](attributes.asScala)) - - /** - * Create a new `Merge` vertex with the specified output type. - */ - def create[T](): Merge[T] = create(OperationAttributes.none) - - /** - * Create a new `Merge` vertex with the specified output type. - */ - def create[T](clazz: Class[T]): Merge[T] = create() - - /** - * Create a new `Merge` vertex with the specified output type and attributes. - * - * @param attributes optional attributes for this vertex - */ - def create[T](clazz: Class[T], attributes: OperationAttributes): Merge[T] = create(attributes) - -} - -/** - * 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 sources - * and one output sink to the `Merge` vertex. - * - * Note that a junction instance describes exactly one place (vertex) in the `FlowGraph` - * that multiple flows can be attached to; if you want to have multiple independent - * junctions within the same `FlowGraph` then you will have to create multiple such - * instances. - */ -class Merge[T] private (delegate: scaladsl.Merge[T]) extends javadsl.Junction[T] { - override def asScala: scaladsl.Merge[T] = delegate -} - -object MergePreferred { - /** - * Create a new `MergePreferred` vertex with the specified output type and attributes. - * - * @param attributes optional attributes for this vertex - */ - def create[T](attributes: OperationAttributes): MergePreferred[T] = - new MergePreferred(new scaladsl.MergePreferred[T](attributes.asScala)) - - /** - * Create a new `MergePreferred` vertex with the specified output type. - */ - def create[T](): MergePreferred[T] = create(OperationAttributes.none) - - /** - * Create a new `MergePreferred` vertex with the specified output type. - */ - def create[T](clazz: Class[T]): MergePreferred[T] = create() - - /** - * Create a new `MergePreferred` vertex with the specified output type and attributes. - * - * @param attributes optional attributes for this vertex - */ - def create[T](clazz: Class[T], attributes: OperationAttributes): MergePreferred[T] = - create(attributes) - - class Preferred[T] private[akka] (delegate: scaladsl.MergePreferred.Preferred[T]) extends JunctionInPort[T] { - override def asScala: scaladsl.JunctionInPort[T] = delegate - } -} - -/** - * Merge several streams, taking elements as they arrive from input streams - * (picking from preferred when several have elements ready). - * - * When building the [[FlowGraph]] you must connect one or more input streams - * and one output sink to the `Merge` vertex. - * - * Note that a junction instance describes exactly one place (vertex) in the `FlowGraph` - * that multiple flows can be attached to; if you want to have multiple independent - * junctions within the same `FlowGraph` then you will have to create multiple such - * instances. - */ -class MergePreferred[T](delegate: scaladsl.MergePreferred[T]) extends javadsl.Junction[T] { - override def asScala: scaladsl.MergePreferred[T] = delegate - - val preferred = new MergePreferred.Preferred[T](delegate.preferred) -} - -object Broadcast { - /** - * Create a new `Broadcast` vertex with the specified input type and attributes. - * - * @param attributes optional attributes for this vertex - */ - def create[T](attributes: OperationAttributes): Broadcast[T] = - new Broadcast(new scaladsl.Broadcast(attributes.asScala)) - - /** - * Create a new `Broadcast` vertex with the specified input type. - */ - def create[T](): Broadcast[T] = create(OperationAttributes.none) - - /** - * Create a new `Broadcast` vertex with the specified input type. - */ - def create[T](clazz: Class[T]): Broadcast[T] = create() - - /** - * Create a new `Broadcast` vertex with the specified input type and attributes. - * - * @param attributes optional attributes for this vertex - */ - def create[T](clazz: Class[T], attributes: OperationAttributes): Broadcast[T] = - create(attributes) -} - -/** - * Fan-out the stream to several streams. Each element is produced to - * the other streams. It will not shutdown until the subscriptions for at least - * two downstream subscribers have been established. - * - * Note that a junction instance describes exactly one place (vertex) in the `FlowGraph` - * that multiple flows can be attached to; if you want to have multiple independent - * junctions within the same `FlowGraph` then you will have to create multiple such - * instances. - */ -class Broadcast[T](delegate: scaladsl.Broadcast[T]) extends javadsl.Junction[T] { - override def asScala: scaladsl.Broadcast[T] = delegate -} - -object Balance { - /** - * Create a new `Balance` vertex with the specified input type and attributes. - * - * @param waitForAllDownstreams if `true` it will not start emitting - * elements to downstream outputs until all of them have requested at least one element - * @param attributes optional attributes for this vertex - */ - def create[T](waitForAllDownstreams: Boolean, attributes: OperationAttributes): Balance[T] = - new Balance(new scaladsl.Balance(waitForAllDownstreams, attributes.asScala)) - - /** - * Create a new `Balance` vertex with the specified input type. - */ - def create[T](): Balance[T] = create(false, OperationAttributes.none) - - /** - * Create a new `Balance` vertex with the specified input type. - */ - def create[T](attributes: OperationAttributes): Balance[T] = create(false, attributes) - - /** - * Create a new `Balance` vertex with the specified input type. - */ - def create[T](clazz: Class[T]): Balance[T] = create() - - /** - * Create a new `Balance` vertex with the specified input type and attributes. - * - * @param attributes optional attributes for this vertex - */ - def create[T](clazz: Class[T], attributes: OperationAttributes): Balance[T] = - create(false, attributes) -} - -/** - * Fan-out the stream to several streams. Each element is produced to - * one of the other streams. It will not shutdown until the subscriptions for at least - * two downstream subscribers have been established. - * - * Note that a junction instance describes exactly one place (vertex) in the `FlowGraph` - * that multiple flows can be attached to; if you want to have multiple independent - * junctions within the same `FlowGraph` then you will have to create multiple such - * instances. - */ -class Balance[T](delegate: scaladsl.Balance[T]) extends javadsl.Junction[T] { - override def asScala: scaladsl.Balance[T] = delegate - - /** - * If you use `withWaitForAllDownstreams(true)` the returned `Balance` will not start emitting - * elements to downstream outputs until all of them have requested at least one element. - */ - def withWaitForAllDowstreams(enabled: Boolean): Balance[T] = - new Balance(new scaladsl.Balance(delegate.waitForAllDownstreams, delegate.attributes)) -} - -object Zip { - import akka.stream.javadsl.japi.Function2 - import akka.japi.Pair - /** - * Create a new anonymous `Zip2With` vertex with the specified input types and zipping-function - * which creates `akka.japi.Pair`s. - * Note that a `ZipWith` 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`. - * @param attributes optional attributes for this vertex - */ - def create[A, B](attributes: OperationAttributes): Zip2With[A, B, A Pair B] = - ZipWith.create(_toPair.asInstanceOf[Function2[A, B, A Pair B]], attributes) - - /** - * Create a new `ZipWith` vertex with the specified input types and zipping-function - * which creates `akka.japi.Pair`s. - */ - def create[A, B]: Zip2With[A, B, A Pair B] = create(OperationAttributes.none) - - private[this] final val _toPair: Function2[Any, Any, Any Pair Any] = - new Function2[Any, Any, Any Pair Any] { override def apply(a: Any, b: Any): Any Pair Any = new Pair(a, b) } -} - -object Unzip { - - /** - * Creates a new `Unzip` vertex with the specified output types and attributes. - * - * @param attributes attributes for this vertex - */ - def create[A, B](attributes: OperationAttributes): Unzip[A, B] = - new Unzip[A, B](new scaladsl.Unzip[A, B](attributes.asScala)) - - /** - * Creates a new `Unzip` vertex with the specified output types and attributes. - */ - def create[A, B](): Unzip[A, B] = create(OperationAttributes.none) - - /** - * Creates a new `Unzip` vertex with the specified output types. - */ - def create[A, B](left: Class[A], right: Class[B]): Unzip[A, B] = create[A, B]() - - /** - * Creates a new `Unzip` vertex with the specified output types and attributes. - * - * @param attributes optional attributes for this vertex - */ - def create[A, B](left: Class[A], right: Class[B], attributes: OperationAttributes): Unzip[A, B] = - create[A, B](attributes) - - class In[A, B](private val unzip: Unzip[A, B]) extends JunctionInPort[akka.japi.Pair[A, B]] { - // this cast is safe thanks to using `ZipAs` in the Ast element, Zip will emit the expected type (Pair) - override def asScala: scaladsl.JunctionInPort[akka.japi.Pair[A, B]] = - unzip.asScala.in.asInstanceOf[scaladsl.JunctionInPort[akka.japi.Pair[A, B]]] - } - class Left[A, B](private val unzip: Unzip[A, B]) extends JunctionOutPort[A] { - override def asScala: scaladsl.JunctionOutPort[A] = - unzip.asScala.left - } - class Right[A, B](private val unzip: Unzip[A, B]) extends JunctionOutPort[B] { - override def asScala: scaladsl.JunctionOutPort[B] = - unzip.asScala.right - } -} - -/** - * Note that a junction instance describes exactly one place (vertex) in the `FlowGraph` - * that multiple flows can be attached to; if you want to have multiple independent - * junctions within the same `FlowGraph` then you will have to create multiple such - * instances. - */ -final class Unzip[A, B] private (delegate: scaladsl.Unzip[A, B]) { - - /** Convert this element to it's `scaladsl` equivalent. */ - def asScala = delegate - - val in = new Unzip.In(this) - val left = new Unzip.Left(this) - val right = new Unzip.Right(this) -} - -object Concat { - /** - * Create a new anonymous `Concat` vertex with the specified input types. - * Note that a `Concat` 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 create[T](): Concat[T] = - create(OperationAttributes.none) - - /** - * Create a new anonymous `Concat` vertex with the specified input types. - * Note that a `Concat` 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 create[T](attributes: OperationAttributes): Concat[T] = - new Concat(scaladsl.Concat[T](attributes.asScala)) - - /** - * Create a new anonymous `Concat` vertex with the specified input types. - * Note that a `Concat` 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 create[T](clazz: Class[T], attributes: OperationAttributes): Concat[T] = create(attributes) - - class First[T] private[akka] (delegate: scaladsl.Concat.First[T]) extends JunctionInPort[T] { - override def asScala: scaladsl.JunctionInPort[T] = delegate - } - class Second[T] private[akka] (delegate: scaladsl.Concat.Second[T]) extends JunctionInPort[T] { - override def asScala: scaladsl.JunctionInPort[T] = delegate - } - class Out[T] private[akka] (delegate: scaladsl.Concat.Out[T]) extends JunctionOutPort[T] { - override def asScala: scaladsl.JunctionOutPort[T] = delegate - } - -} - -/** - * Takes two streams and outputs an output stream formed from the two input streams - * by consuming one stream first emitting all of its elements, then consuming the - * second stream emitting all of its elements. - * - * Note that a junction instance describes exactly one place (vertex) in the `FlowGraph` - * that multiple flows can be attached to; if you want to have multiple independent - * junctions within the same `FlowGraph` then you will have to create multiple such - * instances. - */ -class Concat[T] private (delegate: scaladsl.Concat[T]) { - - /** Convert this element to it's `scaladsl` equivalent. */ - def asScala = delegate - - val first = new Concat.First[T](delegate.first) - val second = new Concat.Second[T](delegate.second) - val out = new Concat.Out[T](delegate.out) -} - -// undefined elements // - -object UndefinedSource { - /** - * Create a new `Undefinedsource` vertex with the specified output type. - */ - def create[T](): UndefinedSource[T] = - new UndefinedSource[T](new scaladsl.UndefinedSource[T](scaladsl.OperationAttributes.none)) - - /** - * Create a new `Undefinedsource` vertex with the specified output type. - */ - def create[T](clazz: Class[T]): UndefinedSource[T] = create() - -} - -/** - * It is possible to define a [[akka.stream.javadsl.PartialFlowGraph]] with input pipes that are not connected - * yet by using this placeholder instead of the real [[Source]]. Later the placeholder can - * be replaced with [[akka.stream.javadsl.FlowGraphBuilder#attachSource]]. - */ -final class UndefinedSource[+T](delegate: scaladsl.UndefinedSource[T]) { - def asScala: scaladsl.UndefinedSource[T] = delegate -} - -object UndefinedSink { - /** - * Create a new `Undefinedsink` vertex with the specified input type. - */ - def create[T](): UndefinedSink[T] = - new UndefinedSink[T](new scaladsl.UndefinedSink[T](OperationAttributes.none.asScala)) - - /** - * Create a new `Undefinedsource` vertex with the specified output type. - */ - def create[T](clazz: Class[T]): UndefinedSink[T] = create() -} - -/** - * It is possible to define a [[akka.stream.javadsl.PartialFlowGraph]] with input pipes that are not connected - * yet by using this placeholder instead of the real [[Sink]]. Later the placeholder can - * be replaced with [[akka.stream.javadsl.FlowGraphBuilder#attachSink]]. - */ -final class UndefinedSink[-T](delegate: scaladsl.UndefinedSink[T]) { - def asScala: scaladsl.UndefinedSink[T] = delegate -} - -// flow graph // - -object FlowGraph { - - /** - * Start building a [[FlowGraph]] or [[PartialFlowGraph]]. - * - * The [[FlowGraphBuilder]] is mutable and not thread-safe, - * thus you should construct your Graph and then share the constructed immutable [[FlowGraph]]. - */ - def builder(): FlowGraphBuilder = new FlowGraphBuilder() - - /** - * Continue building a [[FlowGraph]] from an existing `PartialFlowGraph`. - * For example you can attach undefined sources and sinks with - * [[FlowGraphBuilder#attachSource]] and [[FlowGraphBuilder#attachSink]] - */ - def builder(partialFlowGraph: PartialFlowGraph): FlowGraphBuilder = - new FlowGraphBuilder(partialFlowGraph) - -} - -/** - * Java API - * Builder of [[FlowGraph]] and [[PartialFlowGraph]]. - */ -class FlowGraphBuilder(b: scaladsl.FlowGraphBuilder) { - - /** - * Continue building a [[FlowGraph]] from an existing `PartialFlowGraph`. - * For example you can attach undefined sources and sinks with - * [[#attachSource]] and [[#attachSink]] - */ - def this(partialFlowGraph: PartialFlowGraph) { - this(new scaladsl.FlowGraphBuilder(partialFlowGraph.asScala)) - } - - def this() { - this(new scaladsl.FlowGraphBuilder()) - } - - /** Converts this Java DSL element to it's Scala DSL counterpart. */ - def asScala: scaladsl.FlowGraphBuilder = b - - def addEdge[In, Out](source: javadsl.UndefinedSource[In], flow: javadsl.Flow[In, Out], junctionIn: javadsl.JunctionInPort[Out]): FlowGraphBuilder = { - b.addEdge(source.asScala, flow.asScala, junctionIn.asScala) - this - } - - def addEdge[T](source: javadsl.UndefinedSource[T], junctionIn: javadsl.JunctionInPort[T]) = - addEdge[T, T](source, javadsl.Flow.empty[T], junctionIn); - - def addEdge[In, Out](junctionOut: javadsl.JunctionOutPort[In], flow: javadsl.Flow[In, Out], sink: javadsl.UndefinedSink[Out]): FlowGraphBuilder = { - b.addEdge(junctionOut.asScala, flow.asScala, sink.asScala) - this - } - - def addEdge[T](junctionOut: javadsl.JunctionOutPort[T], sink: javadsl.UndefinedSink[T]): FlowGraphBuilder = - addEdge[T, T](junctionOut, javadsl.Flow.empty[T], sink); - - def addEdge[In, Out](junctionOut: javadsl.JunctionOutPort[In], flow: javadsl.Flow[In, Out], junctionIn: javadsl.JunctionInPort[Out]): FlowGraphBuilder = { - b.addEdge(junctionOut.asScala, flow.asScala, junctionIn.asScala) - this - } - - def addEdge[T](junctionOut: javadsl.JunctionOutPort[T], junctionIn: javadsl.JunctionInPort[T]): FlowGraphBuilder = - addEdge[T, T](junctionOut, javadsl.Flow.empty[T], junctionIn); - - def addEdge[In, Out](source: javadsl.Source[In], flow: javadsl.Flow[In, Out], junctionIn: javadsl.JunctionInPort[Out]): FlowGraphBuilder = { - b.addEdge(source.asScala, flow.asScala, junctionIn.asScala) - this - } - - def addEdge[T](source: javadsl.Source[T], junctionIn: javadsl.JunctionInPort[T]): FlowGraphBuilder = - addEdge[T, T](source, javadsl.Flow.empty[T], junctionIn); - - def addEdge[In, Out](junctionOut: javadsl.JunctionOutPort[In], flow: javadsl.Flow[In, Out], sink: Sink[Out]): FlowGraphBuilder = { - b.addEdge(junctionOut.asScala, flow.asScala, sink.asScala) - this - } - - def addEdge[T](junctionOut: javadsl.JunctionOutPort[T], sink: Sink[T]): FlowGraphBuilder = - addEdge[T, T](junctionOut, javadsl.Flow.empty[T], sink); - - def addEdge[In, Out](source: javadsl.Source[In], flow: javadsl.Flow[In, Out], sink: Sink[Out]): FlowGraphBuilder = { - b.addEdge(source.asScala, flow.asScala, sink.asScala) - this - } - - def addEdge[T](source: javadsl.Source[T], sink: Sink[T]): FlowGraphBuilder = - addEdge[T, T](source, javadsl.Flow.empty[T], sink); - - def addEdge[In, Out](source: javadsl.UndefinedSource[In], flow: javadsl.Flow[In, Out], sink: javadsl.UndefinedSink[Out]): FlowGraphBuilder = { - b.addEdge(source.asScala, flow.asScala, sink.asScala) - this - } - - def addEdge[T](source: javadsl.UndefinedSource[T], sink: javadsl.UndefinedSink[T]): FlowGraphBuilder = - addEdge[T, T](source, javadsl.Flow.empty[T], sink); - - def addEdge[In, Out](source: javadsl.UndefinedSource[In], flow: javadsl.Flow[In, Out], sink: javadsl.Sink[Out]): FlowGraphBuilder = { - b.addEdge(source.asScala, flow.asScala, sink.asScala) - this - } - - def addEdge[T](source: javadsl.UndefinedSource[T], sink: javadsl.Sink[T]): FlowGraphBuilder = - addEdge[T, T](source, javadsl.Flow.empty[T], sink); - - def addEdge[In, Out](source: javadsl.Source[In], flow: javadsl.Flow[In, Out], sink: javadsl.UndefinedSink[Out]): FlowGraphBuilder = { - b.addEdge(source.asScala, flow.asScala, sink.asScala) - this - } - - def addEdge[T](source: javadsl.Source[T], sink: javadsl.UndefinedSink[T]): FlowGraphBuilder = - addEdge[T, T](source, javadsl.Flow.empty[T], sink); - - def attachSink[Out](token: javadsl.UndefinedSink[Out], sink: Sink[Out]): FlowGraphBuilder = { - b.attachSink(token.asScala, sink.asScala) - this - } - - def attachSource[In](token: javadsl.UndefinedSource[In], source: javadsl.Source[In]): FlowGraphBuilder = { - b.attachSource(token.asScala, source.asScala) - this - } - - def connect[A, B](out: javadsl.UndefinedSink[A], flow: javadsl.Flow[A, B], in: javadsl.UndefinedSource[B]): FlowGraphBuilder = { - b.connect(out.asScala, flow.asScala, in.asScala) - this - } - - def importFlowGraph(flowGraph: javadsl.FlowGraph): FlowGraphBuilder = { - b.importFlowGraph(flowGraph.asScala) - this - } - - /** - * Import all edges from another [[akka.stream.scaladsl.PartialFlowGraph]] to this builder. - * After importing you can [[#connect]] undefined sources and sinks in - * two different `PartialFlowGraph` instances. - */ - def importPartialFlowGraph(partialFlowGraph: javadsl.PartialFlowGraph): FlowGraphBuilder = { - b.importPartialFlowGraph(partialFlowGraph.asScala) - this - } - - /** - * Flow graphs with cycles are in general dangerous as it can result in deadlocks. - * Therefore, cycles in the graph are by default disallowed. `IllegalArgumentException` will - * be throw when cycles are detected. Sometimes cycles are needed and then - * you can allow them with this method. - */ - def allowCycles(): FlowGraphBuilder = { - b.allowCycles() - this - } - - /** Build the [[FlowGraph]] but do not materialize it. */ - def build(): javadsl.FlowGraph = - new javadsl.FlowGraph(b.build()) - - /** Build the [[PartialFlowGraph]] but do not materialize it. */ - def buildPartial(): javadsl.PartialFlowGraph = - new PartialFlowGraph(b.partialBuild()) - - /** Build the [[FlowGraph]] and materialize it. */ - def run(materializer: FlowMaterializer): javadsl.MaterializedMap = - new MaterializedMap(b.build().run()(materializer)) - -} - -class PartialFlowGraph(delegate: scaladsl.PartialFlowGraph) { - import akka.stream.scaladsl.JavaConverters._ - - import collection.JavaConverters._ - - def asScala: scaladsl.PartialFlowGraph = delegate - - def undefinedSources(): java.util.Set[UndefinedSource[Any]] = - delegate.undefinedSources.map(s ⇒ s.asJava).asJava - - def undefinedSinks(): java.util.Set[UndefinedSink[_]] = - delegate.undefinedSinks.map(s ⇒ s.asJava).asJava - - /** - * Creates a [[Source]] from this `PartialFlowGraph`. There needs to be only one [[UndefinedSink]] and - * no [[UndefinedSource]] in the graph, and you need to provide it as a parameter. - */ - def toSource[O](out: javadsl.UndefinedSink[O]): javadsl.Source[O] = - delegate.toSource(out.asScala).asJava - - /** - * Creates a [[Flow]] from this `PartialFlowGraph`. There needs to be only one [[UndefinedSource]] and - * one [[UndefinedSink]] in the graph, and you need to provide them as parameters. - */ - def toFlow[I, O](in: javadsl.UndefinedSource[I], out: javadsl.UndefinedSink[O]): Flow[I, O] = - delegate.toFlow(in.asScala, out.asScala).asJava - - /** - * Creates a [[Sink]] from this `PartialFlowGraph`. There needs to be only one [[UndefinedSource]] and - * no [[UndefinedSink]] in the graph, and you need to provide it as a parameter. - */ - def toSink[I](in: UndefinedSource[I]): javadsl.Sink[I] = - delegate.toSink(in.asScala).asJava - -} - -class FlowGraph(delegate: scaladsl.FlowGraph) extends RunnableFlow { - - /** Convert this element to it's `scaladsl` equivalent. */ - def asScala: scaladsl.FlowGraph = delegate - - override def run(materializer: FlowMaterializer): javadsl.MaterializedMap = - new MaterializedMap(delegate.run()(materializer)) - - def runWith[M](key: KeyedMaterializable[M], materializer: FlowMaterializer): M = - delegate.runWith(key.asScala)(materializer) -} - diff --git a/akka-stream/src/main/scala/akka/stream/javadsl/Graph.scala b/akka-stream/src/main/scala/akka/stream/javadsl/Graph.scala new file mode 100644 index 00000000000..164d6305605 --- /dev/null +++ b/akka-stream/src/main/scala/akka/stream/javadsl/Graph.scala @@ -0,0 +1,286 @@ +/** + * Copyright (C) 2014 Typesafe Inc. + */ +package akka.stream.javadsl + +import akka.stream._ +import akka.stream.scaladsl + +/** + * 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 sources + * and one output sink to the `Merge` vertex. + * + * Note that a junction instance describes exactly one place (vertex) in the `FlowGraph` + * that multiple flows can be attached to; if you want to have multiple independent + * junctions within the same `FlowGraph` then you will have to create multiple such + * instances. + */ +object Merge { + + /** + * Create a new `Merge` vertex with the specified output type and attributes. + * + * @param attributes optional attributes for this vertex + */ + def create[T](outputCount: Int, attributes: OperationAttributes): Graph[UniformFanInShape[T, T], Unit] = + scaladsl.Merge(outputCount, attributes.asScala) + + /** + * Create a new `Merge` vertex with the specified output type. + */ + def create[T](outputCount: Int): Graph[UniformFanInShape[T, T], Unit] = create(outputCount, OperationAttributes.none) + + /** + * Create a new `Merge` vertex with the specified output type. + */ + def create[T](clazz: Class[T], outputCount: Int): Graph[UniformFanInShape[T, T], Unit] = create(outputCount) + + /** + * Create a new `Merge` vertex with the specified output type and attributes. + * + * @param attributes optional attributes for this vertex + */ + def create[T](clazz: Class[T], outputCount: Int, attributes: OperationAttributes): Graph[UniformFanInShape[T, T], Unit] = + create(outputCount, attributes) + +} + +/** + * Merge several streams, taking elements as they arrive from input streams + * (picking from preferred when several have elements ready). + * + * When building the [[FlowGraph]] you must connect one or more input streams + * and one output sink to the `Merge` vertex. + * + * Note that a junction instance describes exactly one place (vertex) in the `FlowGraph` + * that multiple flows can be attached to; if you want to have multiple independent + * junctions within the same `FlowGraph` then you will have to create multiple such + * instances. + */ +object MergePreferred { + /** + * Create a new `MergePreferred` vertex with the specified output type and attributes. + * + * @param attributes optional attributes for this vertex + */ + def create[T](outputCount: Int, attributes: OperationAttributes): Graph[scaladsl.MergePreferred.MergePreferredShape[T], Unit] = + scaladsl.MergePreferred(outputCount, attributes.asScala) + + /** + * Create a new `MergePreferred` vertex with the specified output type. + */ + def create[T](outputCount: Int): Graph[scaladsl.MergePreferred.MergePreferredShape[T], Unit] = create(outputCount, OperationAttributes.none) + + /** + * Create a new `MergePreferred` vertex with the specified output type. + */ + def create[T](clazz: Class[T], outputCount: Int): Graph[scaladsl.MergePreferred.MergePreferredShape[T], Unit] = create(outputCount) + + /** + * Create a new `MergePreferred` vertex with the specified output type and attributes. + * + * @param attributes optional attributes for this vertex + */ + def create[T](clazz: Class[T], outputCount: Int, attributes: OperationAttributes): Graph[scaladsl.MergePreferred.MergePreferredShape[T], Unit] = + create(outputCount, attributes) + +} + +/** + * Fan-out the stream to several streams. Each element is produced to + * the other streams. It will not shutdown until the subscriptions for at least + * two downstream subscribers have been established. + * + * Note that a junction instance describes exactly one place (vertex) in the `FlowGraph` + * that multiple flows can be attached to; if you want to have multiple independent + * junctions within the same `FlowGraph` then you will have to create multiple such + * instances. + */ +object Broadcast { + /** + * Create a new `Broadcast` vertex with the specified input type and attributes. + * + * @param attributes optional attributes for this vertex + */ + def create[T](outputCount: Int, attributes: OperationAttributes): Graph[UniformFanOutShape[T, T], Unit] = + scaladsl.Broadcast(outputCount, attributes.asScala) + + /** + * Create a new `Broadcast` vertex with the specified input type. + */ + def create[T](outputCount: Int): Graph[UniformFanOutShape[T, T], Unit] = create(outputCount, OperationAttributes.none) + + /** + * Create a new `Broadcast` vertex with the specified input type. + */ + def create[T](clazz: Class[T], outputCount: Int): Graph[UniformFanOutShape[T, T], Unit] = create(outputCount) + + /** + * Create a new `Broadcast` vertex with the specified input type and attributes. + * + * @param attributes optional attributes for this vertex + */ + def create[T](clazz: Class[T], outputCount: Int, attributes: OperationAttributes): Graph[UniformFanOutShape[T, T], Unit] = + create(outputCount, attributes) +} + +/** + * Fan-out the stream to several streams. Each element is produced to + * one of the other streams. It will not shutdown until the subscriptions for at least + * two downstream subscribers have been established. + * + * Note that a junction instance describes exactly one place (vertex) in the `FlowGraph` + * that multiple flows can be attached to; if you want to have multiple independent + * junctions within the same `FlowGraph` then you will have to create multiple such + * instances. + */ +object Balance { + /** + * Create a new `Balance` vertex with the specified input type and attributes. + * + * @param waitForAllDownstreams if `true` it will not start emitting + * elements to downstream outputs until all of them have requested at least one element + * @param attributes optional attributes for this vertex + */ + def create[T](outputCount: Int, waitForAllDownstreams: Boolean, attributes: OperationAttributes): Graph[UniformFanOutShape[T, T], Unit] = + scaladsl.Balance(outputCount, waitForAllDownstreams, attributes.asScala) + + /** + * Create a new `Balance` vertex with the specified input type. + */ + def create[T](outputCount: Int): Graph[UniformFanOutShape[T, T], Unit] = create(outputCount, false, OperationAttributes.none) + + /** + * Create a new `Balance` vertex with the specified input type. + */ + def create[T](outputCount: Int, attributes: OperationAttributes): Graph[UniformFanOutShape[T, T], Unit] = create(outputCount, false, attributes) + + /** + * Create a new `Balance` vertex with the specified input type. + */ + def create[T](clazz: Class[T], outputCount: Int): Graph[UniformFanOutShape[T, T], Unit] = create(outputCount) + + /** + * Create a new `Balance` vertex with the specified input type and attributes. + * + * @param attributes optional attributes for this vertex + */ + def create[T](clazz: Class[T], outputCount: Int, attributes: OperationAttributes): Graph[UniformFanOutShape[T, T], Unit] = + create(outputCount, false, attributes) +} + +object Zip { + import akka.stream.javadsl.japi.Function2 + import akka.japi.Pair + + /** + * Create a new `ZipWith` vertex with the specified input types and zipping-function + * which creates `akka.japi.Pair`s. + */ + def create[A, B]: Graph[FanInShape2[A, B, A Pair B], Unit] = + ZipWith.create(_toPair.asInstanceOf[Function2[A, B, A Pair B]]) + + private[this] final val _toPair: Function2[Any, Any, Any Pair Any] = + new Function2[Any, Any, Any Pair Any] { override def apply(a: Any, b: Any): Any Pair Any = new Pair(a, b) } +} + +/** + * Note that a junction instance describes exactly one place (vertex) in the `FlowGraph` + * that multiple flows can be attached to; if you want to have multiple independent + * junctions within the same `FlowGraph` then you will have to create multiple such + * instances. + */ +object Unzip { + + /** + * Creates a new `Unzip` vertex with the specified output types and attributes. + * + * @param attributes attributes for this vertex + */ + def create[A, B](attributes: OperationAttributes): Graph[FanOutShape2[(A, B), A, B], Unit] = scaladsl.Unzip[A, B](attributes.asScala) + + /** + * Creates a new `Unzip` vertex with the specified output types and attributes. + */ + def create[A, B](): Graph[FanOutShape2[(A, B), A, B], Unit] = create(OperationAttributes.none) + + /** + * Creates a new `Unzip` vertex with the specified output types. + */ + def create[A, B](left: Class[A], right: Class[B]): Graph[FanOutShape2[(A, B), A, B], Unit] = create[A, B]() + + /** + * Creates a new `Unzip` vertex with the specified output types and attributes. + * + * @param attributes optional attributes for this vertex + */ + def create[A, B](left: Class[A], right: Class[B], attributes: OperationAttributes): Graph[FanOutShape2[(A, B), A, B], Unit] = + create[A, B](attributes) + +} + +/** + * Takes two streams and outputs an output stream formed from the two input streams + * by consuming one stream first emitting all of its elements, then consuming the + * second stream emitting all of its elements. + * + * Note that a junction instance describes exactly one place (vertex) in the `FlowGraph` + * that multiple flows can be attached to; if you want to have multiple independent + * junctions within the same `FlowGraph` then you will have to create multiple such + * instances. + */ +object Concat { + /** + * Create a new anonymous `Concat` vertex with the specified input types. + * Note that a `Concat` 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 create[T](): Graph[UniformFanInShape[T, T], Unit] = create(OperationAttributes.none) + + /** + * Create a new anonymous `Concat` vertex with the specified input types. + * Note that a `Concat` 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 create[T](attributes: OperationAttributes): Graph[UniformFanInShape[T, T], Unit] = scaladsl.Concat[T](attributes.asScala) + + /** + * Create a new anonymous `Concat` vertex with the specified input types. + * Note that a `Concat` 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 create[T](clazz: Class[T], attributes: OperationAttributes): Graph[UniformFanInShape[T, T], Unit] = create(attributes) + +} + +// flow graph // + +object Graph extends GraphCreate { + + /** + * Start building a [[FlowGraph]] or [[PartialFlowGraph]]. + * + * The [[FlowGraphBuilder]] is mutable and not thread-safe, + * thus you should construct your Graph and then share the constructed immutable [[FlowGraph]]. + */ + def builder(): Builder = new Builder(new scaladsl.Graph.Builder) + + class Builder(delegate: scaladsl.Graph.Builder) { + def addEdge[A, B, M](from: Outlet[A], via: Flow[A, B, M], to: Inlet[B]): Unit = delegate.addEdge(from, via.asScala, to) + + def addEdge[T](from: Outlet[T], to: Inlet[T]): Unit = delegate.addEdge(from, to) + + /** + * Import a graph into this module, performing a deep copy, discarding its + * materialized value and returning the copied Ports that are now to be + * connected. + */ + def add[S <: Shape](graph: Graph[S, _]): S = delegate.add(graph) + } +} diff --git a/akka-stream/src/main/scala/akka/stream/javadsl/MaterializedMap.scala b/akka-stream/src/main/scala/akka/stream/javadsl/MaterializedMap.scala deleted file mode 100644 index be672f5d557..00000000000 --- a/akka-stream/src/main/scala/akka/stream/javadsl/MaterializedMap.scala +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright (C) 2014 Typesafe Inc. - */ -package akka.stream.javadsl - -import akka.stream.javadsl -import akka.stream.scaladsl -import scala.collection.JavaConverters.asJavaIteratorConverter - -/** - * Java API - * - * Returned by [[RunnableFlow#run]] and can be used as parameter to the - * accessor method to retrieve the materialized `Source` or `Sink`, e.g. - * [[akka.stream.javadsl.Source#subscriber]] or [[akka.stream.javadsl.Sink#publisher]]. - */ -class MaterializedMap(delegate: scaladsl.MaterializedMap) { - def asScala: scaladsl.MaterializedMap = delegate - - /** - * Retrieve a materialized key, `Source`, `Sink` or `Key`, e.g. the `Subscriber` of a - * [[akka.stream.javadsl.Source#subscriber]]. - */ - def get[T](key: javadsl.KeyedMaterializable[T]): T = - delegate.get(key.asScala) - - /** - * Merge two materialized maps. - */ - def merge(otherMap: MaterializedMap): MaterializedMap = - if (this.isEmpty) otherMap - else if (otherMap.isEmpty) this - else new MaterializedMap(this.asScala.merge(otherMap.asScala)) - - /** - * Update the materialized map with a new value. - */ - def updated(key: KeyedMaterializable[_], value: Object): MaterializedMap = - new MaterializedMap(delegate.updated(key.asScala, value)) - - /** - * Check if this map is empty. - */ - def isEmpty: Boolean = delegate.isEmpty - - /** - * An iterator over the key value pairs in this materialized map. - */ - def iterator: java.util.Iterator[akka.japi.Pair[Object, Object]] = { - delegate.iterator.map { case (a, b) ⇒ new akka.japi.Pair(a.asInstanceOf[Object], b.asInstanceOf[Object]) } asJava - } -} - -/** - * Java API - * - * Common interface for keyed things that can be materialized. - */ -trait KeyedMaterializable[M] { - def asScala: scaladsl.KeyedMaterializable[M] -} - -/** - * Java API - * - * A key that is not directly tied to a sink or source instance. - */ -class Key[M](delegate: scaladsl.Key[M]) extends KeyedMaterializable[M] { - def asScala: scaladsl.Key[M] = delegate - - /** - * Materialize the value for this key. All Sink and Source keys have been materialized and exist in the map. - */ - def materialize(map: MaterializedMap): Object = delegate.materialize(map.asScala).asInstanceOf[Object] -} diff --git a/akka-stream/src/main/scala/akka/stream/javadsl/Sink.scala b/akka-stream/src/main/scala/akka/stream/javadsl/Sink.scala index 2fc8f771575..f6f20e7b7af 100644 --- a/akka-stream/src/main/scala/akka/stream/javadsl/Sink.scala +++ b/akka-stream/src/main/scala/akka/stream/javadsl/Sink.scala @@ -7,19 +7,19 @@ import akka.actor.ActorRef import akka.actor.Props import akka.stream.javadsl import akka.stream.scaladsl -import akka.stream.FlowMaterializer +import akka.stream.{ FlowMaterializer, Inlet } import org.reactivestreams.Publisher import org.reactivestreams.Subscriber import scala.concurrent.Future /** Java API */ -object Sink { +object Sink extends SinkCreate { import akka.stream.scaladsl.JavaConverters._ /** Adapt [[scaladsl.Sink]] for use within Java DSL */ - def adapt[O](sink: scaladsl.Sink[O]): javadsl.Sink[O] = + def adapt[O, M](sink: scaladsl.Sink[O, M]): javadsl.Sink[O, M] = new Sink(sink) /** @@ -29,55 +29,41 @@ object Sink { * function evaluation when the input stream ends, or completed with `Failure` * if there is an error is signaled in the stream. */ - def fold[U, In](zero: U, f: japi.Function2[U, In, U]): javadsl.KeyedSink[In, Future[U]] = - new KeyedSink(scaladsl.Sink.fold[U, In](zero)(f.apply)) + def fold[U, In](zero: U, f: japi.Function2[U, In, U]): javadsl.Sink[In, Future[U]] = + new Sink(scaladsl.Sink.fold[U, In](zero)(f.apply)) /** * Helper to create [[Sink]] from `Subscriber`. */ - def create[In](subs: Subscriber[In]): Sink[In] = - new Sink[In](scaladsl.Sink(subs)) - - /** - * Creates a `Sink` by using an empty [[FlowGraphBuilder]] on a block that expects a [[FlowGraphBuilder]] and - * returns the `UndefinedSource`. - */ - def create[T]()(block: japi.Function[FlowGraphBuilder, UndefinedSource[T]]): Sink[T] = - new Sink(scaladsl.Sink.apply() { b ⇒ block.apply(b.asJava).asScala }) - - /** - * Creates a `Sink` by using a FlowGraphBuilder from this [[PartialFlowGraph]] on a block that expects - * a [[FlowGraphBuilder]] and returns the `UndefinedSource`. - */ - def create[T](graph: PartialFlowGraph, block: japi.Function[FlowGraphBuilder, UndefinedSource[T]]): Sink[T] = - new Sink[T](scaladsl.Sink.apply(graph.asScala) { b ⇒ block.apply(b.asJava).asScala }) + def create[In](subs: Subscriber[In]): Sink[In, Unit] = + new Sink(scaladsl.Sink(subs)) /** * Creates a `Sink` that is materialized to an [[akka.actor.ActorRef]] which points to an Actor * created according to the passed in [[akka.actor.Props]]. Actor created by the `props` should * be [[akka.stream.actor.ActorSubscriber]]. */ - def create[T](props: Props): KeyedSink[T, ActorRef] = - new KeyedSink(scaladsl.Sink.apply(props)) + def create[T](props: Props): Sink[T, ActorRef] = + new Sink(scaladsl.Sink.apply(props)) /** * A `Sink` that immediately cancels its upstream after materialization. */ - def cancelled[T]: Sink[T] = + def cancelled[T]: Sink[T, Unit] = new Sink(scaladsl.Sink.cancelled) /** * A `Sink` that will consume the stream and discard the elements. */ - def ignore[T](): Sink[T] = + def ignore[T](): Sink[T, Unit] = new Sink(scaladsl.Sink.ignore) /** * A `Sink` that materializes into a [[org.reactivestreams.Publisher]]. * that can handle one [[org.reactivestreams.Subscriber]]. */ - def publisher[In](): KeyedSink[In, Publisher[In]] = - new KeyedSink(scaladsl.Sink.publisher) + def publisher[In](): Sink[In, Publisher[In]] = + new Sink(scaladsl.Sink.publisher) /** * A `Sink` that will invoke the given procedure for each received element. The sink is materialized @@ -85,29 +71,29 @@ object Sink { * normal end of the stream, or completed with `Failure` if there is an error is signaled in * the stream.. */ - def foreach[T](f: japi.Procedure[T]): KeyedSink[T, Future[Unit]] = - new KeyedSink(scaladsl.Sink.foreach(f.apply)) + def foreach[T](f: japi.Procedure[T]): Sink[T, Future[Unit]] = + new Sink(scaladsl.Sink.foreach(f.apply)) /** * A `Sink` that materializes into a [[org.reactivestreams.Publisher]] * that can handle more than one [[org.reactivestreams.Subscriber]]. */ - def fanoutPublisher[T](initialBufferSize: Int, maximumBufferSize: Int): KeyedSink[T, Publisher[T]] = - new KeyedSink(scaladsl.Sink.fanoutPublisher(initialBufferSize, maximumBufferSize)) + def fanoutPublisher[T](initialBufferSize: Int, maximumBufferSize: Int): Sink[T, Publisher[T]] = + new Sink(scaladsl.Sink.fanoutPublisher(initialBufferSize, maximumBufferSize)) /** * A `Sink` that when the flow is completed, either through an error or normal * completion, apply the provided function with [[scala.util.Success]] * or [[scala.util.Failure]]. */ - def onComplete[In](onComplete: japi.Procedure[Unit]): Sink[In] = + def onComplete[In](onComplete: japi.Procedure[Unit]): Sink[In, Unit] = new Sink(scaladsl.Sink.onComplete[In](x ⇒ onComplete.apply(x))) /** * A `Sink` that materializes into a `Future` of the first value received. */ - def head[In]: KeyedSink[In, Future[In]] = - new KeyedSink(scaladsl.Sink.head[In]) + def head[In]: Sink[In, Future[In]] = + new Sink(scaladsl.Sink.head[In]) } @@ -117,37 +103,15 @@ object Sink { * A `Sink` is a set of stream processing steps that has one open input and an attached output. * Can be used as a `Subscriber` */ -class Sink[-In](delegate: scaladsl.Sink[In]) { +class Sink[-In, +Mat](delegate: scaladsl.Sink[In, Mat]) { /** Converts this Sink to it's Scala DSL counterpart */ - def asScala: scaladsl.Sink[In] = delegate - - // RUN WITH // - - /** - * Connect the `KeyedSource` to this `Sink` and run it. - * - * The returned value is the materialized value of the `KeyedSource`, e.g. the `Subscriber` of a `Source.subscriber()`. - * - * @tparam T materialized type of given Source - */ - def runWith[T](source: javadsl.KeyedSource[In, T], materializer: FlowMaterializer): T = - asScala.runWith(source.asScala)(materializer).asInstanceOf[T] + def asScala: scaladsl.Sink[In, Mat] = delegate /** * Connect this `Sink` to a `Source` and run it. */ - def runWith(source: javadsl.Source[In], materializer: FlowMaterializer): Unit = + // TODO shouldn’t this return M? + def runWith[M](source: javadsl.Source[In, M], materializer: FlowMaterializer): Mat = asScala.runWith(source.asScala)(materializer) } - -/** - * Java API - * - * 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.) - */ -final class KeyedSink[-In, M](delegate: scaladsl.KeyedSink[In, M]) extends javadsl.Sink[In](delegate) with KeyedMaterializable[M] { - override def asScala: scaladsl.KeyedSink[In, M] = super.asScala.asInstanceOf[scaladsl.KeyedSink[In, M]] -} diff --git a/akka-stream/src/main/scala/akka/stream/javadsl/Source.scala b/akka-stream/src/main/scala/akka/stream/javadsl/Source.scala index 4176956e64d..12b700ca370 100644 --- a/akka-stream/src/main/scala/akka/stream/javadsl/Source.scala +++ b/akka-stream/src/main/scala/akka/stream/javadsl/Source.scala @@ -7,7 +7,7 @@ import java.util.concurrent.Callable import akka.actor.{ Cancellable, ActorRef, Props } import akka.japi.Util import akka.stream._ -import akka.stream.scaladsl.PropsSource +import akka.stream.impl.PropsSource import org.reactivestreams.Publisher import org.reactivestreams.Subscriber import scala.annotation.unchecked.uncheckedVariance @@ -19,19 +19,19 @@ import scala.language.implicitConversions import akka.stream.stage.Stage /** Java API */ -object Source { +object Source extends SourceCreate { import scaladsl.JavaConverters._ /** Adapt [[scaladsl.Source]] for use within JavaDSL */ - def adapt[O](source: scaladsl.Source[O]): Source[O] = + def adapt[O, M](source: scaladsl.Source[O, M]): Source[O, M] = new Source(source) /** * Create a `Source` with no elements, i.e. an empty stream that is completed immediately * for every connected `Sink`. */ - def empty[O](): Source[O] = + def empty[O](): Source[O, Unit] = new Source(scaladsl.Source.empty()) /** @@ -42,7 +42,7 @@ object Source { * that mediate the flow of elements downstream and the propagation of * back-pressure upstream. */ - def from[O](publisher: Publisher[O]): javadsl.Source[O] = + def from[O](publisher: Publisher[O]): javadsl.Source[O, Unit] = new Source(scaladsl.Source.apply(publisher)) /** @@ -63,7 +63,7 @@ object Source { * in accordance with the demand coming from the downstream transformation * steps. */ - def from[O](f: japi.Creator[java.util.Iterator[O]]): javadsl.Source[O] = + def from[O](f: japi.Creator[java.util.Iterator[O]]): javadsl.Source[O, Unit] = new Source(scaladsl.Source(() ⇒ f.create().asScala)) /** @@ -82,7 +82,7 @@ object Source { * stream will see an individual flow of elements (always starting from the * beginning) regardless of when they subscribed. */ - def from[O](iterable: java.lang.Iterable[O]): javadsl.Source[O] = + def from[O](iterable: java.lang.Iterable[O]): javadsl.Source[O, Unit] = new Source(scaladsl.Source(akka.stream.javadsl.japi.Util.immutableIterable(iterable))) /** @@ -91,7 +91,7 @@ object Source { * may happen before or after materializing the `Flow`. * The stream terminates with an error if the `Future` is completed with a failure. */ - def from[O](future: Future[O]): javadsl.Source[O] = + def from[O](future: Future[O]): javadsl.Source[O, Unit] = new Source(scaladsl.Source(future)) /** @@ -101,56 +101,42 @@ object Source { * 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 from[O](initialDelay: FiniteDuration, interval: FiniteDuration, tick: O): javadsl.KeyedSource[O, Cancellable] = - new KeyedSource(scaladsl.Source(initialDelay, interval, tick)) - - /** - * Creates a `Source` by using a [[FlowGraphBuilder]] from this [[PartialFlowGraph]] on a block that expects - * a [[FlowGraphBuilder]] and returns the `UndefinedSink`. - */ - def fromGraph[T](graph: PartialFlowGraph, block: japi.Function[FlowGraphBuilder, UndefinedSink[T]]): Source[T] = - new Source(scaladsl.Source(graph.asScala)(x ⇒ block.apply(x.asJava).asScala)) - - /** - * Creates a `Source` by using a [[FlowGraphBuilder]] from on a block that expects - * a [[FlowGraphBuilder]] and returns the `UndefinedSink`. - */ - def fromGraph[T](block: japi.Function[FlowGraphBuilder, UndefinedSink[T]]): Source[T] = - new Source(scaladsl.Source()(x ⇒ block.apply(x.asJava).asScala)) + def from[O](initialDelay: FiniteDuration, interval: FiniteDuration, tick: O): javadsl.Source[O, Cancellable] = + new Source(scaladsl.Source(initialDelay, interval, tick)) /** * Creates a `Source` that is materialized to an [[akka.actor.ActorRef]] which points to an Actor * created according to the passed in [[akka.actor.Props]]. Actor created by the `props` should * be [[akka.stream.actor.ActorPublisher]]. */ - def from[T](props: Props): KeyedSource[T, ActorRef] = - new KeyedSource(scaladsl.Source.apply(props)) + def from[T](props: Props): Source[T, ActorRef] = + new Source(scaladsl.Source.apply(props)) /** * Create a `Source` with one element. * Every connected `Sink` of this stream will see an individual stream consisting of one element. */ - def single[T](element: T): Source[T] = + def single[T](element: T): Source[T, Unit] = new Source(scaladsl.Source.single(element)) /** * Create a `Source` that immediately ends the stream with the `cause` error to every connected `Sink`. */ - def failed[T](cause: Throwable): Source[T] = + def failed[T](cause: Throwable): Source[T, Unit] = new Source(scaladsl.Source.failed(cause)) /** * Creates a `Source` that is materialized as a [[org.reactivestreams.Subscriber]] */ - def subscriber[T](): KeyedSource[T, Subscriber[T]] = - new KeyedSource(scaladsl.Source.subscriber) + def subscriber[T](): Source[T, Subscriber[T]] = + new Source(scaladsl.Source.subscriber) /** * Concatenates two sources so that the first element * emitted by the second source is emitted after the last element of the first * source. */ - def concat[T](first: Source[T], second: Source[T]): Source[T] = + def concat[T, M1, M2](first: Source[T, M1], second: Source[T, M2]): Source[T, (M1, M2)] = new Source(scaladsl.Source.concat(first.asScala, second.asScala)) } @@ -160,41 +146,31 @@ object Source { * A `Source` is a set of stream processing steps that has one open output and an attached input. * Can be used as a `Publisher` */ -class Source[+Out](delegate: scaladsl.Source[Out]) { +class Source[+Out, +Mat](delegate: scaladsl.Source[Out, Mat]) { import akka.stream.scaladsl.JavaConverters._ import scala.collection.JavaConverters._ /** Converts this Java DSL element to it's Scala DSL counterpart. */ - def asScala: scaladsl.Source[Out] = delegate + def asScala: scaladsl.Source[Out, Mat] = delegate /** * Transform this [[Source]] by appending the given processing stages. */ - def via[T](flow: javadsl.Flow[Out, T]): javadsl.Source[T] = + def via[T, M](flow: javadsl.Flow[Out, T, M]): javadsl.Source[T, M] = new Source(delegate.via(flow.asScala)) /** * Connect this [[Source]] to a [[Sink]], concatenating the processing steps of both. */ - def to(sink: javadsl.Sink[Out]): javadsl.RunnableFlow = + def to[M](sink: javadsl.Sink[Out, M]): javadsl.RunnableFlow[M] = new RunnableFlowAdapter(delegate.to(sink.asScala)) - /** - * Connect this `Source` to a `KeyedSink` and run it. - * - * The returned value is the materialized value of the `Sink`, e.g. the `Publisher` of a `Sink.publisher()`. - * - * @tparam S materialized type of the given Sink - */ - def runWith[S](sink: KeyedSink[Out, S], materializer: FlowMaterializer): S = - asScala.runWith(sink.asScala)(materializer).asInstanceOf[S] - /** * Connect this `Source` to a `Sink` and run it. The returned value is the materialized value * of the `Sink`, e.g. the `Publisher` of a `Sink.publisher()`. */ - def runWith(sink: Sink[Out], materializer: FlowMaterializer): Unit = + def runWith[M](sink: Sink[Out, M], materializer: FlowMaterializer): M = delegate.to(sink.asScala).run()(materializer) /** @@ -213,7 +189,7 @@ class Source[+Out](delegate: scaladsl.Source[Out]) { * emitted by that source is emitted after the last element of this * source. */ - def concat[Out2 >: Out](second: Source[Out2]): Source[Out2] = + def concat[Out2 >: Out, M2](second: Source[Out2, M2]): Source[Out2, (Mat, M2)] = Source.concat(this, second) /** @@ -232,14 +208,14 @@ class Source[+Out](delegate: scaladsl.Source[Out]) { * Transform this stream by applying the given function to each of the elements * as they pass through this processing step. */ - def map[T](f: japi.Function[Out, T]): javadsl.Source[T] = + def map[T](f: japi.Function[Out, T]): javadsl.Source[T, Mat] = new Source(delegate.map(f.apply)) /** * Transform each input element into a sequence of output elements that is * then flattened into the output stream. */ - def mapConcat[T](f: japi.Function[Out, java.util.List[T]]): javadsl.Source[T] = + def mapConcat[T](f: japi.Function[Out, java.util.List[T]]): javadsl.Source[T, Mat] = new Source(delegate.mapConcat(elem ⇒ Util.immutableSeq(f.apply(elem)))) /** @@ -251,7 +227,7 @@ class Source[+Out](delegate: scaladsl.Source[Out]) { * * @see [[#mapAsyncUnordered]] */ - def mapAsync[T](f: japi.Function[Out, Future[T]]): javadsl.Source[T] = + def mapAsync[T](f: japi.Function[Out, Future[T]]): javadsl.Source[T, Mat] = new Source(delegate.mapAsync(f.apply)) /** @@ -264,13 +240,13 @@ class Source[+Out](delegate: scaladsl.Source[Out]) { * * @see [[#mapAsync]] */ - def mapAsyncUnordered[T](f: japi.Function[Out, Future[T]]): javadsl.Source[T] = + def mapAsyncUnordered[T](f: japi.Function[Out, Future[T]]): javadsl.Source[T, Mat] = new Source(delegate.mapAsyncUnordered(f.apply)) /** * Only pass on those elements that satisfy the given predicate. */ - def filter(p: japi.Predicate[Out]): javadsl.Source[Out] = + def filter(p: japi.Predicate[Out]): javadsl.Source[Out, Mat] = new Source(delegate.filter(p.test)) /** @@ -278,7 +254,7 @@ class Source[+Out](delegate: scaladsl.Source[Out]) { * 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]): javadsl.Source[T] = + def collect[T](pf: PartialFunction[Out, T]): javadsl.Source[T, Mat] = new Source(delegate.collect(pf)) /** @@ -287,7 +263,7 @@ class Source[+Out](delegate: scaladsl.Source[Out]) { * * @param n must be positive, otherwise [[IllegalArgumentException]] is thrown. */ - def grouped(n: Int): javadsl.Source[java.util.List[Out @uncheckedVariance]] = + def grouped(n: Int): javadsl.Source[java.util.List[Out @uncheckedVariance], Mat] = new Source(delegate.grouped(n).map(_.asJava)) /** @@ -296,7 +272,7 @@ class Source[+Out](delegate: scaladsl.Source[Out]) { * applies the current and next value to the given function `f`, * yielding the next current value. */ - def scan[T](zero: T)(f: japi.Function2[T, Out, T]): javadsl.Source[T] = + def scan[T](zero: T)(f: japi.Function2[T, Out, T]): javadsl.Source[T, Mat] = new Source(delegate.scan(zero)(f.apply)) /** @@ -308,20 +284,20 @@ class Source[+Out](delegate: scaladsl.Source[Out]) { * * @param n must be positive, and `d` must be greater than 0 seconds, otherwise [[IllegalArgumentException]] is thrown. */ - def groupedWithin(n: Int, d: FiniteDuration): javadsl.Source[java.util.List[Out @uncheckedVariance]] = + def groupedWithin(n: Int, d: FiniteDuration): javadsl.Source[java.util.List[Out @uncheckedVariance], Mat] = new Source(delegate.groupedWithin(n, d).map(_.asJava)) // FIXME optimize to one step /** * 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): javadsl.Source[Out] = + def drop(n: Int): javadsl.Source[Out, Mat] = new Source(delegate.drop(n)) /** * Discard the elements received within the given duration at beginning of the stream. */ - def dropWithin(d: FiniteDuration): javadsl.Source[Out] = + def dropWithin(d: FiniteDuration): javadsl.Source[Out, Mat] = new Source(delegate.dropWithin(d)) /** @@ -332,7 +308,7 @@ class Source[+Out](delegate: scaladsl.Source[Out]) { * * @param n if `n` is zero or negative the stream will be completed without producing any elements. */ - def take(n: Int): javadsl.Source[Out] = + def take(n: Int): javadsl.Source[Out, Mat] = new Source(delegate.take(n)) /** @@ -344,7 +320,7 @@ class Source[+Out](delegate: scaladsl.Source[Out]) { * Note that this can be combined with [[#take]] to limit the number of elements * within the duration. */ - def takeWithin(d: FiniteDuration): javadsl.Source[Out] = + def takeWithin(d: FiniteDuration): javadsl.Source[Out, Mat] = new Source(delegate.takeWithin(d)) /** @@ -358,7 +334,7 @@ class Source[+Out](delegate: scaladsl.Source[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: japi.Function[Out, S], aggregate: japi.Function2[S, Out, S]): javadsl.Source[S] = + def conflate[S](seed: japi.Function[Out, S], aggregate: japi.Function2[S, Out, S]): javadsl.Source[S, Mat] = new Source(delegate.conflate(seed.apply)(aggregate.apply)) /** @@ -374,7 +350,7 @@ class Source[+Out](delegate: scaladsl.Source[Out]) { * @param extrapolate Takes the current extrapolation state to produce an output element and the next extrapolation * state. */ - def expand[S, U](seed: japi.Function[Out, S], extrapolate: japi.Function[S, akka.japi.Pair[U, S]]): javadsl.Source[U] = + def expand[S, U](seed: japi.Function[Out, S], extrapolate: japi.Function[S, akka.japi.Pair[U, S]]): javadsl.Source[U, Mat] = new Source(delegate.expand(seed(_))(s ⇒ { val p = extrapolate(s) (p.first, p.second) @@ -388,7 +364,7 @@ class Source[+Out](delegate: scaladsl.Source[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): javadsl.Source[Out] = + def buffer(size: Int, overflowStrategy: OverflowStrategy): javadsl.Source[Out, Mat] = new Source(delegate.buffer(size, overflowStrategy)) /** @@ -396,7 +372,7 @@ class Source[+Out](delegate: scaladsl.Source[Out]) { * This operator makes it possible to extend the `Flow` API when there is no specialized * operator that performs the transformation. */ - def transform[U](mkStage: japi.Creator[Stage[Out, U]]): javadsl.Source[U] = + def transform[U](mkStage: japi.Creator[Stage[Out, U]]): javadsl.Source[U, Mat] = new Source(delegate.transform(() ⇒ mkStage.create())) /** @@ -404,7 +380,7 @@ class Source[+Out](delegate: scaladsl.Source[Out]) { * 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(n: Int): javadsl.Source[akka.japi.Pair[java.util.List[Out @uncheckedVariance], javadsl.Source[Out @uncheckedVariance]]] = + def prefixAndTail(n: Int): javadsl.Source[akka.japi.Pair[java.util.List[Out @uncheckedVariance], javadsl.Source[Out @uncheckedVariance, Unit]], Mat] = new Source(delegate.prefixAndTail(n).map { case (taken, tail) ⇒ akka.japi.Pair(taken.asJava, tail.asJava) }) /** @@ -418,7 +394,7 @@ class Source[+Out](delegate: scaladsl.Source[Out]) { * care to unblock (or cancel) all of the produced streams even if you want * to consume only one of them. */ - def groupBy[K](f: japi.Function[Out, K]): javadsl.Source[akka.japi.Pair[K, javadsl.Source[Out @uncheckedVariance]]] = + def groupBy[K](f: japi.Function[Out, K]): javadsl.Source[akka.japi.Pair[K, javadsl.Source[Out @uncheckedVariance, Unit]], Mat] = new Source(delegate.groupBy(f.apply).map { case (k, p) ⇒ akka.japi.Pair(k, p.asJava) }) // FIXME optimize to one step /** @@ -434,41 +410,23 @@ class Source[+Out](delegate: scaladsl.Source[Out]) { * true, false, false // elements go into third substream * }}} */ - def splitWhen(p: japi.Predicate[Out]): javadsl.Source[javadsl.Source[Out]] = + def splitWhen(p: japi.Predicate[Out]): javadsl.Source[javadsl.Source[Out, Unit], Mat] = new Source(delegate.splitWhen(p.test).map(_.asJava)) /** * 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 [[Source]]. */ - def flatten[U](strategy: akka.stream.FlattenStrategy[Out, U]): javadsl.Source[U] = + def flatten[U](strategy: akka.stream.FlattenStrategy[Out, U]): javadsl.Source[U, Mat] = new Source(delegate.flatten(strategy)) - /** - * Add a key that will have a value available after materialization. - * The key can only use other keys if they have been added to the source - * before this key. This also includes the keyed source if applicable. - */ - def withKey[T](key: javadsl.Key[T]): javadsl.Source[Out] = - new Source(delegate.withKey(key.asScala)) - /** * Applies given [[OperationAttributes]] to a given section. */ - def section[O](attributes: OperationAttributes, section: japi.Function[javadsl.Source[Out], javadsl.Source[O]]): javadsl.Source[O] = + def section[O, M](attributes: OperationAttributes, section: japi.Function[javadsl.Flow[Out, Out, Unit], javadsl.Flow[Out, O, M]] @uncheckedVariance): javadsl.Source[O, M] = new Source(delegate.section(attributes.asScala) { - val scalaToJava = (source: scaladsl.Source[Out]) ⇒ new javadsl.Source[Out](source) - val javaToScala = (source: javadsl.Source[O]) ⇒ source.asScala + val scalaToJava = (source: scaladsl.Flow[Out, Out, Unit]) ⇒ new javadsl.Flow(source) + val javaToScala = (source: javadsl.Flow[Out, O, M]) ⇒ source.asScala scalaToJava andThen section.apply andThen javaToScala }) } - -/** - * Java API - * - * 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.). - */ -final class KeyedSource[+Out, M](delegate: scaladsl.KeyedSource[Out, M]) extends Source[Out](delegate) with KeyedMaterializable[M] { - override def asScala: scaladsl.KeyedSource[Out, M] = super.asScala.asInstanceOf[scaladsl.KeyedSource[Out, M]] -} diff --git a/akka-stream/src/main/scala/akka/stream/javadsl/StreamTcp.scala b/akka-stream/src/main/scala/akka/stream/javadsl/StreamTcp.scala index ac64e550392..69d679f00cf 100644 --- a/akka-stream/src/main/scala/akka/stream/javadsl/StreamTcp.scala +++ b/akka-stream/src/main/scala/akka/stream/javadsl/StreamTcp.scala @@ -29,18 +29,7 @@ object StreamTcp extends ExtensionId[StreamTcp] with ExtensionIdProvider { * The local address of the endpoint bound by the materialization of the `connections` [[Source]] * whose [[MaterializedMap]] is passed as parameter. */ - def localAddress(materializedMap: MaterializedMap): Future[InetSocketAddress] = - delegate.localAddress(materializedMap.asScala) - - /** - * The stream of accepted incoming connections. - * Can be materialized several times but only one subscription can be "live" at one time, i.e. - * subsequent materializations will reject subscriptions with an [[BindFailedException]] if the previous - * materialization still has an uncancelled subscription. - * Cancelling the subscription to a materialization of this source will cause the listening port to be unbound. - */ - def connections: Source[IncomingConnection] = - Source.adapt(delegate.connections.map(new IncomingConnection(_))) + def localAddress: InetSocketAddress = delegate.localAddress /** * Asynchronously triggers the unbinding of the port that was bound by the materialization of the `connections` @@ -48,8 +37,7 @@ object StreamTcp extends ExtensionId[StreamTcp] with ExtensionIdProvider { * * The produced [[scala.concurrent.Future]] is fulfilled when the unbinding has been completed. */ - def unbind(materializedMap: MaterializedMap): Future[Unit] = - delegate.unbind(materializedMap.asScala) + def unbind(): Future[Unit] = delegate.unbind } /** @@ -72,14 +60,14 @@ object StreamTcp extends ExtensionId[StreamTcp] with ExtensionIdProvider { * * Convenience shortcut for: `flow.join(handler).run()`. */ - def handleWith(handler: Flow[ByteString, ByteString], materializer: FlowMaterializer): MaterializedMap = - new MaterializedMap(delegate.handleWith(handler.asScala)(materializer)) + def handleWith[Mat](handler: Flow[ByteString, ByteString, Mat], materializer: FlowMaterializer): Mat = + delegate.handleWith(handler.asScala)(materializer) /** * A flow representing the client on the other side of the connection. * This flow can be materialized only once. */ - def flow: Flow[ByteString, ByteString] = Flow.adapt(delegate.flow) + def flow: Flow[ByteString, ByteString, Unit] = Flow.adapt(delegate.flow) } /** @@ -95,28 +83,7 @@ object StreamTcp extends ExtensionId[StreamTcp] with ExtensionIdProvider { * The local address of the endpoint bound by the materialization of the connection materialization * whose [[MaterializedMap]] is passed as parameter. */ - def localAddress(mMap: MaterializedMap): Future[InetSocketAddress] = - delegate.localAddress(mMap.asScala) - - /** - * Handles the connection using the given flow. - * This method can be called several times, every call will materialize the given flow exactly once thereby - * triggering a new connection attempt to the `remoteAddress`. - * If the connection cannot be established the materialized stream will immediately be terminated - * with a [[akka.stream.StreamTcpException]]. - * - * Convenience shortcut for: `flow.join(handler).run()`. - */ - def handleWith(handler: Flow[ByteString, ByteString], materializer: FlowMaterializer): MaterializedMap = - new MaterializedMap(delegate.handleWith(handler.asScala)(materializer)) - - /** - * A flow representing the server on the other side of the connection. - * This flow can be materialized several times, every materialization will open a new connection to the - * `remoteAddress`. If the connection cannot be established the materialized stream will immediately be terminated - * with a [[akka.stream.StreamTcpException]]. - */ - def flow: Flow[ByteString, ByteString] = Flow.adapt(delegate.flow) + def localAddress: InetSocketAddress = delegate.localAddress } override def get(system: ActorSystem): StreamTcp = super.get(system) @@ -137,15 +104,15 @@ class StreamTcp(system: ExtendedActorSystem) extends akka.actor.Extension { def bind(endpoint: InetSocketAddress, backlog: Int, options: JIterable[SocketOption], - idleTimeout: Duration): ServerBinding = - new ServerBinding(delegate.bind(endpoint, backlog, immutableSeq(options), idleTimeout)) + idleTimeout: Duration): Source[IncomingConnection, Future[ServerBinding]] = ??? +// Source.adapt(delegate.bind(endpoint, backlog, immutableSeq(options), idleTimeout)) /** * Creates a [[StreamTcp.ServerBinding]] without specifying options. * It represents a prospective TCP server binding on the given `endpoint`. */ - def bind(endpoint: InetSocketAddress): ServerBinding = - new ServerBinding(delegate.bind(endpoint)) + def bind(endpoint: InetSocketAddress): Source[IncomingConnection, Future[ServerBinding]] = ??? +// Source.adapt(delegate.bind(endpoint)) /** * Creates an [[StreamTcp.OutgoingConnection]] instance representing a prospective TCP client connection to the given endpoint. @@ -154,15 +121,14 @@ class StreamTcp(system: ExtendedActorSystem) extends akka.actor.Extension { localAddress: Option[InetSocketAddress], options: JIterable[SocketOption], connectTimeout: Duration, - idleTimeout: Duration): OutgoingConnection = - new OutgoingConnection(delegate.outgoingConnection( - remoteAddress, localAddress, immutableSeq(options), connectTimeout, idleTimeout)) + idleTimeout: Duration): Flow[ByteString, ByteString, Future[OutgoingConnection]] = ??? +// Flow.adapt(delegate.outgoingConnection(remoteAddress, localAddress, immutableSeq(options), connectTimeout, idleTimeout)) /** * Creates an [[StreamTcp.OutgoingConnection]] without specifying options. * It represents a prospective TCP client connection to the given endpoint. */ - def outgoingConnection(remoteAddress: InetSocketAddress): OutgoingConnection = - new OutgoingConnection(delegate.outgoingConnection(remoteAddress)) + def outgoingConnection(remoteAddress: InetSocketAddress): Flow[ByteString, ByteString, Future[OutgoingConnection]] = ??? +// Flow.adapt(delegate.outgoingConnection(remoteAddress)) } diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiMerge.scala b/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiMerge.scala index c3ddaa90c8c..5575bc44d60 100644 --- a/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiMerge.scala +++ b/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiMerge.scala @@ -5,20 +5,13 @@ package akka.stream.scaladsl import akka.stream.impl.Junctions.FlexiMergeModule import akka.stream.scaladsl.FlexiMerge.MergeLogic -import akka.stream.scaladsl.FlowGraph.FlowGraphBuilder -import akka.stream.scaladsl.Graphs.{ InPort, Ports } +import akka.stream.{ Inlet, Shape, InPort, Graph } import scala.collection.immutable import scala.collection.immutable.Seq -import scala.language.higherKinds - -import scala.language.higherKinds import akka.stream.impl.StreamLayout object FlexiMerge { - private type OutP = StreamLayout.OutPort - private type InP = StreamLayout.InPort - sealed trait ReadCondition[T] /** @@ -30,11 +23,11 @@ object FlexiMerge { * has been completed. `IllegalArgumentException` is thrown if * that is not obeyed. */ - final case class Read[T](input: InPort[T]) extends ReadCondition[T] + final case class Read[T](input: Inlet[T]) extends ReadCondition[T] object ReadAny { - def apply[T](inputs: immutable.Seq[InPort[T]]): ReadAny[T] = new ReadAny(inputs: _*) - def apply(p: Ports): ReadAny[Any] = new ReadAny(p.inlets.asInstanceOf[Seq[InPort[Any]]]: _*) + def apply[T](inputs: immutable.Seq[Inlet[T]]): ReadAny[T] = new ReadAny(inputs: _*) + def apply(p: Shape): ReadAny[Any] = new ReadAny(p.inlets.asInstanceOf[Seq[Inlet[Any]]]: _*) } /** @@ -45,10 +38,10 @@ object FlexiMerge { * Cancelled and completed inputs are not used, i.e. it is allowed * to specify them in the list of `inputs`. */ - final case class ReadAny[T](inputs: InPort[T]*) extends ReadCondition[T] + final case class ReadAny[T](inputs: Inlet[T]*) extends ReadCondition[T] object ReadPreferred { - def apply[T](preferred: InPort[T], secondaries: immutable.Seq[InPort[T]]): ReadPreferred[T] = + def apply[T](preferred: Inlet[T], secondaries: immutable.Seq[Inlet[T]]): ReadPreferred[T] = new ReadPreferred(preferred, secondaries: _*) } @@ -62,11 +55,11 @@ object FlexiMerge { * Cancelled and completed inputs are not used, i.e. it is allowed * to specify them in the list of `inputs`. */ - final case class ReadPreferred[T](preferred: InPort[T], secondaries: InPort[T]*) extends ReadCondition[T] + final case class ReadPreferred[T](preferred: Inlet[T], secondaries: Inlet[T]*) extends ReadCondition[T] object ReadAll { - def apply[T](inputs: immutable.Seq[InPort[T]]): ReadAll[T] = new ReadAll(new ReadAllInputs(_), inputs: _*) - def apply[T](inputs: InPort[T]*): ReadAll[T] = new ReadAll(new ReadAllInputs(_), inputs: _*) + def apply[T](inputs: immutable.Seq[Inlet[T]]): ReadAll[T] = new ReadAll(new ReadAllInputs(_), inputs: _*) + def apply[T](inputs: Inlet[T]*): ReadAll[T] = new ReadAll(new ReadAllInputs(_), inputs: _*) } /** @@ -80,18 +73,18 @@ object FlexiMerge { * the resulting [[ReadAllInputs]] will then not contain values for this element, which can be * handled via supplying a default value instead of the value from the (now cancelled) input. */ - final case class ReadAll[T](mkResult: immutable.Map[InP, Any] ⇒ ReadAllInputsBase, inputs: InPort[T]*) extends ReadCondition[ReadAllInputs] + final case class ReadAll[T](mkResult: immutable.Map[InPort, Any] ⇒ ReadAllInputsBase, inputs: Inlet[T]*) extends ReadCondition[ReadAllInputs] /** INTERNAL API */ - sealed private[stream] trait ReadAllInputsBase + private[stream] trait ReadAllInputsBase /** * Provides typesafe accessors to values from inputs supplied to [[ReadAll]]. */ - final class ReadAllInputs(map: immutable.Map[InP, Any]) extends ReadAllInputsBase { - def apply[T](input: InPort[T]): T = map(input).asInstanceOf[T] - def get[T](input: InPort[T]): Option[T] = map.get(input).asInstanceOf[Option[T]] - def getOrElse[T](input: InPort[T], default: ⇒ T): T = map.getOrElse(input, default).asInstanceOf[T] + final class ReadAllInputs(map: immutable.Map[InPort, Any]) extends ReadAllInputsBase { + def apply[T](input: Inlet[T]): T = map(input).asInstanceOf[T] + def get[T](input: Inlet[T]): Option[T] = map.get(input).asInstanceOf[Option[T]] + def getOrElse[T](input: Inlet[T], default: ⇒ T): T = map.getOrElse(input, default).asInstanceOf[T] } /** @@ -136,7 +129,7 @@ object FlexiMerge { /** * Cancel a specific upstream input stream. */ - def cancel(input: InP): Unit + def cancel(input: InPort): Unit /** * Replace current [[CompletionHandling]]. @@ -154,7 +147,7 @@ object FlexiMerge { * The function returns next behavior or [[#SameState]] to keep current behavior. */ sealed case class State[In](condition: ReadCondition[In])( - val onInput: (MergeLogicContext, InP, In) ⇒ State[_]) + val onInput: (MergeLogicContext, InPort, In) ⇒ State[_]) /** * Return this from [[State]] `onInput` to use same state for next element. @@ -184,8 +177,8 @@ object FlexiMerge { * or it can be swallowed to continue with remaining inputs. */ sealed case class CompletionHandling( - onComplete: (MergeLogicContext, InP) ⇒ State[_], - onError: (MergeLogicContext, InP, Throwable) ⇒ State[_]) + onComplete: (MergeLogicContext, InPort) ⇒ State[_], + onError: (MergeLogicContext, InPort, Throwable) ⇒ State[_]) /** * Will continue to operate until a read becomes unsatisfiable, then it completes. @@ -223,12 +216,10 @@ object FlexiMerge { * @param ports ports that this junction exposes * @param attributes optional attributes for this vertex */ -abstract class FlexiMerge[Out, P <: Ports](private[stream] val ports: P, attributes: OperationAttributes) { - - type PortT = P - type InP = StreamLayout.InPort +abstract class FlexiMerge[Out, S <: Shape](val shape: S, attributes: OperationAttributes) extends Graph[S, Unit] { + val module: StreamLayout.Module = new FlexiMergeModule(shape, createMergeLogic) - def createMergeLogic(p: P): MergeLogic[Out] + def createMergeLogic(s: S): MergeLogic[Out] override def toString = attributes.nameLifted match { case Some(n) ⇒ n diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiRoute.scala b/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiRoute.scala index 84f0970c470..ba9095baf4a 100644 --- a/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiRoute.scala +++ b/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiRoute.scala @@ -4,9 +4,9 @@ package akka.stream.scaladsl import akka.stream.impl.StreamLayout -import akka.stream.scaladsl.Graphs.{ OutPort, Ports } - +import akka.stream.{ Outlet, Shape, OutPort, Graph } import scala.collection.immutable +import akka.stream.impl.Junctions.FlexiRouteModule object FlexiRoute { @@ -14,9 +14,6 @@ object FlexiRoute { import scala.language.higherKinds - private type OutP = StreamLayout.OutPort - private type InP = StreamLayout.InPort - sealed trait DemandCondition /** @@ -28,11 +25,11 @@ object FlexiRoute { * has been completed. `IllegalArgumentException` is thrown if * that is not obeyed. */ - final case class DemandFrom(output: OutPort[_]) extends DemandCondition + final case class DemandFrom(output: Outlet[_]) extends DemandCondition object DemandFromAny { - def apply(outputs: immutable.Seq[OutPort[_]]): DemandFromAny = new DemandFromAny(outputs: _*) - def apply(p: Ports): DemandFromAny = new DemandFromAny(p.outlets.asInstanceOf[Seq[OutPort[Nothing]]]: _*) + def apply(outputs: immutable.Seq[Outlet[_]]): DemandFromAny = new DemandFromAny(outputs: _*) + def apply(p: Shape): DemandFromAny = new DemandFromAny(p.outlets.asInstanceOf[Seq[Outlet[Nothing]]]: _*) } /** * Demand condition for the [[RouteLogic#State]] that will be @@ -42,11 +39,11 @@ object FlexiRoute { * Cancelled and completed outputs are not used, i.e. it is allowed * to specify them in the list of `outputs`. */ - final case class DemandFromAny(outputs: OutPort[_]*) extends DemandCondition + final case class DemandFromAny(outputs: Outlet[_]*) extends DemandCondition object DemandFromAll { - def apply(outputs: immutable.Seq[OutPort[_]]): DemandFromAll = new DemandFromAll(outputs: _*) - def apply(p: Ports): DemandFromAll = new DemandFromAll(p.outlets.asInstanceOf[Seq[OutPort[Nothing]]]: _*) + def apply(outputs: immutable.Seq[Outlet[_]]): DemandFromAll = new DemandFromAll(outputs: _*) + def apply(p: Shape): DemandFromAll = new DemandFromAll(p.outlets.asInstanceOf[Seq[Outlet[Nothing]]]: _*) } /** * Demand condition for the [[RouteLogic#State]] that will be @@ -56,7 +53,7 @@ object FlexiRoute { * Cancelled and completed outputs are not used, i.e. it is allowed * to specify them in the list of `outputs`. */ - final case class DemandFromAll(outputs: OutPort[_]*) extends DemandCondition + final case class DemandFromAll(outputs: Outlet[_]*) extends DemandCondition /** * The possibly stateful logic that reads from the input and enables emitting to downstream @@ -78,19 +75,19 @@ object FlexiRoute { /** * @return `true` if at least one element has been requested by the given downstream (output). */ - def isDemandAvailable(output: OutP): Boolean + def isDemandAvailable(output: OutPort): Boolean /** * Emit one element downstream. It is only allowed to `emit` when * [[#isDemandAvailable]] is `true` for the given `output`, otherwise * `IllegalArgumentException` is thrown. */ - def emit(output: OutP, elem: Out): Unit + def emit(output: OutPort, elem: Out): Unit /** * Complete the given downstream successfully. */ - def complete(output: OutP): Unit + def complete(output: OutPort): Unit /** * Complete all downstreams successfully and cancel upstream. @@ -100,7 +97,7 @@ object FlexiRoute { /** * Complete the given downstream with failure. */ - def error(output: OutP, cause: Throwable): Unit + def error(output: OutPort, cause: Throwable): Unit /** * Complete all downstreams with failure and cancel upstream. @@ -124,7 +121,7 @@ object FlexiRoute { * The function returns next behavior or [[#SameState]] to keep current behavior. */ sealed case class State[Out](condition: DemandCondition)( - val onInput: (RouteLogicContext[Out], OutP, In) ⇒ State[_]) + val onInput: (RouteLogicContext[Out], OutPort, In) ⇒ State[_]) /** * Return this from [[State]] `onInput` to use same state for next element. @@ -152,9 +149,9 @@ object FlexiRoute { * It returns next behavior or [[#SameState]] to keep current behavior. */ sealed case class CompletionHandling( - onComplete: RouteLogicContext[Any] ⇒ Unit, - onError: (RouteLogicContext[Any], Throwable) ⇒ Unit, - onCancel: (RouteLogicContext[Any], OutP) ⇒ State[_]) + onComplete: RouteLogicContext[AnyRef] ⇒ Unit, + onError: (RouteLogicContext[AnyRef], Throwable) ⇒ Unit, + onCancel: (RouteLogicContext[AnyRef], OutPort) ⇒ State[_]) /** * When an output cancels it continues with remaining outputs. @@ -194,17 +191,16 @@ object FlexiRoute { * * @param attributes optional attributes for this vertex */ -abstract class FlexiRoute[In, P <: Ports](private[stream] val ports: P, attributes: OperationAttributes) { +abstract class FlexiRoute[In, S <: Shape](val shape: S, attributes: OperationAttributes) extends Graph[S, Unit] { import akka.stream.scaladsl.FlexiRoute._ - type PortT = P - type OutP = StreamLayout.OutPort - + val module: StreamLayout.Module = new FlexiRouteModule(shape, createRouteLogic) + /** * Create the stateful logic that will be used when reading input elements * and emitting output elements. Create a new instance every time. */ - def createRouteLogic(p: P): RouteLogic[In] + def createRouteLogic(s: S): RouteLogic[In] override def toString = attributes.nameLifted match { case Some(n) ⇒ n diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl/Flow.scala b/akka-stream/src/main/scala/akka/stream/scaladsl/Flow.scala index d26eec0df22..502e783c78f 100644 --- a/akka-stream/src/main/scala/akka/stream/scaladsl/Flow.scala +++ b/akka-stream/src/main/scala/akka/stream/scaladsl/Flow.scala @@ -4,11 +4,10 @@ package akka.stream.scaladsl import akka.stream.impl.Stages.{ MaterializingStageFactory, StageModule } -import akka.stream.impl.StreamLayout.{ Module, OutPort, InPort } -import akka.stream.scaladsl.FlowGraph.FlowGraphBuilder -import akka.stream.scaladsl.Graphs.FlowPorts +import akka.stream.impl.StreamLayout.Module +import akka.stream.{ FlowShape, Inlet, Outlet, InPort, OutPort } import akka.stream.scaladsl.OperationAttributes._ -import akka.stream.{ TimerTransformer, TransformerLike, OverflowStrategy } +import akka.stream.{ TimerTransformer, TransformerLike, OverflowStrategy, FlowMaterializer, FlattenStrategy, Graph } import akka.util.Collections.EmptyImmutableSeq import org.reactivestreams.Processor import scala.annotation.unchecked.uncheckedVariance @@ -16,30 +15,28 @@ import scala.collection.immutable import scala.concurrent.duration.{ Duration, FiniteDuration } import scala.concurrent.Future import scala.language.higherKinds -import akka.stream.FlowMaterializer -import akka.stream.FlattenStrategy import akka.stream.stage._ import akka.stream.impl.{ Stages, StreamLayout, FlowModule } /** * A `Flow` is a set of stream processing steps that has one open input and one open output. */ -final class Flow[-In, +Out, +Mat](m: StreamLayout.Module, val inlet: Graphs.InPort[In], val outlet: Graphs.OutPort[Out]) - extends FlowOps[Out, Mat] with Graphs.Graph[Graphs.FlowPorts[In, Out], Mat] { +final class Flow[-In, +Out, +Mat](m: StreamLayout.Module, val inlet: Inlet[In], val outlet: Outlet[Out]) + extends FlowOps[Out, Mat] with Graph[FlowShape[In, Out], Mat] { private[stream] override val module: StreamLayout.Module = m private[stream] def this(module: FlowModule[In @uncheckedVariance, Out @uncheckedVariance, Mat]) = this(module, module.inPort, module.outPort) - override val ports: FlowPorts[In, Out] = FlowPorts(inlet, outlet) + override val shape: FlowShape[In, Out] = FlowShape(inlet, outlet) private[stream] def carbonCopy(): Flow[In, Out, Mat] = { val flowCopy = this.module.carbonCopy() new Flow( flowCopy.module, - flowCopy.inPorts(inlet).asInstanceOf[Graphs.InPort[In]], - flowCopy.outPorts(outlet).asInstanceOf[Graphs.OutPort[Out]]) + flowCopy.inPorts(inlet).asInstanceOf[Inlet[In]], + flowCopy.outPorts(outlet).asInstanceOf[Outlet[Out]]) } override type Repr[+O, +M] = Flow[In @uncheckedVariance, O, M] @@ -78,8 +75,8 @@ final class Flow[-In, +Out, +Mat](m: StreamLayout.Module, val inlet: Graphs.InPo val sourceCopy = module.carbonCopy() new Flow( sourceCopy.module.transformMaterializedValue(f.asInstanceOf[Any ⇒ Any]), - sourceCopy.inPorts(inlet).asInstanceOf[Graphs.InPort[In]], - sourceCopy.outPorts(outlet).asInstanceOf[Graphs.OutPort[Out]]) + sourceCopy.inPorts(inlet).asInstanceOf[Inlet[In]], + sourceCopy.outPorts(outlet).asInstanceOf[Outlet[Out]]) } /** @@ -98,33 +95,33 @@ final class Flow[-In, +Out, +Mat](m: StreamLayout.Module, val inlet: Graphs.InPo } // FIXME: Materialized value is not combined! - def concat[Out2 >: Out](source: Source[Out2, _]): Flow[In, Out2, Unit] = { + def concat[Out2 >: Out, Mat2](source: Source[Out2, Mat2]): Flow[In, Out2, Unit] = { this.via(Flow() { implicit builder ⇒ - import FlowGraph.Implicits._ - val concat = Concat[Out2]() - source ~> concat.second - (concat.first, concat.out) + import Graph.Implicits._ + val concat = builder.add(Concat[Out2]()) + source ~> concat.in(1) + (concat.in(0), concat.out) }) } /** INTERNAL API */ override private[stream] def andThen[U](op: StageModule): Repr[U, Mat] = { //No need to copy here, op is a fresh instance - new Flow[In, U, Mat](module.grow(op).connect(outlet, op.inPort), inlet, op.outPort.asInstanceOf[Graphs.OutPort[U]]) + new Flow[In, U, Mat](module.grow(op).connect(outlet, op.inPort), inlet, op.outPort.asInstanceOf[Outlet[U]]) } private[stream] def andThenMat[U, Mat2](op: MaterializingStageFactory): Repr[U, Mat2] = { - new Flow[In, U, Mat2](module.grow(op, (m: Mat, m2: Mat2) ⇒ m2).connect(outlet, op.inPort), inlet, op.outPort.asInstanceOf[Graphs.OutPort[U]]) + new Flow[In, U, Mat2](module.grow(op, (m: Mat, m2: Mat2) ⇒ m2).connect(outlet, op.inPort), inlet, op.outPort.asInstanceOf[Outlet[U]]) } private[stream] def andThenMat[U, Mat2, O >: Out](processorFactory: () ⇒ (Processor[O, U], Mat2)): Repr[U, Mat2] = { val op = Stages.DirectProcessor(processorFactory.asInstanceOf[() ⇒ (Processor[Any, Any], Any)]) - new Flow[In, U, Mat2](module.grow(op, (m: Mat, m2: Mat2) ⇒ m2).connect(outlet, op.inPort), inlet, op.outPort.asInstanceOf[Graphs.OutPort[U]]) + new Flow[In, U, Mat2](module.grow(op, (m: Mat, m2: Mat2) ⇒ m2).connect(outlet, op.inPort), inlet, op.outPort.asInstanceOf[Outlet[U]]) } override def withAttributes(attr: OperationAttributes): Repr[Out, Mat] = { val newModule = module.withAttributes(attr) - new Flow(newModule, newModule.inPorts.head.asInstanceOf[Graphs.InPort[In]], newModule.outPorts.head.asInstanceOf[Graphs.OutPort[Out]]) + new Flow(newModule, newModule.inPorts.head.asInstanceOf[Inlet[In]], newModule.outPorts.head.asInstanceOf[Outlet[Out]]) } /** @@ -150,7 +147,7 @@ final class Flow[-In, +Out, +Mat](m: StreamLayout.Module, val inlet: Graphs.InPo * Applies given [[OperationAttributes]] to a given section. */ def section[O, O2 >: Out, Mat2](attributes: OperationAttributes)(section: Flow[O2, O2, Unit] ⇒ Flow[O2, O, Mat2]): Flow[In, O, Mat2] = { - this.section[O, O2, Mat2, Mat2](attributes, (parentm: Mat, subm: Mat2) ⇒ subm)(section) + this.section[O, O2, Mat2, Mat2](attributes, Keep.right)(section) } } diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl/FlowGraph.scala b/akka-stream/src/main/scala/akka/stream/scaladsl/FlowGraph.scala deleted file mode 100644 index 2f1856a1040..00000000000 --- a/akka-stream/src/main/scala/akka/stream/scaladsl/FlowGraph.scala +++ /dev/null @@ -1,374 +0,0 @@ -/** - * Copyright (C) 2014 Typesafe Inc. - */ -package akka.stream.scaladsl - -import akka.stream.impl.Junctions._ -import akka.stream.impl.GenJunctions._ -import akka.stream.impl.Stages.{ MaterializingStageFactory, StageModule } -import akka.stream.impl._ -import akka.stream.impl.StreamLayout._ -import akka.stream.scaladsl.FlowGraph.FlowGraphBuilder -import akka.stream.scaladsl.Graphs.{ InPort, OutPort } -import OperationAttributes.name - -import scala.collection.immutable - -object Merge { - - final case class MergePorts[T](in: Vector[InPort[T]], out: OutPort[T]) extends Graphs.Ports { - override val inlets: immutable.Seq[InPort[_]] = in - override val outlets: immutable.Seq[OutPort[_]] = List(out) - - override def deepCopy(): MergePorts[T] = MergePorts(in.map(i ⇒ new InPort[T](i.toString)), new OutPort(out.toString)) - } - - def apply[T](inputPorts: Int, attributes: OperationAttributes = OperationAttributes.none)(implicit b: FlowGraphBuilder): MergePorts[T] = { - val mergeModule = new MergeModule( - Vector.fill(inputPorts)(new InPort[T]("Merge.in")), - new OutPort[T]("Merge.out"), - OperationAttributes.name("Merge") and attributes) - b.addModule(mergeModule) - MergePorts(mergeModule.ins, mergeModule.out) - } - -} - -object MergePreferred { - final case class MergePreferredPorts[T](preferred: InPort[T], in: Vector[InPort[T]], out: OutPort[T]) extends Graphs.Ports { - override val inlets: immutable.Seq[InPort[_]] = in :+ preferred - override val outlets: immutable.Seq[OutPort[_]] = List(out) - - override def deepCopy(): MergePreferredPorts[T] = - MergePreferredPorts(new InPort(preferred.toString), in.map(i ⇒ new InPort[T](i.toString)), new OutPort(out.toString)) - } - - def apply[T](secondaryPorts: Int, attributes: OperationAttributes = OperationAttributes.none)(implicit b: FlowGraphBuilder): MergePreferredPorts[T] = { - val mergeModule = new MergePreferredModule( - new InPort[T]("Preferred.preferred"), - Vector.fill(secondaryPorts)(new InPort[T]("Preferred.in")), - new OutPort[T]("Preferred.out"), - OperationAttributes.name("MergePreferred") and attributes) - b.addModule(mergeModule) - MergePreferredPorts(mergeModule.preferred, mergeModule.ins, mergeModule.out) - } -} - -object Broadcast { - - final case class BroadcastPorts[T](in: InPort[T], out: Vector[OutPort[T]]) extends Graphs.Ports { - override val inlets: immutable.Seq[InPort[_]] = List(in) - override val outlets: immutable.Seq[OutPort[_]] = out - - override def deepCopy(): BroadcastPorts[T] = - BroadcastPorts(new InPort(in.toString), out.map(o ⇒ new OutPort[T](o.toString))) - } - - def apply[T](outputPorts: Int, attributes: OperationAttributes = OperationAttributes.none)(implicit b: FlowGraphBuilder): BroadcastPorts[T] = { - val bcastModule = new BroadcastModule( - new InPort[T]("Bcast.in"), - Vector.fill(outputPorts)(new OutPort[T]("Bcast.out")), - OperationAttributes.name("Broadcast") and attributes) - b.addModule(bcastModule) - BroadcastPorts(bcastModule.in, bcastModule.outs) - } -} - -object Balance { - - final case class BalancePorts[T](in: InPort[T], out: Vector[OutPort[T]]) extends Graphs.Ports { - override val inlets: immutable.Seq[InPort[_]] = List(in) - override val outlets: immutable.Seq[OutPort[_]] = out - - override def deepCopy(): BalancePorts[T] = - BalancePorts(new InPort(in.toString), out.map(o ⇒ new OutPort[T](o.toString))) - } - - def apply[T]( - outputPorts: Int, - waitForAllDownstreams: Boolean = false, - attributes: OperationAttributes = OperationAttributes.none)(implicit b: FlowGraphBuilder): BalancePorts[T] = { - val bcastModule = new BalanceModule( - new InPort[T]("Balance.in"), - Vector.fill(outputPorts)(new OutPort[T]("Balance.out")), - waitForAllDownstreams, - OperationAttributes.name("Balance") and attributes) - b.addModule(bcastModule) - BalancePorts(bcastModule.in, bcastModule.outs) - } - -} - -object Zip { - - final case class ZipPorts[A, B](left: InPort[A], right: InPort[B], out: OutPort[(A, B)]) extends Graphs.Ports { - override val inlets: immutable.Seq[InPort[_]] = List(left, right) - override val outlets: immutable.Seq[OutPort[_]] = List(out) - - override def deepCopy(): ZipPorts[A, B] = - ZipPorts(new InPort(left.toString), new InPort(right.toString), new OutPort(out.toString)) - } - - def apply[A, B](attributes: OperationAttributes = OperationAttributes.none)(implicit b: FlowGraphBuilder): ZipPorts[A, B] = { - val zipWithModule = new ZipWith2Module( - new InPort[A]("Zip.left"), - new InPort[B]("Zip.right"), - new OutPort[(A, B)]("Zip.out"), - (a: A, b: B) ⇒ (a, b), - OperationAttributes.name("Zip") and attributes) - b.addModule(zipWithModule) - ZipPorts(zipWithModule.in1, zipWithModule.in2, zipWithModule.out) - } - -} - -object ZipWith extends ZipWithApply - -object Unzip { - - final case class UnzipPorts[A, B](in: InPort[(A, B)], left: OutPort[A], right: OutPort[B]) extends Graphs.Ports { - override def inlets: immutable.Seq[InPort[_]] = List(in) - override def outlets: immutable.Seq[OutPort[_]] = List(left, right) - - override def deepCopy(): UnzipPorts[A, B] = - UnzipPorts(new InPort(in.toString), new OutPort(left.toString), new OutPort(right.toString)) - } - - def apply[A, B](attributes: OperationAttributes = OperationAttributes.none)(implicit b: FlowGraphBuilder): UnzipPorts[A, B] = { - val unzipModule = new UnzipModule( - new InPort[(A, B)]("Unzip.in"), - new OutPort[A]("Unzip.left"), - new OutPort[B]("Unzip.right"), - OperationAttributes.name("Unzip") and attributes) - b.addModule(unzipModule) - UnzipPorts(unzipModule.in, unzipModule.left, unzipModule.right) - } -} - -object Concat { - - final case class ConcatPorts[A](first: InPort[A], second: InPort[A], out: OutPort[A]) extends Graphs.Ports { - override val inlets: immutable.Seq[InPort[_]] = List(first, second) - override val outlets: immutable.Seq[OutPort[_]] = List(out) - - override def deepCopy(): ConcatPorts[A] = - ConcatPorts(new InPort(first.toString), new InPort(second.toString), new OutPort(out.toString)) - } - - def apply[A](attributes: OperationAttributes = OperationAttributes.none)(implicit b: FlowGraphBuilder): ConcatPorts[A] = { - val concatModdule = new ConcatModule( - new InPort[A]("concat.first"), - new InPort[A]("concat.second"), - new OutPort[A]("concat.out"), - OperationAttributes.name("Concat") and attributes) - b.addModule(concatModdule) - ConcatPorts(concatModdule.first, concatModdule.second, concatModdule.out) - } - -} - -object FlowGraph extends FlowGraphApply { - import akka.stream.scaladsl.Graphs._ - - class FlowGraphBuilder private[stream] () { - private var moduleInProgress: Module = EmptyModule - private var inPortMapping = Map.empty[StreamLayout.InPort, StreamLayout.InPort] - private var outPortMapping = Map.empty[StreamLayout.OutPort, StreamLayout.OutPort] - - private[stream] def chainEdge[A, B](from: OutPort[A], via: Flow[A, B, _]): OutPort[B] = { - val flowCopy = via.carbonCopy() - moduleInProgress = - moduleInProgress - .grow(flowCopy.module) - .connect(resolvePort(from), flowCopy.inlet) - flowCopy.outlet - } - - def addEdge[A, B](from: OutPort[A], via: Flow[A, B, _], to: InPort[B]): Unit = { - val flowCopy = via.carbonCopy() - moduleInProgress = - moduleInProgress - .grow(flowCopy.module) - .connect(resolvePort(from), flowCopy.inlet) - .connect(flowCopy.outlet, resolvePort(to)) - } - - def addEdge[T](from: OutPort[T], to: InPort[T]): Unit = { - moduleInProgress = moduleInProgress.connect(resolvePort(from), resolvePort(to)) - } - - def add[T, P <: Ports](merge: FlexiMerge[T, P]): P = { - val p = merge.ports.deepCopy().asInstanceOf[P] - val module = new FlexiMergeModule(p, merge.createMergeLogic) - addModule(module) - p - } - - def add[T, P <: Ports](route: FlexiRoute[T, P]): P = { - val p = route.ports.deepCopy().asInstanceOf[P] - val module = new FlexiRouteModule(p, route.createRouteLogic) - addModule(module) - p - } - - // Assumes that junction is a new instance, so no copying needed here - private[stream] def addModule(module: Module): Unit = { - moduleInProgress = moduleInProgress.grow(module) - } - - private[stream] def importModule(module: Module): Mapping = { - val moduleCopy = module.carbonCopy() - addModule(moduleCopy.module) - moduleCopy - } - - private[stream] def remapPorts[P <: Ports, M1, M2](graph: Graph[P, _], moduleCopy: Mapping): P = { - /* - * This cast should not be necessary if we could express the constraint - * that deepCopy returns the same type as its receiver has. Would’a, could’a. - */ - val ports = graph.ports.deepCopy().asInstanceOf[P] - - val newInPortMap = ports.inlets.zip(graph.ports.inlets) map { - case (newGraphPort, oldGraphPort) ⇒ - newGraphPort -> moduleCopy.inPorts(oldGraphPort) - } - val newOutPortMap = ports.outlets.zip(graph.ports.outlets) map { - case (newGraphPort, oldGraphPort) ⇒ - newGraphPort -> moduleCopy.outPorts(oldGraphPort) - } - inPortMapping ++= newInPortMap - outPortMapping ++= newOutPortMap - ports - } - - /** - * Import a graph into this module, performing a deep copy, discarding its - * materialized value and returning the copied Ports that are now to be - * connected. - */ - def importGraph[P <: Ports](graph: Graph[P, _]): P = importGraph(graph, Keep.left) - - private[stream] def importGraph[P <: Ports, M1, M2](graph: Graph[P, _], combine: (M1, M2) ⇒ Any): P = { - val moduleCopy = graph.module.carbonCopy() - moduleInProgress = moduleInProgress.grow( - moduleCopy.module, - combine.asInstanceOf[(Any, Any) ⇒ Any]) - - remapPorts(graph, moduleCopy) - } - - private[stream] def resolvePort[T](port: StreamLayout.InPort): StreamLayout.InPort = { - inPortMapping.getOrElse(port, port) - } - - private[stream] def resolvePort[T](port: StreamLayout.OutPort): StreamLayout.OutPort = { - outPortMapping.getOrElse(port, port) - } - - private[stream] def andThen(port: StreamLayout.OutPort, op: StageModule): Unit = { - addModule(op) - moduleInProgress = moduleInProgress.connect(resolvePort(port), op.inPort) - } - - private[stream] def buildRunnable[Mat](): RunnableFlow[Mat] = { - if (!moduleInProgress.isRunnable) { - throw new IllegalStateException( - "Cannot build the RunnableFlow because there are unconnected ports: " + - (moduleInProgress.outPorts ++ moduleInProgress.inPorts).mkString(", ")) - } - new RunnableFlow(moduleInProgress) - } - - private[stream] def buildSource[T, Mat](outport: OutPort[T]): Source[T, Mat] = { - if (moduleInProgress.isRunnable) - throw new IllegalStateException("Cannot build the Source since no ports remain open") - if (!moduleInProgress.isSource) - throw new IllegalStateException( - s"Cannot build Source with open inputs (${moduleInProgress.inPorts.mkString(",")}) and outputs (${moduleInProgress.outPorts.mkString(",")})") - if (moduleInProgress.outPorts.head != resolvePort(outport)) - throw new IllegalStateException(s"provided OutPort $outport does not equal the module’s open OutPort ${moduleInProgress.outPorts.head}") - new Source(moduleInProgress, resolvePort(outport).asInstanceOf[OutPort[T]]) - } - - private[stream] def buildFlow[In, Out, Mat](inlet: InPort[In], outlet: OutPort[Out]): Flow[In, Out, Mat] = { - if (!moduleInProgress.isFlow) - throw new IllegalStateException( - s"Cannot build Flow with open inputs (${moduleInProgress.inPorts.mkString(",")}) and outputs (${moduleInProgress.outPorts.mkString(",")})") - if (moduleInProgress.outPorts.head != resolvePort(outlet)) - throw new IllegalStateException(s"provided OutPort $outlet does not equal the module’s open OutPort ${moduleInProgress.outPorts.head}") - if (moduleInProgress.inPorts.head != resolvePort(inlet)) - throw new IllegalStateException(s"provided InPort $inlet does not equal the module’s open InPort ${moduleInProgress.inPorts.head}") - new Flow(moduleInProgress, resolvePort(inlet).asInstanceOf[InPort[In]], resolvePort(outlet).asInstanceOf[OutPort[Out]]) - } - - private[stream] def buildSink[T, Mat](inport: InPort[T]): Sink[T, Mat] = { - if (moduleInProgress.isRunnable) - throw new IllegalStateException("Cannot build the Sink since no ports remain open") - if (!moduleInProgress.isSink) - throw new IllegalStateException( - s"Cannot build Sink with open inputs (${moduleInProgress.inPorts.mkString(",")}) and outputs (${moduleInProgress.outPorts.mkString(",")})") - if (moduleInProgress.inPorts.head != resolvePort(inport)) - throw new IllegalStateException(s"provided InPort $inport does not equal the module’s open InPort ${moduleInProgress.inPorts.head}") - new Sink(moduleInProgress, resolvePort(inport).asInstanceOf[InPort[T]]) - } - - private[stream] def module: Module = moduleInProgress - - } - - object Implicits { - - trait CombinerBase[+T] extends Any { - def importAndGetPort(b: FlowGraphBuilder): OutPort[T] - - def ~>(to: InPort[T])(implicit b: FlowGraphBuilder): Unit = { - b.addEdge(importAndGetPort(b), to) - } - - def ~>[Out](via: Flow[T, Out, _])(implicit b: FlowGraphBuilder): PortOps[Out, Unit] = { - b.chainEdge(importAndGetPort(b), via) - } - - def ~>(to: Sink[T, _])(implicit b: FlowGraphBuilder): Unit = { - val sinkCopy = to.carbonCopy() - b.addModule(sinkCopy.module) - b.addEdge(importAndGetPort(b), sinkCopy.inlet) - } - } - - class PortOps[+Out, +Mat](port: StreamLayout.OutPort, b: FlowGraphBuilder) extends FlowOps[Out, Mat] with CombinerBase[Out] { - override type Repr[+O, +M] = PortOps[O, M] - - def outlet: OutPort[Out] = port.asInstanceOf[OutPort[Out]] - - override def withAttributes(attr: OperationAttributes): Repr[Out, Mat] = - throw new UnsupportedOperationException("Cannot set attributes on chained ops from a junction output port") - - override private[scaladsl] def andThen[U](op: StageModule): Repr[U, Mat] = { - b.andThen(port, op) - new PortOps(op.outPort, b) - } - - override private[scaladsl] def andThenMat[U, Mat2](op: MaterializingStageFactory): Repr[U, Mat2] = { - // We don't track materialization here - b.andThen(port, op) - new PortOps(op.outPort, b) - } - - override def importAndGetPort(b: FlowGraphBuilder): OutPort[Out] = port.asInstanceOf[Graphs.OutPort[Out]] - } - - import scala.language.implicitConversions - implicit def port2flow[T](from: OutPort[T])(implicit b: FlowGraphBuilder): PortOps[T, Unit] = new PortOps(from, b) - - implicit class SourceArrow[T](val s: Source[T, _]) extends AnyVal with CombinerBase[T] { - override def importAndGetPort(b: FlowGraphBuilder): OutPort[T] = { - val mapping = b.importModule(s.module) - mapping.outPorts(s.outlet).asInstanceOf[OutPort[T]] - } - } - - } - -} diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl/Graph.scala b/akka-stream/src/main/scala/akka/stream/scaladsl/Graph.scala new file mode 100644 index 00000000000..3ee52741984 --- /dev/null +++ b/akka-stream/src/main/scala/akka/stream/scaladsl/Graph.scala @@ -0,0 +1,269 @@ +/** + * Copyright (C) 2014 Typesafe Inc. + */ +package akka.stream.scaladsl + +import akka.stream.impl.Junctions._ +import akka.stream.impl.GenJunctions._ +import akka.stream.impl.Stages.{ MaterializingStageFactory, StageModule } +import akka.stream.impl._ +import akka.stream.impl.StreamLayout._ +import akka.stream._ +import OperationAttributes.name + +import scala.collection.immutable + +object Merge { + def apply[T](inputPorts: Int, attributes: OperationAttributes = OperationAttributes.none): Graph[UniformFanInShape[T, T], Unit] = + new Graph[UniformFanInShape[T, T], Unit] { + val shape = new UniformFanInShape[T, T](inputPorts) + val module = new MergeModule(shape, OperationAttributes.name("Merge") and attributes) + } +} + +object MergePreferred { + final class MergePreferredShape[T](secondaryPorts: Int) extends UniformFanInShape[T, T](secondaryPorts) { + val preferred = newInlet[T]("preferred") + override def deepCopy(): MergePreferredShape[T] = new MergePreferredShape(secondaryPorts) + } + + def apply[T](secondaryPorts: Int, attributes: OperationAttributes = OperationAttributes.none): Graph[MergePreferredShape[T], Unit] = + new Graph[MergePreferredShape[T], Unit] { + val shape = new MergePreferredShape[T](secondaryPorts) + val module = new MergePreferredModule(shape, OperationAttributes.name("MergePreferred") and attributes) + } +} + +object Broadcast { + def apply[T](outputPorts: Int, attributes: OperationAttributes = OperationAttributes.none): Graph[UniformFanOutShape[T, T], Unit] = + new Graph[UniformFanOutShape[T, T], Unit] { + val shape = new UniformFanOutShape[T, T](outputPorts) + val module = new BroadcastModule(shape, OperationAttributes.name("Broadcast") and attributes) + } +} + +object Balance { + def apply[T](outputPorts: Int, waitForAllDownstreams: Boolean = false, attributes: OperationAttributes = OperationAttributes.none): Graph[UniformFanOutShape[T, T], Unit] = + new Graph[UniformFanOutShape[T, T], Unit] { + val shape = new UniformFanOutShape[T, T](outputPorts) + val module = new BalanceModule(shape, waitForAllDownstreams, OperationAttributes.name("Balance") and attributes) + } +} + +object Zip { + def apply[A, B](attributes: OperationAttributes = OperationAttributes.none): Graph[FanInShape2[A, B, (A, B)], Unit] = + new Graph[FanInShape2[A, B, (A, B)], Unit] { + val shape = new FanInShape2[A, B, (A, B)] + val module = new ZipWith2Module[A, B, (A, B)](shape, Keep.both, OperationAttributes.name("Zip") and attributes) + } +} + +// FIXME express ZipWithXModule in terms of generic FanInShapeX +object ZipWith extends ZipWithApply + +object Unzip { + def apply[A, B](attributes: OperationAttributes = OperationAttributes.none): Graph[FanOutShape2[(A, B), A, B], Unit] = + new Graph[FanOutShape2[(A, B), A, B], Unit] { + val shape = new FanOutShape2[(A, B), A, B] + val module = new UnzipModule(shape, OperationAttributes.name("Unzip") and attributes) + } +} + +object Concat { + def apply[A](attributes: OperationAttributes = OperationAttributes.none): Graph[UniformFanInShape[A, A], Unit] = + new Graph[UniformFanInShape[A, A], Unit] { + val shape = new UniformFanInShape[A, A](2) + val module = new ConcatModule(shape, OperationAttributes.name("Concat") and attributes) + } +} + +object Graph extends GraphApply { + + class Builder private[stream] () { + private var moduleInProgress: Module = EmptyModule + private var InletMapping = Map.empty[InPort, InPort] + private var OutletMapping = Map.empty[OutPort, OutPort] + + private[stream] def chainEdge[A, B](from: Outlet[A], via: Flow[A, B, _]): Outlet[B] = { + val flowCopy = via.carbonCopy() + moduleInProgress = + moduleInProgress + .grow(flowCopy.module) + .connect(resolvePort(from), flowCopy.inlet) + flowCopy.outlet + } + + def addEdge[A, B, M](from: Outlet[A], via: Flow[A, B, M], to: Inlet[B]): Unit = { + val flowCopy = via.carbonCopy() + moduleInProgress = + moduleInProgress + .grow(flowCopy.module) + .connect(resolvePort(from), flowCopy.inlet) + .connect(flowCopy.outlet, resolvePort(to)) + } + + def addEdge[T](from: Outlet[T], to: Inlet[T]): Unit = { + moduleInProgress = moduleInProgress.connect(resolvePort(from), resolvePort(to)) + } + + /** + * Import a graph into this module, performing a deep copy, discarding its + * materialized value and returning the copied Ports that are now to be + * connected. + */ + def add[S <: Shape](graph: Graph[S, _]): S = importGraph(graph, Keep.left) + + // Assumes that junction is a new instance, so no copying needed here + private[stream] def addModule(module: Module): Unit = { + moduleInProgress = moduleInProgress.grow(module) + } + + private[stream] def importModule(module: Module): Mapping = { + val moduleCopy = module.carbonCopy() + addModule(moduleCopy.module) + moduleCopy + } + + private[stream] def remapPorts[S <: Shape, M1, M2](graph: Graph[S, _], moduleCopy: Mapping): S = { + /* + * This cast should not be necessary if we could express the constraint + * that deepCopy returns the same type as its receiver has. Would’a, could’a. + */ + val ports = graph.shape.deepCopy().asInstanceOf[S] + + val newInletMap = ports.inlets.zip(graph.shape.inlets) map { + case (newGraphPort, oldGraphPort) ⇒ + newGraphPort -> moduleCopy.inPorts(oldGraphPort) + } + val newOutletMap = ports.outlets.zip(graph.shape.outlets) map { + case (newGraphPort, oldGraphPort) ⇒ + newGraphPort -> moduleCopy.outPorts(oldGraphPort) + } + InletMapping ++= newInletMap + OutletMapping ++= newOutletMap + ports + } + + private[stream] def importGraph[S <: Shape, M1, M2](graph: Graph[S, _], combine: (M1, M2) ⇒ Any): S = { + val moduleCopy = graph.module.carbonCopy() + moduleInProgress = moduleInProgress.grow( + moduleCopy.module, + combine.asInstanceOf[(Any, Any) ⇒ Any]) + + remapPorts(graph, moduleCopy) + } + + private[stream] def resolvePort[T](port: InPort): InPort = { + InletMapping.getOrElse(port, port) + } + + private[stream] def resolvePort[T](port: OutPort): OutPort = { + OutletMapping.getOrElse(port, port) + } + + private[stream] def andThen(port: OutPort, op: StageModule): Unit = { + addModule(op) + moduleInProgress = moduleInProgress.connect(resolvePort(port), op.inPort) + } + + private[stream] def buildRunnable[Mat](): RunnableFlow[Mat] = { + if (!moduleInProgress.isRunnable) { + throw new IllegalStateException( + "Cannot build the RunnableFlow because there are unconnected ports: " + + (moduleInProgress.outPorts ++ moduleInProgress.inPorts).mkString(", ")) + } + new RunnableFlow(moduleInProgress) + } + + private[stream] def buildSource[T, Mat](Outlet: Outlet[T]): Source[T, Mat] = { + if (moduleInProgress.isRunnable) + throw new IllegalStateException("Cannot build the Source since no ports remain open") + if (!moduleInProgress.isSource) + throw new IllegalStateException( + s"Cannot build Source with open inputs (${moduleInProgress.inPorts.mkString(",")}) and outputs (${moduleInProgress.outPorts.mkString(",")})") + if (moduleInProgress.outPorts.head != resolvePort(Outlet)) + throw new IllegalStateException(s"provided Outlet $Outlet does not equal the module’s open Outlet ${moduleInProgress.outPorts.head}") + new Source(moduleInProgress, resolvePort(Outlet).asInstanceOf[Outlet[T]]) + } + + private[stream] def buildFlow[In, Out, Mat](inlet: Inlet[In], outlet: Outlet[Out]): Flow[In, Out, Mat] = { + if (!moduleInProgress.isFlow) + throw new IllegalStateException( + s"Cannot build Flow with open inputs (${moduleInProgress.inPorts.mkString(",")}) and outputs (${moduleInProgress.outPorts.mkString(",")})") + if (moduleInProgress.outPorts.head != resolvePort(outlet)) + throw new IllegalStateException(s"provided Outlet $outlet does not equal the module’s open Outlet ${moduleInProgress.outPorts.head}") + if (moduleInProgress.inPorts.head != resolvePort(inlet)) + throw new IllegalStateException(s"provided Inlet $inlet does not equal the module’s open Inlet ${moduleInProgress.inPorts.head}") + new Flow(moduleInProgress, resolvePort(inlet).asInstanceOf[Inlet[In]], resolvePort(outlet).asInstanceOf[Outlet[Out]]) + } + + private[stream] def buildSink[T, Mat](Inlet: Inlet[T]): Sink[T, Mat] = { + if (moduleInProgress.isRunnable) + throw new IllegalStateException("Cannot build the Sink since no ports remain open") + if (!moduleInProgress.isSink) + throw new IllegalStateException( + s"Cannot build Sink with open inputs (${moduleInProgress.inPorts.mkString(",")}) and outputs (${moduleInProgress.outPorts.mkString(",")})") + if (moduleInProgress.inPorts.head != resolvePort(Inlet)) + throw new IllegalStateException(s"provided Inlet $Inlet does not equal the module’s open Inlet ${moduleInProgress.inPorts.head}") + new Sink(moduleInProgress, resolvePort(Inlet).asInstanceOf[Inlet[T]]) + } + + private[stream] def module: Module = moduleInProgress + + } + + object Implicits { + + trait CombinerBase[+T] extends Any { + def importAndGetPort(b: Graph.Builder): Outlet[T] + + def ~>(to: Inlet[T])(implicit b: Graph.Builder): Unit = { + b.addEdge(importAndGetPort(b), to) + } + + def ~>[Out](via: Flow[T, Out, _])(implicit b: Graph.Builder): PortOps[Out, Unit] = { + b.chainEdge(importAndGetPort(b), via) + } + + def ~>(to: Sink[T, _])(implicit b: Graph.Builder): Unit = { + val sinkCopy = to.carbonCopy() + b.addModule(sinkCopy.module) + b.addEdge(importAndGetPort(b), sinkCopy.inlet) + } + } + + class PortOps[+Out, +Mat](port: OutPort, b: Graph.Builder) extends FlowOps[Out, Mat] with CombinerBase[Out] { + override type Repr[+O, +M] = PortOps[O, M] + + def outlet: Outlet[Out] = port.asInstanceOf[Outlet[Out]] + + override def withAttributes(attr: OperationAttributes): Repr[Out, Mat] = + throw new UnsupportedOperationException("Cannot set attributes on chained ops from a junction output port") + + override private[scaladsl] def andThen[U](op: StageModule): Repr[U, Mat] = { + b.andThen(port, op) + new PortOps(op.outPort, b) + } + + override private[scaladsl] def andThenMat[U, Mat2](op: MaterializingStageFactory): Repr[U, Mat2] = { + // We don't track materialization here + b.andThen(port, op) + new PortOps(op.outPort, b) + } + + override def importAndGetPort(b: Graph.Builder): Outlet[Out] = port.asInstanceOf[Outlet[Out]] + } + + import scala.language.implicitConversions + implicit def port2flow[T](from: Outlet[T])(implicit b: Graph.Builder): PortOps[T, Unit] = new PortOps(from, b) + + implicit class SourceArrow[T](val s: Source[T, _]) extends AnyVal with CombinerBase[T] { + override def importAndGetPort(b: Graph.Builder): Outlet[T] = { + val mapping = b.importModule(s.module) + mapping.outPorts(s.outlet).asInstanceOf[Outlet[T]] + } + } + + } + +} diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl/Graphs.scala b/akka-stream/src/main/scala/akka/stream/scaladsl/Graphs.scala deleted file mode 100644 index 6f63d13b48f..00000000000 --- a/akka-stream/src/main/scala/akka/stream/scaladsl/Graphs.scala +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (C) 2015 Typesafe Inc. - */ -package akka.stream.scaladsl - -import akka.stream.impl.StreamLayout - -import scala.collection.immutable - -object Graphs { - - final class InPort[-T](override val toString: String) extends StreamLayout.InPort - final class OutPort[+T](override val toString: String) extends StreamLayout.OutPort - - trait Ports { - def inlets: immutable.Seq[InPort[_]] - def outlets: immutable.Seq[OutPort[_]] - - /** - * Create a copy of this Ports object, returning the same type as the - * original; this constraint can unfortunately not be expressed in the - * type system. - */ - def deepCopy(): Ports - } - - final case class SourcePorts[+T](outlet: OutPort[T]) extends Ports { - override val inlets: immutable.Seq[InPort[_]] = Nil - override val outlets: immutable.Seq[OutPort[_]] = List(outlet) - - override def deepCopy(): SourcePorts[T] = SourcePorts(new OutPort(outlet.toString)) - } - - final case class FlowPorts[-I, +O](inlet: InPort[I], outlet: OutPort[O]) extends Ports { - override val inlets: immutable.Seq[InPort[_]] = List(inlet) - override val outlets: immutable.Seq[OutPort[_]] = List(outlet) - - override def deepCopy(): FlowPorts[I, O] = FlowPorts(new InPort(inlet.toString), new OutPort(outlet.toString)) - } - - final case class SinkPorts[-T](inlet: InPort[T]) extends Ports { - override val inlets: immutable.Seq[InPort[_]] = List(inlet) - override val outlets: immutable.Seq[OutPort[_]] = Nil - - override def deepCopy(): SinkPorts[T] = SinkPorts(new InPort(inlet.toString)) - } - - /** - * In1 => Out1 - * Out2 <= In2 - */ - final case class BidiPorts[-In1, +Out1, -In2, +Out2](in1: InPort[In1], out1: OutPort[Out1], in2: InPort[In2], out2: OutPort[Out2]) extends Ports { - override val inlets: immutable.Seq[InPort[_]] = List(in1, in2) - override val outlets: immutable.Seq[OutPort[_]] = List(out1, out2) - - override def deepCopy(): BidiPorts[In1, Out1, In2, Out2] = - BidiPorts(new InPort(in1.toString), new OutPort(out1.toString), new InPort(in2.toString), new OutPort(out2.toString)) - } - - trait Graph[+P <: Ports, +M] extends Materializable { - override type MaterializedType <: M - type Ports = P - def ports: P - } -} \ No newline at end of file diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl/JavaConverters.scala b/akka-stream/src/main/scala/akka/stream/scaladsl/JavaConverters.scala index 3c39312cb3d..d9e93e09588 100644 --- a/akka-stream/src/main/scala/akka/stream/scaladsl/JavaConverters.scala +++ b/akka-stream/src/main/scala/akka/stream/scaladsl/JavaConverters.scala @@ -11,41 +11,29 @@ import akka.stream.scaladsl */ private[akka] object JavaConverters { - implicit final class AddAsJavaSource[Out](val source: scaladsl.Source[Out]) extends AnyVal { - def asJava: javadsl.Source[Out] = new javadsl.Source(source) + implicit final class AddAsJavaSource[Out, Mat](val source: scaladsl.Source[Out, Mat]) extends AnyVal { + def asJava: javadsl.Source[Out, Mat] = new javadsl.Source(source) } - implicit final class AddAsJavaUndefinedSource[Out](val source: scaladsl.UndefinedSource[Out]) extends AnyVal { - def asJava: javadsl.UndefinedSource[Out] = new javadsl.UndefinedSource(source) + implicit final class AddAsJavaFlow[In, Out, Mat](val flow: scaladsl.Flow[In, Out, Mat]) extends AnyVal { + def asJava: javadsl.Flow[In, Out, Mat] = new javadsl.Flow(flow) } - implicit final class AddAsJavaFlow[In, Out](val flow: scaladsl.Flow[In, Out]) extends AnyVal { - def asJava: javadsl.Flow[In, Out] = new javadsl.Flow[In, Out](flow) + implicit final class AddAsJavaSink[In, Mat](val sink: scaladsl.Sink[In, Mat]) extends AnyVal { + def asJava: javadsl.Sink[In, Mat] = new javadsl.Sink(sink) } - implicit final class AddAsJavaSink[In](val sink: scaladsl.Sink[In]) extends AnyVal { - def asJava: javadsl.Sink[In] = new javadsl.Sink[In](sink) - } - implicit final class AddAsJavaUndefinedSink[Out](val sink: scaladsl.UndefinedSink[Out]) extends AnyVal { - def asJava: javadsl.UndefinedSink[Out] = new javadsl.UndefinedSink(sink) - } - implicit final class AsAsJavaFlowGraphBuilder[Out](val builder: scaladsl.FlowGraphBuilder) extends AnyVal { - def asJava: javadsl.FlowGraphBuilder = new javadsl.FlowGraphBuilder(builder) + implicit final class AsAsJavaFlowGraphBuilder[Out](val builder: scaladsl.Graph.Builder) extends AnyVal { + def asJava: javadsl.Graph.Builder = new javadsl.Graph.Builder(builder) } - implicit final class AddAsScalaSource[Out](val source: javadsl.Source[Out]) extends AnyVal { - def asScala: scaladsl.Source[Out] = source.asInstanceOf[javadsl.Source[Out]].asScala - } - implicit final class AsAsScalaUndefinedSource[Out](val source: javadsl.UndefinedSource[Out]) extends AnyVal { - def asScala: scaladsl.UndefinedSource[Out] = source.asScala - } - implicit final class AddAsScalaFlow[In, Out](val flow: javadsl.Flow[In, Out]) extends AnyVal { - def asScala: scaladsl.Flow[In, Out] = flow.asInstanceOf[javadsl.Flow[In, Out]].asScala + implicit final class AddAsScalaSource[Out, Mat](val source: javadsl.Source[Out, Mat]) extends AnyVal { + def asScala: scaladsl.Source[Out, Mat] = source.asScala } - implicit final class AddAsScalaSink[In](val sink: javadsl.Sink[In]) extends AnyVal { - def asScala: scaladsl.Sink[In] = sink.asInstanceOf[javadsl.Sink[In]].asScala + implicit final class AddAsScalaFlow[In, Out, Mat](val flow: javadsl.Flow[In, Out, Mat]) extends AnyVal { + def asScala: scaladsl.Flow[In, Out, Mat] = flow.asScala } - implicit final class AsAsScalaUndefinedSink[Out](val sink: javadsl.UndefinedSink[Out]) extends AnyVal { - def asScala: scaladsl.UndefinedSink[Out] = sink.asScala + implicit final class AddAsScalaSink[In, Mat](val sink: javadsl.Sink[In, Mat]) extends AnyVal { + def asScala: scaladsl.Sink[In, Mat] = sink.asScala } - implicit final class AsAsScalaFlowGraphBuilder[Out](val builder: javadsl.FlowGraphBuilder) extends AnyVal { - def asScala: FlowGraphBuilder = builder.asScala + implicit final class AsAsScalaFlowGraphBuilder[Out](val builder: javadsl.Graph.Builder) extends AnyVal { + def asScala: Graph.Builder = builder.asScala } } diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl/Materializable.scala b/akka-stream/src/main/scala/akka/stream/scaladsl/Materialization.scala similarity index 73% rename from akka-stream/src/main/scala/akka/stream/scaladsl/Materializable.scala rename to akka-stream/src/main/scala/akka/stream/scaladsl/Materialization.scala index 60cf3711c5b..d0911e74b31 100644 --- a/akka-stream/src/main/scala/akka/stream/scaladsl/Materializable.scala +++ b/akka-stream/src/main/scala/akka/stream/scaladsl/Materialization.scala @@ -18,15 +18,3 @@ object Keep { def right[L, R]: (L, R) ⇒ R = _right.asInstanceOf[(L, R) ⇒ R] def both[L, R]: (L, R) ⇒ (L, R) = _both.asInstanceOf[(L, R) ⇒ (L, R)] } - -/** - * Common trait for things that have a MaterializedType. - */ -trait Materializable { - type MaterializedType - - /** - * Every materializable element must be backed by a stream layout module - */ - private[stream] def module: StreamLayout.Module -} diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl/Sink.scala b/akka-stream/src/main/scala/akka/stream/scaladsl/Sink.scala index 39633e3aef5..3bbcc12c26c 100644 --- a/akka-stream/src/main/scala/akka/stream/scaladsl/Sink.scala +++ b/akka-stream/src/main/scala/akka/stream/scaladsl/Sink.scala @@ -5,7 +5,7 @@ package akka.stream.scaladsl import akka.actor.{ ActorRef, Props } import akka.stream.impl._ -import akka.stream.scaladsl.Graphs.SinkPorts +import akka.stream.{ SinkShape, Inlet, Outlet, Graph } import akka.stream.scaladsl.OperationAttributes._ import akka.stream.stage.{ TerminationDirective, Directive, Context, PushStage } import org.reactivestreams.{ Publisher, Subscriber } @@ -18,17 +18,17 @@ import akka.stream.FlowMaterializer * A `Sink` is a set of stream processing steps that has one open input and an attached output. * Can be used as a `Subscriber` */ -final class Sink[-In, Mat](m: StreamLayout.Module, val inlet: Graphs.InPort[In]) - extends Graphs.Graph[Graphs.SinkPorts[In], Mat] { +final class Sink[-In, +Mat](m: StreamLayout.Module, val inlet: Inlet[In]) + extends Graph[SinkShape[In], Mat] { private[stream] override val module: StreamLayout.Module = m private[akka] def this(module: SinkModule[In @uncheckedVariance, Mat]) = this(module, module.inPort) - override def ports: SinkPorts[In] = SinkPorts(inlet) + override val shape: SinkShape[In] = SinkShape(inlet) private[stream] def carbonCopy(): Sink[In, Mat] = { val sinkCopy = module.carbonCopy() - new Sink(sinkCopy.module, sinkCopy.inPorts(inlet).asInstanceOf[Graphs.InPort[In]]) + new Sink(sinkCopy.module, sinkCopy.inPorts(inlet).asInstanceOf[Inlet[In]]) } /** @@ -42,12 +42,12 @@ final class Sink[-In, Mat](m: StreamLayout.Module, val inlet: Graphs.InPort[In]) val sinkCopy = module.carbonCopy() new Sink( sinkCopy.module.transformMaterializedValue(f.asInstanceOf[Any ⇒ Any]), - sinkCopy.inPorts(inlet).asInstanceOf[Graphs.InPort[In]]) + sinkCopy.inPorts(inlet).asInstanceOf[Inlet[In]]) } def withAttributes(attr: OperationAttributes): Sink[In, Mat] = { val newModule = module.withAttributes(attr) - new Sink(newModule, newModule.inPorts.head.asInstanceOf[Graphs.InPort[In]]) + new Sink(newModule, newModule.inPorts.head.asInstanceOf[Inlet[In]]) } } diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl/Source.scala b/akka-stream/src/main/scala/akka/stream/scaladsl/Source.scala index 75e9752fb17..909a8e24d7b 100644 --- a/akka-stream/src/main/scala/akka/stream/scaladsl/Source.scala +++ b/akka-stream/src/main/scala/akka/stream/scaladsl/Source.scala @@ -4,7 +4,7 @@ package akka.stream.scaladsl import akka.stream.impl.Stages.{ MaterializingStageFactory, StageModule } -import akka.stream.scaladsl.Graphs.SourcePorts +import akka.stream.{ SourceShape, Inlet, Outlet } import akka.stream.stage.{ TerminationDirective, Directive, Context, PushPullStage } import scala.annotation.unchecked.uncheckedVariance @@ -15,7 +15,7 @@ import org.reactivestreams.Publisher import scala.collection.immutable import scala.concurrent.duration.FiniteDuration import scala.concurrent.{ ExecutionContext, Future } -import akka.stream.FlowMaterializer +import akka.stream.{ FlowMaterializer, Graph } import akka.stream.impl._ import akka.actor.Cancellable import akka.actor.ActorRef @@ -28,15 +28,15 @@ import org.reactivestreams.Subscriber * an “atomic” source, e.g. from a collection or a file. Materialization turns a Source into * a Reactive Streams `Publisher` (at least conceptually). */ -final class Source[+Out, +Mat](m: StreamLayout.Module, val outlet: Graphs.OutPort[Out]) - extends FlowOps[Out, Mat] with Graphs.Graph[Graphs.SourcePorts[Out], Mat] { +final class Source[+Out, +Mat](m: StreamLayout.Module, val outlet: Outlet[Out]) + extends FlowOps[Out, Mat] with Graph[SourceShape[Out], Mat] { private[stream] override val module: StreamLayout.Module = m def this(sourceModule: SourceModule[Out @uncheckedVariance, Mat]) = this(sourceModule, sourceModule.outPort) override type Repr[+O, +M] = Source[O, M] - override val ports = SourcePorts[Out](outlet) + override val shape = SourceShape[Out](outlet) def via[T, Mat2](flow: Flow[Out, T, Mat2]): Source[T, Mat2] = via(flow, (sourcem: Mat, flowm: Mat2) ⇒ flowm) @@ -69,18 +69,18 @@ final class Source[+Out, +Mat](m: StreamLayout.Module, val outlet: Graphs.OutPor val sourceCopy = module.carbonCopy() new Source( sourceCopy.module.transformMaterializedValue(f.asInstanceOf[Any ⇒ Any]), - sourceCopy.outPorts(outlet).asInstanceOf[Graphs.OutPort[Out]]) + sourceCopy.outPorts(outlet).asInstanceOf[Outlet[Out]]) } /** INTERNAL API */ override private[scaladsl] def andThen[U](op: StageModule): Repr[U, Mat] = { // No need to copy here, op is a fresh instance // FIXME: currently combine ignores here - new Source(module.grow(op).connect(outlet, op.inPort), op.outPort.asInstanceOf[Graphs.OutPort[U]]) + new Source(module.grow(op).connect(outlet, op.inPort), op.outPort.asInstanceOf[Outlet[U]]) } override private[scaladsl] def andThenMat[U, Mat2](op: MaterializingStageFactory): Repr[U, Mat2] = { - new Source(module.grow(op, (m: Mat, m2: Mat2) ⇒ m2).connect(outlet, op.inPort), op.outPort.asInstanceOf[Graphs.OutPort[U]]) + new Source(module.grow(op, (m: Mat, m2: Mat2) ⇒ m2).connect(outlet, op.inPort), op.outPort.asInstanceOf[Outlet[U]]) } /** @@ -114,7 +114,7 @@ final class Source[+Out, +Mat](m: StreamLayout.Module, val outlet: Graphs.OutPor * emitted by that source is emitted after the last element of this * source. */ - def concat[Out2 >: Out](second: Source[Out2, _]): Source[Out2, Unit] = Source.concat(this, second) + def concat[Out2 >: Out, M](second: Source[Out2, M]): Source[Out2, (Mat, M)] = Source.concat(this, second) /** * Concatenates a second source so that the first element @@ -123,7 +123,7 @@ final class Source[+Out, +Mat](m: StreamLayout.Module, val outlet: Graphs.OutPor * * This is a shorthand for [[concat]] */ - def ++[Out2 >: Out](second: Source[Out2, _]): Source[Out2, Unit] = concat(second) + def ++[Out2 >: Out, M](second: Source[Out2, M]): Source[Out2, (Mat, M)] = concat(second) /** * Applies given [[OperationAttributes]] to a given section. @@ -133,8 +133,8 @@ final class Source[+Out, +Mat](m: StreamLayout.Module, val outlet: Graphs.OutPor new Source( module .grow(subFlow.module.wrap(), combine) - .connect(outlet, subFlow.ports.inlet), - subFlow.ports.outlet) + .connect(outlet, subFlow.shape.inlet), + subFlow.shape.outlet) } def section[O, O2 >: Out, Mat2](attributes: OperationAttributes)(section: Flow[O2, O2, Unit] ⇒ Flow[O2, O, Mat2]): Source[O, Mat2] = { @@ -143,7 +143,7 @@ final class Source[+Out, +Mat](m: StreamLayout.Module, val outlet: Graphs.OutPor override def withAttributes(attr: OperationAttributes): Repr[Out, Mat] = { val newModule = module.withAttributes(attr) - new Source(newModule, newModule.outPorts.head.asInstanceOf[Graphs.OutPort[Out]]) + new Source(newModule, newModule.outPorts.head.asInstanceOf[Outlet[Out]]) } } @@ -279,13 +279,14 @@ object Source extends SourceApply { * emitted by the second source is emitted after the last element of the first * source. */ - def concat[T](source1: Source[T, _], source2: Source[T, _]): Source[T, Unit] = { + def concat[T, M1, M2](source1: Source[T, M1], source2: Source[T, M2]): Source[T, (M1, M2)] = { source1.via(Flow() { implicit builder ⇒ - import FlowGraph.Implicits._ - val concat = Concat[T]() - source2 ~> concat.second - (concat.first, concat.out) + import Graph.Implicits._ + val concat = builder add Concat[T]() + source2 ~> concat.in(1) + (concat.in(0), concat.out) }) + ??? // FIXME (the fix is already in a later commit by RK } /** From 618c3f71d920956cfc0ff326308e20d47bd90523 Mon Sep 17 00:00:00 2001 From: Roland Kuhn Date: Thu, 19 Feb 2015 22:21:10 +0100 Subject: [PATCH 2/4] ALMOST ALL TESTS COMPILE! --- .../akka/stream/DslConsistencySpec.scala | 35 +++----- .../stream/DslFactoriesConsistencySpec.scala | 37 +++----- .../stream/actor/ActorPublisherSpec.scala | 4 +- .../akka/stream/impl/StreamLayoutSpec.scala | 4 +- .../scaladsl/FlowGraphCompileSpec.scala | 12 +-- .../akka/stream/scaladsl/FlowJoinSpec.scala | 6 +- .../stream/scaladsl/GraphBalanceSpec.scala | 34 ++++---- .../stream/scaladsl/GraphBroadcastSpec.scala | 26 +++--- .../stream/scaladsl/GraphConcatSpec.scala | 38 ++++----- .../stream/scaladsl/GraphFlexiMergeSpec.scala | 83 +++++++++--------- .../stream/scaladsl/GraphFlexiRouteSpec.scala | 12 +-- .../akka/stream/scaladsl/GraphFlowSpec.scala | 54 ++++++------ .../GraphJunctionAttributesSpec.scala | 8 +- .../akka/stream/scaladsl/GraphMergeSpec.scala | 27 +++--- .../scaladsl/GraphOpsIntegrationSpec.scala | 63 +++++++------- .../stream/scaladsl/GraphPartialSpec.scala | 58 ++++++------- .../scaladsl/GraphPreferredMergeSpec.scala | 20 ++--- .../akka/stream/scaladsl/GraphUnzipSpec.scala | 44 +++++----- .../akka/stream/scaladsl/GraphZipSpec.scala | 23 +++-- .../stream/scaladsl/GraphZipWithSpec.scala | 85 +++++++++---------- .../stream/scaladsl/PublisherSinkSpec.scala | 6 +- .../scala/akka/stream/scaladsl/SinkSpec.scala | 10 +-- .../akka/stream/scaladsl/SourceSpec.scala | 4 +- .../akka/stream/scaladsl/TickSourceSpec.scala | 2 +- .../akka/stream/scaladsl/FlexiMerge.scala | 2 + 25 files changed, 333 insertions(+), 364 deletions(-) diff --git a/akka-stream-tests/src/test/scala/akka/stream/DslConsistencySpec.scala b/akka-stream-tests/src/test/scala/akka/stream/DslConsistencySpec.scala index bdcdd91a39f..15abd7a7251 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/DslConsistencySpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/DslConsistencySpec.scala @@ -10,26 +10,17 @@ import org.scalatest.WordSpec class DslConsistencySpec extends WordSpec with Matchers { - val sFlowClass = classOf[akka.stream.scaladsl.Flow[_, _]] - val jFlowClass = classOf[akka.stream.javadsl.Flow[_, _]] + val sFlowClass = classOf[akka.stream.scaladsl.Flow[_, _, _]] + val jFlowClass = classOf[akka.stream.javadsl.Flow[_, _, _]] - val sSourceClass = classOf[akka.stream.scaladsl.Source[_]] - val jSourceClass = classOf[akka.stream.javadsl.Source[_]] + val sSourceClass = classOf[akka.stream.scaladsl.Source[_, _]] + val jSourceClass = classOf[akka.stream.javadsl.Source[_, _]] - val sSinkClass = classOf[akka.stream.scaladsl.Sink[_]] - val jSinkClass = classOf[akka.stream.javadsl.Sink[_]] + val sSinkClass = classOf[akka.stream.scaladsl.Sink[_, _]] + val jSinkClass = classOf[akka.stream.javadsl.Sink[_, _]] - val sKeyClass = classOf[akka.stream.scaladsl.Key[_]] - val jKeyClass = classOf[akka.stream.javadsl.Key[_]] - - val sMaterializedMapClass = classOf[akka.stream.scaladsl.MaterializedMap] - val jMaterializedMapClass = classOf[akka.stream.javadsl.MaterializedMap] - - val jFlowGraphClass = classOf[akka.stream.javadsl.FlowGraph] - val sFlowGraphClass = classOf[akka.stream.scaladsl.FlowGraph] - - val jPartialFlowGraphClass = classOf[akka.stream.javadsl.PartialFlowGraph] - val sPartialFlowGraphClass = classOf[akka.stream.scaladsl.PartialFlowGraph] + val jRunnableFlowClass = classOf[akka.stream.javadsl.RunnableFlow[_]] + val sRunnableFlowClass = classOf[akka.stream.scaladsl.RunnableFlow[_]] val ignore = Set("equals", "hashCode", "notify", "notifyAll", "wait", "toString", "getClass") ++ @@ -46,9 +37,8 @@ class DslConsistencySpec extends WordSpec with Matchers { jSourceClass -> Set("timerTransform"), jSinkClass -> Set(), - sFlowGraphClass -> Set("builder"), - jFlowGraphClass → Set("graph", "cyclesAllowed"), - jPartialFlowGraphClass → Set("graph", "cyclesAllowed", "disconnectedAllowed")) + sRunnableFlowClass -> Set("builder"), + jRunnableFlowClass → Set("graph", "cyclesAllowed")) def materializing(m: Method): Boolean = m.getParameterTypes.contains(classOf[FlowMaterializer]) @@ -63,10 +53,7 @@ class DslConsistencySpec extends WordSpec with Matchers { ("Source" -> List(sSourceClass, jSourceClass)) :: ("Flow" -> List(sFlowClass, jFlowClass)) :: ("Sink" -> List(sSinkClass, jSinkClass)) :: - ("Key" -> List(sKeyClass, jKeyClass)) :: - ("MaterializedMap" -> List(sMaterializedMapClass, jMaterializedMapClass)) :: - ("FlowGraph" -> List(sFlowGraphClass, jFlowGraphClass)) :: - ("PartialFlowGraph" -> List(sPartialFlowGraphClass, jPartialFlowGraphClass)) :: + ("RunanbleFlow" -> List(sRunnableFlowClass, jRunnableFlowClass)) :: Nil foreach { case (element, classes) ⇒ diff --git a/akka-stream-tests/src/test/scala/akka/stream/DslFactoriesConsistencySpec.scala b/akka-stream-tests/src/test/scala/akka/stream/DslFactoriesConsistencySpec.scala index 4bd8e3c9906..08bfdacd06f 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/DslFactoriesConsistencySpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/DslFactoriesConsistencySpec.scala @@ -35,30 +35,21 @@ class DslFactoriesConsistencySpec extends WordSpec with Matchers { (classOf[scala.Function1[_, _]], classOf[akka.stream.javadsl.japi.Function[_, _]]) :: (classOf[scala.Function1[_, _]], classOf[akka.stream.javadsl.japi.Creator[_]]) :: (classOf[scala.Function2[_, _, _]], classOf[akka.stream.javadsl.japi.Function2[_, _, _]]) :: - (classOf[akka.stream.scaladsl.Source[_]], classOf[akka.stream.javadsl.Source[_]]) :: - (classOf[akka.stream.scaladsl.KeyedSource[_, _]], classOf[akka.stream.javadsl.KeyedSource[_, _]]) :: - (classOf[akka.stream.scaladsl.Sink[_]], classOf[akka.stream.javadsl.Sink[_]]) :: - (classOf[akka.stream.scaladsl.KeyedSink[_, _]], classOf[akka.stream.javadsl.KeyedSink[_, _]]) :: - (classOf[akka.stream.scaladsl.Flow[_, _]], classOf[akka.stream.javadsl.Flow[_, _]]) :: - (classOf[akka.stream.scaladsl.FlowGraph], classOf[akka.stream.javadsl.FlowGraph]) :: - (classOf[akka.stream.scaladsl.PartialFlowGraph], classOf[akka.stream.javadsl.PartialFlowGraph]) :: + (classOf[akka.stream.scaladsl.Source[_, _]], classOf[akka.stream.javadsl.Source[_, _]]) :: + (classOf[akka.stream.scaladsl.Sink[_, _]], classOf[akka.stream.javadsl.Sink[_, _]]) :: + (classOf[akka.stream.scaladsl.Flow[_, _, _]], classOf[akka.stream.javadsl.Flow[_, _, _]]) :: + (classOf[akka.stream.scaladsl.RunnableFlow[_]], classOf[akka.stream.javadsl.RunnableFlow[_]]) :: Nil // format: ON - val sKeyedSource = classOf[scaladsl.KeyedSource[_, _]] - val jKeyedSource = classOf[javadsl.KeyedSource[_, _]] + val sSource = classOf[scaladsl.Source[_, _]] + val jSource = classOf[javadsl.Source[_, _]] - val sKeyedSink = classOf[scaladsl.KeyedSink[_, _]] - val jKeyedSink = classOf[javadsl.KeyedSink[_, _]] + val sSink = classOf[scaladsl.Sink[_, _]] + val jSink = classOf[javadsl.Sink[_, _]] - val sSource = classOf[scaladsl.Source[_]] - val jSource = classOf[javadsl.Source[_]] - - val sSink = classOf[scaladsl.Sink[_]] - val jSink = classOf[javadsl.Sink[_]] - - val sFlow = classOf[scaladsl.Flow[_, _]] - val jFlow = classOf[javadsl.Flow[_, _]] + val sFlow = classOf[scaladsl.Flow[_, _, _]] + val jFlow = classOf[javadsl.Flow[_, _, _]] "Java DSL" must provide { "Source" which { @@ -96,7 +87,7 @@ class DslFactoriesConsistencySpec extends WordSpec with Matchers { if (m.getDeclaringClass == akka.stream.scaladsl.Source.getClass && m.getName == "apply" && m.getParameterTypes.length == 1 - && m.getParameterTypes()(0) == classOf[scala.Function1[akka.stream.scaladsl.FlowGraphBuilder, akka.stream.scaladsl.UndefinedSink[_]]]) + && m.getParameterTypes()(0) == classOf[scala.Function1[_, _]]) false // conflict between two Source.apply(Function1) else true @@ -182,10 +173,8 @@ class DslFactoriesConsistencySpec extends WordSpec with Matchers { * If scaladsl is not a keyed type, javadsl shouldn't be as well. */ def returnTypeMatch(s: Class[_], j: Class[_]): Boolean = - (sKeyedSink.isAssignableFrom(s) && jKeyedSink.isAssignableFrom(j)) || - (sKeyedSource.isAssignableFrom(s) && jKeyedSource.isAssignableFrom(j)) || - (sSource.isAssignableFrom(s) && jSource.isAssignableFrom(j) && !jKeyedSource.isAssignableFrom(j)) || - (sSink.isAssignableFrom(s) && jSink.isAssignableFrom(j) && !jKeyedSink.isAssignableFrom(j)) || + (sSource.isAssignableFrom(s) && jSource.isAssignableFrom(j)) || + (sSink.isAssignableFrom(s) && jSink.isAssignableFrom(j)) || (sFlow.isAssignableFrom(s) && jFlow.isAssignableFrom(j)) def typeMatch(scalaParams: Array[Class[_]], javaParams: Array[Class[_]]): Boolean = diff --git a/akka-stream-tests/src/test/scala/akka/stream/actor/ActorPublisherSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/actor/ActorPublisherSpec.scala index 6fd62a6f3ef..ab2968eb1d9 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/actor/ActorPublisherSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/actor/ActorPublisherSpec.scala @@ -290,8 +290,8 @@ class ActorPublisherSpec extends AkkaSpec with ImplicitSender { source2 ⇒ import Graph.Implicits._ - val merge = Merge[Int](2) - val bcast = Broadcast[String](2) + val merge = b.add(Merge[Int](2)) + val bcast = b.add(Broadcast[String](2)) source1 ~> merge.in(0) source2.outlet ~> merge.in(1) diff --git a/akka-stream-tests/src/test/scala/akka/stream/impl/StreamLayoutSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/impl/StreamLayoutSpec.scala index aabbb83cb31..b4c3582b9c4 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/impl/StreamLayoutSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/impl/StreamLayoutSpec.scala @@ -6,13 +6,13 @@ package akka.stream.impl import akka.stream.scaladsl._ import akka.stream.testkit.AkkaSpec import org.reactivestreams.{ Subscription, Subscriber, Publisher } +import akka.stream._ class StreamLayoutSpec extends AkkaSpec { import StreamLayout._ def testAtomic(inPortCount: Int, outPortCount: Int): Module = new Module { - override val inPorts: Set[InPort] = List.fill(inPortCount)(new InPort).toSet - override val outPorts: Set[OutPort] = List.fill(outPortCount)(new OutPort).toSet + override val shape = AmorphousShape(List.fill(inPortCount)(new Inlet("")), List.fill(outPortCount)(new Outlet(""))) override def subModules: Set[Module] = Set.empty override def downstreams: Map[OutPort, InPort] = Map.empty diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/FlowGraphCompileSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/FlowGraphCompileSpec.scala index 405920a9ec8..ca823de55f4 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/FlowGraphCompileSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/FlowGraphCompileSpec.scala @@ -214,7 +214,7 @@ class FlowGraphCompileSpec extends AkkaSpec { val undefinedSink2 = UndefinedSink[String] - val partial2 = PartialFlowGraph(partial1) { implicit b ⇒ + val partial2 = PartialGraph.closed(partial1) { implicit b ⇒ import FlowGraphImplicits._ b.attachSource(undefinedSource1, in1) b.attachSource(undefinedSource2, in2) @@ -223,17 +223,17 @@ class FlowGraphCompileSpec extends AkkaSpec { partial2.undefinedSources should be(Set.empty) partial2.undefinedSinks should be(Set(undefinedSink1, undefinedSink2)) - FlowGraph(partial2) { b ⇒ + Graph.closed(partial2) { b ⇒ b.attachSink(undefinedSink1, out1) b.attachSink(undefinedSink2, out2) }.run() - FlowGraph(partial2) { b ⇒ + Graph.closed(partial2) { b ⇒ b.attachSink(undefinedSink1, f1.to(out1)) b.attachSink(undefinedSink2, f2.to(out2)) }.run() - FlowGraph(partial1) { implicit b ⇒ + Graph.closed(partial1) { implicit b ⇒ import FlowGraphImplicits._ b.attachSink(undefinedSink1, f1.to(out1)) b.attachSource(undefinedSource1, Source(List("a", "b", "c")).via(f1)) @@ -520,8 +520,8 @@ class FlowGraphCompileSpec extends AkkaSpec { } FlowGraph { b ⇒ - b.importPartialFlowGraph(partial1) - b.importPartialFlowGraph(partial2) + b.importPartialGraph.closed(partial1) + b.importPartialGraph.closed(partial2) b.connect(output1, f1, input1) b.connect(output2, f2, input2) }.run() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/FlowJoinSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/FlowJoinSpec.scala index 4044fdca6df..2b19fd6f6b7 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/FlowJoinSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/FlowJoinSpec.scala @@ -25,9 +25,9 @@ class FlowJoinSpec extends AkkaSpec(ConfigFactory.parseString("akka.loglevel=INF val probe = StreamTestKit.SubscriberProbe[Seq[Int]]() val flow1 = Flow() { implicit b ⇒ - import FlowGraph.Implicits._ - val merge = Merge[Int](2) - val broadcast = Broadcast[Int](2) + import Graph.Implicits._ + val merge = b.add(Merge[Int](2)) + val broadcast = b.add(Broadcast[Int](2)) source ~> merge.in(0) merge.out ~> broadcast.in broadcast.out(0).grouped(1000) ~> Sink(probe) diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphBalanceSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphBalanceSpec.scala index 0fe40d48d74..d3438dd0335 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphBalanceSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphBalanceSpec.scala @@ -16,14 +16,14 @@ class GraphBalanceSpec extends AkkaSpec { implicit val materializer = FlowMaterializer(settings) "A balance" must { - import FlowGraph.Implicits._ + import Graph.Implicits._ "balance between subscribers which signal demand" in { val c1 = StreamTestKit.SubscriberProbe[Int]() val c2 = StreamTestKit.SubscriberProbe[Int]() - FlowGraph() { implicit b ⇒ - val balance = Balance[Int](2) + Graph.closed() { implicit b ⇒ + val balance = b.add(Balance[Int](2)) Source(List(1, 2, 3)) ~> balance.in balance.out(0) ~> Sink(c1) balance.out(1) ~> Sink(c2) @@ -45,9 +45,9 @@ class GraphBalanceSpec extends AkkaSpec { "support waiting for demand from all downstream subscriptions" in { val s1 = StreamTestKit.SubscriberProbe[Int]() - val p2 = FlowGraph(Sink.publisher[Int]) { implicit b ⇒ + val p2 = Graph.closed(Sink.publisher[Int]) { implicit b ⇒ p2Sink ⇒ - val balance = Balance[Int](2, waitForAllDownstreams = true) + val balance = b.add(Balance[Int](2, waitForAllDownstreams = true)) Source(List(1, 2, 3)) ~> balance.in balance.out(0) ~> Sink(s1) balance.out(1) ~> p2Sink.inlet @@ -75,9 +75,9 @@ class GraphBalanceSpec extends AkkaSpec { "support waiting for demand from all non-cancelled downstream subscriptions" in { val s1 = StreamTestKit.SubscriberProbe[Int]() - val (p2, p3) = FlowGraph(Sink.publisher[Int], Sink.publisher[Int])(Pair.apply) { implicit b ⇒ + val (p2, p3) = Graph.closed(Sink.publisher[Int], Sink.publisher[Int])(Pair.apply) { implicit b ⇒ (p2Sink, p3Sink) ⇒ - val balance = Balance[Int](3, waitForAllDownstreams = true) + val balance = b.add(Balance[Int](3, waitForAllDownstreams = true)) Source(List(1, 2, 3)) ~> balance.in balance.out(0) ~> Sink(s1) balance.out(1) ~> p2Sink.inlet @@ -108,10 +108,10 @@ class GraphBalanceSpec extends AkkaSpec { "work with 5-way balance" in { - val (s1, s2, s3, s4, s5) = FlowGraph(Sink.head[Seq[Int]], Sink.head[Seq[Int]], Sink.head[Seq[Int]], Sink.head[Seq[Int]], Sink.head[Seq[Int]])(Tuple5.apply) { + val (s1, s2, s3, s4, s5) = Graph.closed(Sink.head[Seq[Int]], Sink.head[Seq[Int]], Sink.head[Seq[Int]], Sink.head[Seq[Int]], Sink.head[Seq[Int]])(Tuple5.apply) { implicit b ⇒ (f1, f2, f3, f4, f5) ⇒ - val balance = Balance[Int](5, waitForAllDownstreams = true) + val balance = b.add(Balance[Int](5, waitForAllDownstreams = true)) Source(0 to 14) ~> balance.in balance.out(0).grouped(15) ~> f1.inlet balance.out(1).grouped(15) ~> f2.inlet @@ -127,9 +127,9 @@ class GraphBalanceSpec extends AkkaSpec { val numElementsForSink = 10000 val outputs = Sink.fold[Int, Int](0)(_ + _) - val (r1, r2, r3) = FlowGraph(outputs, outputs, outputs)(Tuple3.apply) { implicit b ⇒ + val (r1, r2, r3) = Graph.closed(outputs, outputs, outputs)(Tuple3.apply) { implicit b ⇒ (o1, o2, o3) ⇒ - val balance = Balance[Int](3, waitForAllDownstreams = true) + val balance = b.add(Balance[Int](3, waitForAllDownstreams = true)) Source(Stream.fill(numElementsForSink * 3)(1)) ~> balance.in balance.out(0) ~> o1.inlet balance.out(1) ~> o2.inlet @@ -145,8 +145,8 @@ class GraphBalanceSpec extends AkkaSpec { val c1 = StreamTestKit.SubscriberProbe[Int]() val c2 = StreamTestKit.SubscriberProbe[Int]() - FlowGraph() { implicit b ⇒ - val balance = Balance[Int](2) + Graph.closed() { implicit b ⇒ + val balance = b.add(Balance[Int](2)) Source(List(1, 2, 3)) ~> balance.in balance.out(0) ~> Sink(c1) balance.out(1) ~> Sink(c2) @@ -166,8 +166,8 @@ class GraphBalanceSpec extends AkkaSpec { val c1 = StreamTestKit.SubscriberProbe[Int]() val c2 = StreamTestKit.SubscriberProbe[Int]() - FlowGraph() { implicit b ⇒ - val balance = Balance[Int](2) + Graph.closed() { implicit b ⇒ + val balance = b.add(Balance[Int](2)) Source(List(1, 2, 3)) ~> balance.in balance.out(0) ~> Sink(c1) balance.out(1) ~> Sink(c2) @@ -188,8 +188,8 @@ class GraphBalanceSpec extends AkkaSpec { val c1 = StreamTestKit.SubscriberProbe[Int]() val c2 = StreamTestKit.SubscriberProbe[Int]() - FlowGraph() { implicit b ⇒ - val balance = Balance[Int](2) + Graph.closed() { implicit b ⇒ + val balance = b.add(Balance[Int](2)) Source(p1.getPublisher) ~> balance.in balance.out(0) ~> Sink(c1) balance.out(1) ~> Sink(c2) diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphBroadcastSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphBroadcastSpec.scala index 76bf050481c..df9ee508043 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphBroadcastSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphBroadcastSpec.scala @@ -15,14 +15,14 @@ class GraphBroadcastSpec extends AkkaSpec { implicit val materializer = FlowMaterializer(settings) "A broadcast" must { - import FlowGraph.Implicits._ + import Graph.Implicits._ "broadcast to other subscriber" in { val c1 = StreamTestKit.SubscriberProbe[Int]() val c2 = StreamTestKit.SubscriberProbe[Int]() - FlowGraph() { implicit b ⇒ - val bcast = Broadcast[Int](2) + Graph.closed() { implicit b ⇒ + val bcast = b.add(Broadcast[Int](2)) Source(List(1, 2, 3)) ~> bcast.in bcast.out(0) ~> Flow[Int].buffer(16, OverflowStrategy.backpressure) ~> Sink(c1) bcast.out(1) ~> Flow[Int].buffer(16, OverflowStrategy.backpressure) ~> Sink(c2) @@ -50,7 +50,7 @@ class GraphBroadcastSpec extends AkkaSpec { val headSink = Sink.head[Seq[Int]] import system.dispatcher - val result = FlowGraph( + val result = Graph.closed( headSink, headSink, headSink, @@ -58,7 +58,7 @@ class GraphBroadcastSpec extends AkkaSpec { headSink)( (fut1, fut2, fut3, fut4, fut5) ⇒ Future.sequence(List(fut1, fut2, fut3, fut4, fut5))) { implicit b ⇒ (p1, p2, p3, p4, p5) ⇒ - val bcast = Broadcast[Int](5) + val bcast = b.add(Broadcast[Int](5)) Source(List(1, 2, 3)) ~> bcast.in bcast.out(0).grouped(5) ~> p1.inlet bcast.out(1).grouped(5) ~> p2.inlet @@ -80,7 +80,7 @@ class GraphBroadcastSpec extends AkkaSpec { (f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19, f20, f21, f22) ⇒ Future.sequence(List(f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19, f20, f21, f22)) - val result = FlowGraph( + val result = Graph.closed( headSink, headSink, headSink, headSink, headSink, headSink, headSink, headSink, headSink, headSink, headSink, headSink, headSink, headSink, headSink, @@ -88,7 +88,7 @@ class GraphBroadcastSpec extends AkkaSpec { headSink, headSink)(combine) { implicit b ⇒ (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, p21, p22) ⇒ - val bcast = Broadcast[Int](22) + val bcast = b.add(Broadcast[Int](22)) Source(List(1, 2, 3)) ~> bcast.in bcast.out(0).grouped(5) ~> p1.inlet bcast.out(1).grouped(5) ~> p2.inlet @@ -121,8 +121,8 @@ class GraphBroadcastSpec extends AkkaSpec { val c1 = StreamTestKit.SubscriberProbe[Int]() val c2 = StreamTestKit.SubscriberProbe[Int]() - FlowGraph() { implicit b ⇒ - val bcast = Broadcast[Int](2) + Graph.closed() { implicit b ⇒ + val bcast = b.add(Broadcast[Int](2)) Source(List(1, 2, 3)) ~> bcast.in bcast.out(0) ~> Flow[Int] ~> Sink(c1) bcast.out(1) ~> Flow[Int] ~> Sink(c2) @@ -142,8 +142,8 @@ class GraphBroadcastSpec extends AkkaSpec { val c1 = StreamTestKit.SubscriberProbe[Int]() val c2 = StreamTestKit.SubscriberProbe[Int]() - FlowGraph() { implicit b ⇒ - val bcast = Broadcast[Int](2) + Graph.closed() { implicit b ⇒ + val bcast = b.add(Broadcast[Int](2)) Source(List(1, 2, 3)) ~> bcast.in bcast.out(0) ~> Flow[Int] ~> Sink(c1) bcast.out(1) ~> Flow[Int] ~> Sink(c2) @@ -164,8 +164,8 @@ class GraphBroadcastSpec extends AkkaSpec { val c1 = StreamTestKit.SubscriberProbe[Int]() val c2 = StreamTestKit.SubscriberProbe[Int]() - FlowGraph() { implicit b ⇒ - val bcast = Broadcast[Int](2) + Graph.closed() { implicit b ⇒ + val bcast = b.add(Broadcast[Int](2)) Source(p1.getPublisher) ~> bcast.in bcast.out(0) ~> Flow[Int] ~> Sink(c1) bcast.out(1) ~> Flow[Int] ~> Sink(c2) diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphConcatSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphConcatSpec.scala index ea62bb91b7f..f9d7d9c7aad 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphConcatSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphConcatSpec.scala @@ -3,11 +3,9 @@ */ package akka.stream.scaladsl -import akka.stream.scaladsl.FlowGraph.FlowGraphBuilder -import akka.stream.scaladsl.Graphs.{ OutPort, InPort } - import scala.concurrent.Promise +import akka.stream._ import akka.stream.scaladsl._ import akka.stream.testkit.StreamTestKit import akka.stream.testkit.TwoStreamsSetup @@ -16,31 +14,31 @@ class GraphConcatSpec extends TwoStreamsSetup { override type Outputs = Int - override def fixture(b: FlowGraphBuilder): Fixture = new Fixture(b: FlowGraphBuilder) { - val concat = Concat[Outputs]()(b) + override def fixture(b: Graph.Builder): Fixture = new Fixture(b: Graph.Builder) { + val concat = b add Concat[Outputs]() - override def left: InPort[Outputs] = concat.first - override def right: InPort[Outputs] = concat.second - override def out: OutPort[Outputs] = concat.out + override def left: Inlet[Outputs] = concat.in(0) + override def right: Inlet[Outputs] = concat.in(1) + override def out: Outlet[Outputs] = concat.out } "Concat" must { - import FlowGraph.Implicits._ + import Graph.Implicits._ "work in the happy case" in { val probe = StreamTestKit.SubscriberProbe[Int]() - FlowGraph() { implicit b ⇒ + Graph.closed() { implicit b ⇒ - val concat1 = Concat[Int]() - val concat2 = Concat[Int]() + val concat1 = b add Concat[Int]() + val concat2 = b add Concat[Int]() - Source(List.empty[Int]) ~> concat1.first - Source(1 to 4) ~> concat1.second + Source(List.empty[Int]) ~> concat1.in(0) + Source(1 to 4) ~> concat1.in(1) - concat1.out ~> concat2.first - Source(5 to 10) ~> concat2.second + concat1.out ~> concat2.in(0) + Source(5 to 10) ~> concat2.in(1) concat2.out ~> Sink(probe) }.run() @@ -133,10 +131,10 @@ class GraphConcatSpec extends TwoStreamsSetup { val promise = Promise[Int]() val subscriber = StreamTestKit.SubscriberProbe[Int]() - FlowGraph() { implicit b ⇒ - val concat = Concat[Int]() - Source(List(1, 2, 3)) ~> concat.first - Source(promise.future) ~> concat.second + Graph.closed() { implicit b ⇒ + val concat = b add Concat[Int]() + Source(List(1, 2, 3)) ~> concat.in(0) + Source(promise.future) ~> concat.in(1) concat.out ~> Sink(subscriber) }.run() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphFlexiMergeSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphFlexiMergeSpec.scala index b8a137d4887..f2fef4ff890 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphFlexiMergeSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphFlexiMergeSpec.scala @@ -5,19 +5,17 @@ package akka.stream.scaladsl import akka.stream.FlowMaterializer import akka.stream.scaladsl.FlexiMerge._ -import akka.stream.scaladsl.FlowGraph.FlowGraphBuilder -import akka.stream.scaladsl.FlowGraph.Implicits._ -import akka.stream.scaladsl.Graphs.{ InPort, Ports, OutPort } import akka.stream.testkit.AkkaSpec import akka.stream.testkit.StreamTestKit.{ PublisherProbe, AutoPublisher, OnNext, SubscriberProbe } import org.reactivestreams.Publisher +import akka.stream._ import scala.util.control.NoStackTrace import scala.collection.immutable object GraphFlexiMergeSpec { - class Fair[T] extends FlexiMerge[T, UniformFanIn[T, T]](new UniformFanIn(2), OperationAttributes.name("FairMerge")) { + class Fair[T] extends FlexiMerge[T, UniformFanInShape[T, T]](new UniformFanInShape(2), OperationAttributes.name("FairMerge")) { def createMergeLogic(p: PortT): MergeLogic[T] = new MergeLogic[T] { override def initialState = State[T](ReadAny(p.in(0), p.in(1))) { (ctx, input, element) ⇒ ctx.emit(element) @@ -26,7 +24,7 @@ object GraphFlexiMergeSpec { } } - class StrictRoundRobin[T] extends FlexiMerge[T, UniformFanIn[T, T]](new UniformFanIn(2), OperationAttributes.name("RoundRobinMerge")) { + class StrictRoundRobin[T] extends FlexiMerge[T, UniformFanInShape[T, T]](new UniformFanInShape(2), OperationAttributes.name("RoundRobinMerge")) { def createMergeLogic(p: PortT): MergeLogic[T] = new MergeLogic[T] { val emitOtherOnClose = CompletionHandling( onComplete = { (ctx, input) ⇒ @@ -38,7 +36,7 @@ object GraphFlexiMergeSpec { SameState }) - def other(input: InP): InPort[T] = if (input eq p.in(0)) p.in(1) else p.in(0) + def other(input: InPort): Inlet[T] = if (input eq p.in(0)) p.in(1) else p.in(0) val read1: State[T] = State(Read(p.in(0))) { (ctx, input, element) ⇒ ctx.emit(element) @@ -50,7 +48,7 @@ object GraphFlexiMergeSpec { read1 } - def readRemaining(input: InPort[T]) = State(Read(input)) { (ctx, input, element) ⇒ + def readRemaining(input: Inlet[T]) = State(Read(input)) { (ctx, input, element) ⇒ ctx.emit(element) SameState } @@ -61,7 +59,7 @@ object GraphFlexiMergeSpec { } } - class MyZip[A, B] extends FlexiMerge[(A, B), FanIn2[A, B, (A, B)]](new FanIn2, OperationAttributes.name("MyZip")) { + class MyZip[A, B] extends FlexiMerge[(A, B), FanInShape2[A, B, (A, B)]](new FanInShape2, OperationAttributes.name("MyZip")) { def createMergeLogic(p: PortT): MergeLogic[(A, B)] = new MergeLogic[(A, B)] { var lastInA: A = _ @@ -82,7 +80,7 @@ object GraphFlexiMergeSpec { } class TripleCancellingZip[A, B, C](var cancelAfter: Int = Int.MaxValue, defVal: Option[A] = None) - extends FlexiMerge[(A, B, C), FanIn3[A, B, C, (A, B, C)]](new FanIn3, OperationAttributes.name("TripleCancellingZip")) { + extends FlexiMerge[(A, B, C), FanInShape3[A, B, C, (A, B, C)]](new FanInShape3, OperationAttributes.name("TripleCancellingZip")) { def createMergeLogic(p: PortT) = new MergeLogic[(A, B, C)] { override def initialState = State(ReadAll(p.in0, p.in1, p.in2)) { case (ctx, input, inputs) ⇒ @@ -102,7 +100,7 @@ object GraphFlexiMergeSpec { } } - object OrderedMerge extends FlexiMerge[Int, UniformFanIn[Int, Int]](new UniformFanIn(2), OperationAttributes.name("OrderedMerge")) { + object OrderedMerge extends FlexiMerge[Int, UniformFanInShape[Int, Int]](new UniformFanInShape(2), OperationAttributes.name("OrderedMerge")) { def createMergeLogic(p: PortT) = new MergeLogic[Int] { private var reference = 0 @@ -116,7 +114,7 @@ object GraphFlexiMergeSpec { SameState }) - def other(input: InP): InPort[Int] = if (input eq p.in(0)) p.in(1) else p.in(0) + def other(input: InPort): Inlet[Int] = if (input eq p.in(0)) p.in(1) else p.in(0) def getFirstElement = State[Int](ReadAny(p.in(0), p.in(1))) { (ctx, input, element) ⇒ reference = element @@ -124,7 +122,7 @@ object GraphFlexiMergeSpec { readUntilLarger(other(input)) } - def readUntilLarger(input: InPort[Int]): State[Int] = State(Read(input)) { + def readUntilLarger(input: Inlet[Int]): State[Int] = State(Read(input)) { (ctx, input, element) ⇒ if (element <= reference) { ctx.emit(element) @@ -136,7 +134,7 @@ object GraphFlexiMergeSpec { } } - def readRemaining(input: InPort[Int]) = State(Read(input)) { + def readRemaining(input: Inlet[Int]) = State(Read(input)) { (ctx, input, element) ⇒ if (element <= reference) ctx.emit(element) @@ -162,7 +160,7 @@ object GraphFlexiMergeSpec { } } - object PreferringMerge extends FlexiMerge[Int, UniformFanIn[Int, Int]](new UniformFanIn(3), OperationAttributes.name("PreferringMerge")) { + object PreferringMerge extends FlexiMerge[Int, UniformFanInShape[Int, Int]](new UniformFanInShape(3), OperationAttributes.name("PreferringMerge")) { def createMergeLogic(p: PortT) = new MergeLogic[Int] { override def initialState = State(Read(p.in(0))) { (ctx, input, element) ⇒ @@ -177,7 +175,7 @@ object GraphFlexiMergeSpec { } } - object TestMerge extends FlexiMerge[String, UniformFanIn[String, String]](new UniformFanIn(3), OperationAttributes.name("TestMerge")) { + object TestMerge extends FlexiMerge[String, UniformFanInShape[String, String]](new UniformFanInShape(3), OperationAttributes.name("TestMerge")) { def createMergeLogic(p: PortT) = new MergeLogic[String] { var throwFromOnComplete = false @@ -221,6 +219,7 @@ object GraphFlexiMergeSpec { class GraphFlexiMergeSpec extends AkkaSpec { import GraphFlexiMergeSpec._ + import Graph.Implicits._ implicit val materializer = FlowMaterializer() @@ -234,7 +233,7 @@ class GraphFlexiMergeSpec extends AkkaSpec { "FlexiMerge" must { "build simple fair merge" in { - val p = FlowGraph(out) { implicit b ⇒ + val p = Graph.closed(out) { implicit b ⇒ o ⇒ val merge = b.add(fairString) @@ -253,7 +252,7 @@ class GraphFlexiMergeSpec extends AkkaSpec { } "be able to have two fleximerges in a graph" in { - val p = FlowGraph(in1, in2, out)((i1, i2, o) ⇒ o) { implicit b ⇒ + val p = Graph.closed(in1, in2, out)((i1, i2, o) ⇒ o) { implicit b ⇒ (in1, in2, o) ⇒ val m1 = b.add(fairString) val m2 = b.add(fairString) @@ -286,11 +285,11 @@ class GraphFlexiMergeSpec extends AkkaSpec { merge.in(1) → merge.out } - val g = FlowGraph(out) { implicit b ⇒ + val g = Graph.closed(out) { implicit b ⇒ o ⇒ - val zip = Zip[String, String]() - in1 ~> flow ~> Flow[String].map { of ⇒ of } ~> zip.left - in2 ~> flow ~> Flow[String].map { tf ⇒ tf } ~> zip.right + val zip = b add Zip[String, String]() + in1 ~> flow ~> Flow[String].map { of ⇒ of } ~> zip.in0 + in2 ~> flow ~> Flow[String].map { tf ⇒ tf } ~> zip.in1 zip.out.map { x ⇒ x.toString } ~> o.inlet } @@ -318,12 +317,12 @@ class GraphFlexiMergeSpec extends AkkaSpec { (merge.in1, merge.out) } - val g = FlowGraph(out) { implicit b ⇒ + val g = Graph.closed(out) { implicit b ⇒ o ⇒ - val zip = Zip[String, String]() + val zip = b add Zip[String, String]() - in1 ~> flow.map(_.toString()) ~> zip.left - in2 ~> zip.right + in1 ~> flow.map(_.toString()) ~> zip.in0 + in2 ~> zip.in1 zip.out.map(_.toString()) ~> o.inlet } @@ -338,7 +337,7 @@ class GraphFlexiMergeSpec extends AkkaSpec { } "build simple round robin merge" in { - val p = FlowGraph(out) { implicit b ⇒ + val p = Graph.closed(out) { implicit b ⇒ o ⇒ val merge = b.add(new StrictRoundRobin[String]) in1 ~> merge.in(0) @@ -360,7 +359,7 @@ class GraphFlexiMergeSpec extends AkkaSpec { } "build simple zip merge" in { - val p = FlowGraph(Sink.publisher[(Int, String)]) { implicit b ⇒ + val p = Graph.closed(Sink.publisher[(Int, String)]) { implicit b ⇒ o ⇒ val merge = b.add(new MyZip[Int, String]) Source(List(1, 2, 3, 4)) ~> merge.in0 @@ -379,7 +378,7 @@ class GraphFlexiMergeSpec extends AkkaSpec { } "build simple triple-zip merge using ReadAll" in { - val p = FlowGraph(Sink.publisher[(Long, Int, String)]) { implicit b ⇒ + val p = Graph.closed(Sink.publisher[(Long, Int, String)]) { implicit b ⇒ o ⇒ val merge = b.add(new TripleCancellingZip[Long, Int, String]) // format: OFF @@ -401,7 +400,7 @@ class GraphFlexiMergeSpec extends AkkaSpec { } "build simple triple-zip merge using ReadAll, and continue with provided value for cancelled input" in { - val p = FlowGraph(Sink.publisher[(Long, Int, String)]) { implicit b ⇒ + val p = Graph.closed(Sink.publisher[(Long, Int, String)]) { implicit b ⇒ o ⇒ val merge = b.add(new TripleCancellingZip[Long, Int, String](1, Some(0L))) // format: OFF @@ -425,7 +424,7 @@ class GraphFlexiMergeSpec extends AkkaSpec { } "build simple ordered merge 1" in { - val p = FlowGraph(Sink.publisher[Int]) { implicit b ⇒ + val p = Graph.closed(Sink.publisher[Int]) { implicit b ⇒ o ⇒ val merge = b.add(OrderedMerge) Source(List(3, 5, 6, 7, 8)) ~> merge.in(0) @@ -446,7 +445,7 @@ class GraphFlexiMergeSpec extends AkkaSpec { "build simple ordered merge 2" in { val output = Sink.publisher[Int] - val p = FlowGraph(output) { implicit b ⇒ + val p = Graph.closed(output) { implicit b ⇒ o ⇒ val merge = b.add(OrderedMerge) Source(List(3, 5, 6, 7, 8)) ~> merge.in(0) @@ -474,7 +473,7 @@ class GraphFlexiMergeSpec extends AkkaSpec { "build perferring merge" in { val output = Sink.publisher[Int] - val p = FlowGraph(output) { implicit b ⇒ + val p = Graph.closed(output) { implicit b ⇒ o ⇒ val merge = b.add(PreferringMerge) Source(List(1, 2, 3)) ~> merge.in(0) @@ -516,7 +515,7 @@ class GraphFlexiMergeSpec extends AkkaSpec { val otherDriver1 = PublisherProbe[Int]() val otherDriver2 = PublisherProbe[Int]() - val p = FlowGraph(output) { implicit b ⇒ + val p = Graph.closed(output) { implicit b ⇒ o ⇒ val merge = b.add(PreferringMerge) Source(preferredDriver) ~> merge.in(0) @@ -569,7 +568,7 @@ class GraphFlexiMergeSpec extends AkkaSpec { "support cancel of input" in { val publisher = PublisherProbe[String] - val p = FlowGraph(out) { implicit b ⇒ + val p = Graph.closed(out) { implicit b ⇒ o ⇒ val merge = b.add(TestMerge) Source(publisher) ~> merge.in(0) @@ -610,7 +609,7 @@ class GraphFlexiMergeSpec extends AkkaSpec { val publisher1 = PublisherProbe[String] val publisher2 = PublisherProbe[String] val publisher3 = PublisherProbe[String] - val p = FlowGraph(out) { implicit b ⇒ + val p = Graph.closed(out) { implicit b ⇒ o ⇒ val merge = b.add(TestMerge) Source(publisher1) ~> merge.in(0) @@ -643,7 +642,7 @@ class GraphFlexiMergeSpec extends AkkaSpec { } "handle error" in { - val p = FlowGraph(out) { implicit b ⇒ + val p = Graph.closed(out) { implicit b ⇒ o ⇒ val merge = b.add(TestMerge) Source.failed[String](new IllegalArgumentException("ERROR") with NoStackTrace) ~> merge.in(0) @@ -674,7 +673,7 @@ class GraphFlexiMergeSpec extends AkkaSpec { "propagate error" in { val publisher = PublisherProbe[String] - val p = FlowGraph(out) { implicit b ⇒ + val p = Graph.closed(out) { implicit b ⇒ o ⇒ val merge = b.add(TestMerge) Source(publisher) ~> merge.in(0) @@ -690,7 +689,7 @@ class GraphFlexiMergeSpec extends AkkaSpec { "emit error" in { val publisher = PublisherProbe[String] - val p = FlowGraph(out) { implicit b ⇒ + val p = Graph.closed(out) { implicit b ⇒ o ⇒ val merge = b.add(TestMerge) Source(List("err")) ~> merge.in(0) @@ -709,7 +708,7 @@ class GraphFlexiMergeSpec extends AkkaSpec { "emit error for user thrown exception" in { val publisher = PublisherProbe[String] - val p = FlowGraph(out) { implicit b ⇒ + val p = Graph.closed(out) { implicit b ⇒ o ⇒ val merge = b.add(TestMerge) Source(List("exc")) ~> merge.in(0) @@ -727,7 +726,7 @@ class GraphFlexiMergeSpec extends AkkaSpec { "emit error for user thrown exception in onComplete" in { val publisher = PublisherProbe[String] - val p = FlowGraph(out) { implicit b ⇒ + val p = Graph.closed(out) { implicit b ⇒ o ⇒ val merge = b.add(TestMerge) Source(List("onComplete-exc")) ~> merge.in(0) @@ -745,7 +744,7 @@ class GraphFlexiMergeSpec extends AkkaSpec { "emit error for user thrown exception in onComplete 2" in { val publisher = PublisherProbe[String] - val p = FlowGraph(out) { implicit b ⇒ + val p = Graph.closed(out) { implicit b ⇒ o ⇒ val merge = b.add(TestMerge) Source.empty[String] ~> merge.in(0) @@ -770,7 +769,7 @@ class GraphFlexiMergeSpec extends AkkaSpec { "support complete from onInput" in { val publisher = PublisherProbe[String] - val p = FlowGraph(out) { implicit b ⇒ + val p = Graph.closed(out) { implicit b ⇒ o ⇒ val merge = b.add(TestMerge) Source(List("a", "complete")) ~> merge.in(0) diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphFlexiRouteSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphFlexiRouteSpec.scala index 224aa80a9f4..f102f928041 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphFlexiRouteSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphFlexiRouteSpec.scala @@ -2,7 +2,7 @@ package akka.stream.scaladsl import scala.concurrent.duration._ import scala.util.control.NoStackTrace -import FlowGraph.Implicits._ +import Graph.Implicits._ import akka.stream.FlowMaterializer import akka.stream.testkit.AkkaSpec import akka.stream.testkit.StreamTestKit.AutoPublisher @@ -133,7 +133,7 @@ object GraphFlexiRouteSpec { val publisher = PublisherProbe[String] val s1 = SubscriberProbe[String] val s2 = SubscriberProbe[String] - FlowGraph() { implicit b ⇒ + Graph.closed() { implicit b ⇒ val route = b.add(new TestRoute) Source(publisher) ~> route.in route.out0 ~> Sink(s1) @@ -167,8 +167,8 @@ class GraphFlexiRouteSpec extends AkkaSpec { // we can't know exactly which elements that go to each output, because if subscription/request // from one of the downstream is delayed the elements will be pushed to the other output val s = SubscriberProbe[String] - val m = FlowGraph() { implicit b ⇒ - val merge = Merge[String](2) + val m = Graph.closed() { implicit b ⇒ + val merge = b.add(Merge[String](2)) val route = b.add(new Fair[String]) in ~> route.in route.out(0) ~> merge.in(0) @@ -186,7 +186,7 @@ class GraphFlexiRouteSpec extends AkkaSpec { } "build simple round-robin route" in { - val (p1, p2) = FlowGraph(out1, out2)(Pair.apply) { implicit b ⇒ + val (p1, p2) = Graph.closed(out1, out2)(Pair.apply) { implicit b ⇒ (o1, o2) ⇒ val route = b.add(new StrictRoundRobin[String]) in ~> route.in @@ -218,7 +218,7 @@ class GraphFlexiRouteSpec extends AkkaSpec { val outA = Sink.publisher[Int] val outB = Sink.publisher[String] - val (p1, p2) = FlowGraph(outA, outB)(Pair.apply) { implicit b ⇒ + val (p1, p2) = Graph.closed(outA, outB)(Pair.apply) { implicit b ⇒ (oa, ob) ⇒ val route = b.add(new Unzip[Int, String]) Source(List(1 -> "A", 2 -> "B", 3 -> "C", 4 -> "D")) ~> route.in diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphFlowSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphFlowSpec.scala index 91947e03f42..d65ae0bded9 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphFlowSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphFlowSpec.scala @@ -5,24 +5,24 @@ package akka.stream.scaladsl import akka.stream.FlowMaterializer import akka.stream.MaterializerSettings -import akka.stream.scaladsl.Graphs.FlowPorts import akka.stream.testkit.AkkaSpec import akka.stream.testkit.StreamTestKit.SubscriberProbe import akka.stream.testkit.StreamTestKit import org.reactivestreams.Subscriber +import akka.stream._ object GraphFlowSpec { val source1 = Source(0 to 3) - val partialGraph = FlowGraph.partial { implicit b ⇒ - import FlowGraph.Implicits._ + val partialGraph = Graph.partial() { implicit b ⇒ + import Graph.Implicits._ val source2 = Source(4 to 9) val source3 = Source.empty[Int] val source4 = Source.empty[String] - val inMerge = Merge[Int](2) - val outMerge = Merge[String](2) - val m2 = Merge[Int](2) + val inMerge = b.add(Merge[Int](2)) + val outMerge = b.add(Merge[String](2)) + val m2 = b.add(Merge[Int](2)) inMerge.out.map(_ * 2) ~> m2.in(0) m2.out.map(_ / 2).map(i ⇒ (i + 1).toString) ~> outMerge.in(0) @@ -30,7 +30,7 @@ object GraphFlowSpec { source2 ~> inMerge.in(0) source3 ~> m2.in(1) source4 ~> outMerge.in(1) - FlowPorts(inMerge.in(1), outMerge.out) + FlowShape(inMerge.in(1), outMerge.out) } val stdRequests = 10 @@ -66,7 +66,7 @@ class GraphFlowSpec extends AkkaSpec { val flow = Flow(partialGraph) { implicit b ⇒ partial ⇒ - import FlowGraph.Implicits._ + import Graph.Implicits._ (partial.inlet, partial.outlet.map(_.toInt).outlet) } @@ -115,8 +115,8 @@ class GraphFlowSpec extends AkkaSpec { (importFlow.inlet, importFlow.outlet) } - FlowGraph() { implicit b ⇒ - import FlowGraph.Implicits._ + Graph.closed() { implicit b ⇒ + import Graph.Implicits._ Source(1 to 5) ~> flow ~> flow ~> Sink(probe) }.run() @@ -130,7 +130,7 @@ class GraphFlowSpec extends AkkaSpec { val source = Source(partialGraph) { implicit b ⇒ partial ⇒ - import FlowGraph.Implicits._ + import Graph.Implicits._ source1 ~> partial.inlet partial.outlet.map(_.toInt).outlet } @@ -160,7 +160,7 @@ class GraphFlowSpec extends AkkaSpec { val source = Source(partialGraph) { implicit b ⇒ partial ⇒ - import FlowGraph.Implicits._ + import Graph.Implicits._ source1 ~> partial.inlet partial.outlet } @@ -175,7 +175,7 @@ class GraphFlowSpec extends AkkaSpec { val source = Source(partialGraph) { implicit b ⇒ partial ⇒ - import FlowGraph.Implicits._ + import Graph.Implicits._ source1 ~> partial.inlet partial.outlet } @@ -195,14 +195,14 @@ class GraphFlowSpec extends AkkaSpec { val source = Source(Source(1 to 5)) { implicit b ⇒ s ⇒ - import FlowGraph.Implicits._ + import Graph.Implicits._ s.outlet.map(_ * 2).outlet } - FlowGraph(source, source)(Pair.apply) { implicit b ⇒ + Graph.closed(source, source)(Pair.apply) { implicit b ⇒ (s1, s2) ⇒ - import FlowGraph.Implicits._ - val merge = Merge[Int](2) + import Graph.Implicits._ + val merge = b.add(Merge[Int](2)) s1.outlet ~> merge.in(0) merge.out ~> Sink(probe) s2.outlet.map(_ * 10) ~> merge.in(1) @@ -218,7 +218,7 @@ class GraphFlowSpec extends AkkaSpec { val sink = Sink(partialGraph) { implicit b ⇒ partial ⇒ - import FlowGraph.Implicits._ + import Graph.Implicits._ partial.outlet.map(_.toInt) ~> Sink(probe) partial.inlet } @@ -248,7 +248,7 @@ class GraphFlowSpec extends AkkaSpec { val sink = Sink(partialGraph, Flow[String].map(_.toInt))(Pair.apply) { implicit b ⇒ (partial, flow) ⇒ - import FlowGraph.Implicits._ + import Graph.Implicits._ flow.outlet ~> partial.inlet partial.outlet.map(_.toInt) ~> Sink(probe) flow.inlet @@ -271,7 +271,7 @@ class GraphFlowSpec extends AkkaSpec { val sink = Sink(Flow[String].map(_.toInt)) { implicit b ⇒ flow ⇒ - import FlowGraph.Implicits._ + import Graph.Implicits._ flow.outlet ~> Sink(probe) flow.inlet } @@ -290,27 +290,27 @@ class GraphFlowSpec extends AkkaSpec { val flow = Flow(partialGraph) { implicit b ⇒ partial ⇒ - import FlowGraph.Implicits._ + import Graph.Implicits._ (partial.inlet, partial.outlet.map(_.toInt).outlet) } val source = Source(Flow[Int].map(_.toString), inSource)(Keep.right) { implicit b ⇒ (flow, src) ⇒ - import FlowGraph.Implicits._ + import Graph.Implicits._ src.outlet ~> flow.inlet flow.outlet } val sink = Sink(Flow[String].map(_.toInt), outSink)(Keep.right) { implicit b ⇒ (flow, snk) ⇒ - import FlowGraph.Implicits._ + import Graph.Implicits._ flow.outlet ~> snk.inlet flow.inlet } - val (m1, m2, m3) = FlowGraph(source, flow, sink)(Tuple3.apply) { implicit b ⇒ + val (m1, m2, m3) = Graph.closed(source, flow, sink)(Tuple3.apply) { implicit b ⇒ (src, f, snk) ⇒ - import FlowGraph.Implicits._ + import Graph.Implicits._ src.outlet.map(_.toInt) ~> f.inlet f.outlet.map(_.toString) ~> snk.inlet }.run() @@ -338,9 +338,9 @@ class GraphFlowSpec extends AkkaSpec { snk.inlet } - val (m1, m2) = FlowGraph(source, sink)(Pair.apply) { implicit b ⇒ + val (m1, m2) = Graph.closed(source, sink)(Pair.apply) { implicit b ⇒ (src, snk) ⇒ - import FlowGraph.Implicits._ + import Graph.Implicits._ src.outlet ~> snk.inlet }.run() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphJunctionAttributesSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphJunctionAttributesSpec.scala index 1638bb8640a..e6b926d3077 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphJunctionAttributesSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphJunctionAttributesSpec.scala @@ -27,15 +27,15 @@ class GraphJunctionAttributesSpec extends AkkaSpec { case object FastTick extends FastTick val source = Source[(SlowTick, List[FastTick])]() { implicit b ⇒ - import FlowGraph.Implicits._ + import Graph.Implicits._ val slow = Source(0.seconds, 100.millis, SlowTick) val fast = Source(0.seconds, 10.millis, FastTick) - val zip = Zip[SlowTick, List[FastTick]](inputBuffer(1, 1)) + val zip = b add Zip[SlowTick, List[FastTick]](inputBuffer(1, 1)) - slow ~> zip.left - fast.conflate(tick ⇒ List(tick)) { case (list, tick) ⇒ tick :: list } ~> zip.right + slow ~> zip.in0 + fast.conflate(tick ⇒ List(tick)) { case (list, tick) ⇒ tick :: list } ~> zip.in1 zip.out } diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphMergeSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphMergeSpec.scala index 4f9f947f834..9f42a4d708d 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphMergeSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphMergeSpec.scala @@ -3,26 +3,23 @@ */ package akka.stream.scaladsl -import akka.stream.scaladsl.FlowGraph.FlowGraphBuilder -import akka.stream.scaladsl.Graphs.{ OutPort, InPort } -import akka.stream.{ FlowMaterializer, MaterializerSettings } +import akka.stream.{ FlowMaterializer, MaterializerSettings, Inlet, Outlet } import scala.concurrent.duration._ -import akka.stream.scaladsl.FlowGraph.Implicits._ import akka.stream.testkit.{ TwoStreamsSetup, AkkaSpec, StreamTestKit } -import FlowGraph.Implicits._ class GraphMergeSpec extends TwoStreamsSetup { + import Graph.Implicits._ override type Outputs = Int - override def fixture(b: FlowGraphBuilder): Fixture = new Fixture(b: FlowGraphBuilder) { - val merge = Merge[Outputs](2)(b) + override def fixture(b: Graph.Builder): Fixture = new Fixture(b: Graph.Builder) { + val merge = b add Merge[Outputs](2) - override def left: InPort[Outputs] = merge.in(0) - override def right: InPort[Outputs] = merge.in(1) - override def out: OutPort[Outputs] = merge.out + override def left: Inlet[Outputs] = merge.in(0) + override def right: Inlet[Outputs] = merge.in(1) + override def out: Outlet[Outputs] = merge.out } @@ -35,9 +32,9 @@ class GraphMergeSpec extends TwoStreamsSetup { val source3 = Source(List[Int]()) val probe = StreamTestKit.SubscriberProbe[Int]() - FlowGraph() { implicit b ⇒ - val m1 = Merge[Int](2) - val m2 = Merge[Int](2) + Graph.closed() { implicit b ⇒ + val m1 = b.add(Merge[Int](2)) + val m2 = b.add(Merge[Int](2)) source1 ~> m1.in(0) m1.out ~> Flow[Int].map(_ * 2) ~> m2.in(0) @@ -69,8 +66,8 @@ class GraphMergeSpec extends TwoStreamsSetup { val probe = StreamTestKit.SubscriberProbe[Int]() - FlowGraph() { implicit b ⇒ - val merge = Merge[Int](6) + Graph.closed() { implicit b ⇒ + val merge = b.add(Merge[Int](6)) source1 ~> merge.in(0) source2 ~> merge.in(1) diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphOpsIntegrationSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphOpsIntegrationSpec.scala index 1ca70dcebc2..ecd6172e619 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphOpsIntegrationSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphOpsIntegrationSpec.scala @@ -6,29 +6,29 @@ import scala.concurrent.duration._ import akka.stream.FlowMaterializer import akka.stream.MaterializerSettings -import akka.stream.scaladsl.FlowGraph.Implicits._ import akka.stream.testkit.AkkaSpec import akka.stream.testkit.StreamTestKit.{ OnNext, SubscriberProbe } import akka.util.ByteString +import akka.stream.{ Inlet, Outlet, Shape, Graph } object GraphOpsIntegrationSpec { - import Graphs._ + import Graph.Implicits._ object Shuffle { - case class ShufflePorts[In, Out](in1: InPort[In], in2: InPort[In], out1: OutPort[Out], out2: OutPort[Out]) extends Graphs.Ports { - override def inlets: immutable.Seq[InPort[_]] = List(in1, in2) - override def outlets: immutable.Seq[OutPort[_]] = List(out1, out2) + case class ShufflePorts[In, Out](in1: Inlet[In], in2: Inlet[In], out1: Outlet[Out], out2: Outlet[Out]) extends Shape { + override def inlets: immutable.Seq[Inlet[_]] = List(in1, in2) + override def outlets: immutable.Seq[Outlet[_]] = List(out1, out2) - override def deepCopy(): Ports = ShufflePorts( - new InPort[In](in1.toString), new InPort[In](in2.toString), - new OutPort[Out](out1.toString), new OutPort[Out](out2.toString)) + override def deepCopy() = ShufflePorts( + new Inlet[In](in1.toString), new Inlet[In](in2.toString), + new Outlet[Out](out1.toString), new Outlet[Out](out2.toString)) } def apply[In, Out](pipeline: Flow[In, Out, _]): Graph[ShufflePorts[In, Out], Unit] = { - FlowGraph.partial { implicit builder ⇒ - val merge = Merge[In](2) - val balance = Balance[Out](2) + Graph.partial() { implicit b ⇒ + val merge = b.add(Merge[In](2)) + val balance = b.add(Balance[Out](2)) merge.out ~> pipeline ~> balance.in ShufflePorts(merge.in(0), merge.in(1), balance.out(0), balance.out(1)) } @@ -40,6 +40,7 @@ object GraphOpsIntegrationSpec { class GraphOpsIntegrationSpec extends AkkaSpec { import akka.stream.scaladsl.GraphOpsIntegrationSpec._ + import Graph.Implicits._ val settings = MaterializerSettings(system) .withInputBuffer(initialSize = 2, maxSize = 16) @@ -49,10 +50,10 @@ class GraphOpsIntegrationSpec extends AkkaSpec { "FlowGraphs" must { "support broadcast - merge layouts" in { - val resultFuture = FlowGraph(Sink.head[Seq[Int]]) { implicit b ⇒ + val resultFuture = Graph.closed(Sink.head[Seq[Int]]) { implicit b ⇒ (sink) ⇒ - val bcast = Broadcast[Int](2) - val merge = Merge[Int](2) + val bcast = b.add(Broadcast[Int](2)) + val merge = b.add(Merge[Int](2)) Source(List(1, 2, 3)) ~> bcast.in bcast.out(0) ~> merge.in(0) @@ -65,10 +66,10 @@ class GraphOpsIntegrationSpec extends AkkaSpec { "support balance - merge (parallelization) layouts" in { val elements = 0 to 10 - val out = FlowGraph(Sink.head[Seq[Int]]) { implicit b ⇒ + val out = Graph.closed(Sink.head[Seq[Int]]) { implicit b ⇒ (sink) ⇒ - val balance = Balance[Int](5) - val merge = Merge[Int](5) + val balance = b.add(Balance[Int](5)) + val merge = b.add(Merge[Int](5)) Source(elements) ~> balance.in @@ -85,16 +86,16 @@ class GraphOpsIntegrationSpec extends AkkaSpec { // see https://en.wikipedia.org/wiki/Topological_sorting#mediaviewer/File:Directed_acyclic_graph.png val seqSink = Sink.head[Seq[Int]] - val (resultFuture2, resultFuture9, resultFuture10) = FlowGraph(seqSink, seqSink, seqSink)(Tuple3.apply) { implicit b ⇒ + val (resultFuture2, resultFuture9, resultFuture10) = Graph.closed(seqSink, seqSink, seqSink)(Tuple3.apply) { implicit b ⇒ (sink2, sink9, sink10) ⇒ // FIXME: Attributes for junctions - val b3 = Broadcast[Int](2) - val b7 = Broadcast[Int](2) - val b11 = Broadcast[Int](3) - val m8 = Merge[Int](2) - val m9 = Merge[Int](2) - val m10 = Merge[Int](2) - val m11 = Merge[Int](2) + val b3 = b.add(Broadcast[Int](2)) + val b7 = b.add(Broadcast[Int](2)) + val b11 = b.add(Broadcast[Int](3)) + val m8 = b.add(Merge[Int](2)) + val m9 = b.add(Merge[Int](2)) + val m10 = b.add(Merge[Int](2)) + val m11 = b.add(Merge[Int](2)) val in3 = Source(List(3)) val in5 = Source(List(5)) val in7 = Source(List(7)) @@ -132,10 +133,10 @@ class GraphOpsIntegrationSpec extends AkkaSpec { "allow adding of flows to sources and sinks to flows" in { - val resultFuture = FlowGraph(Sink.head[Seq[Int]]) { implicit b ⇒ + val resultFuture = Graph.closed(Sink.head[Seq[Int]]) { implicit b ⇒ (sink) ⇒ - val bcast = Broadcast[Int](2) - val merge = Merge[Int](2) + val bcast = b.add(Broadcast[Int](2)) + val merge = b.add(Merge[Int](2)) Source(List(1, 2, 3)).map(_ * 2) ~> bcast.in bcast.out(0) ~> merge.in(0) @@ -150,7 +151,7 @@ class GraphOpsIntegrationSpec extends AkkaSpec { val p = Source(List(1, 2, 3)).runWith(Sink.publisher) val s = SubscriberProbe[Int] val flow = Flow[Int].map(_ * 2) - FlowGraph() { implicit builder ⇒ + Graph.closed() { implicit builder ⇒ Source(p) ~> flow ~> Sink(s) }.run() val sub = s.expectSubscription() @@ -164,9 +165,9 @@ class GraphOpsIntegrationSpec extends AkkaSpec { "be possible to use as lego bricks" in { val shuffler = Shuffle(Flow[Int].map(_ + 1)) - val f: Future[Seq[Int]] = FlowGraph(shuffler, shuffler, shuffler, Sink.head[Seq[Int]])((_, _, _, fut) ⇒ fut) { implicit builder ⇒ + val f: Future[Seq[Int]] = Graph.closed(shuffler, shuffler, shuffler, Sink.head[Seq[Int]])((_, _, _, fut) ⇒ fut) { implicit b ⇒ (s1, s2, s3, sink) ⇒ - val merge = Merge[Int](2) + val merge = b.add(Merge[Int](2)) Source(List(1, 2, 3)) ~> s1.in1 Source(List(10, 11, 12)) ~> s1.in2 diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphPartialSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphPartialSpec.scala index 0f0d109e64a..3697e512c14 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphPartialSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphPartialSpec.scala @@ -1,13 +1,13 @@ package akka.stream.scaladsl -import akka.stream.{ FlowMaterializer, MaterializerSettings } -import akka.stream.scaladsl.Graphs.FlowPorts +import akka.stream.{ FlowMaterializer, MaterializerSettings, FlowShape } import akka.stream.testkit.AkkaSpec import scala.concurrent.{ Await, Future } import scala.concurrent.duration._ class GraphPartialSpec extends AkkaSpec { + import Graph.Implicits._ val settings = MaterializerSettings(system) .withInputBuffer(initialSize = 2, maxSize = 16) @@ -15,19 +15,19 @@ class GraphPartialSpec extends AkkaSpec { implicit val materializer = FlowMaterializer(settings) "FlowGraph.partial" must { - import FlowGraph.Implicits._ + import Graph.Implicits._ "be able to build and reuse simple partial graphs" in { - val doubler = FlowGraph.partial { implicit b ⇒ - val bcast = Broadcast[Int](2) - val zip = ZipWith2[Int, Int, Int]((a, b) ⇒ a + b) + val doubler = Graph.partial() { implicit b ⇒ + val bcast = b.add(Broadcast[Int](2)) + val zip = b.add(ZipWith((a: Int, b: Int) ⇒ a + b)) - bcast.out(0) ~> zip.in1 - bcast.out(1) ~> zip.in2 - FlowPorts(bcast.in, zip.out) + bcast.out(0) ~> zip.in0 + bcast.out(1) ~> zip.in1 + FlowShape(bcast.in, zip.out) } - val (_, _, result) = FlowGraph(doubler, doubler, Sink.head[Seq[Int]])(Tuple3.apply) { implicit b ⇒ + val (_, _, result) = Graph.closed(doubler, doubler, Sink.head[Seq[Int]])(Tuple3.apply) { implicit b ⇒ (d1, d2, sink) ⇒ Source(List(1, 2, 3)) ~> d1.inlet d1.outlet ~> d2.inlet @@ -38,18 +38,18 @@ class GraphPartialSpec extends AkkaSpec { } "be able to build and reuse simple materializing partial graphs" in { - val doubler = FlowGraph.partial(Sink.head[Seq[Int]]) { implicit b ⇒ + val doubler = Graph.partial(Sink.head[Seq[Int]]) { implicit b ⇒ sink ⇒ - val bcast = Broadcast[Int](3) - val zip = ZipWith2[Int, Int, Int]((a, b) ⇒ a + b) + val bcast = b.add(Broadcast[Int](3)) + val zip = b.add(ZipWith((a: Int, b: Int) ⇒ a + b)) - bcast.out(0) ~> zip.in1 - bcast.out(1) ~> zip.in2 + bcast.out(0) ~> zip.in0 + bcast.out(1) ~> zip.in1 bcast.out(2).grouped(100) ~> sink.inlet - FlowPorts(bcast.in, zip.out) + FlowShape(bcast.in, zip.out) } - val (sub1, sub2, result) = FlowGraph(doubler, doubler, Sink.head[Seq[Int]])(Tuple3.apply) { implicit b ⇒ + val (sub1, sub2, result) = Graph.closed(doubler, doubler, Sink.head[Seq[Int]])(Tuple3.apply) { implicit b ⇒ (d1, d2, sink) ⇒ Source(List(1, 2, 3)) ~> d1.inlet d1.outlet ~> d2.inlet @@ -64,23 +64,23 @@ class GraphPartialSpec extends AkkaSpec { "be able to build and reuse complex materializing partial graphs" in { val summer = Sink.fold[Int, Int](0)(_ + _) - val doubler = FlowGraph.partial(summer, summer)(Tuple2.apply) { implicit b ⇒ + val doubler = Graph.partial(summer, summer)(Tuple2.apply) { implicit b ⇒ (s1, s2) ⇒ - val bcast = Broadcast[Int](3) - val bcast2 = Broadcast[Int](2) - val zip = ZipWith2[Int, Int, Int]((a, b) ⇒ a + b) + val bcast = b.add(Broadcast[Int](3)) + val bcast2 = b.add(Broadcast[Int](2)) + val zip = b.add(ZipWith((a: Int, b: Int) ⇒ a + b)) - bcast.out(0) ~> zip.in1 - bcast.out(1) ~> zip.in2 + bcast.out(0) ~> zip.in0 + bcast.out(1) ~> zip.in1 bcast.out(2) ~> s1.inlet zip.out ~> bcast2.in bcast2.out(0) ~> s2.inlet - FlowPorts(bcast.in, bcast2.out(1)) + FlowShape(bcast.in, bcast2.out(1)) } - val (sub1, sub2, result) = FlowGraph(doubler, doubler, Sink.head[Seq[Int]])(Tuple3.apply) { implicit b ⇒ + val (sub1, sub2, result) = Graph.closed(doubler, doubler, Sink.head[Seq[Int]])(Tuple3.apply) { implicit b ⇒ (d1, d2, sink) ⇒ Source(List(1, 2, 3)) ~> d1.inlet d1.outlet ~> d2.inlet @@ -95,14 +95,14 @@ class GraphPartialSpec extends AkkaSpec { } "be able to expose the ports of imported graphs" in { - val p = FlowGraph.partial(Flow[Int].map(_ + 1)) { implicit b ⇒ + val p = Graph.partial(Flow[Int].map(_ + 1)) { implicit b ⇒ flow ⇒ - FlowPorts(flow.inlet, flow.outlet) + FlowShape(flow.inlet, flow.outlet) } - val fut = FlowGraph(Sink.head[Int], p)(Keep.left) { implicit b ⇒ + val fut = Graph.closed(Sink.head[Int], p)(Keep.left) { implicit b ⇒ (sink, flow) ⇒ - import FlowGraph.Implicits._ + import Graph.Implicits._ Source.single(0) ~> flow.inlet flow.outlet ~> sink.inlet }.run() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphPreferredMergeSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphPreferredMergeSpec.scala index 8fc78fdfcaf..9cb1f01e0d6 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphPreferredMergeSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphPreferredMergeSpec.scala @@ -3,29 +3,27 @@ */ package akka.stream.scaladsl -import akka.stream.scaladsl.FlowGraph.FlowGraphBuilder -import akka.stream.scaladsl.Graphs.{ InPort, OutPort } import akka.stream.testkit.TwoStreamsSetup +import akka.stream._ import scala.concurrent.Await import scala.concurrent.duration._ class GraphPreferredMergeSpec extends TwoStreamsSetup { + import Graph.Implicits._ override type Outputs = Int - override def fixture(b: FlowGraphBuilder): Fixture = new Fixture(b: FlowGraphBuilder) { - val merge = MergePreferred[Outputs](1)(b) + override def fixture(b: Graph.Builder): Fixture = new Fixture(b: Graph.Builder) { + val merge = b.add(MergePreferred[Outputs](1)) - override def left: InPort[Outputs] = merge.preferred - override def right: InPort[Outputs] = merge.in(0) - override def out: OutPort[Outputs] = merge.out + override def left: Inlet[Outputs] = merge.preferred + override def right: Inlet[Outputs] = merge.in(0) + override def out: Outlet[Outputs] = merge.out } "preferred merge" must { - import akka.stream.scaladsl.FlowGraph.Implicits._ - commonTests() "prefer selected input more than others" in { @@ -34,9 +32,9 @@ class GraphPreferredMergeSpec extends TwoStreamsSetup { val preferred = Source(Stream.fill(numElements)(1)) val aux = Source(Stream.fill(numElements)(2)) - val result = FlowGraph(Sink.head[Seq[Int]]) { implicit b ⇒ + val result = Graph.closed(Sink.head[Seq[Int]]) { implicit b ⇒ sink ⇒ - val merge = MergePreferred[Int](3) + val merge = b.add(MergePreferred[Int](3)) preferred ~> merge.preferred merge.out.grouped(numElements * 2) ~> sink.inlet diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphUnzipSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphUnzipSpec.scala index bf9085ddf6a..863468671bf 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphUnzipSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphUnzipSpec.scala @@ -14,17 +14,17 @@ class GraphUnzipSpec extends AkkaSpec { implicit val materializer = FlowMaterializer(settings) "A unzip" must { - import FlowGraph.Implicits._ + import Graph.Implicits._ "unzip to two subscribers" in { val c1 = StreamTestKit.SubscriberProbe[Int]() val c2 = StreamTestKit.SubscriberProbe[String]() - FlowGraph() { implicit b ⇒ - val unzip = Unzip[Int, String]() + Graph.closed() { implicit b ⇒ + val unzip = b.add(Unzip[Int, String]()) Source(List(1 -> "a", 2 -> "b", 3 -> "c")) ~> unzip.in - unzip.right ~> Flow[String].buffer(16, OverflowStrategy.backpressure) ~> Sink(c2) - unzip.left ~> Flow[Int].buffer(16, OverflowStrategy.backpressure).map(_ * 2) ~> Sink(c1) + unzip.out1 ~> Flow[String].buffer(16, OverflowStrategy.backpressure) ~> Sink(c2) + unzip.out0 ~> Flow[Int].buffer(16, OverflowStrategy.backpressure).map(_ * 2) ~> Sink(c1) }.run() val sub1 = c1.expectSubscription() @@ -49,11 +49,11 @@ class GraphUnzipSpec extends AkkaSpec { val c1 = StreamTestKit.SubscriberProbe[Int]() val c2 = StreamTestKit.SubscriberProbe[String]() - FlowGraph() { implicit b ⇒ - val unzip = Unzip[Int, String]() + Graph.closed() { implicit b ⇒ + val unzip = b.add(Unzip[Int, String]()) Source(List(1 -> "a", 2 -> "b", 3 -> "c")) ~> unzip.in - unzip.left ~> Sink(c1) - unzip.right ~> Sink(c2) + unzip.out0 ~> Sink(c1) + unzip.out1 ~> Sink(c2) }.run() val sub1 = c1.expectSubscription() @@ -70,11 +70,11 @@ class GraphUnzipSpec extends AkkaSpec { val c1 = StreamTestKit.SubscriberProbe[Int]() val c2 = StreamTestKit.SubscriberProbe[String]() - FlowGraph() { implicit b ⇒ - val unzip = Unzip[Int, String]() + Graph.closed() { implicit b ⇒ + val unzip = b.add(Unzip[Int, String]()) Source(List(1 -> "a", 2 -> "b", 3 -> "c")) ~> unzip.in - unzip.left ~> Sink(c1) - unzip.right ~> Sink(c2) + unzip.out0 ~> Sink(c1) + unzip.out1 ~> Sink(c2) }.run() val sub1 = c1.expectSubscription() @@ -92,11 +92,11 @@ class GraphUnzipSpec extends AkkaSpec { val c1 = StreamTestKit.SubscriberProbe[Int]() val c2 = StreamTestKit.SubscriberProbe[String]() - FlowGraph() { implicit b ⇒ - val unzip = Unzip[Int, String]() + Graph.closed() { implicit b ⇒ + val unzip = b.add(Unzip[Int, String]()) Source(p1.getPublisher) ~> unzip.in - unzip.left ~> Sink(c1) - unzip.right ~> Sink(c2) + unzip.out0 ~> Sink(c1) + unzip.out1 ~> Sink(c2) }.run() val p1Sub = p1.expectSubscription() @@ -118,12 +118,12 @@ class GraphUnzipSpec extends AkkaSpec { "work with zip" in { val c1 = StreamTestKit.SubscriberProbe[(Int, String)]() - FlowGraph() { implicit b ⇒ - val zip = Zip[Int, String]() - val unzip = Unzip[Int, String]() + Graph.closed() { implicit b ⇒ + val zip = b.add(Zip[Int, String]()) + val unzip = b.add(Unzip[Int, String]()) Source(List(1 -> "a", 2 -> "b", 3 -> "c")) ~> unzip.in - unzip.left ~> zip.left - unzip.right ~> zip.right + unzip.out0 ~> zip.in0 + unzip.out1 ~> zip.in1 zip.out ~> Sink(c1) }.run() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphZipSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphZipSpec.scala index 0e9c2e0fa5a..0833941a91b 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphZipSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphZipSpec.scala @@ -3,22 +3,21 @@ */ package akka.stream.scaladsl -import akka.stream.scaladsl.FlowGraph.FlowGraphBuilder -import akka.stream.scaladsl.Graphs.{ OutPort, InPort } -import akka.stream.scaladsl.FlowGraph.Implicits._ import akka.stream.testkit.StreamTestKit import akka.stream.testkit.TwoStreamsSetup +import akka.stream._ class GraphZipSpec extends TwoStreamsSetup { + import Graph.Implicits._ override type Outputs = (Int, Int) - override def fixture(b: FlowGraphBuilder): Fixture = new Fixture(b: FlowGraphBuilder) { - val zip = Zip[Int, Int]()(b) + override def fixture(b: Graph.Builder): Fixture = new Fixture(b: Graph.Builder) { + val zip = b.add(Zip[Int, Int]()) - override def left: InPort[Int] = zip.left - override def right: InPort[Int] = zip.right - override def out: OutPort[(Int, Int)] = zip.out + override def left: Inlet[Int] = zip.in0 + override def right: Inlet[Int] = zip.in1 + override def out: Outlet[(Int, Int)] = zip.out } "Zip" must { @@ -26,11 +25,11 @@ class GraphZipSpec extends TwoStreamsSetup { "work in the happy case" in { val probe = StreamTestKit.SubscriberProbe[(Int, String)]() - FlowGraph() { implicit b ⇒ - val zip = Zip[Int, String]() + Graph.closed() { implicit b ⇒ + val zip = b.add(Zip[Int, String]()) - Source(1 to 4) ~> zip.left - Source(List("A", "B", "C", "D", "E", "F")) ~> zip.right + Source(1 to 4) ~> zip.in0 + Source(List("A", "B", "C", "D", "E", "F")) ~> zip.in1 zip.out ~> Sink(probe) }.run() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphZipWithSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphZipWithSpec.scala index 6a1fc970b07..722c9068b5b 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphZipWithSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphZipWithSpec.scala @@ -1,21 +1,20 @@ package akka.stream.scaladsl -import akka.stream.scaladsl.FlowGraph.FlowGraphBuilder -import akka.stream.scaladsl.FlowGraph.Implicits._ -import akka.stream.scaladsl.Graphs.{ OutPort, InPort } import akka.stream.testkit.StreamTestKit import akka.stream.testkit.TwoStreamsSetup import scala.concurrent.duration._ +import akka.stream._ class GraphZipWithSpec extends TwoStreamsSetup { + import Graph.Implicits._ override type Outputs = Int - override def fixture(b: FlowGraphBuilder): Fixture = new Fixture(b: FlowGraphBuilder) { - val zip = ZipWith((_: Int) + (_: Int))(b) - override def left: InPort[Int] = zip.in1 - override def right: InPort[Int] = zip.in2 - override def out: OutPort[Int] = zip.out + override def fixture(b: Graph.Builder): Fixture = new Fixture(b: Graph.Builder) { + val zip = b.add(ZipWith((_: Int) + (_: Int))) + override def left: Inlet[Int] = zip.in0 + override def right: Inlet[Int] = zip.in1 + override def out: Outlet[Int] = zip.out } "ZipWith" must { @@ -23,10 +22,10 @@ class GraphZipWithSpec extends TwoStreamsSetup { "work in the happy case" in { val probe = StreamTestKit.SubscriberProbe[Outputs]() - FlowGraph() { implicit b ⇒ - val zip = ZipWith((_: Int) + (_: Int)) - Source(1 to 4) ~> zip.in1 - Source(10 to 40 by 10) ~> zip.in2 + Graph.closed() { implicit b ⇒ + val zip = b.add(ZipWith((_: Int) + (_: Int))) + Source(1 to 4) ~> zip.in0 + Source(10 to 40 by 10) ~> zip.in1 zip.out ~> Sink(probe) }.run() @@ -48,11 +47,11 @@ class GraphZipWithSpec extends TwoStreamsSetup { "work in the sad case" in { val probe = StreamTestKit.SubscriberProbe[Outputs]() - FlowGraph() { implicit b ⇒ - val zip = ZipWith[Int, Int, Int]((_: Int) / (_: Int)) + Graph.closed() { implicit b ⇒ + val zip = b.add(ZipWith[Int, Int, Int]((_: Int) / (_: Int))) - Source(1 to 4) ~> zip.in1 - Source(-2 to 2) ~> zip.in2 + Source(1 to 4) ~> zip.in0 + Source(-2 to 2) ~> zip.in1 zip.out ~> Sink(probe) }.run() @@ -109,12 +108,12 @@ class GraphZipWithSpec extends TwoStreamsSetup { case class Person(name: String, surname: String, int: Int) - FlowGraph() { implicit b ⇒ - val zip = ZipWith(Person.apply _) + Graph.closed() { implicit b ⇒ + val zip = b.add(ZipWith(Person.apply _)) - Source.single("Caplin") ~> zip.in1 - Source.single("Capybara") ~> zip.in2 - Source.single(3) ~> zip.in3 + Source.single("Caplin") ~> zip.in0 + Source.single("Capybara") ~> zip.in1 + Source.single(3) ~> zip.in2 zip.out ~> Sink(probe) }.run() @@ -130,7 +129,7 @@ class GraphZipWithSpec extends TwoStreamsSetup { "work with up to 22 inputs" in { val probe = StreamTestKit.SubscriberProbe[String]() - FlowGraph() { implicit b ⇒ + Graph.closed() { implicit b ⇒ val sum19 = (v1: Int, v2: String, v3: Int, v4: String, v5: Int, v6: String, v7: Int, v8: String, v9: Int, v10: String, v11: Int, v12: String, v13: Int, v14: String, v15: Int, v16: String, v17: Int, v18: String, v19: Int) ⇒ @@ -138,27 +137,27 @@ class GraphZipWithSpec extends TwoStreamsSetup { v11 + v12 + v13 + v14 + v15 + v16 + v17 + v18 + v19 // odd input ports will be Int, even input ports will be String - val zip = ZipWith(sum19) - - Source.single(1) ~> zip.in1 - Source.single(2).map(_.toString) ~> zip.in2 - Source.single(3) ~> zip.in3 - Source.single(4).map(_.toString) ~> zip.in4 - Source.single(5) ~> zip.in5 - Source.single(6).map(_.toString) ~> zip.in6 - Source.single(7) ~> zip.in7 - Source.single(8).map(_.toString) ~> zip.in8 - Source.single(9) ~> zip.in9 - Source.single(10).map(_.toString) ~> zip.in10 - Source.single(11) ~> zip.in11 - Source.single(12).map(_.toString) ~> zip.in12 - Source.single(13) ~> zip.in13 - Source.single(14).map(_.toString) ~> zip.in14 - Source.single(15) ~> zip.in15 - Source.single(16).map(_.toString) ~> zip.in16 - Source.single(17) ~> zip.in17 - Source.single(18).map(_.toString) ~> zip.in18 - Source.single(19) ~> zip.in19 + val zip = b.add(ZipWith(sum19)) + + Source.single(1) ~> zip.in0 + Source.single(2).map(_.toString) ~> zip.in1 + Source.single(3) ~> zip.in2 + Source.single(4).map(_.toString) ~> zip.in3 + Source.single(5) ~> zip.in4 + Source.single(6).map(_.toString) ~> zip.in5 + Source.single(7) ~> zip.in6 + Source.single(8).map(_.toString) ~> zip.in7 + Source.single(9) ~> zip.in8 + Source.single(10).map(_.toString) ~> zip.in9 + Source.single(11) ~> zip.in10 + Source.single(12).map(_.toString) ~> zip.in11 + Source.single(13) ~> zip.in12 + Source.single(14).map(_.toString) ~> zip.in13 + Source.single(15) ~> zip.in14 + Source.single(16).map(_.toString) ~> zip.in15 + Source.single(17) ~> zip.in16 + Source.single(18).map(_.toString) ~> zip.in17 + Source.single(19) ~> zip.in18 zip.out ~> Sink(probe) }.run() diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/PublisherSinkSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/PublisherSinkSpec.scala index 51efdb836ea..df03e2a36f2 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/PublisherSinkSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/PublisherSinkSpec.scala @@ -18,11 +18,11 @@ class PublisherSinkSpec extends AkkaSpec { "be unique when created twice" in { - val (pub1, pub2) = FlowGraph(Sink.publisher[Int], Sink.publisher[Int])(Pair.apply) { implicit b ⇒ + val (pub1, pub2) = Graph.closed(Sink.publisher[Int], Sink.publisher[Int])(Pair.apply) { implicit b ⇒ (p1, p2) ⇒ - import FlowGraph.Implicits._ + import Graph.Implicits._ - val bcast = Broadcast[Int](2) + val bcast = b.add(Broadcast[Int](2)) Source(0 to 5) ~> bcast.in bcast.out(0).map(_ * 2) ~> p1.inlet diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/SinkSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/SinkSpec.scala index c192add0d83..71b89b7ca2a 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/SinkSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/SinkSpec.scala @@ -8,7 +8,7 @@ import akka.stream.testkit.StreamTestKit.SubscriberProbe import akka.stream.FlowMaterializer class SinkSpec extends AkkaSpec { - import FlowGraph.Implicits._ + import Graph.Implicits._ implicit val mat = FlowMaterializer() @@ -17,7 +17,7 @@ class SinkSpec extends AkkaSpec { "be composable without importing modules" in { val probes = Array.fill(3)(SubscriberProbe[Int]) val sink = Sink() { implicit b ⇒ - val bcast = Broadcast[Int](3) + val bcast = b.add(Broadcast[Int](3)) for (i ← 0 to 2) bcast.out(i).filter(_ == i) ~> Sink(probes(i)) bcast.in } @@ -35,7 +35,7 @@ class SinkSpec extends AkkaSpec { val probes = Array.fill(3)(SubscriberProbe[Int]) val sink = Sink(Sink(probes(0))) { implicit b ⇒ s0 ⇒ - val bcast = Broadcast[Int](3) + val bcast = b.add(Broadcast[Int](3)) bcast.out(0) ~> Flow[Int].filter(_ == 0) ~> s0.inlet for (i ← 1 to 2) bcast.out(i).filter(_ == i) ~> Sink(probes(i)) bcast.in @@ -54,7 +54,7 @@ class SinkSpec extends AkkaSpec { val probes = Array.fill(3)(SubscriberProbe[Int]) val sink = Sink(Sink(probes(0)), Sink(probes(1)))(List(_, _)) { implicit b ⇒ (s0, s1) ⇒ - val bcast = Broadcast[Int](3) + val bcast = b.add(Broadcast[Int](3)) bcast.out(0).filter(_ == 0) ~> s0.inlet bcast.out(1).filter(_ == 1) ~> s1.inlet bcast.out(2).filter(_ == 2) ~> Sink(probes(2)) @@ -74,7 +74,7 @@ class SinkSpec extends AkkaSpec { val probes = Array.fill(3)(SubscriberProbe[Int]) val sink = Sink(Sink(probes(0)), Sink(probes(1)), Sink(probes(2)))(List(_, _, _)) { implicit b ⇒ (s0, s1, s2) ⇒ - val bcast = Broadcast[Int](3) + val bcast = b.add(Broadcast[Int](3)) bcast.out(0).filter(_ == 0) ~> s0.inlet bcast.out(1).filter(_ == 1) ~> s1.inlet bcast.out(2).filter(_ == 2) ~> s2.inlet diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/SourceSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/SourceSpec.scala index f2d98eb4500..cb102779ef3 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/SourceSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/SourceSpec.scala @@ -81,8 +81,8 @@ class SourceSpec extends AkkaSpec { val s = Source(source, source, source, source, source)(Seq(_, _, _, _, _)) { implicit b ⇒ (i0, i1, i2, i3, i4) ⇒ - import FlowGraph.Implicits._ - val m = Merge[Int](5) + import Graph.Implicits._ + val m = b.add(Merge[Int](5)) i0.outlet ~> m.in(0) i1.outlet ~> m.in(1) i2.outlet ~> m.in(2) diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/TickSourceSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/TickSourceSpec.scala index 9c1b49ebfed..edad752164d 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/TickSourceSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/TickSourceSpec.scala @@ -69,7 +69,7 @@ class TickSourceSpec extends AkkaSpec { // val c = StreamTestKit.SubscriberProbe[Int]() // // FlowGraph { implicit b ⇒ - // import FlowGraph.Implicits._ + // import Graph.Implicits._ // val zip = Zip[Int, String] // Source(1 to 100) ~> zip.left // Source(1.second, 1.second, "tick") ~> zip.right diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiMerge.scala b/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiMerge.scala index 5575bc44d60..f63dd9edb6f 100644 --- a/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiMerge.scala +++ b/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiMerge.scala @@ -218,6 +218,8 @@ object FlexiMerge { */ abstract class FlexiMerge[Out, S <: Shape](val shape: S, attributes: OperationAttributes) extends Graph[S, Unit] { val module: StreamLayout.Module = new FlexiMergeModule(shape, createMergeLogic) + + type PortT = S def createMergeLogic(s: S): MergeLogic[Out] From b1c48d5210c03112634a5272421c5e2fd9c86cd7 Mon Sep 17 00:00:00 2001 From: Roland Kuhn Date: Wed, 18 Feb 2015 21:15:30 +0100 Subject: [PATCH 3/4] make FlexiRoute demand tracking type-safe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is one caveat: in truth our variance annotations on Source/Flow/Sink which need to be threaded all the way through to InPort and OutPort are in fact not really correct (actually: inverted) when writing the “inside” of a stream element, as an InPort behaves as a source of things and should therefore be covariant. The only point where this surfaces is the State (in both FlexiMerge/Route) which ties together the port and element types. In the case of FlexiMerge things work out by chance because type inference for varargs works covariantly, “fixing” the wrong contravariance of InPort. For FlexiRoute there is no such luck, scalac will happily accept all kinds of nonsense code due to incorrect covariance. The problematic case is DemandFromAny which demands GLB instead of LUB to be calculated. I fixed this by introducing a mini-DSL that does this. It works like this: val select = p.out0 | p.out1 // will create a set with GLB of both // types and error out if none exists ... ctx.emit(select(port))(element) where `port` is an untyped OutP that is found in the selector set using object equality. The `port` argument is what the State gives the user, forcing them to do this kind of resolution (they can also use `if` if they want). Combine this with DemandFromAll not giving the user any port info at all (since all of them have demand and can thus nicely be used directly) and we got all the bases covered—hopefully. --- .../stream/scaladsl/GraphFlexiRouteSpec.scala | 49 +++++++------- .../akka/stream/impl/FlexiRouteImpl.scala | 23 ++++--- .../akka/stream/javadsl/FlexiRoute.scala | 34 +++++----- .../akka/stream/scaladsl/FlexiRoute.scala | 66 +++++++++++++------ 4 files changed, 101 insertions(+), 71 deletions(-) diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphFlexiRouteSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphFlexiRouteSpec.scala index f102f928041..0bfcfc4d2f2 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphFlexiRouteSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/GraphFlexiRouteSpec.scala @@ -10,6 +10,7 @@ import akka.stream.testkit.StreamTestKit.OnNext import akka.stream.testkit.StreamTestKit.PublisherProbe import akka.stream.testkit.StreamTestKit.SubscriberProbe import akka.actor.ActorSystem +import akka.stream._ object GraphFlexiRouteSpec { @@ -18,19 +19,20 @@ object GraphFlexiRouteSpec { * they are have requested elements. Or in other words, if all outputs have demand available at the same * time then in finite steps all elements are enqueued to them. */ - class Fair[T] extends FlexiRoute[T, UniformFanOut[T, T]](new UniformFanOut(2), OperationAttributes.name("FairBalance")) { + class Fair[T] extends FlexiRoute[T, UniformFanOutShape[T, T]](new UniformFanOutShape(2), OperationAttributes.name("FairBalance")) { import FlexiRoute._ override def createRouteLogic(p: PortT): RouteLogic[T] = new RouteLogic[T] { + val select = p.out(0) | p.out(1) - val emitToAnyWithDemand = State[T](DemandFromAny(p)) { (ctx, preferredOutput, element) ⇒ - ctx.emit(preferredOutput, element) + val emitToAnyWithDemand = State(DemandFromAny(p)) { (ctx, out, element) ⇒ + ctx.emit(select(out))(element) SameState } // initally, wait for demand from all - override def initialState = State[T](DemandFromAll(p)) { (ctx, preferredOutput, element) ⇒ - ctx.emit(preferredOutput, element) + override def initialState = State(DemandFromAll(p)) { (ctx, _, element) ⇒ + ctx.emit(p.out(0))(element) emitToAnyWithDemand } } @@ -40,18 +42,18 @@ object GraphFlexiRouteSpec { * It never skips an output while cycling but waits on it instead (closed outputs are skipped though). * The fair route above is a non-strict round-robin (skips currently unavailable outputs). */ - class StrictRoundRobin[T] extends FlexiRoute[T, UniformFanOut[T, T]](new UniformFanOut(2), OperationAttributes.name("RoundRobinBalance")) { + class StrictRoundRobin[T] extends FlexiRoute[T, UniformFanOutShape[T, T]](new UniformFanOutShape(2), OperationAttributes.name("RoundRobinBalance")) { import FlexiRoute._ override def createRouteLogic(p: PortT) = new RouteLogic[T] { - val toOutput1: State[T] = State[T](DemandFrom(p.out(0))) { (ctx, _, element) ⇒ - ctx.emit(p.out(0), element) + val toOutput1: State[Outlet[T]] = State(DemandFrom(p.out(0))) { (ctx, out, element) ⇒ + ctx.emit(out)(element) toOutput2 } - val toOutput2 = State[T](DemandFrom(p.out(1))) { (ctx, _, element) ⇒ - ctx.emit(p.out(1), element) + val toOutput2 = State(DemandFrom(p.out(1))) { (ctx, out, element) ⇒ + ctx.emit(out)(element) toOutput1 } @@ -59,15 +61,15 @@ object GraphFlexiRouteSpec { } } - class Unzip[A, B] extends FlexiRoute[(A, B), FanOut2[(A, B), A, B]](new FanOut2, OperationAttributes.name("Unzip")) { + class Unzip[A, B] extends FlexiRoute[(A, B), FanOutShape2[(A, B), A, B]](new FanOutShape2, OperationAttributes.name("Unzip")) { import FlexiRoute._ override def createRouteLogic(p: PortT) = new RouteLogic[(A, B)] { - override def initialState = State[Any](DemandFromAll(p)) { (ctx, _, element) ⇒ + override def initialState = State(DemandFromAll(p)) { (ctx, _, element) ⇒ val (a, b) = element - ctx.emit(p.out0, a) - ctx.emit(p.out1, b) + ctx.emit(p.out0)(a) + ctx.emit(p.out1)(b) SameState } @@ -75,14 +77,15 @@ object GraphFlexiRouteSpec { } } - class TestRoute extends FlexiRoute[String, FanOut2[String, String, String]](new FanOut2, OperationAttributes.name("TestRoute")) { + class TestRoute extends FlexiRoute[String, FanOutShape2[String, String, String]](new FanOutShape2, OperationAttributes.name("TestRoute")) { import FlexiRoute._ var throwFromOnComplete = false def createRouteLogic(p: PortT): RouteLogic[String] = new RouteLogic[String] { + val select = p.out0 | p.out1 - override def initialState = State[String](DemandFromAny(p)) { + override def initialState = State(DemandFromAny(p)) { (ctx, preferred, element) ⇒ if (element == "err") ctx.error(new RuntimeException("err") with NoStackTrace) @@ -95,7 +98,7 @@ object GraphFlexiRouteSpec { else if (element == "complete") ctx.complete() else - ctx.emit(preferred, "onInput: " + element) + ctx.emit(select(preferred))("onInput: " + element) SameState } @@ -104,25 +107,25 @@ object GraphFlexiRouteSpec { onComplete = { ctx ⇒ if (throwFromOnComplete) throw new RuntimeException("onComplete-exc") with NoStackTrace - p.outlets.foreach { output ⇒ + select.all foreach { output ⇒ if (ctx.isDemandAvailable(output)) - ctx.emit(output, "onComplete") + ctx.emit(output)("onComplete") } }, onError = { (ctx, cause) ⇒ cause match { case _: IllegalArgumentException ⇒ // swallow case _ ⇒ - p.outlets.foreach { output ⇒ + select.all foreach { output ⇒ if (ctx.isDemandAvailable(output)) - ctx.emit(output, "onError") + ctx.emit(output)("onError") } } }, onCancel = { (ctx, cancelledOutput) ⇒ - p.outlets.foreach { output ⇒ + select.all foreach { output ⇒ if (output != cancelledOutput && ctx.isDemandAvailable(output)) - ctx.emit(output, "onCancel: " + cancelledOutput) + ctx.emit(output)("onCancel: " + cancelledOutput) } SameState }) diff --git a/akka-stream/src/main/scala/akka/stream/impl/FlexiRouteImpl.scala b/akka-stream/src/main/scala/akka/stream/impl/FlexiRouteImpl.scala index 617d3df37b3..a44b9996991 100644 --- a/akka-stream/src/main/scala/akka/stream/impl/FlexiRouteImpl.scala +++ b/akka-stream/src/main/scala/akka/stream/impl/FlexiRouteImpl.scala @@ -5,7 +5,7 @@ package akka.stream.impl import akka.stream.{ scaladsl, MaterializerSettings } import akka.stream.impl.FanOut.OutputBunch -import akka.stream.{ Shape, OutPort } +import akka.stream.{ Shape, OutPort, Outlet } import scala.util.control.NonFatal @@ -19,12 +19,13 @@ private[akka] class FlexiRouteImpl[T, S <: Shape](_settings: MaterializerSetting import akka.stream.scaladsl.FlexiRoute._ - private type StateT = routeLogic.State[AnyRef] + private type StateT = routeLogic.State[_] private type CompletionT = routeLogic.CompletionHandling - val outputMapping: Array[OutPort] = shape.outlets.toArray + val outputMapping: Array[Outlet[_]] = shape.outlets.toArray val indexOf: Map[OutPort, Int] = shape.outlets.zipWithIndex.toMap + private def anyBehavior = behavior.asInstanceOf[routeLogic.State[Outlet[Any]]] private var behavior: StateT = _ private var completion: CompletionT = _ @@ -49,11 +50,11 @@ private[akka] class FlexiRouteImpl[T, S <: Shape](_settings: MaterializerSetting } } - private val ctx: routeLogic.RouteLogicContext[AnyRef] = new routeLogic.RouteLogicContext[AnyRef] { + private val ctx: routeLogic.RouteLogicContext = new routeLogic.RouteLogicContext { override def isDemandAvailable(output: OutPort): Boolean = (indexOf(output) < outputCount) && outputBunch.isPending(indexOf(output)) - override def emit(output: OutPort, elem: AnyRef): Unit = { + override def emit[Out](output: Outlet[Out])(elem: Out): Unit = { val idx = indexOf(output) require(outputBunch.isPending(idx), s"emit to [$output] not allowed when no demand available") outputBunch.enqueue(idx, elem) @@ -91,8 +92,8 @@ private[akka] class FlexiRouteImpl[T, S <: Shape](_settings: MaterializerSetting private def precondition: TransferState = { behavior.condition match { - case _: DemandFrom | _: DemandFromAny ⇒ primaryInputs.NeedsInput && outputBunch.AnyOfMarkedOutputs - case _: DemandFromAll ⇒ primaryInputs.NeedsInput && outputBunch.AllOfMarkedOutputs + case _: DemandFrom[_] | _: DemandFromAny ⇒ primaryInputs.NeedsInput && outputBunch.AnyOfMarkedOutputs + case _: DemandFromAll ⇒ primaryInputs.NeedsInput && outputBunch.AllOfMarkedOutputs } } @@ -127,15 +128,13 @@ private[akka] class FlexiRouteImpl[T, S <: Shape](_settings: MaterializerSetting case any: DemandFromAny ⇒ val id = outputBunch.idToEnqueueAndYield() val outputHandle = outputMapping(id) - changeBehavior(behavior.onInput(ctx, outputHandle, elem.asInstanceOf[T])) + changeBehavior(anyBehavior.onInput(ctx, outputHandle, elem.asInstanceOf[T])) case DemandFrom(outputHandle) ⇒ - changeBehavior(behavior.onInput(ctx, outputHandle, elem.asInstanceOf[T])) + changeBehavior(anyBehavior.onInput(ctx, outputHandle, elem.asInstanceOf[T])) case all: DemandFromAll ⇒ - val id = outputBunch.idToEnqueueAndYield() - val outputHandle = outputMapping(id) - changeBehavior(behavior.onInput(ctx, outputHandle, elem.asInstanceOf[T])) + changeBehavior(behavior.asInstanceOf[routeLogic.State[Unit]].onInput(ctx, (), elem.asInstanceOf[T])) } diff --git a/akka-stream/src/main/scala/akka/stream/javadsl/FlexiRoute.scala b/akka-stream/src/main/scala/akka/stream/javadsl/FlexiRoute.scala index 54cde4ca1e6..e387911b7ec 100644 --- a/akka-stream/src/main/scala/akka/stream/javadsl/FlexiRoute.scala +++ b/akka-stream/src/main/scala/akka/stream/javadsl/FlexiRoute.scala @@ -12,7 +12,7 @@ import akka.stream._ object FlexiRoute { - sealed trait DemandCondition + sealed trait DemandCondition[T] /** * Demand condition for the [[State]] that will be @@ -23,7 +23,7 @@ object FlexiRoute { * has been completed. `IllegalArgumentException` is thrown if * that is not obeyed. */ - class DemandFrom(val output: OutPort) extends DemandCondition + class DemandFrom(val output: OutPort) extends DemandCondition[OutPort] /** * Demand condition for the [[State]] that will be @@ -33,7 +33,7 @@ object FlexiRoute { * Cancelled and completed inputs are not used, i.e. it is allowed * to specify them in the list of `outputs`. */ - class DemandFromAny(val outputs: JList[OutPort]) extends DemandCondition + class DemandFromAny(val outputs: JList[OutPort]) extends DemandCondition[OutPort] /** * Demand condition for the [[State]] that will be @@ -43,7 +43,7 @@ object FlexiRoute { * Cancelled and completed outputs are not used, i.e. it is allowed * to specify them in the list of `outputs`. */ - class DemandFromAll(val outputs: JList[OutPort]) extends DemandCondition + class DemandFromAll(val outputs: JList[OutPort]) extends DemandCondition[Unit] /** * Context that is passed to the functions of [[State]] and [[CompletionHandling]]. @@ -105,7 +105,7 @@ object FlexiRoute { abstract class CompletionHandling { def onComplete(ctx: RouteLogicContext): Unit def onError(ctx: RouteLogicContext, cause: Throwable): Unit - def onCancel(ctx: RouteLogicContext, output: OutPort): State + def onCancel(ctx: RouteLogicContext, output: OutPort): State[_] } /** @@ -118,8 +118,8 @@ object FlexiRoute { * The `onInput` method is called when an `element` was read from upstream. * The function returns next behavior or [[#sameState]] to keep current behavior. */ - abstract class State(val condition: DemandCondition) { - def onInput(ctx: RouteLogicContext, preferredOutput: OutPort, element: AnyRef): State + abstract class State[T](val condition: DemandCondition[T]) { + def onInput(ctx: RouteLogicContext, preferredOutput: T, element: AnyRef): State[_] } /** @@ -131,13 +131,13 @@ object FlexiRoute { */ abstract class RouteLogic { - def initialState: State + def initialState: State[_] def initialCompletionHandling: CompletionHandling = defaultCompletionHandling /** * Return this from [[State]] `onInput` to use same state for next element. */ - def sameState: State = FlexiRoute.sameStateInstance + def sameState[T]: State[T] = FlexiRoute.sameStateInstance.asInstanceOf[State[T]] /** * Convenience to create a [[DemandFromAny]] condition. @@ -168,7 +168,7 @@ object FlexiRoute { new CompletionHandling { override def onComplete(ctx: RouteLogicContext): Unit = () override def onError(ctx: RouteLogicContext, cause: Throwable): Unit = () - override def onCancel(ctx: RouteLogicContext, output: OutPort): State = + override def onCancel(ctx: RouteLogicContext, output: OutPort): State[_] = sameState } @@ -180,7 +180,7 @@ object FlexiRoute { new CompletionHandling { override def onComplete(ctx: RouteLogicContext): Unit = () override def onError(ctx: RouteLogicContext, cause: Throwable): Unit = () - override def onCancel(ctx: RouteLogicContext, output: OutPort): State = { + override def onCancel(ctx: RouteLogicContext, output: OutPort): State[_] = { ctx.complete() sameState } @@ -188,7 +188,7 @@ object FlexiRoute { } private val sameStateInstance = new State(new DemandFromAny(java.util.Collections.emptyList[OutPort])) { - override def onInput(ctx: RouteLogicContext, output: OutPort, element: AnyRef): State = + override def onInput(ctx: RouteLogicContext, output: OutPort, element: AnyRef): State[_] = throw new UnsupportedOperationException("SameState.onInput should not be called") override def toString: String = "SameState" @@ -205,11 +205,11 @@ object FlexiRoute { override def initialCompletionHandling: this.CompletionHandling = wrapCompletionHandling(delegate.initialCompletionHandling) - private def wrapState(delegateState: FlexiRoute.State): State[_] = + private def wrapState[T](delegateState: FlexiRoute.State[T]): State[T] = if (sameStateInstance == delegateState) SameState else - State[AnyRef](convertDemandCondition(delegateState.condition)) { (ctx, outputHandle, elem) ⇒ + State(convertDemandCondition(delegateState.condition)) { (ctx, outputHandle, elem) ⇒ val newDelegateState = delegateState.onInput(new RouteLogicContextWrapper(ctx), outputHandle, elem) wrapState(newDelegateState) @@ -230,9 +230,9 @@ object FlexiRoute { wrapState(newDelegateState) }) - class RouteLogicContextWrapper(delegate: RouteLogicContext[AnyRef]) extends FlexiRoute.RouteLogicContext { + class RouteLogicContextWrapper(delegate: RouteLogicContext) extends FlexiRoute.RouteLogicContext { override def isDemandAvailable(output: OutPort): Boolean = delegate.isDemandAvailable(output) - override def emit(output: OutPort, elem: AnyRef): Unit = delegate.emit(output, elem) + override def emit(output: OutPort, elem: AnyRef): Unit = delegate.emit(output.asInstanceOf[Outlet[AnyRef]])(elem) override def complete(): Unit = delegate.complete() override def complete(output: OutPort): Unit = delegate.complete(output) override def error(cause: Throwable): Unit = delegate.error(cause) @@ -245,7 +245,7 @@ object FlexiRoute { private def toAnyRefSeq(l: JList[OutPort]) = immutableIndexedSeq(l).asInstanceOf[immutable.Seq[Outlet[AnyRef]]] - def convertDemandCondition(condition: DemandCondition): scaladsl.FlexiRoute.DemandCondition = + def convertDemandCondition[T](condition: DemandCondition[T]): scaladsl.FlexiRoute.DemandCondition[T] = condition match { case c: DemandFromAny ⇒ scaladsl.FlexiRoute.DemandFromAny(toAnyRefSeq(c.outputs)) case c: DemandFromAll ⇒ scaladsl.FlexiRoute.DemandFromAll(toAnyRefSeq(c.outputs)) diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiRoute.scala b/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiRoute.scala index ba9095baf4a..1086884591c 100644 --- a/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiRoute.scala +++ b/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiRoute.scala @@ -12,9 +12,7 @@ object FlexiRoute { import akka.stream.impl.StreamLayout - import scala.language.higherKinds - - sealed trait DemandCondition + sealed trait DemandCondition[+T] /** * Demand condition for the [[RouteLogic#State]] that will be @@ -25,11 +23,11 @@ object FlexiRoute { * has been completed. `IllegalArgumentException` is thrown if * that is not obeyed. */ - final case class DemandFrom(output: Outlet[_]) extends DemandCondition + final case class DemandFrom[+T](output: Outlet[T]) extends DemandCondition[Outlet[T]] object DemandFromAny { - def apply(outputs: immutable.Seq[Outlet[_]]): DemandFromAny = new DemandFromAny(outputs: _*) - def apply(p: Shape): DemandFromAny = new DemandFromAny(p.outlets.asInstanceOf[Seq[Outlet[Nothing]]]: _*) + def apply(outputs: OutPort*): DemandFromAny = new DemandFromAny(outputs.to[immutable.Seq]) + def apply(p: Shape): DemandFromAny = new DemandFromAny(p.outlets) } /** * Demand condition for the [[RouteLogic#State]] that will be @@ -39,11 +37,11 @@ object FlexiRoute { * Cancelled and completed outputs are not used, i.e. it is allowed * to specify them in the list of `outputs`. */ - final case class DemandFromAny(outputs: Outlet[_]*) extends DemandCondition + final case class DemandFromAny(outputs: immutable.Seq[OutPort]) extends DemandCondition[OutPort] object DemandFromAll { - def apply(outputs: immutable.Seq[Outlet[_]]): DemandFromAll = new DemandFromAll(outputs: _*) - def apply(p: Shape): DemandFromAll = new DemandFromAll(p.outlets.asInstanceOf[Seq[Outlet[Nothing]]]: _*) + def apply(outputs: OutPort*): DemandFromAll = new DemandFromAll(outputs.to[immutable.Seq]) + def apply(p: Shape): DemandFromAll = new DemandFromAll(p.outlets) } /** * Demand condition for the [[RouteLogic#State]] that will be @@ -53,7 +51,7 @@ object FlexiRoute { * Cancelled and completed outputs are not used, i.e. it is allowed * to specify them in the list of `outputs`. */ - final case class DemandFromAll(outputs: Outlet[_]*) extends DemandCondition + final case class DemandFromAll(outputs: immutable.Seq[OutPort]) extends DemandCondition[Unit] /** * The possibly stateful logic that reads from the input and enables emitting to downstream @@ -71,7 +69,7 @@ object FlexiRoute { * The context provides means for performing side effects, such as emitting elements * downstream. */ - trait RouteLogicContext[Out] { + trait RouteLogicContext { /** * @return `true` if at least one element has been requested by the given downstream (output). */ @@ -82,7 +80,7 @@ object FlexiRoute { * [[#isDemandAvailable]] is `true` for the given `output`, otherwise * `IllegalArgumentException` is thrown. */ - def emit(output: OutPort, elem: Out): Unit + def emit[Out](output: Outlet[Out])(elem: Out): Unit /** * Complete the given downstream successfully. @@ -120,15 +118,15 @@ object FlexiRoute { * The `onInput` function is called when an `element` was read from upstream. * The function returns next behavior or [[#SameState]] to keep current behavior. */ - sealed case class State[Out](condition: DemandCondition)( - val onInput: (RouteLogicContext[Out], OutPort, In) ⇒ State[_]) + sealed case class State[Out](condition: DemandCondition[Out])( + val onInput: (RouteLogicContext, Out, In) ⇒ State[_]) /** * Return this from [[State]] `onInput` to use same state for next element. */ - def SameState[In]: State[In] = sameStateInstance.asInstanceOf[State[In]] + def SameState[T]: State[T] = sameStateInstance.asInstanceOf[State[T]] - private val sameStateInstance = new State[Any](DemandFromAny(Nil))((_, _, _) ⇒ + private val sameStateInstance = new State(DemandFromAny(Nil))((_, _, _) ⇒ throw new UnsupportedOperationException("SameState.onInput should not be called")) { // unique instance, don't use case class @@ -149,9 +147,9 @@ object FlexiRoute { * It returns next behavior or [[#SameState]] to keep current behavior. */ sealed case class CompletionHandling( - onComplete: RouteLogicContext[AnyRef] ⇒ Unit, - onError: (RouteLogicContext[AnyRef], Throwable) ⇒ Unit, - onCancel: (RouteLogicContext[AnyRef], OutPort) ⇒ State[_]) + onComplete: RouteLogicContext ⇒ Unit, + onError: (RouteLogicContext, Throwable) ⇒ Unit, + onCancel: (RouteLogicContext, OutPort) ⇒ State[_]) /** * When an output cancels it continues with remaining outputs. @@ -196,6 +194,36 @@ abstract class FlexiRoute[In, S <: Shape](val shape: S, attributes: OperationAtt val module: StreamLayout.Module = new FlexiRouteModule(shape, createRouteLogic) + /** + * This allows a type-safe mini-DSL for selecting one of several ports, very useful in + * conjunction with DemandFromAny(...): + * + * {{{ + * State(DemandFromAny(p1, p2, p2)) { (ctx, out, element) => + * ctx.emit((p1 | p2 | p3)(out))(element) + * } + * }}} + * + * This will ensure that the either of the three ports would accept the type of `element`. + */ + implicit class PortUnion[L](left: Outlet[L]) { + def |[R <: L](right: Outlet[R]): InnerPortUnion[R] = new InnerPortUnion(Map((left, left.asInstanceOf[Outlet[R]]), (right, right))) + /* + * It would be nicer to use `Map[OutP, OutPort[_ <: T]]` to get rid of the casts, + * but unfortunately this kills the compiler (and quite violently so). + */ + class InnerPortUnion[T] private[PortUnion] (ports: Map[OutPort, Outlet[T]]) { + def |[R <: T](right: Outlet[R]): InnerPortUnion[R] = new InnerPortUnion(ports.asInstanceOf[Map[OutPort, Outlet[R]]].updated(right, right)) + def apply(p: OutPort) = ports get p match { + case Some(p) ⇒ p + case None ⇒ throw new IllegalStateException(s"port $p was not among the allowed ones (${ports.keys.mkString(", ")})") + } + def all: Iterable[Outlet[T]] = ports.values + } + } + + type PortT = S + /** * Create the stateful logic that will be used when reading input elements * and emitting output elements. Create a new instance every time. From b2a7279002a6fb9e8f4995bd2a24db3df00d3b69 Mon Sep 17 00:00:00 2001 From: Roland Kuhn Date: Thu, 19 Feb 2015 23:58:50 +0100 Subject: [PATCH 4/4] it compiles, 90% tests green --- .../stream/DslFactoriesConsistencySpec.scala | 2 +- .../scaladsl/FlowGraphCompileSpec.scala | 605 ++++++------------ .../src/main/scala/akka/stream/Graph.scala | 2 +- .../src/main/scala/akka/stream/Shape.scala | 17 +- .../main/scala/akka/stream/impl/Flows.scala | 2 +- .../main/scala/akka/stream/impl/Sources.scala | 2 +- .../scala/akka/stream/impl/StreamLayout.scala | 4 +- .../akka/stream/javadsl/FlexiMerge.scala | 2 +- .../akka/stream/javadsl/FlexiRoute.scala | 2 +- .../scala/akka/stream/javadsl/Graph.scala | 2 +- .../scala/akka/stream/javadsl/StreamTcp.scala | 8 +- .../akka/stream/scaladsl/FlexiMerge.scala | 2 +- .../akka/stream/scaladsl/FlexiRoute.scala | 4 +- .../scala/akka/stream/scaladsl/Graph.scala | 3 + 14 files changed, 220 insertions(+), 437 deletions(-) diff --git a/akka-stream-tests/src/test/scala/akka/stream/DslFactoriesConsistencySpec.scala b/akka-stream-tests/src/test/scala/akka/stream/DslFactoriesConsistencySpec.scala index 08bfdacd06f..f5447d88a5e 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/DslFactoriesConsistencySpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/DslFactoriesConsistencySpec.scala @@ -173,7 +173,7 @@ class DslFactoriesConsistencySpec extends WordSpec with Matchers { * If scaladsl is not a keyed type, javadsl shouldn't be as well. */ def returnTypeMatch(s: Class[_], j: Class[_]): Boolean = - (sSource.isAssignableFrom(s) && jSource.isAssignableFrom(j)) || + (sSource.isAssignableFrom(s) && jSource.isAssignableFrom(j)) || (sSink.isAssignableFrom(s) && jSink.isAssignableFrom(j)) || (sFlow.isAssignableFrom(s) && jFlow.isAssignableFrom(j)) diff --git a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/FlowGraphCompileSpec.scala b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/FlowGraphCompileSpec.scala index ca823de55f4..166105a451f 100644 --- a/akka-stream-tests/src/test/scala/akka/stream/scaladsl/FlowGraphCompileSpec.scala +++ b/akka-stream-tests/src/test/scala/akka/stream/scaladsl/FlowGraphCompileSpec.scala @@ -41,246 +41,180 @@ class FlowGraphCompileSpec extends AkkaSpec { val out1 = Sink.publisher[String] val out2 = Sink.head[String] - "FlowGraph" should { + "A Graph" should { "build simple merge" in { - FlowGraph { b ⇒ - val merge = Merge[String] - b. - addEdge(in1, f1, merge). - addEdge(in2, f2, merge). - addEdge(merge, f3, out1) + Graph.closed() { b ⇒ + val merge = b.add(Merge[String](2)) + b.addEdge(b.add(in1), f1, merge.in(0)) + b.addEdge(b.add(in2), f2, merge.in(1)) + b.addEdge(merge.out, f3, b.add(out1)) }.run() } "build simple broadcast" in { - FlowGraph { b ⇒ - val bcast = Broadcast[String] - b. - addEdge(in1, f1, bcast). - addEdge(bcast, f2, out1). - addEdge(bcast, f3, out2) + Graph.closed() { b ⇒ + val bcast = b.add(Broadcast[String](2)) + b.addEdge(b.add(in1), f1, bcast.in) + b.addEdge(bcast.out(0), f2, b.add(out1)) + b.addEdge(bcast.out(1), f3, b.add(out2)) }.run() } "build simple balance" in { - FlowGraph { b ⇒ - val balance = Balance[String] - b. - addEdge(in1, f1, balance). - addEdge(balance, f2, out1). - addEdge(balance, f3, out2) + Graph.closed() { b ⇒ + val balance = b.add(Balance[String](2)) + b.addEdge(b.add(in1), f1, balance.in) + b.addEdge(balance.out(0), f2, b.add(out1)) + b.addEdge(balance.out(1), f3, b.add(out2)) } } "build simple merge - broadcast" in { - FlowGraph { b ⇒ - val merge = Merge[String] - val bcast = Broadcast[String] - b. - addEdge(in1, f1, merge). - addEdge(in2, f2, merge). - addEdge(merge, f3, bcast). - addEdge(bcast, f4, out1). - addEdge(bcast, f5, out2) + Graph.closed() { b ⇒ + val merge = b.add(Merge[String](2)) + val bcast = b.add(Broadcast[String](2)) + b.addEdge(b.add(in1), f1, merge.in(0)) + b.addEdge(b.add(in2), f2, merge.in(1)) + b.addEdge(merge.out, f3, bcast.in) + b.addEdge(bcast.out(0), f4, b.add(out1)) + b.addEdge(bcast.out(1), f5, b.add(out2)) }.run() } "build simple merge - broadcast with implicits" in { - FlowGraph { implicit b ⇒ - import FlowGraphImplicits._ - val merge = Merge[String] - val bcast = Broadcast[String] - in1 ~> f1 ~> merge ~> f2 ~> bcast ~> f3 ~> out1 - in2 ~> f4 ~> merge - bcast ~> f5 ~> out2 + Graph.closed() { implicit b ⇒ + import Graph.Implicits._ + val merge = b.add(Merge[String](2)) + val bcast = b.add(Broadcast[String](2)) + b.add(in1) ~> f1 ~> merge.in(0) + merge.out ~> f2 ~> bcast.in + bcast.out(0) ~> f3 ~> b.add(out1) + b.add(in2) ~> f4 ~> merge.in(1) + bcast.out(1) ~> f5 ~> b.add(out2) }.run() } /** - * in ---> f1 -+-> f2 -+-> f3 ---> out1 + * in ---> f1 -+-> f2 -+-> f3 ---> b.add(out1) * ^ | * | V * f5 <-+- f4 * | * V - * f6 ---> out2 + * f6 ---> b.add(out2) */ "detect cycle in " in { + pending intercept[IllegalArgumentException] { - FlowGraph { b ⇒ - val merge = Merge[String] - val bcast1 = Broadcast[String] - val bcast2 = Broadcast[String] + Graph.closed() { b ⇒ + val merge = b.add(Merge[String](2)) + val bcast1 = b.add(Broadcast[String](2)) + val bcast2 = b.add(Broadcast[String](2)) val feedbackLoopBuffer = Flow[String].buffer(10, OverflowStrategy.dropBuffer) - b. - addEdge(in1, f1, merge). - addEdge(merge, f2, bcast1). - addEdge(bcast1, f3, out1). - addEdge(bcast1, feedbackLoopBuffer, bcast2). - addEdge(bcast2, f5, merge). // cycle - addEdge(bcast2, f6, out2) + b.addEdge(b.add(in1), f1, merge.in(0)) + b.addEdge(merge.out, f2, bcast1.in) + b.addEdge(bcast1.out(0), f3, b.add(out1)) + b.addEdge(bcast1.out(1), feedbackLoopBuffer, bcast2.in) + b.addEdge(bcast2.out(0), f5, merge.in(1)) // cycle + b.addEdge(bcast2.out(1), f6, b.add(out2)) } }.getMessage.toLowerCase should include("cycle") } - "express complex topologies in a readable way" in { - FlowGraph { implicit b ⇒ - b.allowCycles() - val merge = Merge[String] - val bcast1 = Broadcast[String] - val bcast2 = Broadcast[String] - val feedbackLoopBuffer = Flow[String].buffer(10, OverflowStrategy.dropBuffer) - import FlowGraphImplicits._ - in1 ~> f1 ~> merge ~> f2 ~> bcast1 ~> f3 ~> out1 - bcast1 ~> feedbackLoopBuffer ~> bcast2 ~> f5 ~> merge - bcast2 ~> f6 ~> out2 - }.run() - } - - "build broadcast - merge" in { - FlowGraph { implicit b ⇒ - val bcast = Broadcast[String] - val bcast2 = Broadcast[String] - val merge = Merge[String] - import FlowGraphImplicits._ - in1 ~> f1 ~> bcast ~> f2 ~> merge ~> f3 ~> out1 - bcast ~> f4 ~> merge - }.run() - } - - "build wikipedia Topological_sorting" in { - // see https://en.wikipedia.org/wiki/Topological_sorting#mediaviewer/File:Directed_acyclic_graph.png - FlowGraph { implicit b ⇒ - val b3 = Broadcast[String] - val b7 = Broadcast[String] - val b11 = Broadcast[String] - val m8 = Merge[String] - val m9 = Merge[String] - val m10 = Merge[String] - val m11 = Merge[String] - val in3 = Source(List("b")) - val in5 = Source(List("b")) - val in7 = Source(List("a")) - val out2 = Sink.publisher[String] - val out9 = Sink.publisher[String] - val out10 = Sink.publisher[String] - def f(s: String) = Flow[String].section(name(s))(_.transform(op[String, String])) - import FlowGraphImplicits._ - - in7 ~> f("a") ~> b7 ~> f("b") ~> m11 ~> f("c") ~> b11 ~> f("d") ~> out2 - b11 ~> f("e") ~> m9 ~> f("f") ~> out9 - b7 ~> f("g") ~> m8 ~> f("h") ~> m9 - b11 ~> f("i") ~> m10 ~> f("j") ~> out10 - in5 ~> f("k") ~> m11 - in3 ~> f("l") ~> b3 ~> f("m") ~> m8 - b3 ~> f("n") ~> m10 - }.run() - } - - "attachSource and attachSink" in { - val mg = FlowGraph { b ⇒ - val merge = Merge[String] - val undefinedSource1 = UndefinedSource[String] - val undefinedSource2 = UndefinedSource[String] - val undefinedSink1 = UndefinedSink[String] - b. - addEdge(undefinedSource1, f1, merge). - addEdge(undefinedSource2, f2, merge). - addEdge(merge, f3, undefinedSink1) - - b.attachSource(undefinedSource1, in1) - b.attachSource(undefinedSource2, in2) - b.attachSink(undefinedSink1, out1) - - }.run() - mg.get(out1) should not be (null) - } - - "build partial flow graphs" in { - val undefinedSource1 = UndefinedSource[String] - val undefinedSource2 = UndefinedSource[String] - val undefinedSink1 = UndefinedSink[String] - val bcast = Broadcast[String] - - val partial1 = PartialFlowGraph { implicit b ⇒ - import FlowGraphImplicits._ - val merge = Merge[String] - undefinedSource1 ~> f1 ~> merge ~> f2 ~> bcast ~> f3 ~> undefinedSink1 - undefinedSource2 ~> f4 ~> merge - } - partial1.undefinedSources should be(Set(undefinedSource1, undefinedSource2)) - partial1.undefinedSinks should be(Set(undefinedSink1)) - - val undefinedSink2 = UndefinedSink[String] - - val partial2 = PartialGraph.closed(partial1) { implicit b ⇒ - import FlowGraphImplicits._ - b.attachSource(undefinedSource1, in1) - b.attachSource(undefinedSource2, in2) - bcast ~> f5 ~> undefinedSink2 - } - partial2.undefinedSources should be(Set.empty) - partial2.undefinedSinks should be(Set(undefinedSink1, undefinedSink2)) - - Graph.closed(partial2) { b ⇒ - b.attachSink(undefinedSink1, out1) - b.attachSink(undefinedSink2, out2) - }.run() - - Graph.closed(partial2) { b ⇒ - b.attachSink(undefinedSink1, f1.to(out1)) - b.attachSink(undefinedSink2, f2.to(out2)) - }.run() - - Graph.closed(partial1) { implicit b ⇒ - import FlowGraphImplicits._ - b.attachSink(undefinedSink1, f1.to(out1)) - b.attachSource(undefinedSource1, Source(List("a", "b", "c")).via(f1)) - b.attachSource(undefinedSource2, Source(List("d", "e", "f")).via(f2)) - bcast ~> f5 ~> out2 - }.run() - } - - "make it optional to specify flows" in { - FlowGraph { implicit b ⇒ - val merge = Merge[String] - val bcast = Broadcast[String] - import FlowGraphImplicits._ - in1 ~> merge ~> bcast ~> out1 - in2 ~> merge - bcast ~> out2 - }.run() - } - - "chain input and output ports" in { - FlowGraph { implicit b ⇒ - val zip = Zip[Int, String] - val out = Sink.publisher[(Int, String)] - import FlowGraphImplicits._ - Source(List(1, 2, 3)) ~> zip.left ~> out - Source(List("a", "b", "c")) ~> zip.right - }.run() - } - - "build unzip - zip" in { - FlowGraph { implicit b ⇒ - val zip = Zip[Int, String] - val unzip = Unzip[Int, String] - val out = Sink.publisher[(Int, String)] - import FlowGraphImplicits._ - 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() - } + // "express complex topologies in a readable way" in { + // Graph.closed() { implicit b ⇒ + // b.allowCycles() + // val merge = b.add(Merge[String] + // val bcast1 = b.add(Broadcast[String] + // val bcast2 = b.add(Broadcast[String] + // val feedbackLoopBuffer = Flow[String].buffer(10, OverflowStrategy.dropBuffer) + // import FlowGraphImplicits._ + // b.add(in1) ~> f1 ~> merge ~> f2 ~> bcast1 ~> f3 ~> b.add(out1) + // bcast1 ~> feedbackLoopBuffer ~> bcast2 ~> f5 ~> merge + // bcast2 ~> f6 ~> b.add(out2) + // }.run() + // } + + // "build broadcast - merge" in { + // Graph.closed() { implicit b ⇒ + // val bcast = b.add(Broadcast[String] + // val bcast2 = b.add(Broadcast[String] + // val merge = b.add(Merge[String] + // import FlowGraphImplicits._ + // b.add(in1) ~> f1 ~> bcast ~> f2 ~> merge ~> f3 ~> b.add(out1) + // bcast ~> f4 ~> merge + // }.run() + // } + + // "build wikipedia Topological_sorting" in { + // // see https://en.wikipedia.org/wiki/Topological_sorting#mediaviewer/File:Directed_acyclic_graph.png + // Graph.closed() { implicit b ⇒ + // val b3 = b.add(Broadcast[String] + // val b7 = b.add(Broadcast[String] + // val b11 = b.add(Broadcast[String] + // val m8 = b.add(Merge[String] + // val m9 = b.add(Merge[String] + // val m10 = b.add(Merge[String] + // val m11 = b.add(Merge[String] + // val in3 = Source(List("b")) + // val in5 = Source(List("b")) + // val in7 = Source(List("a")) + // val b.add(out2) = Sink.publisher[String] + // val out9 = Sink.publisher[String] + // val out10 = Sink.publisher[String] + // def f(s: String) = Flow[String].section(name(s))(_.transform(op[String, String])) + // import FlowGraphImplicits._ + // + // in7 ~> f("a") ~> b7 ~> f("b") ~> m11 ~> f("c") ~> b11 ~> f("d") ~> b.add(out2) + // b11 ~> f("e") ~> m9 ~> f("f") ~> out9 + // b7 ~> f("g") ~> m8 ~> f("h") ~> m9 + // b11 ~> f("i") ~> m10 ~> f("j") ~> out10 + // in5 ~> f("k") ~> m11 + // in3 ~> f("l") ~> b3 ~> f("m") ~> m8 + // b3 ~> f("n") ~> m10 + // }.run() + // } + + // "make it optional to specify flows" in { + // Graph.closed() { implicit b ⇒ + // val merge = b.add(Merge[String] + // val bcast = b.add(Broadcast[String] + // import FlowGraphImplicits._ + // b.add(in1) ~> merge ~> bcast ~> b.add(out1) + // b.add(in2) ~> merge + // bcast ~> b.add(out2) + // }.run() + // } + // + // "chain input and output ports" in { + // Graph.closed() { implicit b ⇒ + // val zip = Zip[Int, String] + // val out = Sink.publisher[(Int, String)] + // import FlowGraphImplicits._ + // Source(List(1, 2, 3)) ~> zip.left ~> out + // Source(List("a", "b", "c")) ~> zip.right + // }.run() + // } + // + // "build unzip - zip" in { + // Graph.closed() { implicit b ⇒ + // val zip = Zip[Int, String] + // val unzip = Unzip[Int, String] + // val out = Sink.publisher[(Int, String)] + // import FlowGraphImplicits._ + // 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() + // } "distinguish between input and output ports" in { intercept[IllegalArgumentException] { - FlowGraph { implicit b ⇒ - val zip = Zip[Int, String] - val unzip = Unzip[Int, String] + Graph.closed() { implicit b ⇒ + val zip = b.add(Zip[Int, String]()) + val unzip = b.add(Unzip[Int, String]()) val wrongOut = Sink.publisher[(Int, Int)] val whatever = Sink.publisher[Any] "Flow(List(1, 2, 3)) ~> zip.left ~> wrongOut" shouldNot compile @@ -293,237 +227,84 @@ class FlowGraphCompileSpec extends AkkaSpec { }.getMessage should include("empty") } - "check maximumInputCount" in { - intercept[IllegalArgumentException] { - FlowGraph { implicit b ⇒ - val bcast = Broadcast[String] - import FlowGraphImplicits._ - in1 ~> bcast ~> out1 - in2 ~> bcast // wrong - } - }.getMessage should include("at most 1 incoming") - } - - "check maximumOutputCount" in { - intercept[IllegalArgumentException] { - FlowGraph { implicit b ⇒ - val merge = Merge[String] - import FlowGraphImplicits._ - in1 ~> merge ~> out1 - in2 ~> merge - merge ~> out2 // wrong - } - }.getMessage should include("at most 1 outgoing") - } - "build with variance" in { val out = Sink(SubscriberProbe[Fruit]()) - FlowGraph { b ⇒ - val merge = Merge[Fruit] - b. - addEdge(Source[Fruit](apples), Flow[Fruit], merge). - addEdge(Source[Apple](apples), Flow[Apple], merge). - addEdge(merge, Flow[Fruit].map(identity), out) + Graph.closed() { b ⇒ + val merge = b.add(Merge[Fruit](2)) + b.addEdge(b add Source[Fruit](apples), Flow[Fruit], merge.in(0)) + b.addEdge(b add Source[Apple](apples), Flow[Apple], merge.in(1)) + b.addEdge(merge.out, Flow[Fruit].map(identity), b add out) } } "build with implicits and variance" in { - PartialFlowGraph { implicit b ⇒ - val inA = Source(PublisherProbe[Fruit]()) - val inB = Source(PublisherProbe[Apple]()) - val outA = Sink(SubscriberProbe[Fruit]()) - val outB = Sink(SubscriberProbe[Fruit]()) - val merge = Merge[Fruit] - val unzip = Unzip[Int, String] - val whatever = Sink.publisher[Any] - import FlowGraphImplicits._ - Source[Fruit](apples) ~> merge - Source[Apple](apples) ~> merge - inA ~> merge - inB ~> merge - inA ~> Flow[Fruit].map(identity) ~> merge - inB ~> Flow[Apple].map(identity) ~> merge - UndefinedSource[Apple] ~> merge - UndefinedSource[Apple] ~> Flow[Fruit].map(identity) ~> merge - UndefinedSource[Apple] ~> Flow[Apple].map(identity) ~> merge - merge ~> Flow[Fruit].map(identity) ~> outA - - Source[Apple](apples) ~> Broadcast[Apple] ~> merge - Source[Apple](apples) ~> Broadcast[Apple] ~> outB - Source[Apple](apples) ~> Broadcast[Apple] ~> UndefinedSink[Fruit] - inB ~> Broadcast[Apple] ~> merge + Graph.closed() { implicit b ⇒ + val inA = b add Source(PublisherProbe[Fruit]()) + val inB = b add Source(PublisherProbe[Apple]()) + val outA = b add Sink(SubscriberProbe[Fruit]()) + val outB = b add Sink(SubscriberProbe[Fruit]()) + val merge = b add Merge[Fruit](12) + val unzip = b add Unzip[Int, String]() + val whatever = b add Sink.publisher[Any] + import Graph.Implicits._ + b.add(Source[Fruit](apples)) ~> merge.in(0) + Source[Apple](apples) ~> merge.in(1) + inA ~> merge.in(2) + inB ~> merge.in(3) + inA ~> Flow[Fruit].map(identity) ~> merge.in(4) + inB ~> Flow[Apple].map(identity) ~> merge.in(5) + b.add(Source(apples)) ~> merge.in(6) + b.add(Source(apples)) ~> Flow[Fruit].map(identity) ~> merge.in(7) + b.add(Source(apples)) ~> Flow[Apple].map(identity) ~> merge.in(8) + merge.out ~> Flow[Fruit].map(identity) ~> outA + + b.add(Source(apples)) ~> Flow[Apple] ~> merge.in(9) + b.add(Source(apples)) ~> Flow[Apple] ~> outB + b.add(Source(apples)) ~> Flow[Apple] ~> b.add(Sink.publisher[Fruit]) + inB ~> Flow[Apple] ~> merge.in(11) Source(List(1 -> "a", 2 -> "b", 3 -> "c")) ~> unzip.in - unzip.right ~> whatever - unzip.left ~> UndefinedSink[Any] + unzip.out1 ~> whatever + unzip.out0 ~> b.add(Sink.publisher[Any]) "UndefinedSource[Fruit] ~> Flow[Apple].map(identity) ~> merge" shouldNot compile - "UndefinedSource[Fruit] ~> Broadcast[Apple]" shouldNot compile - "merge ~> Broadcast[Apple]" shouldNot compile - "merge ~> Flow[Fruit].map(identity) ~> Broadcast[Apple]" shouldNot compile - "inB ~> merge ~> Broadcast[Apple]" shouldNot compile - "inA ~> Broadcast[Apple]" shouldNot compile + "UndefinedSource[Fruit] ~> b.add(Broadcast[Apple]" shouldNot compile + "merge ~> b.add(Broadcast[Apple]" shouldNot compile + "merge ~> Flow[Fruit].map(identity) ~> b.add(Broadcast[Apple]" shouldNot compile + "inB ~> merge ~> b.add(Broadcast[Apple]" shouldNot compile + "inA ~> b.add(Broadcast[Apple]" shouldNot compile } } "build with plain flow without junctions" in { - FlowGraph { b ⇒ - b.addEdge(in1, f1, out1) - }.run() - FlowGraph { b ⇒ - b.addEdge(in1, f1, f2.to(out1)) + Graph.closed() { b ⇒ + b.addEdge(b.add(in1), f1, b.add(out1)) }.run() - FlowGraph { b ⇒ - b.addEdge(in1.via(f1), f2, out1) + Graph.closed() { b ⇒ + b.addEdge(b.add(in1), f1, b.add(f2.to(out1))) }.run() - FlowGraph { implicit b ⇒ - import FlowGraphImplicits._ - in1 ~> f1 ~> out1 + Graph.closed() { b ⇒ + b.addEdge(b.add(in1 via f1), f2, b.add(out1)) }.run() - FlowGraph { implicit b ⇒ - import FlowGraphImplicits._ - in1 ~> out1 + Graph.closed() { implicit b ⇒ + import Graph.Implicits._ + b.add(in1) ~> f1 ~> b.add(out1) }.run() - FlowGraph { implicit b ⇒ - import FlowGraphImplicits._ - in1 ~> f1.to(out1) + Graph.closed() { implicit b ⇒ + import Graph.Implicits._ + b.add(in1) ~> b.add(out1) }.run() - FlowGraph { implicit b ⇒ - import FlowGraphImplicits._ - in1.via(f1) ~> out1 + Graph.closed() { implicit b ⇒ + import Graph.Implicits._ + b.add(in1) ~> b.add(f1 to out1) }.run() - FlowGraph { implicit b ⇒ - import FlowGraphImplicits._ - in1.via(f1) ~> f2.to(out1) + Graph.closed() { implicit b ⇒ + import Graph.Implicits._ + b.add(in1 via f1) ~> b.add(out1) }.run() - } - - "build all combinations with implicits" when { - - "Source is connected directly" in { - PartialFlowGraph { implicit b ⇒ - import FlowGraphImplicits._ - Source.empty[Int] ~> Flow[Int] - Source.empty[Int] ~> Broadcast[Int] - Source.empty[Int] ~> Sink.ignore - Source.empty[Int] ~> UndefinedSink[Int] - } - } - - "Source is connected through flow" in { - PartialFlowGraph { implicit b ⇒ - import FlowGraphImplicits._ - Source.empty[Int] ~> Flow[Int] ~> Flow[Int] - Source.empty[Int] ~> Flow[Int] ~> Broadcast[Int] - Source.empty[Int] ~> Flow[Int] ~> Sink.ignore - Source.empty[Int] ~> Flow[Int] ~> UndefinedSink[Int] - } - } - - "Junction is connected directly" in { - PartialFlowGraph { implicit b ⇒ - import FlowGraphImplicits._ - Broadcast[Int] ~> Flow[Int] - Broadcast[Int] ~> Broadcast[Int] - Broadcast[Int] ~> Sink.ignore - Broadcast[Int] ~> UndefinedSink[Int] - } - } - - "Junction is connected through flow" in { - PartialFlowGraph { implicit b ⇒ - import FlowGraphImplicits._ - Broadcast[Int] ~> Flow[Int] ~> Flow[Int] - Broadcast[Int] ~> Flow[Int] ~> Broadcast[Int] - Broadcast[Int] ~> Flow[Int] ~> Sink.ignore - Broadcast[Int] ~> Flow[Int] ~> UndefinedSink[Int] - } - } - - "Junction is connected through GraphFlow" in { - val gflow = Flow[Int, String]() { implicit builder ⇒ - import FlowGraphImplicits._ - - val in = UndefinedSource[Int] - val out = UndefinedSink[String] - - in ~> Flow[Int].map(_.toString) ~> out - - (in, out) - } - - val sink = Sink.fold[Int, Int](0)(_ + _) - val graph = FlowGraph { implicit builder ⇒ - import FlowGraphImplicits._ - - val merge = Merge[Int] - - Source(List(1, 2, 3)) ~> merge - Source.empty[Int] ~> merge - merge ~> gflow.map(_.toInt) ~> sink - } - - graph.run() - } - - "UndefinedSource is connected directly" in { - PartialFlowGraph { implicit b ⇒ - import FlowGraphImplicits._ - UndefinedSource[Int] ~> Flow[Int] - UndefinedSource[Int] ~> Broadcast[Int] - UndefinedSource[Int] ~> Sink.ignore - UndefinedSource[Int] ~> UndefinedSink[Int] - } - } - - "UndefinedSource is connected through flow" in { - PartialFlowGraph { implicit b ⇒ - import FlowGraphImplicits._ - UndefinedSource[Int] ~> Flow[Int] ~> Flow[Int] - UndefinedSource[Int] ~> Flow[Int] ~> Broadcast[Int] - UndefinedSource[Int] ~> Flow[Int] ~> Sink.ignore - UndefinedSource[Int] ~> Flow[Int] ~> UndefinedSink[Int] - } - } - - } - - "build partial with only undefined sources and sinks" in { - PartialFlowGraph { b ⇒ - b.addEdge(UndefinedSource[String], f1, UndefinedSink[String]) - } - PartialFlowGraph { b ⇒ - b.addEdge(UndefinedSource[String], f1, out1) - } - PartialFlowGraph { b ⇒ - b.addEdge(in1, f1, UndefinedSink[String]) - } - } - - "support interconnect between two partial flow graphs" in { - val output1 = UndefinedSink[String] - val output2 = UndefinedSink[String] - val partial1 = PartialFlowGraph { implicit b ⇒ - import FlowGraphImplicits._ - val bcast = Broadcast[String] - in1 ~> bcast ~> output1 - bcast ~> output2 - } - - val input1 = UndefinedSource[String] - val input2 = UndefinedSource[String] - val partial2 = PartialFlowGraph { implicit b ⇒ - import FlowGraphImplicits._ - val merge = Merge[String] - input1 ~> merge ~> out1 - input2 ~> merge - } - - FlowGraph { b ⇒ - b.importPartialGraph.closed(partial1) - b.importPartialGraph.closed(partial2) - b.connect(output1, f1, input1) - b.connect(output2, f2, input2) + Graph.closed() { implicit b ⇒ + import Graph.Implicits._ + b.add(in1 via f1) ~> b.add(f2 to out1) }.run() } diff --git a/akka-stream/src/main/scala/akka/stream/Graph.scala b/akka-stream/src/main/scala/akka/stream/Graph.scala index 62f34c632e7..790e27e7779 100644 --- a/akka-stream/src/main/scala/akka/stream/Graph.scala +++ b/akka-stream/src/main/scala/akka/stream/Graph.scala @@ -16,7 +16,7 @@ trait Graph[+S <: Shape, +M] { def shape: S /** * INTERNAL API. - * + * * Every materializable element must be backed by a stream layout module */ private[stream] def module: StreamLayout.Module diff --git a/akka-stream/src/main/scala/akka/stream/Shape.scala b/akka-stream/src/main/scala/akka/stream/Shape.scala index 30ad86e7be9..13bea32e9d6 100644 --- a/akka-stream/src/main/scala/akka/stream/Shape.scala +++ b/akka-stream/src/main/scala/akka/stream/Shape.scala @@ -17,7 +17,7 @@ abstract class Shape { * Scala API: get a list of all input ports */ def inlets: immutable.Seq[Inlet[_]] - + /** * Scala API: get a list of all output ports */ @@ -29,12 +29,12 @@ abstract class Shape { * type system. */ def deepCopy(): Shape - + /** * Java API: get a list of all input ports */ def getInlets: java.util.List[Inlet[_]] = inlets.asJava - + /** * Java API: get a list of all output ports */ @@ -47,10 +47,10 @@ abstract class Shape { abstract class AbstractShape extends Shape { def allInlets: java.util.List[Inlet[_]] def allOutlets: java.util.List[Outlet[_]] - + final override lazy val inlets = allInlets.asScala.toList final override lazy val outlets = allOutlets.asScala.toList - + final override def getInlets = allInlets final override def getOutlets = allOutlets } @@ -59,7 +59,7 @@ object EmptyShape extends Shape { override val inlets = Nil override val outlets = Nil override def deepCopy() = this - + /** * Java API: obtain EmptyShape instance */ @@ -68,9 +68,8 @@ object EmptyShape extends Shape { case class AmorphousShape(inlets: immutable.Seq[Inlet[_]], outlets: immutable.Seq[Outlet[_]]) extends Shape { override def deepCopy() = AmorphousShape( - inlets.map(i => new Inlet[Any](i.toString)), - outlets.map(o => new Outlet[Any](o.toString)) - ) + inlets.map(i ⇒ new Inlet[Any](i.toString)), + outlets.map(o ⇒ new Outlet[Any](o.toString))) } final case class SourceShape[+T](outlet: Outlet[T]) extends Shape { diff --git a/akka-stream/src/main/scala/akka/stream/impl/Flows.scala b/akka-stream/src/main/scala/akka/stream/impl/Flows.scala index 4a976e7f48d..013e3196008 100644 --- a/akka-stream/src/main/scala/akka/stream/impl/Flows.scala +++ b/akka-stream/src/main/scala/akka/stream/impl/Flows.scala @@ -9,7 +9,7 @@ trait FlowModule[In, Out, Mat] extends StreamLayout.Module { override def subModules = Set.empty override def downstreams = Map.empty override def upstreams = Map.empty - + val inPort = new Inlet[In]("Flow.in") val outPort = new Outlet[Out]("Flow.out") override val shape = new FlowShape(inPort, outPort) diff --git a/akka-stream/src/main/scala/akka/stream/impl/Sources.scala b/akka-stream/src/main/scala/akka/stream/impl/Sources.scala index 5dca396e5f0..db54356cf43 100644 --- a/akka-stream/src/main/scala/akka/stream/impl/Sources.scala +++ b/akka-stream/src/main/scala/akka/stream/impl/Sources.scala @@ -20,7 +20,7 @@ import akka.stream.{ Inlet, Outlet, InPort, OutPort } import akka.stream.SourceShape trait SourceModule[+Out, +Mat] extends Module { - + val outPort: Outlet[Out] = new Outlet[Out]("Source.out") override val shape = new SourceShape[Out](outPort) diff --git a/akka-stream/src/main/scala/akka/stream/impl/StreamLayout.scala b/akka-stream/src/main/scala/akka/stream/impl/StreamLayout.scala index 05356a85419..3613722fc40 100644 --- a/akka-stream/src/main/scala/akka/stream/impl/StreamLayout.scala +++ b/akka-stream/src/main/scala/akka/stream/impl/StreamLayout.scala @@ -27,8 +27,8 @@ private[akka] object StreamLayout { trait Module { def shape: Shape - final val inPorts: Set[InPort] = shape.inlets.toSet - final val outPorts: Set[OutPort] = shape.outlets.toSet + final lazy val inPorts: Set[InPort] = shape.inlets.toSet + final lazy val outPorts: Set[OutPort] = shape.outlets.toSet def isRunnable: Boolean = inPorts.isEmpty && outPorts.isEmpty def isSink: Boolean = (inPorts.size == 1) && outPorts.isEmpty diff --git a/akka-stream/src/main/scala/akka/stream/javadsl/FlexiMerge.scala b/akka-stream/src/main/scala/akka/stream/javadsl/FlexiMerge.scala index 601ed3e065d..42c77c61154 100644 --- a/akka-stream/src/main/scala/akka/stream/javadsl/FlexiMerge.scala +++ b/akka-stream/src/main/scala/akka/stream/javadsl/FlexiMerge.scala @@ -269,7 +269,7 @@ object FlexiMerge { } } - + private def toAnyRefSeq(l: JList[InPort]) = immutableIndexedSeq(l).asInstanceOf[immutable.Seq[Inlet[AnyRef]]] def convertReadCondition(condition: ReadCondition): scaladsl.FlexiMerge.ReadCondition[AnyRef] = { diff --git a/akka-stream/src/main/scala/akka/stream/javadsl/FlexiRoute.scala b/akka-stream/src/main/scala/akka/stream/javadsl/FlexiRoute.scala index e387911b7ec..d23b4bae24a 100644 --- a/akka-stream/src/main/scala/akka/stream/javadsl/FlexiRoute.scala +++ b/akka-stream/src/main/scala/akka/stream/javadsl/FlexiRoute.scala @@ -242,7 +242,7 @@ object FlexiRoute { } } - + private def toAnyRefSeq(l: JList[OutPort]) = immutableIndexedSeq(l).asInstanceOf[immutable.Seq[Outlet[AnyRef]]] def convertDemandCondition[T](condition: DemandCondition[T]): scaladsl.FlexiRoute.DemandCondition[T] = diff --git a/akka-stream/src/main/scala/akka/stream/javadsl/Graph.scala b/akka-stream/src/main/scala/akka/stream/javadsl/Graph.scala index 164d6305605..fa09e3fbf90 100644 --- a/akka-stream/src/main/scala/akka/stream/javadsl/Graph.scala +++ b/akka-stream/src/main/scala/akka/stream/javadsl/Graph.scala @@ -175,7 +175,7 @@ object Balance { object Zip { import akka.stream.javadsl.japi.Function2 import akka.japi.Pair - + /** * Create a new `ZipWith` vertex with the specified input types and zipping-function * which creates `akka.japi.Pair`s. diff --git a/akka-stream/src/main/scala/akka/stream/javadsl/StreamTcp.scala b/akka-stream/src/main/scala/akka/stream/javadsl/StreamTcp.scala index 69d679f00cf..cc03b19f783 100644 --- a/akka-stream/src/main/scala/akka/stream/javadsl/StreamTcp.scala +++ b/akka-stream/src/main/scala/akka/stream/javadsl/StreamTcp.scala @@ -105,14 +105,14 @@ class StreamTcp(system: ExtendedActorSystem) extends akka.actor.Extension { backlog: Int, options: JIterable[SocketOption], idleTimeout: Duration): Source[IncomingConnection, Future[ServerBinding]] = ??? -// Source.adapt(delegate.bind(endpoint, backlog, immutableSeq(options), idleTimeout)) + // Source.adapt(delegate.bind(endpoint, backlog, immutableSeq(options), idleTimeout)) /** * Creates a [[StreamTcp.ServerBinding]] without specifying options. * It represents a prospective TCP server binding on the given `endpoint`. */ def bind(endpoint: InetSocketAddress): Source[IncomingConnection, Future[ServerBinding]] = ??? -// Source.adapt(delegate.bind(endpoint)) + // Source.adapt(delegate.bind(endpoint)) /** * Creates an [[StreamTcp.OutgoingConnection]] instance representing a prospective TCP client connection to the given endpoint. @@ -122,13 +122,13 @@ class StreamTcp(system: ExtendedActorSystem) extends akka.actor.Extension { options: JIterable[SocketOption], connectTimeout: Duration, idleTimeout: Duration): Flow[ByteString, ByteString, Future[OutgoingConnection]] = ??? -// Flow.adapt(delegate.outgoingConnection(remoteAddress, localAddress, immutableSeq(options), connectTimeout, idleTimeout)) + // Flow.adapt(delegate.outgoingConnection(remoteAddress, localAddress, immutableSeq(options), connectTimeout, idleTimeout)) /** * Creates an [[StreamTcp.OutgoingConnection]] without specifying options. * It represents a prospective TCP client connection to the given endpoint. */ def outgoingConnection(remoteAddress: InetSocketAddress): Flow[ByteString, ByteString, Future[OutgoingConnection]] = ??? -// Flow.adapt(delegate.outgoingConnection(remoteAddress)) + // Flow.adapt(delegate.outgoingConnection(remoteAddress)) } diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiMerge.scala b/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiMerge.scala index f63dd9edb6f..fca03571d5a 100644 --- a/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiMerge.scala +++ b/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiMerge.scala @@ -218,7 +218,7 @@ object FlexiMerge { */ abstract class FlexiMerge[Out, S <: Shape](val shape: S, attributes: OperationAttributes) extends Graph[S, Unit] { val module: StreamLayout.Module = new FlexiMergeModule(shape, createMergeLogic) - + type PortT = S def createMergeLogic(s: S): MergeLogic[Out] diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiRoute.scala b/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiRoute.scala index 1086884591c..99e8039cafe 100644 --- a/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiRoute.scala +++ b/akka-stream/src/main/scala/akka/stream/scaladsl/FlexiRoute.scala @@ -193,7 +193,7 @@ abstract class FlexiRoute[In, S <: Shape](val shape: S, attributes: OperationAtt import akka.stream.scaladsl.FlexiRoute._ val module: StreamLayout.Module = new FlexiRouteModule(shape, createRouteLogic) - + /** * This allows a type-safe mini-DSL for selecting one of several ports, very useful in * conjunction with DemandFromAny(...): @@ -221,7 +221,7 @@ abstract class FlexiRoute[In, S <: Shape](val shape: S, attributes: OperationAtt def all: Iterable[Outlet[T]] = ports.values } } - + type PortT = S /** diff --git a/akka-stream/src/main/scala/akka/stream/scaladsl/Graph.scala b/akka-stream/src/main/scala/akka/stream/scaladsl/Graph.scala index 3ee52741984..cb61fe70764 100644 --- a/akka-stream/src/main/scala/akka/stream/scaladsl/Graph.scala +++ b/akka-stream/src/main/scala/akka/stream/scaladsl/Graph.scala @@ -113,6 +113,9 @@ object Graph extends GraphApply { */ def add[S <: Shape](graph: Graph[S, _]): S = importGraph(graph, Keep.left) + def add[T](s: Source[T, _]): Outlet[T] = importGraph(s, Keep.left).outlet + def add[T](s: Sink[T, _]): Inlet[T] = importGraph(s, Keep.left).inlet + // Assumes that junction is a new instance, so no copying needed here private[stream] def addModule(module: Module): Unit = { moduleInProgress = moduleInProgress.grow(module)