!str make Inlet/Outlet invariant and add Java variance

This necessitates the removal of method overloading in the Java Graph
DSL: the to() and via() methods were not otherwise resolved correctly by
javac, leading to incomprehensible error messages. The new approach is
to offer just one way of doing things which is a bit more verbose but
should be easier to read and learn. In this vein auto-importing while
using the DSL is also gone for Java—not sure about Scala yet.
This commit is contained in:
Roland Kuhn 2015-10-08 12:12:35 +02:00
parent c6a5864e25
commit dc07fd250c
23 changed files with 111 additions and 1649 deletions

View file

@ -6,8 +6,8 @@ package docs.http.scaladsl.server
package directives
import akka.event.Logging
import akka.http.scaladsl.model.{HttpRequest, HttpResponse}
import akka.http.scaladsl.server.directives.{DebuggingDirectives, LogEntry, LoggingMagnet}
import akka.http.scaladsl.model.{ HttpRequest, HttpResponse }
import akka.http.scaladsl.server.directives.{ DebuggingDirectives, LogEntry, LoggingMagnet }
class DebuggingDirectivesExamplesSpec extends RoutingSpec {
"logRequest-0" in {

View file

@ -30,7 +30,6 @@ class RangeDirectivesExamplesSpec extends RoutingSpec {
responseAs[String] shouldEqual "DE"
}
// we set "akka.http.routing.range-coalescing-threshold = 2"
// above to make sure we get two BodyParts
Get() ~> addHeader(Range(ByteRange(0, 1), ByteRange(1, 2), ByteRange(6, 7))) ~> route ~> check {

View file

@ -50,7 +50,7 @@ object BidiFlowDocSpec {
// construct and add the bottom flow, going inbound
val inbound = b.add(Flow[ByteString].map(fromBytes))
// fuse them together into a BidiShape
BidiShape(outbound, inbound)
BidiShape.fromFlows(outbound, inbound)
}
// this is the same as the above
@ -112,18 +112,18 @@ object BidiFlowDocSpec {
val outbound = b.add(Flow[ByteString].map(addLengthHeader))
val inbound = b.add(Flow[ByteString].transform(() => new FrameParser))
BidiShape(outbound, inbound)
BidiShape.fromFlows(outbound, inbound)
}
//#framing
val chopUp = BidiFlow() { b =>
val f = Flow[ByteString].mapConcat(_.map(ByteString(_)))
BidiShape(b.add(f), b.add(f))
BidiShape.fromFlows(b.add(f), b.add(f))
}
val accumulate = BidiFlow() { b =>
val f = Flow[ByteString].grouped(1000).map(_.fold(ByteString.empty)(_ ++ _))
BidiShape(b.add(f), b.add(f))
BidiShape.fromFlows(b.add(f), b.add(f))
}
}

View file

@ -1,291 +0,0 @@
/**
* Copyright (C) 2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.stream
import akka.stream._
import akka.stream.scaladsl._
import akka.stream.testkit.AkkaSpec
import scala.concurrent.Await
import scala.concurrent.duration._
import scala.util.control.NoStackTrace
import akka.stream.Attributes
object FlexiDocSpec {
//#fleximerge-zip-states
//#fleximerge-zip-readall
import akka.stream.FanInShape._
class ZipPorts[A, B](_init: Init[(A, B)] = Name("Zip"))
extends FanInShape[(A, B)](_init) {
val left = newInlet[A]("left")
val right = newInlet[B]("right")
protected override def construct(i: Init[(A, B)]) = new ZipPorts(i)
}
//#fleximerge-zip-readall
//#fleximerge-zip-states
}
class FlexiDocSpec extends AkkaSpec {
import FlexiDocSpec._
implicit val ec = system.dispatcher
implicit val mat = ActorMaterializer()
"implement zip using readall" in {
//#fleximerge-zip-readall
class Zip[A, B] extends FlexiMerge[(A, B), ZipPorts[A, B]](
new ZipPorts, Attributes.name("Zip1State")) {
import FlexiMerge._
override def createMergeLogic(p: PortT) = new MergeLogic[(A, B)] {
override def initialState =
State(ReadAll(p.left, p.right)) { (ctx, _, inputs) =>
val a = inputs(p.left)
val b = inputs(p.right)
ctx.emit((a, b))
SameState
}
override def initialCompletionHandling = eagerClose
}
}
//#fleximerge-zip-readall
//format: OFF
val res =
//#fleximerge-zip-connecting
FlowGraph.closed(Sink.head[(Int, String)]) { implicit b =>
o =>
import FlowGraph.Implicits._
val zip = b.add(new Zip[Int, String])
Source.single(1) ~> zip.left
Source.single("1") ~> zip.right
zip.out ~> o.inlet
}
//#fleximerge-zip-connecting
.run()
//format: ON
Await.result(res, 300.millis) should equal((1, "1"))
}
"implement zip using two states" in {
//#fleximerge-zip-states
class Zip[A, B] extends FlexiMerge[(A, B), ZipPorts[A, B]](
new ZipPorts, Attributes.name("Zip2State")) {
import FlexiMerge._
override def createMergeLogic(p: PortT) = new MergeLogic[(A, B)] {
var lastInA: A = _
val readA: State[A] = State[A](Read(p.left)) { (ctx, input, element) =>
lastInA = element
readB
}
val readB: State[B] = State[B](Read(p.right)) { (ctx, input, element) =>
ctx.emit((lastInA, element))
readA
}
override def initialState: State[_] = readA
override def initialCompletionHandling = eagerClose
}
}
//#fleximerge-zip-states
val res = FlowGraph.closed(Sink.head[(Int, String)]) { implicit b =>
o =>
import FlowGraph.Implicits._
val zip = b.add(new Zip[Int, String])
Source(1 to 2) ~> zip.left
Source((1 to 2).map(_.toString)) ~> zip.right
zip.out ~> o.inlet
}.run()
Await.result(res, 300.millis) should equal((1, "1"))
}
"fleximerge completion handling" in {
import FanInShape._
//#fleximerge-completion
class ImportantWithBackupShape[A](_init: Init[A] = Name("Zip"))
extends FanInShape[A](_init) {
val important = newInlet[A]("important")
val replica1 = newInlet[A]("replica1")
val replica2 = newInlet[A]("replica2")
protected override def construct(i: Init[A]) =
new ImportantWithBackupShape(i)
}
class ImportantWithBackups[A] extends FlexiMerge[A, ImportantWithBackupShape[A]](
new ImportantWithBackupShape, Attributes.name("ImportantWithBackups")) {
import FlexiMerge._
override def createMergeLogic(p: PortT) = new MergeLogic[A] {
import p.important
override def initialCompletionHandling =
CompletionHandling(
onUpstreamFinish = (ctx, input) => input match {
case `important` =>
log.info("Important input completed, shutting down.")
ctx.finish()
SameState
case replica =>
log.info("Replica {} completed, " +
"no more replicas available, " +
"applying eagerClose completion handling.", replica)
ctx.changeCompletionHandling(eagerClose)
SameState
},
onUpstreamFailure = (ctx, input, cause) => input match {
case `important` =>
ctx.fail(cause)
SameState
case replica =>
log.error(cause, "Replica {} failed, " +
"no more replicas available, " +
"applying eagerClose completion handling.", replica)
ctx.changeCompletionHandling(eagerClose)
SameState
})
override def initialState =
State[A](ReadAny(p.important, p.replica1, p.replica2)) {
(ctx, input, element) =>
ctx.emit(element)
SameState
}
}
}
//#fleximerge-completion
FlowGraph.closed() { implicit b =>
import FlowGraph.Implicits._
val importantWithBackups = b.add(new ImportantWithBackups[Int])
Source.single(1) ~> importantWithBackups.important
Source.single(2) ~> importantWithBackups.replica1
Source.failed[Int](new Exception("Boom!") with NoStackTrace) ~> importantWithBackups.replica2
importantWithBackups.out ~> Sink.ignore
}.run()
}
"flexi preferring merge" in {
import FanInShape._
//#flexi-preferring-merge-ports
class PreferringMergeShape[A](_init: Init[A] = Name("PreferringMerge"))
extends FanInShape[A](_init) {
val preferred = newInlet[A]("preferred")
val secondary1 = newInlet[A]("secondary1")
val secondary2 = newInlet[A]("secondary2")
protected override def construct(i: Init[A]) = new PreferringMergeShape(i)
}
//#flexi-preferring-merge-ports
//#flexi-preferring-merge
class PreferringMerge extends FlexiMerge[Int, PreferringMergeShape[Int]](
new PreferringMergeShape, Attributes.name("ImportantWithBackups")) {
import akka.stream.scaladsl.FlexiMerge._
override def createMergeLogic(p: PortT) = new MergeLogic[Int] {
override def initialState =
State[Int](ReadPreferred(p.preferred, p.secondary1, p.secondary2)) {
(ctx, input, element) =>
ctx.emit(element)
SameState
}
}
}
//#flexi-preferring-merge
}
"flexi route" in {
//#flexiroute-unzip
import FanOutShape._
class UnzipShape[A, B](_init: Init[(A, B)] = Name[(A, B)]("Unzip"))
extends FanOutShape[(A, B)](_init) {
val outA = newOutlet[A]("outA")
val outB = newOutlet[B]("outB")
protected override def construct(i: Init[(A, B)]) = new UnzipShape(i)
}
class Unzip[A, B] extends FlexiRoute[(A, B), UnzipShape[A, B]](
new UnzipShape, Attributes.name("Unzip")) {
import FlexiRoute._
override def createRouteLogic(p: PortT) = new RouteLogic[(A, B)] {
override def initialState =
State[Any](DemandFromAll(p.outA, p.outB)) {
(ctx, _, element) =>
val (a, b) = element
ctx.emit(p.outA)(a)
ctx.emit(p.outB)(b)
SameState
}
override def initialCompletionHandling = eagerClose
}
}
//#flexiroute-unzip
}
"flexi route completion handling" in {
import FanOutShape._
//#flexiroute-completion
class ImportantRouteShape[A](_init: Init[A] = Name[A]("ImportantRoute"))
extends FanOutShape[A](_init) {
val important = newOutlet[A]("important")
val additional1 = newOutlet[A]("additional1")
val additional2 = newOutlet[A]("additional2")
protected override def construct(i: Init[A]) = new ImportantRouteShape(i)
}
class ImportantRoute[A] extends FlexiRoute[A, ImportantRouteShape[A]](
new ImportantRouteShape, Attributes.name("ImportantRoute")) {
import FlexiRoute._
override def createRouteLogic(p: PortT) = new RouteLogic[A] {
import p.important
private val select = (p.important | p.additional1 | p.additional2)
override def initialCompletionHandling =
CompletionHandling(
// upstream:
onUpstreamFinish = (ctx) => (),
onUpstreamFailure = (ctx, thr) => (),
// downstream:
onDownstreamFinish = (ctx, output) => output match {
case `important` =>
// finish all downstreams, and cancel the upstream
ctx.finish()
SameState
case _ =>
SameState
})
override def initialState =
State(DemandFromAny(p.important, p.additional1, p.additional2)) {
(ctx, output, element) =>
ctx.emit(select(output))(element)
SameState
}
}
}
//#flexiroute-completion
FlowGraph.closed() { implicit b =>
import FlowGraph.Implicits._
val route = b.add(new ImportantRoute[Int])
Source.single(1) ~> route.in
route.important ~> Sink.ignore
route.additional1 ~> Sink.ignore
route.additional2 ~> Sink.ignore
}.run()
}
}

View file

@ -142,8 +142,7 @@ class FlowGraphDocSpec extends AkkaSpec {
assert(inlets.size == this.inlets.size)
assert(outlets.size == this.outlets.size)
// This is why order matters when overriding inlets and outlets.
// The "[Nothing, Any]" is equivalent to casting the Inlets/Outlets.
PriorityWorkerPoolShape[Nothing, Any](inlets(0), inlets(1), outlets(0))
PriorityWorkerPoolShape[In, Out](inlets(0).as[In], inlets(1).as[In], outlets(0).as[Out])
}
}
//#flow-graph-components-shape

View file

@ -117,7 +117,7 @@ class StreamPartialFlowGraphDocSpec extends AkkaSpec {
val sendRmotely = Sink.actorRef(actorRef, "Done")
val localProcessing = Sink.foreach[Int](_ => /* do something usefull */ ())
val sink = Sink.combine(sendRmotely, localProcessing)(Broadcast(_))
val sink = Sink.combine(sendRmotely, localProcessing)(Broadcast[Int](_))
Source(List(0, 1, 2)).runWith(sink)
//#sink-combine