Documentation improvements
* Re enabling Java tests in akka-docs (they were not run before) * Fixed bug #19764 * #19735 Rewrote every sample using the deprecated PushPullStage and friends using GraphStage * Pruned old unused graph images * Added missing graffle file for new graph images
This commit is contained in:
parent
8f3c5aa17f
commit
737991c01c
103 changed files with 1136 additions and 4749 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package docs.stream.cookbook
|
||||
|
||||
import akka.NotUsed
|
||||
import akka.stream.{ Attributes, Outlet, Inlet, FlowShape }
|
||||
import akka.stream.scaladsl.{ Flow, Sink, Source }
|
||||
import akka.util.ByteString
|
||||
|
||||
|
|
@ -18,34 +19,49 @@ class RecipeByteStrings extends RecipeSpec {
|
|||
//#bytestring-chunker
|
||||
import akka.stream.stage._
|
||||
|
||||
class Chunker(val chunkSize: Int) extends PushPullStage[ByteString, ByteString] {
|
||||
private var buffer = ByteString.empty
|
||||
class Chunker(val chunkSize: Int) extends GraphStage[FlowShape[ByteString, ByteString]] {
|
||||
val in = Inlet[ByteString]("Chunker.in")
|
||||
val out = Outlet[ByteString]("Chunker.out")
|
||||
override val shape = FlowShape.of(in, out)
|
||||
|
||||
override def onPush(elem: ByteString, ctx: Context[ByteString]): SyncDirective = {
|
||||
buffer ++= elem
|
||||
emitChunkOrPull(ctx)
|
||||
}
|
||||
override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) {
|
||||
private var buffer = ByteString.empty
|
||||
|
||||
override def onPull(ctx: Context[ByteString]): SyncDirective = emitChunkOrPull(ctx)
|
||||
setHandler(out, new OutHandler {
|
||||
override def onPull(): Unit = {
|
||||
if (isClosed(in)) emitChunk()
|
||||
else pull(in)
|
||||
}
|
||||
})
|
||||
setHandler(in, new InHandler {
|
||||
override def onPush(): Unit = {
|
||||
val elem = grab(in)
|
||||
buffer ++= elem
|
||||
emitChunk()
|
||||
}
|
||||
|
||||
override def onUpstreamFinish(ctx: Context[ByteString]): TerminationDirective =
|
||||
if (buffer.nonEmpty) ctx.absorbTermination()
|
||||
else ctx.finish()
|
||||
override def onUpstreamFinish(): Unit = {
|
||||
if (buffer.isEmpty) completeStage()
|
||||
// elements left in buffer, keep accepting downstream pulls
|
||||
// and push from buffer until buffer is emitted
|
||||
}
|
||||
})
|
||||
|
||||
private def emitChunkOrPull(ctx: Context[ByteString]): SyncDirective = {
|
||||
if (buffer.isEmpty) {
|
||||
if (ctx.isFinishing) ctx.finish()
|
||||
else ctx.pull()
|
||||
} else {
|
||||
val (emit, nextBuffer) = buffer.splitAt(chunkSize)
|
||||
buffer = nextBuffer
|
||||
ctx.push(emit)
|
||||
private def emitChunk(): Unit = {
|
||||
if (buffer.isEmpty) {
|
||||
if (isClosed(in)) completeStage()
|
||||
else pull(in)
|
||||
} else {
|
||||
val (chunk, nextBuffer) = buffer.splitAt(chunkSize)
|
||||
buffer = nextBuffer
|
||||
push(out, chunk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
val chunksStream = rawBytes.transform(() => new Chunker(ChunkLimit))
|
||||
val chunksStream = rawBytes.via(new Chunker(ChunkLimit))
|
||||
//#bytestring-chunker
|
||||
|
||||
val chunksFuture = chunksStream.limit(10).runWith(Sink.seq)
|
||||
|
|
@ -61,17 +77,31 @@ class RecipeByteStrings extends RecipeSpec {
|
|||
|
||||
//#bytes-limiter
|
||||
import akka.stream.stage._
|
||||
class ByteLimiter(val maximumBytes: Long) extends PushStage[ByteString, ByteString] {
|
||||
private var count = 0
|
||||
class ByteLimiter(val maximumBytes: Long) extends GraphStage[FlowShape[ByteString, ByteString]] {
|
||||
val in = Inlet[ByteString]("ByteLimiter.in")
|
||||
val out = Outlet[ByteString]("ByteLimiter.out")
|
||||
override val shape = FlowShape.of(in, out)
|
||||
|
||||
override def onPush(chunk: ByteString, ctx: Context[ByteString]): SyncDirective = {
|
||||
count += chunk.size
|
||||
if (count > maximumBytes) ctx.fail(new IllegalStateException("Too much bytes"))
|
||||
else ctx.push(chunk)
|
||||
override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) {
|
||||
private var count = 0
|
||||
|
||||
setHandlers(in, out, new InHandler with OutHandler {
|
||||
|
||||
override def onPull(): Unit = {
|
||||
pull(in)
|
||||
}
|
||||
|
||||
override def onPush(): Unit = {
|
||||
val chunk = grab(in)
|
||||
count += chunk.size
|
||||
if (count > maximumBytes) failStage(new IllegalStateException("Too much bytes"))
|
||||
else push(out, chunk)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
val limiter = Flow[ByteString].transform(() => new ByteLimiter(SizeLimit))
|
||||
val limiter = Flow[ByteString].via(new ByteLimiter(SizeLimit))
|
||||
//#bytes-limiter
|
||||
|
||||
val bytes1 = Source(List(ByteString(1, 2), ByteString(3), ByteString(4, 5, 6), ByteString(7, 8, 9)))
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package docs.stream.cookbook
|
|||
import java.security.MessageDigest
|
||||
|
||||
import akka.NotUsed
|
||||
import akka.stream.{ Attributes, Outlet, Inlet, FlowShape }
|
||||
import akka.stream.scaladsl.{ Sink, Source }
|
||||
import akka.util.ByteString
|
||||
|
||||
|
|
@ -21,28 +22,36 @@ class RecipeDigest extends RecipeSpec {
|
|||
|
||||
//#calculating-digest
|
||||
import akka.stream.stage._
|
||||
def digestCalculator(algorithm: String) = new PushPullStage[ByteString, ByteString] {
|
||||
val digest = MessageDigest.getInstance(algorithm)
|
||||
class DigestCalculator(algorithm: String) extends GraphStage[FlowShape[ByteString, ByteString]] {
|
||||
val in = Inlet[ByteString]("DigestCalculator.in")
|
||||
val out = Outlet[ByteString]("DigestCalculator.out")
|
||||
override val shape = FlowShape.of(in, out)
|
||||
|
||||
override def onPush(chunk: ByteString, ctx: Context[ByteString]): SyncDirective = {
|
||||
digest.update(chunk.toArray)
|
||||
ctx.pull()
|
||||
}
|
||||
override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) {
|
||||
val digest = MessageDigest.getInstance(algorithm)
|
||||
|
||||
override def onPull(ctx: Context[ByteString]): SyncDirective = {
|
||||
if (ctx.isFinishing) ctx.pushAndFinish(ByteString(digest.digest()))
|
||||
else ctx.pull()
|
||||
}
|
||||
setHandler(out, new OutHandler {
|
||||
override def onPull(): Trigger = {
|
||||
pull(in)
|
||||
}
|
||||
})
|
||||
|
||||
setHandler(in, new InHandler {
|
||||
override def onPush(): Trigger = {
|
||||
val chunk = grab(in)
|
||||
digest.update(chunk.toArray)
|
||||
pull(in)
|
||||
}
|
||||
|
||||
override def onUpstreamFinish(): Unit = {
|
||||
emit(out, ByteString(digest.digest()))
|
||||
completeStage()
|
||||
}
|
||||
})
|
||||
|
||||
override def onUpstreamFinish(ctx: Context[ByteString]): TerminationDirective = {
|
||||
// If the stream is finished, we need to emit the last element in the onPull block.
|
||||
// It is not allowed to directly emit elements from a termination block
|
||||
// (onUpstreamFinish or onUpstreamFailure)
|
||||
ctx.absorbTermination()
|
||||
}
|
||||
}
|
||||
|
||||
val digest: Source[ByteString, NotUsed] = data.transform(() => digestCalculator("SHA-256"))
|
||||
val digest: Source[ByteString, NotUsed] = data.via(new DigestCalculator("SHA-256"))
|
||||
//#calculating-digest
|
||||
|
||||
Await.result(digest.runWith(Sink.head), 3.seconds) should be(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package docs.stream.cookbook
|
||||
|
||||
import akka.stream.Attributes
|
||||
import akka.stream.scaladsl.{ Sink, Source }
|
||||
import akka.stream.testkit._
|
||||
|
||||
|
|
@ -7,40 +8,68 @@ import scala.concurrent.duration._
|
|||
|
||||
object HoldOps {
|
||||
//#hold-version-1
|
||||
import akka.stream._
|
||||
import akka.stream.stage._
|
||||
class HoldWithInitial[T](initial: T) extends DetachedStage[T, T] {
|
||||
private var currentValue: T = initial
|
||||
final class HoldWithInitial[T](initial: T) extends GraphStage[FlowShape[T, T]] {
|
||||
val in = Inlet[T]("HoldWithInitial.in")
|
||||
val out = Outlet[T]("HoldWithInitial.out")
|
||||
|
||||
override def onPush(elem: T, ctx: DetachedContext[T]): UpstreamDirective = {
|
||||
currentValue = elem
|
||||
ctx.pull()
|
||||
}
|
||||
override val shape = FlowShape.of(in, out)
|
||||
|
||||
override def onPull(ctx: DetachedContext[T]): DownstreamDirective = {
|
||||
ctx.push(currentValue)
|
||||
override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) {
|
||||
private var currentValue: T = initial
|
||||
|
||||
setHandlers(in, out, new InHandler with OutHandler {
|
||||
override def onPush(): Unit = {
|
||||
currentValue = grab(in)
|
||||
pull(in)
|
||||
}
|
||||
|
||||
override def onPull(): Unit = {
|
||||
push(out, currentValue)
|
||||
}
|
||||
})
|
||||
|
||||
override def preStart(): Unit = {
|
||||
pull(in)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//#hold-version-1
|
||||
|
||||
//#hold-version-2
|
||||
import akka.stream._
|
||||
import akka.stream.stage._
|
||||
class HoldWithWait[T] extends DetachedStage[T, T] {
|
||||
private var currentValue: T = _
|
||||
private var waitingFirstValue = true
|
||||
final class HoldWithWait[T] extends GraphStage[FlowShape[T, T]] {
|
||||
val in = Inlet[T]("HoldWithWait.in")
|
||||
val out = Outlet[T]("HoldWithWait.out")
|
||||
|
||||
override def onPush(elem: T, ctx: DetachedContext[T]): UpstreamDirective = {
|
||||
currentValue = elem
|
||||
waitingFirstValue = false
|
||||
if (ctx.isHoldingDownstream) ctx.pushAndPull(currentValue)
|
||||
else ctx.pull()
|
||||
override val shape = FlowShape.of(in, out)
|
||||
|
||||
override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) {
|
||||
private var currentValue: T = _
|
||||
private var waitingFirstValue = true
|
||||
|
||||
setHandlers(in, out, new InHandler with OutHandler {
|
||||
override def onPush(): Unit = {
|
||||
currentValue = grab(in)
|
||||
if (waitingFirstValue) {
|
||||
waitingFirstValue = false
|
||||
if (isAvailable(out)) push(out, currentValue)
|
||||
}
|
||||
pull(in)
|
||||
}
|
||||
|
||||
override def onPull(): Unit = {
|
||||
if (!waitingFirstValue) push(out, currentValue)
|
||||
}
|
||||
})
|
||||
|
||||
override def preStart(): Unit = {
|
||||
pull(in)
|
||||
}
|
||||
}
|
||||
|
||||
override def onPull(ctx: DetachedContext[T]): DownstreamDirective = {
|
||||
if (waitingFirstValue) ctx.holdDownstream()
|
||||
else ctx.push(currentValue)
|
||||
}
|
||||
|
||||
}
|
||||
//#hold-version-2
|
||||
}
|
||||
|
|
@ -57,7 +86,9 @@ class RecipeHold extends RecipeSpec {
|
|||
val source = Source.fromPublisher(pub)
|
||||
val sink = Sink.fromSubscriber(sub)
|
||||
|
||||
source.transform(() => new HoldWithInitial(0)).to(sink).run()
|
||||
source.via(new HoldWithInitial(0)).to(sink)
|
||||
.withAttributes(Attributes.inputBuffer(1, 1))
|
||||
.run()
|
||||
|
||||
val subscription = sub.expectSubscription()
|
||||
sub.expectNoMsg(100.millis)
|
||||
|
|
@ -87,7 +118,7 @@ class RecipeHold extends RecipeSpec {
|
|||
val source = Source.fromPublisher(pub)
|
||||
val sink = Sink.fromSubscriber(sub)
|
||||
|
||||
source.transform(() => new HoldWithWait).to(sink).run()
|
||||
source.via(new HoldWithWait).to(sink).run()
|
||||
|
||||
val subscription = sub.expectSubscription()
|
||||
sub.expectNoMsg(100.millis)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue