diff --git a/akka-actor/src/main/scala/akka/routing/RouterConfig.scala b/akka-actor/src/main/scala/akka/routing/RouterConfig.scala index bf187c237c..a32142642c 100644 --- a/akka-actor/src/main/scala/akka/routing/RouterConfig.scala +++ b/akka-actor/src/main/scala/akka/routing/RouterConfig.scala @@ -3,6 +3,8 @@ */ package akka.routing +import akka.util.Collections.EmptyImmutableSeq + import scala.collection.immutable import akka.ConfigurationException import akka.actor.Actor @@ -40,6 +42,7 @@ trait RouterConfig extends Serializable { /** * Create the actual router, responsible for routing messages to routees. + * * @param system the ActorSystem this router belongs to */ def createRouter(system: ActorSystem): Router @@ -132,7 +135,11 @@ abstract class GroupBase extends Group { def getPaths: java.lang.Iterable[String] = null @deprecated("Use paths with ActorSystem parameter instead", "2.4") - override final def paths: immutable.Iterable[String] = immutableSeq(getPaths) + override final def paths: immutable.Iterable[String] = { + val tmp = getPaths + if (tmp != null) immutableSeq(tmp) + else null + } def getPaths(system: ActorSystem): java.lang.Iterable[String] diff --git a/akka-docs/rst/images/graph_stage_diagrams.graffle b/akka-docs/rst/images/graph_stage_diagrams.graffle new file mode 100644 index 0000000000..957e6e8478 Binary files /dev/null and b/akka-docs/rst/images/graph_stage_diagrams.graffle differ diff --git a/akka-docs/rst/images/stage_chain.png b/akka-docs/rst/images/stage_chain.png deleted file mode 100644 index 4a97712093..0000000000 Binary files a/akka-docs/rst/images/stage_chain.png and /dev/null differ diff --git a/akka-docs/rst/images/stage_conceptual.png b/akka-docs/rst/images/stage_conceptual.png deleted file mode 100644 index 1cec7e33f9..0000000000 Binary files a/akka-docs/rst/images/stage_conceptual.png and /dev/null differ diff --git a/akka-docs/rst/images/stage_doubler.png b/akka-docs/rst/images/stage_doubler.png deleted file mode 100644 index c2495f969b..0000000000 Binary files a/akka-docs/rst/images/stage_doubler.png and /dev/null differ diff --git a/akka-docs/rst/images/stage_filter.png b/akka-docs/rst/images/stage_filter.png deleted file mode 100644 index 743b675dd0..0000000000 Binary files a/akka-docs/rst/images/stage_filter.png and /dev/null differ diff --git a/akka-docs/rst/images/stage_map.png b/akka-docs/rst/images/stage_map.png deleted file mode 100644 index 8d04b1be44..0000000000 Binary files a/akka-docs/rst/images/stage_map.png and /dev/null differ diff --git a/akka-docs/rst/images/stage_msc_absorb_1.png b/akka-docs/rst/images/stage_msc_absorb_1.png deleted file mode 100644 index 7b0aa27db6..0000000000 Binary files a/akka-docs/rst/images/stage_msc_absorb_1.png and /dev/null differ diff --git a/akka-docs/rst/images/stage_msc_absorb_2.png b/akka-docs/rst/images/stage_msc_absorb_2.png deleted file mode 100644 index a54ea66b6a..0000000000 Binary files a/akka-docs/rst/images/stage_msc_absorb_2.png and /dev/null differ diff --git a/akka-docs/rst/images/stage_msc_buffer.png b/akka-docs/rst/images/stage_msc_buffer.png deleted file mode 100644 index f6507381fb..0000000000 Binary files a/akka-docs/rst/images/stage_msc_buffer.png and /dev/null differ diff --git a/akka-docs/rst/images/stage_msc_general.png b/akka-docs/rst/images/stage_msc_general.png deleted file mode 100644 index 583fb91318..0000000000 Binary files a/akka-docs/rst/images/stage_msc_general.png and /dev/null differ diff --git a/akka-docs/rst/images/stages.svg b/akka-docs/rst/images/stages.svg deleted file mode 100644 index cbc02c63ff..0000000000 --- a/akka-docs/rst/images/stages.svg +++ /dev/null @@ -1,1124 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - onPush(in,ctx) - onPull(ctx) - ctx.pull() - ctx.push(out) - PushPullStage - - - - Sink - - Source - - - - - - - - - - - - - - - - onPush(in,ctx) - onPull(ctx) - ctx.pull() - ctx.push(out) - Map - - f(in) - - - - - - - - onPush(in,ctx) - onPull(ctx) - ctx.pull() - ctx.push(out) - Filter - - if p(in) - if !p(in) - - - - - - - - onPush(in,ctx) - onPull(ctx) - ctx.pull() - ctx.push(out) - Duplicator - - in - if oneLeft - if !oneLeft - - - Source - - - - - - - - - - - - - onPush(in,ctx) - onPull(ctx) - ctx.pull() - ctx.push(out) - Map - - f(in) - - - - - - - - onPush(in,ctx) - onPull(ctx) - ctx.pull() - ctx.push(out) - Duplicator - - in - if oneLeft - if !oneLeft - - - - - - - - onPush(in,ctx) - onPull(ctx) - ctx.pull() - ctx.push(out) - Filter - - if p(in) - if !p(in) - - - - Sink - - diff --git a/akka-docs/rst/images/stages_sequence_charts.svg b/akka-docs/rst/images/stages_sequence_charts.svg deleted file mode 100644 index 39e2037214..0000000000 --- a/akka-docs/rst/images/stages_sequence_charts.svg +++ /dev/null @@ -1,2310 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - ctx.pull() - onPull() - ctx.pull() - onPull() - ctx.pull() - onPull() - ctx.pull() - onPull() - ctx.pull() - onPull() - ctx.pull() - onPull() - ctx.pull() - onPull() - onPush() - ctx.push() - onPush() - ctx.push() - onPush() - ctx.push() - onPush() - ctx.push() - onPush() - ctx.push() - ... - ... - ... - ... - Source - Filter - Doubler - Sink - - - - - - - - onPull() - ctx.pull() - onPull() - ctx.pull() - onPull() - onUpstreamFinished() - ctx.finish() - ... - ... - - ctx.absorbTermination() - onPull() - - - - - ctx.pull() - onPull() - ctx.pull() - onPull() - onPush() - ctx.push() - onPush() - ctx.push() - Restoring token - - - - - - - - onPull() - ctx.pull() - onPull() - ctx.pull() - onPull() - onUpstreamFinished() - ctx.finish() - ... - ... - ctx.absorbTermination() - - - - ctx.pull() - onPull() - ctx.pull() - onPull() - onPush() - ctx.push() - - onPush() - ctx.push() - - onPush() - ctx.push() - - ctx.push() - - onPull() - - - - - - - ctx.pull() - onPull() - onPull() - onPush() - ctx.push() - ... - ... - ... - ctx.holdDownstream() - - - ctx.pushAndPull() - - onPush() - ctx.push() - - ctx.pull() - onPull() - - ctx.pull() - onPull() - - onPush() - ctx.push() - - onPush() - ctx.push() - - ctx.pull() - onPull() - - onPush() - ctx.push() - ctx.holdUpstream() - - ctx.pull() - onPull() - - - ctx.pushAndPull() - - (initialization) - 0 - 1 - - 0 - 1 - 0 - 1 - 2 - 1 - Buffer size - Buffer(capacity 2) - - diff --git a/akka-docs/rst/java/code/docs/AbstractJavaTest.scala b/akka-docs/rst/java/code/docs/AbstractJavaTest.scala new file mode 100644 index 0000000000..26c760c426 --- /dev/null +++ b/akka-docs/rst/java/code/docs/AbstractJavaTest.scala @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2016 Typesafe Inc. + */ +package docs + +import org.scalatest.junit.JUnitSuite + +/** + * Base class for all runnable example tests written in Java + */ +abstract class AbstractJavaTest extends JUnitSuite { + +} diff --git a/akka-docs/rst/java/code/docs/actor/ActorDocTest.java b/akka-docs/rst/java/code/docs/actor/ActorDocTest.java index 016e90f31b..469de97077 100644 --- a/akka-docs/rst/java/code/docs/actor/ActorDocTest.java +++ b/akka-docs/rst/java/code/docs/actor/ActorDocTest.java @@ -11,6 +11,8 @@ import akka.testkit.EventFilter; import akka.testkit.TestEvent; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; +import docs.AbstractJavaTest; +import org.scalatest.junit.JUnitSuite; import scala.PartialFunction; import scala.runtime.BoxedUnit; import static docs.actor.Messages.Swap.Swap; @@ -44,7 +46,7 @@ import scala.concurrent.Future; import static akka.pattern.Patterns.gracefulStop; //#import-graceFulStop -public class ActorDocTest { +public class ActorDocTest extends AbstractJavaTest { public static Config config = ConfigFactory.parseString( "akka {\n" + @@ -323,7 +325,7 @@ public class ActorDocTest { //#system-actorOf // ActorSystem is a heavy object: create only one per application final ActorSystem system = ActorSystem.create("MySystem", config); - final ActorRef myActor = system.actorOf(Props.create(MyActor.class), "myactor"); + final ActorRef myActor = system.actorOf(Props.create(FirstActor.class), "myactor"); //#system-actorOf try { new JavaTestKit(system) { diff --git a/akka-docs/rst/java/code/docs/actor/FSMDocTest.java b/akka-docs/rst/java/code/docs/actor/FSMDocTest.java index a624962591..ab74c21232 100644 --- a/akka-docs/rst/java/code/docs/actor/FSMDocTest.java +++ b/akka-docs/rst/java/code/docs/actor/FSMDocTest.java @@ -20,8 +20,9 @@ import akka.actor.Props; import akka.testkit.JavaTestKit; import akka.testkit.TestProbe; import akka.testkit.AkkaSpec; +import docs.AbstractJavaTest; -public class FSMDocTest { +public class FSMDocTest extends AbstractJavaTest { static //#data diff --git a/akka-docs/rst/java/code/docs/actor/FaultHandlingTest.java b/akka-docs/rst/java/code/docs/actor/FaultHandlingTest.java index e879d3e85e..c66dcce8a5 100644 --- a/akka-docs/rst/java/code/docs/actor/FaultHandlingTest.java +++ b/akka-docs/rst/java/code/docs/actor/FaultHandlingTest.java @@ -16,6 +16,7 @@ import akka.actor.OneForOneStrategy; import akka.actor.Props; import akka.actor.Terminated; import akka.actor.UntypedActor; +import docs.AbstractJavaTest; import scala.collection.immutable.Seq; import scala.concurrent.Await; import static akka.pattern.Patterns.ask; @@ -37,7 +38,7 @@ import org.junit.BeforeClass; import org.junit.AfterClass; //#testkit -public class FaultHandlingTest { +public class FaultHandlingTest extends AbstractJavaTest { //#testkit static //#supervisor diff --git a/akka-docs/rst/java/code/docs/actor/FaultHandlingTestJava8.java b/akka-docs/rst/java/code/docs/actor/FaultHandlingTestJava8.java index d1f653854a..5b5795a056 100644 --- a/akka-docs/rst/java/code/docs/actor/FaultHandlingTestJava8.java +++ b/akka-docs/rst/java/code/docs/actor/FaultHandlingTestJava8.java @@ -14,6 +14,7 @@ import akka.japi.pf.DeciderBuilder; import akka.japi.pf.ReceiveBuilder; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; +import org.scalatest.junit.JUnitSuite; import scala.PartialFunction; import scala.concurrent.Await; import static akka.pattern.Patterns.ask; @@ -35,7 +36,7 @@ import org.junit.AfterClass; import scala.runtime.BoxedUnit; //#testkit -public class FaultHandlingTestJava8 { +public class FaultHandlingTestJava8 extends JUnitSuite { //#testkit public static Config config = ConfigFactory.parseString( diff --git a/akka-docs/rst/java/code/docs/actor/InboxDocTest.java b/akka-docs/rst/java/code/docs/actor/InboxDocTest.java index 6bfcaf1d57..23ca52d77b 100644 --- a/akka-docs/rst/java/code/docs/actor/InboxDocTest.java +++ b/akka-docs/rst/java/code/docs/actor/InboxDocTest.java @@ -7,6 +7,7 @@ package docs.actor; import java.util.concurrent.TimeUnit; import akka.testkit.AkkaJUnitActorSystemResource; +import docs.AbstractJavaTest; import org.junit.ClassRule; import org.junit.Test; @@ -19,7 +20,7 @@ import akka.actor.Terminated; import akka.testkit.AkkaSpec; import akka.testkit.JavaTestKit; -public class InboxDocTest { +public class InboxDocTest extends AbstractJavaTest { @ClassRule public static AkkaJUnitActorSystemResource actorSystemResource = diff --git a/akka-docs/rst/java/code/docs/actor/InitializationDocSpecJava.java b/akka-docs/rst/java/code/docs/actor/InitializationDocSpecJava.java index 424df81f11..c9fef8c03d 100644 --- a/akka-docs/rst/java/code/docs/actor/InitializationDocSpecJava.java +++ b/akka-docs/rst/java/code/docs/actor/InitializationDocSpecJava.java @@ -7,11 +7,12 @@ import akka.actor.*; import akka.japi.Procedure; import akka.testkit.AkkaJUnitActorSystemResource; import akka.testkit.JavaTestKit; +import docs.AbstractJavaTest; import org.junit.ClassRule; import org.junit.Test; import scala.Option; -public class InitializationDocSpecJava { +public class InitializationDocSpecJava extends AbstractJavaTest { static public class PreStartInitExample extends UntypedActor { diff --git a/akka-docs/rst/java/code/docs/actor/InitializationDocTest.java b/akka-docs/rst/java/code/docs/actor/InitializationDocTest.java index fb3343a2e7..dab8144288 100644 --- a/akka-docs/rst/java/code/docs/actor/InitializationDocTest.java +++ b/akka-docs/rst/java/code/docs/actor/InitializationDocTest.java @@ -7,6 +7,7 @@ package docs.actor; import akka.actor.*; import akka.japi.pf.ReceiveBuilder; import akka.testkit.JavaTestKit; +import docs.AbstractJavaTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -17,7 +18,7 @@ import scala.concurrent.Await; import java.util.concurrent.TimeUnit; -public class InitializationDocTest { +public class InitializationDocTest extends AbstractJavaTest { static ActorSystem system = null; diff --git a/akka-docs/rst/java/code/docs/actor/SampleActorTest.java b/akka-docs/rst/java/code/docs/actor/SampleActorTest.java index d1ea16a1a6..f068390775 100644 --- a/akka-docs/rst/java/code/docs/actor/SampleActorTest.java +++ b/akka-docs/rst/java/code/docs/actor/SampleActorTest.java @@ -8,13 +8,14 @@ import akka.actor.ActorRef; import akka.actor.ActorSystem; import akka.actor.Props; import akka.testkit.JavaTestKit; +import docs.AbstractJavaTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import static org.junit.Assert.*; -public class SampleActorTest { +public class SampleActorTest extends AbstractJavaTest { static ActorSystem system; diff --git a/akka-docs/rst/java/code/docs/actor/SchedulerDocTest.java b/akka-docs/rst/java/code/docs/actor/SchedulerDocTest.java index 5cdad7acec..b58a9aa1d4 100644 --- a/akka-docs/rst/java/code/docs/actor/SchedulerDocTest.java +++ b/akka-docs/rst/java/code/docs/actor/SchedulerDocTest.java @@ -5,6 +5,7 @@ package docs.actor; //#imports1 import akka.actor.Props; +import docs.AbstractJavaTest; import scala.concurrent.duration.Duration; import java.util.concurrent.TimeUnit; //#imports1 @@ -20,7 +21,7 @@ import akka.testkit.AkkaSpec; import akka.testkit.AkkaJUnitActorSystemResource; import org.junit.*; -public class SchedulerDocTest { +public class SchedulerDocTest extends AbstractJavaTest { @Rule public AkkaJUnitActorSystemResource actorSystemResource = diff --git a/akka-docs/rst/java/code/docs/actor/TypedActorDocTest.java b/akka-docs/rst/java/code/docs/actor/TypedActorDocTest.java index 541de57881..62e048a306 100644 --- a/akka-docs/rst/java/code/docs/actor/TypedActorDocTest.java +++ b/akka-docs/rst/java/code/docs/actor/TypedActorDocTest.java @@ -10,6 +10,7 @@ import akka.actor.*; import akka.japi.*; import akka.dispatch.Futures; +import docs.AbstractJavaTest; import scala.concurrent.Await; import scala.concurrent.Future; import scala.concurrent.duration.Duration; @@ -25,7 +26,7 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; //#imports -public class TypedActorDocTest { +public class TypedActorDocTest extends AbstractJavaTest { Object someReference = null; ActorSystem system = null; diff --git a/akka-docs/rst/java/code/docs/actor/UntypedActorDocTest.java b/akka-docs/rst/java/code/docs/actor/UntypedActorDocTest.java index 0d83ce0988..c03661213d 100644 --- a/akka-docs/rst/java/code/docs/actor/UntypedActorDocTest.java +++ b/akka-docs/rst/java/code/docs/actor/UntypedActorDocTest.java @@ -18,6 +18,7 @@ import java.util.concurrent.TimeUnit; import akka.testkit.AkkaJUnitActorSystemResource; +import docs.AbstractJavaTest; import org.junit.ClassRule; import org.junit.Test; @@ -83,7 +84,7 @@ import akka.testkit.JavaTestKit; import akka.util.Timeout; //#import-ask -public class UntypedActorDocTest { +public class UntypedActorDocTest extends AbstractJavaTest { @ClassRule public static AkkaJUnitActorSystemResource actorSystemResource = diff --git a/akka-docs/rst/java/code/docs/actor/fsm/BuncherTest.java b/akka-docs/rst/java/code/docs/actor/fsm/BuncherTest.java index 678b7b8bab..8c7947c4db 100644 --- a/akka-docs/rst/java/code/docs/actor/fsm/BuncherTest.java +++ b/akka-docs/rst/java/code/docs/actor/fsm/BuncherTest.java @@ -8,6 +8,7 @@ import akka.actor.ActorRef; import akka.actor.ActorSystem; import akka.actor.Props; import akka.testkit.JavaTestKit; +import docs.AbstractJavaTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -20,7 +21,7 @@ import static docs.actor.fsm.Events.SetTarget; import static docs.actor.fsm.Events.Flush.Flush; //#test-code -public class BuncherTest { +public class BuncherTest extends AbstractJavaTest { static ActorSystem system; diff --git a/akka-docs/rst/java/code/docs/actor/fsm/FSMDocTest.java b/akka-docs/rst/java/code/docs/actor/fsm/FSMDocTest.java index 155d0c02c1..3d8a42b805 100644 --- a/akka-docs/rst/java/code/docs/actor/fsm/FSMDocTest.java +++ b/akka-docs/rst/java/code/docs/actor/fsm/FSMDocTest.java @@ -6,6 +6,7 @@ package docs.actor.fsm; import akka.actor.*; import akka.testkit.JavaTestKit; +import docs.AbstractJavaTest; import org.hamcrest.CoreMatchers; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -18,7 +19,7 @@ import static docs.actor.fsm.FSMDocTest.StateType.*; import static docs.actor.fsm.FSMDocTest.Messages.*; import static java.util.concurrent.TimeUnit.*; -public class FSMDocTest { +public class FSMDocTest extends AbstractJavaTest { static ActorSystem system; @BeforeClass diff --git a/akka-docs/rst/java/code/docs/actorlambda/ActorDocTest.java b/akka-docs/rst/java/code/docs/actorlambda/ActorDocTest.java index 242c5d56d8..1501a5c7b0 100644 --- a/akka-docs/rst/java/code/docs/actorlambda/ActorDocTest.java +++ b/akka-docs/rst/java/code/docs/actorlambda/ActorDocTest.java @@ -11,6 +11,7 @@ import akka.testkit.EventFilter; import akka.testkit.TestEvent; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; +import docs.AbstractJavaTest; import scala.PartialFunction; import scala.runtime.BoxedUnit; import static docs.actorlambda.Messages.Swap.Swap; @@ -45,7 +46,7 @@ import scala.concurrent.Future; import static akka.pattern.Patterns.gracefulStop; //#import-graceFulStop -public class ActorDocTest { +public class ActorDocTest extends AbstractJavaTest { public static Config config = ConfigFactory.parseString( "akka {\n" + diff --git a/akka-docs/rst/java/code/docs/actorlambda/FaultHandlingTest.java b/akka-docs/rst/java/code/docs/actorlambda/FaultHandlingTest.java index 579c0a61d7..a9eb90fb38 100644 --- a/akka-docs/rst/java/code/docs/actorlambda/FaultHandlingTest.java +++ b/akka-docs/rst/java/code/docs/actorlambda/FaultHandlingTest.java @@ -14,6 +14,7 @@ import akka.japi.pf.DeciderBuilder; import akka.japi.pf.ReceiveBuilder; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; +import docs.AbstractJavaTest; import scala.PartialFunction; import scala.concurrent.Await; import static akka.pattern.Patterns.ask; @@ -35,7 +36,7 @@ import org.junit.AfterClass; import scala.runtime.BoxedUnit; //#testkit -public class FaultHandlingTest { +public class FaultHandlingTest extends AbstractJavaTest { //#testkit public static Config config = ConfigFactory.parseString( diff --git a/akka-docs/rst/java/code/docs/actorlambda/InitializationDocTest.java b/akka-docs/rst/java/code/docs/actorlambda/InitializationDocTest.java index 236cd0bb42..ec0997352e 100644 --- a/akka-docs/rst/java/code/docs/actorlambda/InitializationDocTest.java +++ b/akka-docs/rst/java/code/docs/actorlambda/InitializationDocTest.java @@ -10,6 +10,7 @@ import akka.actor.Props; import akka.japi.pf.FI; import akka.japi.pf.ReceiveBuilder; import akka.testkit.JavaTestKit; +import docs.AbstractJavaTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -18,7 +19,7 @@ import scala.concurrent.duration.Duration; import java.util.concurrent.TimeUnit; -public class InitializationDocTest { +public class InitializationDocTest extends AbstractJavaTest { static ActorSystem system = null; diff --git a/akka-docs/rst/java/code/docs/actorlambda/SampleActorTest.java b/akka-docs/rst/java/code/docs/actorlambda/SampleActorTest.java index 141225570f..8bb74332a1 100644 --- a/akka-docs/rst/java/code/docs/actorlambda/SampleActorTest.java +++ b/akka-docs/rst/java/code/docs/actorlambda/SampleActorTest.java @@ -8,13 +8,14 @@ import akka.actor.ActorRef; import akka.actor.ActorSystem; import akka.actor.Props; import akka.testkit.JavaTestKit; +import docs.AbstractJavaTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import static org.junit.Assert.*; -public class SampleActorTest { +public class SampleActorTest extends AbstractJavaTest { static ActorSystem system; diff --git a/akka-docs/rst/java/code/docs/actorlambda/fsm/BuncherTest.java b/akka-docs/rst/java/code/docs/actorlambda/fsm/BuncherTest.java index fe52e96796..d1c83dbd6c 100644 --- a/akka-docs/rst/java/code/docs/actorlambda/fsm/BuncherTest.java +++ b/akka-docs/rst/java/code/docs/actorlambda/fsm/BuncherTest.java @@ -8,6 +8,7 @@ import akka.actor.ActorRef; import akka.actor.ActorSystem; import akka.actor.Props; import akka.testkit.JavaTestKit; +import docs.AbstractJavaTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -20,7 +21,7 @@ import static docs.actorlambda.fsm.Events.SetTarget; import static docs.actorlambda.fsm.Events.Flush.Flush; //#test-code -public class BuncherTest { +public class BuncherTest extends AbstractJavaTest { static ActorSystem system; diff --git a/akka-docs/rst/java/code/docs/actorlambda/fsm/FSMDocTest.java b/akka-docs/rst/java/code/docs/actorlambda/fsm/FSMDocTest.java index 8e5636198c..068ed7c9cb 100644 --- a/akka-docs/rst/java/code/docs/actorlambda/fsm/FSMDocTest.java +++ b/akka-docs/rst/java/code/docs/actorlambda/fsm/FSMDocTest.java @@ -6,6 +6,7 @@ package docs.actorlambda.fsm; import akka.actor.*; import akka.testkit.JavaTestKit; +import docs.AbstractJavaTest; import org.hamcrest.CoreMatchers; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -18,7 +19,7 @@ import static docs.actorlambda.fsm.FSMDocTest.StateType.*; import static docs.actorlambda.fsm.FSMDocTest.Messages.*; import static java.util.concurrent.TimeUnit.*; -public class FSMDocTest { +public class FSMDocTest extends AbstractJavaTest { static ActorSystem system; @BeforeClass diff --git a/akka-docs/rst/java/code/docs/camel/ActivationTestBase.java b/akka-docs/rst/java/code/docs/camel/ActivationTestBase.java index 405aa09a63..9e7b883d93 100644 --- a/akka-docs/rst/java/code/docs/camel/ActivationTestBase.java +++ b/akka-docs/rst/java/code/docs/camel/ActivationTestBase.java @@ -9,6 +9,7 @@ package docs.camel; import akka.testkit.JavaTestKit; import akka.testkit.TestKit; import akka.util.Timeout; + import docs.AbstractJavaTest; import scala.concurrent.Future; import scala.concurrent.duration.Duration; import static java.util.concurrent.TimeUnit.SECONDS; @@ -16,7 +17,7 @@ package docs.camel; import org.junit.Test; -public class ActivationTestBase { +public class ActivationTestBase extends AbstractJavaTest { @SuppressWarnings("unused") @Test diff --git a/akka-docs/rst/java/code/docs/camel/CamelExtensionTest.java b/akka-docs/rst/java/code/docs/camel/CamelExtensionTest.java index 21b48740a9..8cb3388cae 100644 --- a/akka-docs/rst/java/code/docs/camel/CamelExtensionTest.java +++ b/akka-docs/rst/java/code/docs/camel/CamelExtensionTest.java @@ -4,11 +4,12 @@ import akka.actor.ActorSystem; import akka.camel.Camel; import akka.camel.CamelExtension; import akka.testkit.JavaTestKit; +import docs.AbstractJavaTest; import org.apache.camel.CamelContext; import org.apache.camel.ProducerTemplate; import org.junit.Test; -public class CamelExtensionTest { +public class CamelExtensionTest extends AbstractJavaTest { @Test public void getCamelExtension() { //#CamelExtension diff --git a/akka-docs/rst/java/code/docs/cluster/ClusterDocTest.java b/akka-docs/rst/java/code/docs/cluster/ClusterDocTest.java index b74c8eb127..1bcdcf5ea3 100644 --- a/akka-docs/rst/java/code/docs/cluster/ClusterDocTest.java +++ b/akka-docs/rst/java/code/docs/cluster/ClusterDocTest.java @@ -4,6 +4,7 @@ package docs.cluster; import com.typesafe.config.ConfigFactory; +import docs.AbstractJavaTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -13,7 +14,7 @@ import akka.cluster.Cluster; import akka.testkit.JavaTestKit; -public class ClusterDocTest { +public class ClusterDocTest extends AbstractJavaTest { static ActorSystem system; diff --git a/akka-docs/rst/java/code/docs/ddata/DistributedDataDocTest.java b/akka-docs/rst/java/code/docs/ddata/DistributedDataDocTest.java index f69c4312f5..62bcbd5239 100644 --- a/akka-docs/rst/java/code/docs/ddata/DistributedDataDocTest.java +++ b/akka-docs/rst/java/code/docs/ddata/DistributedDataDocTest.java @@ -8,7 +8,10 @@ import java.util.Arrays; import java.util.Set; import java.math.BigInteger; import java.util.Optional; + +import akka.actor.*; import com.typesafe.config.ConfigFactory; +import docs.AbstractJavaTest; import scala.concurrent.duration.Duration; import scala.runtime.BoxedUnit; import static java.util.concurrent.TimeUnit.SECONDS; @@ -20,9 +23,6 @@ import org.junit.BeforeClass; import scala.concurrent.duration.FiniteDuration; import java.util.concurrent.ThreadLocalRandom; -import akka.actor.Actor; -import akka.actor.ActorLogging; -import akka.actor.ActorSystem; import akka.cluster.Cluster; import akka.cluster.ddata.*; import akka.japi.pf.ReceiveBuilder; @@ -33,26 +33,16 @@ import akka.testkit.AkkaSpec; import akka.testkit.ImplicitSender; import akka.testkit.JavaTestKit; import akka.testkit.TestProbe; -import akka.actor.ActorRef; import akka.serialization.SerializationExtension; -public class DistributedDataDocTest { +public class DistributedDataDocTest extends AbstractJavaTest { static ActorSystem system; void receive(PartialFunction pf) { } - - JavaTestKit probe = new JavaTestKit(system); - - ActorRef self() { - return probe.getRef(); - } - - ActorRef sender() { - return probe.getRef(); - } - + + @BeforeClass public static void setup() { system = ActorSystem.create("DistributedDataDocTest", @@ -67,180 +57,189 @@ public class DistributedDataDocTest { @Test public void demonstrateUpdate() { - probe = new JavaTestKit(system); + new JavaTestKit(system) { + { + //#update + final Cluster node = Cluster.get(system); + final ActorRef replicator = DistributedData.get(system).replicator(); - //#update - final Cluster node = Cluster.get(system); - final ActorRef replicator = DistributedData.get(system).replicator(); + final Key counter1Key = PNCounterKey.create("counter1"); + final Key> set1Key = GSetKey.create("set1"); + final Key> set2Key = ORSetKey.create("set2"); + final Key activeFlagKey = FlagKey.create("active"); - final Key counter1Key = PNCounterKey.create("counter1"); - final Key> set1Key = GSetKey.create("set1"); - final Key> set2Key = ORSetKey.create("set2"); - final Key activeFlagKey = FlagKey.create("active"); - - replicator.tell(new Replicator.Update(counter1Key, PNCounter.create(), - Replicator.writeLocal(), curr -> curr.increment(node, 1)), self()); + replicator.tell(new Replicator.Update(counter1Key, PNCounter.create(), + Replicator.writeLocal(), curr -> curr.increment(node, 1)), getTestActor()); - final WriteConsistency writeTo3 = new WriteTo(3, Duration.create(1, SECONDS)); - replicator.tell(new Replicator.Update>(set1Key, GSet.create(), - writeTo3, curr -> curr.add("hello")), self()); + final WriteConsistency writeTo3 = new WriteTo(3, Duration.create(1, SECONDS)); + replicator.tell(new Replicator.Update>(set1Key, GSet.create(), + writeTo3, curr -> curr.add("hello")), getTestActor()); - final WriteConsistency writeMajority = - new WriteMajority(Duration.create(5, SECONDS)); - replicator.tell(new Replicator.Update>(set2Key, ORSet.create(), - writeMajority, curr -> curr.add(node, "hello")), self()); + final WriteConsistency writeMajority = + new WriteMajority(Duration.create(5, SECONDS)); + replicator.tell(new Replicator.Update>(set2Key, ORSet.create(), + writeMajority, curr -> curr.add(node, "hello")), getTestActor()); - final WriteConsistency writeAll = new WriteAll(Duration.create(5, SECONDS)); - replicator.tell(new Replicator.Update(activeFlagKey, Flag.create(), - writeAll, curr -> curr.switchOn()), self()); - //#update + final WriteConsistency writeAll = new WriteAll(Duration.create(5, SECONDS)); + replicator.tell(new Replicator.Update(activeFlagKey, Flag.create(), + writeAll, curr -> curr.switchOn()), getTestActor()); + //#update - probe.expectMsgClass(UpdateSuccess.class); - //#update-response1 - receive(ReceiveBuilder. - match(UpdateSuccess.class, a -> a.key().equals(counter1Key), a -> { - // ok - }).build()); - //#update-response1 + expectMsgClass(UpdateSuccess.class); + //#update-response1 + receive(ReceiveBuilder. + match(UpdateSuccess.class, a -> a.key().equals(counter1Key), a -> { + // ok + }).build()); + //#update-response1 - //#update-response2 - receive(ReceiveBuilder. - match(UpdateSuccess.class, a -> a.key().equals(set1Key), a -> { - // ok - }). - match(UpdateTimeout.class, a -> a.key().equals(set1Key), a -> { - // write to 3 nodes failed within 1.second - }).build()); - //#update-response2 + //#update-response2 + receive(ReceiveBuilder. + match(UpdateSuccess.class, a -> a.key().equals(set1Key), a -> { + // ok + }). + match(UpdateTimeout.class, a -> a.key().equals(set1Key), a -> { + // write to 3 nodes failed within 1.second + }).build()); + //#update-response2 + }}; } @Test public void demonstrateUpdateWithRequestContext() { - probe = new JavaTestKit(system); + new JavaTestKit(system) { + { - //#update-request-context - final Cluster node = Cluster.get(system); - final ActorRef replicator = DistributedData.get(system).replicator(); + //#update-request-context + final Cluster node = Cluster.get(system); + final ActorRef replicator = DistributedData.get(system).replicator(); - final WriteConsistency writeTwo = new WriteTo(2, Duration.create(3, SECONDS)); - final Key counter1Key = PNCounterKey.create("counter1"); - - receive(ReceiveBuilder. - match(String.class, a -> a.equals("increment"), a -> { - // incoming command to increase the counter - Optional reqContext = Optional.of(sender()); - Replicator.Update upd = new Replicator.Update(counter1Key, - PNCounter.create(), writeTwo, reqContext, curr -> curr.increment(node, 1)); - replicator.tell(upd, self()); - }). - - match(UpdateSuccess.class, a -> a.key().equals(counter1Key), a -> { - ActorRef replyTo = (ActorRef) a.getRequest().get(); - replyTo.tell("ack", self()); - }). - - match(UpdateTimeout.class, a -> a.key().equals(counter1Key), a -> { - ActorRef replyTo = (ActorRef) a.getRequest().get(); - replyTo.tell("nack", self()); - }).build()); + final WriteConsistency writeTwo = new WriteTo(2, Duration.create(3, SECONDS)); + final Key counter1Key = PNCounterKey.create("counter1"); - //#update-request-context + receive(ReceiveBuilder. + match(String.class, a -> a.equals("increment"), a -> { + // incoming command to increase the counter + Optional reqContext = Optional.of(getRef()); + Replicator.Update upd = new Replicator.Update(counter1Key, + PNCounter.create(), writeTwo, reqContext, curr -> curr.increment(node, 1)); + replicator.tell(upd, getTestActor()); + }). + + match(UpdateSuccess.class, a -> a.key().equals(counter1Key), a -> { + ActorRef replyTo = (ActorRef) a.getRequest().get(); + replyTo.tell("ack", getTestActor()); + }). + + match(UpdateTimeout.class, a -> a.key().equals(counter1Key), a -> { + ActorRef replyTo = (ActorRef) a.getRequest().get(); + replyTo.tell("nack", getTestActor()); + }).build()); + + //#update-request-context + } + }; } @SuppressWarnings({ "unused", "unchecked" }) @Test public void demonstrateGet() { - probe = new JavaTestKit(system); + new JavaTestKit(system) { + { - //#get - final ActorRef replicator = DistributedData.get(system).replicator(); - final Key counter1Key = PNCounterKey.create("counter1"); - final Key> set1Key = GSetKey.create("set1"); - final Key> set2Key = ORSetKey.create("set2"); - final Key activeFlagKey = FlagKey.create("active"); + //#get + final ActorRef replicator = DistributedData.get(system).replicator(); + final Key counter1Key = PNCounterKey.create("counter1"); + final Key> set1Key = GSetKey.create("set1"); + final Key> set2Key = ORSetKey.create("set2"); + final Key activeFlagKey = FlagKey.create("active"); - replicator.tell(new Replicator.Get(counter1Key, - Replicator.readLocal()), self()); + replicator.tell(new Replicator.Get(counter1Key, + Replicator.readLocal()), getTestActor()); - final ReadConsistency readFrom3 = new ReadFrom(3, Duration.create(1, SECONDS)); - replicator.tell(new Replicator.Get>(set1Key, - readFrom3), self()); + final ReadConsistency readFrom3 = new ReadFrom(3, Duration.create(1, SECONDS)); + replicator.tell(new Replicator.Get>(set1Key, + readFrom3), getTestActor()); - final ReadConsistency readMajority = new ReadMajority(Duration.create(5, SECONDS)); - replicator.tell(new Replicator.Get>(set2Key, - readMajority), self()); + final ReadConsistency readMajority = new ReadMajority(Duration.create(5, SECONDS)); + replicator.tell(new Replicator.Get>(set2Key, + readMajority), getTestActor()); - final ReadConsistency readAll = new ReadAll(Duration.create(5, SECONDS)); - replicator.tell(new Replicator.Get(activeFlagKey, - readAll), self()); - //#get + final ReadConsistency readAll = new ReadAll(Duration.create(5, SECONDS)); + replicator.tell(new Replicator.Get(activeFlagKey, + readAll), getTestActor()); + //#get - //#get-response1 - receive(ReceiveBuilder. - match(GetSuccess.class, a -> a.key().equals(counter1Key), a -> { - GetSuccess g = a; - BigInteger value = g.dataValue().getValue(); - }). - match(NotFound.class, a -> a.key().equals(counter1Key), a -> { - // key counter1 does not exist - }).build()); - //#get-response1 + //#get-response1 + receive(ReceiveBuilder. + match(GetSuccess.class, a -> a.key().equals(counter1Key), a -> { + GetSuccess g = a; + BigInteger value = g.dataValue().getValue(); + }). + match(NotFound.class, a -> a.key().equals(counter1Key), a -> { + // key counter1 does not exist + }).build()); + //#get-response1 - //#get-response2 - receive(ReceiveBuilder. - match(GetSuccess.class, a -> a.key().equals(set1Key), a -> { - GetSuccess> g = a; - Set value = g.dataValue().getElements(); - }). - match(GetFailure.class, a -> a.key().equals(set1Key), a -> { - // read from 3 nodes failed within 1.second - }). - match(NotFound.class, a -> a.key().equals(set1Key), a -> { - // key set1 does not exist - }).build()); - //#get-response2 - + //#get-response2 + receive(ReceiveBuilder. + match(GetSuccess.class, a -> a.key().equals(set1Key), a -> { + GetSuccess> g = a; + Set value = g.dataValue().getElements(); + }). + match(GetFailure.class, a -> a.key().equals(set1Key), a -> { + // read from 3 nodes failed within 1.second + }). + match(NotFound.class, a -> a.key().equals(set1Key), a -> { + // key set1 does not exist + }).build()); + //#get-response2 + } + }; } @SuppressWarnings("unchecked") @Test public void demonstrateGetWithRequestContext() { - probe = new JavaTestKit(system); + new JavaTestKit(system) { + { - //#get-request-context - final ActorRef replicator = DistributedData.get(system).replicator(); - final ReadConsistency readTwo = new ReadFrom(2, Duration.create(3, SECONDS)); - final Key counter1Key = PNCounterKey.create("counter1"); + //#get-request-context + final ActorRef replicator = DistributedData.get(system).replicator(); + final ReadConsistency readTwo = new ReadFrom(2, Duration.create(3, SECONDS)); + final Key counter1Key = PNCounterKey.create("counter1"); - receive(ReceiveBuilder. - match(String.class, a -> a.equals("get-count"), a -> { - // incoming request to retrieve current value of the counter - Optional reqContext = Optional.of(sender()); - replicator.tell(new Replicator.Get(counter1Key, - readTwo), self()); - }). - - match(GetSuccess.class, a -> a.key().equals(counter1Key), a -> { - ActorRef replyTo = (ActorRef) a.getRequest().get(); - GetSuccess g = a; - long value = g.dataValue().getValue().longValue(); - replyTo.tell(value, self()); - }). - - match(GetFailure.class, a -> a.key().equals(counter1Key), a -> { - ActorRef replyTo = (ActorRef) a.getRequest().get(); - replyTo.tell(-1L, self()); - }). - - match(NotFound.class, a -> a.key().equals(counter1Key), a -> { - ActorRef replyTo = (ActorRef) a.getRequest().get(); - replyTo.tell(0L, self()); - }).build()); - //#get-request-context + receive(ReceiveBuilder. + match(String.class, a -> a.equals("get-count"), a -> { + // incoming request to retrieve current value of the counter + Optional reqContext = Optional.of(getTestActor()); + replicator.tell(new Replicator.Get(counter1Key, + readTwo), getTestActor()); + }). + + match(GetSuccess.class, a -> a.key().equals(counter1Key), a -> { + ActorRef replyTo = (ActorRef) a.getRequest().get(); + GetSuccess g = a; + long value = g.dataValue().getValue().longValue(); + replyTo.tell(value, getTestActor()); + }). + + match(GetFailure.class, a -> a.key().equals(counter1Key), a -> { + ActorRef replyTo = (ActorRef) a.getRequest().get(); + replyTo.tell(-1L, getTestActor()); + }). + + match(NotFound.class, a -> a.key().equals(counter1Key), a -> { + ActorRef replyTo = (ActorRef) a.getRequest().get(); + replyTo.tell(0L, getTestActor()); + }).build()); + //#get-request-context + } + }; } @SuppressWarnings("unchecked") - abstract class MyActor { + abstract class MyActor extends AbstractActor { //#subscribe final ActorRef replicator = DistributedData.get(system).replicator(); final Key counter1Key = PNCounterKey.create("counter1"); @@ -270,21 +269,22 @@ public class DistributedDataDocTest { @Test public void demonstrateDelete() { - probe = new JavaTestKit(system); + new JavaTestKit(system) { + { + //#delete + final ActorRef replicator = DistributedData.get(system).replicator(); + final Key counter1Key = PNCounterKey.create("counter1"); + final Key> set2Key = ORSetKey.create("set2"); - //#delete - final ActorRef replicator = DistributedData.get(system).replicator(); - final Key counter1Key = PNCounterKey.create("counter1"); - final Key> set2Key = ORSetKey.create("set2"); + replicator.tell(new Delete(counter1Key, + Replicator.writeLocal()), getTestActor()); - replicator.tell(new Delete(counter1Key, - Replicator.writeLocal()), self()); - - final WriteConsistency writeMajority = - new WriteMajority(Duration.create(5, SECONDS)); - replicator.tell(new Delete(counter1Key, - writeMajority), self()); - //#delete + final WriteConsistency writeMajority = + new WriteMajority(Duration.create(5, SECONDS)); + replicator.tell(new Delete(counter1Key, + writeMajority), getTestActor()); + //#delete + }}; } public void demonstratePNCounter() { diff --git a/akka-docs/rst/java/code/docs/dispatcher/DispatcherDocTest.java b/akka-docs/rst/java/code/docs/dispatcher/DispatcherDocTest.java index 0ea2b35ffc..8662eb9748 100644 --- a/akka-docs/rst/java/code/docs/dispatcher/DispatcherDocTest.java +++ b/akka-docs/rst/java/code/docs/dispatcher/DispatcherDocTest.java @@ -7,6 +7,7 @@ import akka.dispatch.ControlMessage; import akka.dispatch.RequiresMessageQueue; import akka.testkit.AkkaSpec; import com.typesafe.config.ConfigFactory; +import docs.AbstractJavaTest; import docs.actor.MyBoundedUntypedActor; import docs.actor.MyUntypedActor; import org.junit.ClassRule; @@ -36,7 +37,7 @@ import com.typesafe.config.Config; //#imports-required-mailbox -public class DispatcherDocTest { +public class DispatcherDocTest extends AbstractJavaTest { @ClassRule public static AkkaJUnitActorSystemResource actorSystemResource = diff --git a/akka-docs/rst/java/code/docs/event/EventBusDocTest.java b/akka-docs/rst/java/code/docs/event/EventBusDocTest.java index 2dd4ae911b..7d47d56e0b 100644 --- a/akka-docs/rst/java/code/docs/event/EventBusDocTest.java +++ b/akka-docs/rst/java/code/docs/event/EventBusDocTest.java @@ -7,6 +7,7 @@ import akka.event.japi.EventBus; import java.util.concurrent.TimeUnit; +import docs.AbstractJavaTest; import org.junit.ClassRule; import org.junit.Test; @@ -43,7 +44,7 @@ import akka.event.japi.ManagedActorEventBus; //#actor-bus -public class EventBusDocTest { +public class EventBusDocTest extends AbstractJavaTest { public static class Event {} public static class Subscriber {} diff --git a/akka-docs/rst/java/code/docs/event/LoggingDocTest.java b/akka-docs/rst/java/code/docs/event/LoggingDocTest.java index b47638c710..71e1ca413e 100644 --- a/akka-docs/rst/java/code/docs/event/LoggingDocTest.java +++ b/akka-docs/rst/java/code/docs/event/LoggingDocTest.java @@ -19,6 +19,7 @@ import akka.event.Logging.Debug; //#imports-listener +import docs.AbstractJavaTest; import org.junit.Test; import akka.testkit.JavaTestKit; import scala.Option; @@ -35,7 +36,7 @@ import akka.actor.ActorRef; import akka.actor.ActorSystem; //#imports-deadletter -public class LoggingDocTest { +public class LoggingDocTest extends AbstractJavaTest { @Test public void useLoggingActor() { @@ -79,7 +80,7 @@ public class LoggingDocTest { } } - class Listener extends UntypedActor { + static class Listener extends UntypedActor { @Override public void onReceive(Object message) throws Exception { if (message instanceof Jazz) { diff --git a/akka-docs/rst/java/code/docs/extension/ExtensionDocTest.java b/akka-docs/rst/java/code/docs/extension/ExtensionDocTest.java index fe46d37d1c..93042202cb 100644 --- a/akka-docs/rst/java/code/docs/extension/ExtensionDocTest.java +++ b/akka-docs/rst/java/code/docs/extension/ExtensionDocTest.java @@ -9,9 +9,10 @@ import java.util.concurrent.atomic.AtomicLong; //#imports +import docs.AbstractJavaTest; import org.junit.Test; -public class ExtensionDocTest { +public class ExtensionDocTest extends AbstractJavaTest { static //#extension diff --git a/akka-docs/rst/java/code/docs/extension/SettingsExtensionDocTest.java b/akka-docs/rst/java/code/docs/extension/SettingsExtensionDocTest.java index 058bac5a70..b3987c1849 100644 --- a/akka-docs/rst/java/code/docs/extension/SettingsExtensionDocTest.java +++ b/akka-docs/rst/java/code/docs/extension/SettingsExtensionDocTest.java @@ -9,6 +9,7 @@ import akka.actor.AbstractExtensionId; import akka.actor.ExtensionIdProvider; import akka.actor.ActorSystem; import akka.actor.ExtendedActorSystem; +import docs.AbstractJavaTest; import scala.concurrent.duration.Duration; import com.typesafe.config.Config; import java.util.concurrent.TimeUnit; @@ -18,7 +19,7 @@ import java.util.concurrent.TimeUnit; import akka.actor.UntypedActor; import org.junit.Test; -public class SettingsExtensionDocTest { +public class SettingsExtensionDocTest extends AbstractJavaTest { static //#extension diff --git a/akka-docs/rst/java/code/docs/future/FutureDocTest.java b/akka-docs/rst/java/code/docs/future/FutureDocTest.java index be0d2b16a5..c6d3271c10 100644 --- a/akka-docs/rst/java/code/docs/future/FutureDocTest.java +++ b/akka-docs/rst/java/code/docs/future/FutureDocTest.java @@ -5,6 +5,7 @@ package docs.future; //#imports1 import akka.dispatch.*; +import docs.AbstractJavaTest; import scala.concurrent.ExecutionContext; import scala.concurrent.Future; import scala.concurrent.Await; @@ -66,7 +67,7 @@ import akka.pattern.Patterns; import static org.junit.Assert.*; -public class FutureDocTest { +public class FutureDocTest extends AbstractJavaTest { @ClassRule public static AkkaJUnitActorSystemResource actorSystemResource = diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/HttpServerExampleDocTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/HttpServerExampleDocTest.java index 3cabf1bd1d..f6431e2ab8 100644 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/HttpServerExampleDocTest.java +++ b/akka-docs/rst/java/code/docs/http/javadsl/server/HttpServerExampleDocTest.java @@ -6,8 +6,6 @@ package docs.http.javadsl.server; import akka.NotUsed; import akka.actor.ActorSystem; -import akka.dispatch.OnFailure; -import akka.http.impl.util.Util; import akka.http.javadsl.Http; import akka.http.javadsl.IncomingConnection; import akka.http.javadsl.ServerBinding; @@ -19,16 +17,11 @@ import akka.http.javadsl.model.HttpResponse; import akka.http.javadsl.model.Uri; import akka.http.scaladsl.model.HttpEntity; import akka.japi.function.Function; -import akka.japi.function.Procedure; import akka.stream.ActorMaterializer; import akka.stream.Materializer; import akka.stream.javadsl.Flow; import akka.stream.javadsl.Sink; import akka.stream.javadsl.Source; -import akka.stream.stage.Context; -import akka.stream.stage.PushStage; -import akka.stream.stage.SyncDirective; -import akka.stream.stage.TerminationDirective; import akka.util.ByteString; import java.io.BufferedReader; @@ -88,19 +81,14 @@ public class HttpServerExampleDocTest { Http.get(system).bind("localhost", 8080, materializer); Flow failureDetection = - Flow.of(IncomingConnection.class).transform(() -> - new PushStage() { - @Override - public SyncDirective onPush(IncomingConnection elem, Context ctx) { - return ctx.push(elem); - } - - @Override - public TerminationDirective onUpstreamFailure(Throwable cause, Context ctx) { + Flow.of(IncomingConnection.class).watchTermination((notUsed, termination) -> { + termination.whenComplete((done, cause) -> { + if (cause != null) { // signal the failure to external monitoring service! - return super.onUpstreamFailure(cause, ctx); - } + } }); + return NotUsed.getInstance(); + }); CompletionStage serverBindingFuture = serverSource @@ -123,18 +111,14 @@ public class HttpServerExampleDocTest { Http.get(system).bind("localhost", 8080, materializer); Flow failureDetection = - Flow.of(HttpRequest.class).transform(() -> - new PushStage() { - @Override - public SyncDirective onPush(HttpRequest elem, Context ctx) { - return ctx.push(elem); - } - - @Override - public TerminationDirective onUpstreamFailure(Throwable cause, Context ctx) { - // signal the failure to external monitoring service! - return super.onUpstreamFailure(cause, ctx); - } + Flow.of(HttpRequest.class) + .watchTermination((notUsed, termination) -> { + termination.whenComplete((done, cause) -> { + if (cause != null) { + // signal the failure to external monitoring service! + } + }); + return NotUsed.getInstance(); }); Flow httpEcho = diff --git a/akka-docs/rst/java/code/docs/io/JavaUdpMulticastTest.java b/akka-docs/rst/java/code/docs/io/JavaUdpMulticastTest.java index d79d8ef2be..7c4ae60f45 100644 --- a/akka-docs/rst/java/code/docs/io/JavaUdpMulticastTest.java +++ b/akka-docs/rst/java/code/docs/io/JavaUdpMulticastTest.java @@ -9,6 +9,7 @@ import akka.actor.ActorSystem; import akka.actor.Props; import akka.io.Udp; import akka.testkit.JavaTestKit; +import docs.AbstractJavaTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -19,6 +20,9 @@ import java.net.NetworkInterface; import java.util.Enumeration; import java.util.Random; +// not part of the test suite because we have not figured out +// a way to find an interface that is sure to work on all platforms +// to listen for udp on public class JavaUdpMulticastTest { static ActorSystem system; diff --git a/akka-docs/rst/java/code/docs/io/japi/IODocTest.java b/akka-docs/rst/java/code/docs/io/japi/IODocTest.java index fceec37de1..0276812d6b 100644 --- a/akka-docs/rst/java/code/docs/io/japi/IODocTest.java +++ b/akka-docs/rst/java/code/docs/io/japi/IODocTest.java @@ -6,6 +6,7 @@ package docs.io.japi; import akka.testkit.AkkaJUnitActorSystemResource; +import docs.AbstractJavaTest; import org.junit.ClassRule; import org.junit.Test; @@ -29,7 +30,7 @@ import akka.util.ByteString; import akka.testkit.JavaTestKit; import akka.testkit.AkkaSpec; -public class IODocTest { +public class IODocTest extends AbstractJavaTest { static //#server diff --git a/akka-docs/rst/java/code/docs/jrouting/ConsistentHashingRouterDocTest.java b/akka-docs/rst/java/code/docs/jrouting/ConsistentHashingRouterDocTest.java index 0673211239..d99b104a81 100644 --- a/akka-docs/rst/java/code/docs/jrouting/ConsistentHashingRouterDocTest.java +++ b/akka-docs/rst/java/code/docs/jrouting/ConsistentHashingRouterDocTest.java @@ -5,6 +5,7 @@ package docs.jrouting; import akka.testkit.AkkaJUnitActorSystemResource; +import docs.AbstractJavaTest; import org.junit.ClassRule; import org.junit.Test; @@ -30,7 +31,7 @@ import akka.routing.ConsistentHashingRouter.ConsistentHashMapper; import akka.routing.ConsistentHashingRouter.ConsistentHashableEnvelope; //#imports2 -public class ConsistentHashingRouterDocTest { +public class ConsistentHashingRouterDocTest extends AbstractJavaTest { @ClassRule public static AkkaJUnitActorSystemResource actorSystemResource = diff --git a/akka-docs/rst/java/code/docs/jrouting/CustomRouterDocTest.java b/akka-docs/rst/java/code/docs/jrouting/CustomRouterDocTest.java index d346d70ec5..06e32b5b9d 100644 --- a/akka-docs/rst/java/code/docs/jrouting/CustomRouterDocTest.java +++ b/akka-docs/rst/java/code/docs/jrouting/CustomRouterDocTest.java @@ -10,6 +10,7 @@ import akka.routing.RoutingLogic; import akka.routing.SeveralRoutees; import akka.testkit.AkkaJUnitActorSystemResource; +import docs.AbstractJavaTest; import org.junit.ClassRule; import org.junit.Test; @@ -36,7 +37,7 @@ import java.util.List; //#imports1 -public class CustomRouterDocTest { +public class CustomRouterDocTest extends AbstractJavaTest { @ClassRule public static AkkaJUnitActorSystemResource actorSystemResource = diff --git a/akka-docs/rst/java/code/docs/jrouting/RouterDocTest.java b/akka-docs/rst/java/code/docs/jrouting/RouterDocTest.java index 3a4b5a0cba..73f37ecaba 100644 --- a/akka-docs/rst/java/code/docs/jrouting/RouterDocTest.java +++ b/akka-docs/rst/java/code/docs/jrouting/RouterDocTest.java @@ -5,6 +5,7 @@ package docs.jrouting; import akka.testkit.AkkaJUnitActorSystemResource; +import docs.AbstractJavaTest; import org.junit.ClassRule; import org.junit.Test; @@ -66,7 +67,7 @@ import akka.routing.TailChoppingPool; //#imports2 -public class RouterDocTest { +public class RouterDocTest extends AbstractJavaTest { @ClassRule public static AkkaJUnitActorSystemResource actorSystemResource = diff --git a/akka-docs/rst/java/code/docs/pattern/SchedulerPatternTest.java b/akka-docs/rst/java/code/docs/pattern/SchedulerPatternTest.java index 2a20345b08..1043914898 100644 --- a/akka-docs/rst/java/code/docs/pattern/SchedulerPatternTest.java +++ b/akka-docs/rst/java/code/docs/pattern/SchedulerPatternTest.java @@ -8,13 +8,14 @@ import akka.actor.*; import akka.testkit.*; import akka.testkit.TestEvent.Mute; import akka.testkit.TestEvent.UnMute; +import docs.AbstractJavaTest; import org.junit.*; import scala.concurrent.duration.Duration; import scala.concurrent.duration.FiniteDuration; import java.util.Arrays; import java.util.concurrent.TimeUnit; -public class SchedulerPatternTest { +public class SchedulerPatternTest extends AbstractJavaTest { @ClassRule public static AkkaJUnitActorSystemResource actorSystemResource = diff --git a/akka-docs/rst/java/code/docs/stream/ActorPublisherDocTest.java b/akka-docs/rst/java/code/docs/stream/ActorPublisherDocTest.java index 45031232b4..9f3427ddab 100644 --- a/akka-docs/rst/java/code/docs/stream/ActorPublisherDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/ActorPublisherDocTest.java @@ -14,6 +14,7 @@ import akka.stream.actor.ActorPublisherMessage; import akka.stream.javadsl.Sink; import akka.stream.javadsl.Source; import akka.testkit.JavaTestKit; +import docs.AbstractJavaTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -21,100 +22,100 @@ import org.junit.Test; import java.util.ArrayList; import java.util.List; -public class ActorPublisherDocTest { +public class ActorPublisherDocTest extends AbstractJavaTest { static ActorSystem system; - + static Materializer mat; @BeforeClass public static void setup() { system = ActorSystem.create("ActorPublisherDocTest"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - - //#job-manager - public static class JobManagerProtocol { - final public static class Job { - public final String payload; - - public Job(String payload) { - this.payload = payload; - } + //#job-manager + public static class JobManagerProtocol { + final public static class Job { + public final String payload; + public Job(String payload) { + this.payload = payload; } - public static class JobAcceptedMessage { - @Override - public String toString() { - return "JobAccepted"; - } - } - public static final JobAcceptedMessage JobAccepted = new JobAcceptedMessage(); - - public static class JobDeniedMessage { - @Override - public String toString() { - return "JobDenied"; - } - } - public static final JobDeniedMessage JobDenied = new JobDeniedMessage(); } - public static class JobManager extends AbstractActorPublisher { - public static Props props() { return Props.create(JobManager.class); } - - private final int MAX_BUFFER_SIZE = 100; - private final List buf = new ArrayList<>(); - - public JobManager() { - receive(ReceiveBuilder. - match(JobManagerProtocol.Job.class, job -> buf.size() == MAX_BUFFER_SIZE, job -> { - sender().tell(JobManagerProtocol.JobDenied, self()); - }). - match(JobManagerProtocol.Job.class, job -> { - sender().tell(JobManagerProtocol.JobAccepted, self()); - - if (buf.isEmpty() && totalDemand() > 0) - onNext(job); - else { - buf.add(job); - deliverBuf(); - } - }). - match(ActorPublisherMessage.Request.class, request -> deliverBuf()). - match(ActorPublisherMessage.Cancel.class, cancel -> context().stop(self())). - build()); + public static class JobAcceptedMessage { + @Override + public String toString() { + return "JobAccepted"; } + } + public static final JobAcceptedMessage JobAccepted = new JobAcceptedMessage(); - void deliverBuf() { - while (totalDemand() > 0) { - /* - * totalDemand is a Long and could be larger than - * what buf.splitAt can accept - */ - if (totalDemand() <= Integer.MAX_VALUE) { - final List took = - buf.subList(0, Math.min(buf.size(), (int) totalDemand())); - took.forEach(this::onNext); - buf.removeAll(took); - break; - } else { - final List took = - buf.subList(0, Math.min(buf.size(), Integer.MAX_VALUE)); - took.forEach(this::onNext); - buf.removeAll(took); + public static class JobDeniedMessage { + @Override + public String toString() { + return "JobDenied"; + } + } + public static final JobDeniedMessage JobDenied = new JobDeniedMessage(); + } + public static class JobManager extends AbstractActorPublisher { + + public static Props props() { return Props.create(JobManager.class); } + + private final int MAX_BUFFER_SIZE = 100; + private final List buf = new ArrayList<>(); + + public JobManager() { + receive(ReceiveBuilder. + match(JobManagerProtocol.Job.class, job -> buf.size() == MAX_BUFFER_SIZE, job -> { + sender().tell(JobManagerProtocol.JobDenied, self()); + }). + match(JobManagerProtocol.Job.class, job -> { + sender().tell(JobManagerProtocol.JobAccepted, self()); + + if (buf.isEmpty() && totalDemand() > 0) + onNext(job); + else { + buf.add(job); + deliverBuf(); } + }). + match(ActorPublisherMessage.Request.class, request -> deliverBuf()). + match(ActorPublisherMessage.Cancel.class, cancel -> context().stop(self())). + build()); + } + + void deliverBuf() { + while (totalDemand() > 0) { + /* + * totalDemand is a Long and could be larger than + * what buf.splitAt can accept + */ + if (totalDemand() <= Integer.MAX_VALUE) { + final List took = + buf.subList(0, Math.min(buf.size(), (int) totalDemand())); + took.forEach(this::onNext); + buf.removeAll(took); + break; + } else { + final List took = + buf.subList(0, Math.min(buf.size(), Integer.MAX_VALUE)); + took.forEach(this::onNext); + buf.removeAll(took); } } } - //#job-manager + } + //#job-manager @Test public void demonstrateActorPublisherUsage() { diff --git a/akka-docs/rst/java/code/docs/stream/ActorSubscriberDocTest.java b/akka-docs/rst/java/code/docs/stream/ActorSubscriberDocTest.java index e8751b1538..be27428617 100644 --- a/akka-docs/rst/java/code/docs/stream/ActorSubscriberDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/ActorSubscriberDocTest.java @@ -22,6 +22,7 @@ import akka.stream.actor.RequestStrategy; import akka.stream.javadsl.Sink; import akka.stream.javadsl.Source; import akka.testkit.JavaTestKit; +import docs.AbstractJavaTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -30,24 +31,24 @@ import java.util.*; import static org.junit.Assert.assertEquals; -public class ActorSubscriberDocTest { +public class ActorSubscriberDocTest extends AbstractJavaTest { static ActorSystem system; - + static Materializer mat; @BeforeClass public static void setup() { system = ActorSystem.create("ActorSubscriberDocTest"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - //#worker-pool public static class WorkerPoolProtocol { diff --git a/akka-docs/rst/java/code/docs/stream/BidiFlowDocTest.java b/akka-docs/rst/java/code/docs/stream/BidiFlowDocTest.java index ca58f66d2f..8d38246540 100644 --- a/akka-docs/rst/java/code/docs/stream/BidiFlowDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/BidiFlowDocTest.java @@ -11,6 +11,7 @@ import java.util.concurrent.TimeUnit; import akka.NotUsed; import akka.stream.javadsl.GraphDSL; +import docs.AbstractJavaTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -30,23 +31,24 @@ import scala.concurrent.duration.Duration; import scala.concurrent.duration.FiniteDuration; import static org.junit.Assert.assertArrayEquals; -public class BidiFlowDocTest { +public class BidiFlowDocTest extends AbstractJavaTest { - private static ActorSystem system; + static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { - system = ActorSystem.create("FlowDocTest"); + system = ActorSystem.create("FlowDocTest"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { - JavaTestKit.shutdownActorSystem(system); - system = null; + JavaTestKit.shutdownActorSystem(system); + system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - //#codec static interface Message {} static class Ping implements Message { @@ -132,58 +134,77 @@ public class BidiFlowDocTest { .append(bytes) .result(); } - - public static class FrameParser extends PushPullStage { - // this holds the received but not yet parsed bytes - private ByteString stash = ByteString.empty(); - // this holds the current message length or -1 if at a boundary - private int needed = -1; - + + public static class FrameParser extends GraphStage> { + public Inlet in = Inlet.create("FrameParser.in"); + public Outlet out = Outlet.create("FrameParser.out"); + private FlowShape shape = FlowShape.of(in, out); + @Override - public SyncDirective onPull(Context ctx) { - return run(ctx); + public FlowShape shape() { + return shape; } @Override - public SyncDirective onPush(ByteString bytes, Context ctx) { - stash = stash.concat(bytes); - return run(ctx); - } - - @Override - public TerminationDirective onUpstreamFinish(Context ctx) { - if (stash.isEmpty()) return ctx.finish(); - else return ctx.absorbTermination(); // we still have bytes to emit - } - - private SyncDirective run(Context ctx) { - if (needed == -1) { - // are we at a boundary? then figure out next length - if (stash.size() < 4) return pullOrFinish(ctx); - else { - needed = stash.iterator().getInt(ByteOrder.LITTLE_ENDIAN); - stash = stash.drop(4); - return run(ctx); // cycle back to possibly already emit the next chunk + public GraphStageLogic createLogic(Attributes inheritedAttributes) { + return new GraphStageLogic(shape) { + + // this holds the received but not yet parsed bytes + private ByteString stash = ByteString.empty(); + // this holds the current message length or -1 if at a boundary + private int needed = -1; + + { + setHandler(in, new AbstractInHandler() { + @Override + public void onPush() throws Exception { + ByteString bytes = grab(in); + stash = stash.concat(bytes); + run(); + } + + @Override + public void onUpstreamFinish() throws Exception { + if (stash.isEmpty()) completeStage(); + // wait with completion and let run() complete when the + // rest of the stash has been sent downstream + } + }); + + setHandler(out, new AbstractOutHandler() { + @Override + public void onPull() throws Exception { + if (isClosed(in)) run(); + else pull(in); + } + }); } - } else if (stash.size() < needed) { - // we are in the middle of a message, need more bytes - return pullOrFinish(ctx); - } else { - // we have enough to emit at least one message, so do it - final ByteString emit = stash.take(needed); - stash = stash.drop(needed); - needed = -1; - return ctx.push(emit); - } - } - - /* - * After having called absorbTermination() we cannot pull any more, so if we need - * more data we will just have to give up. - */ - private SyncDirective pullOrFinish(Context ctx) { - if (ctx.isFinishing()) return ctx.finish(); - else return ctx.pull(); + + private void run() { + if (needed == -1) { + // are we at a boundary? then figure out next length + if (stash.size() < 4) { + if (isClosed(in)) completeStage(); + else pull(in); + } else { + needed = stash.iterator().getInt(ByteOrder.LITTLE_ENDIAN); + stash = stash.drop(4); + run(); // cycle back to possibly already emit the next chunk + } + } else if (stash.size() < needed) { + // we are in the middle of a message, need more bytes + // or in is already closed and we cannot pull any more + if (isClosed(in)) completeStage(); + else pull(in); + } else { + // we have enough to emit at least one message, so do it + final ByteString emit = stash.take(needed); + stash = stash.drop(needed); + needed = -1; + push(out, emit); + } + } + }; } } @@ -192,7 +213,7 @@ public class BidiFlowDocTest { final FlowShape top = b.add(Flow.of(ByteString.class).map(BidiFlowDocTest::addLengthHeader)); final FlowShape bottom = - b.add(Flow.of(ByteString.class).transform(() -> new FrameParser())); + b.add(Flow.of(ByteString.class).via(new FrameParser())); return BidiShape.fromFlows(top, bottom); })); //#framing diff --git a/akka-docs/rst/java/code/docs/stream/CompositionDocTest.java b/akka-docs/rst/java/code/docs/stream/CompositionDocTest.java index 60f64c4ad7..899251a348 100644 --- a/akka-docs/rst/java/code/docs/stream/CompositionDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/CompositionDocTest.java @@ -10,6 +10,7 @@ import java.util.concurrent.CompletionStage; import akka.NotUsed; import akka.stream.ClosedShape; +import docs.AbstractJavaTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -26,23 +27,24 @@ import scala.concurrent.*; import scala.Option; -public class CompositionDocTest { +public class CompositionDocTest extends AbstractJavaTest { - private static ActorSystem system; + static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { - system = ActorSystem.create("FlowDocTest"); + system = ActorSystem.create("CompositionDocTest"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - @Test public void nonNestedFlow() throws Exception { //#non-nested-flow diff --git a/akka-docs/rst/java/code/docs/stream/FlowDocTest.java b/akka-docs/rst/java/code/docs/stream/FlowDocTest.java index 0a5a775dd0..8625e3837b 100644 --- a/akka-docs/rst/java/code/docs/stream/FlowDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/FlowDocTest.java @@ -13,6 +13,7 @@ import java.util.stream.Stream; import akka.NotUsed; import akka.japi.Pair; +import docs.AbstractJavaTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -30,22 +31,23 @@ import akka.stream.*; import akka.stream.javadsl.*; import akka.testkit.JavaTestKit; -public class FlowDocTest { +public class FlowDocTest extends AbstractJavaTest { - private static ActorSystem system; + static ActorSystem system; + static Materializer mat; - @BeforeClass - public static void setup() { - system = ActorSystem.create("FlowDocTest"); - } + @BeforeClass + public static void setup() { + system = ActorSystem.create("FlowDocTest"); + mat = ActorMaterializer.create(system); + } - @AfterClass - public static void tearDown() { - JavaTestKit.shutdownActorSystem(system); - system = null; - } - - final Materializer mat = ActorMaterializer.create(system); + @AfterClass + public static void tearDown() { + JavaTestKit.shutdownActorSystem(system); + system = null; + mat = null; + } @Test public void sourceIsImmutable() throws Exception { diff --git a/akka-docs/rst/java/code/docs/stream/FlowErrorDocTest.java b/akka-docs/rst/java/code/docs/stream/FlowErrorDocTest.java index 537cdd6ee7..f5dbfed7ea 100644 --- a/akka-docs/rst/java/code/docs/stream/FlowErrorDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/FlowErrorDocTest.java @@ -7,9 +7,11 @@ import static org.junit.Assert.assertEquals; import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import akka.NotUsed; +import docs.AbstractJavaTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -29,7 +31,7 @@ import akka.stream.javadsl.Source; import akka.japi.function.Function; import akka.testkit.JavaTestKit; -public class FlowErrorDocTest { +public class FlowErrorDocTest extends AbstractJavaTest { private static ActorSystem system; @@ -44,7 +46,7 @@ public class FlowErrorDocTest { system = null; } - @Test(expected = ArithmeticException.class) + @Test(expected = ExecutionException.class) public void demonstrateFailStream() throws Exception { //#stop final Materializer mat = ActorMaterializer.create(system); diff --git a/akka-docs/rst/java/code/docs/stream/FlowGraphDocTest.java b/akka-docs/rst/java/code/docs/stream/FlowGraphDocTest.java index e30d9dd759..d31937b384 100644 --- a/akka-docs/rst/java/code/docs/stream/FlowGraphDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/FlowGraphDocTest.java @@ -13,6 +13,7 @@ import java.util.concurrent.TimeUnit; import akka.NotUsed; import akka.stream.ClosedShape; import akka.stream.SourceShape; +import docs.AbstractJavaTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -26,23 +27,24 @@ import akka.stream.*; import akka.stream.javadsl.*; import akka.testkit.JavaTestKit; -public class FlowGraphDocTest { +public class FlowGraphDocTest extends AbstractJavaTest { static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { system = ActorSystem.create("FlowGraphDocTest"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - @Test public void demonstrateBuildSimpleGraph() throws Exception { //#simple-flow-graph diff --git a/akka-docs/rst/java/code/docs/stream/FlowParallelismDocTest.java b/akka-docs/rst/java/code/docs/stream/FlowParallelismDocTest.java index 34693fc2f2..7824d30184 100644 --- a/akka-docs/rst/java/code/docs/stream/FlowParallelismDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/FlowParallelismDocTest.java @@ -7,6 +7,7 @@ package docs.stream; import static org.junit.Assert.assertEquals; import akka.NotUsed; +import docs.AbstractJavaTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -15,23 +16,24 @@ import akka.stream.*; import akka.stream.javadsl.*; import akka.testkit.JavaTestKit; -public class FlowParallelismDocTest { +public class FlowParallelismDocTest extends AbstractJavaTest { - private static ActorSystem system; + static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { - system = ActorSystem.create("FlowDocTest"); + system = ActorSystem.create("FlowParallellismDocTest"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - static class ScoopOfBatter {} static class HalfCookedPancake {} static class Pancake {} diff --git a/akka-docs/rst/java/code/docs/stream/FlowStagesDocTest.java b/akka-docs/rst/java/code/docs/stream/FlowStagesDocTest.java deleted file mode 100644 index bfcb21d29a..0000000000 --- a/akka-docs/rst/java/code/docs/stream/FlowStagesDocTest.java +++ /dev/null @@ -1,257 +0,0 @@ -/** - * Copyright (C) 2015-2016 Typesafe Inc. - */ -package docs.stream; - -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; -import java.util.function.Predicate; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import scala.concurrent.Await; -import scala.concurrent.Future; -import scala.concurrent.duration.Duration; -import scala.concurrent.duration.FiniteDuration; - -import akka.actor.ActorSystem; -import akka.japi.Pair; -import akka.stream.*; -import akka.stream.javadsl.*; -import akka.stream.stage.*; -import akka.stream.testkit.*; -import akka.stream.testkit.javadsl.*; -import akka.testkit.JavaTestKit; - -public class FlowStagesDocTest { - - static ActorSystem system; - - @BeforeClass - public static void setup() { - system = ActorSystem.create("FlowStagesDocTest"); - } - - @AfterClass - public static void tearDown() { - JavaTestKit.shutdownActorSystem(system); - system = null; - } - - final Materializer mat = ActorMaterializer.create(system); - - static //#one-to-one - public class Map extends PushPullStage { - private final Function f; - public Map(Function f) { - this.f = f; - } - - @Override public SyncDirective onPush(A elem, Context ctx) { - return ctx.push(f.apply(elem)); - } - - @Override public SyncDirective onPull(Context ctx) { - return ctx.pull(); - } - } - //#one-to-one - - static //#many-to-one - public class Filter extends PushPullStage { - private final Predicate p; - public Filter(Predicate p) { - this.p = p; - } - - @Override public SyncDirective onPush(A elem, Context ctx) { - if (p.test(elem)) return ctx.push(elem); - else return ctx.pull(); - } - - @Override public SyncDirective onPull(Context ctx) { - return ctx.pull(); - } - } - //#many-to-one - - //#one-to-many - class Duplicator extends PushPullStage { - private A lastElem = null; - private boolean oneLeft = false; - - @Override public SyncDirective onPush(A elem, Context ctx) { - lastElem = elem; - oneLeft = true; - return ctx.push(elem); - } - - @Override public SyncDirective onPull(Context ctx) { - if (!ctx.isFinishing()) { - // the main pulling logic is below as it is demonstrated on the illustration - if (oneLeft) { - oneLeft = false; - return ctx.push(lastElem); - } else - return ctx.pull(); - } else { - // If we need to emit a final element after the upstream - // finished - if (oneLeft) return ctx.pushAndFinish(lastElem); - else return ctx.finish(); - } - } - - @Override public TerminationDirective onUpstreamFinish(Context ctx) { - return ctx.absorbTermination(); - } - - } - //#one-to-many - - static//#pushstage - public class Map2 extends PushStage { - private final Function f; - public Map2(Function f) { - this.f = f; - } - - @Override public SyncDirective onPush(A elem, Context ctx) { - return ctx.push(f.apply(elem)); - } - } - - public class Filter2 extends PushStage { - private final Predicate p; - public Filter2(Predicate p) { - this.p = p; - } - - @Override public SyncDirective onPush(A elem, Context ctx) { - if (p.test(elem)) return ctx.push(elem); - else return ctx.pull(); - } - } - //#pushstage - - static //#doubler-stateful - public class Duplicator2 extends StatefulStage { - @Override public StageState initial() { - return new StageState() { - @Override public SyncDirective onPush(A elem, Context ctx) { - return emit(Arrays.asList(elem, elem).iterator(), ctx); - } - }; - } - } - //#doubler-stateful - - @Test - public void demonstrateVariousPushPullStages() throws Exception { - final Sink>> sink = - Flow.of(Integer.class).limit(10).toMat(Sink.seq(), Keep.right()); - - //#stage-chain - final RunnableGraph>> runnable = - Source - .from(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) - .transform(() -> new Filter(elem -> elem % 2 == 0)) - .transform(() -> new Duplicator()) - .transform(() -> new Map(elem -> elem / 2)) - .toMat(sink, Keep.right()); - //#stage-chain - - assertEquals(Arrays.asList(1, 1, 2, 2, 3, 3, 4, 4, 5, 5), - runnable.run(mat).toCompletableFuture().get(3, TimeUnit.SECONDS)); - } - - //#detached - class Buffer2 extends DetachedStage { - final private Integer SIZE = 2; - final private List buf = new ArrayList<>(SIZE); - private Integer capacity = SIZE; - - private boolean isFull() { - return capacity == 0; - } - - private boolean isEmpty() { - return capacity == SIZE; - } - - private T dequeue() { - capacity += 1; - return buf.remove(0); - } - - private void enqueue(T elem) { - capacity -= 1; - buf.add(elem); - } - - public DownstreamDirective onPull(DetachedContext ctx) { - if (isEmpty()) { - if (ctx.isFinishing()) return ctx.finish(); // No more elements will arrive - else return ctx.holdDownstream(); // waiting until new elements - } else { - final T next = dequeue(); - if (ctx.isHoldingUpstream()) return ctx.pushAndPull(next); // release upstream - else return ctx.push(next); - } - } - - public UpstreamDirective onPush(T elem, DetachedContext ctx) { - enqueue(elem); - if (isFull()) return ctx.holdUpstream(); // Queue is now full, wait until new empty slot - else { - if (ctx.isHoldingDownstream()) return ctx.pushAndPull(dequeue()); // Release downstream - else return ctx.pull(); - } - } - - public TerminationDirective onUpstreamFinish(DetachedContext ctx) { - if (!isEmpty()) return ctx.absorbTermination(); // still need to flush from buffer - else return ctx.finish(); // already empty, finishing - } - } - //#detached - - @Test - public void demonstrateDetachedStage() throws Exception { - final Pair,TestSubscriber.Probe> pair = - TestSource.probe(system) - .transform(() -> new Buffer2()) - .toMat(TestSink.probe(system), Keep.both()) - .run(mat); - - final TestPublisher.Probe pub = pair.first(); - final TestSubscriber.Probe sub = pair.second(); - - final FiniteDuration timeout = Duration.create(100, TimeUnit.MILLISECONDS); - - sub.request(2); - sub.expectNoMsg(timeout); - - pub.sendNext(1); - pub.sendNext(2); - sub.expectNext(1, 2); - - pub.sendNext(3); - pub.sendNext(4); - sub.expectNoMsg(timeout); - - sub.request(2); - sub.expectNext(3, 4); - - pub.sendComplete(); - sub.expectComplete(); - } - -} diff --git a/akka-docs/rst/java/code/docs/stream/GraphCyclesDocTest.java b/akka-docs/rst/java/code/docs/stream/GraphCyclesDocTest.java index a5609a8fa2..97777e3307 100644 --- a/akka-docs/rst/java/code/docs/stream/GraphCyclesDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/GraphCyclesDocTest.java @@ -3,6 +3,7 @@ package docs.stream; import java.util.Arrays; import akka.NotUsed; +import docs.AbstractJavaTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -14,24 +15,24 @@ import akka.stream.scaladsl.MergePreferred.MergePreferredShape; import akka.testkit.JavaTestKit; -public class GraphCyclesDocTest { +public class GraphCyclesDocTest extends AbstractJavaTest { static ActorSystem system; - + static Materializer mat; @BeforeClass public static void setup() { system = ActorSystem.create("GraphCyclesDocTest"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - final static SilenceSystemOut.System System = SilenceSystemOut.get(); final Source source = Source.from(Arrays.asList(1, 2, 3, 4, 5)); diff --git a/akka-docs/rst/java/code/docs/stream/GraphStageDocTest.java b/akka-docs/rst/java/code/docs/stream/GraphStageDocTest.java index 3bdca026eb..a313dca4e5 100644 --- a/akka-docs/rst/java/code/docs/stream/GraphStageDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/GraphStageDocTest.java @@ -18,6 +18,7 @@ import akka.stream.testkit.TestPublisher; import akka.stream.testkit.TestSubscriber; import akka.testkit.JavaTestKit; import akka.japi.Function; +import docs.AbstractJavaTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -37,22 +38,23 @@ import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertEquals; -public class GraphStageDocTest { +public class GraphStageDocTest extends AbstractJavaTest { static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { - system = ActorSystem.create("FlowGraphDocTest"); + system = ActorSystem.create("GraphStageDocTest"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - //#simple-source public class NumbersSource extends GraphStage> { diff --git a/akka-docs/rst/java/code/docs/stream/IntegrationDocTest.java b/akka-docs/rst/java/code/docs/stream/IntegrationDocTest.java index 3c785de436..e508927d57 100644 --- a/akka-docs/rst/java/code/docs/stream/IntegrationDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/IntegrationDocTest.java @@ -14,6 +14,7 @@ import akka.testkit.TestProbe; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; +import docs.AbstractJavaTest; import docs.stream.TwitterStreamQuickstartDocTest.Model.Author; import docs.stream.TwitterStreamQuickstartDocTest.Model.Tweet; import org.junit.AfterClass; @@ -32,11 +33,12 @@ import static docs.stream.TwitterStreamQuickstartDocTest.Model.AKKA; import static docs.stream.TwitterStreamQuickstartDocTest.Model.tweets; import static junit.framework.TestCase.assertTrue; -public class IntegrationDocTest { +public class IntegrationDocTest extends AbstractJavaTest { private static final SilenceSystemOut.System System = SilenceSystemOut.get(); static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { @@ -51,15 +53,16 @@ public class IntegrationDocTest { "akka.actor.default-mailbox.mailbox-type = akka.dispatch.UnboundedMailbox\n"); system = ActorSystem.create("ActorPublisherDocTest", config); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); class AddressSystem { //#email-address-lookup diff --git a/akka-docs/rst/java/code/docs/stream/RateTransformationDocTest.java b/akka-docs/rst/java/code/docs/stream/RateTransformationDocTest.java index ceb36117f3..ae6ed8019a 100644 --- a/akka-docs/rst/java/code/docs/stream/RateTransformationDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/RateTransformationDocTest.java @@ -14,6 +14,7 @@ import java.util.stream.DoubleStream; import java.util.stream.Stream; import akka.NotUsed; +import docs.AbstractJavaTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -36,23 +37,24 @@ import scala.concurrent.Future; import scala.concurrent.duration.Duration; import scala.util.Random; -public class RateTransformationDocTest { +public class RateTransformationDocTest extends AbstractJavaTest { - private static ActorSystem system; + static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { system = ActorSystem.create("RateTransformationDocTest"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - final Random r = new Random(); @Test diff --git a/akka-docs/rst/java/code/docs/stream/ReactiveStreamsDocTest.java b/akka-docs/rst/java/code/docs/stream/ReactiveStreamsDocTest.java index 04bf0f3a3a..3a249448fa 100644 --- a/akka-docs/rst/java/code/docs/stream/ReactiveStreamsDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/ReactiveStreamsDocTest.java @@ -13,10 +13,12 @@ import akka.stream.*; import akka.stream.javadsl.*; import akka.testkit.JavaTestKit; import akka.testkit.TestProbe; +import docs.AbstractJavaTest; import docs.stream.TwitterStreamQuickstartDocTest.Model.Author; import docs.stream.TwitterStreamQuickstartDocTest.Model.Tweet; import org.junit.AfterClass; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; //#imports @@ -32,24 +34,30 @@ import java.lang.Exception; import static docs.stream.ReactiveStreamsDocTest.Fixture.Data.authors; import static docs.stream.TwitterStreamQuickstartDocTest.Model.AKKA; -public class ReactiveStreamsDocTest { +public class ReactiveStreamsDocTest extends AbstractJavaTest { static ActorSystem system; + static Materializer mat; + static TestProbe storageProbe; + static TestProbe alertProbe; @BeforeClass public static void setup() { system = ActorSystem.create("ReactiveStreamsDocTest"); + mat = ActorMaterializer.create(system); + storageProbe = new TestProbe(system); + alertProbe = new TestProbe(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; + storageProbe = null; + alertProbe = null; } - final Materializer mat = ActorMaterializer.create(system); - - static class Fixture { // below class additionally helps with aligning code includes nicely static class Data { @@ -77,9 +85,6 @@ public class ReactiveStreamsDocTest { } } - final TestProbe storageProbe = TestProbe.apply(system); - final TestProbe alertProbe = TestProbe.apply(system); - final Fixture.RS rs = new Fixture.RS() { @Override public Publisher tweets() { diff --git a/akka-docs/rst/java/code/docs/stream/StreamBuffersRateDocTest.java b/akka-docs/rst/java/code/docs/stream/StreamBuffersRateDocTest.java index 194225318e..7d6e64a531 100644 --- a/akka-docs/rst/java/code/docs/stream/StreamBuffersRateDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/StreamBuffersRateDocTest.java @@ -7,6 +7,7 @@ import java.util.Arrays; import java.util.concurrent.TimeUnit; import akka.NotUsed; +import docs.AbstractJavaTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -18,25 +19,26 @@ import akka.stream.*; import akka.stream.javadsl.*; import akka.testkit.JavaTestKit; -public class StreamBuffersRateDocTest { +public class StreamBuffersRateDocTest extends AbstractJavaTest { static class Job {} static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { - system = ActorSystem.create("StreamBufferRateDocTest"); + system = ActorSystem.create("StreamBuffersDocTest"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - final SilenceSystemOut.System System = SilenceSystemOut.get(); @Test diff --git a/akka-docs/rst/java/code/docs/stream/StreamPartialFlowGraphDocTest.java b/akka-docs/rst/java/code/docs/stream/StreamPartialFlowGraphDocTest.java index c1504b65b8..88f4432ce8 100644 --- a/akka-docs/rst/java/code/docs/stream/StreamPartialFlowGraphDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/StreamPartialFlowGraphDocTest.java @@ -11,6 +11,7 @@ import java.util.concurrent.TimeUnit; import akka.Done; import akka.NotUsed; +import docs.AbstractJavaTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -24,24 +25,24 @@ import akka.stream.*; import akka.stream.javadsl.*; import akka.testkit.JavaTestKit; -public class StreamPartialFlowGraphDocTest { +public class StreamPartialFlowGraphDocTest extends AbstractJavaTest { static ActorSystem system; - + static Materializer mat; @BeforeClass public static void setup() { system = ActorSystem.create("StreamPartialFlowGraphDocTest"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - @Test public void demonstrateBuildWithOpenPorts() throws Exception { //#simple-partial-flow-graph diff --git a/akka-docs/rst/java/code/docs/stream/StreamTestKitDocTest.java b/akka-docs/rst/java/code/docs/stream/StreamTestKitDocTest.java index fe906a9514..d1b917cbf0 100644 --- a/akka-docs/rst/java/code/docs/stream/StreamTestKitDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/StreamTestKitDocTest.java @@ -12,6 +12,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import akka.NotUsed; +import docs.AbstractJavaTest; import org.junit.*; import static org.junit.Assert.assertEquals; @@ -29,23 +30,24 @@ import scala.concurrent.duration.Duration; import scala.concurrent.duration.FiniteDuration; -public class StreamTestKitDocTest { +public class StreamTestKitDocTest extends AbstractJavaTest { static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { system = ActorSystem.create("StreamTestKitDocTest"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - @Test public void strictCollection() throws Exception { //#strict-collection diff --git a/akka-docs/rst/java/code/docs/stream/TwitterStreamQuickstartDocTest.java b/akka-docs/rst/java/code/docs/stream/TwitterStreamQuickstartDocTest.java index 6789e3d572..d438048114 100644 --- a/akka-docs/rst/java/code/docs/stream/TwitterStreamQuickstartDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/TwitterStreamQuickstartDocTest.java @@ -11,6 +11,7 @@ import akka.japi.JavaPartialFunction; import akka.testkit.JavaTestKit; import akka.stream.*; import akka.stream.javadsl.*; +import docs.AbstractJavaTest; import docs.stream.TwitterStreamQuickstartDocTest.Model.Author; import docs.stream.TwitterStreamQuickstartDocTest.Model.Hashtag; import docs.stream.TwitterStreamQuickstartDocTest.Model.Tweet; @@ -36,20 +37,23 @@ import static docs.stream.TwitterStreamQuickstartDocTest.Model.AKKA; import static docs.stream.TwitterStreamQuickstartDocTest.Model.tweets; @SuppressWarnings("unused") -public class TwitterStreamQuickstartDocTest { +public class TwitterStreamQuickstartDocTest extends AbstractJavaTest { static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { system = ActorSystem.create("SampleActorTest"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } static abstract class Model { @@ -213,9 +217,6 @@ public class TwitterStreamQuickstartDocTest { } } - - final Materializer mat = ActorMaterializer.create(system); - @Test public void demonstrateFilterAndMap() { final SilenceSystemOut.System System = SilenceSystemOut.get(); diff --git a/akka-docs/rst/java/code/docs/stream/io/StreamFileDocTest.java b/akka-docs/rst/java/code/docs/stream/io/StreamFileDocTest.java index 1febdd92aa..be558fbbf9 100644 --- a/akka-docs/rst/java/code/docs/stream/io/StreamFileDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/io/StreamFileDocTest.java @@ -13,6 +13,7 @@ import akka.stream.ActorAttributes; import akka.stream.io.IOResult; import akka.stream.javadsl.Sink; import akka.stream.javadsl.FileIO; +import docs.AbstractJavaTest; import docs.stream.SilenceSystemOut; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -23,22 +24,25 @@ import akka.stream.*; import akka.testkit.JavaTestKit; import akka.util.ByteString; -public class StreamFileDocTest { +public class StreamFileDocTest extends AbstractJavaTest { static ActorSystem system; + static Materializer mat; + @BeforeClass public static void setup() { system = ActorSystem.create("StreamFileDocTest"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); final SilenceSystemOut.System System = SilenceSystemOut.get(); diff --git a/akka-docs/rst/java/code/docs/stream/io/StreamTcpDocTest.java b/akka-docs/rst/java/code/docs/stream/io/StreamTcpDocTest.java index c9367fdf1e..c795c37d63 100644 --- a/akka-docs/rst/java/code/docs/stream/io/StreamTcpDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/io/StreamTcpDocTest.java @@ -8,6 +8,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; import akka.NotUsed; import akka.stream.io.Framing; +import docs.AbstractJavaTest; import docs.stream.SilenceSystemOut; import java.net.InetSocketAddress; @@ -26,22 +27,24 @@ import akka.testkit.JavaTestKit; import akka.testkit.TestProbe; import akka.util.ByteString; -public class StreamTcpDocTest { +public class StreamTcpDocTest extends AbstractJavaTest { static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { system = ActorSystem.create("StreamTcpDocTest"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); final SilenceSystemOut.System System = SilenceSystemOut.get(); @@ -77,9 +80,9 @@ public class StreamTcpDocTest { final Flow echo = Flow.of(ByteString.class) .via(Framing.delimiter(ByteString.fromString("\n"), 256, false)) - .map(bytes -> bytes.utf8String()) + .map(ByteString::utf8String) .map(s -> s + "!!!\n") - .map(s -> ByteString.fromString(s)); + .map(ByteString::fromString); connection.handleWith(echo, mat); }, mat); @@ -95,50 +98,33 @@ public class StreamTcpDocTest { final TestProbe serverProbe = new TestProbe(system); final Source> connections = - Tcp.get(system).bind(localhost.getHostName(), localhost.getPort()); // TODO getHostString in Java7 + Tcp.get(system).bind(localhost.getHostString(), localhost.getPort()); //#welcome-banner-chat-server connections.runForeach(connection -> { // server logic, parses incoming commands - final PushStage commandParser = new PushStage() { - @Override public SyncDirective onPush(String elem, Context ctx) { - if (elem.equals("BYE")) - return ctx.finish(); - else - return ctx.push(elem + "!"); - } - }; + final Flow commandParser = + Flow.create() + .takeWhile(elem -> !elem.equals("BYE")) + .map(elem -> elem + "!"); final String welcomeMsg = "Welcome to: " + connection.localAddress() + - " you are: " + connection.remoteAddress() + "!\n"; + " you are: " + connection.remoteAddress() + "!"; - final Source welcome = - Source.single(ByteString.fromString(welcomeMsg)); - final Flow echoFlow = + final Source welcome = Source.single(welcomeMsg); + final Flow serverLogic = Flow.of(ByteString.class) .via(Framing.delimiter(ByteString.fromString("\n"), 256, false)) - .map(bytes -> bytes.utf8String()) + .map(ByteString::utf8String) //#welcome-banner-chat-server .map(command -> { serverProbe.ref().tell(command, null); return command; }) //#welcome-banner-chat-server - .transform(() -> commandParser) + .via(commandParser) + .merge(welcome) .map(s -> s + "\n") - .map(s -> ByteString.fromString(s)); - - final Flow serverLogic = - Flow.fromGraph(GraphDSL.create(builder -> { - final UniformFanInShape concat = - builder.add(Concat.create()); - final FlowShape echo = builder.add(echoFlow); - - builder - .from(builder.add(welcome)).toFanIn(concat) - .from(echo).toFanIn(concat); - - return FlowShape.of(echo.in(), concat.out()); - })); + .map(ByteString::fromString); connection.handleWith(serverLogic, mat); }, mat); @@ -156,27 +142,25 @@ public class StreamTcpDocTest { final Flow> connection = Tcp.get(system).outgoingConnection(localhost.getHostString(), localhost.getPort()); //#repl-client - - final PushStage replParser = new PushStage() { - @Override public SyncDirective onPush(String elem, Context ctx) { - if (elem.equals("q")) - return ctx.pushAndFinish(ByteString.fromString("BYE\n")); - else - return ctx.push(ByteString.fromString(elem + "\n")); - } - }; + final Flow replParser = + Flow.create() + .takeWhile(elem -> !elem.equals("q")) + .concat(Source.single("BYE")) // will run after the original flow completes + .map(elem -> ByteString.fromString(elem + "\n")); final Flow repl = Flow.of(ByteString.class) .via(Framing.delimiter(ByteString.fromString("\n"), 256, false)) - .map(bytes -> bytes.utf8String()) + .map(ByteString::utf8String) .map(text -> {System.out.println("Server: " + text); return "next";}) .map(elem -> readLine("> ")) - .transform(() -> replParser); + .via(replParser); connection.join(repl).run(mat); - //#repl-client + //#repl-client } + + serverProbe.expectMsg("Hello world"); serverProbe.expectMsg("What a lovely day"); serverProbe.expectMsg("BYE"); diff --git a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeByteStrings.java b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeByteStrings.java index 9232d21e42..e4519ad559 100644 --- a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeByteStrings.java +++ b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeByteStrings.java @@ -5,15 +5,11 @@ package docs.stream.javadsl.cookbook; import akka.NotUsed; import akka.actor.ActorSystem; -import akka.stream.ActorMaterializer; -import akka.stream.Materializer; +import akka.stream.*; import akka.stream.javadsl.Flow; import akka.stream.javadsl.Sink; import akka.stream.javadsl.Source; -import akka.stream.stage.Context; -import akka.stream.stage.PushPullStage; -import akka.stream.stage.PushStage; -import akka.stream.stage.SyncDirective; +import akka.stream.stage.*; import akka.testkit.JavaTestKit; import akka.util.ByteString; import org.junit.AfterClass; @@ -24,6 +20,7 @@ import scala.Tuple2; import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertEquals; @@ -31,19 +28,21 @@ import static org.junit.Assert.assertTrue; public class RecipeByteStrings extends RecipeTest { static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { system = ActorSystem.create("RecipeByteStrings"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); final Source rawBytes = Source.from(Arrays.asList( ByteString.fromArray(new byte[] { 1, 2 }), @@ -57,42 +56,77 @@ public class RecipeByteStrings extends RecipeTest { final int CHUNK_LIMIT = 2; //#bytestring-chunker - class Chunker extends PushPullStage { + class Chunker extends GraphStage> { + private final int chunkSize; - private ByteString buffer = ByteString.empty(); + + public Inlet in = Inlet.create("Chunker.in"); + public Outlet out = Outlet.create("Chunker.out"); + private FlowShape shape = FlowShape.of(in, out); public Chunker(int chunkSize) { this.chunkSize = chunkSize; } @Override - public SyncDirective onPush(ByteString elem, Context ctx) { - buffer = buffer.concat(elem); - return emitChunkOrPull(ctx); + public FlowShape shape() { + return shape; } @Override - public SyncDirective onPull(Context ctx) { - return emitChunkOrPull(ctx); + public GraphStageLogic createLogic(Attributes inheritedAttributes) { + return new GraphStageLogic(shape) { + private ByteString buffer = ByteString.empty(); + + { + setHandler(out, new AbstractOutHandler(){ + @Override + public void onPull() throws Exception { + if (isClosed(in)) emitChunk(); + else pull(in); + } + + }); + + setHandler(in, new AbstractInHandler() { + + @Override + public void onPush() throws Exception { + ByteString elem = grab(in); + buffer = buffer.concat(elem); + emitChunk(); + } + + @Override + public void onUpstreamFinish() throws Exception { + if (buffer.isEmpty()) completeStage(); + // elements left in buffer, keep accepting downstream pulls + // and push from buffer until buffer is emitted + } + }); + } + + private void emitChunk() { + if (buffer.isEmpty()) { + if (isClosed(in)) completeStage(); + else pull(in); + } else { + Tuple2 split = buffer.splitAt(chunkSize); + ByteString chunk = split._1(); + buffer = split._2(); + push(out, chunk); + } + } + }; } - public SyncDirective emitChunkOrPull(Context ctx) { - if (buffer.isEmpty()) { - return ctx.pull(); - } else { - Tuple2 split = buffer.splitAt(chunkSize); - ByteString emit = split._1(); - buffer = split._2(); - return ctx.push(emit); - } - } } //#bytestring-chunker { //#bytestring-chunker2 Source chunksStream = - rawBytes.transform(() -> new Chunker(CHUNK_LIMIT)); + rawBytes.via(new Chunker(CHUNK_LIMIT)); //#bytestring-chunker2 CompletionStage> chunksFuture = chunksStream.limit(10).runWith(Sink.seq(), mat); @@ -119,22 +153,49 @@ public class RecipeByteStrings extends RecipeTest { final int SIZE_LIMIT = 9; //#bytes-limiter - class ByteLimiter extends PushStage { + class ByteLimiter extends GraphStage> { + final long maximumBytes; - private int count = 0; + + public Inlet in = Inlet.create("ByteLimiter.in"); + public Outlet out = Outlet.create("ByteLimiter.out"); + private FlowShape shape = FlowShape.of(in, out); public ByteLimiter(long maximumBytes) { this.maximumBytes = maximumBytes; } @Override - public SyncDirective onPush(ByteString chunk, Context ctx) { - count += chunk.size(); - if (count > maximumBytes) { - return ctx.fail(new IllegalStateException("Too much bytes")); - } else { - return ctx.push(chunk); - } + public FlowShape shape() { + return shape; + } + @Override + public GraphStageLogic createLogic(Attributes inheritedAttributes) { + return new GraphStageLogic(shape) { + private int count = 0; + + { + setHandler(out, new AbstractOutHandler() { + @Override + public void onPull() throws Exception { + pull(in); + } + }); + setHandler(in, new AbstractInHandler() { + @Override + public void onPush() throws Exception { + ByteString chunk = grab(in); + count += chunk.size(); + if (count > maximumBytes) { + failStage(new IllegalStateException("Too much bytes")); + } else { + push(out, chunk); + } + } + }); + } + + }; } } //#bytes-limiter @@ -142,7 +203,7 @@ public class RecipeByteStrings extends RecipeTest { { //#bytes-limiter2 Flow limiter = - Flow.of(ByteString.class).transform(() -> new ByteLimiter(SIZE_LIMIT)); + Flow.of(ByteString.class).via(new ByteLimiter(SIZE_LIMIT)); //#bytes-limiter2 final Source bytes1 = Source.from(Arrays.asList( @@ -167,11 +228,12 @@ public class RecipeByteStrings extends RecipeTest { boolean thrown = false; try { bytes2.via(limiter).limit(10).runWith(Sink.seq(), mat).toCompletableFuture().get(3, TimeUnit.SECONDS); - } catch (IllegalStateException ex) { + } catch (ExecutionException ex) { + assertEquals(ex.getCause().getClass(), IllegalStateException.class); thrown = true; } - assertTrue("Expected IllegalStateException to be thrown", thrown); + } }; } @@ -187,7 +249,7 @@ public class RecipeByteStrings extends RecipeTest { ByteString.fromArray(new byte[] { 7, 8, 9 }))); //#compacting-bytestrings - Source compacted = rawBytes.map(bs -> bs.compact()); + Source compacted = rawBytes.map(ByteString::compact); //#compacting-bytestrings List got = compacted.limit(10).runWith(Sink.seq(), mat).toCompletableFuture().get(3, TimeUnit.SECONDS); diff --git a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeDigest.java b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeDigest.java index 0a9c606f6c..b8a9c8dd1d 100644 --- a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeDigest.java +++ b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeDigest.java @@ -5,14 +5,10 @@ package docs.stream.javadsl.cookbook; import akka.NotUsed; import akka.actor.ActorSystem; -import akka.stream.ActorMaterializer; -import akka.stream.Materializer; +import akka.stream.*; import akka.stream.javadsl.Sink; import akka.stream.javadsl.Source; -import akka.stream.stage.Context; -import akka.stream.stage.PushPullStage; -import akka.stream.stage.SyncDirective; -import akka.stream.stage.TerminationDirective; +import akka.stream.stage.*; import akka.testkit.JavaTestKit; import akka.util.ByteString; import org.junit.AfterClass; @@ -21,6 +17,7 @@ import org.junit.Test; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.security.Security; import java.util.Arrays; import java.util.concurrent.TimeUnit; @@ -28,54 +25,84 @@ import static org.junit.Assert.assertEquals; public class RecipeDigest extends RecipeTest { static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { system = ActorSystem.create("RecipeDigest"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); + + //#calculating-digest + class DigestCalculator extends GraphStage> { + private final String algorithm; + public Inlet in = Inlet.create("DigestCalculator.in"); + public Outlet out = Outlet.create("DigestCalculator.out"); + private FlowShape shape = FlowShape.of(in, out); + + public DigestCalculator(String algorithm) { + this.algorithm = algorithm; + } + + @Override + public FlowShape shape() { + return shape; + } + + @Override + public GraphStageLogic createLogic(Attributes inheritedAttributes) { + return new GraphStageLogic(shape) { + final MessageDigest digest; + + { + try { + digest = MessageDigest.getInstance(algorithm); + } catch(NoSuchAlgorithmException ex) { + throw new RuntimeException(ex); + } + + setHandler(out, new AbstractOutHandler() { + @Override + public void onPull() throws Exception { + pull(in); + } + }); + setHandler(in, new AbstractInHandler() { + @Override + public void onPush() throws Exception { + ByteString chunk = grab(in); + digest.update(chunk.toArray()); + pull(in); + } + + @Override + public void onUpstreamFinish() throws Exception { + // If the stream is finished, we need to emit the digest + // before completing + emit(out, ByteString.fromArray(digest.digest())); + completeStage(); + } + }); + } + + + }; + } + + } + //#calculating-digest @Test public void work() throws Exception { new JavaTestKit(system) { - //#calculating-digest - public PushPullStage digestCalculator(String algorithm) - throws NoSuchAlgorithmException { - return new PushPullStage() { - final MessageDigest digest = MessageDigest.getInstance(algorithm); - - @Override - public SyncDirective onPush(ByteString chunk, Context ctx) { - digest.update(chunk.toArray()); - return ctx.pull(); - } - - @Override - public SyncDirective onPull(Context ctx) { - if (ctx.isFinishing()) { - return ctx.pushAndFinish(ByteString.fromArray(digest.digest())); - } else { - return ctx.pull(); - } - } - - @Override - public TerminationDirective onUpstreamFinish(Context ctx) { - // 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) - return ctx.absorbTermination(); - } - }; - } - //#calculating-digest { Source data = Source.from(Arrays.asList( @@ -84,7 +111,7 @@ public class RecipeDigest extends RecipeTest { //#calculating-digest2 final Source digest = data - .transform(() -> digestCalculator("SHA-256")); + .via(new DigestCalculator("SHA-256")); //#calculating-digest2 ByteString got = digest.runWith(Sink.head(), mat).toCompletableFuture().get(3, TimeUnit.SECONDS); diff --git a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeDroppyBroadcast.java b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeDroppyBroadcast.java index e8824f28f8..e70134d889 100644 --- a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeDroppyBroadcast.java +++ b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeDroppyBroadcast.java @@ -20,20 +20,21 @@ import java.util.concurrent.CompletionStage; public class RecipeDroppyBroadcast extends RecipeTest { static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { - system = ActorSystem.create("RecipeLoggingElements"); + system = ActorSystem.create("RecipeDroppyBroadcast"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - @Test public void work() throws Exception { new JavaTestKit(system) { diff --git a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeFlattenList.java b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeFlattenList.java index de2ae6ec15..870ac8092e 100644 --- a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeFlattenList.java +++ b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeFlattenList.java @@ -22,20 +22,21 @@ import static org.junit.Assert.assertEquals; public class RecipeFlattenList extends RecipeTest { static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { system = ActorSystem.create("RecipeFlattenList"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - @Test public void workWithMapConcat() throws Exception { new JavaTestKit(system) { diff --git a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeGlobalRateLimit.java b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeGlobalRateLimit.java index 13655198ef..505af65848 100644 --- a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeGlobalRateLimit.java +++ b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeGlobalRateLimit.java @@ -32,20 +32,21 @@ import static junit.framework.TestCase.assertTrue; public class RecipeGlobalRateLimit extends RecipeTest { static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { system = ActorSystem.create("RecipeGlobalRateLimit"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - static //#global-limiter-actor public class Limiter extends AbstractActor { diff --git a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeHold.java b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeHold.java index dd851b4e11..004ba4f7a0 100644 --- a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeHold.java +++ b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeHold.java @@ -5,20 +5,17 @@ package docs.stream.javadsl.cookbook; import akka.actor.ActorSystem; import akka.japi.Pair; -import akka.stream.ActorMaterializer; -import akka.stream.Materializer; +import akka.stream.*; import akka.stream.javadsl.Keep; import akka.stream.javadsl.Sink; import akka.stream.javadsl.Source; -import akka.stream.stage.DetachedContext; -import akka.stream.stage.DetachedStage; -import akka.stream.stage.DownstreamDirective; -import akka.stream.stage.UpstreamDirective; +import akka.stream.stage.*; import akka.stream.testkit.TestPublisher; import akka.stream.testkit.TestSubscriber; import akka.stream.testkit.javadsl.TestSink; import akka.stream.testkit.javadsl.TestSource; import akka.testkit.JavaTestKit; +import akka.util.ByteString; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -28,64 +25,112 @@ import java.util.concurrent.TimeUnit; public class RecipeHold extends RecipeTest { static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { - system = ActorSystem.create("RecipeMultiGroupBy"); + system = ActorSystem.create("RecipeHold"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - //#hold-version-1 - class HoldWithInitial extends DetachedStage { - private T currentValue; + class HoldWithInitial extends GraphStage> { + + public Inlet in = Inlet.create("HoldWithInitial.in"); + public Outlet out = Outlet.create("HoldWithInitial.out"); + private FlowShape shape = FlowShape.of(in, out); + + private final T initial; public HoldWithInitial(T initial) { - currentValue = initial; + this.initial = initial; } @Override - public UpstreamDirective onPush(T elem, DetachedContext ctx) { - currentValue = elem; - return ctx.pull(); + public FlowShape shape() { + return shape; } @Override - public DownstreamDirective onPull(DetachedContext ctx) { - return ctx.push(currentValue); + public GraphStageLogic createLogic(Attributes inheritedAttributes) { + return new GraphStageLogic(shape) { + private T currentValue = initial; + + { + setHandler(in, new AbstractInHandler() { + @Override + public void onPush() throws Exception { + currentValue = grab(in); + pull(in); + } + }); + setHandler(out, new AbstractOutHandler() { + @Override + public void onPull() throws Exception { + push(out, currentValue); + } + }); + } + + @Override + public void preStart() { + pull(in); + } + }; } } //#hold-version-1 //#hold-version-2 - class HoldWithWait extends DetachedStage { - private T currentValue = null; - private boolean waitingFirstValue = true; + class HoldWithWait extends GraphStage> { + public Inlet in = Inlet.create("HoldWithInitial.in"); + public Outlet out = Outlet.create("HoldWithInitial.out"); + private FlowShape shape = FlowShape.of(in, out); @Override - public UpstreamDirective onPush(T elem, DetachedContext ctx) { - currentValue = elem; - waitingFirstValue = false; - if (ctx.isHoldingDownstream()) { - return ctx.pushAndPull(currentValue); - } else { - return ctx.pull(); - } + public FlowShape shape() { + return shape; } @Override - public DownstreamDirective onPull(DetachedContext ctx) { - if (waitingFirstValue) { - return ctx.holdDownstream(); - } else { - return ctx.push(currentValue); - } + public GraphStageLogic createLogic(Attributes inheritedAttributes) { + return new GraphStageLogic(shape) { + private T currentValue = null; + private boolean waitingFirstValue = true; + + { + setHandler(in, new AbstractInHandler() { + @Override + public void onPush() throws Exception { + currentValue = grab(in); + if (waitingFirstValue) { + waitingFirstValue = false; + if (isAvailable(out)) push(out, currentValue); + } + pull(in); + } + }); + setHandler(out, new AbstractOutHandler() { + @Override + public void onPull() throws Exception { + if (!waitingFirstValue) push(out, currentValue); + } + }); + } + + @Override + public void preStart() { + pull(in); + } + + }; } } //#hold-version-2 @@ -98,7 +143,7 @@ public class RecipeHold extends RecipeTest { final Sink> sink = TestSink.probe(system); Pair, TestSubscriber.Probe> pubSub = - source.transform(() -> new HoldWithInitial<>(0)).toMat(sink, Keep.both()).run(mat); + source.via(new HoldWithInitial<>(0)).toMat(sink, Keep.both()).run(mat); TestPublisher.Probe pub = pubSub.first(); TestSubscriber.Probe sub = pubSub.second(); @@ -126,7 +171,7 @@ public class RecipeHold extends RecipeTest { final Sink> sink = TestSink.probe(system); Pair, TestSubscriber.Probe> pubSub = - source.transform(() -> new HoldWithWait<>()).toMat(sink, Keep.both()).run(mat); + source.via(new HoldWithWait<>()).toMat(sink, Keep.both()).run(mat); TestPublisher.Probe pub = pubSub.first(); TestSubscriber.Probe sub = pubSub.second(); diff --git a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeKeepAlive.java b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeKeepAlive.java index 5d5ee3d622..2dda5845f2 100644 --- a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeKeepAlive.java +++ b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeKeepAlive.java @@ -18,20 +18,21 @@ import java.util.concurrent.TimeUnit; public class RecipeKeepAlive extends RecipeTest { static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { system = ActorSystem.create("RecipeKeepAlive"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - class Tick {} public final Tick TICK = new Tick(); diff --git a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeLoggingElements.java b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeLoggingElements.java index 051416675f..e8266caba2 100644 --- a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeLoggingElements.java +++ b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeLoggingElements.java @@ -25,20 +25,21 @@ import java.util.Arrays; public class RecipeLoggingElements extends RecipeTest { static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { system = ActorSystem.create("RecipeLoggingElements", ConfigFactory.parseString("akka.loglevel=DEBUG\nakka.loggers = [akka.testkit.TestEventListener]")); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - @Test public void workWithPrintln() throws Exception { new JavaTestKit(system) { diff --git a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeManualTrigger.java b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeManualTrigger.java index c06fd76667..f038274691 100644 --- a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeManualTrigger.java +++ b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeManualTrigger.java @@ -22,20 +22,21 @@ import java.util.concurrent.TimeUnit; public class RecipeManualTrigger extends RecipeTest { static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { - system = ActorSystem.create("RecipeKeepAlive"); + system = ActorSystem.create("RecipeManualTrigger"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - class Trigger { } diff --git a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeMissedTicks.java b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeMissedTicks.java index 63d8f4c17b..7e2157706f 100644 --- a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeMissedTicks.java +++ b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeMissedTicks.java @@ -29,20 +29,21 @@ import java.util.concurrent.TimeUnit; public class RecipeMissedTicks extends RecipeTest { static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { - system = ActorSystem.create("RecipeMultiGroupBy"); + system = ActorSystem.create("RecipeMissedTicks"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - @Test public void work() throws Exception { new JavaTestKit(system) { diff --git a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeMultiGroupByTest.java b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeMultiGroupByTest.java index 73080f89ff..905be54919 100644 --- a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeMultiGroupByTest.java +++ b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeMultiGroupByTest.java @@ -31,20 +31,21 @@ import static junit.framework.TestCase.assertTrue; public class RecipeMultiGroupByTest extends RecipeTest { static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { system = ActorSystem.create("RecipeMultiGroupBy"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - static class Topic { private final String name; diff --git a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeParseLines.java b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeParseLines.java index a25b62ef9a..f97bec000c 100644 --- a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeParseLines.java +++ b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeParseLines.java @@ -22,20 +22,21 @@ import java.util.concurrent.TimeUnit; public class RecipeParseLines extends RecipeTest { static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { - system = ActorSystem.create("RecipeLoggingElements"); + system = ActorSystem.create("RecipeParseLines"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - @Test public void parseLines() throws Exception { final Source rawData = Source.from(Arrays.asList( diff --git a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeReduceByKeyTest.java b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeReduceByKeyTest.java index 4ad5cae04b..49ac2c3990 100644 --- a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeReduceByKeyTest.java +++ b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeReduceByKeyTest.java @@ -31,20 +31,21 @@ import java.util.stream.Collectors; public class RecipeReduceByKeyTest extends RecipeTest { static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { - system = ActorSystem.create("RecipeLoggingElements"); + system = ActorSystem.create("RecipeReduceByKey"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - @Test public void work() throws Exception { new JavaTestKit(system) { diff --git a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeSeq.java b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeSeq.java index 8957040b4e..4ddae66123 100644 --- a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeSeq.java +++ b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeSeq.java @@ -24,20 +24,20 @@ import java.util.concurrent.TimeUnit; public class RecipeSeq extends RecipeTest { static ActorSystem system; - + static Materializer mat; @BeforeClass public static void setup() { system = ActorSystem.create("RecipeLoggingElements"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - @Test public void drainSourceToList() throws Exception { new JavaTestKit(system) { diff --git a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeSimpleDrop.java b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeSimpleDrop.java index a4b6003f9d..fb91b893f5 100644 --- a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeSimpleDrop.java +++ b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeSimpleDrop.java @@ -25,20 +25,21 @@ import java.util.concurrent.TimeUnit; public class RecipeSimpleDrop extends RecipeTest { static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { system = ActorSystem.create("RecipeSimpleDrop"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - @Test public void work() throws Exception { new JavaTestKit(system) { diff --git a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeTest.java b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeTest.java index c4f93a76a3..e74b5cdeb0 100644 --- a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeTest.java +++ b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeTest.java @@ -1,6 +1,8 @@ package docs.stream.javadsl.cookbook; -public class RecipeTest { +import docs.AbstractJavaTest; + +public abstract class RecipeTest extends AbstractJavaTest { final class Message { public final String msg; diff --git a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeWorkerPool.java b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeWorkerPool.java index c90a756ad2..2e33894122 100644 --- a/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeWorkerPool.java +++ b/akka-docs/rst/java/code/docs/stream/javadsl/cookbook/RecipeWorkerPool.java @@ -24,20 +24,21 @@ import static org.junit.Assert.assertTrue; public class RecipeWorkerPool extends RecipeTest { static ActorSystem system; + static Materializer mat; @BeforeClass public static void setup() { system = ActorSystem.create("RecipeWorkerPool"); + mat = ActorMaterializer.create(system); } @AfterClass public static void tearDown() { JavaTestKit.shutdownActorSystem(system); system = null; + mat = null; } - final Materializer mat = ActorMaterializer.create(system); - //#worker-pool public static Flow balancer( Flow worker, int workerCount) { diff --git a/akka-docs/rst/java/http/server-side/low-level-server-side-api.rst b/akka-docs/rst/java/http/server-side/low-level-server-side-api.rst index ec883e5e26..1dc660ad0c 100644 --- a/akka-docs/rst/java/http/server-side/low-level-server-side-api.rst +++ b/akka-docs/rst/java/http/server-side/low-level-server-side-api.rst @@ -210,7 +210,7 @@ through the stream starting from the stage which failed, all the way downstream Connections Source failures ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -In the example below we add a custom ``PushStage`` (see :ref:`stream-customize-java`) in order to react to the +In the example below we add a custom ``GraphStage`` (see :ref:`stream-customize-java`) in order to react to the stream's failure. We signal a ``failureMonitor`` actor with the cause why the stream is going down, and let the Actor handle the rest – maybe it'll decide to restart the server or shutdown the ActorSystem, that however is not our concern anymore. diff --git a/akka-docs/rst/java/stream/stream-cookbook.rst b/akka-docs/rst/java/stream/stream-cookbook.rst index 3775870f89..bdd138d62e 100644 --- a/akka-docs/rst/java/stream/stream-cookbook.rst +++ b/akka-docs/rst/java/stream/stream-cookbook.rst @@ -75,17 +75,15 @@ Calculating the digest of a ByteString stream **Situation:** A stream of bytes is given as a stream of ``ByteStrings`` and we want to calculate the cryptographic digest of the stream. -This recipe uses a :class:`PushPullStage` to host a mutable :class:`MessageDigest` class (part of the Java Cryptography +This recipe uses a :class:`GraphStage` to host a mutable :class:`MessageDigest` class (part of the Java Cryptography API) and update it with the bytes arriving from the stream. When the stream starts, the ``onPull`` handler of the stage is called, which just bubbles up the ``pull`` event to its upstream. As a response to this pull, a ByteString chunk will arrive (``onPush``) which we use to update the digest, then it will pull for the next chunk. Eventually the stream of ``ByteStrings`` depletes and we get a notification about this event via ``onUpstreamFinish``. -At this point we want to emit the digest value, but we cannot do it in this handler directly. Instead we call -``ctx.absorbTermination()`` signalling to our context that we do not yet want to finish. When the environment decides that -we can emit further elements ``onPull`` is called again, and we see ``ctx.isFinishing()`` returning ``true`` (since the upstream -source has been depleted already). Since we only want to emit a final element it is enough to call ``ctx.pushAndFinish`` -passing the digest ByteString to be emitted. +At this point we want to emit the digest value, but we cannot do it with ``push`` in this handler directly since there may +be no downstream demand. Instead we call ``emit`` which will temporarily replace the handlers, emit the provided value when +demand comes in and then reset the stage state. It will then complete the stage. .. includecode:: ../code/docs/stream/javadsl/cookbook/RecipeDigest.java#calculating-digest @@ -278,14 +276,11 @@ Create a stream processor that repeats the last element seen of them is slowing down the other by dropping earlier unconsumed elements from the upstream if necessary, and repeating the last value for the downstream if necessary. -We have two options to implement this feature. In both cases we will use :class:`DetachedStage` to build our custom -element (:class:`DetachedStage` is specifically designed for rate translating elements just like ``conflate``, -``expand`` or ``buffer``). In the first version we will use a provided initial value ``initial`` that will be used +We have two options to implement this feature. In both cases we will use :class:`GraphStage` to build our custom +element. In the first version we will use a provided initial value ``initial`` that will be used to feed the downstream if no upstream element is ready yet. In the ``onPush()`` handler we just overwrite the -``currentValue`` variable and immediately relieve the upstream by calling ``pull()`` (remember, implementations of -:class:`DetachedStage` are not allowed to call ``push()`` as a response to ``onPush()`` or call ``pull()`` as a response -of ``onPull()``). The downstream ``onPull`` handler is very similar, we immediately relieve the downstream by -emitting ``currentValue``. +``currentValue`` variable and immediately relieve the upstream by calling ``pull()``. The downstream ``onPull`` handler +is very similar, we immediately relieve the downstream by emitting ``currentValue``. .. includecode:: ../code/docs/stream/javadsl/cookbook/RecipeHold.java#hold-version-1 @@ -296,9 +291,9 @@ case: if the very first element is not yet available. We introduce a boolean variable ``waitingFirstValue`` to denote whether the first element has been provided or not (alternatively an :class:`Optional` can be used for ``currentValue`` or if the element type is a subclass of Object a null can be used with the same purpose). In the downstream ``onPull()`` handler the difference from the previous -version is that we call ``holdDownstream()`` if the first element is not yet available and thus blocking our downstream. The -upstream ``onPush()`` handler sets ``waitingFirstValue`` to false, and after checking if ``holdDownstream()`` has been called it -either relieves the upstream producer, or both the upstream producer and downstream consumer by calling ``pushAndPull()`` +version is that we check if we have received the the first value and only emit if we have. This leads to that when the +first element comes in we must check if there possibly already was demand from downstream so that we in that case can +push the element directly. .. includecode:: ../code/docs/stream/javadsl/cookbook/RecipeHold.java#hold-version-2 @@ -343,14 +338,14 @@ Chunking up a stream of ByteStrings into limited size ByteStrings the same sequence, but capping the size of ByteStrings. In other words we want to slice up ByteStrings into smaller chunks if they exceed a size threshold. -This can be achieved with a single :class:`PushPullStage`. The main logic of our stage is in ``emitChunkOrPull()`` +This can be achieved with a single :class:`GraphStage`. The main logic of our stage is in ``emitChunk()`` which implements the following logic: -* if the buffer is empty, we pull for more bytes +* if the buffer is empty, and upstream is not closed we pull for more bytes, if it is closed we complete * if the buffer is nonEmpty, we split it according to the ``chunkSize``. This will give a next chunk that we will emit, and an empty or nonempty remaining buffer. -Both ``onPush()`` and ``onPull()`` calls ``emitChunkOrPull()`` the only difference is that the push handler also stores +Both ``onPush()`` and ``onPull()`` calls ``emitChunk()`` the only difference is that the push handler also stores the incoming chunk by appending to the end of the buffer. .. includecode:: ../code/docs/stream/javadsl/cookbook/RecipeByteStrings.java#bytestring-chunker @@ -363,7 +358,7 @@ Limit the number of bytes passing through a stream of ByteStrings **Situation:** Given a stream of ByteStrings we want to fail the stream if more than a given maximum of bytes has been consumed. -This recipe uses a :class:`PushStage` to implement the desired feature. In the only handler we override, +This recipe uses a :class:`GraphStage` to implement the desired feature. In the only handler we override, ``onPush()`` we just update a counter and see if it gets larger than ``maximumBytes``. If a violation happens we signal failure, otherwise we forward the chunk we have received. diff --git a/akka-docs/rst/java/stream/stream-flows-and-basics.rst b/akka-docs/rst/java/stream/stream-flows-and-basics.rst index aadc3b3bc6..2e68327e57 100644 --- a/akka-docs/rst/java/stream/stream-flows-and-basics.rst +++ b/akka-docs/rst/java/stream/stream-flows-and-basics.rst @@ -36,9 +36,8 @@ Graph is running. Processing Stage The common name for all building blocks that build up a Graph. - Examples of a processing stage would be operations like ``map()``, ``filter()``, stages added by ``transform()`` like - :class:`PushStage`, :class:`PushPullStage`, :class:`StatefulStage` and graph junctions like ``Merge`` or ``Broadcast``. - For the full list of built-in processing stages see :ref:`stages-overview_java` + Examples of a processing stage would be operations like ``map()``, ``filter()``, custom ``GraphStage`` s and graph + junctions like ``Merge`` or ``Broadcast``. For the full list of built-in processing stages see :ref:`stages-overview_java` When we talk about *asynchronous, non-blocking backpressure* we mean that the processing stages available in Akka Streams will not use blocking calls but asynchronous message passing to exchange messages between each other, and they diff --git a/akka-docs/rst/java/stream/stream-io.rst b/akka-docs/rst/java/stream/stream-io.rst index 17f2bdc12a..d3fb4fd808 100644 --- a/akka-docs/rst/java/stream/stream-io.rst +++ b/akka-docs/rst/java/stream/stream-io.rst @@ -86,16 +86,11 @@ it makes sense to make the Server initiate the conversation by emitting a "hello .. includecode:: ../code/docs/stream/io/StreamTcpDocTest.java#welcome-banner-chat-server -The way we constructed a :class:`Flow` using the :class:`GraphDSL` is explained in detail in -:ref:`constructing-sources-sinks-flows-from-partial-graphs-java`, however the basic concepts is rather simple– -we can encapsulate arbitrarily complex logic within a :class:`Flow` as long as it exposes the same interface, which means -exposing exactly one :class:`Outlet` and exactly one :class:`Inlet` which will be connected to the TCP -pipeline. In this example we use a :class:`Concat` graph processing stage to inject the initial message, and then -continue with handling all incoming data using the echo handler. You should use this pattern of encapsulating complex -logic in Flows and attaching those to :class:`StreamIO` in order to implement your custom and possibly sophisticated TCP servers. +To emit the initial message we merge a ``Source`` with a single element, after the command processing but before the +framing and transformation to ``ByteStrings`` this way we do not have to repeat such logic. In this example both client and server may need to close the stream based on a parsed command - ``BYE`` in the case -of the server, and ``q`` in the case of the client. This is implemented by using a custom :class:`PushStage` +of the server, and ``q`` in the case of the client. This is implemented by using a custom :class:`GraphStage` which completes the stream once it encounters such command. Streaming File IO diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/HttpServerExampleSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/HttpServerExampleSpec.scala index 2733e221ce..924a9ad6c5 100644 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/HttpServerExampleSpec.scala +++ b/akka-docs/rst/scala/code/docs/http/scaladsl/HttpServerExampleSpec.scala @@ -11,7 +11,6 @@ import akka.http.scaladsl.Http.ServerBinding import akka.http.scaladsl.model._ import akka.stream.ActorMaterializer import akka.stream.scaladsl.{ Flow, Sink } -import akka.stream.stage.{ Context, PushStage } import akka.testkit.TestActors import org.scalatest.{ Matchers, WordSpec } import scala.language.postfixOps @@ -107,17 +106,9 @@ class HttpServerExampleSpec extends WordSpec with Matchers { val failureMonitor: ActorRef = system.actorOf(MyExampleMonitoringActor.props) val reactToTopLevelFailures = Flow[IncomingConnection] - .transform { () => - new PushStage[IncomingConnection, IncomingConnection] { - override def onPush(elem: IncomingConnection, ctx: Context[IncomingConnection]) = - ctx.push(elem) - - override def onUpstreamFailure(cause: Throwable, ctx: Context[IncomingConnection]) = { - failureMonitor ! cause - super.onUpstreamFailure(cause, ctx) - } - } - } + .watchTermination()((_, termination) => termination.onFailure { + case cause => failureMonitor ! cause + }) serverSource .via(reactToTopLevelFailures) @@ -134,16 +125,10 @@ class HttpServerExampleSpec extends WordSpec with Matchers { val serverSource = Http().bind(host, port) val reactToConnectionFailure = Flow[HttpRequest] - .transform { () => - new PushStage[HttpRequest, HttpRequest] { - override def onPush(elem: HttpRequest, ctx: Context[HttpRequest]) = - ctx.push(elem) - - override def onUpstreamFailure(cause: Throwable, ctx: Context[HttpRequest]) = { - // handle the failure somehow - super.onUpstreamFailure(cause, ctx) - } - } + .recover[HttpRequest] { + case ex => + // handle the failure somehow + throw ex } val httpEcho = Flow[HttpRequest] diff --git a/akka-docs/rst/scala/code/docs/stream/BidiFlowDocSpec.scala b/akka-docs/rst/scala/code/docs/stream/BidiFlowDocSpec.scala index 2182a5b190..2d3959a676 100644 --- a/akka-docs/rst/scala/code/docs/stream/BidiFlowDocSpec.scala +++ b/akka-docs/rst/scala/code/docs/stream/BidiFlowDocSpec.scala @@ -66,52 +66,68 @@ object BidiFlowDocSpec { ByteString.newBuilder.putInt(len).append(bytes).result() } - class FrameParser extends PushPullStage[ByteString, ByteString] { - // this holds the received but not yet parsed bytes - var stash = ByteString.empty - // this holds the current message length or -1 if at a boundary - var needed = -1 + class FrameParser extends GraphStage[FlowShape[ByteString, ByteString]] { - override def onPush(bytes: ByteString, ctx: Context[ByteString]) = { - stash ++= bytes - run(ctx) - } - override def onPull(ctx: Context[ByteString]) = run(ctx) - override def onUpstreamFinish(ctx: Context[ByteString]) = - if (stash.isEmpty) ctx.finish() - else ctx.absorbTermination() // we still have bytes to emit + val in = Inlet[ByteString]("FrameParser.in") + val out = Outlet[ByteString]("FrameParser.out") + override val shape = FlowShape.of(in, out) - private def run(ctx: Context[ByteString]): SyncDirective = - if (needed == -1) { - // are we at a boundary? then figure out next length - if (stash.length < 4) pullOrFinish(ctx) - else { - needed = stash.iterator.getInt - stash = stash.drop(4) - run(ctx) // cycle back to possibly already emit the next chunk + override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) { + + // this holds the received but not yet parsed bytes + var stash = ByteString.empty + // this holds the current message length or -1 if at a boundary + var needed = -1 + + setHandler(out, new OutHandler { + override def onPull(): Unit = { + if (isClosed(in)) run() + else pull(in) + } + }) + setHandler(in, new InHandler { + override def onPush(): Unit = { + val bytes = grab(in) + stash = stash ++ bytes + run() } - } else if (stash.length < needed) { - // we are in the middle of a message, need more bytes - pullOrFinish(ctx) - } else { - // we have enough to emit at least one message, so do it - val emit = stash.take(needed) - stash = stash.drop(needed) - needed = -1 - ctx.push(emit) - } - /* - * After having called absorbTermination() we cannot pull any more, so if we need - * more data we will just have to give up. - */ - private def pullOrFinish(ctx: Context[ByteString]) = - if (ctx.isFinishing) ctx.finish() - else ctx.pull() + override def onUpstreamFinish(): Unit = { + if (stash.isEmpty) completeStage() + // wait with completion and let run() complete when the + // rest of the stash has been sent downstream + } + }) + + private def run(): Unit = { + if (needed == -1) { + // are we at a boundary? then figure out next length + if (stash.length < 4) { + if (isClosed(in)) completeStage() + else pull(in) + } else { + needed = stash.iterator.getInt + stash = stash.drop(4) + run() // cycle back to possibly already emit the next chunk + } + } else if (stash.length < needed) { + // we are in the middle of a message, need more bytes, + // or have to stop if input closed + if (isClosed(in)) completeStage() + else pull(in) + } else { + // we have enough to emit at least one message, so do it + val emit = stash.take(needed) + stash = stash.drop(needed) + needed = -1 + push(out, emit) + } + } + } } val outbound = b.add(Flow[ByteString].map(addLengthHeader)) - val inbound = b.add(Flow[ByteString].transform(() => new FrameParser)) + val inbound = b.add(Flow[ByteString].via(new FrameParser)) BidiShape.fromFlows(outbound, inbound) }) //#framing diff --git a/akka-docs/rst/scala/code/docs/stream/FlowStagesSpec.scala b/akka-docs/rst/scala/code/docs/stream/FlowStagesSpec.scala deleted file mode 100644 index 28f79286b4..0000000000 --- a/akka-docs/rst/scala/code/docs/stream/FlowStagesSpec.scala +++ /dev/null @@ -1,192 +0,0 @@ -package docs.stream - -import akka.stream._ -import akka.stream.scaladsl.{ Sink, Source, Flow, Keep } -import akka.stream.testkit.AkkaSpec -import org.scalatest.concurrent.ScalaFutures - -import scala.collection.immutable -import scala.concurrent.Await -import scala.concurrent.duration._ - -class FlowStagesSpec extends AkkaSpec with ScalaFutures { - //#import-stage - import akka.stream.stage._ - //#import-stage - - implicit val materializer = ActorMaterializer() - - "stages demo" must { - - "demonstrate various PushPullStages" in { - - //#one-to-one - class Map[A, B](f: A => B) extends PushPullStage[A, B] { - override def onPush(elem: A, ctx: Context[B]): SyncDirective = - ctx.push(f(elem)) - - override def onPull(ctx: Context[B]): SyncDirective = - ctx.pull() - } - //#one-to-one - - //#many-to-one - class Filter[A](p: A => Boolean) extends PushPullStage[A, A] { - override def onPush(elem: A, ctx: Context[A]): SyncDirective = - if (p(elem)) ctx.push(elem) - else ctx.pull() - - override def onPull(ctx: Context[A]): SyncDirective = - ctx.pull() - } - //#many-to-one - - //#one-to-many - class Duplicator[A]() extends PushPullStage[A, A] { - private var lastElem: A = _ - private var oneLeft = false - - override def onPush(elem: A, ctx: Context[A]): SyncDirective = { - lastElem = elem - oneLeft = true - ctx.push(elem) - } - - override def onPull(ctx: Context[A]): SyncDirective = - if (!ctx.isFinishing) { - // the main pulling logic is below as it is demonstrated on the illustration - if (oneLeft) { - oneLeft = false - ctx.push(lastElem) - } else - ctx.pull() - } else { - // If we need to emit a final element after the upstream - // finished - if (oneLeft) ctx.pushAndFinish(lastElem) - else ctx.finish() - } - - override def onUpstreamFinish(ctx: Context[A]): TerminationDirective = - ctx.absorbTermination() - - } - //#one-to-many - - val keyedSink = Sink.head[immutable.Seq[Int]] - val sink = Flow[Int].grouped(10).toMat(keyedSink)(Keep.right) - - //#stage-chain - val resultFuture = Source(1 to 10) - .transform(() => new Filter(_ % 2 == 0)) - .transform(() => new Duplicator()) - .transform(() => new Map(_ / 2)) - .runWith(sink) - //#stage-chain - - Await.result(resultFuture, 3.seconds) should be(Seq(1, 1, 2, 2, 3, 3, 4, 4, 5, 5)) - - } - - "demonstrate various PushStages" in { - - import akka.stream.stage._ - - //#pushstage - class Map[A, B](f: A => B) extends PushStage[A, B] { - override def onPush(elem: A, ctx: Context[B]): SyncDirective = - ctx.push(f(elem)) - } - - class Filter[A](p: A => Boolean) extends PushStage[A, A] { - override def onPush(elem: A, ctx: Context[A]): SyncDirective = - if (p(elem)) ctx.push(elem) - else ctx.pull() - } - //#pushstage - } - - "demonstrate GraphStage" in { - - //#doubler-stateful - class Duplicator[A] extends GraphStage[FlowShape[A, A]] { - val in = Inlet[A]("Duplicator.in") - val out = Outlet[A]("Duplicator.out") - - val shape: FlowShape[A, A] = FlowShape(in, out) - - override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = - new GraphStageLogic(shape) { - setHandler(in, new InHandler { - override def onPush(): Unit = { - val elem = grab(in) - emitMultiple(out, List(elem, elem)) - } - }) - - setHandler(out, new OutHandler { - override def onPull(): Unit = pull(in) - }) - } - } - //#doubler-stateful - - val duplicator = Flow.fromGraph(new Duplicator[Int]) - val fold = Source(1 to 2).via(duplicator).runFold("")(_ + _) - whenReady(fold) { s ⇒ - s should be("1122") - } - - } - - "demonstrate DetachedStage" in { - //#detached - class Buffer2[T]() extends DetachedStage[T, T] { - private var buf = Vector.empty[T] - private var capacity = 2 - - private def isFull = capacity == 0 - private def isEmpty = capacity == 2 - - private def dequeue(): T = { - capacity += 1 - val next = buf.head - buf = buf.tail - next - } - - private def enqueue(elem: T) = { - capacity -= 1 - buf = buf :+ elem - } - - override def onPull(ctx: DetachedContext[T]): DownstreamDirective = { - if (isEmpty) { - if (ctx.isFinishing) ctx.finish() // No more elements will arrive - else ctx.holdDownstream() // waiting until new elements - } else { - val next = dequeue() - if (ctx.isHoldingUpstream) ctx.pushAndPull(next) // release upstream - else ctx.push(next) - } - } - - override def onPush(elem: T, ctx: DetachedContext[T]): UpstreamDirective = { - enqueue(elem) - if (isFull) ctx.holdUpstream() // Queue is now full, wait until new empty slot - else { - if (ctx.isHoldingDownstream) ctx.pushAndPull(dequeue()) // Release downstream - else ctx.pull() - } - } - - override def onUpstreamFinish(ctx: DetachedContext[T]): TerminationDirective = { - if (!isEmpty) ctx.absorbTermination() // still need to flush from buffer - else ctx.finish() // already empty, finishing - } - } - //#detached - } - } - -} diff --git a/akka-docs/rst/scala/code/docs/stream/GraphStageDocSpec.scala b/akka-docs/rst/scala/code/docs/stream/GraphStageDocSpec.scala index c25d68100d..54859c00c7 100644 --- a/akka-docs/rst/scala/code/docs/stream/GraphStageDocSpec.scala +++ b/akka-docs/rst/scala/code/docs/stream/GraphStageDocSpec.scala @@ -9,6 +9,7 @@ import akka.stream.stage._ import akka.stream._ import akka.stream.testkit.{ TestPublisher, TestSubscriber, AkkaSpec } +import akka.testkit.TestLatch import scala.collection.mutable import scala.concurrent.{ Promise, Await, Future } @@ -271,6 +272,7 @@ class GraphStageDocSpec extends AkkaSpec { "Demonstrate an asynchronous side channel" in { import system.dispatcher + //#async-side-channel // will close upstream when the future completes class KillSwitch[A](switch: Future[Unit]) extends GraphStage[FlowShape[A, A]] { @@ -301,20 +303,31 @@ class GraphStageDocSpec extends AkkaSpec { //#async-side-channel // tests: + val switch = Promise[Unit]() val duplicator = Flow.fromGraph(new KillSwitch[Int](switch.future)) - // TODO this is probably racey, is there a way to make sure it happens after? - val valueAfterKill = switch.future.flatMap(_ => Future(4)) + val in = TestPublisher.probe[Int]() + val out = TestSubscriber.probe[Int]() - val result = - Source(Vector(1, 2, 3)).concat(Source.fromFuture(valueAfterKill)) - .via(duplicator) - .runFold(Seq.empty[Int])((elem, acc) => elem :+ acc) + Source.fromPublisher(in) + .via(duplicator) + .to(Sink.fromSubscriber(out)) + .withAttributes(Attributes.inputBuffer(1, 1)) + .run() + + val sub = in.expectSubscription() + + out.request(1) + + sub.expectRequest() + sub.sendNext(1) + + out.expectNext(1) switch.success(Unit) - Await.result(result, 3.seconds) should ===(Seq(1, 2, 3)) + out.expectComplete() } "Demonstrate a graph stage with a timer" in { diff --git a/akka-docs/rst/scala/code/docs/stream/cookbook/RecipeByteStrings.scala b/akka-docs/rst/scala/code/docs/stream/cookbook/RecipeByteStrings.scala index 28248ac255..8f3ddab71d 100644 --- a/akka-docs/rst/scala/code/docs/stream/cookbook/RecipeByteStrings.scala +++ b/akka-docs/rst/scala/code/docs/stream/cookbook/RecipeByteStrings.scala @@ -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))) diff --git a/akka-docs/rst/scala/code/docs/stream/cookbook/RecipeDigest.scala b/akka-docs/rst/scala/code/docs/stream/cookbook/RecipeDigest.scala index 2ad50e1c27..cd43252e70 100644 --- a/akka-docs/rst/scala/code/docs/stream/cookbook/RecipeDigest.scala +++ b/akka-docs/rst/scala/code/docs/stream/cookbook/RecipeDigest.scala @@ -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( diff --git a/akka-docs/rst/scala/code/docs/stream/cookbook/RecipeHold.scala b/akka-docs/rst/scala/code/docs/stream/cookbook/RecipeHold.scala index 928392e8ca..6b8cd8b511 100644 --- a/akka-docs/rst/scala/code/docs/stream/cookbook/RecipeHold.scala +++ b/akka-docs/rst/scala/code/docs/stream/cookbook/RecipeHold.scala @@ -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) diff --git a/akka-docs/rst/scala/code/docs/stream/io/StreamTcpDocSpec.scala b/akka-docs/rst/scala/code/docs/stream/io/StreamTcpDocSpec.scala index aad39f6f16..d861fe26ba 100644 --- a/akka-docs/rst/scala/code/docs/stream/io/StreamTcpDocSpec.scala +++ b/akka-docs/rst/scala/code/docs/stream/io/StreamTcpDocSpec.scala @@ -8,7 +8,6 @@ import java.util.concurrent.atomic.AtomicReference import akka.stream._ import akka.stream.scaladsl.Tcp._ import akka.stream.scaladsl._ -import akka.stream.stage.{ Context, PushStage, SyncDirective } import akka.stream.testkit.AkkaSpec import akka.testkit.TestProbe import akka.util.ByteString @@ -70,46 +69,29 @@ class StreamTcpDocSpec extends AkkaSpec { import akka.stream.io.Framing //#welcome-banner-chat-server - connections runForeach { connection => + connections.runForeach { connection => - val serverLogic = Flow.fromGraph(GraphDSL.create() { implicit b => - import GraphDSL.Implicits._ + // server logic, parses incoming commands + val commandParser = Flow[String].takeWhile(_ != "BYE").map(_ + "!") - // server logic, parses incoming commands - val commandParser = new PushStage[String, String] { - override def onPush(elem: String, ctx: Context[String]): SyncDirective = { - elem match { - case "BYE" ⇒ ctx.finish() - case _ ⇒ ctx.push(elem + "!") - } - } - } + import connection._ + val welcomeMsg = s"Welcome to: $localAddress, you are: $remoteAddress!" + val welcome = Source.single(welcomeMsg) - import connection._ - val welcomeMsg = s"Welcome to: $localAddress, you are: $remoteAddress!\n" - - val welcome = Source.single(ByteString(welcomeMsg)) - val echo = b.add(Flow[ByteString] - .via(Framing.delimiter( - ByteString("\n"), - maximumFrameLength = 256, - allowTruncation = true)) - .map(_.utf8String) - //#welcome-banner-chat-server - .map { command ⇒ serverProbe.ref ! command; command } - //#welcome-banner-chat-server - .transform(() ⇒ commandParser) - .map(_ + "\n") - .map(ByteString(_))) - - val concat = b.add(Concat[ByteString]()) - // first we emit the welcome message, - welcome ~> concat.in(0) - // then we continue using the echo-logic Flow - echo.outlet ~> concat.in(1) - - FlowShape(echo.in, concat.out) - }) + val serverLogic = Flow[ByteString] + .via(Framing.delimiter( + ByteString("\n"), + maximumFrameLength = 256, + allowTruncation = true)) + .map(_.utf8String) + //#welcome-banner-chat-server + .map { command ⇒ serverProbe.ref ! command; command } + //#welcome-banner-chat-server + .via(commandParser) + // merge in the initial banner after parser + .merge(welcome) + .map(_ + "\n") + .map(ByteString(_)) connection.handleWith(serverLogic) } @@ -135,14 +117,10 @@ class StreamTcpDocSpec extends AkkaSpec { val connection = Tcp().outgoingConnection(localhost) //#repl-client - val replParser = new PushStage[String, ByteString] { - override def onPush(elem: String, ctx: Context[ByteString]): SyncDirective = { - elem match { - case "q" ⇒ ctx.pushAndFinish(ByteString("BYE\n")) - case _ ⇒ ctx.push(ByteString(s"$elem\n")) - } - } - } + val replParser = + Flow[String].takeWhile(_ != "q") + .concat(Source.single("BYE")) + .map(elem => ByteString(s"$elem\n")) val repl = Flow[ByteString] .via(Framing.delimiter( @@ -152,7 +130,7 @@ class StreamTcpDocSpec extends AkkaSpec { .map(_.utf8String) .map(text => println("Server: " + text)) .map(_ => readLine("> ")) - .transform(() ⇒ replParser) + .via(replParser) connection.join(repl).run() } diff --git a/akka-docs/rst/scala/http/low-level-server-side-api.rst b/akka-docs/rst/scala/http/low-level-server-side-api.rst index 91b7faadd2..666d9102aa 100644 --- a/akka-docs/rst/scala/http/low-level-server-side-api.rst +++ b/akka-docs/rst/scala/http/low-level-server-side-api.rst @@ -213,7 +213,7 @@ through the stream starting from the stage which failed, all the way downstream Connections Source failures ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -In the example below we add a custom ``PushStage`` (see :ref:`stream-customize-scala`) in order to react to the +In the example below we add a custom ``GraphStage`` (see :ref:`stream-customize-scala`) in order to react to the stream's failure. We signal a ``failureMonitor`` actor with the cause why the stream is going down, and let the Actor handle the rest – maybe it'll decide to restart the server or shutdown the ActorSystem, that however is not our concern anymore. diff --git a/akka-docs/rst/scala/stream/stream-cookbook.rst b/akka-docs/rst/scala/stream/stream-cookbook.rst index 139f993be3..30abe02653 100644 --- a/akka-docs/rst/scala/stream/stream-cookbook.rst +++ b/akka-docs/rst/scala/stream/stream-cookbook.rst @@ -75,17 +75,15 @@ Calculating the digest of a ByteString stream **Situation:** A stream of bytes is given as a stream of ``ByteStrings`` and we want to calculate the cryptographic digest of the stream. -This recipe uses a :class:`PushPullStage` to host a mutable :class:`MessageDigest` class (part of the Java Cryptography +This recipe uses a :class:`GraphStage` to host a mutable :class:`MessageDigest` class (part of the Java Cryptography API) and update it with the bytes arriving from the stream. When the stream starts, the ``onPull`` handler of the stage is called, which just bubbles up the ``pull`` event to its upstream. As a response to this pull, a ByteString chunk will arrive (``onPush``) which we use to update the digest, then it will pull for the next chunk. Eventually the stream of ``ByteStrings`` depletes and we get a notification about this event via ``onUpstreamFinish``. -At this point we want to emit the digest value, but we cannot do it in this handler directly. Instead we call -``ctx.absorbTermination()`` signalling to our context that we do not yet want to finish. When the environment decides that -we can emit further elements ``onPull`` is called again, and we see ``ctx.isFinishing`` returning ``true`` (since the upstream -source has been depleted already). Since we only want to emit a final element it is enough to call ``ctx.pushAndFinish`` -passing the digest ByteString to be emitted. +At this point we want to emit the digest value, but we cannot do it with ``push`` in this handler directly since there may +be no downstream demand. Instead we call ``emit`` which will temporarily replace the handlers, emit the provided value when +demand comes in and then reset the stage state. It will then complete the stage. .. includecode:: ../code/docs/stream/cookbook/RecipeDigest.scala#calculating-digest @@ -271,14 +269,11 @@ Create a stream processor that repeats the last element seen of them is slowing down the other by dropping earlier unconsumed elements from the upstream if necessary, and repeating the last value for the downstream if necessary. -We have two options to implement this feature. In both cases we will use :class:`DetachedStage` to build our custom -element (:class:`DetachedStage` is specifically designed for rate translating elements just like ``conflate``, -``expand`` or ``buffer``). In the first version we will use a provided initial value ``initial`` that will be used +We have two options to implement this feature. In both cases we will use :class:`GraphStage` to build our custom +element. In the first version we will use a provided initial value ``initial`` that will be used to feed the downstream if no upstream element is ready yet. In the ``onPush()`` handler we just overwrite the -``currentValue`` variable and immediately relieve the upstream by calling ``pull()`` (remember, implementations of -:class:`DetachedStage` are not allowed to call ``push()`` as a response to ``onPush()`` or call ``pull()`` as a response -of ``onPull()``). The downstream ``onPull`` handler is very similar, we immediately relieve the downstream by -emitting ``currentValue``. +``currentValue`` variable and immediately relieve the upstream by calling ``pull()``. The downstream ``onPull`` handler +is very similar, we immediately relieve the downstream by emitting ``currentValue``. .. includecode:: ../code/docs/stream/cookbook/RecipeHold.scala#hold-version-1 @@ -289,9 +284,9 @@ case: if the very first element is not yet available. We introduce a boolean variable ``waitingFirstValue`` to denote whether the first element has been provided or not (alternatively an :class:`Option` can be used for ``currentValue`` or if the element type is a subclass of AnyRef a null can be used with the same purpose). In the downstream ``onPull()`` handler the difference from the previous -version is that we call ``holdDownstream()`` if the first element is not yet available and thus blocking our downstream. The -upstream ``onPush()`` handler sets ``waitingFirstValue`` to false, and after checking if ``holdDownstream()`` has been called it -either relieves the upstream producer, or both the upstream producer and downstream consumer by calling ``pushAndPull()`` +version is that we check if we have received the the first value and only emit if we have. This leads to that when the +first element comes in we must check if there possibly already was demand from downstream so that we in that case can +push the element directly. .. includecode:: ../code/docs/stream/cookbook/RecipeHold.scala#hold-version-2 @@ -336,14 +331,14 @@ Chunking up a stream of ByteStrings into limited size ByteStrings the same sequence, but capping the size of ByteStrings. In other words we want to slice up ByteStrings into smaller chunks if they exceed a size threshold. -This can be achieved with a single :class:`PushPullStage`. The main logic of our stage is in ``emitChunkOrPull()`` +This can be achieved with a single :class:`GraphStage`. The main logic of our stage is in ``emitChunk()`` which implements the following logic: -* if the buffer is empty, we pull for more bytes +* if the buffer is empty, and upstream is not closed we pull for more bytes, if it is closed we complete * if the buffer is nonEmpty, we split it according to the ``chunkSize``. This will give a next chunk that we will emit, and an empty or nonempty remaining buffer. -Both ``onPush()`` and ``onPull()`` calls ``emitChunkOrPull()`` the only difference is that the push handler also stores +Both ``onPush()`` and ``onPull()`` calls ``emitChunk()`` the only difference is that the push handler also stores the incoming chunk by appending to the end of the buffer. .. includecode:: ../code/docs/stream/cookbook/RecipeByteStrings.scala#bytestring-chunker @@ -354,7 +349,7 @@ Limit the number of bytes passing through a stream of ByteStrings **Situation:** Given a stream of ByteStrings we want to fail the stream if more than a given maximum of bytes has been consumed. -This recipe uses a :class:`PushStage` to implement the desired feature. In the only handler we override, +This recipe uses a :class:`GraphStage` to implement the desired feature. In the only handler we override, ``onPush()`` we just update a counter and see if it gets larger than ``maximumBytes``. If a violation happens we signal failure, otherwise we forward the chunk we have received. diff --git a/akka-docs/rst/scala/stream/stream-flows-and-basics.rst b/akka-docs/rst/scala/stream/stream-flows-and-basics.rst index d32b4dceef..0778932066 100644 --- a/akka-docs/rst/scala/stream/stream-flows-and-basics.rst +++ b/akka-docs/rst/scala/stream/stream-flows-and-basics.rst @@ -36,9 +36,8 @@ Graph is running. Processing Stage The common name for all building blocks that build up a Graph. - Examples of a processing stage would be operations like ``map()``, ``filter()``, stages added by ``transform()`` like - :class:`PushStage`, :class:`PushPullStage`, :class:`StatefulStage` and graph junctions like ``Merge`` or ``Broadcast``. - For the full list of built-in processing stages see :ref:`stages-overview_scala` + Examples of a processing stage would be operations like ``map()``, ``filter()``, custom ``GraphStage`` s and graph + junctions like ``Merge`` or ``Broadcast``. For the full list of built-in processing stages see :ref:`stages-overview_scala` When we talk about *asynchronous, non-blocking backpressure* we mean that the processing stages available in Akka Streams will not use blocking calls but asynchronous message passing to exchange messages between each other, and they diff --git a/akka-docs/rst/scala/stream/stream-io.rst b/akka-docs/rst/scala/stream/stream-io.rst index bb59479a04..d43f941c2b 100644 --- a/akka-docs/rst/scala/stream/stream-io.rst +++ b/akka-docs/rst/scala/stream/stream-io.rst @@ -86,17 +86,12 @@ it makes sense to make the Server initiate the conversation by emitting a "hello .. includecode:: ../code/docs/stream/io/StreamTcpDocSpec.scala#welcome-banner-chat-server -The way we constructed a :class:`Flow` using the :class:`GraphDSL` is explained in detail in -:ref:`constructing-sources-sinks-flows-from-partial-graphs-scala`, however the basic concepts is rather simple– -we can encapsulate arbitrarily complex logic within a :class:`Flow` as long as it exposes the same interface, which means -exposing exactly one :class:`Outlet` and exactly one :class:`Inlet` which will be connected to the TCP -pipeline. In this example we use a :class:`Concat` graph processing stage to inject the initial message, and then -continue with handling all incoming data using the echo handler. You should use this pattern of encapsulating complex -logic in Flows and attaching those to :class:`StreamIO` in order to implement your custom and possibly sophisticated TCP servers. +To emit the initial message we merge a ``Source`` with a single element, after the command processing but before the +framing and transformation to ``ByteStrings`` this way we do not have to repeat such logic. In this example both client and server may need to close the stream based on a parsed command - ``BYE`` in the case -of the server, and ``q`` in the case of the client. This is implemented by using a custom :class:`PushStage` -which completes the stream once it encounters such command. +of the server, and ``q`` in the case of the client. This is implemented by taking from the stream until ``q`` and +and concatenating a ``Source`` with a single ``BYE`` element which will then be sent after the original source completed. Streaming File IO =================