From 4bd6b7aab1cde1cfecd219a36ce9fef7dca544d9 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 13 Dec 2016 10:59:29 +0100 Subject: [PATCH] improve AbstractActor, #21717 * Receive class that wraps PartialFunction, to avoid scary scala types * move AbstractActorContext to AbstractActor.ActorContext * converting docs, many, many UntypedActor * removing UntypedActor docs * add unit test for ReceiveBuilder * MiMa filters * consistent use of getContext(), self(), sender() * rename cross references * migration guide * skip samples for now * improve match type safetyi, add matchUnchecked * the `? extends P` caused code like this to compile: `match(String.class, (Integer i) -> {})` * added matchUnchecked, since it can still be useful (um, convenient) to be able to do: `matchUnchecked(List.class, (List list) -> {})` * eleminate some scala.Option * preRestart * findChild * ActorIdentity.getActorRef --- .../java/akka/actor/ActorCreationTest.java | 82 +- .../src/test/java/akka/actor/JavaAPI.java | 5 +- .../java/akka/actor/JavaAPITestActor.java | 2 +- .../test/java/akka/actor/NonPublicClass.java | 2 +- .../java/akka/actor/NonStaticCreator.java | 4 +- .../akka/actor/StashJavaAPITestActors.java | 45 +- .../test/java/akka/event/ActorWithMDC.java | 67 +- .../test/java/akka/japi/MatchBuilderTest.java | 4 +- .../test/java/akka/japi/pf/PFBuilderTest.java | 3 +- .../java/akka/japi/pf/ReceiveBuilderTest.java | 261 ++++ .../test/java/akka/pattern/PatternsTest.java | 2 +- .../test/scala/akka/event/LoggerSpec.scala | 6 +- .../akka/serialization/SerializeSpec.scala | 7 +- .../main/java/akka/japi/pf/AbstractMatch.java | 2 - .../java/akka/japi/pf/AbstractPFBuilder.java | 1 - .../java/akka/japi/pf/DeciderBuilder.java | 1 - akka-actor/src/main/java/akka/japi/pf/FI.java | 1 - .../akka/japi/pf/FSMStateFunctionBuilder.java | 1 - .../java/akka/japi/pf/FSMStopBuilder.java | 1 - .../japi/pf/FSMTransitionHandlerBuilder.java | 3 +- .../src/main/java/akka/japi/pf/Match.java | 44 +- .../src/main/java/akka/japi/pf/PFBuilder.java | 51 +- .../java/akka/japi/pf/ReceiveBuilder.java | 195 ++- .../src/main/java/akka/japi/pf/UnitMatch.java | 33 +- .../main/java/akka/japi/pf/UnitPFBuilder.java | 52 +- .../main/scala/akka/actor/AbstractActor.scala | 233 +++- .../main/scala/akka/actor/AbstractFSM.scala | 5 - .../src/main/scala/akka/actor/Actor.scala | 21 +- .../src/main/scala/akka/actor/ActorCell.scala | 24 +- .../src/main/scala/akka/actor/ActorRef.scala | 34 +- .../scala/akka/actor/ActorRefProvider.scala | 15 - .../main/scala/akka/actor/FaultHandling.scala | 9 - .../main/scala/akka/actor/UntypedActor.scala | 1 + .../akka/actor/UntypedActorWithStash.scala | 3 + .../scala/akka/actor/dungeon/Children.scala | 2 + .../src/main/scala/akka/event/Logging.scala | 1 + .../scala/akka/event/LoggingReceive.scala | 11 +- .../cluster/sharding/ClusterShardingTest.java | 77 +- .../cluster/client/ClusterClientTest.java | 66 +- .../pubsub/DistributedPubSubMediatorTest.java | 81 +- akka-docs/rst/additional/osgi.rst | 3 +- .../circuitbreaker/DangerousJavaActor.java | 25 +- .../circuitbreaker/TellPatternJavaActor.java | 30 +- akka-docs/rst/experimental/index.rst | 2 - akka-docs/rst/general/actors.rst | 2 +- akka-docs/rst/intro/what-is-akka.rst | 2 +- akka-docs/rst/java.rst | 2 +- akka-docs/rst/java/camel.rst | 4 +- akka-docs/rst/java/cluster-sharding.rst | 2 +- akka-docs/rst/java/cluster-usage.rst | 6 +- .../java/code/docs/actor/ActorDocTest.java | 639 ---------- .../rst/java/code/docs/actor/FSMDocTest.java | 209 ---- .../code/docs/actor/FaultHandlingTest.java | 224 ---- .../docs/actor/FaultHandlingTestJava8.java | 205 ---- .../code/docs/actor/FirstUntypedActor.java | 21 - .../docs/actor/InitializationDocSpecJava.java | 87 -- .../docs/actor/InitializationDocTest.java | 68 -- .../rst/java/code/docs/actor/MyJavaActor.java | 33 - .../actor/MyReceiveTimeoutUntypedActor.java | 41 - .../java/code/docs/actor/MyStoppingActor.java | 29 - .../java/code/docs/actor/MyUntypedActor.java | 23 - .../rst/java/code/docs/actor/SampleActor.java | 36 - .../java/code/docs/actor/SampleActorTest.java | 56 - .../code/docs/actor/SchedulerDocTest.java | 19 +- .../code/docs/actor/UntypedActorDocTest.java | 794 ------------ .../code/docs/actor/UntypedActorSwapper.java | 56 - .../rst/java/code/docs/actor/fsm/Buncher.java | 136 --- .../java/code/docs/actor/fsm/BuncherTest.java | 79 -- .../rst/java/code/docs/actor/fsm/Events.java | 108 -- .../java/code/docs/actor/fsm/FSMDocTest.java | 180 --- .../actor/japi/FaultHandlingDocSample.java | 489 -------- .../japi/FaultHandlingDocSampleJava8.java | 470 -------- .../code/docs/actorlambda/ActorDocTest.java | 602 ++++++---- .../DependencyInjectionDocTest.java | 105 ++ .../docs/actorlambda/FaultHandlingTest.java | 85 +- .../docs/actorlambda/GraduallyBuiltActor.java | 13 +- .../actorlambda/InitializationDocTest.java | 89 +- .../java/code/docs/actorlambda/MyActor.java | 16 +- .../MyBoundedActor.java} | 4 +- .../docs/actorlambda/MyStoppingActor.java | 29 + .../code/docs/actorlambda/SampleActor.java | 32 +- .../code/docs/actorlambda/fsm/FSMDocTest.java | 2 +- .../japi/FaultHandlingDocSample.java | 66 +- .../rst/java/code/docs/camel/Consumer2.java | 2 +- .../rst/java/code/docs/camel/Consumer3.java | 4 +- .../rst/java/code/docs/camel/Consumer4.java | 2 +- .../docs/camel/ErrorThrowingConsumer.java | 2 +- .../rst/java/code/docs/camel/MyActor.java | 4 +- .../code/docs/camel/RequestBodyActor.java | 17 +- .../rst/java/code/docs/camel/Responder.java | 6 +- .../code/docs/camel/ResponseReceiver.java | 4 +- .../rst/java/code/docs/ddata/DataBot.java | 18 +- .../docs/ddata/DistributedDataDocTest.java | 378 +++--- .../docs/dispatcher/DispatcherDocTest.java | 50 +- .../java/code/docs/event/LoggingDocTest.java | 111 +- .../code/docs/extension/ExtensionDocTest.java | 15 +- .../extension/SettingsExtensionDocTest.java | 10 +- .../java/code/docs/future/FutureDocTest.java | 31 +- .../rst/java/code/docs/io/IODocTest.java | 112 +- .../code/docs/io/JavaReadBackPressure.java | 22 +- .../java/code/docs/io/JavaUdpMulticast.java | 10 +- .../code/docs/io/UdpConnectedDocTest.java | 12 +- .../rst/java/code/docs/io/UdpDocTest.java | 42 +- .../java/code/docs/io/japi/EchoHandler.java | 210 ++-- .../java/code/docs/io/japi/EchoManager.java | 61 +- .../rst/java/code/docs/io/japi/IODocTest.java | 127 +- .../code/docs/io/japi/SimpleEchoHandler.java | 66 +- .../rst/java/code/docs/io/japi/Watcher.java | 21 +- .../ConsistentHashingRouterDocTest.java | 2 +- .../docs/jrouting/CustomRouterDocTest.java | 2 +- .../code/docs/jrouting/RouterDocTest.java | 8 +- .../docs/pattern/SchedulerPatternTest.java | 10 +- .../java/code/docs/pattern/SupervisedAsk.java | 4 +- .../persistence/LambdaPersistenceDocTest.java | 114 +- .../LambdaPersistencePluginDocTest.java | 106 +- .../docs/persistence/PersistenceDocTest.java | 584 --------- .../persistence/PersistencePluginDocTest.java | 193 --- .../persistence/PersistenceQueryDocTest.java | 23 +- .../query/MyEventsByTagJavaPublisher.java | 27 +- .../remoting/RemoteDeploymentDocTest.java | 2 +- .../docs/stream/ActorPublisherDocTest.java | 20 +- .../docs/stream/ActorSubscriberDocTest.java | 67 +- .../code/docs/stream/IntegrationDocTest.java | 19 +- .../TwitterStreamQuickstartDocTest.java | 2 +- .../cookbook/RecipeGlobalRateLimit.java | 24 +- .../code/docs/testkit/ParentChildTest.java | 14 +- .../code/docs/testkit/TestKitDocTest.java | 2 +- .../code/docs/testkit/TestKitSampleTest.java | 4 +- akka-docs/rst/java/fault-tolerance-sample.rst | 53 - akka-docs/rst/java/fault-tolerance.rst | 175 --- akka-docs/rst/java/fsm.rst | 76 -- akka-docs/rst/java/futures.rst | 2 +- akka-docs/rst/java/howto.rst | 2 +- akka-docs/rst/java/index-actors.rst | 10 +- akka-docs/rst/java/lambda-actors.rst | 163 +-- .../java/lambda-fault-tolerance-sample.rst | 2 +- akka-docs/rst/java/lambda-fault-tolerance.rst | 10 +- akka-docs/rst/java/lambda-fsm.rst | 6 +- akka-docs/rst/java/lambda-persistence.rst | 178 ++- akka-docs/rst/java/mailboxes.rst | 4 +- akka-docs/rst/java/persistence.rst | 1059 ----------------- akka-docs/rst/java/remoting.rst | 4 +- ...mbda-index-actors.rst => scala-compat.rst} | 14 +- .../rst/java/stream/stream-integrations.rst | 2 +- akka-docs/rst/java/testing.rst | 2 +- akka-docs/rst/java/untyped-actors.rst | 991 --------------- .../project/migration-guide-2.3.x-2.4.x.rst | 1 + .../project/migration-guide-2.4.x-2.5.x.rst | 215 +++- .../src/test/scala}/docs/osgi/Activator.scala | 2 +- .../src/test/scala}/docs/osgi/blueprint.xml | 0 .../persistence/AtLeastOnceDelivery.scala | 1 + .../scala/akka/persistence/Eventsourced.scala | 3 +- .../akka/persistence/PersistentActor.scala | 30 + .../scala/akka/stream/stage/GraphStage.scala | 3 +- project/AkkaBuild.scala | 3 +- project/MiMa.scala | 14 + project/Sample.scala | 2 +- 157 files changed, 3290 insertions(+), 8882 deletions(-) create mode 100644 akka-actor-tests/src/test/java/akka/japi/pf/ReceiveBuilderTest.java delete mode 100644 akka-docs/rst/java/code/docs/actor/ActorDocTest.java delete mode 100644 akka-docs/rst/java/code/docs/actor/FSMDocTest.java delete mode 100644 akka-docs/rst/java/code/docs/actor/FaultHandlingTest.java delete mode 100644 akka-docs/rst/java/code/docs/actor/FaultHandlingTestJava8.java delete mode 100644 akka-docs/rst/java/code/docs/actor/FirstUntypedActor.java delete mode 100644 akka-docs/rst/java/code/docs/actor/InitializationDocSpecJava.java delete mode 100644 akka-docs/rst/java/code/docs/actor/InitializationDocTest.java delete mode 100644 akka-docs/rst/java/code/docs/actor/MyJavaActor.java delete mode 100644 akka-docs/rst/java/code/docs/actor/MyReceiveTimeoutUntypedActor.java delete mode 100644 akka-docs/rst/java/code/docs/actor/MyStoppingActor.java delete mode 100644 akka-docs/rst/java/code/docs/actor/MyUntypedActor.java delete mode 100644 akka-docs/rst/java/code/docs/actor/SampleActor.java delete mode 100644 akka-docs/rst/java/code/docs/actor/SampleActorTest.java delete mode 100644 akka-docs/rst/java/code/docs/actor/UntypedActorDocTest.java delete mode 100644 akka-docs/rst/java/code/docs/actor/UntypedActorSwapper.java delete mode 100644 akka-docs/rst/java/code/docs/actor/fsm/Buncher.java delete mode 100644 akka-docs/rst/java/code/docs/actor/fsm/BuncherTest.java delete mode 100644 akka-docs/rst/java/code/docs/actor/fsm/Events.java delete mode 100644 akka-docs/rst/java/code/docs/actor/fsm/FSMDocTest.java delete mode 100644 akka-docs/rst/java/code/docs/actor/japi/FaultHandlingDocSample.java delete mode 100644 akka-docs/rst/java/code/docs/actor/japi/FaultHandlingDocSampleJava8.java create mode 100644 akka-docs/rst/java/code/docs/actorlambda/DependencyInjectionDocTest.java rename akka-docs/rst/java/code/docs/{actor/MyBoundedUntypedActor.java => actorlambda/MyBoundedActor.java} (79%) create mode 100644 akka-docs/rst/java/code/docs/actorlambda/MyStoppingActor.java delete mode 100644 akka-docs/rst/java/code/docs/persistence/PersistenceDocTest.java delete mode 100644 akka-docs/rst/java/code/docs/persistence/PersistencePluginDocTest.java delete mode 100644 akka-docs/rst/java/fault-tolerance-sample.rst delete mode 100644 akka-docs/rst/java/fault-tolerance.rst delete mode 100644 akka-docs/rst/java/fsm.rst delete mode 100644 akka-docs/rst/java/persistence.rst rename akka-docs/rst/java/{lambda-index-actors.rst => scala-compat.rst} (83%) delete mode 100644 akka-docs/rst/java/untyped-actors.rst rename {akka-docs/rst/additional/code => akka-osgi/src/test/scala}/docs/osgi/Activator.scala (93%) rename {akka-docs/rst/additional/code => akka-osgi/src/test/scala}/docs/osgi/blueprint.xml (100%) diff --git a/akka-actor-tests/src/test/java/akka/actor/ActorCreationTest.java b/akka-actor-tests/src/test/java/akka/actor/ActorCreationTest.java index a91959652e..e744ed5e79 100644 --- a/akka-actor-tests/src/test/java/akka/actor/ActorCreationTest.java +++ b/akka-actor-tests/src/test/java/akka/actor/ActorCreationTest.java @@ -21,9 +21,9 @@ import org.scalatest.junit.JUnitSuite; public class ActorCreationTest extends JUnitSuite { - static class C implements Creator { + static class C implements Creator { @Override - public UntypedActor create() throws Exception { + public AbstractActor create() throws Exception { return null; } } @@ -35,19 +35,19 @@ public class ActorCreationTest extends JUnitSuite { } } - static class E implements Creator { + static class E implements Creator { @Override public T create() { return null; } } - static interface I extends Creator { + static interface I extends Creator { } static class F implements I { @Override - public UntypedActor create() { + public AbstractActor create() { return null; } } @@ -59,12 +59,12 @@ public class ActorCreationTest extends JUnitSuite { } } - abstract class H extends UntypedActor { + abstract class H extends AbstractActor { public H(String a) { } } - static class P implements Creator { + static class P implements Creator { final String value; public P(String value) { @@ -72,7 +72,7 @@ public class ActorCreationTest extends JUnitSuite { } @Override - public UntypedActor create() throws Exception { + public AbstractActor create() throws Exception { return null; } } @@ -94,40 +94,46 @@ public class ActorCreationTest extends JUnitSuite { TestActor(Integer magicNumber) { this.magicNumber = magicNumber; } + + @Override + public Receive createReceive() { + return receiveBuilder().build(); + } + } - public static class UntypedTestActor extends UntypedActor { + public static class TestActor2 extends UntypedAbstractActor { public static Props propsUsingCreator(final int magicNumber) { // You need to specify the actual type of the returned actor // since runtime type information erased - return Props.create(UntypedTestActor.class, new Creator() { + return Props.create(TestActor2.class, new Creator() { private static final long serialVersionUID = 1L; @Override - public UntypedTestActor create() throws Exception { - return new UntypedTestActor(magicNumber); + public TestActor2 create() throws Exception { + return new TestActor2(magicNumber); } }); } public static Props propsUsingCreatorWithoutClass(final int magicNumber) { - return Props.create(new Creator() { + return Props.create(new Creator() { private static final long serialVersionUID = 1L; @Override - public UntypedTestActor create() throws Exception { - return new UntypedTestActor(magicNumber); + public TestActor2 create() throws Exception { + return new TestActor2(magicNumber); } }); } - private static final Creator staticCreator = new Creator() { + private static final Creator staticCreator = new Creator() { private static final long serialVersionUID = 1L; @Override - public UntypedTestActor create() throws Exception { - return new UntypedTestActor(12); + public TestActor2 create() throws Exception { + return new TestActor2(12); } }; @@ -140,7 +146,7 @@ public class ActorCreationTest extends JUnitSuite { final int magicNumber; - UntypedTestActor(int magicNumber) { + TestActor2(int magicNumber) { this.magicNumber = magicNumber; } @@ -149,7 +155,7 @@ public class ActorCreationTest extends JUnitSuite { } } - public static class Issue20537Reproducer extends UntypedActor { + public static class Issue20537Reproducer extends UntypedAbstractActor { static final class ReproducerCreator implements Creator { @@ -197,13 +203,13 @@ public class ActorCreationTest extends JUnitSuite { } catch (IllegalArgumentException e) { assertEquals("erased Creator types are unsupported, use Props.create(actorClass, creator) instead", e.getMessage()); } - Props.create(UntypedActor.class, new G()); + Props.create(AbstractActor.class, new G()); } @Test public void testRightStaticCreator() { final Props p = Props.create(new C()); - assertEquals(UntypedActor.class, p.actorClass()); + assertEquals(AbstractActor.class, p.actorClass()); } @Test @@ -218,27 +224,27 @@ public class ActorCreationTest extends JUnitSuite { @Test public void testRightTopLevelNonStaticCreator() { - final Creator nonStatic = new NonStaticCreator(); + final Creator nonStatic = new NonStaticCreator(); final Props p = Props.create(nonStatic); - assertEquals(UntypedActor.class, p.actorClass()); + assertEquals(UntypedAbstractActor.class, p.actorClass()); } @Test public void testRightStaticParametricCreator() { - final Props p = Props.create(new D()); + final Props p = Props.create(new D()); assertEquals(Actor.class, p.actorClass()); } @Test public void testRightStaticBoundedCreator() { - final Props p = Props.create(new E()); - assertEquals(UntypedActor.class, p.actorClass()); + final Props p = Props.create(new E()); + assertEquals(AbstractActor.class, p.actorClass()); } @Test public void testRightStaticSuperinterface() { final Props p = Props.create(new F()); - assertEquals(UntypedActor.class, p.actorClass()); + assertEquals(AbstractActor.class, p.actorClass()); } @Test @@ -251,10 +257,10 @@ public class ActorCreationTest extends JUnitSuite { } } - private static Creator createAnonymousCreatorInStaticMethod() { - return new Creator() { + private static Creator createAnonymousCreatorInStaticMethod() { + return new Creator() { @Override - public UntypedActor create() throws Exception { + public AbstractActor create() throws Exception { return null; } }; @@ -262,20 +268,20 @@ public class ActorCreationTest extends JUnitSuite { @Test public void testAnonymousClassCreatedInStaticMethodCreator() { - final Creator anonymousCreatorFromStaticMethod = createAnonymousCreatorInStaticMethod(); + final Creator anonymousCreatorFromStaticMethod = createAnonymousCreatorInStaticMethod(); Props.create(anonymousCreatorFromStaticMethod); } @Test public void testClassCreatorWithArguments() { - final Creator anonymousCreatorFromStaticMethod = new P("hello"); + final Creator anonymousCreatorFromStaticMethod = new P("hello"); Props.create(anonymousCreatorFromStaticMethod); } @Test public void testAnonymousClassCreatorWithArguments() { try { - final Creator anonymousCreatorFromStaticMethod = new P("hello") { + final Creator anonymousCreatorFromStaticMethod = new P("hello") { // captures enclosing class }; Props.create(anonymousCreatorFromStaticMethod); @@ -306,14 +312,14 @@ public class ActorCreationTest extends JUnitSuite { @Test public void testRightPropsUsingCreator() { - final Props p = UntypedTestActor.propsUsingCreator(17); - assertEquals(UntypedTestActor.class, p.actorClass()); + final Props p = TestActor2.propsUsingCreator(17); + assertEquals(TestActor2.class, p.actorClass()); } @Test public void testPropsUsingCreatorWithoutClass() { - final Props p = UntypedTestActor.propsUsingCreatorWithoutClass(17); - assertEquals(UntypedTestActor.class, p.actorClass()); + final Props p = TestActor2.propsUsingCreatorWithoutClass(17); + assertEquals(TestActor2.class, p.actorClass()); } @Test diff --git a/akka-actor-tests/src/test/java/akka/actor/JavaAPI.java b/akka-actor-tests/src/test/java/akka/actor/JavaAPI.java index 446a78a5cc..8471a10cc0 100644 --- a/akka-actor-tests/src/test/java/akka/actor/JavaAPI.java +++ b/akka-actor-tests/src/test/java/akka/actor/JavaAPI.java @@ -137,7 +137,7 @@ public class JavaAPI extends JUnitSuite { final Tuple4 t4 = Tuple4.create(1, "2", 3, 4L); Tuple22.create(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22); } - + @Test public void mustBeAbleToCreateOptionFromOptional() { Option empty = Util.option(Optional.ofNullable(null)); @@ -147,7 +147,7 @@ public class JavaAPI extends JUnitSuite { assertTrue(full.isDefined()); } - public static class ActorWithConstructorParams extends UntypedActor { + public static class ActorWithConstructorParams extends UntypedAbstractActor { private final String a; private final String b; @@ -189,7 +189,6 @@ public class JavaAPI extends JUnitSuite { this.d = d; } - @Override public void onReceive(Object msg) { String reply = String.valueOf(a) + "-" + String.valueOf(b) + "-" + String.valueOf(c) + "-" + String.valueOf(d); getSender().tell(reply, getSelf()); diff --git a/akka-actor-tests/src/test/java/akka/actor/JavaAPITestActor.java b/akka-actor-tests/src/test/java/akka/actor/JavaAPITestActor.java index aa1b870efb..d6e28fe3d8 100644 --- a/akka-actor-tests/src/test/java/akka/actor/JavaAPITestActor.java +++ b/akka-actor-tests/src/test/java/akka/actor/JavaAPITestActor.java @@ -1,6 +1,6 @@ package akka.actor; -public class JavaAPITestActor extends UntypedActor { +public class JavaAPITestActor extends UntypedAbstractActor { public static String ANSWER = "got it!"; public void onReceive(Object msg) { diff --git a/akka-actor-tests/src/test/java/akka/actor/NonPublicClass.java b/akka-actor-tests/src/test/java/akka/actor/NonPublicClass.java index 0f59d1e32b..3c83cb6057 100644 --- a/akka-actor-tests/src/test/java/akka/actor/NonPublicClass.java +++ b/akka-actor-tests/src/test/java/akka/actor/NonPublicClass.java @@ -10,7 +10,7 @@ public class NonPublicClass { } } -class MyNonPublicActorClass extends UntypedActor { +class MyNonPublicActorClass extends UntypedAbstractActor { @Override public void onReceive(Object msg) { getSender().tell(msg, getSelf()); } diff --git a/akka-actor-tests/src/test/java/akka/actor/NonStaticCreator.java b/akka-actor-tests/src/test/java/akka/actor/NonStaticCreator.java index b06da54944..c3409bd320 100644 --- a/akka-actor-tests/src/test/java/akka/actor/NonStaticCreator.java +++ b/akka-actor-tests/src/test/java/akka/actor/NonStaticCreator.java @@ -2,9 +2,9 @@ package akka.actor; import akka.japi.Creator; -public class NonStaticCreator implements Creator { +public class NonStaticCreator implements Creator { @Override - public UntypedActor create() throws Exception { + public UntypedAbstractActor create() throws Exception { return null; } } diff --git a/akka-actor-tests/src/test/java/akka/actor/StashJavaAPITestActors.java b/akka-actor-tests/src/test/java/akka/actor/StashJavaAPITestActors.java index 435707b9dd..1542c6fe7d 100644 --- a/akka-actor-tests/src/test/java/akka/actor/StashJavaAPITestActors.java +++ b/akka-actor-tests/src/test/java/akka/actor/StashJavaAPITestActors.java @@ -5,9 +5,9 @@ import static org.junit.Assert.*; public class StashJavaAPITestActors { /* - * Helper method to make the tests of UntypedActorWithStash, UntypedActorWithUnboundedStash and - * UntypedActorWithUnrestrictedStash more DRY since mixin is not possible. - */ + * Helper method to make the tests of AbstractActorWithStash, AbstractActorWithUnboundedStash and + * AbstractActorWithUnrestrictedStash more DRY since mixin is not possible. + */ private static int testReceive(Object msg, int count, ActorRef sender, ActorRef self, UnrestrictedStash stash) { if (msg instanceof String) { if (count < 0) { @@ -26,28 +26,43 @@ public class StashJavaAPITestActors { return count; } - public static class WithStash extends UntypedActorWithStash { - int count = 0; + public static class WithStash extends AbstractActorWithStash { + int count = 0; - public void onReceive(Object msg) { + @Override + public Receive createReceive() { + return receiveBuilder() + .match(Object.class, msg -> { count = testReceive(msg, count, getSender(), getSelf(), this); - } + }) + .build(); + } } - public static class WithUnboundedStash extends UntypedActorWithUnboundedStash { - int count = 0; + public static class WithUnboundedStash extends AbstractActorWithUnboundedStash { + int count = 0; - public void onReceive(Object msg) { + @Override + public Receive createReceive() { + return receiveBuilder() + .match(Object.class, msg -> { count = testReceive(msg, count, getSender(), getSelf(), this); - } + }) + .build(); + } } - public static class WithUnrestrictedStash extends UntypedActorWithUnrestrictedStash { - int count = 0; + public static class WithUnrestrictedStash extends AbstractActorWithUnrestrictedStash { + int count = 0; - public void onReceive(Object msg) { + @Override + public Receive createReceive() { + return receiveBuilder() + .match(Object.class, msg -> { count = testReceive(msg, count, getSender(), getSelf(), this); - } + }) + .build(); + } } } diff --git a/akka-actor-tests/src/test/java/akka/event/ActorWithMDC.java b/akka-actor-tests/src/test/java/akka/event/ActorWithMDC.java index 3728022ed0..ed7cf744be 100644 --- a/akka-actor-tests/src/test/java/akka/event/ActorWithMDC.java +++ b/akka-actor-tests/src/test/java/akka/event/ActorWithMDC.java @@ -1,47 +1,50 @@ package akka.event; -import akka.actor.UntypedActor; +import akka.actor.AbstractActor; +import akka.japi.pf.ReceiveBuilder; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; -public class ActorWithMDC extends UntypedActor { +public class ActorWithMDC extends AbstractActor { private final DiagnosticLoggingAdapter logger = Logging.getLogger(this); - @Override - public void onReceive(Object message) throws Exception { - Log log = (Log) message; + @Override + public Receive createReceive() { + return receiveBuilder().match(Log.class, this::receiveLog).build(); + } - Map mdc; - if(log.message.startsWith("No MDC")) { - mdc = Collections.emptyMap(); - } else if(log.message.equals("Null MDC")) { - mdc = null; - } else { - mdc = new LinkedHashMap(); - mdc.put("messageLength", log.message.length()); - } - logger.setMDC(mdc); - - switch (log.level()) { - case 1: - logger.error(log.message); - break; - case 2: - logger.warning(log.message); - break; - case 3: - logger.info(log.message); - break; - default: - logger.debug(log.message); - break; - } - - logger.clearMDC(); + private void receiveLog(Log log) { + Map mdc; + if (log.message.startsWith("No MDC")) { + mdc = Collections.emptyMap(); + } else if (log.message.equals("Null MDC")) { + mdc = null; + } else { + mdc = new LinkedHashMap(); + mdc.put("messageLength", log.message.length()); } + logger.setMDC(mdc); + + switch (log.level()) { + case 1: + logger.error(log.message); + break; + case 2: + logger.warning(log.message); + break; + case 3: + logger.info(log.message); + break; + default: + logger.debug(log.message); + break; + } + + logger.clearMDC(); + } public static class Log { private final Object level; diff --git a/akka-actor-tests/src/test/java/akka/japi/MatchBuilderTest.java b/akka-actor-tests/src/test/java/akka/japi/MatchBuilderTest.java index e5eedef3c0..1c80904de1 100644 --- a/akka-actor-tests/src/test/java/akka/japi/MatchBuilderTest.java +++ b/akka-actor-tests/src/test/java/akka/japi/MatchBuilderTest.java @@ -51,7 +51,7 @@ public class MatchBuilderTest extends JUnitSuite { @Test public void shouldHandleMatchOnGenericClass() { - Match pf = Match.create(Match.match(GenericClass.class, new FI.Apply, String>() { + Match pf = Match.create(Match.matchUnchecked(GenericClass.class, new FI.Apply, String>() { @Override public String apply(GenericClass stringGenericClass) { return stringGenericClass.val; @@ -64,7 +64,7 @@ public class MatchBuilderTest extends JUnitSuite { @Test public void shouldHandleMatchWithPredicateOnGenericClass() { - Match pf = Match.create(Match.match(GenericClass.class, new FI.TypedPredicate>() { + Match pf = Match.create(Match.matchUnchecked(GenericClass.class, new FI.TypedPredicate>() { @Override public boolean defined(GenericClass genericClass) { return !genericClass.val.isEmpty(); diff --git a/akka-actor-tests/src/test/java/akka/japi/pf/PFBuilderTest.java b/akka-actor-tests/src/test/java/akka/japi/pf/PFBuilderTest.java index 18a3c28184..d1569e38e6 100644 --- a/akka-actor-tests/src/test/java/akka/japi/pf/PFBuilderTest.java +++ b/akka-actor-tests/src/test/java/akka/japi/pf/PFBuilderTest.java @@ -10,6 +10,7 @@ import scala.PartialFunction; import static org.junit.Assert.*; +@SuppressWarnings("serial") public class PFBuilderTest extends JUnitSuite { @Test @@ -18,7 +19,7 @@ public class PFBuilderTest extends JUnitSuite { .matchEquals("hello", s -> 1) .matchAny(s -> Integer.valueOf(s)) .build(); - + assertTrue(pf.isDefinedAt("hello")); assertTrue(pf.isDefinedAt("42")); assertEquals(42, pf.apply("42").intValue()); diff --git a/akka-actor-tests/src/test/java/akka/japi/pf/ReceiveBuilderTest.java b/akka-actor-tests/src/test/java/akka/japi/pf/ReceiveBuilderTest.java new file mode 100644 index 0000000000..257384aa56 --- /dev/null +++ b/akka-actor-tests/src/test/java/akka/japi/pf/ReceiveBuilderTest.java @@ -0,0 +1,261 @@ +/** + * Copyright (C) 2017 Lightbend Inc. + */ + +package akka.japi.pf; + +import java.util.Arrays; +import java.util.List; +import org.junit.Test; +import org.junit.Before; +import org.scalatest.junit.JUnitSuite; + +import akka.actor.AbstractActor.Receive; + +import static org.junit.Assert.*; + +@SuppressWarnings("serial") +public class ReceiveBuilderTest extends JUnitSuite { + + public static interface Msg {} + public static class Msg1 implements Msg { + @Override + public String toString() { + return "Msg1"; + } + } + public static class Msg2 implements Msg { + public final String value; + + public Msg2(String value) { + this.value = value; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((value == null) ? 0 : value.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Msg2 other = (Msg2) obj; + if (value == null) { + if (other.value != null) + return false; + } else if (!value.equals(other.value)) + return false; + return true; + } + + @Override + public String toString() { + return "Msg2 [value=" + value + "]"; + } + + } + + // using instance variable, because lambdas can only modify final fields + private String result = ""; + + private String result() { + String r = result; + result = ""; + return r; + } + + private void result(String r) { + result = r; + } + + + @Before + public void beforeEach() { + result = ""; + } + + @Test + public void shouldNotMatchWhenEmpty() { + Receive rcv = ReceiveBuilder.create().build(); + assertFalse(rcv.onMessage().isDefinedAt("hello")); + assertFalse(rcv.onMessage().isDefinedAt(42)); + } + + @Test + public void shouldMatchByClass() { + Receive rcv = ReceiveBuilder.create() + .match(Msg1.class, m -> result("match Msg1")) + .build(); + assertTrue(rcv.onMessage().isDefinedAt(new Msg1())); + rcv.onMessage().apply(new Msg1()); + assertEquals("match Msg1", result()); + + assertFalse(rcv.onMessage().isDefinedAt(new Msg2("foo"))); + assertFalse(rcv.onMessage().isDefinedAt("hello")); + assertFalse(rcv.onMessage().isDefinedAt(42)); + } + + @Test + public void shouldMatchBySubclass() { + Receive rcv = ReceiveBuilder.create() + .match(Msg.class, m -> result("match Msg")) + .build(); + assertTrue(rcv.onMessage().isDefinedAt(new Msg1())); + rcv.onMessage().apply(new Msg1()); + assertEquals("match Msg", result()); + + assertTrue(rcv.onMessage().isDefinedAt(new Msg2("foo"))); + rcv.onMessage().apply(new Msg2("foo")); + assertEquals("match Msg", result()); + + assertFalse(rcv.onMessage().isDefinedAt("hello")); + assertFalse(rcv.onMessage().isDefinedAt(42)); + } + + private void handleMsg(Msg msg) { + result("match Msg"); + } + + private void handleMsg(Msg1 msg) { + result("match Msg1"); + } + + private void handleMsg(Msg2 msg) { + result("match Msg2"); + } + + @Test + public void shouldMatchDelegatingToSpecificMethod() { + Receive rcv = ReceiveBuilder.create() + .match(Msg1.class, this::handleMsg) + .match(Msg2.class, this::handleMsg) + .match(Msg.class, this::handleMsg) + .build(); + assertTrue(rcv.onMessage().isDefinedAt(new Msg1())); + rcv.onMessage().apply(new Msg1()); + assertEquals("match Msg1", result()); + + assertTrue(rcv.onMessage().isDefinedAt(new Msg2("foo"))); + rcv.onMessage().apply(new Msg2("foo")); + assertEquals("match Msg2", result()); + } + + private void anotherHandleMsg(Msg msg) { + result("match Msg"); + } + + private void anotherHandleMsg(Msg1 msg) { + result("match Msg1"); + } + + @Test + public void shouldMatchDelegatingToGeneralMethod() { + Receive rcv = ReceiveBuilder.create() + .match(Msg1.class, this::anotherHandleMsg) + .match(Msg2.class, this::anotherHandleMsg) + .build(); + assertTrue(rcv.onMessage().isDefinedAt(new Msg1())); + rcv.onMessage().apply(new Msg1()); + assertEquals("match Msg1", result()); + + assertTrue(rcv.onMessage().isDefinedAt(new Msg2("foo"))); + rcv.onMessage().apply(new Msg2("foo")); + assertEquals("match Msg", result()); + } + + @Test + public void shouldMatchByPredicate() { + Receive rcv = ReceiveBuilder.create() + .match(Msg1.class, m -> true, m -> result("match Msg1")) + .match(Msg2.class, m -> m.value.equals("foo"), m -> result("match Msg2")) + .build(); + assertTrue(rcv.onMessage().isDefinedAt(new Msg1())); + rcv.onMessage().apply(new Msg1()); + assertEquals("match Msg1", result()); + + assertTrue(rcv.onMessage().isDefinedAt(new Msg2("foo"))); + rcv.onMessage().apply(new Msg2("foo")); + assertEquals("match Msg2", result()); + + assertFalse(rcv.onMessage().isDefinedAt(new Msg2("bar"))); + + assertFalse(rcv.onMessage().isDefinedAt("hello")); + assertFalse(rcv.onMessage().isDefinedAt(42)); + } + + @Test + public void shouldMatchEquals() { + Msg2 msg2 = new Msg2("foo"); + Receive rcv = ReceiveBuilder.create() + .matchEquals(msg2, m -> result("match msg2")) + .matchEquals("foo", m -> result("match foo")) + .matchEquals(17, m -> result("match 17")) + .build(); + assertTrue(rcv.onMessage().isDefinedAt(new Msg2("foo"))); + rcv.onMessage().apply(new Msg2("foo")); + assertEquals("match msg2", result()); + + assertTrue(rcv.onMessage().isDefinedAt("foo")); + rcv.onMessage().apply("foo"); + assertEquals("match foo", result()); + + assertTrue(rcv.onMessage().isDefinedAt(17)); + rcv.onMessage().apply(17); + assertEquals("match 17", result()); + + assertFalse(rcv.onMessage().isDefinedAt(new Msg2("bar"))); + assertFalse(rcv.onMessage().isDefinedAt("hello")); + assertFalse(rcv.onMessage().isDefinedAt(42)); + } + + @Test + public void shouldMatchAny() { + Receive rcv = ReceiveBuilder.create() + .match(Msg1.class, m -> result("match Msg1")) + .matchAny(m -> result("match any")) + .build(); + assertTrue(rcv.onMessage().isDefinedAt(new Msg1())); + rcv.onMessage().apply(new Msg1()); + assertEquals("match Msg1", result()); + + assertTrue(rcv.onMessage().isDefinedAt(new Msg2("foo"))); + rcv.onMessage().apply(new Msg2("foo")); + assertEquals("match any", result()); + + assertTrue(rcv.onMessage().isDefinedAt("hello")); + assertTrue(rcv.onMessage().isDefinedAt(42)); + } + + @Test + public void shouldMatchUnchecked() { + Receive rcv = ReceiveBuilder.create() + .matchUnchecked(List.class, (List list) -> { + result("match List"); + }) + .build(); + List list = Arrays.asList("foo"); + assertTrue(rcv.onMessage().isDefinedAt(list)); + rcv.onMessage().apply(list); + assertEquals("match List", result()); + } + + @Test(expected = ClassCastException.class) + public void shouldThrowWhenUncheckedWithWrongTypes() { + // note that this doesn't compile with ordinary match + Receive rcv = ReceiveBuilder.create() + .matchUnchecked(String.class, (Integer i) -> { + result(String.valueOf(i + 2)); + }) + .build(); + assertTrue(rcv.onMessage().isDefinedAt("foo")); + rcv.onMessage().apply("foo"); + } +} diff --git a/akka-actor-tests/src/test/java/akka/pattern/PatternsTest.java b/akka-actor-tests/src/test/java/akka/pattern/PatternsTest.java index 40ef160564..c65b6f4b95 100644 --- a/akka-actor-tests/src/test/java/akka/pattern/PatternsTest.java +++ b/akka-actor-tests/src/test/java/akka/pattern/PatternsTest.java @@ -39,7 +39,7 @@ public class PatternsTest extends JUnitSuite { ActorRef testActor = system.actorOf(Props.create(JavaAPITestActor.class), "test2"); ActorSelection selection = system.actorSelection("/user/test2"); ActorIdentity id = (ActorIdentity) Await.result(ask(selection, new Identify("yo!"), 3000), Duration.create(3, "seconds")); - assertEquals("Ask (Identify) should return the proper ActorIdentity", testActor, id.getRef()); + assertEquals("Ask (Identify) should return the proper ActorIdentity", testActor, id.getActorRef().get()); } @Test diff --git a/akka-actor-tests/src/test/scala/akka/event/LoggerSpec.scala b/akka-actor-tests/src/test/scala/akka/event/LoggerSpec.scala index e9978d2b25..8237d45ada 100644 --- a/akka-actor-tests/src/test/scala/akka/event/LoggerSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/event/LoggerSpec.scala @@ -51,15 +51,15 @@ object LoggerSpec { } """).withFallback(AkkaSpec.testConf) - val ticket3165Config = ConfigFactory.parseString(""" + val ticket3165Config = ConfigFactory.parseString(s""" akka { stdout-loglevel = "WARNING" loglevel = "DEBUG" - loggers = ["akka.event.LoggerSpec$TestLogger1"] + loggers = ["akka.event.LoggerSpec$$TestLogger1"] actor { serialize-messages = on serialization-bindings { - "akka.event.Logging$LogEvent" = bytes + "akka.event.Logging$$LogEvent" = bytes "java.io.Serializable" = java } } diff --git a/akka-actor-tests/src/test/scala/akka/serialization/SerializeSpec.scala b/akka-actor-tests/src/test/scala/akka/serialization/SerializeSpec.scala index 8e4d2e56cd..196c3ff4c1 100644 --- a/akka-actor-tests/src/test/scala/akka/serialization/SerializeSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/serialization/SerializeSpec.scala @@ -88,8 +88,9 @@ object SerializationTests { } } - class FooUntypedActor extends UntypedActor { - def onReceive(message: Any) {} + class FooAbstractActor extends AbstractActor { + override def createReceive(): AbstractActor.Receive = + receiveBuilder().build() } class NonSerializableActor(system: ActorSystem) extends Actor { @@ -286,7 +287,7 @@ class VerifySerializabilitySpec extends AkkaSpec(SerializationTests.verifySerial val a = system.actorOf(Props[FooActor]) system stop a - val b = system.actorOf(Props(new FooActor)) + val b = system.actorOf(Props(new FooAbstractActor)) system stop b intercept[IllegalArgumentException] { diff --git a/akka-actor/src/main/java/akka/japi/pf/AbstractMatch.java b/akka-actor/src/main/java/akka/japi/pf/AbstractMatch.java index 269c0fd367..b2a6b69fe7 100644 --- a/akka-actor/src/main/java/akka/japi/pf/AbstractMatch.java +++ b/akka-actor/src/main/java/akka/japi/pf/AbstractMatch.java @@ -12,8 +12,6 @@ import scala.PartialFunction; * * @param the input type, that this PartialFunction will be applied to * @param the return type, that the results of the application will have - * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ class AbstractMatch { diff --git a/akka-actor/src/main/java/akka/japi/pf/AbstractPFBuilder.java b/akka-actor/src/main/java/akka/japi/pf/AbstractPFBuilder.java index 00ddd733f7..fd52b69175 100644 --- a/akka-actor/src/main/java/akka/japi/pf/AbstractPFBuilder.java +++ b/akka-actor/src/main/java/akka/japi/pf/AbstractPFBuilder.java @@ -12,7 +12,6 @@ import scala.PartialFunction; * @param the input type, that this PartialFunction will be applied to * @param the return type, that the results of the application will have * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ abstract class AbstractPFBuilder { diff --git a/akka-actor/src/main/java/akka/japi/pf/DeciderBuilder.java b/akka-actor/src/main/java/akka/japi/pf/DeciderBuilder.java index 4c272c0ff7..00a903ca8a 100644 --- a/akka-actor/src/main/java/akka/japi/pf/DeciderBuilder.java +++ b/akka-actor/src/main/java/akka/japi/pf/DeciderBuilder.java @@ -28,7 +28,6 @@ import static akka.actor.SupervisorStrategy.Directive; * } * * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ public class DeciderBuilder { private DeciderBuilder() { diff --git a/akka-actor/src/main/java/akka/japi/pf/FI.java b/akka-actor/src/main/java/akka/japi/pf/FI.java index 715c101cd9..842290d379 100644 --- a/akka-actor/src/main/java/akka/japi/pf/FI.java +++ b/akka-actor/src/main/java/akka/japi/pf/FI.java @@ -8,7 +8,6 @@ package akka.japi.pf; * Class that encapsulates all the Functional Interfaces * used for creating partial functions. * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ public final class FI { private FI() { diff --git a/akka-actor/src/main/java/akka/japi/pf/FSMStateFunctionBuilder.java b/akka-actor/src/main/java/akka/japi/pf/FSMStateFunctionBuilder.java index a6939cb645..1dc446a8e6 100644 --- a/akka-actor/src/main/java/akka/japi/pf/FSMStateFunctionBuilder.java +++ b/akka-actor/src/main/java/akka/japi/pf/FSMStateFunctionBuilder.java @@ -14,7 +14,6 @@ import java.util.List; * @param the state type * @param the data type * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ @SuppressWarnings("rawtypes") public class FSMStateFunctionBuilder { diff --git a/akka-actor/src/main/java/akka/japi/pf/FSMStopBuilder.java b/akka-actor/src/main/java/akka/japi/pf/FSMStopBuilder.java index cb4959c957..931ab9a7a1 100644 --- a/akka-actor/src/main/java/akka/japi/pf/FSMStopBuilder.java +++ b/akka-actor/src/main/java/akka/japi/pf/FSMStopBuilder.java @@ -14,7 +14,6 @@ import scala.runtime.BoxedUnit; * @param the state type * @param the data type * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ public class FSMStopBuilder { diff --git a/akka-actor/src/main/java/akka/japi/pf/FSMTransitionHandlerBuilder.java b/akka-actor/src/main/java/akka/japi/pf/FSMTransitionHandlerBuilder.java index 627ead01c5..89808b356f 100644 --- a/akka-actor/src/main/java/akka/japi/pf/FSMTransitionHandlerBuilder.java +++ b/akka-actor/src/main/java/akka/japi/pf/FSMTransitionHandlerBuilder.java @@ -13,11 +13,10 @@ import scala.Tuple2; * * @param the state type * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ public class FSMTransitionHandlerBuilder { - private UnitPFBuilder> builder = + private final UnitPFBuilder> builder = new UnitPFBuilder>(); /** diff --git a/akka-actor/src/main/java/akka/japi/pf/Match.java b/akka-actor/src/main/java/akka/japi/pf/Match.java index c8c57c5e0a..d18e854b14 100644 --- a/akka-actor/src/main/java/akka/japi/pf/Match.java +++ b/akka-actor/src/main/java/akka/japi/pf/Match.java @@ -14,7 +14,6 @@ import scala.PartialFunction; * @param the input type, that this PartialFunction will be applied to * @param the return type, that the results of the application will have * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ public class Match extends AbstractMatch { @@ -27,11 +26,25 @@ public class Match extends AbstractMatch { * @return a builder with the case statement added * @see PFBuilder#match(Class, FI.Apply) */ - public static PFBuilder match(final Class type, - final FI.Apply apply) { + public static PFBuilder match(final Class

type, + final FI.Apply apply) { return new PFBuilder().match(type, apply); } + /** + * Convenience function to create a {@link PFBuilder} with the first + * case statement added without compile time type check of the parameters. + * Should normally not be used, but when matching on class with generic type + * argument it can be useful, e.g. List.class and + * (List<String> list) -> {}. + * + * @see PFBuilder#matchUnchecked(Class, FI.Apply) + */ + public static PFBuilder matchUnchecked(final Class type, + final FI.Apply apply) { + return new PFBuilder().matchUnchecked(type, apply); + } + /** * Convenience function to create a {@link PFBuilder} with the first * case statement added. @@ -42,12 +55,27 @@ public class Match extends AbstractMatch { * @return a builder with the case statement added * @see PFBuilder#match(Class, FI.TypedPredicate, FI.Apply) */ - public static PFBuilder match(final Class type, - final FI.TypedPredicate predicate, - final FI.Apply apply) { + public static PFBuilder match(final Class

type, + final FI.TypedPredicate

predicate, + final FI.Apply apply) { return new PFBuilder().match(type, predicate, apply); } + /** + * Convenience function to create a {@link PFBuilder} with the first + * case statement added without compile time type check of the parameters. + * Should normally not be used, but when matching on class with generic type + * argument it can be useful, e.g. List.class and + * (List<String> list) -> {}. + * + * @see PFBuilder#matchUnchecked(Class, FI.TypedPredicate, FI.Apply) + */ + public static PFBuilder matchUnchecked(final Class type, + final FI.TypedPredicate predicate, + final FI.Apply apply) { + return new PFBuilder().matchUnchecked(type, predicate, apply); + } + /** * Convenience function to create a {@link PFBuilder} with the first * case statement added. @@ -91,10 +119,10 @@ public class Match extends AbstractMatch { /** * Convenience function to make the Java code more readable. *

- * + * *

    *   Matcher<X, Y> matcher = Matcher.create(...);
-   * 
+   *
    *   Y someY = matcher.match(obj);
    * 
* diff --git a/akka-actor/src/main/java/akka/japi/pf/PFBuilder.java b/akka-actor/src/main/java/akka/japi/pf/PFBuilder.java index 6b77704ccf..d344f3c0a4 100644 --- a/akka-actor/src/main/java/akka/japi/pf/PFBuilder.java +++ b/akka-actor/src/main/java/akka/japi/pf/PFBuilder.java @@ -10,7 +10,6 @@ package akka.japi.pf; * @param the input type, that this PartialFunction will be applied to * @param the return type, that the results of the application will have * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ public final class PFBuilder extends AbstractPFBuilder { @@ -27,8 +26,22 @@ public final class PFBuilder extends AbstractPFBuilder { * @param apply an action to apply to the argument if the type matches * @return a builder with the case statement added */ + public

PFBuilder match(final Class

type, FI.Apply apply) { + return matchUnchecked(type, apply); + } + + /** + * Add a new case statement to this builder without compile time type check of the parameters. + * Should normally not be used, but when matching on class with generic type + * argument it can be useful, e.g. List.class and + * (List<String> list) -> {}. + * + * @param type a type to match the argument against + * @param apply an action to apply to the argument if the type matches + * @return a builder with the case statement added + */ @SuppressWarnings("unchecked") - public

PFBuilder match(final Class type, FI.Apply apply) { + public PFBuilder matchUnchecked(final Class type, FI.Apply apply) { FI.Predicate predicate = new FI.Predicate() { @Override @@ -37,7 +50,7 @@ public final class PFBuilder extends AbstractPFBuilder { } }; - addStatement(new CaseStatement(predicate, (FI.Apply) apply)); + addStatement(new CaseStatement(predicate, (FI.Apply) apply)); return this; } @@ -49,23 +62,37 @@ public final class PFBuilder extends AbstractPFBuilder { * @param apply an action to apply to the argument if the type matches and the predicate returns true * @return a builder with the case statement added */ + public

PFBuilder match(final Class

type, + final FI.TypedPredicate

predicate, + final FI.Apply apply) { + return matchUnchecked(type, predicate, apply); + } + + /** + * Add a new case statement to this builder without compile time type check of the parameters. + * Should normally not be used, but when matching on class with generic type + * argument it can be useful, e.g. List.class and + * (List<String> list) -> {}. + * + * @param type a type to match the argument against + * @param predicate a predicate that will be evaluated on the argument if the type matches + * @param apply an action to apply to the argument if the type matches and the predicate returns true + * @return a builder with the case statement added + */ @SuppressWarnings("unchecked") - public

PFBuilder match(final Class type, - final FI.TypedPredicate predicate, - final FI.Apply apply) { + public PFBuilder matchUnchecked(final Class type, + final FI.TypedPredicate predicate, + final FI.Apply apply) { FI.Predicate fiPredicate = new FI.Predicate() { @Override public boolean defined(Object o) { if (!type.isInstance(o)) return false; - else { - @SuppressWarnings("unchecked") - P p = (P) o; - return ((FI.TypedPredicate

) predicate).defined(p); - } + else + return ((FI.TypedPredicate) predicate).defined(o); } }; - addStatement(new CaseStatement(fiPredicate, (FI.Apply) apply)); + addStatement(new CaseStatement(fiPredicate, (FI.Apply) apply)); return this; } diff --git a/akka-actor/src/main/java/akka/japi/pf/ReceiveBuilder.java b/akka-actor/src/main/java/akka/japi/pf/ReceiveBuilder.java index 2bb0000028..80b9c0a937 100644 --- a/akka-actor/src/main/java/akka/japi/pf/ReceiveBuilder.java +++ b/akka-actor/src/main/java/akka/japi/pf/ReceiveBuilder.java @@ -4,6 +4,11 @@ package akka.japi.pf; +import scala.PartialFunction; +import scala.runtime.BoxedUnit; +import akka.actor.AbstractActor; +import akka.actor.AbstractActor.Receive; + /** * Used for building a partial function for {@link akka.actor.Actor#receive() Actor.receive()}. * @@ -30,80 +35,198 @@ package akka.japi.pf; * } * * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ public class ReceiveBuilder { - private ReceiveBuilder() { + + private PartialFunction statements = null; + + protected void addStatement(PartialFunction statement) { + if (statements == null) + statements = statement; + else + statements = statements.orElse(statement); } /** - * Return a new {@link UnitPFBuilder} with no case statements. They can be added later as the returned {@link - * UnitPFBuilder} is a mutable object. + * Build a {@link scala.PartialFunction} from this builder. After this call + * the builder will be reset. + * + * @return a PartialFunction for this builder. + */ + public Receive build() { + PartialFunction empty = CaseStatement.empty(); + + if (statements == null) + return new Receive(empty); + else + return new Receive(statements.orElse(empty)); // FIXME why no new Receive(statements)? + } + + /** + * Return a new {@link ReceiveBuilder} with no case statements. They can be + * added later as the returned {@link ReceiveBuilder} is a mutable object. * * @return a builder with no case statements */ - public static UnitPFBuilder create() { - return new UnitPFBuilder<>(); + public static ReceiveBuilder create() { + return new ReceiveBuilder(); } /** - * Return a new {@link UnitPFBuilder} with a case statement added. + * Add a new case statement to this builder. * - * @param type a type to match the argument against - * @param apply an action to apply to the argument if the type matches + * @param type + * a type to match the argument against + * @param apply + * an action to apply to the argument if the type matches * @return a builder with the case statement added */ - public static

UnitPFBuilder match(final Class type, FI.UnitApply apply) { - return UnitMatch.match(type, apply); + public

ReceiveBuilder match(final Class

type, final FI.UnitApply

apply) { + return matchUnchecked(type, apply); } /** - * Return a new {@link UnitPFBuilder} with a case statement added. + * Add a new case statement to this builder without compile time type check. + * Should normally not be used, but when matching on class with generic type + * argument it can be useful, e.g. List.class and + * (List<String> list) -> {}. * - * @param type a type to match the argument against - * @param predicate a predicate that will be evaluated on the argument if the type matches - * @param apply an action to apply to the argument if the type matches and the predicate returns true + * @param type + * a type to match the argument against + * @param apply + * an action to apply to the argument if the type matches * @return a builder with the case statement added */ - public static

UnitPFBuilder match(final Class type, - FI.TypedPredicate predicate, - FI.UnitApply apply) { - return UnitMatch.match(type, predicate, apply); + @SuppressWarnings("unchecked") + public ReceiveBuilder matchUnchecked(final Class type, final FI.UnitApply apply) { + + FI.Predicate predicate = new FI.Predicate() { + @Override + public boolean defined(Object o) { + return type.isInstance(o); + } + }; + + addStatement(new UnitCaseStatement(predicate, (FI.UnitApply) apply)); + + return this; } /** - * Return a new {@link UnitPFBuilder} with a case statement added. + * Add a new case statement to this builder. * - * @param object the object to compare equals with - * @param apply an action to apply to the argument if the object compares equal + * @param type + * a type to match the argument against + * @param predicate + * a predicate that will be evaluated on the argument if the type + * matches + * @param apply + * an action to apply to the argument if the type matches and the + * predicate returns true * @return a builder with the case statement added */ - public static

UnitPFBuilder matchEquals(P object, FI.UnitApply

apply) { - return UnitMatch.matchEquals(object, apply); + public

ReceiveBuilder match(final Class

type, final FI.TypedPredicate

predicate, + final FI.UnitApply

apply) { + return matchUnchecked(type, predicate, apply); + } + + /** + * Add a new case statement to this builder without compile time type check. + * Should normally not be used, but when matching on class with generic type + * argument it can be useful, e.g. List.class and + * (List<String> list) -> {}. + * + * @param type + * a type to match the argument against + * @param predicate + * a predicate that will be evaluated on the argument if the type + * matches + * @param apply + * an action to apply to the argument if the type matches and the + * predicate returns true + * @return a builder with the case statement added + */ + @SuppressWarnings("unchecked") + public

ReceiveBuilder matchUnchecked(final Class type, final FI.TypedPredicate predicate, + final FI.UnitApply

apply) { + FI.Predicate fiPredicate = new FI.Predicate() { + @Override + public boolean defined(Object o) { + if (!type.isInstance(o)) + return false; + else + return ((FI.TypedPredicate) predicate).defined(o); + } + }; + + addStatement(new UnitCaseStatement(fiPredicate, (FI.UnitApply) apply)); + + return this; } /** - * Return a new {@link UnitPFBuilder} with a case statement added. + * Add a new case statement to this builder. * - * @param object the object to compare equals with - * @param predicate a predicate that will be evaluated on the argument if the object compares equal - * @param apply an action to apply to the argument if the object compares equal + * @param object + * the object to compare equals with + * @param apply + * an action to apply to the argument if the object compares equal * @return a builder with the case statement added */ - public static

UnitPFBuilder matchEquals(P object, - FI.TypedPredicate

predicate, - FI.UnitApply

apply) { - return UnitMatch.matchEquals(object, predicate, apply); + public

ReceiveBuilder matchEquals(final P object, final FI.UnitApply

apply) { + addStatement(new UnitCaseStatement(new FI.Predicate() { + @Override + public boolean defined(Object o) { + return object.equals(o); + } + }, apply)); + return this; } /** - * Return a new {@link UnitPFBuilder} with a case statement added. + * Add a new case statement to this builder. * - * @param apply an action to apply to the argument + * @param object + * the object to compare equals with + * @param predicate + * a predicate that will be evaluated on the argument if the object + * compares equal + * @param apply + * an action to apply to the argument if the object compares equal * @return a builder with the case statement added */ - public static UnitPFBuilder matchAny(FI.UnitApply apply) { - return UnitMatch.matchAny(apply); + public

ReceiveBuilder matchEquals(final P object, final FI.TypedPredicate

predicate, + final FI.UnitApply

apply) { + addStatement(new UnitCaseStatement(new FI.Predicate() { + @Override + public boolean defined(Object o) { + if (!object.equals(o)) + return false; + else { + @SuppressWarnings("unchecked") + P p = (P) o; + return predicate.defined(p); + } + } + }, apply)); + return this; + } + + /** + * Add a new case statement to this builder, that matches any argument. + * + * @param apply + * an action to apply to the argument + * @return a builder with the case statement added + */ + public ReceiveBuilder matchAny(final FI.UnitApply apply) { + addStatement(new UnitCaseStatement(new FI.Predicate() { + @Override + public boolean defined(Object o) { + return true; + } + }, apply)); + return this; } } diff --git a/akka-actor/src/main/java/akka/japi/pf/UnitMatch.java b/akka-actor/src/main/java/akka/japi/pf/UnitMatch.java index dffb621219..8381029977 100644 --- a/akka-actor/src/main/java/akka/japi/pf/UnitMatch.java +++ b/akka-actor/src/main/java/akka/japi/pf/UnitMatch.java @@ -16,7 +16,6 @@ import scala.runtime.BoxedUnit; * * @param the input type, that this PartialFunction will be applied to * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ public class UnitMatch extends AbstractMatch { @@ -29,10 +28,20 @@ public class UnitMatch extends AbstractMatch { * @return a builder with the case statement added * @see UnitPFBuilder#match(Class, FI.UnitApply) */ - public static UnitPFBuilder match(final Class type, FI.UnitApply apply) { + public static UnitPFBuilder match(final Class

type, FI.UnitApply

apply) { return new UnitPFBuilder().match(type, apply); } + /** + * Convenience function to create a {@link UnitPFBuilder} with the first + * case statement added. Should normally not be used. + * + * @see UnitPFBuilder#matchUnchecked(Class, FI.UnitApply) + */ + public static UnitPFBuilder matchUnchecked(final Class type, final FI.UnitApply apply) { + return new UnitPFBuilder().matchUnchecked(type, apply); + } + /** * Convenience function to create a {@link UnitPFBuilder} with the first * case statement added. @@ -43,12 +52,24 @@ public class UnitMatch extends AbstractMatch { * @return a builder with the case statement added * @see UnitPFBuilder#match(Class, FI.TypedPredicate, FI.UnitApply) */ - public static UnitPFBuilder match(final Class type, - final FI.TypedPredicate predicate, - final FI.UnitApply apply) { + public static UnitPFBuilder match(final Class

type, + final FI.TypedPredicate

predicate, + final FI.UnitApply

apply) { return new UnitPFBuilder().match(type, predicate, apply); } + /** + * Convenience function to create a {@link UnitPFBuilder} with the first + * case statement added. Should normally not be used. + * + * @see UnitPFBuilder#matchUnchecked(Class, FI.TypedPredicate, FI.UnitApply) + */ + public static UnitPFBuilder matchUnchecked(final Class type, + final FI.TypedPredicate predicate, + final FI.UnitApply apply) { + return new UnitPFBuilder().matchUnchecked(type, predicate, apply); + } + /** * Convenience function to create a {@link UnitPFBuilder} with the first * case statement added. @@ -110,7 +131,7 @@ public class UnitMatch extends AbstractMatch { *

*


    *   UnitMatcher<X> matcher = UnitMatcher.create(...);
-   * 
+   *
    *   matcher.match(obj);
    * 
* diff --git a/akka-actor/src/main/java/akka/japi/pf/UnitPFBuilder.java b/akka-actor/src/main/java/akka/japi/pf/UnitPFBuilder.java index 59f615aaae..8d24690836 100644 --- a/akka-actor/src/main/java/akka/japi/pf/UnitPFBuilder.java +++ b/akka-actor/src/main/java/akka/japi/pf/UnitPFBuilder.java @@ -13,7 +13,6 @@ import scala.runtime.BoxedUnit; * * @param the input type, that this PartialFunction to be applied to * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ public final class UnitPFBuilder extends AbstractPFBuilder { @@ -30,9 +29,25 @@ public final class UnitPFBuilder extends AbstractPFBuilder { * @param apply an action to apply to the argument if the type matches * @return a builder with the case statement added */ + public

UnitPFBuilder match(final Class

type, + final FI.UnitApply

apply) { + return matchUnchecked(type, apply); + } + + /** + * Add a new case statement to this builder without compile time type check. + * Should normally not be used, but when matching on class with generic type + * argument it can be useful, e.g. List.class and + * (List<String> list) -> {}. + * + * @param type + * a type to match the argument against + * @param apply + * an action to apply to the argument if the type matches + * @return a builder with the case statement added + */ @SuppressWarnings("unchecked") - public

UnitPFBuilder match(final Class type, - final FI.UnitApply apply) { + public UnitPFBuilder matchUnchecked(final Class type, final FI.UnitApply apply) { FI.Predicate predicate = new FI.Predicate() { @Override @@ -41,7 +56,7 @@ public final class UnitPFBuilder extends AbstractPFBuilder { } }; - addStatement(new UnitCaseStatement(predicate, (FI.UnitApply

) apply)); + addStatement(new UnitCaseStatement(predicate, (FI.UnitApply) apply)); return this; } @@ -54,24 +69,39 @@ public final class UnitPFBuilder extends AbstractPFBuilder { * @param apply an action to apply to the argument if the type matches and the predicate returns true * @return a builder with the case statement added */ + public

UnitPFBuilder match(final Class

type, + final FI.TypedPredicate

predicate, + final FI.UnitApply

apply) { + return matchUnchecked(type, predicate, apply); + } + + /** + * Add a new case statement to this builder without compile time type check. + * Should normally not be used, but when matching on class with generic type + * argument it can be useful, e.g. List.class and + * (List<String> list) -> {}. + * + * @param type a type to match the argument against + * @param predicate a predicate that will be evaluated on the argument if the type matches + * @param apply an action to apply to the argument if the type matches and the predicate returns true + * @return a builder with the case statement added + */ @SuppressWarnings("unchecked") - public

UnitPFBuilder match(final Class type, - final FI.TypedPredicate predicate, - final FI.UnitApply apply) { + public UnitPFBuilder matchUnchecked(final Class type, + final FI.TypedPredicate predicate, + final FI.UnitApply apply) { FI.Predicate fiPredicate = new FI.Predicate() { @Override public boolean defined(Object o) { if (!type.isInstance(o)) return false; else { - @SuppressWarnings("unchecked") - P p = (P) o; - return ((FI.TypedPredicate

) predicate).defined(p); + return ((FI.TypedPredicate) predicate).defined(o); } } }; - addStatement(new UnitCaseStatement(fiPredicate, (FI.UnitApply

) apply)); + addStatement(new UnitCaseStatement(fiPredicate, (FI.UnitApply) apply)); return this; } diff --git a/akka-actor/src/main/scala/akka/actor/AbstractActor.scala b/akka-actor/src/main/scala/akka/actor/AbstractActor.scala index 7d7ee2025a..c002d5c7e2 100644 --- a/akka-actor/src/main/scala/akka/actor/AbstractActor.scala +++ b/akka-actor/src/main/scala/akka/actor/AbstractActor.scala @@ -5,17 +5,82 @@ package akka.actor import akka.annotation.ApiMayChange +import akka.japi.pf.ReceiveBuilder +import akka.japi.pf.UnitPFBuilder +import scala.runtime.BoxedUnit +import java.util.Optional /** * Java API: compatible with lambda expressions - * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ object AbstractActor { + + /** + * Defines which messages the Actor can handle, along with the implementation of + * how the messages should be processed. You can build such behavior with the + * [[akka.japi.pf.receivebuilder]] but it can be implemented in other ways than + * using the `ReceiveBuilder` since it in the end is just a wrapper around a + * Scala `PartialFunction`. In Java, you can implement `PartialFunction` by + * extending `AbstractPartialFunction`. + */ + final class Receive(val onMessage: PartialFunction[Any, BoxedUnit]) { + /** + * Composes this `Receive` with a fallback which gets applied + * where this partial function is not defined. + */ + def orElse(other: Receive): Receive = new Receive(onMessage.orElse(other.onMessage)) + } + /** * emptyBehavior is a Receive-expression that matches no messages at all, ever. */ - final val emptyBehavior = Actor.emptyBehavior + final val emptyBehavior: Receive = new Receive(PartialFunction.empty) + + /** + * The actor context - the view of the actor cell from the actor. + * Exposes contextual information for the actor and the current message. + */ + trait ActorContext extends akka.actor.ActorContext { + + /** + * Returns an unmodifiable Java Collection containing the linked actors, + * please note that the backing map is thread-safe but not immutable + */ + def getChildren(): java.lang.Iterable[ActorRef] + + /** + * Returns a reference to the named child or null if no child with + * that name exists. + */ + @deprecated("Use findChild instead", "2.5.0") + def getChild(name: String): ActorRef + + /** + * Returns a reference to the named child if it exists. + */ + def findChild(name: String): Optional[ActorRef] + + /** + * Changes the Actor's behavior to become the new 'Receive' handler. + * Replaces the current behavior on the top of the behavior stack. + */ + def become(behavior: Receive): Unit = + become(behavior, discardOld = true) + + /** + * Changes the Actor's behavior to become the new 'Receive' handler. + * This method acts upon the behavior stack as follows: + * + * - if `discardOld = true` it will replace the top element (i.e. the current behavior) + * - if `discardOld = false` it will keep the current behavior and push the given one atop + * + * The default of replacing the current behavior on the stack has been chosen to avoid memory + * leaks in case client code is written without consulting this documentation first (i.e. + * always pushing new behaviors and never issuing an `unbecome()`) + */ + def become(behavior: Receive, discardOld: Boolean): Unit = + become(behavior.onMessage.asInstanceOf[PartialFunction[Any, Unit]], discardOld) + } } /** @@ -29,49 +94,151 @@ object AbstractActor { * int count = 0; * * public MyActor() { - * receive(ReceiveBuilder. - * match(Double.class, d -> { + * receive(receiveBuilder() + * .match(Double.class, d -> { * sender().tell(d.isNaN() ? 0 : d, self()); - * }). - * match(Integer.class, i -> { + * }) + * .match(Integer.class, i -> { * sender().tell(i * 10, self()); - * }). - * match(String.class, s -> s.startsWith("foo"), s -> { + * }) + * .match(String.class, s -> s.startsWith("foo"), s -> { * sender().tell(s.toUpperCase(), self()); - * }).build() + * }) + * .build(); * ); * } * } * * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ -@ApiMayChange abstract class AbstractActor extends Actor { - private var _receive: Receive = null - /** - * Set up the initial receive behavior of the Actor. - * - * @param receive The receive behavior. - */ - @throws(classOf[IllegalActorStateException]) - protected def receive(receive: Receive): Unit = - if (_receive == null) _receive = receive - else throw IllegalActorStateException("Actor behavior has already been set with receive(...), " + - "use context().become(...) to change it later") - - /** - * Returns this AbstractActor's AbstractActorContext - * The AbstractActorContext is not thread safe so do not expose it outside of the + * Returns this AbstractActor's ActorContext + * The ActorContext is not thread safe so do not expose it outside of the * AbstractActor. */ - def getContext(): AbstractActorContext = context.asInstanceOf[AbstractActorContext] + def getContext(): AbstractActor.ActorContext = context.asInstanceOf[AbstractActor.ActorContext] - override def receive = - if (_receive != null) _receive - else throw IllegalActorStateException("Actor behavior has not been set with receive(...)") + /** + * Returns the ActorRef for this actor. + * + * Same as `self()`. + */ + def getSelf(): ActorRef = self + + /** + * The reference sender Actor of the currently processed message. This is + * always a legal destination to send to, even if there is no logical recipient + * for the reply, in which case it will be sent to the dead letter mailbox. + * + * Same as `sender()`. + * + * WARNING: Only valid within the Actor itself, so do not close over it and + * publish it to other threads! + */ + def getSender(): ActorRef = sender() + + /** + * User overridable definition the strategy to use for supervising + * child actors. + */ + override def supervisorStrategy: SupervisorStrategy = super.supervisorStrategy + + /** + * User overridable callback. + *

+ * Is called when an Actor is started. + * Actor are automatically started asynchronously when created. + * Empty default implementation. + */ + @throws(classOf[Exception]) + override def preStart(): Unit = super.preStart() + + /** + * User overridable callback. + *

+ * Is called asynchronously after `getContext().stop()` is invoked. + * Empty default implementation. + */ + @throws(classOf[Exception]) + override def postStop(): Unit = super.postStop() + + // TODO In 2.6.0 we can remove deprecation and make the method final + @deprecated("Override preRestart with message parameter with Optional type instead", "2.5.0") + @throws(classOf[Exception]) + override def preRestart(reason: Throwable, message: Option[Any]): Unit = { + import scala.compat.java8.OptionConverters._ + preRestart(reason, message.asJava) + } + + /** + * User overridable callback: '''By default it disposes of all children and then calls `postStop()`.''' + *

+ * Is called on a crashed Actor right BEFORE it is restarted to allow clean + * up of resources before Actor is terminated. + */ + @throws(classOf[Exception]) + def preRestart(reason: Throwable, message: Optional[Any]): Unit = { + import scala.compat.java8.OptionConverters._ + super.preRestart(reason, message.asScala) + } + + /** + * User overridable callback: By default it calls `preStart()`. + *

+ * Is called right AFTER restart on the newly created Actor to allow reinitialization after an Actor crash. + */ + @throws(classOf[Exception]) + override def postRestart(reason: Throwable): Unit = super.postRestart(reason) + + /** + * An actor has to define its initial receive behavior by implementing + * the `createReceive` method. + */ + def createReceive(): AbstractActor.Receive + + override def receive: PartialFunction[Any, Unit] = + createReceive().onMessage.asInstanceOf[PartialFunction[Any, Unit]] + + /** + * Convenience factory of the `ReceiveBuilder`. + * Creates a new empty `ReceiveBuilder`. + */ + final def receiveBuilder(): ReceiveBuilder = ReceiveBuilder.create() +} + +/** + * If the validation of the `ReceiveBuilder` match logic turns out to be a bottleneck for some of your + * actors you can consider to implement it at lower level by extending `UntypedAbstractActor` instead + * of `AbstractActor`. The partial functions created by the `ReceiveBuilder` consist of multiple lambda + * expressions for every match statement, where each lambda is referencing the code to be run. This is something + * that the JVM can have problems optimizing and the resulting code might not be as performant as the + * untyped version. When extending `UntypedAbstractActor` each message is received as an untyped + * `Object` and you have to inspect and cast it to the actual message type in other ways (instanceof checks). + */ +abstract class UntypedAbstractActor extends AbstractActor { + + final override def createReceive(): AbstractActor.Receive = + throw new UnsupportedOperationException("createReceive should not be used by UntypedAbstractActor") + + override def receive: PartialFunction[Any, Unit] = { case msg ⇒ onReceive(msg) } + + /** + * To be implemented by concrete UntypedAbstractActor, this defines the behavior of the + * actor. + */ + @throws(classOf[Throwable]) + def onReceive(message: Any): Unit + + /** + * Recommended convention is to call this method if the message + * isn't handled in [[#onReceive]] (e.g. unknown message type). + * By default it fails with either a [[akka.actor.DeathPactException]] (in + * case of an unhandled [[akka.actor.Terminated]] message) or publishes an [[akka.actor.UnhandledMessage]] + * to the actor's system's [[akka.event.EventStream]]. + */ + override def unhandled(message: Any): Unit = super.unhandled(message) } /** @@ -79,7 +246,6 @@ abstract class AbstractActor extends Actor { * * Actor base class that mixes in logging into the Actor. * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ abstract class AbstractLoggingActor extends AbstractActor with ActorLogging @@ -126,7 +292,6 @@ abstract class AbstractLoggingActor extends AbstractActor with ActorLogging * There is also an unrestricted version [[akka.actor.AbstractActorWithUnrestrictedStash]] that does not * enforce the mailbox type. * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ abstract class AbstractActorWithStash extends AbstractActor with Stash @@ -137,7 +302,6 @@ abstract class AbstractActorWithStash extends AbstractActor with Stash * manually, and the mailbox should extend the [[akka.dispatch.DequeBasedMessageQueueSemantics]] marker trait. * See [[akka.actor.AbstractActorWithStash]] for details on how `Stash` works. * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ abstract class AbstractActorWithUnboundedStash extends AbstractActor with UnboundedStash @@ -147,6 +311,5 @@ abstract class AbstractActorWithUnboundedStash extends AbstractActor with Unboun * Actor base class with `Stash` that does not enforce any mailbox type. The mailbox of the actor has to be configured * manually. See [[akka.actor.AbstractActorWithStash]] for details on how `Stash` works. * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ abstract class AbstractActorWithUnrestrictedStash extends AbstractActor with UnrestrictedStash diff --git a/akka-actor/src/main/scala/akka/actor/AbstractFSM.scala b/akka-actor/src/main/scala/akka/actor/AbstractFSM.scala index 72673f17f0..afb778c3d0 100644 --- a/akka-actor/src/main/scala/akka/actor/AbstractFSM.scala +++ b/akka-actor/src/main/scala/akka/actor/AbstractFSM.scala @@ -11,9 +11,7 @@ import scala.concurrent.duration.FiniteDuration /** * Java API: compatible with lambda expressions * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ -@ApiMayChange object AbstractFSM { /** * A partial function value which does not match anything and can be used to @@ -31,7 +29,6 @@ object AbstractFSM { * * Finite State Machine actor abstract base class. * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ abstract class AbstractFSM[S, D] extends FSM[S, D] { import akka.japi.pf._ @@ -382,7 +379,6 @@ abstract class AbstractFSM[S, D] extends FSM[S, D] { * * Finite State Machine actor abstract base class. * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ abstract class AbstractLoggingFSM[S, D] extends AbstractFSM[S, D] with LoggingFSM[S, D] @@ -391,6 +387,5 @@ abstract class AbstractLoggingFSM[S, D] extends AbstractFSM[S, D] with LoggingFS * * Finite State Machine actor abstract base class with Stash support. * - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ abstract class AbstractFSMWithStash[S, D] extends AbstractFSM[S, D] with Stash diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index 1ae1622dca..59abb36f71 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -10,6 +10,7 @@ import akka.event.LoggingAdapter import scala.annotation.tailrec import scala.beans.BeanProperty import scala.util.control.NoStackTrace +import java.util.Optional /** * INTERNAL API @@ -76,7 +77,17 @@ final case class ActorIdentity(correlationId: Any, ref: Option[ActorRef]) { * Java API: `ActorRef` of the actor replying to the request or * null if no actor matched the request. */ + @deprecated("Use getActorRef instead", "2.5.0") def getRef: ActorRef = ref.orNull + + /** + * Java API: `ActorRef` of the actor replying to the request or + * not defined if no actor matched the request. + */ + def getActorRef: Optional[ActorRef] = { + import scala.compat.java8.OptionConverters._ + ref.asJava + } } /** @@ -390,7 +401,7 @@ object Actor { * initial behavior of the actor as a partial function (behavior can be changed * using `context.become` and `context.unbecome`). * - * This is the Scala API (hence the Scala code below), for the Java API see [[akka.actor.UntypedActor]]. + * This is the Scala API (hence the Scala code below), for the Java API see [[akka.actor.AbstractActor]]. * * {{{ * class ExampleActor extends Actor { @@ -434,14 +445,14 @@ trait Actor { type Receive = Actor.Receive /** - * Stores the context for this actor, including self, and sender. + * Scala API: Stores the context for this actor, including self, and sender. * It is implicit to support operations such as `forward`. * * WARNING: Only valid within the Actor itself, so do not close over it and * publish it to other threads! * * [[akka.actor.ActorContext]] is the Scala API. `getContext` returns a - * [[akka.actor.UntypedActorContext]], which is the Java API of the actor + * [[akka.actor.AbstractActor.ActorContext]], which is the Java API of the actor * context. */ implicit val context: ActorContext = { @@ -476,7 +487,7 @@ trait Actor { final def sender(): ActorRef = context.sender() /** - * This defines the initial actor behavior, it must return a partial function + * Scala API: This defines the initial actor behavior, it must return a partial function * with the actor logic. */ //#receive @@ -550,7 +561,7 @@ trait Actor { //#lifecycle-hooks /** - * User overridable callback: '''By default it disposes of all children and then calls `postStop()`.''' + * Scala API: User overridable callback: '''By default it disposes of all children and then calls `postStop()`.''' * @param reason the Throwable that caused the restart to happen * @param message optionally the current message the actor processed when failing, if applicable *

diff --git a/akka-actor/src/main/scala/akka/actor/ActorCell.scala b/akka-actor/src/main/scala/akka/actor/ActorCell.scala index b34f5f1d43..925fc9ff14 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorCell.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorCell.scala @@ -18,6 +18,8 @@ import java.util.concurrent.ThreadLocalRandom import scala.util.control.NonFatal import akka.dispatch.MessageDispatcher import akka.util.Reflect +import akka.japi.pf.ReceiveBuilder +import akka.actor.AbstractActor.Receive /** * The actor context - the view of the actor cell from the actor. @@ -160,29 +162,11 @@ trait ActorContext extends ActorRefFactory { throw new NotSerializableException("ActorContext is not serializable!") } -/** - * AbstractActorContext is the AbstractActor equivalent of ActorContext, - * containing the Java API - */ -trait AbstractActorContext extends ActorContext { - - /** - * Returns an unmodifiable Java Collection containing the linked actors, - * please note that the backing map is thread-safe but not immutable - */ - def getChildren(): java.lang.Iterable[ActorRef] - - /** - * Returns a reference to the named child or null if no child with - * that name exists. - */ - def getChild(name: String): ActorRef -} - /** * UntypedActorContext is the UntypedActor equivalent of ActorContext, * containing the Java API */ +@deprecated("Use AbstractActor instead of UntypedActor.", since = "2.5.0") trait UntypedActorContext extends ActorContext { /** @@ -377,7 +361,7 @@ private[akka] class ActorCell( final val props: Props, // Must be final so that it can be properly cleared in clearActorCellFields val dispatcher: MessageDispatcher, val parent: InternalActorRef) - extends UntypedActorContext with AbstractActorContext with Cell + extends UntypedActorContext with AbstractActor.ActorContext with Cell with dungeon.ReceiveTimeout with dungeon.Children with dungeon.Dispatch diff --git a/akka-actor/src/main/scala/akka/actor/ActorRef.scala b/akka-actor/src/main/scala/akka/actor/ActorRef.scala index c0adcb367e..d50495d968 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRef.scala @@ -65,29 +65,23 @@ object ActorRef { * import static akka.pattern.Patterns.ask; * import static akka.pattern.Patterns.pipe; * - * public class ExampleActor extends UntypedActor { + * public class ExampleActor extends AbstractActor { * // this child will be destroyed and re-created upon restart by default * final ActorRef other = getContext().actorOf(Props.create(OtherActor.class), "childName"); - * * @Override - * public void onReceive(Object o) { - * if (o instanceof Request1) { - * Msg msg = ((Request1) o).getMsg(); - * other.tell(msg, getSelf()); // uses this actor as sender reference, reply goes to us - * - * } else if (o instanceof Request2) { - * Msg msg = ((Request2) o).getMsg(); - * other.tell(msg, getSender()); // forward sender reference, enabling direct reply - * - * } else if (o instanceof Request3) { - * Msg msg = ((Request3) o).getMsg(); - * pipe(ask(other, msg, 5000), context().dispatcher()).to(getSender()); - * // the ask call will get a future from other's reply - * // when the future is complete, send its value to the original sender - * - * } else { - * unhandled(o); - * } + * public Receive createReceive() { + * return receiveBuilder() + * .match(Request1.class, msg -> + * // uses this actor as sender reference, reply goes to us + * other.tell(msg, getSelf())) + * .match(Request2.class, msg -> + * // forward sender reference, enabling direct reply + * other.tell(msg, getSender())) + * .match(Request3.class, msg -> + * // the ask call will get a future from other's reply + * // when the future is complete, send its value to the original sender + * pipe(ask(other, msg, 5000), context().dispatcher()).to(getSender())) + * .build(); * } * } * }}} diff --git a/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala b/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala index a29c18944b..4952ccc6dc 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala @@ -316,21 +316,6 @@ trait ActorRefFactory { * Java API: Look-up an actor by applying the given path elements, starting from the * current context, where `".."` signifies the parent of an actor. * - * Example: - * {{{ - * public class MyActor extends UntypedActor { - * public void onReceive(Object msg) throws Exception { - * ... - * final List path = new ArrayList(); - * path.add(".."); - * path.add("myBrother"); - * path.add("myNephew"); - * final ActorRef target = getContext().actorFor(path); - * ... - * } - * } - * }}} - * * For maximum performance use a collection with efficient head & tail operations. * * Actor references acquired with `actorFor` do not always include the full information diff --git a/akka-actor/src/main/scala/akka/actor/FaultHandling.scala b/akka-actor/src/main/scala/akka/actor/FaultHandling.scala index aa6d38229b..075d7b0104 100644 --- a/akka-actor/src/main/scala/akka/actor/FaultHandling.scala +++ b/akka-actor/src/main/scala/akka/actor/FaultHandling.scala @@ -411,21 +411,18 @@ case class AllForOneStrategy( /** * Java API: compatible with lambda expressions - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ def this(maxNrOfRetries: Int, withinTimeRange: Duration, decider: SupervisorStrategy.Decider) = this(maxNrOfRetries = maxNrOfRetries, withinTimeRange = withinTimeRange)(decider) /** * Java API: compatible with lambda expressions - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ def this(loggingEnabled: Boolean, decider: SupervisorStrategy.Decider) = this(loggingEnabled = loggingEnabled)(decider) /** * Java API: compatible with lambda expressions - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ def this(decider: SupervisorStrategy.Decider) = this()(decider) @@ -487,21 +484,15 @@ case class OneForOneStrategy( /** * Java API: compatible with lambda expressions - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ def this(maxNrOfRetries: Int, withinTimeRange: Duration, decider: SupervisorStrategy.Decider) = this(maxNrOfRetries = maxNrOfRetries, withinTimeRange = withinTimeRange)(decider) - /** - * Java API: compatible with lambda expressions - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. - */ def this(loggingEnabled: Boolean, decider: SupervisorStrategy.Decider) = this(loggingEnabled = loggingEnabled)(decider) /** * Java API: compatible with lambda expressions - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ def this(decider: SupervisorStrategy.Decider) = this()(decider) diff --git a/akka-actor/src/main/scala/akka/actor/UntypedActor.scala b/akka-actor/src/main/scala/akka/actor/UntypedActor.scala index 1f5eee759b..4925561fa8 100644 --- a/akka-actor/src/main/scala/akka/actor/UntypedActor.scala +++ b/akka-actor/src/main/scala/akka/actor/UntypedActor.scala @@ -92,6 +92,7 @@ package akka.actor * } * }}} */ +@deprecated("Use AbstractActor instead of UntypedActor.", since = "2.5.0") abstract class UntypedActor extends Actor { /** diff --git a/akka-actor/src/main/scala/akka/actor/UntypedActorWithStash.scala b/akka-actor/src/main/scala/akka/actor/UntypedActorWithStash.scala index 4dfe965e93..20fc03bb0d 100644 --- a/akka-actor/src/main/scala/akka/actor/UntypedActorWithStash.scala +++ b/akka-actor/src/main/scala/akka/actor/UntypedActorWithStash.scala @@ -44,12 +44,14 @@ package akka.actor * There is also an unrestricted version [[akka.actor.UntypedActorWithUnrestrictedStash]] that does not * enforce the mailbox type. */ +@deprecated("Use AbstractActor instead of UntypedActor.", since = "2.5.0") abstract class UntypedActorWithStash extends UntypedActor with Stash /** * Actor base class with `Stash` that enforces an unbounded deque for the actor. * See [[akka.actor.UntypedActorWithStash]] for details on how `Stash` works. */ +@deprecated("Use AbstractActor instead of UntypedActor.", since = "2.5.0") abstract class UntypedActorWithUnboundedStash extends UntypedActor with UnboundedStash /** @@ -57,4 +59,5 @@ abstract class UntypedActorWithUnboundedStash extends UntypedActor with Unbounde * manually, and the mailbox should extend the [[akka.dispatch.DequeBasedMessageQueueSemantics]] marker trait. * See [[akka.actor.UntypedActorWithStash]] for details on how `Stash` works. */ +@deprecated("Use AbstractActor instead of UntypedActor.", since = "2.5.0") abstract class UntypedActorWithUnrestrictedStash extends UntypedActor with UnrestrictedStash diff --git a/akka-actor/src/main/scala/akka/actor/dungeon/Children.scala b/akka-actor/src/main/scala/akka/actor/dungeon/Children.scala index 4cdc142525..b08f4710ce 100644 --- a/akka-actor/src/main/scala/akka/actor/dungeon/Children.scala +++ b/akka-actor/src/main/scala/akka/actor/dungeon/Children.scala @@ -11,6 +11,7 @@ import akka.actor._ import akka.serialization.SerializationExtension import akka.util.{ Unsafe, Helpers } import akka.serialization.SerializerWithStringManifest +import java.util.Optional private[akka] object Children { val GetNobody = () ⇒ Nobody @@ -35,6 +36,7 @@ private[akka] trait Children { this: ActorCell ⇒ case Some(s: ChildRestartStats) ⇒ s.child case _ ⇒ null } + def findChild(name: String): Optional[ActorRef] = Optional.ofNullable(getChild(name)) def actorOf(props: Props): ActorRef = makeChild(this, props, randomName(), async = false, systemService = false) diff --git a/akka-actor/src/main/scala/akka/event/Logging.scala b/akka-actor/src/main/scala/akka/event/Logging.scala index 498882da6b..0514071fd3 100644 --- a/akka-actor/src/main/scala/akka/event/Logging.scala +++ b/akka-actor/src/main/scala/akka/event/Logging.scala @@ -642,6 +642,7 @@ object Logging { * Obtain LoggingAdapter with MDC support for the given actor. * Don't use it outside its specific Actor as it isn't thread safe */ + @deprecated("Use AbstractActor instead of UntypedActor.", since = "2.5.0") def getLogger(logSource: UntypedActor): DiagnosticLoggingAdapter = { val (str, clazz) = LogSource.fromAnyRef(logSource) val system = logSource.getContext().system.asInstanceOf[ExtendedActorSystem] diff --git a/akka-actor/src/main/scala/akka/event/LoggingReceive.scala b/akka-actor/src/main/scala/akka/event/LoggingReceive.scala index e81dfdc61a..55b86b7dbd 100644 --- a/akka-actor/src/main/scala/akka/event/LoggingReceive.scala +++ b/akka-actor/src/main/scala/akka/event/LoggingReceive.scala @@ -9,6 +9,8 @@ import akka.actor.ActorContext import akka.actor.ActorCell import akka.actor.DiagnosticActorLogging import akka.event.Logging.{ LogEvent, LogLevel } +import akka.actor.AbstractActor +import scala.runtime.BoxedUnit object LoggingReceive { @@ -37,10 +39,17 @@ object LoggingReceive { /** * Java API: compatible with lambda expressions - * This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing. */ + @deprecated("Use the create method with `AbstractActor.Receive` parameter instead.", since = "2.5.0") def create(r: Receive, context: ActorContext): Receive = apply(r)(context) + /** + * Java API: compatible with lambda expressions + */ + def create(r: AbstractActor.Receive, context: AbstractActor.ActorContext): AbstractActor.Receive = + new AbstractActor.Receive(apply(r.onMessage.asInstanceOf[PartialFunction[Any, Unit]])(context) + .asInstanceOf[PartialFunction[Any, BoxedUnit]]) + /** * Create a decorated logger which will append `" in state " + label` to each message it logs. */ diff --git a/akka-cluster-sharding/src/test/java/akka/cluster/sharding/ClusterShardingTest.java b/akka-cluster-sharding/src/test/java/akka/cluster/sharding/ClusterShardingTest.java index bf93ee3c3f..69630eec5e 100644 --- a/akka-cluster-sharding/src/test/java/akka/cluster/sharding/ClusterShardingTest.java +++ b/akka-cluster-sharding/src/test/java/akka/cluster/sharding/ClusterShardingTest.java @@ -5,6 +5,7 @@ package akka.cluster.sharding; import static java.util.concurrent.TimeUnit.SECONDS; + import scala.concurrent.duration.Duration; import akka.actor.AbstractActor; @@ -17,13 +18,10 @@ import akka.actor.Props; import akka.actor.SupervisorStrategy; import akka.actor.Terminated; import akka.actor.ReceiveTimeout; -import akka.actor.UntypedActor; -import akka.japi.Procedure; import akka.japi.Option; -import akka.persistence.UntypedPersistentActor; +import akka.persistence.AbstractPersistentActor; import akka.cluster.Cluster; import akka.japi.pf.DeciderBuilder; -import akka.japi.pf.ReceiveBuilder; // Doc code, compile only public class ClusterShardingTest { @@ -96,7 +94,7 @@ public class ClusterShardingTest { } static//#counter-actor - public class Counter extends UntypedPersistentActor { + public class Counter extends AbstractPersistentActor { public static enum CounterOp { INCREMENT, DECREMENT @@ -139,7 +137,7 @@ public class ClusterShardingTest { @Override public void preStart() throws Exception { super.preStart(); - context().setReceiveTimeout(Duration.create(120, SECONDS)); + getContext().setReceiveTimeout(Duration.create(120, SECONDS)); } void updateState(CounterChanged event) { @@ -147,45 +145,45 @@ public class ClusterShardingTest { } @Override - public void onReceiveRecover(Object msg) { - if (msg instanceof CounterChanged) - updateState((CounterChanged) msg); - else - unhandled(msg); + public Receive createReceiveRecover() { + return receiveBuilder() + .match(CounterChanged.class, this::updateState) + .build(); } @Override - public void onReceiveCommand(Object msg) { - if (msg instanceof Get) - getSender().tell(count, getSelf()); - - else if (msg == CounterOp.INCREMENT) - persist(new CounterChanged(+1), new Procedure() { - public void apply(CounterChanged evt) { - updateState(evt); - } - }); - - else if (msg == CounterOp.DECREMENT) - persist(new CounterChanged(-1), new Procedure() { - public void apply(CounterChanged evt) { - updateState(evt); - } - }); - - else if (msg.equals(ReceiveTimeout.getInstance())) - getContext().parent().tell( - new ShardRegion.Passivate(PoisonPill.getInstance()), getSelf()); - - else - unhandled(msg); + public Receive createReceive() { + return receiveBuilder() + .match(Get.class, this::receiveGet) + .matchEquals(CounterOp.INCREMENT, msg -> receiveIncrement()) + .matchEquals(CounterOp.DECREMENT, msg -> receiveDecrement()) + .matchEquals(ReceiveTimeout.getInstance(), msg -> passivate()) + .build(); } + + private void receiveGet(Get msg) { + getSender().tell(count, getSelf()); + } + + private void receiveIncrement() { + persist(new CounterChanged(+1), this::updateState); + } + + private void receiveDecrement() { + persist(new CounterChanged(-1), this::updateState); + } + + private void passivate() { + getContext().parent().tell( + new ShardRegion.Passivate(PoisonPill.getInstance()), getSelf()); + } + } //#counter-actor static//#supervisor - public class CounterSupervisor extends UntypedActor { + public class CounterSupervisor extends AbstractActor { private final ActorRef counter = getContext().actorOf( Props.create(Counter.class), "theCounter"); @@ -203,9 +201,12 @@ public class ClusterShardingTest { } @Override - public void onReceive(Object msg) { - counter.forward(msg, getContext()); + public Receive createReceive() { + return receiveBuilder() + .match(Object.class, msg -> counter.forward(msg, getContext())) + .build(); } + } //#supervisor diff --git a/akka-cluster-tools/src/test/java/akka/cluster/client/ClusterClientTest.java b/akka-cluster-tools/src/test/java/akka/cluster/client/ClusterClientTest.java index dd8e03d3ec..c25891b015 100644 --- a/akka-cluster-tools/src/test/java/akka/cluster/client/ClusterClientTest.java +++ b/akka-cluster-tools/src/test/java/akka/cluster/client/ClusterClientTest.java @@ -58,13 +58,13 @@ public class ClusterClientTest extends JUnitSuite { system.actorOf(Props.create(ReceptionistListener.class, ClusterClientReceptionist.get(system).underlying())); } - static public class Service extends UntypedActor { + static public class Service extends UntypedAbstractActor { public void onReceive(Object msg) { } } //#clientEventsListener - static public class ClientListener extends UntypedActor { + static public class ClientListener extends AbstractActor { private final ActorRef targetClient; private final Set contactPoints = new HashSet<>(); @@ -78,26 +78,28 @@ public class ClusterClientTest extends JUnitSuite { } @Override - public void onReceive(Object message) { - if (message instanceof ContactPoints) { - ContactPoints msg = (ContactPoints)message; - contactPoints.addAll(msg.getContactPoints()); - // Now do something with an up-to-date "contactPoints" - } else if (message instanceof ContactPointAdded) { - ContactPointAdded msg = (ContactPointAdded) message; - contactPoints.add(msg.contactPoint()); - // Now do something with an up-to-date "contactPoints" - } else if (message instanceof ContactPointRemoved) { - ContactPointRemoved msg = (ContactPointRemoved)message; - contactPoints.remove(msg.contactPoint()); - // Now do something with an up-to-date "contactPoints" - } + public Receive createReceive() { + return receiveBuilder() + .match(ContactPoints.class, msg -> { + contactPoints.addAll(msg.getContactPoints()); + // Now do something with an up-to-date "contactPoints" + }) + .match(ContactPointAdded.class, msg -> { + contactPoints.add(msg.contactPoint()); + // Now do something with an up-to-date "contactPoints" + }) + .match(ContactPointRemoved.class, msg -> { + contactPoints.remove(msg.contactPoint()); + // Now do something with an up-to-date "contactPoints" + }) + .build(); } + } //#clientEventsListener //#receptionistEventsListener - static public class ReceptionistListener extends UntypedActor { + static public class ReceptionistListener extends AbstractActor { private final ActorRef targetReceptionist; private final Set clusterClients = new HashSet<>(); @@ -111,21 +113,23 @@ public class ClusterClientTest extends JUnitSuite { } @Override - public void onReceive(Object message) { - if (message instanceof ClusterClients) { - ClusterClients msg = (ClusterClients) message; - clusterClients.addAll(msg.getClusterClients()); - // Now do something with an up-to-date "clusterClients" - } else if (message instanceof ClusterClientUp) { - ClusterClientUp msg = (ClusterClientUp) message; - clusterClients.add(msg.clusterClient()); - // Now do something with an up-to-date "clusterClients" - } else if (message instanceof ClusterClientUnreachable) { - ClusterClientUnreachable msg = (ClusterClientUnreachable) message; - clusterClients.remove(msg.clusterClient()); - // Now do something with an up-to-date "clusterClients" - } + public Receive createReceive() { + return receiveBuilder() + .match(ClusterClients.class, msg -> { + clusterClients.addAll(msg.getClusterClients()); + // Now do something with an up-to-date "clusterClients" + }) + .match(ClusterClientUp.class, msg -> { + clusterClients.add(msg.clusterClient()); + // Now do something with an up-to-date "clusterClients" + }) + .match(ClusterClientUnreachable.class, msg -> { + clusterClients.remove(msg.clusterClient()); + // Now do something with an up-to-date "clusterClients" + }) + .build(); } + } //#receptionistEventsListener diff --git a/akka-cluster-tools/src/test/java/akka/cluster/pubsub/DistributedPubSubMediatorTest.java b/akka-cluster-tools/src/test/java/akka/cluster/pubsub/DistributedPubSubMediatorTest.java index dc1515da4a..5de6397475 100644 --- a/akka-cluster-tools/src/test/java/akka/cluster/pubsub/DistributedPubSubMediatorTest.java +++ b/akka-cluster-tools/src/test/java/akka/cluster/pubsub/DistributedPubSubMediatorTest.java @@ -14,7 +14,7 @@ import org.junit.Test; import akka.actor.ActorRef; import akka.actor.ActorSystem; import akka.actor.Props; -import akka.actor.UntypedActor; +import akka.actor.AbstractActor; import akka.event.Logging; import akka.event.LoggingAdapter; import org.scalatest.junit.JUnitSuite; @@ -65,7 +65,7 @@ public class DistributedPubSubMediatorTest extends JUnitSuite { } static//#subscriber - public class Subscriber extends UntypedActor { + public class Subscriber extends AbstractActor { LoggingAdapter log = Logging.getLogger(getContext().system(), this); public Subscriber() { @@ -76,40 +76,42 @@ public class DistributedPubSubMediatorTest extends JUnitSuite { getSelf()); } - public void onReceive(Object msg) { - if (msg instanceof String) - log.info("Got: {}", msg); - else if (msg instanceof DistributedPubSubMediator.SubscribeAck) - log.info("subscribing"); - else - unhandled(msg); + @Override + public Receive createReceive() { + return receiveBuilder() + .match(String.class, msg -> + log.info("Got: {}", msg)) + .match(DistributedPubSubMediator.SubscribeAck.class, msg -> + log.info("subscribing")) + .build(); } } //#subscriber static//#publisher - public class Publisher extends UntypedActor { + public class Publisher extends AbstractActor { // activate the extension ActorRef mediator = DistributedPubSub.get(getContext().system()).mediator(); - public void onReceive(Object msg) { - if (msg instanceof String) { - String in = (String) msg; - String out = in.toUpperCase(); - mediator.tell(new DistributedPubSubMediator.Publish("content", out), - getSelf()); - } else { - unhandled(msg); + @Override + public Receive createReceive() { + return receiveBuilder() + .match(String.class, in -> { + String out = in.toUpperCase(); + mediator.tell(new DistributedPubSubMediator.Publish("content", out), + getSelf()); + }) + .build(); } - } + } //#publisher static//#send-destination - public class Destination extends UntypedActor { + public class Destination extends AbstractActor { LoggingAdapter log = Logging.getLogger(getContext().system(), this); public Destination() { @@ -119,36 +121,39 @@ public class DistributedPubSubMediatorTest extends JUnitSuite { mediator.tell(new DistributedPubSubMediator.Put(getSelf()), getSelf()); } - public void onReceive(Object msg) { - if (msg instanceof String) - log.info("Got: {}", msg); - else if (msg instanceof DistributedPubSubMediator.SubscribeAck) - log.info("subscribing"); - else - unhandled(msg); + @Override + public Receive createReceive() { + return receiveBuilder() + .match(String.class, msg -> + log.info("Got: {}", msg)) + .match(DistributedPubSubMediator.SubscribeAck.class, msg -> + log.info("subscribing")) + .build(); } + } //#send-destination static//#sender - public class Sender extends UntypedActor { + public class Sender extends AbstractActor { // activate the extension ActorRef mediator = DistributedPubSub.get(getContext().system()).mediator(); - public void onReceive(Object msg) { - if (msg instanceof String) { - String in = (String) msg; - String out = in.toUpperCase(); - boolean localAffinity = true; - mediator.tell(new DistributedPubSubMediator.Send("/user/destination", out, - localAffinity), getSelf()); - } else { - unhandled(msg); - } + @Override + public Receive createReceive() { + return receiveBuilder() + .match(String.class, in -> { + String out = in.toUpperCase(); + boolean localAffinity = true; + mediator.tell(new DistributedPubSubMediator.Send("/user/destination", out, + localAffinity), getSelf()); + }) + .build(); } + } //#sender } diff --git a/akka-docs/rst/additional/osgi.rst b/akka-docs/rst/additional/osgi.rst index 553b3f7f78..a9c4e536fe 100644 --- a/akka-docs/rst/additional/osgi.rst +++ b/akka-docs/rst/additional/osgi.rst @@ -114,8 +114,7 @@ Activator To bootstrap Akka inside an OSGi environment, you can use the ``akka.osgi.ActorSystemActivator`` class to conveniently set up the ActorSystem. -.. includecode:: code/docs/osgi/Activator.scala#Activator - +.. includecode:: ../../../akka-osgi/src/test/scala/docs/osgi/Activator.scala#Activator The goal here is to map the OSGi lifecycle more directly to the Akka lifecycle. The ``ActorSystemActivator`` creates the actor system with a class loader that finds resources (``application.conf`` and ``reference.conf`` files) and classes diff --git a/akka-docs/rst/common/code/docs/circuitbreaker/DangerousJavaActor.java b/akka-docs/rst/common/code/docs/circuitbreaker/DangerousJavaActor.java index a818c0e206..ea84a943e2 100644 --- a/akka-docs/rst/common/code/docs/circuitbreaker/DangerousJavaActor.java +++ b/akka-docs/rst/common/code/docs/circuitbreaker/DangerousJavaActor.java @@ -5,7 +5,7 @@ package docs.circuitbreaker; //#imports1 -import akka.actor.UntypedActor; +import akka.actor.AbstractActor; import scala.concurrent.Future; import akka.event.LoggingAdapter; import scala.concurrent.duration.Duration; @@ -20,7 +20,7 @@ import java.util.concurrent.Callable; //#imports1 //#circuit-breaker-initialization -public class DangerousJavaActor extends UntypedActor { +public class DangerousJavaActor extends AbstractActor { private final CircuitBreaker breaker; private final LoggingAdapter log = Logging.getLogger(getContext().system(), this); @@ -47,22 +47,21 @@ public class DangerousJavaActor extends UntypedActor { } @Override - public void onReceive(Object message) { - if (message instanceof String) { - String m = (String) message; - if ("is my middle name".equals(m)) { + public Receive createReceive() { + return receiveBuilder(). + match(String.class, m -> "is my middle name".equals(m), m -> { pipe( breaker.callWithCircuitBreaker(() -> future(() -> dangerousCall(), getContext().dispatcher()) ), getContext().dispatcher() - ).to(getSender()); - } - if ("block for me".equals(m)) { - getSender().tell(breaker + ).to(sender()); + }) + .match(String.class, m -> "block for me".equals(m), m -> { + sender().tell(breaker .callWithSyncCircuitBreaker( - () -> dangerousCall()), getSelf()); - } - } + () -> dangerousCall()), self()); + }) + .build(); } //#circuit-breaker-usage diff --git a/akka-docs/rst/common/code/docs/circuitbreaker/TellPatternJavaActor.java b/akka-docs/rst/common/code/docs/circuitbreaker/TellPatternJavaActor.java index 253704e21d..3e937702f5 100644 --- a/akka-docs/rst/common/code/docs/circuitbreaker/TellPatternJavaActor.java +++ b/akka-docs/rst/common/code/docs/circuitbreaker/TellPatternJavaActor.java @@ -5,13 +5,14 @@ package docs.circuitbreaker; import akka.actor.ActorRef; import akka.actor.ReceiveTimeout; -import akka.actor.UntypedActor; +import akka.actor.AbstractActor.Receive; +import akka.actor.AbstractActor; import akka.event.Logging; import akka.event.LoggingAdapter; import akka.pattern.CircuitBreaker; import scala.concurrent.duration.Duration; -public class TellPatternJavaActor extends UntypedActor { +public class TellPatternJavaActor extends AbstractActor { private final ActorRef target; private final CircuitBreaker breaker; @@ -35,16 +36,21 @@ public class TellPatternJavaActor extends UntypedActor { //#circuit-breaker-tell-pattern @Override - public void onReceive(Object payload) { - if ( "call".equals(payload) && breaker.isClosed() ) { - target.tell("message", getSelf()); - } else if ( "response".equals(payload) ) { - breaker.succeed(); - } else if ( payload instanceof Throwable ) { - breaker.fail(); - } else if ( payload instanceof ReceiveTimeout ) { - breaker.fail(); - } + public Receive createReceive() { + return receiveBuilder() + .match(String.class, payload -> "call".equals(payload) && breaker.isClosed(), payload -> + target.tell("message", self()) + ) + .matchEquals("response", payload -> + breaker.succeed() + ) + .match(Throwable.class, t -> + breaker.fail() + ) + .match(ReceiveTimeout.class, t -> + breaker.fail() + ) + .build(); } //#circuit-breaker-tell-pattern diff --git a/akka-docs/rst/experimental/index.rst b/akka-docs/rst/experimental/index.rst index 2dd65841d2..80c91693f4 100644 --- a/akka-docs/rst/experimental/index.rst +++ b/akka-docs/rst/experimental/index.rst @@ -20,8 +20,6 @@ prior deprecation. :maxdepth: 1 ../dev/multi-node-testing - ../java/lambda-actors - ../java/lambda-fsm ../scala/typed Another reason for marking a module as experimental is that it's too early diff --git a/akka-docs/rst/general/actors.rst b/akka-docs/rst/general/actors.rst index a304b0dafd..e31feeb17d 100644 --- a/akka-docs/rst/general/actors.rst +++ b/akka-docs/rst/general/actors.rst @@ -8,7 +8,7 @@ hierarchies and are the smallest unit when building an application. This section looks at one such actor in isolation, explaining the concepts you encounter while implementing it. For a more in depth reference with all the details please refer to -:ref:`Actors (Scala) ` and :ref:`Untyped Actors (Java) `. +:ref:`Actors (Scala) ` and :ref:`Actors (Java) `. An actor is a container for `State`_, `Behavior`_, a `Mailbox`_, `Child Actors`_ and a `Supervisor Strategy`_. All of this is encapsulated behind an `Actor diff --git a/akka-docs/rst/intro/what-is-akka.rst b/akka-docs/rst/intro/what-is-akka.rst index 2361b06dcf..cf9d1c4ece 100644 --- a/akka-docs/rst/intro/what-is-akka.rst +++ b/akka-docs/rst/intro/what-is-akka.rst @@ -38,7 +38,7 @@ Actors give you: - Asynchronous, non-blocking and highly performant message-driven programming model. - Very lightweight event-driven processes (several million actors per GB of heap memory). -See the chapter for :ref:`Scala ` or :ref:`Java `. +See the chapter for :ref:`Scala ` or :ref:`Java `. Fault Tolerance --------------- diff --git a/akka-docs/rst/java.rst b/akka-docs/rst/java.rst index af63887894..750bc3e8f5 100644 --- a/akka-docs/rst/java.rst +++ b/akka-docs/rst/java.rst @@ -9,13 +9,13 @@ Java Documentation intro/index-java general/index java/index-actors - java/lambda-index-actors java/index-futures java/index-network java/index-utilities java/stream/index java/http/index java/howto + java/scala-compat experimental/index-java dev/index project/index diff --git a/akka-docs/rst/java/camel.rst b/akka-docs/rst/java/camel.rst index d5f5e6a7cc..e41c173c4a 100644 --- a/akka-docs/rst/java/camel.rst +++ b/akka-docs/rst/java/camel.rst @@ -166,7 +166,7 @@ from localhost on port 8877. After starting the actor, clients can send messages to that actor by POSTing to ``http://localhost:8877/camel/default``. The actor sends a response by using the -getSender().tell method. For returning a message body and headers to the HTTP +sender().tell method. For returning a message body and headers to the HTTP client the response type should be `CamelMessage`_. For any other response type, a new CamelMessage object is created by akka-camel with the actor response as message body. @@ -304,7 +304,7 @@ designed to be asynchronous. This is the case for both, consumer and producer actors. * A consumer endpoint sends request messages to its consumer actor using the ``tell`` - method and the actor returns responses with ``getSender().tell`` once they are + method and the actor returns responses with ``sender().tell`` once they are ready. * A producer actor sends request messages to its endpoint using Camel's diff --git a/akka-docs/rst/java/cluster-sharding.rst b/akka-docs/rst/java/cluster-sharding.rst index fb7db68922..ca932fa34d 100644 --- a/akka-docs/rst/java/cluster-sharding.rst +++ b/akka-docs/rst/java/cluster-sharding.rst @@ -301,7 +301,7 @@ to the ``ShardRegion`` actor to handoff all shards that are hosted by that ``Sha During this period other regions will buffer messages for those shards in the same way as when a rebalance is triggered by the coordinator. When the shards have been stopped the coordinator will allocate these shards elsewhere. -This is performed automatically by the :ref:`coordinated-shutdown-lambda` and is therefore part of the +This is performed automatically by the :ref:`coordinated-shutdown-java` and is therefore part of the graceful leaving process of a cluster member. .. _RemoveInternalClusterShardingData-java: diff --git a/akka-docs/rst/java/cluster-usage.rst b/akka-docs/rst/java/cluster-usage.rst index 2183592f72..f63c0a39f0 100644 --- a/akka-docs/rst/java/cluster-usage.rst +++ b/akka-docs/rst/java/cluster-usage.rst @@ -188,7 +188,7 @@ It can also be performed programmatically with: Note that this command can be issued to any member in the cluster, not necessarily the one that is leaving. -The :ref:`coordinated-shutdown-lambda` will automatically run when the cluster node sees itself as +The :ref:`coordinated-shutdown-java` will automatically run when the cluster node sees itself as ``Exiting``, i.e. leaving from another node will trigger the shutdown process on the leaving node. Tasks for graceful leaving of cluster including graceful shutdown of Cluster Singletons and Cluster Sharding are added automatically when Akka Cluster is used, i.e. running the shutdown @@ -362,7 +362,7 @@ How To Cleanup when Member is Removed You can do some clean up in a ``registerOnMemberRemoved`` callback, which will be invoked when the current member status is changed to 'Removed' or the cluster have been shutdown. -An alternative is to register tasks to the :ref:`coordinated-shutdown-lambda`. +An alternative is to register tasks to the :ref:`coordinated-shutdown-java`. .. note:: Register a OnMemberRemoved callback on a cluster that have been shutdown, the callback will be invoked immediately on @@ -532,7 +532,7 @@ That is not done by the router. The configuration for a group looks like this: The actor paths without address information that are defined in ``routees.paths`` are used for selecting the actors to which the messages will be forwarded to by the router. -Messages will be forwarded to the routees using :ref:`ActorSelection `, so the same delivery semantics should be expected. +Messages will be forwarded to the routees using :ref:`ActorSelection `, so the same delivery semantics should be expected. It is possible to limit the lookup of routees to member nodes tagged with a certain role by specifying ``use-role``. ``max-total-nr-of-instances`` defines total number of routees in the cluster. By default ``max-total-nr-of-instances`` diff --git a/akka-docs/rst/java/code/docs/actor/ActorDocTest.java b/akka-docs/rst/java/code/docs/actor/ActorDocTest.java deleted file mode 100644 index 3215957a67..0000000000 --- a/akka-docs/rst/java/code/docs/actor/ActorDocTest.java +++ /dev/null @@ -1,639 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ - -package docs.actor; - -import akka.actor.*; -import akka.japi.pf.ReceiveBuilder; -import akka.testkit.ErrorFilter; -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; -import static akka.japi.Util.immutableSeq; - -import java.util.concurrent.TimeUnit; - -import akka.testkit.JavaTestKit; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import static org.junit.Assert.*; - -//#import-props -import akka.actor.Props; -//#import-props -//#import-actorRef -import akka.actor.ActorRef; -import akka.actor.ActorSystem; -//#import-actorRef -//#import-identify -import akka.actor.ActorIdentity; -import akka.actor.ActorSelection; -import akka.actor.Identify; -//#import-identify -//#import-graceFulStop -import akka.pattern.AskTimeoutException; -import scala.concurrent.Await; -import scala.concurrent.duration.Duration; -import scala.concurrent.Future; -import static akka.pattern.Patterns.gracefulStop; -//#import-graceFulStop - -public class ActorDocTest extends AbstractJavaTest { - - public static Config config = ConfigFactory.parseString( - "akka {\n" + - " loggers = [\"akka.testkit.TestEventListener\"]\n" + - " loglevel = \"WARNING\"\n" + - " stdout-loglevel = \"WARNING\"\n" + - "}\n" - ); - - static ActorSystem system = null; - - @BeforeClass - public static void beforeClass() { - system = ActorSystem.create("ActorDocTest", config); - } - - @AfterClass - public static void afterClass() throws Exception { - Await.result(system.terminate(), Duration.create("5 seconds")); - } - - static - //#context-actorOf - public class FirstActor extends AbstractActor { - final ActorRef child = context().actorOf(Props.create(MyActor.class), "myChild"); - //#plus-some-behavior - public FirstActor() { - receive(ReceiveBuilder. - matchAny(x -> { - sender().tell(x, self()); - }).build() - ); - } - //#plus-some-behavior - } - //#context-actorOf - - static public abstract class SomeActor extends AbstractActor { - //#receive-constructor - public SomeActor() { - receive(ReceiveBuilder. - //#and-some-behavior - match(String.class, s -> System.out.println(s.toLowerCase())). - //#and-some-behavior - build()); - } - //#receive-constructor - @Override - //#receive - public abstract PartialFunction receive(); - //#receive - } - - static public class ActorWithArgs extends AbstractActor { - private final String args; - - ActorWithArgs(String args) { - this.args = args; - receive(ReceiveBuilder. - matchAny(x -> { }).build() - ); - } - } - - static - //#props-factory - public class DemoActor extends AbstractActor { - /** - * Create Props for an actor of this type. - * @param magicNumber The magic number to be passed to this actor’s constructor. - * @return a Props for creating this actor, which can then be further configured - * (e.g. calling `.withDispatcher()` on it) - */ - static Props props(Integer magicNumber) { - // You need to specify the actual type of the returned actor - // since Java 8 lambdas have some runtime type information erased - return Props.create(DemoActor.class, () -> new DemoActor(magicNumber)); - } - - private final Integer magicNumber; - - DemoActor(Integer magicNumber) { - this.magicNumber = magicNumber; - receive(ReceiveBuilder. - match(Integer.class, i -> { - sender().tell(i + magicNumber, self()); - }).build() - ); - } - } - - //#props-factory - static - //#props-factory - public class SomeOtherActor extends AbstractActor { - // Props(new DemoActor(42)) would not be safe - ActorRef demoActor = context().actorOf(DemoActor.props(42), "demo"); - // ... - //#props-factory - public SomeOtherActor() { - receive(emptyBehavior()); - } - //#props-factory - } - //#props-factory - - public static class Hook extends AbstractActor { - ActorRef target = null; - public Hook() { - receive(emptyBehavior()); - } - //#preStart - @Override - public void preStart() { - target = context().actorOf(Props.create(MyActor.class, "target")); - } - //#preStart - //#postStop - @Override - public void postStop() { - //#clean-up-some-resources - final String message = "stopped"; - //#tell - // don’t forget to think about who is the sender (2nd argument) - target.tell(message, self()); - //#tell - final Object result = ""; - //#forward - target.forward(result, context()); - //#forward - target = null; - //#clean-up-some-resources - } - //#postStop - - // compilation test only - public void compileSelections() { - //#selection-local - // will look up this absolute path - context().actorSelection("/user/serviceA/actor"); - // will look up sibling beneath same supervisor - context().actorSelection("../joe"); - //#selection-local - - //#selection-wildcard - // will look all children to serviceB with names starting with worker - context().actorSelection("/user/serviceB/worker*"); - // will look up all siblings beneath same supervisor - context().actorSelection("../*"); - //#selection-wildcard - - //#selection-remote - context().actorSelection("akka.tcp://app@otherhost:1234/user/serviceB"); - //#selection-remote - } - } - - public static class ReplyException extends AbstractActor { - public ReplyException() { - receive(ReceiveBuilder. - matchAny(x -> { - //#reply-exception - try { - String result = operation(); - sender().tell(result, self()); - } catch (Exception e) { - sender().tell(new akka.actor.Status.Failure(e), self()); - throw e; - } - //#reply-exception - }).build() - ); - } - - private String operation() { - return "Hi"; - } - } - - static - //#gracefulStop-actor - public class Manager extends AbstractActor { - private static enum Shutdown { - Shutdown - } - public static final Shutdown SHUTDOWN = Shutdown.Shutdown; - - private ActorRef worker = - context().watch(context().actorOf(Props.create(Cruncher.class), "worker")); - - public Manager() { - receive(ReceiveBuilder. - matchEquals("job", s -> { - worker.tell("crunch", self()); - }). - matchEquals(SHUTDOWN, x -> { - worker.tell(PoisonPill.getInstance(), self()); - context().become(shuttingDown); - }).build() - ); - } - - public PartialFunction shuttingDown = - ReceiveBuilder. - matchEquals("job", s -> { - sender().tell("service unavailable, shutting down", self()); - }). - match(Terminated.class, t -> t.actor().equals(worker), t -> { - context().stop(self()); - }).build(); - } - //#gracefulStop-actor - - @Test - public void usePatternsGracefulStop() throws Exception { - ActorRef actorRef = system.actorOf(Props.create(Manager.class)); - //#gracefulStop - try { - Future stopped = - gracefulStop(actorRef, Duration.create(5, TimeUnit.SECONDS), Manager.SHUTDOWN); - Await.result(stopped, Duration.create(6, TimeUnit.SECONDS)); - // the actor has been stopped - } catch (AskTimeoutException e) { - // the actor wasn't stopped within 5 seconds - } - //#gracefulStop - } - - - public static class Cruncher extends AbstractActor { - public Cruncher() { - receive(ReceiveBuilder. - matchEquals("crunch", s -> { }).build() - ); - } - } - - static - //#swapper - public class Swapper extends AbstractLoggingActor { - public Swapper() { - receive(ReceiveBuilder. - matchEquals(Swap, s -> { - log().info("Hi"); - context().become(ReceiveBuilder. - matchEquals(Swap, x -> { - log().info("Ho"); - context().unbecome(); // resets the latest 'become' (just for fun) - }).build(), false); // push on top instead of replace - }).build() - ); - } - } - - //#swapper - static - //#swapper - public class SwapperApp { - public static void main(String[] args) { - ActorSystem system = ActorSystem.create("SwapperSystem"); - ActorRef swapper = system.actorOf(Props.create(Swapper.class), "swapper"); - swapper.tell(Swap, ActorRef.noSender()); // logs Hi - swapper.tell(Swap, ActorRef.noSender()); // logs Ho - swapper.tell(Swap, ActorRef.noSender()); // logs Hi - swapper.tell(Swap, ActorRef.noSender()); // logs Ho - swapper.tell(Swap, ActorRef.noSender()); // logs Hi - swapper.tell(Swap, ActorRef.noSender()); // logs Ho - system.terminate(); - } - } - //#swapper - - - @Test - public void creatingActorWithSystemActorOf() { - //#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(FirstActor.class), "myactor"); - //#system-actorOf - try { - new JavaTestKit(system) { - { - myActor.tell("hello", getRef()); - expectMsgEquals("hello"); - } - }; - } finally { - JavaTestKit.shutdownActorSystem(system); - } - } - - @Test - public void creatingPropsConfig() { - //#creating-props - Props props1 = Props.create(MyActor.class); - Props props2 = Props.create(ActorWithArgs.class, - () -> new ActorWithArgs("arg")); // careful, see below - Props props3 = Props.create(ActorWithArgs.class, "arg"); - //#creating-props - - //#creating-props-deprecated - // NOT RECOMMENDED within another actor: - // encourages to close over enclosing class - Props props7 = Props.create(ActorWithArgs.class, - () -> new ActorWithArgs("arg")); - //#creating-props-deprecated - } - - @Test(expected=IllegalArgumentException.class) - public void creatingPropsIllegal() { - //#creating-props-illegal - // This will throw an IllegalArgumentException since some runtime - // type information of the lambda is erased. - // Use Props.create(actorClass, Creator) instead. - Props props = Props.create(() -> new ActorWithArgs("arg")); - //#creating-props-illegal - } - - static - //#receive-timeout - public class ReceiveTimeoutActor extends AbstractActor { - //#receive-timeout - ActorRef target = context().system().deadLetters(); - //#receive-timeout - public ReceiveTimeoutActor() { - // To set an initial delay - context().setReceiveTimeout(Duration.create("10 seconds")); - - receive(ReceiveBuilder. - matchEquals("Hello", s -> { - // To set in a response to a message - context().setReceiveTimeout(Duration.create("1 second")); - //#receive-timeout - target = sender(); - target.tell("Hello world", self()); - //#receive-timeout - }). - match(ReceiveTimeout.class, r -> { - // To turn it off - context().setReceiveTimeout(Duration.Undefined()); - //#receive-timeout - target.tell("timeout", self()); - //#receive-timeout - }).build() - ); - } - } - //#receive-timeout - - @Test - public void using_receiveTimeout() { - final ActorRef myActor = system.actorOf(Props.create(ReceiveTimeoutActor.class)); - new JavaTestKit(system) { - { - myActor.tell("Hello", getRef()); - expectMsgEquals("Hello world"); - expectMsgEquals("timeout"); - } - }; - } - - static - //#hot-swap-actor - public class HotSwapActor extends AbstractActor { - private PartialFunction angry; - private PartialFunction happy; - - public HotSwapActor() { - angry = - ReceiveBuilder. - matchEquals("foo", s -> { - sender().tell("I am already angry?", self()); - }). - matchEquals("bar", s -> { - context().become(happy); - }).build(); - - happy = ReceiveBuilder. - matchEquals("bar", s -> { - sender().tell("I am already happy :-)", self()); - }). - matchEquals("foo", s -> { - context().become(angry); - }).build(); - - receive(ReceiveBuilder. - matchEquals("foo", s -> { - context().become(angry); - }). - matchEquals("bar", s -> { - context().become(happy); - }).build() - ); - } - } - //#hot-swap-actor - - @Test - public void using_hot_swap() { - final ActorRef actor = system.actorOf(Props.create(HotSwapActor.class), "hot"); - new JavaTestKit(system) { - { - actor.tell("foo", getRef()); - actor.tell("foo", getRef()); - expectMsgEquals("I am already angry?"); - actor.tell("bar", getRef()); - actor.tell("bar", getRef()); - expectMsgEquals("I am already happy :-)"); - actor.tell("foo", getRef()); - actor.tell("foo", getRef()); - expectMsgEquals("I am already angry?"); - expectNoMsg(Duration.create(1, TimeUnit.SECONDS)); - } - }; - } - - - static - //#stash - public class ActorWithProtocol extends AbstractActorWithStash { - public ActorWithProtocol() { - receive(ReceiveBuilder. - matchEquals("open", s -> { - context().become(ReceiveBuilder. - matchEquals("write", ws -> { /* do writing */ }). - matchEquals("close", cs -> { - unstashAll(); - context().unbecome(); - }). - matchAny(msg -> stash()).build(), false); - }). - matchAny(msg -> stash()).build() - ); - } - } - //#stash - - @Test - public void using_Stash() { - final ActorRef actor = system.actorOf(Props.create(ActorWithProtocol.class), "stash"); - } - - static - //#watch - public class WatchActor extends AbstractActor { - private final ActorRef child = context().actorOf(Props.empty(), "target"); - private ActorRef lastSender = system.deadLetters(); - - public WatchActor() { - context().watch(child); // <-- this is the only call needed for registration - - receive(ReceiveBuilder. - matchEquals("kill", s -> { - context().stop(child); - lastSender = sender(); - }). - match(Terminated.class, t -> t.actor().equals(child), t -> { - lastSender.tell("finished", self()); - }).build() - ); - } - } - //#watch - - @Test - public void using_watch() { - ActorRef actor = system.actorOf(Props.create(WatchActor.class)); - - new JavaTestKit(system) { - { - actor.tell("kill", getRef()); - expectMsgEquals("finished"); - } - }; - } - - static - //#identify - public class Follower extends AbstractActor { - final Integer identifyId = 1; - - public Follower(){ - ActorSelection selection = context().actorSelection("/user/another"); - selection.tell(new Identify(identifyId), self()); - - receive(ReceiveBuilder. - match(ActorIdentity.class, id -> id.getRef() != null, id -> { - ActorRef ref = id.getRef(); - context().watch(ref); - context().become(active(ref)); - }). - match(ActorIdentity.class, id -> id.getRef() == null, id -> { - context().stop(self()); - }).build() - ); - } - - final PartialFunction active(final ActorRef another) { - return ReceiveBuilder. - match(Terminated.class, t -> t.actor().equals(another), t -> { - context().stop(self()); - }).build(); - } - } - //#identify - - @Test - public void using_Identify() { - ActorRef a = system.actorOf(Props.empty()); - ActorRef b = system.actorOf(Props.create(Follower.class)); - - new JavaTestKit(system) { - { - watch(b); - system.stop(a); - assertEquals(expectMsgClass(Duration.create(2, TimeUnit.SECONDS), Terminated.class).actor(), b); - } - }; - } - - public static class NoReceiveActor extends AbstractActor { - } - - @Test - public void noReceiveActor() { - EventFilter ex1 = new ErrorFilter(ActorInitializationException.class); - EventFilter[] ignoreExceptions = { ex1 }; - try { - system.eventStream().publish(new TestEvent.Mute(immutableSeq(ignoreExceptions))); - new JavaTestKit(system) {{ - final ActorRef victim = new EventFilter(ActorInitializationException.class) { - protected ActorRef run() { - return system.actorOf(Props.create(NoReceiveActor.class), "victim"); - } - }.message("Actor behavior has not been set with receive(...)").occurrences(1).exec(); - - assertEquals(true, victim.isTerminated()); - }}; - } finally { - system.eventStream().publish(new TestEvent.UnMute(immutableSeq(ignoreExceptions))); - } - } - - public static class MultipleReceiveActor extends AbstractActor { - public MultipleReceiveActor() { - receive(ReceiveBuilder. - match(String.class, s1 -> s1.toLowerCase().equals("become"), s1 -> { - sender().tell(s1.toUpperCase(), self()); - receive(ReceiveBuilder. - match(String.class, s2 -> { - sender().tell(s2.toLowerCase(), self()); - }).build() - ); - }). - match(String.class, s1 -> { - sender().tell(s1.toUpperCase(), self()); - }).build() - ); - } - } - - @Test - public void multipleReceiveActor() { - EventFilter ex1 = new ErrorFilter(IllegalActorStateException.class); - EventFilter[] ignoreExceptions = { ex1 }; - try { - system.eventStream().publish(new TestEvent.Mute(immutableSeq(ignoreExceptions))); - new JavaTestKit(system) {{ - new EventFilter(IllegalActorStateException.class) { - protected Boolean run() { - ActorRef victim = system.actorOf(Props.create(MultipleReceiveActor.class), "victim2"); - victim.tell("Foo", getRef()); - expectMsgEquals("FOO"); - victim.tell("bEcoMe", getRef()); - expectMsgEquals("BECOME"); - victim.tell("Foo", getRef()); - // if it's upper case, then the actor was restarted - expectMsgEquals("FOO"); - return true; - } - }.message("Actor behavior has already been set with receive(...), " + - "use context().become(...) to change it later").occurrences(1).exec(); - }}; - } finally { - system.eventStream().publish(new TestEvent.UnMute(immutableSeq(ignoreExceptions))); - } - } - -} diff --git a/akka-docs/rst/java/code/docs/actor/FSMDocTest.java b/akka-docs/rst/java/code/docs/actor/FSMDocTest.java deleted file mode 100644 index d07d048770..0000000000 --- a/akka-docs/rst/java/code/docs/actor/FSMDocTest.java +++ /dev/null @@ -1,209 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ -package docs.actor; - -//#imports-data -import java.util.ArrayList; -import java.util.List; -import akka.actor.ActorRef; -//#imports-data - -//#imports-actor -import akka.event.LoggingAdapter; -import akka.event.Logging; -import akka.actor.UntypedActor; -//#imports-actor - -import akka.actor.ActorSystem; -import akka.actor.Props; -import akka.testkit.JavaTestKit; -import akka.testkit.TestProbe; -import akka.testkit.AkkaSpec; -import docs.AbstractJavaTest; - -public class FSMDocTest extends AbstractJavaTest { - - static - //#data - public final class SetTarget { - final ActorRef ref; - - public SetTarget(ActorRef ref) { - this.ref = ref; - } - } - - //#data - static - //#data - public final class Queue { - final Object o; - - public Queue(Object o) { - this.o = o; - } - } - - //#data - static - //#data - public final Object flush = new Object(); - - //#data - static - //#data - public final class Batch { - final List objects; - - public Batch(List objects) { - this.objects = objects; - } - } - - //#data - - static - //#base - public abstract class MyFSMBase extends UntypedActor { - - /* - * This is the mutable state of this state machine. - */ - protected enum State { - IDLE, ACTIVE; - } - - private State state = State.IDLE; - private ActorRef target; - private List queue; - - /* - * Then come all the mutator methods: - */ - protected void init(ActorRef target) { - this.target = target; - queue = new ArrayList(); - } - - protected void setState(State s) { - if (state != s) { - transition(state, s); - state = s; - } - } - - protected void enqueue(Object o) { - if (queue != null) - queue.add(o); - } - - protected List drainQueue() { - final List q = queue; - if (q == null) - throw new IllegalStateException("drainQueue(): not yet initialized"); - queue = new ArrayList(); - return q; - } - - /* - * Here are the interrogation methods: - */ - protected boolean isInitialized() { - return target != null; - } - - protected State getState() { - return state; - } - - protected ActorRef getTarget() { - if (target == null) - throw new IllegalStateException("getTarget(): not yet initialized"); - return target; - } - - /* - * And finally the callbacks (only one in this example: react to state change) - */ - abstract protected void transition(State old, State next); - } - - //#base - - static - //#actor - public class MyFSM extends MyFSMBase { - - private final LoggingAdapter log = - Logging.getLogger(getContext().system(), this); - - @Override - public void onReceive(Object o) { - - if (getState() == State.IDLE) { - - if (o instanceof SetTarget) - init(((SetTarget) o).ref); - - else - whenUnhandled(o); - - } else if (getState() == State.ACTIVE) { - - if (o == flush) - setState(State.IDLE); - - else - whenUnhandled(o); - } - } - - @Override - public void transition(State old, State next) { - if (old == State.ACTIVE) { - getTarget().tell(new Batch(drainQueue()), getSelf()); - } - } - - private void whenUnhandled(Object o) { - if (o instanceof Queue && isInitialized()) { - enqueue(((Queue) o).o); - setState(State.ACTIVE); - - } else { - log.warning("received unknown message {} in state {}", o, getState()); - } - } - } - - //#actor - - ActorSystem system; - - @org.junit.Before - public void setUp() { - system = ActorSystem.create("FSMSystem", AkkaSpec.testConf()); - } - - @org.junit.Test - public void mustBunch() { - final ActorRef buncher = system.actorOf(Props.create(MyFSM.class)); - final TestProbe probe = new TestProbe(system); - buncher.tell(new SetTarget(probe.ref()), ActorRef.noSender()); - buncher.tell(new Queue(1), ActorRef.noSender()); - buncher.tell(new Queue(2), ActorRef.noSender()); - buncher.tell(flush, ActorRef.noSender()); - buncher.tell(new Queue(3), ActorRef.noSender()); - final Batch b = probe.expectMsgClass(Batch.class); - assert b.objects.size() == 2; - assert b.objects.contains(1); - assert b.objects.contains(2); - } - - @org.junit.After - public void cleanup() { - JavaTestKit.shutdownActorSystem(system); - } - -} diff --git a/akka-docs/rst/java/code/docs/actor/FaultHandlingTest.java b/akka-docs/rst/java/code/docs/actor/FaultHandlingTest.java deleted file mode 100644 index 8f4f966881..0000000000 --- a/akka-docs/rst/java/code/docs/actor/FaultHandlingTest.java +++ /dev/null @@ -1,224 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ -package docs.actor; - -//#testkit -import akka.actor.ActorRef; -import akka.actor.ActorSystem; -import akka.actor.SupervisorStrategy; -import static akka.actor.SupervisorStrategy.resume; -import static akka.actor.SupervisorStrategy.restart; -import static akka.actor.SupervisorStrategy.stop; -import static akka.actor.SupervisorStrategy.escalate; -import akka.actor.SupervisorStrategy.Directive; -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; -import scala.concurrent.duration.Duration; -import akka.testkit.TestProbe; - -//#testkit -import akka.testkit.ErrorFilter; -import akka.testkit.EventFilter; -import akka.testkit.TestEvent; -import akka.testkit.JavaTestKit; -import static java.util.concurrent.TimeUnit.SECONDS; -import static akka.japi.Util.immutableSeq; -import akka.japi.Function; -import scala.Option; - -import org.junit.Test; -import org.junit.BeforeClass; -import org.junit.AfterClass; - -//#testkit -public class FaultHandlingTest extends AbstractJavaTest { - //#testkit - static - //#supervisor - public class Supervisor extends UntypedActor { - - //#strategy - private static SupervisorStrategy strategy = - new OneForOneStrategy(10, Duration.create("1 minute"), - new Function() { - @Override - public Directive apply(Throwable t) { - if (t instanceof ArithmeticException) { - return resume(); - } else if (t instanceof NullPointerException) { - return restart(); - } else if (t instanceof IllegalArgumentException) { - return stop(); - } else { - return escalate(); - } - } - }); - - @Override - public SupervisorStrategy supervisorStrategy() { - return strategy; - } - - //#strategy - - public void onReceive(Object o) { - if (o instanceof Props) { - getSender().tell(getContext().actorOf((Props) o), getSelf()); - } else { - unhandled(o); - } - } - } - - //#supervisor - - static - //#supervisor2 - public class Supervisor2 extends UntypedActor { - - //#strategy2 - private static SupervisorStrategy strategy = new OneForOneStrategy(10, - Duration.create("1 minute"), - new Function() { - @Override - public Directive apply(Throwable t) { - if (t instanceof ArithmeticException) { - return resume(); - } else if (t instanceof NullPointerException) { - return restart(); - } else if (t instanceof IllegalArgumentException) { - return stop(); - } else { - return escalate(); - } - } - }); - - @Override - public SupervisorStrategy supervisorStrategy() { - return strategy; - } - - //#strategy2 - - public void onReceive(Object o) { - if (o instanceof Props) { - getSender().tell(getContext().actorOf((Props) o), getSelf()); - } else { - unhandled(o); - } - } - - @Override - public void preRestart(Throwable cause, Option msg) { - // do not kill all children, which is the default here - } - } - - //#supervisor2 - - static - //#child - public class Child extends UntypedActor { - int state = 0; - - public void onReceive(Object o) throws Exception { - if (o instanceof Exception) { - throw (Exception) o; - } else if (o instanceof Integer) { - state = (Integer) o; - } else if (o.equals("get")) { - getSender().tell(state, getSelf()); - } else { - unhandled(o); - } - } - } - - //#child - - //#testkit - static ActorSystem system; - Duration timeout = Duration.create(5, SECONDS); - - @BeforeClass - public static void start() { - system = ActorSystem.create("FaultHandlingTest"); - } - - @AfterClass - public static void cleanup() { - JavaTestKit.shutdownActorSystem(system); - system = null; - } - - @Test - public void mustEmploySupervisorStrategy() throws Exception { - // code here - //#testkit - EventFilter ex1 = new ErrorFilter(ArithmeticException.class); - EventFilter ex2 = new ErrorFilter(NullPointerException.class); - EventFilter ex3 = new ErrorFilter(IllegalArgumentException.class); - EventFilter ex4 = new ErrorFilter(Exception.class); - EventFilter[] ignoreExceptions = { ex1, ex2, ex3, ex4 }; - Seq seq = immutableSeq(ignoreExceptions); - system.eventStream().publish(new TestEvent.Mute(seq)); - - //#create - Props superprops = Props.create(Supervisor.class); - ActorRef supervisor = system.actorOf(superprops, "supervisor"); - ActorRef child = (ActorRef) Await.result(ask(supervisor, - Props.create(Child.class), 5000), timeout); - //#create - - //#resume - child.tell(42, ActorRef.noSender()); - assert Await.result(ask(child, "get", 5000), timeout).equals(42); - child.tell(new ArithmeticException(), ActorRef.noSender()); - assert Await.result(ask(child, "get", 5000), timeout).equals(42); - //#resume - - //#restart - child.tell(new NullPointerException(), ActorRef.noSender()); - assert Await.result(ask(child, "get", 5000), timeout).equals(0); - //#restart - - //#stop - final TestProbe probe = new TestProbe(system); - probe.watch(child); - child.tell(new IllegalArgumentException(), ActorRef.noSender()); - probe.expectMsgClass(Terminated.class); - //#stop - - //#escalate-kill - child = (ActorRef) Await.result(ask(supervisor, - Props.create(Child.class), 5000), timeout); - probe.watch(child); - assert Await.result(ask(child, "get", 5000), timeout).equals(0); - child.tell(new Exception(), ActorRef.noSender()); - probe.expectMsgClass(Terminated.class); - //#escalate-kill - - //#escalate-restart - superprops = Props.create(Supervisor2.class); - supervisor = system.actorOf(superprops); - child = (ActorRef) Await.result(ask(supervisor, - Props.create(Child.class), 5000), timeout); - child.tell(23, ActorRef.noSender()); - assert Await.result(ask(child, "get", 5000), timeout).equals(23); - child.tell(new Exception(), ActorRef.noSender()); - assert Await.result(ask(child, "get", 5000), timeout).equals(0); - //#escalate-restart - //#testkit - } - -} -//#testkit diff --git a/akka-docs/rst/java/code/docs/actor/FaultHandlingTestJava8.java b/akka-docs/rst/java/code/docs/actor/FaultHandlingTestJava8.java deleted file mode 100644 index 8b56211e8d..0000000000 --- a/akka-docs/rst/java/code/docs/actor/FaultHandlingTestJava8.java +++ /dev/null @@ -1,205 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ -package docs.actor; - -//#testkit -import akka.actor.*; - -import static akka.actor.SupervisorStrategy.resume; -import static akka.actor.SupervisorStrategy.restart; -import static akka.actor.SupervisorStrategy.stop; -import static akka.actor.SupervisorStrategy.escalate; -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; -import scala.concurrent.duration.Duration; -import akka.testkit.TestProbe; - -//#testkit -import akka.testkit.ErrorFilter; -import akka.testkit.EventFilter; -import akka.testkit.TestEvent; -import akka.testkit.JavaTestKit; -import static java.util.concurrent.TimeUnit.SECONDS; -import static akka.japi.Util.immutableSeq; -import scala.Option; - -import org.junit.Test; -import org.junit.BeforeClass; -import org.junit.AfterClass; -import scala.runtime.BoxedUnit; - -//#testkit -public class FaultHandlingTestJava8 extends JUnitSuite { -//#testkit - - public static Config config = ConfigFactory.parseString( - "akka {\n" + - " loggers = [\"akka.testkit.TestEventListener\"]\n" + - " loglevel = \"WARNING\"\n" + - " stdout-loglevel = \"WARNING\"\n" + - "}\n"); - - static - //#supervisor - public class Supervisor extends AbstractActor { - - //#strategy - private static SupervisorStrategy strategy = - new OneForOneStrategy(10, Duration.create("1 minute"), DeciderBuilder. - match(ArithmeticException.class, e -> resume()). - match(NullPointerException.class, e -> restart()). - match(IllegalArgumentException.class, e -> stop()). - matchAny(o -> escalate()).build()); - - @Override - public SupervisorStrategy supervisorStrategy() { - return strategy; - } - - //#strategy - - public Supervisor() { - receive(ReceiveBuilder. - match(Props.class, props -> { - sender().tell(context().actorOf(props), self()); - }).build() - ); - } - } - - //#supervisor - - static - //#supervisor2 - public class Supervisor2 extends AbstractActor { - - //#strategy2 - private static SupervisorStrategy strategy = - new OneForOneStrategy(10, Duration.create("1 minute"), DeciderBuilder. - match(ArithmeticException.class, e -> resume()). - match(NullPointerException.class, e -> restart()). - match(IllegalArgumentException.class, e -> stop()). - matchAny(o -> escalate()).build()); - - @Override - public SupervisorStrategy supervisorStrategy() { - return strategy; - } - - //#strategy2 - - public Supervisor2() { - receive(ReceiveBuilder. - match(Props.class, props -> { - sender().tell(context().actorOf(props), self()); - }).build() - ); - } - - @Override - public void preRestart(Throwable cause, Option msg) { - // do not kill all children, which is the default here - } - } - - //#supervisor2 - - static - //#child - public class Child extends AbstractActor { - int state = 0; - - public Child() { - receive(ReceiveBuilder. - match(Exception.class, exception -> { throw exception; }). - match(Integer.class, i -> state = i). - matchEquals("get", s -> sender().tell(state, self())).build() - ); - } - } - - //#child - - //#testkit - static ActorSystem system; - Duration timeout = Duration.create(5, SECONDS); - - @BeforeClass - public static void start() { - system = ActorSystem.create("FaultHandlingTest", config); - } - - @AfterClass - public static void cleanup() { - JavaTestKit.shutdownActorSystem(system); - system = null; - } - - @Test - public void mustEmploySupervisorStrategy() throws Exception { - // code here - //#testkit - EventFilter ex1 = new ErrorFilter(ArithmeticException.class); - EventFilter ex2 = new ErrorFilter(NullPointerException.class); - EventFilter ex3 = new ErrorFilter(IllegalArgumentException.class); - EventFilter ex4 = new ErrorFilter(Exception.class); - EventFilter[] ignoreExceptions = { ex1, ex2, ex3, ex4 }; - system.eventStream().publish(new TestEvent.Mute(immutableSeq(ignoreExceptions))); - - //#create - Props superprops = Props.create(Supervisor.class); - ActorRef supervisor = system.actorOf(superprops, "supervisor"); - ActorRef child = (ActorRef) Await.result(ask(supervisor, - Props.create(Child.class), 5000), timeout); - //#create - - //#resume - child.tell(42, ActorRef.noSender()); - assert Await.result(ask(child, "get", 5000), timeout).equals(42); - child.tell(new ArithmeticException(), ActorRef.noSender()); - assert Await.result(ask(child, "get", 5000), timeout).equals(42); - //#resume - - //#restart - child.tell(new NullPointerException(), ActorRef.noSender()); - assert Await.result(ask(child, "get", 5000), timeout).equals(0); - //#restart - - //#stop - final TestProbe probe = new TestProbe(system); - probe.watch(child); - child.tell(new IllegalArgumentException(), ActorRef.noSender()); - probe.expectMsgClass(Terminated.class); - //#stop - - //#escalate-kill - child = (ActorRef) Await.result(ask(supervisor, - Props.create(Child.class), 5000), timeout); - probe.watch(child); - assert Await.result(ask(child, "get", 5000), timeout).equals(0); - child.tell(new Exception(), ActorRef.noSender()); - probe.expectMsgClass(Terminated.class); - //#escalate-kill - - //#escalate-restart - superprops = Props.create(Supervisor2.class); - supervisor = system.actorOf(superprops); - child = (ActorRef) Await.result(ask(supervisor, - Props.create(Child.class), 5000), timeout); - child.tell(23, ActorRef.noSender()); - assert Await.result(ask(child, "get", 5000), timeout).equals(23); - child.tell(new Exception(), ActorRef.noSender()); - assert Await.result(ask(child, "get", 5000), timeout).equals(0); - //#escalate-restart - //#testkit - } - -} -//#testkit diff --git a/akka-docs/rst/java/code/docs/actor/FirstUntypedActor.java b/akka-docs/rst/java/code/docs/actor/FirstUntypedActor.java deleted file mode 100644 index 5cbe436b0c..0000000000 --- a/akka-docs/rst/java/code/docs/actor/FirstUntypedActor.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ -package docs.actor; - -import akka.actor.ActorRef; -import akka.actor.Props; -import akka.actor.PoisonPill; -import akka.actor.UntypedActor; - -//#context-actorOf -public class FirstUntypedActor extends UntypedActor { - ActorRef myActor = getContext().actorOf(Props.create(MyActor.class), "myactor"); - - //#context-actorOf - - public void onReceive(Object message) { - myActor.forward(message, getContext()); - myActor.tell(PoisonPill.getInstance(), ActorRef.noSender()); - } -} diff --git a/akka-docs/rst/java/code/docs/actor/InitializationDocSpecJava.java b/akka-docs/rst/java/code/docs/actor/InitializationDocSpecJava.java deleted file mode 100644 index 4238a4c4cc..0000000000 --- a/akka-docs/rst/java/code/docs/actor/InitializationDocSpecJava.java +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ -package docs.actor; - -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 extends AbstractJavaTest { - - static public class PreStartInitExample extends UntypedActor { - - public void onReceive(Object message) throws Exception {} - - //#preStartInit - @Override - public void preStart() { - // Initialize children here - } - - // Overriding postRestart to disable the call to preStart() - // after restarts - @Override - public void postRestart(Throwable reason) { - } - - // The default implementation of preRestart() stops all the children - // of the actor. To opt-out from stopping the children, we - // have to override preRestart() - @Override - public void preRestart(Throwable reason, Option message) - throws Exception { - // Keep the call to postStop(), but no stopping of children - postStop(); - } - //#preStartInit - - } - - public static class MessageInitExample extends UntypedActor { - //#messageInit - private String initializeMe = null; - - @Override - public void onReceive(Object message) throws Exception { - if (message.equals("init")) { - initializeMe = "Up and running"; - getContext().become(new Procedure() { - @Override - public void apply(Object message) throws Exception { - if (message.equals("U OK?")) - getSender().tell(initializeMe, getSelf()); - } - }); - } - } - //#messageInit - } - - @ClassRule - public static AkkaJUnitActorSystemResource actorSystemResource = - new AkkaJUnitActorSystemResource("InitializationDocSpecJava"); - - private final ActorSystem system = actorSystemResource.getSystem(); - - @Test - public void testIt() { - - new JavaTestKit(system) {{ - ActorRef testactor = system.actorOf(Props.create(MessageInitExample.class), "testactor"); - String probe = "U OK?"; - - testactor.tell(probe, getRef()); - expectNoMsg(); - - testactor.tell("init", getRef()); - testactor.tell(probe, getRef()); - expectMsgEquals("Up and running"); - }}; - } -} diff --git a/akka-docs/rst/java/code/docs/actor/InitializationDocTest.java b/akka-docs/rst/java/code/docs/actor/InitializationDocTest.java deleted file mode 100644 index 76d3f2904f..0000000000 --- a/akka-docs/rst/java/code/docs/actor/InitializationDocTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ - -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; -import scala.PartialFunction; -import scala.concurrent.duration.Duration; -import scala.runtime.BoxedUnit; -import scala.concurrent.Await; - -import java.util.concurrent.TimeUnit; - -public class InitializationDocTest extends AbstractJavaTest { - - static ActorSystem system = null; - - @BeforeClass - public static void beforeClass() { - system = ActorSystem.create("InitializationDocTest"); - } - - @AfterClass - public static void afterClass() throws Exception { - Await.result(system.terminate(), Duration.create("5 seconds")); - } - - public static class MessageInitExample extends AbstractActor { - private String initializeMe = null; - - public MessageInitExample() { - //#messageInit - receive(ReceiveBuilder. - matchEquals("init", m1 -> { - initializeMe = "Up and running"; - context().become(ReceiveBuilder. - matchEquals("U OK?", m2 -> { - sender().tell(initializeMe, self()); - }).build()); - }).build() - //#messageInit - ); - } - } - - @Test - public void testIt() { - - new JavaTestKit(system) {{ - ActorRef testactor = system.actorOf(Props.create(MessageInitExample.class), "testactor"); - String msg = "U OK?"; - - testactor.tell(msg, getRef()); - expectNoMsg(Duration.create(1, TimeUnit.SECONDS)); - - testactor.tell("init", getRef()); - testactor.tell(msg, getRef()); - expectMsgEquals("Up and running"); - }}; - } -} diff --git a/akka-docs/rst/java/code/docs/actor/MyJavaActor.java b/akka-docs/rst/java/code/docs/actor/MyJavaActor.java deleted file mode 100644 index d29171ebb0..0000000000 --- a/akka-docs/rst/java/code/docs/actor/MyJavaActor.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ - -package docs.actor; - -//#imports -import akka.actor.AbstractActor; -import akka.event.Logging; -import akka.event.LoggingAdapter; -import akka.japi.pf.ReceiveBuilder; - -//#imports - -//#my-actor -public class MyJavaActor extends AbstractActor { - private final LoggingAdapter log = Logging.getLogger(context().system(), this); - - public MyJavaActor() { - receive(ReceiveBuilder. - match(String.class, s -> { - log.info("Received String message: {}", s); - //#my-actor - //#reply - sender().tell(s, self()); - //#reply - //#my-actor - }). - matchAny(o -> log.info("received unknown message")).build() - ); - } -} -//#my-actor diff --git a/akka-docs/rst/java/code/docs/actor/MyReceiveTimeoutUntypedActor.java b/akka-docs/rst/java/code/docs/actor/MyReceiveTimeoutUntypedActor.java deleted file mode 100644 index 724a659d98..0000000000 --- a/akka-docs/rst/java/code/docs/actor/MyReceiveTimeoutUntypedActor.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ -package docs.actor; - -//#receive-timeout -import akka.actor.ActorRef; -import akka.actor.ReceiveTimeout; -import akka.actor.UntypedActor; -import scala.concurrent.duration.Duration; - -public class MyReceiveTimeoutUntypedActor extends UntypedActor { - //#receive-timeout - ActorRef target = getContext().system().deadLetters(); - //#receive-timeout - - public MyReceiveTimeoutUntypedActor() { - // To set an initial delay - getContext().setReceiveTimeout(Duration.create("30 seconds")); - } - - public void onReceive(Object message) { - if (message.equals("Hello")) { - // To set in a response to a message - getContext().setReceiveTimeout(Duration.create("1 second")); - //#receive-timeout - target = getSender(); - target.tell("Hello world", getSelf()); - //#receive-timeout - } else if (message instanceof ReceiveTimeout) { - // To turn it off - getContext().setReceiveTimeout(Duration.Undefined()); - //#receive-timeout - target.tell("timeout", getSelf()); - //#receive-timeout - } else { - unhandled(message); - } - } -} -//#receive-timeout diff --git a/akka-docs/rst/java/code/docs/actor/MyStoppingActor.java b/akka-docs/rst/java/code/docs/actor/MyStoppingActor.java deleted file mode 100644 index 4fd1a93f43..0000000000 --- a/akka-docs/rst/java/code/docs/actor/MyStoppingActor.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ -package docs.actor; - -//#my-stopping-actor -import akka.actor.ActorRef; -import akka.actor.UntypedActor; -import akka.event.Logging; -import akka.event.LoggingAdapter; - -public class MyStoppingActor extends UntypedActor { - - ActorRef child = null; - - // ... creation of child ... - - public void onReceive(Object message) throws Exception { - if (message.equals("interrupt-child")) { - context().stop(child); - } else if (message.equals("done")) { - context().stop(getSelf()); - } else { - unhandled(message); - } - } -} -//#my-stopping-actor - diff --git a/akka-docs/rst/java/code/docs/actor/MyUntypedActor.java b/akka-docs/rst/java/code/docs/actor/MyUntypedActor.java deleted file mode 100644 index f5972170dc..0000000000 --- a/akka-docs/rst/java/code/docs/actor/MyUntypedActor.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ -package docs.actor; - -//#my-untyped-actor -import akka.actor.UntypedActor; -import akka.event.Logging; -import akka.event.LoggingAdapter; - -public class MyUntypedActor extends UntypedActor { - LoggingAdapter log = Logging.getLogger(getContext().system(), this); - - public void onReceive(Object message) throws Exception { - if (message instanceof String) { - log.info("Received String message: {}", message); - getSender().tell(message, getSelf()); - } else - unhandled(message); - } -} -//#my-untyped-actor - diff --git a/akka-docs/rst/java/code/docs/actor/SampleActor.java b/akka-docs/rst/java/code/docs/actor/SampleActor.java deleted file mode 100644 index 0220069011..0000000000 --- a/akka-docs/rst/java/code/docs/actor/SampleActor.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ - -package docs.actor; - -//#sample-actor -import akka.actor.AbstractActor; -import akka.japi.pf.ReceiveBuilder; -import scala.PartialFunction; -import scala.runtime.BoxedUnit; - -public class SampleActor extends AbstractActor { - - private PartialFunction guarded = ReceiveBuilder. - match(String.class, s -> s.contains("guard"), s -> { - sender().tell("contains(guard): " + s, self()); - context().unbecome(); - }).build(); - - public SampleActor() { - receive(ReceiveBuilder. - match(Double.class, d -> { - sender().tell(d.isNaN() ? 0 : d, self()); - }). - match(Integer.class, i -> { - sender().tell(i * 10, self()); - }). - match(String.class, s -> s.startsWith("guard"), s -> { - sender().tell("startsWith(guard): " + s.toUpperCase(), self()); - context().become(guarded, false); - }).build() - ); - } -} -//#sample-actor diff --git a/akka-docs/rst/java/code/docs/actor/SampleActorTest.java b/akka-docs/rst/java/code/docs/actor/SampleActorTest.java deleted file mode 100644 index 51792eb8e4..0000000000 --- a/akka-docs/rst/java/code/docs/actor/SampleActorTest.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ - -package docs.actor; - -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 extends AbstractJavaTest { - - static ActorSystem system; - - @BeforeClass - public static void setup() { - system = ActorSystem.create("SampleActorTest"); - } - - @AfterClass - public static void tearDown() { - JavaTestKit.shutdownActorSystem(system); - system = null; - } - - @Test - public void testSampleActor() - { - new JavaTestKit(system) {{ - final ActorRef subject = system.actorOf(Props.create(SampleActor.class), "sample-actor"); - final ActorRef probeRef = getRef(); - - subject.tell(47.11, probeRef); - subject.tell("and no guard in the beginning", probeRef); - subject.tell("guard is a good thing", probeRef); - subject.tell(47.11, probeRef); - subject.tell(4711, probeRef); - subject.tell("and no guard in the beginning", probeRef); - subject.tell(4711, probeRef); - subject.tell("and an unmatched message", probeRef); - - expectMsgEquals(47.11); - assertTrue(expectMsgClass(String.class).startsWith("startsWith(guard):")); - assertTrue(expectMsgClass(String.class).startsWith("contains(guard):")); - expectMsgEquals(47110); - expectNoMsg(); - }}; - } -} diff --git a/akka-docs/rst/java/code/docs/actor/SchedulerDocTest.java b/akka-docs/rst/java/code/docs/actor/SchedulerDocTest.java index 07751554cc..12bddbd84b 100644 --- a/akka-docs/rst/java/code/docs/actor/SchedulerDocTest.java +++ b/akka-docs/rst/java/code/docs/actor/SchedulerDocTest.java @@ -11,10 +11,11 @@ import java.util.concurrent.TimeUnit; //#imports1 //#imports2 -import akka.actor.UntypedActor; import akka.actor.Cancellable; //#imports2 +import docs.actorlambda.MyActor; +import akka.actor.AbstractActor; import akka.actor.ActorRef; import akka.actor.ActorSystem; import akka.testkit.AkkaSpec; @@ -28,7 +29,7 @@ public class SchedulerDocTest extends AbstractJavaTest { AkkaSpec.testConf()); private final ActorSystem system = actorSystemResource.getSystem(); - private ActorRef testActor = system.actorOf(Props.create(MyUntypedActor.class)); + private ActorRef testActor = system.actorOf(Props.create(MyActor.class)); @Test public void scheduleOneOffTask() { @@ -51,14 +52,14 @@ public class SchedulerDocTest extends AbstractJavaTest { @Test public void scheduleRecurringTask() { //#schedule-recurring - class Ticker extends UntypedActor { + class Ticker extends AbstractActor { @Override - public void onReceive(Object message) { - if (message.equals("Tick")) { - // Do someting - } else { - unhandled(message); - } + public Receive createReceive() { + return receiveBuilder() + .matchEquals("Tick", m -> { + // Do someting + }) + .build(); } } diff --git a/akka-docs/rst/java/code/docs/actor/UntypedActorDocTest.java b/akka-docs/rst/java/code/docs/actor/UntypedActorDocTest.java deleted file mode 100644 index 57f3026218..0000000000 --- a/akka-docs/rst/java/code/docs/actor/UntypedActorDocTest.java +++ /dev/null @@ -1,794 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ -package docs.actor; - -//#import-ask -import static akka.pattern.Patterns.ask; -import static akka.pattern.Patterns.pipe; -//#import-ask -//#import-gracefulStop -import static akka.pattern.Patterns.gracefulStop; -//#import-gracefulStop - -import akka.actor.PoisonPill; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.concurrent.TimeUnit; - -import akka.testkit.AkkaJUnitActorSystemResource; - -import docs.AbstractJavaTest; -import org.junit.ClassRule; -import org.junit.Test; - - -//#import-gracefulStop -import scala.concurrent.Await; -//#import-ask -import scala.concurrent.Future; -import scala.concurrent.duration.Duration; -//#import-ask -//#import-gracefulStop -//#import-indirect -import akka.actor.Actor; -//#import-indirect -//#import-identify -import akka.actor.ActorIdentity; -//#import-identify -import akka.actor.ActorKilledException; -//#import-identify -import akka.actor.ActorSelection; -//#import-identify -//#import-actorRef -import akka.actor.ActorRef; -import akka.actor.ActorSystem; -//#import-actorRef -//#import-identify -import akka.actor.Identify; -//#import-identify -//#import-indirect -import akka.actor.IndirectActorProducer; -//#import-indirect -import akka.actor.OneForOneStrategy; -//#import-props -import akka.actor.Props; -import akka.japi.Creator; -//#import-props -import akka.actor.SupervisorStrategy; -import akka.actor.SupervisorStrategy.Directive; -//#import-terminated -import akka.actor.Terminated; -//#import-terminated -//#import-untypedActor -import akka.actor.UntypedActor; -//#import-untypedActor -//#import-stash -import akka.actor.UntypedActorWithStash; -//#import-stash -//#import-ask -import akka.dispatch.Futures; -import akka.dispatch.Mapper; -//#import-ask -import akka.japi.Function; -//#import-procedure -import akka.japi.Procedure; -//#import-procedure -//#import-gracefulStop -import akka.pattern.AskTimeoutException; -//#import-gracefulStop -import akka.pattern.Patterns; -import akka.testkit.AkkaSpec; -import akka.testkit.JavaTestKit; -//#import-ask -import akka.util.Timeout; -//#import-ask - -public class UntypedActorDocTest extends AbstractJavaTest { - - @ClassRule - public static AkkaJUnitActorSystemResource actorSystemResource = - new AkkaJUnitActorSystemResource("UntypedActorDocTest", AkkaSpec.testConf()); - - private final ActorSystem system = actorSystemResource.getSystem(); - - //#creating-props-config - static class MyActorC implements Creator { - @Override public MyActor create() { - return new MyActor("..."); - } - } - - //#creating-props-config - - @SuppressWarnings("unused") - @Test - public void createProps() { - //#creating-props-config - Props props1 = Props.create(MyUntypedActor.class); - Props props2 = Props.create(MyActor.class, "..."); - Props props3 = Props.create(new MyActorC()); - //#creating-props-config - } - - //#parametric-creator - static class ParametricCreator implements Creator { - @Override public T create() { - // ... fabricate actor here - //#parametric-creator - return null; - //#parametric-creator - } - } - //#parametric-creator - - @Test - public void systemActorOf() { - //#system-actorOf - // ActorSystem is a heavy object: create only one per application - final ActorSystem system = ActorSystem.create("MySystem"); - final ActorRef myActor = system.actorOf(Props.create(MyUntypedActor.class), - "myactor"); - //#system-actorOf - try { - new JavaTestKit(system) { - { - myActor.tell("hello", getRef()); - expectMsgEquals("hello"); - } - }; - } finally { - JavaTestKit.shutdownActorSystem(system); - } - } - - @Test - public void contextActorOf() { - new JavaTestKit(system) { - { - //#context-actorOf - class A extends UntypedActor { - final ActorRef child = - getContext().actorOf(Props.create(MyUntypedActor.class), "myChild"); - //#plus-some-behavior - @Override - public void onReceive(Object msg) { - getSender().tell(child, getSelf()); - } - //#plus-some-behavior - } - //#context-actorOf - final ActorRef top = system.actorOf(Props.create(A.class, this)); - top.tell("hello", getRef()); - final ActorRef child = expectMsgClass(ActorRef.class); - child.tell("hello", getRef()); - expectMsgEquals("hello"); - } - }; - } - - // this is just to make the test below a tiny fraction nicer - private ActorSystem getContext() { - return system; - } - - static - //#creating-indirectly - class DependencyInjector implements IndirectActorProducer { - final Object applicationContext; - final String beanName; - - public DependencyInjector(Object applicationContext, String beanName) { - this.applicationContext = applicationContext; - this.beanName = beanName; - } - - @Override - public Class actorClass() { - return MyActor.class; - } - - @Override - public MyActor produce() { - MyActor result; - //#obtain-fresh-Actor-instance-from-DI-framework - result = new MyActor((String) applicationContext); - //#obtain-fresh-Actor-instance-from-DI-framework - return result; - } - } - //#creating-indirectly - - @Test - public void indirectActorOf() { - final String applicationContext = "..."; - //#creating-indirectly - - final ActorRef myActor = getContext().actorOf( - Props.create(DependencyInjector.class, applicationContext, "MyActor"), - "myactor3"); - //#creating-indirectly - new JavaTestKit(system) { - { - myActor.tell("hello", getRef()); - expectMsgEquals("..."); - } - }; - } - - @SuppressWarnings("unused") - @Test - public void usingAsk() throws Exception { - ActorRef myActor = system.actorOf(Props.create(MyAskActor.class, this), "myactor5"); - - //#using-ask - Future future = Patterns.ask(myActor, "Hello", 1000); - Object result = Await.result(future, Duration.create(1, TimeUnit.SECONDS)); - //#using-ask - } - - @Test - public void receiveTimeout() { - final ActorRef myActor = system.actorOf(Props.create(MyReceiveTimeoutUntypedActor.class)); - new JavaTestKit(system) { - { - new Within(Duration.create(1, TimeUnit.SECONDS), Duration.create(1500, - TimeUnit.MILLISECONDS)) { - @Override - protected void run() { - myActor.tell("Hello", getRef()); - expectMsgEquals("Hello world"); - expectMsgEquals("timeout"); - } - }; - } - }; - } - - @Test - public void usePoisonPill() { - final ActorRef myActor = system.actorOf(Props.create(MyUntypedActor.class)); - new JavaTestKit(system) { - { - final ActorRef sender = getRef(); - //#poison-pill - myActor.tell(akka.actor.PoisonPill.getInstance(), sender); - //#poison-pill - watch(myActor); - expectTerminated(myActor); - } - }; - } - - @Test - public void useKill() { - new JavaTestKit(system) { - { - class Master extends UntypedActor { - private SupervisorStrategy strategy = new OneForOneStrategy(-1, - Duration.Undefined(), new Function() { - @Override - public Directive apply(Throwable thr) { - if (thr instanceof ActorKilledException) { - target.tell("killed", getSelf()); - getContext().stop(getSelf()); - return SupervisorStrategy.stop(); - } - return SupervisorStrategy.escalate(); - } - }); - final ActorRef target; - ActorRef child; - - //#preStart - @Override - public void preStart() { - child = getContext().actorOf(Props.empty()); - } - //#preStart - - @SuppressWarnings("unused") - public Master(ActorRef target) { - this.target = target; - - /* - * Only compilation of `forward` is verified here. - */ - final Object result = ""; - //#forward - target.forward(result, getContext()); - //#forward - } - - @Override - public SupervisorStrategy supervisorStrategy() { - return strategy; - } - - //#reply - @Override - public void onReceive(Object msg) { - Object result = - //#calculate-result - child; - //#calculate-result - - // do not forget the second argument! - getSender().tell(result, getSelf()); - } - //#reply - - //#postStop - @Override - public void postStop() { - //#clean-up-resources-here - final String message = "stopped"; - //#tell - // don’t forget to think about who is the sender (2nd argument) - target.tell(message, getSelf()); - //#tell - //#clean-up-resources-here - } - //#postStop - } - final ActorRef master = system.actorOf(Props.create(Master.class, this, getRef())); - expectMsgEquals(""); - master.tell("", getRef()); - final ActorRef victim = expectMsgClass(ActorRef.class); - //#kill - victim.tell(akka.actor.Kill.getInstance(), ActorRef.noSender()); - //#kill - expectMsgEquals("killed"); - expectMsgEquals("stopped"); - assert getLastSender().equals(master); - } - }; - } - - @Test - public void useBecome() { - new JavaTestKit(system) { - { - ActorRef myActor = system.actorOf(Props.create(HotSwapActor.class)); - myActor.tell("foo", getRef()); - myActor.tell("bar", getRef()); - expectMsgEquals("I am already happy :-)"); - myActor.tell("bar", getRef()); - expectMsgEquals("I am already happy :-)"); - } - }; - } - - @Test - public void useWatch() throws Exception { - ActorRef myActor = system.actorOf(Props.create(WatchActor.class)); - Future future = Patterns.ask(myActor, "kill", 1000); - assert Await.result(future, Duration.create("1 second")).equals("finished"); - } - - // compilation test only - public void compileSelections() { - //#selection-local - // will look up this absolute path - getContext().actorSelection("/user/serviceA/actor"); - // will look up sibling beneath same supervisor - getContext().actorSelection("../joe"); - //#selection-local - - //#selection-wildcard - // will look all children to serviceB with names starting with worker - getContext().actorSelection("/user/serviceB/worker*"); - // will look up all siblings beneath same supervisor - getContext().actorSelection("../*"); - //#selection-wildcard - - //#selection-remote - getContext().actorSelection("akka.tcp://app@otherhost:1234/user/serviceB"); - //#selection-remote - } - - @Test - public void useIdentify() throws Exception { - new JavaTestKit(system) { - { - ActorRef a = system.actorOf(Props.create(MyUntypedActor.class), "another"); - ActorRef b = system.actorOf(Props.create(Follower.class, getRef())); - expectMsgEquals(a); - system.stop(a); - watch(b); - expectTerminated(b); - } - }; - } - - @Test - public void usePatternsGracefulStop() throws Exception { - ActorRef actorRef = system.actorOf(Props.create(Manager.class)); - //#gracefulStop - try { - Future stopped = - gracefulStop(actorRef, Duration.create(5, TimeUnit.SECONDS), Manager.SHUTDOWN); - Await.result(stopped, Duration.create(6, TimeUnit.SECONDS)); - // the actor has been stopped - } catch (AskTimeoutException e) { - // the actor wasn't stopped within 5 seconds - } - //#gracefulStop - } - - class Result { - final String x; - final String s; - - public Result(String x, String s) { - this.x = x; - this.s = s; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((s == null) ? 0 : s.hashCode()); - result = prime * result + ((x == null) ? 0 : x.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Result other = (Result) obj; - if (s == null) { - if (other.s != null) - return false; - } else if (!s.equals(other.s)) - return false; - if (x == null) { - if (other.x != null) - return false; - } else if (!x.equals(other.x)) - return false; - return true; - } - } - - @Test - public void usePatternsAskPipe() { - new JavaTestKit(system) { - { - ActorRef actorA = system.actorOf(Props.create(MyUntypedActor.class)); - ActorRef actorB = system.actorOf(Props.create(MyUntypedActor.class)); - ActorRef actorC = getRef(); - - //#ask-pipe - final Timeout t = new Timeout(Duration.create(5, TimeUnit.SECONDS)); - - final ArrayList> futures = new ArrayList>(); - futures.add(ask(actorA, "request", 1000)); // using 1000ms timeout - futures.add(ask(actorB, "another request", t)); // using timeout from - // above - - final Future> aggregate = Futures.sequence(futures, - system.dispatcher()); - - final Future transformed = aggregate.map( - new Mapper, Result>() { - public Result apply(Iterable coll) { - final Iterator it = coll.iterator(); - final String x = (String) it.next(); - final String s = (String) it.next(); - return new Result(x, s); - } - }, system.dispatcher()); - - pipe(transformed, system.dispatcher()).to(actorC); - //#ask-pipe - - expectMsgEquals(new Result("request", "another request")); - } - }; - } - - static - //#props-factory - public class DemoActor extends UntypedActor { - - /** - * Create Props for an actor of this type. - * @param magicNumber The magic number to be passed to this actor’s constructor. - * @return a Props for creating this actor, which can then be further configured - * (e.g. calling `.withDispatcher()` on it) - */ - public static Props props(final int magicNumber) { - return Props.create(new Creator() { - private static final long serialVersionUID = 1L; - - @Override - public DemoActor create() throws Exception { - return new DemoActor(magicNumber); - } - }); - } - - final int magicNumber; - - public DemoActor(int magicNumber) { - this.magicNumber = magicNumber; - } - - @Override - public void onReceive(Object msg) { - // some behavior here - } - - } - - //#props-factory - @Test - public void demoActor() { - //#props-factory - system.actorOf(DemoActor.props(42), "demo"); - //#props-factory - } - - static - //#messages-in-companion - public class DemoMessagesActor extends UntypedActor { - - static public class Greeting { - private final String from; - - public Greeting(String from) { - this.from = from; - } - - public String getGreeter() { - return from; - } - } - - public void onReceive(Object message) throws Exception { - if (message instanceof Greeting) { - getSender().tell("Hello " + ((Greeting) message).getGreeter(), getSelf()); - } else - unhandled(message); - } - } - //#messages-in-companion - - public static class MyActor extends UntypedActor { - - final String s; - - public MyActor(String s) { - this.s = s; - } - - public void onReceive(Object message) { - getSender().tell(s, getSelf()); - } - - /* - * This section must be kept in sync with the actual Actor trait. - * - * BOYSCOUT RULE: whenever you read this, verify that! - */ - //#lifecycle-callbacks - public void preStart() { - } - - public void preRestart(Throwable reason, scala.Option message) { - for (ActorRef each : getContext().getChildren()) { - getContext().unwatch(each); - getContext().stop(each); - } - postStop(); - } - - public void postRestart(Throwable reason) { - preStart(); - } - - public void postStop() { - } - //#lifecycle-callbacks - } - - public class MyAskActor extends UntypedActor { - - public void onReceive(Object message) throws Exception { - //#reply-exception - try { - String result = operation(); - getSender().tell(result, getSelf()); - } catch (Exception e) { - getSender().tell(new akka.actor.Status.Failure(e), getSelf()); - throw e; - } - //#reply-exception - } - - private String operation() { - return "Hi"; - } - } - - static - //#gracefulStop-actor - public class Manager extends UntypedActor { - - public static final String SHUTDOWN = "shutdown"; - - ActorRef worker = getContext().watch(getContext().actorOf( - Props.create(Cruncher.class), "worker")); - - public void onReceive(Object message) { - if (message.equals("job")) { - worker.tell("crunch", getSelf()); - } else if (message.equals(SHUTDOWN)) { - worker.tell(PoisonPill.getInstance(), getSelf()); - getContext().become(shuttingDown); - } - } - - Procedure shuttingDown = new Procedure() { - @Override - public void apply(Object message) { - if (message.equals("job")) { - getSender().tell("service unavailable, shutting down", getSelf()); - } else if (message instanceof Terminated) { - getContext().stop(getSelf()); - } - } - }; - } - //#gracefulStop-actor - - static class Cruncher extends UntypedActor { - public void onReceive(Object message) { - // crunch... - } - } - - static - //#hot-swap-actor - public class HotSwapActor extends UntypedActor { - - Procedure angry = new Procedure() { - @Override - public void apply(Object message) { - if (message.equals("bar")) { - getSender().tell("I am already angry?", getSelf()); - } else if (message.equals("foo")) { - getContext().become(happy); - } - } - }; - - Procedure happy = new Procedure() { - @Override - public void apply(Object message) { - if (message.equals("bar")) { - getSender().tell("I am already happy :-)", getSelf()); - } else if (message.equals("foo")) { - getContext().become(angry); - } - } - }; - - public void onReceive(Object message) { - if (message.equals("bar")) { - getContext().become(angry); - } else if (message.equals("foo")) { - getContext().become(happy); - } else { - unhandled(message); - } - } - } - - //#hot-swap-actor - - static - //#stash - public class ActorWithProtocol extends UntypedActorWithStash { - public void onReceive(Object msg) { - if (msg.equals("open")) { - unstashAll(); - getContext().become(new Procedure() { - public void apply(Object msg) throws Exception { - if (msg.equals("write")) { - // do writing... - } else if (msg.equals("close")) { - unstashAll(); - getContext().unbecome(); - } else { - stash(); - } - } - }, false); // add behavior on top instead of replacing - } else { - stash(); - } - } - } - //#stash - - static - //#watch - public class WatchActor extends UntypedActor { - final ActorRef child = this.getContext().actorOf(Props.empty(), "child"); - { - this.getContext().watch(child); // <-- the only call needed for registration - } - ActorRef lastSender = getContext().system().deadLetters(); - - @Override - public void onReceive(Object message) { - if (message.equals("kill")) { - getContext().stop(child); - lastSender = getSender(); - } else if (message instanceof Terminated) { - final Terminated t = (Terminated) message; - if (t.getActor() == child) { - lastSender.tell("finished", getSelf()); - } - } else { - unhandled(message); - } - } - } - //#watch - - static - //#identify - public class Follower extends UntypedActor { - final String identifyId = "1"; - { - ActorSelection selection = - getContext().actorSelection("/user/another"); - selection.tell(new Identify(identifyId), getSelf()); - } - ActorRef another; - - //#test-omitted - final ActorRef probe; - public Follower(ActorRef probe) { - this.probe = probe; - } - //#test-omitted - - @Override - public void onReceive(Object message) { - if (message instanceof ActorIdentity) { - ActorIdentity identity = (ActorIdentity) message; - if (identity.correlationId().equals(identifyId)) { - ActorRef ref = identity.getRef(); - if (ref == null) - getContext().stop(getSelf()); - else { - another = ref; - getContext().watch(another); - //#test-omitted - probe.tell(ref, getSelf()); - //#test-omitted - } - } - } else if (message instanceof Terminated) { - final Terminated t = (Terminated) message; - if (t.getActor().equals(another)) { - getContext().stop(getSelf()); - } - } else { - unhandled(message); - } - } - } - //#identify - -} diff --git a/akka-docs/rst/java/code/docs/actor/UntypedActorSwapper.java b/akka-docs/rst/java/code/docs/actor/UntypedActorSwapper.java deleted file mode 100644 index 69f0dca385..0000000000 --- a/akka-docs/rst/java/code/docs/actor/UntypedActorSwapper.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ -package docs.actor; - -import static docs.actor.UntypedActorSwapper.Swap.SWAP; -import akka.actor.ActorRef; -import akka.actor.Props; -import akka.actor.ActorSystem; -import akka.actor.UntypedActor; -import akka.event.Logging; -import akka.event.LoggingAdapter; -import akka.japi.Procedure; - -//#swapper -public class UntypedActorSwapper { - - public static class Swap { - public static Swap SWAP = new Swap(); - - private Swap() { - } - } - - public static class Swapper extends UntypedActor { - LoggingAdapter log = Logging.getLogger(getContext().system(), this); - - public void onReceive(Object message) { - if (message == SWAP) { - log.info("Hi"); - getContext().become(new Procedure() { - @Override - public void apply(Object message) { - log.info("Ho"); - getContext().unbecome(); // resets the latest 'become' - } - }, false); // this signals stacking of the new behavior - } else { - unhandled(message); - } - } - } - - public static void main(String... args) { - ActorSystem system = ActorSystem.create("MySystem"); - ActorRef swap = system.actorOf(Props.create(Swapper.class)); - swap.tell(SWAP, ActorRef.noSender()); // logs Hi - swap.tell(SWAP, ActorRef.noSender()); // logs Ho - swap.tell(SWAP, ActorRef.noSender()); // logs Hi - swap.tell(SWAP, ActorRef.noSender()); // logs Ho - swap.tell(SWAP, ActorRef.noSender()); // logs Hi - swap.tell(SWAP, ActorRef.noSender()); // logs Ho - } - -} -//#swapper diff --git a/akka-docs/rst/java/code/docs/actor/fsm/Buncher.java b/akka-docs/rst/java/code/docs/actor/fsm/Buncher.java deleted file mode 100644 index 05ae10c276..0000000000 --- a/akka-docs/rst/java/code/docs/actor/fsm/Buncher.java +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ - -package docs.actor.fsm; - -//#simple-imports -import akka.actor.AbstractFSM; -import akka.actor.ActorRef; -import akka.japi.pf.UnitMatch; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import scala.concurrent.duration.Duration; -//#simple-imports - -import static docs.actor.fsm.Buncher.Data; -import static docs.actor.fsm.Buncher.State.*; -import static docs.actor.fsm.Buncher.State; -import static docs.actor.fsm.Buncher.Uninitialized.*; -import static docs.actor.fsm.Events.*; - -//#simple-fsm -public class Buncher extends AbstractFSM { - { - //#fsm-body - startWith(Idle, Uninitialized); - - //#when-syntax - when(Idle, - matchEvent(SetTarget.class, Uninitialized.class, - (setTarget, uninitialized) -> - stay().using(new Todo(setTarget.getRef(), new LinkedList<>())))); - //#when-syntax - - //#transition-elided - onTransition( - matchState(Active, Idle, () -> { - // reuse this matcher - final UnitMatch m = UnitMatch.create( - matchData(Todo.class, - todo -> todo.getTarget().tell(new Batch(todo.getQueue()), self()))); - m.match(stateData()); - }). - state(Idle, Active, () -> {/* Do something here */})); - //#transition-elided - - when(Active, Duration.create(1, "second"), - matchEvent(Arrays.asList(Flush.class, StateTimeout()), Todo.class, - (event, todo) -> goTo(Idle).using(todo.copy(new LinkedList<>())))); - - //#unhandled-elided - whenUnhandled( - matchEvent(Queue.class, Todo.class, - (queue, todo) -> goTo(Active).using(todo.addElement(queue.getObj()))). - anyEvent((event, state) -> { - log().warning("received unhandled request {} in state {}/{}", - event, stateName(), state); - return stay(); - })); - //#unhandled-elided - - initialize(); - //#fsm-body - } - //#simple-fsm - - static - //#simple-state - // states - enum State { - Idle, Active - } - - //#simple-state - static - //#simple-state - // state data - interface Data { - } - - //#simple-state - static - //#simple-state - enum Uninitialized implements Data { - Uninitialized - } - - //#simple-state - static - //#simple-state - final class Todo implements Data { - private final ActorRef target; - private final List queue; - - public Todo(ActorRef target, List queue) { - this.target = target; - this.queue = queue; - } - - public ActorRef getTarget() { - return target; - } - - public List getQueue() { - return queue; - } - //#boilerplate - - @Override - public String toString() { - return "Todo{" + - "target=" + target + - ", queue=" + queue + - '}'; - } - - public Todo addElement(Object element) { - List nQueue = new LinkedList<>(queue); - nQueue.add(element); - return new Todo(this.target, nQueue); - } - - public Todo copy(List queue) { - return new Todo(this.target, queue); - } - - public Todo copy(ActorRef target) { - return new Todo(target, this.queue); - } - //#boilerplate - } - //#simple-state - //#simple-fsm -} -//#simple-fsm diff --git a/akka-docs/rst/java/code/docs/actor/fsm/BuncherTest.java b/akka-docs/rst/java/code/docs/actor/fsm/BuncherTest.java deleted file mode 100644 index c121ab50c7..0000000000 --- a/akka-docs/rst/java/code/docs/actor/fsm/BuncherTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ - -package docs.actor.fsm; - -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 java.util.LinkedList; - -import docs.actor.fsm.*; -import static docs.actor.fsm.Events.Batch; -import static docs.actor.fsm.Events.Queue; -import static docs.actor.fsm.Events.SetTarget; -import static docs.actor.fsm.Events.Flush.Flush; - -//#test-code -public class BuncherTest extends AbstractJavaTest { - - static ActorSystem system; - - @BeforeClass - public static void setup() { - system = ActorSystem.create("BuncherTest"); - } - - @AfterClass - public static void tearDown() { - JavaTestKit.shutdownActorSystem(system); - system = null; - } - - @Test - public void testBuncherActorBatchesCorrectly() { - new JavaTestKit(system) {{ - final ActorRef buncher = - system.actorOf(Props.create(Buncher.class)); - final ActorRef probe = getRef(); - - buncher.tell(new SetTarget(probe), probe); - buncher.tell(new Queue(42), probe); - buncher.tell(new Queue(43), probe); - LinkedList list1 = new LinkedList<>(); - list1.add(42); - list1.add(43); - expectMsgEquals(new Batch(list1)); - buncher.tell(new Queue(44), probe); - buncher.tell(Flush, probe); - buncher.tell(new Queue(45), probe); - LinkedList list2 = new LinkedList<>(); - list2.add(44); - expectMsgEquals(new Batch(list2)); - LinkedList list3 = new LinkedList<>(); - list3.add(45); - expectMsgEquals(new Batch(list3)); - system.stop(buncher); - }}; - } - - @Test - public void testBuncherActorDoesntBatchUninitialized() { - new JavaTestKit(system) {{ - final ActorRef buncher = - system.actorOf(Props.create(Buncher.class)); - final ActorRef probe = getRef(); - - buncher.tell(new Queue(42), probe); - expectNoMsg(); - system.stop(buncher); - }}; - } -} -//#test-code diff --git a/akka-docs/rst/java/code/docs/actor/fsm/Events.java b/akka-docs/rst/java/code/docs/actor/fsm/Events.java deleted file mode 100644 index 3b9bf5fe06..0000000000 --- a/akka-docs/rst/java/code/docs/actor/fsm/Events.java +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ - -package docs.actor.fsm; - -import akka.actor.ActorRef; -import java.util.List; - -public class Events { - - static - //#simple-events - public final class SetTarget { - private final ActorRef ref; - - public SetTarget(ActorRef ref) { - this.ref = ref; - } - - public ActorRef getRef() { - return ref; - } - //#boilerplate - - @Override - public String toString() { - return "SetTarget{" + - "ref=" + ref + - '}'; - } - //#boilerplate - } - - //#simple-events - static - //#simple-events - public final class Queue { - private final Object obj; - - public Queue(Object obj) { - this.obj = obj; - } - - public Object getObj() { - return obj; - } - //#boilerplate - - @Override - public String toString() { - return "Queue{" + - "obj=" + obj + - '}'; - } - //#boilerplate - } - - //#simple-events - static - //#simple-events - public final class Batch { - private final List list; - - public Batch(List list) { - this.list = list; - } - - public List getList() { - return list; - } - //#boilerplate - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Batch batch = (Batch) o; - - return list.equals(batch.list); - } - - @Override - public int hashCode() { - return list.hashCode(); - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append( "Batch{list="); - list.stream().forEachOrdered(e -> { builder.append(e); builder.append(","); }); - int len = builder.length(); - builder.replace(len, len, "}"); - return builder.toString(); - } - //#boilerplate - } - - //#simple-events - static - //#simple-events - public enum Flush { - Flush - } - //#simple-events -} diff --git a/akka-docs/rst/java/code/docs/actor/fsm/FSMDocTest.java b/akka-docs/rst/java/code/docs/actor/fsm/FSMDocTest.java deleted file mode 100644 index d6ebeca432..0000000000 --- a/akka-docs/rst/java/code/docs/actor/fsm/FSMDocTest.java +++ /dev/null @@ -1,180 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ - -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; -import org.junit.Test; -import scala.concurrent.duration.Duration; - -import static org.junit.Assert.*; - -import static docs.actor.fsm.FSMDocTest.StateType.*; -import static docs.actor.fsm.FSMDocTest.Messages.*; -import static java.util.concurrent.TimeUnit.*; - -public class FSMDocTest extends AbstractJavaTest { - static ActorSystem system; - - @BeforeClass - public static void setup() { - system = ActorSystem.create("FSMDocTest"); - } - - @AfterClass - public static void tearDown() { - JavaTestKit.shutdownActorSystem(system); - system = null; - } - - public static enum StateType { - SomeState, - Processing, - Idle, - Active, - Error - } - - public static enum Messages { - WillDo, - Tick - } - - public static enum Data { - Foo, - Bar - }; - - public static interface X {}; - - public static class DummyFSM extends AbstractFSM { - Integer newData = 42; - //#alt-transition-syntax - public void handler(StateType from, StateType to) { - // handle transition here - } - - //#alt-transition-syntax - { - //#modifier-syntax - when(SomeState, matchAnyEvent((msg, data) -> { - return goTo(Processing).using(newData). - forMax(Duration.create(5, SECONDS)).replying(WillDo); - })); - //#modifier-syntax - - //#NullFunction - when(SomeState, AbstractFSM.NullFunction()); - //#NullFunction - - //#transition-syntax - onTransition( - matchState(Active, Idle, () -> setTimer("timeout", - Tick, Duration.create(1, SECONDS), true)). - state(Active, null, () -> cancelTimer("timeout")). - state(null, Idle, (f, t) -> log().info("entering Idle from " + f))); - //#transition-syntax - - //#alt-transition-syntax - onTransition(this::handler); - //#alt-transition-syntax - - //#stop-syntax - when(Error, matchEventEquals("stop", (event, data) -> { - // do cleanup ... - return stop(); - })); - //#stop-syntax - - //#termination-syntax - onTermination( - matchStop(Normal(), - (state, data) -> {/* Do something here */}). - stop(Shutdown(), - (state, data) -> {/* Do something here */}). - stop(Failure.class, - (reason, state, data) -> {/* Do something here */})); - //#termination-syntax - - //#unhandled-syntax - whenUnhandled( - matchEvent(X.class, (x, data) -> { - log().info("Received unhandled event: " + x); - return stay(); - }). - anyEvent((event, data) -> { - log().warning("Received unknown event: " + event); - return goTo(Error); - })); - } - //#unhandled-syntax - } - - static - //#logging-fsm - public class MyFSM extends AbstractLoggingFSM { - //#body-elided - //#logging-fsm - ActorRef target = null; - //#logging-fsm - @Override - public int logDepth() { return 12; } - { - onTermination( - matchStop(Failure.class, (reason, state, data) -> { - String lastEvents = getLog().mkString("\n\t"); - log().warning("Failure in state " + state + " with data " + data + "\n" + - "Events leading up to this point:\n\t" + lastEvents); - //#logging-fsm - target.tell(reason.cause(), self()); - target.tell(state, self()); - target.tell(data, self()); - target.tell(lastEvents, self()); - //#logging-fsm - }) - ); - //... - //#logging-fsm - startWith(SomeState, Data.Foo); - when(SomeState, matchEvent(ActorRef.class, Data.class, (ref, data) -> { - target = ref; - target.tell("going active", self()); - return goTo(Active); - })); - when(Active, matchEventEquals("stop", (event, data) -> { - target.tell("stopping", self()); - return stop(new Failure("This is not the error you're looking for")); - })); - initialize(); - //#logging-fsm - } - //#body-elided - } - //#logging-fsm - - @Test - public void testLoggingFSM() - { - new JavaTestKit(system) {{ - final ActorRef logger = - system.actorOf(Props.create(MyFSM.class)); - final ActorRef probe = getRef(); - - logger.tell(probe, probe); - expectMsgEquals("going active"); - logger.tell("stop", probe); - expectMsgEquals("stopping"); - expectMsgEquals("This is not the error you're looking for"); - expectMsgEquals(Active); - expectMsgEquals(Data.Foo); - String msg = expectMsgClass(String.class); - assertThat(msg, CoreMatchers.startsWith("LogEntry(SomeState,Foo,Actor[akka://FSMDocTest/system/")); - }}; - } -} diff --git a/akka-docs/rst/java/code/docs/actor/japi/FaultHandlingDocSample.java b/akka-docs/rst/java/code/docs/actor/japi/FaultHandlingDocSample.java deleted file mode 100644 index 6781ded3c6..0000000000 --- a/akka-docs/rst/java/code/docs/actor/japi/FaultHandlingDocSample.java +++ /dev/null @@ -1,489 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ -package docs.actor.japi; - -//#all -//#imports -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import akka.actor.*; -import akka.dispatch.Mapper; -import akka.japi.Function; -import scala.concurrent.duration.Duration; -import akka.util.Timeout; -import akka.event.Logging; -import akka.event.LoggingAdapter; -import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; - -import static akka.japi.Util.classTag; - -import static akka.actor.SupervisorStrategy.restart; -import static akka.actor.SupervisorStrategy.stop; -import static akka.actor.SupervisorStrategy.escalate; -import akka.actor.SupervisorStrategy.Directive; -import static akka.pattern.Patterns.ask; -import static akka.pattern.Patterns.pipe; - -import static docs.actor.japi.FaultHandlingDocSample.WorkerApi.*; -import static docs.actor.japi.FaultHandlingDocSample.CounterServiceApi.*; -import static docs.actor.japi.FaultHandlingDocSample.CounterApi.*; -import static docs.actor.japi.FaultHandlingDocSample.StorageApi.*; - -//#imports - -public class FaultHandlingDocSample { - - /** - * Runs the sample - */ - public static void main(String[] args) { - Config config = ConfigFactory.parseString("akka.loglevel = DEBUG \n" + - "akka.actor.debug.lifecycle = on"); - - ActorSystem system = ActorSystem.create("FaultToleranceSample", config); - ActorRef worker = system.actorOf(Props.create(Worker.class), "worker"); - ActorRef listener = system.actorOf(Props.create(Listener.class), "listener"); - // start the work and listen on progress - // note that the listener is used as sender of the tell, - // i.e. it will receive replies from the worker - worker.tell(Start, listener); - } - - /** - * Listens on progress from the worker and shuts down the system when enough - * work has been done. - */ - public static class Listener extends UntypedActor { - final LoggingAdapter log = Logging.getLogger(getContext().system(), this); - - @Override - public void preStart() { - // If we don't get any progress within 15 seconds then the service - // is unavailable - getContext().setReceiveTimeout(Duration.create("15 seconds")); - } - - public void onReceive(Object msg) { - log.debug("received message {}", msg); - if (msg instanceof Progress) { - Progress progress = (Progress) msg; - log.info("Current progress: {} %", progress.percent); - if (progress.percent >= 100.0) { - log.info("That's all, shutting down"); - getContext().system().terminate(); - } - } else if (msg == ReceiveTimeout.getInstance()) { - // No progress within 15 seconds, ServiceUnavailable - log.error("Shutting down due to unavailable service"); - getContext().system().terminate(); - } else { - unhandled(msg); - } - } - } - - //#messages - public interface WorkerApi { - public static final Object Start = "Start"; - public static final Object Do = "Do"; - - public static class Progress { - public final double percent; - - public Progress(double percent) { - this.percent = percent; - } - - public String toString() { - return String.format("%s(%s)", getClass().getSimpleName(), percent); - } - } - } - - //#messages - - /** - * Worker performs some work when it receives the Start message. It will - * continuously notify the sender of the Start message of current Progress. - * The Worker supervise the CounterService. - */ - public static class Worker extends UntypedActor { - final LoggingAdapter log = Logging.getLogger(getContext().system(), this); - final Timeout askTimeout = new Timeout(Duration.create(5, "seconds")); - - // The sender of the initial Start message will continuously be notified - // about progress - ActorRef progressListener; - final ActorRef counterService = getContext().actorOf( - Props.create(CounterService.class), "counter"); - final int totalCount = 51; - - // Stop the CounterService child if it throws ServiceUnavailable - private static SupervisorStrategy strategy = new OneForOneStrategy(-1, - Duration.Inf(), new Function() { - @Override - public Directive apply(Throwable t) { - if (t instanceof ServiceUnavailable) { - return stop(); - } else { - return escalate(); - } - } - }); - - @Override - public SupervisorStrategy supervisorStrategy() { - return strategy; - } - - public void onReceive(Object msg) { - log.debug("received message {}", msg); - if (msg.equals(Start) && progressListener == null) { - progressListener = getSender(); - getContext().system().scheduler().schedule( - Duration.Zero(), Duration.create(1, "second"), getSelf(), Do, - getContext().dispatcher(), null - ); - } else if (msg.equals(Do)) { - counterService.tell(new Increment(1), getSelf()); - counterService.tell(new Increment(1), getSelf()); - counterService.tell(new Increment(1), getSelf()); - - // Send current progress to the initial sender - pipe(ask(counterService, GetCurrentCount, askTimeout) - .mapTo(classTag(CurrentCount.class)) - .map(new Mapper() { - public Progress apply(CurrentCount c) { - return new Progress(100.0 * c.count / totalCount); - } - }, getContext().dispatcher()), getContext().dispatcher()) - .to(progressListener); - } else { - unhandled(msg); - } - } - } - - //#messages - public interface CounterServiceApi { - - public static final Object GetCurrentCount = "GetCurrentCount"; - - public static class CurrentCount { - public final String key; - public final long count; - - public CurrentCount(String key, long count) { - this.key = key; - this.count = count; - } - - public String toString() { - return String.format("%s(%s, %s)", getClass().getSimpleName(), key, count); - } - } - - public static class Increment { - public final long n; - - public Increment(long n) { - this.n = n; - } - - public String toString() { - return String.format("%s(%s)", getClass().getSimpleName(), n); - } - } - - public static class ServiceUnavailable extends RuntimeException { - private static final long serialVersionUID = 1L; - public ServiceUnavailable(String msg) { - super(msg); - } - } - - } - - //#messages - - /** - * Adds the value received in Increment message to a persistent counter. - * Replies with CurrentCount when it is asked for CurrentCount. CounterService - * supervise Storage and Counter. - */ - public static class CounterService extends UntypedActor { - - // Reconnect message - static final Object Reconnect = "Reconnect"; - - private static class SenderMsgPair { - final ActorRef sender; - final Object msg; - - SenderMsgPair(ActorRef sender, Object msg) { - this.msg = msg; - this.sender = sender; - } - } - - final LoggingAdapter log = Logging.getLogger(getContext().system(), this); - final String key = getSelf().path().name(); - ActorRef storage; - ActorRef counter; - final List backlog = new ArrayList(); - final int MAX_BACKLOG = 10000; - - // Restart the storage child when StorageException is thrown. - // After 3 restarts within 5 seconds it will be stopped. - private static SupervisorStrategy strategy = new OneForOneStrategy(3, - Duration.create("5 seconds"), new Function() { - @Override - public Directive apply(Throwable t) { - if (t instanceof StorageException) { - return restart(); - } else { - return escalate(); - } - } - }); - - @Override - public SupervisorStrategy supervisorStrategy() { - return strategy; - } - - @Override - public void preStart() { - initStorage(); - } - - /** - * The child storage is restarted in case of failure, but after 3 restarts, - * and still failing it will be stopped. Better to back-off than - * continuously failing. When it has been stopped we will schedule a - * Reconnect after a delay. Watch the child so we receive Terminated message - * when it has been terminated. - */ - void initStorage() { - storage = getContext().watch(getContext().actorOf( - Props.create(Storage.class), "storage")); - // Tell the counter, if any, to use the new storage - if (counter != null) - counter.tell(new UseStorage(storage), getSelf()); - // We need the initial value to be able to operate - storage.tell(new Get(key), getSelf()); - } - - @Override - public void onReceive(Object msg) { - log.debug("received message {}", msg); - if (msg instanceof Entry && ((Entry) msg).key.equals(key) && - counter == null) { - // Reply from Storage of the initial value, now we can create the Counter - final long value = ((Entry) msg).value; - counter = getContext().actorOf(Props.create(Counter.class, key, value)); - // Tell the counter to use current storage - counter.tell(new UseStorage(storage), getSelf()); - // and send the buffered backlog to the counter - for (SenderMsgPair each : backlog) { - counter.tell(each.msg, each.sender); - } - backlog.clear(); - } else if (msg instanceof Increment) { - forwardOrPlaceInBacklog(msg); - } else if (msg.equals(GetCurrentCount)) { - forwardOrPlaceInBacklog(msg); - } else if (msg instanceof Terminated) { - // After 3 restarts the storage child is stopped. - // We receive Terminated because we watch the child, see initStorage. - storage = null; - // Tell the counter that there is no storage for the moment - counter.tell(new UseStorage(null), getSelf()); - // Try to re-establish storage after while - getContext().system().scheduler().scheduleOnce( - Duration.create(10, "seconds"), getSelf(), Reconnect, - getContext().dispatcher(), null); - } else if (msg.equals(Reconnect)) { - // Re-establish storage after the scheduled delay - initStorage(); - } else { - unhandled(msg); - } - } - - void forwardOrPlaceInBacklog(Object msg) { - // We need the initial value from storage before we can start delegate to - // the counter. Before that we place the messages in a backlog, to be sent - // to the counter when it is initialized. - if (counter == null) { - if (backlog.size() >= MAX_BACKLOG) - throw new ServiceUnavailable("CounterService not available," + - " lack of initial value"); - backlog.add(new SenderMsgPair(getSender(), msg)); - } else { - counter.forward(msg, getContext()); - } - } - } - - //#messages - public interface CounterApi { - public static class UseStorage { - public final ActorRef storage; - - public UseStorage(ActorRef storage) { - this.storage = storage; - } - - public String toString() { - return String.format("%s(%s)", getClass().getSimpleName(), storage); - } - } - } - - //#messages - - /** - * The in memory count variable that will send current value to the Storage, - * if there is any storage available at the moment. - */ - public static class Counter extends UntypedActor { - final LoggingAdapter log = Logging.getLogger(getContext().system(), this); - final String key; - long count; - ActorRef storage; - - public Counter(String key, long initialValue) { - this.key = key; - this.count = initialValue; - } - - @Override - public void onReceive(Object msg) { - log.debug("received message {}", msg); - if (msg instanceof UseStorage) { - storage = ((UseStorage) msg).storage; - storeCount(); - } else if (msg instanceof Increment) { - count += ((Increment) msg).n; - storeCount(); - } else if (msg.equals(GetCurrentCount)) { - getSender().tell(new CurrentCount(key, count), getSelf()); - } else { - unhandled(msg); - } - } - - void storeCount() { - // Delegate dangerous work, to protect our valuable state. - // We can continue without storage. - if (storage != null) { - storage.tell(new Store(new Entry(key, count)), getSelf()); - } - } - } - - //#messages - public interface StorageApi { - - public static class Store { - public final Entry entry; - - public Store(Entry entry) { - this.entry = entry; - } - - public String toString() { - return String.format("%s(%s)", getClass().getSimpleName(), entry); - } - } - - public static class Entry { - public final String key; - public final long value; - - public Entry(String key, long value) { - this.key = key; - this.value = value; - } - - public String toString() { - return String.format("%s(%s, %s)", getClass().getSimpleName(), key, value); - } - } - - public static class Get { - public final String key; - - public Get(String key) { - this.key = key; - } - - public String toString() { - return String.format("%s(%s)", getClass().getSimpleName(), key); - } - } - - public static class StorageException extends RuntimeException { - private static final long serialVersionUID = 1L; - public StorageException(String msg) { - super(msg); - } - } - } - - //#messages - - /** - * Saves key/value pairs to persistent storage when receiving Store message. - * Replies with current value when receiving Get message. Will throw - * StorageException if the underlying data store is out of order. - */ - public static class Storage extends UntypedActor { - - final LoggingAdapter log = Logging.getLogger(getContext().system(), this); - final DummyDB db = DummyDB.instance; - - @Override - public void onReceive(Object msg) { - log.debug("received message {}", msg); - if (msg instanceof Store) { - Store store = (Store) msg; - db.save(store.entry.key, store.entry.value); - } else if (msg instanceof Get) { - Get get = (Get) msg; - Long value = db.load(get.key); - getSender().tell(new Entry(get.key, value == null ? - Long.valueOf(0L) : value), getSelf()); - } else { - unhandled(msg); - } - } - } - - //#dummydb - public static class DummyDB { - public static final DummyDB instance = new DummyDB(); - private final Map db = new HashMap(); - - private DummyDB() { - } - - public synchronized void save(String key, Long value) throws StorageException { - if (11 <= value && value <= 14) - throw new StorageException("Simulated store failure " + value); - db.put(key, value); - } - - public synchronized Long load(String key) throws StorageException { - return db.get(key); - } - } - //#dummydb -} -//#all diff --git a/akka-docs/rst/java/code/docs/actor/japi/FaultHandlingDocSampleJava8.java b/akka-docs/rst/java/code/docs/actor/japi/FaultHandlingDocSampleJava8.java deleted file mode 100644 index 3e38dd2f97..0000000000 --- a/akka-docs/rst/java/code/docs/actor/japi/FaultHandlingDocSampleJava8.java +++ /dev/null @@ -1,470 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ -package docs.actor.japi; - -//#all -//#imports -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import akka.actor.*; -import akka.dispatch.Mapper; -import akka.event.LoggingReceive; -import akka.japi.pf.DeciderBuilder; -import akka.japi.pf.ReceiveBuilder; -import akka.util.Timeout; -import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; -import scala.concurrent.duration.Duration; -import scala.PartialFunction; -import scala.runtime.BoxedUnit; - -import static akka.japi.Util.classTag; -import static akka.actor.SupervisorStrategy.resume; -import static akka.actor.SupervisorStrategy.restart; -import static akka.actor.SupervisorStrategy.stop; -import static akka.actor.SupervisorStrategy.escalate; - -import static akka.pattern.Patterns.ask; -import static akka.pattern.Patterns.pipe; - -import static docs.actor.japi.FaultHandlingDocSample.WorkerApi.*; -import static docs.actor.japi.FaultHandlingDocSample.CounterServiceApi.*; -import static docs.actor.japi.FaultHandlingDocSample.CounterApi.*; -import static docs.actor.japi.FaultHandlingDocSample.StorageApi.*; - -//#imports - -public class FaultHandlingDocSampleJava8 { - - /** - * Runs the sample - */ - public static void main(String[] args) { - Config config = ConfigFactory.parseString( - "akka.loglevel = \"DEBUG\"\n" + - "akka.actor.debug {\n" + - " receive = on\n" + - " lifecycle = on\n" + - "}\n"); - - ActorSystem system = ActorSystem.create("FaultToleranceSample", config); - ActorRef worker = system.actorOf(Props.create(Worker.class), "worker"); - ActorRef listener = system.actorOf(Props.create(Listener.class), "listener"); - // start the work and listen on progress - // note that the listener is used as sender of the tell, - // i.e. it will receive replies from the worker - worker.tell(Start, listener); - } - - /** - * Listens on progress from the worker and shuts down the system when enough - * work has been done. - */ - public static class Listener extends AbstractLoggingActor { - - @Override - public void preStart() { - // If we don't get any progress within 15 seconds then the service - // is unavailable - context().setReceiveTimeout(Duration.create("15 seconds")); - } - - public Listener() { - receive(LoggingReceive.create(ReceiveBuilder. - match(Progress.class, progress -> { - log().info("Current progress: {} %", progress.percent); - if (progress.percent >= 100.0) { - log().info("That's all, shutting down"); - context().system().shutdown(); - } - }). - matchEquals(ReceiveTimeout.getInstance(), x -> { - // No progress within 15 seconds, ServiceUnavailable - log().error("Shutting down due to unavailable service"); - context().system().shutdown(); - }).build(), context() - )); - } - } - - //#messages - public interface WorkerApi { - public static final Object Start = "Start"; - public static final Object Do = "Do"; - - public static class Progress { - public final double percent; - - public Progress(double percent) { - this.percent = percent; - } - - public String toString() { - return String.format("%s(%s)", getClass().getSimpleName(), percent); - } - } - } - - //#messages - - /** - * Worker performs some work when it receives the Start message. It will - * continuously notify the sender of the Start message of current Progress. - * The Worker supervise the CounterService. - */ - public static class Worker extends AbstractLoggingActor { - final Timeout askTimeout = new Timeout(Duration.create(5, "seconds")); - - // The sender of the initial Start message will continuously be notified - // about progress - ActorRef progressListener; - final ActorRef counterService = context().actorOf( - Props.create(CounterService.class), "counter"); - final int totalCount = 51; - - // Stop the CounterService child if it throws ServiceUnavailable - private static final SupervisorStrategy strategy = - new OneForOneStrategy(DeciderBuilder. - match(ServiceUnavailable.class, e -> stop()). - matchAny(o -> escalate()).build()); - - @Override - public SupervisorStrategy supervisorStrategy() { - return strategy; - } - - public Worker() { - receive(LoggingReceive.create(ReceiveBuilder. - matchEquals(Start, x -> progressListener == null, x -> { - progressListener = sender(); - context().system().scheduler().schedule( - Duration.Zero(), Duration.create(1, "second"), self(), Do, - context().dispatcher(), null - ); - }). - matchEquals(Do, x -> { - counterService.tell(new Increment(1), self()); - counterService.tell(new Increment(1), self()); - counterService.tell(new Increment(1), self()); - // Send current progress to the initial sender - pipe(ask(counterService, GetCurrentCount, askTimeout) - .mapTo(classTag(CurrentCount.class)) - .map(new Mapper() { - public Progress apply(CurrentCount c) { - return new Progress(100.0 * c.count / totalCount); - } - }, context().dispatcher()), context().dispatcher()) - .to(progressListener); - }).build(), context()) - ); - } - } - - //#messages - public interface CounterServiceApi { - - public static final Object GetCurrentCount = "GetCurrentCount"; - - public static class CurrentCount { - public final String key; - public final long count; - - public CurrentCount(String key, long count) { - this.key = key; - this.count = count; - } - - public String toString() { - return String.format("%s(%s, %s)", getClass().getSimpleName(), key, count); - } - } - - public static class Increment { - public final long n; - - public Increment(long n) { - this.n = n; - } - - public String toString() { - return String.format("%s(%s)", getClass().getSimpleName(), n); - } - } - - public static class ServiceUnavailable extends RuntimeException { - private static final long serialVersionUID = 1L; - public ServiceUnavailable(String msg) { - super(msg); - } - } - - } - - //#messages - - /** - * Adds the value received in Increment message to a persistent counter. - * Replies with CurrentCount when it is asked for CurrentCount. CounterService - * supervise Storage and Counter. - */ - public static class CounterService extends AbstractLoggingActor { - - // Reconnect message - static final Object Reconnect = "Reconnect"; - - private static class SenderMsgPair { - final ActorRef sender; - final Object msg; - - SenderMsgPair(ActorRef sender, Object msg) { - this.msg = msg; - this.sender = sender; - } - } - - final String key = self().path().name(); - ActorRef storage; - ActorRef counter; - final List backlog = new ArrayList<>(); - final int MAX_BACKLOG = 10000; - - // Restart the storage child when StorageException is thrown. - // After 3 restarts within 5 seconds it will be stopped. - private static final SupervisorStrategy strategy = - new OneForOneStrategy(3, Duration.create("5 seconds"), DeciderBuilder. - match(StorageException.class, e -> restart()). - matchAny(o -> escalate()).build()); - - @Override - public SupervisorStrategy supervisorStrategy() { - return strategy; - } - - @Override - public void preStart() { - initStorage(); - } - - /** - * The child storage is restarted in case of failure, but after 3 restarts, - * and still failing it will be stopped. Better to back-off than - * continuously failing. When it has been stopped we will schedule a - * Reconnect after a delay. Watch the child so we receive Terminated message - * when it has been terminated. - */ - void initStorage() { - storage = context().watch(context().actorOf( - Props.create(Storage.class), "storage")); - // Tell the counter, if any, to use the new storage - if (counter != null) - counter.tell(new UseStorage(storage), self()); - // We need the initial value to be able to operate - storage.tell(new Get(key), self()); - } - - public CounterService() { - receive(LoggingReceive.create(ReceiveBuilder. - match(Entry.class, entry -> entry.key.equals(key) && counter == null, entry -> { - // Reply from Storage of the initial value, now we can create the Counter - final long value = entry.value; - counter = context().actorOf(Props.create(Counter.class, key, value)); - // Tell the counter to use current storage - counter.tell(new UseStorage(storage), self()); - // and send the buffered backlog to the counter - for (SenderMsgPair each : backlog) { - counter.tell(each.msg, each.sender); - } - backlog.clear(); - }). - match(Increment.class, increment -> { - forwardOrPlaceInBacklog(increment); - }). - matchEquals(GetCurrentCount, gcc -> { - forwardOrPlaceInBacklog(gcc); - }). - match(Terminated.class, o -> { - // After 3 restarts the storage child is stopped. - // We receive Terminated because we watch the child, see initStorage. - storage = null; - // Tell the counter that there is no storage for the moment - counter.tell(new UseStorage(null), self()); - // Try to re-establish storage after while - context().system().scheduler().scheduleOnce( - Duration.create(10, "seconds"), self(), Reconnect, - context().dispatcher(), null); - }). - matchEquals(Reconnect, o -> { - // Re-establish storage after the scheduled delay - initStorage(); - }).build(), context()) - ); - } - - void forwardOrPlaceInBacklog(Object msg) { - // We need the initial value from storage before we can start delegate to - // the counter. Before that we place the messages in a backlog, to be sent - // to the counter when it is initialized. - if (counter == null) { - if (backlog.size() >= MAX_BACKLOG) - throw new ServiceUnavailable("CounterService not available," + - " lack of initial value"); - backlog.add(new SenderMsgPair(sender(), msg)); - } else { - counter.forward(msg, context()); - } - } - } - - //#messages - public interface CounterApi { - public static class UseStorage { - public final ActorRef storage; - - public UseStorage(ActorRef storage) { - this.storage = storage; - } - - public String toString() { - return String.format("%s(%s)", getClass().getSimpleName(), storage); - } - } - } - - //#messages - - /** - * The in memory count variable that will send current value to the Storage, - * if there is any storage available at the moment. - */ - public static class Counter extends AbstractLoggingActor { - final String key; - long count; - ActorRef storage; - - public Counter(String key, long initialValue) { - this.key = key; - this.count = initialValue; - - receive(LoggingReceive.create(ReceiveBuilder. - match(UseStorage.class, useStorage -> { - storage = useStorage.storage; - storeCount(); - }). - match(Increment.class, increment -> { - count += increment.n; - storeCount(); - }). - matchEquals(GetCurrentCount, gcc -> { - sender().tell(new CurrentCount(key, count), self()); - }).build(), context()) - ); - } - - void storeCount() { - // Delegate dangerous work, to protect our valuable state. - // We can continue without storage. - if (storage != null) { - storage.tell(new Store(new Entry(key, count)), self()); - } - } - } - - //#messages - public interface StorageApi { - - public static class Store { - public final Entry entry; - - public Store(Entry entry) { - this.entry = entry; - } - - public String toString() { - return String.format("%s(%s)", getClass().getSimpleName(), entry); - } - } - - public static class Entry { - public final String key; - public final long value; - - public Entry(String key, long value) { - this.key = key; - this.value = value; - } - - public String toString() { - return String.format("%s(%s, %s)", getClass().getSimpleName(), key, value); - } - } - - public static class Get { - public final String key; - - public Get(String key) { - this.key = key; - } - - public String toString() { - return String.format("%s(%s)", getClass().getSimpleName(), key); - } - } - - public static class StorageException extends RuntimeException { - private static final long serialVersionUID = 1L; - public StorageException(String msg) { - super(msg); - } - } - } - - //#messages - - /** - * Saves key/value pairs to persistent storage when receiving Store message. - * Replies with current value when receiving Get message. Will throw - * StorageException if the underlying data store is out of order. - */ - public static class Storage extends AbstractLoggingActor { - - final DummyDB db = DummyDB.instance; - - public Storage() { - receive(LoggingReceive.create(ReceiveBuilder. - match(Store.class, store -> { - db.save(store.entry.key, store.entry.value); - }). - match(Get.class, get -> { - Long value = db.load(get.key); - sender().tell(new Entry(get.key, value == null ? - Long.valueOf(0L) : value), self()); - }).build(), context()) - ); - } - } - - //#dummydb - public static class DummyDB { - public static final DummyDB instance = new DummyDB(); - private final Map db = new HashMap(); - - private DummyDB() { - } - - public synchronized void save(String key, Long value) throws StorageException { - if (11 <= value && value <= 14) - throw new StorageException("Simulated store failure " + value); - db.put(key, value); - } - - public synchronized Long load(String key) throws StorageException { - return db.get(key); - } - } - //#dummydb -} -//#all diff --git a/akka-docs/rst/java/code/docs/actorlambda/ActorDocTest.java b/akka-docs/rst/java/code/docs/actorlambda/ActorDocTest.java index 67cc354909..83f4066815 100644 --- a/akka-docs/rst/java/code/docs/actorlambda/ActorDocTest.java +++ b/akka-docs/rst/java/code/docs/actorlambda/ActorDocTest.java @@ -12,19 +12,23 @@ import akka.testkit.TestEvent; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import docs.AbstractJavaTest; -import docs.actor.ActorDocTest.FirstActor; -import scala.PartialFunction; -import scala.runtime.BoxedUnit; import static docs.actorlambda.Messages.Swap.Swap; import static docs.actorlambda.Messages.*; import static akka.japi.Util.immutableSeq; import akka.actor.CoordinatedShutdown; -import static akka.pattern.PatternsCS.ask; + import akka.util.Timeout; import akka.Done; import java.util.concurrent.CompletionStage; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Optional; import java.util.concurrent.TimeUnit; +import akka.testkit.TestActors; +import akka.dispatch.Mapper; +import akka.dispatch.Futures; +import akka.util.Timeout; import akka.testkit.JavaTestKit; import org.junit.AfterClass; @@ -44,13 +48,25 @@ import akka.actor.ActorIdentity; import akka.actor.ActorSelection; import akka.actor.Identify; //#import-identify -//#import-graceFulStop +//#import-ask +import static akka.pattern.Patterns.ask; +import static akka.pattern.Patterns.pipe; +import akka.dispatch.Futures; +import akka.dispatch.Mapper; +import akka.util.Timeout; +//#import-ask +//#import-gracefulStop import akka.pattern.AskTimeoutException; import scala.concurrent.Await; import scala.concurrent.duration.Duration; +//#import-ask import scala.concurrent.Future; import static akka.pattern.Patterns.gracefulStop; -//#import-graceFulStop +//#import-ask +//#import-gracefulStop +//#import-terminated +import akka.actor.Terminated; +//#import-terminated public class ActorDocTest extends AbstractJavaTest { @@ -71,49 +87,111 @@ public class ActorDocTest extends AbstractJavaTest { @AfterClass public static void afterClass() throws Exception { - Await.ready(system.terminate(), Duration.create("5 seconds")); + Await.ready(system.terminate(), Duration.create(5, TimeUnit.SECONDS)); } static //#context-actorOf public class FirstActor extends AbstractActor { - final ActorRef child = context().actorOf(Props.create(MyActor.class), "myChild"); + final ActorRef child = getContext().actorOf(Props.create(MyActor.class), "myChild"); + //#plus-some-behavior - public FirstActor() { - receive(ReceiveBuilder. - matchAny(x -> { - sender().tell(x, self()); - }).build() - ); + @Override + public Receive createReceive() { + return receiveBuilder() + .matchAny(x -> sender().tell(x, self())) + .build(); } //#plus-some-behavior } //#context-actorOf - static public abstract class SomeActor extends AbstractActor { - //#receive-constructor - public SomeActor() { - receive(ReceiveBuilder. - //#and-some-behavior - match(String.class, s -> System.out.println(s.toLowerCase())). - //#and-some-behavior - build()); - } - //#receive-constructor + static public class SomeActor extends AbstractActor { + //#createReceive @Override - //#receive - public abstract PartialFunction receive(); - //#receive + public Receive createReceive() { + return receiveBuilder() + .match(String.class, s -> System.out.println(s.toLowerCase())) + .build(); + } + //#createReceive } + + static + //#well-structured + public class WellStructuredActor extends AbstractActor { + + public static class Msg1 {} + public static class Msg2 {} + public static class Msg3 {} + + @Override + public Receive createReceive() { + return receiveBuilder() + .match(Msg1.class, this::receiveMsg1) + .match(Msg2.class, this::receiveMsg2) + .match(Msg3.class, this::receiveMsg3) + .build(); + } + + private void receiveMsg1(Msg1 msg) { + // actual work + } + + private void receiveMsg2(Msg2 msg) { + // actual work + } + + private void receiveMsg3(Msg3 msg) { + // actual work + } + } + //#well-structured + + static + //#optimized + public class OptimizedActor extends UntypedAbstractActor { + + public static class Msg1 {} + public static class Msg2 {} + public static class Msg3 {} + + @Override + public void onReceive(Object msg) throws Exception { + if (msg instanceof Msg1) + receiveMsg1((Msg1) msg); + else if (msg instanceof Msg1) + receiveMsg2((Msg2) msg); + else if (msg instanceof Msg3) + receiveMsg3((Msg3) msg); + else + unhandled(msg); + } + + private void receiveMsg1(Msg1 msg) { + // actual work + } + + private void receiveMsg2(Msg2 msg) { + // actual work + } + + private void receiveMsg3(Msg3 msg) { + // actual work + } + } + //#optimized static public class ActorWithArgs extends AbstractActor { private final String args; - ActorWithArgs(String args) { + public ActorWithArgs(String args) { this.args = args; - receive(ReceiveBuilder. - matchAny(x -> { }).build() - ); + } + + @Override + public Receive createReceive() { + return receiveBuilder().matchAny(x -> { }).build(); } } @@ -133,14 +211,18 @@ public class ActorDocTest extends AbstractJavaTest { } private final Integer magicNumber; - - DemoActor(Integer magicNumber) { + + public DemoActor(Integer magicNumber) { this.magicNumber = magicNumber; - receive(ReceiveBuilder. - match(Integer.class, i -> { + } + + @Override + public Receive createReceive() { + return receiveBuilder() + .match(Integer.class, i -> { sender().tell(i + magicNumber, self()); - }).build() - ); + }) + .build(); } } @@ -149,11 +231,12 @@ public class ActorDocTest extends AbstractJavaTest { //#props-factory public class SomeOtherActor extends AbstractActor { // Props(new DemoActor(42)) would not be safe - ActorRef demoActor = context().actorOf(DemoActor.props(42), "demo"); + ActorRef demoActor = getContext().actorOf(DemoActor.props(42), "demo"); // ... //#props-factory - public SomeOtherActor() { - receive(emptyBehavior()); + @Override + public Receive createReceive() { + return AbstractActor.emptyBehavior(); } //#props-factory } @@ -174,26 +257,65 @@ public class ActorDocTest extends AbstractJavaTest { return from; } } - - DemoMessagesActor() { - receive(ReceiveBuilder. - match(Greeting.class, g -> { + + @Override + public Receive createReceive() { + return receiveBuilder() + .match(Greeting.class, g -> { log().info("I was greeted by {}", g.getGreeter()); - }).build() - ); - }; + }) + .build(); + } } //#messages-in-companion + + public static class LifecycleMethods extends AbstractActor { + + @Override + public Receive createReceive() { + return AbstractActor.emptyBehavior(); + } + + /* + * This section must be kept in sync with the actual Actor trait. + * + * BOYSCOUT RULE: whenever you read this, verify that! + */ + //#lifecycle-callbacks + public void preStart() { + } + + public void preRestart(Throwable reason, Optional message) { + for (ActorRef each : getContext().getChildren()) { + getContext().unwatch(each); + getContext().stop(each); + } + postStop(); + } + + public void postRestart(Throwable reason) { + preStart(); + } + + public void postStop() { + } + //#lifecycle-callbacks + + } + public static class Hook extends AbstractActor { ActorRef target = null; - public Hook() { - receive(emptyBehavior()); + + @Override + public Receive createReceive() { + return AbstractActor.emptyBehavior(); } + //#preStart @Override public void preStart() { - target = context().actorOf(Props.create(MyActor.class, "target")); + target = getContext().actorOf(Props.create(MyActor.class, "target")); } //#preStart //#postStop @@ -207,7 +329,7 @@ public class ActorDocTest extends AbstractJavaTest { //#tell final Object result = ""; //#forward - target.forward(result, context()); + target.forward(result, getContext()); //#forward target = null; //#clean-up-some-resources @@ -218,28 +340,30 @@ public class ActorDocTest extends AbstractJavaTest { public void compileSelections() { //#selection-local // will look up this absolute path - context().actorSelection("/user/serviceA/actor"); + getContext().actorSelection("/user/serviceA/actor"); // will look up sibling beneath same supervisor - context().actorSelection("../joe"); + getContext().actorSelection("../joe"); //#selection-local //#selection-wildcard // will look all children to serviceB with names starting with worker - context().actorSelection("/user/serviceB/worker*"); + getContext().actorSelection("/user/serviceB/worker*"); // will look up all siblings beneath same supervisor - context().actorSelection("../*"); + getContext().actorSelection("../*"); //#selection-wildcard //#selection-remote - context().actorSelection("akka.tcp://app@otherhost:1234/user/serviceB"); + getContext().actorSelection("akka.tcp://app@otherhost:1234/user/serviceB"); //#selection-remote } } public static class ReplyException extends AbstractActor { - public ReplyException() { - receive(ReceiveBuilder. - matchAny(x -> { + + @Override + public Receive createReceive() { + return receiveBuilder() + .matchAny(x -> { //#reply-exception try { String result = operation(); @@ -249,8 +373,8 @@ public class ActorDocTest extends AbstractJavaTest { throw e; } //#reply-exception - }).build() - ); + }) + .build(); } private String operation() { @@ -267,28 +391,31 @@ public class ActorDocTest extends AbstractJavaTest { public static final Shutdown SHUTDOWN = Shutdown.Shutdown; private ActorRef worker = - context().watch(context().actorOf(Props.create(Cruncher.class), "worker")); + getContext().watch(getContext().actorOf(Props.create(Cruncher.class), "worker")); - public Manager() { - receive(ReceiveBuilder. - matchEquals("job", s -> { + @Override + public Receive createReceive() { + return receiveBuilder() + .matchEquals("job", s -> { worker.tell("crunch", self()); - }). - matchEquals(SHUTDOWN, x -> { + }) + .matchEquals(SHUTDOWN, x -> { worker.tell(PoisonPill.getInstance(), self()); - context().become(shuttingDown); - }).build() - ); + getContext().become(shuttingDown()); + }) + .build(); } - public PartialFunction shuttingDown = - ReceiveBuilder. - matchEquals("job", s -> { - sender().tell("service unavailable, shutting down", self()); - }). - match(Terminated.class, t -> t.actor().equals(worker), t -> { - context().stop(self()); - }).build(); + private AbstractActor.Receive shuttingDown() { + return receiveBuilder() + .matchEquals("job", s -> + sender().tell("service unavailable, shutting down", self()) + ) + .match(Terminated.class, t -> t.actor().equals(worker), t -> + getContext().stop(self()) + ) + .build(); + } } //#gracefulStop-actor @@ -309,27 +436,26 @@ public class ActorDocTest extends AbstractJavaTest { public static class Cruncher extends AbstractActor { - public Cruncher() { - receive(ReceiveBuilder. - matchEquals("crunch", s -> { }).build() - ); + @Override + public Receive createReceive() { + return receiveBuilder().matchEquals("crunch", s -> { }).build(); } } static //#swapper public class Swapper extends AbstractLoggingActor { - public Swapper() { - receive(ReceiveBuilder. - matchEquals(Swap, s -> { - log().info("Hi"); - context().become(ReceiveBuilder. - matchEquals(Swap, x -> { - log().info("Ho"); - context().unbecome(); // resets the latest 'become' (just for fun) - }).build(), false); // push on top instead of replace - }).build() - ); + @Override + public Receive createReceive() { + return receiveBuilder() + .matchEquals(Swap, s -> { + log().info("Hi"); + getContext().become(receiveBuilder(). + matchEquals(Swap, x -> { + log().info("Ho"); + getContext().unbecome(); // resets the latest 'become' (just for fun) + }).build(), false); // push on top instead of replace + }).build(); } } @@ -418,29 +544,32 @@ public class ActorDocTest extends AbstractJavaTest { //#receive-timeout public class ReceiveTimeoutActor extends AbstractActor { //#receive-timeout - ActorRef target = context().system().deadLetters(); + ActorRef target = getContext().system().deadLetters(); //#receive-timeout public ReceiveTimeoutActor() { // To set an initial delay - context().setReceiveTimeout(Duration.create("10 seconds")); - - receive(ReceiveBuilder. - matchEquals("Hello", s -> { + getContext().setReceiveTimeout(Duration.create(10, TimeUnit.SECONDS)); + } + + @Override + public Receive createReceive() { + return receiveBuilder() + .matchEquals("Hello", s -> { // To set in a response to a message - context().setReceiveTimeout(Duration.create("1 second")); + getContext().setReceiveTimeout(Duration.create(1, TimeUnit.SECONDS)); //#receive-timeout target = sender(); target.tell("Hello world", self()); //#receive-timeout - }). - match(ReceiveTimeout.class, r -> { + }) + .match(ReceiveTimeout.class, r -> { // To turn it off - context().setReceiveTimeout(Duration.Undefined()); + getContext().setReceiveTimeout(Duration.Undefined()); //#receive-timeout target.tell("timeout", self()); //#receive-timeout - }).build() - ); + }) + .build(); } } //#receive-timeout @@ -460,35 +589,40 @@ public class ActorDocTest extends AbstractJavaTest { static //#hot-swap-actor public class HotSwapActor extends AbstractActor { - private PartialFunction angry; - private PartialFunction happy; + private AbstractActor.Receive angry; + private AbstractActor.Receive happy; public HotSwapActor() { angry = - ReceiveBuilder. - matchEquals("foo", s -> { + receiveBuilder() + .matchEquals("foo", s -> { sender().tell("I am already angry?", self()); - }). - matchEquals("bar", s -> { - context().become(happy); - }).build(); + }) + .matchEquals("bar", s -> { + getContext().become(happy); + }) + .build(); - happy = ReceiveBuilder. - matchEquals("bar", s -> { + happy = receiveBuilder() + .matchEquals("bar", s -> { sender().tell("I am already happy :-)", self()); - }). - matchEquals("foo", s -> { - context().become(angry); - }).build(); - - receive(ReceiveBuilder. - matchEquals("foo", s -> { - context().become(angry); - }). - matchEquals("bar", s -> { - context().become(happy); - }).build() - ); + }) + .matchEquals("foo", s -> { + getContext().become(angry); + }) + .build(); + } + + @Override + public Receive createReceive() { + return receiveBuilder() + .matchEquals("foo", s -> + getContext().become(angry) + ) + .matchEquals("bar", s -> + getContext().become(happy) + ) + .build(); } } //#hot-swap-actor @@ -516,19 +650,21 @@ public class ActorDocTest extends AbstractJavaTest { static //#stash public class ActorWithProtocol extends AbstractActorWithStash { - public ActorWithProtocol() { - receive(ReceiveBuilder. - matchEquals("open", s -> { - context().become(ReceiveBuilder. - matchEquals("write", ws -> { /* do writing */ }). - matchEquals("close", cs -> { + @Override + public Receive createReceive() { + return receiveBuilder() + .matchEquals("open", s -> { + getContext().become(receiveBuilder() + .matchEquals("write", ws -> { /* do writing */ }) + .matchEquals("close", cs -> { unstashAll(); - context().unbecome(); - }). - matchAny(msg -> stash()).build(), false); - }). - matchAny(msg -> stash()).build() - ); + getContext().unbecome(); + }) + .matchAny(msg -> stash()) + .build(), false); + }) + .matchAny(msg -> stash()) + .build(); } } //#stash @@ -541,21 +677,24 @@ public class ActorDocTest extends AbstractJavaTest { static //#watch public class WatchActor extends AbstractActor { - private final ActorRef child = context().actorOf(Props.empty(), "target"); + private final ActorRef child = getContext().actorOf(Props.empty(), "target"); private ActorRef lastSender = system.deadLetters(); public WatchActor() { - context().watch(child); // <-- this is the only call needed for registration - - receive(ReceiveBuilder. - matchEquals("kill", s -> { - context().stop(child); + getContext().watch(child); // <-- this is the only call needed for registration + } + + @Override + public Receive createReceive() { + return receiveBuilder() + .matchEquals("kill", s -> { + getContext().stop(child); lastSender = sender(); - }). - match(Terminated.class, t -> t.actor().equals(child), t -> { + }) + .match(Terminated.class, t -> t.actor().equals(child), t -> { lastSender.tell("finished", self()); - }).build() - ); + }) + .build(); } } //#watch @@ -578,26 +717,30 @@ public class ActorDocTest extends AbstractJavaTest { final Integer identifyId = 1; public Follower(){ - ActorSelection selection = context().actorSelection("/user/another"); + ActorSelection selection = getContext().actorSelection("/user/another"); selection.tell(new Identify(identifyId), self()); - - receive(ReceiveBuilder. - match(ActorIdentity.class, id -> id.getRef() != null, id -> { - ActorRef ref = id.getRef(); - context().watch(ref); - context().become(active(ref)); - }). - match(ActorIdentity.class, id -> id.getRef() == null, id -> { - context().stop(self()); - }).build() - ); } - - final PartialFunction active(final ActorRef another) { - return ReceiveBuilder. - match(Terminated.class, t -> t.actor().equals(another), t -> { - context().stop(self()); - }).build(); + + @Override + public Receive createReceive() { + return receiveBuilder() + .match(ActorIdentity.class, id -> id.getActorRef().isPresent(), id -> { + ActorRef ref = id.getActorRef().get(); + getContext().watch(ref); + getContext().become(active(ref)); + }) + .match(ActorIdentity.class, id -> !id.getActorRef().isPresent(), id -> { + getContext().stop(self()); + }) + .build(); + } + + final AbstractActor.Receive active(final ActorRef another) { + return receiveBuilder() + .match(Terminated.class, t -> t.actor().equals(another), t -> + getContext().stop(self()) + ) + .build(); } } //#identify @@ -615,73 +758,70 @@ public class ActorDocTest extends AbstractJavaTest { } }; } - - public static class NoReceiveActor extends AbstractActor { - } - + @Test - public void noReceiveActor() { - EventFilter ex1 = new ErrorFilter(ActorInitializationException.class); - EventFilter[] ignoreExceptions = { ex1 }; - try { - system.eventStream().publish(new TestEvent.Mute(immutableSeq(ignoreExceptions))); - new JavaTestKit(system) {{ - final ActorRef victim = new EventFilter(ActorInitializationException.class) { - protected ActorRef run() { - return system.actorOf(Props.create(NoReceiveActor.class), "victim"); - } - }.message("Actor behavior has not been set with receive(...)").occurrences(1).exec(); + public void usePatternsAskPipe() { + new JavaTestKit(system) { + { + ActorRef actorA = system.actorOf(TestActors.echoActorProps()); + ActorRef actorB = system.actorOf(TestActors.echoActorProps()); + ActorRef actorC = getRef(); - assertEquals(true, victim.isTerminated()); - }}; - } finally { - system.eventStream().publish(new TestEvent.UnMute(immutableSeq(ignoreExceptions))); - } + //#ask-pipe + final Timeout t = new Timeout(Duration.create(5, TimeUnit.SECONDS)); + + final ArrayList> futures = new ArrayList>(); + futures.add(ask(actorA, "request", 1000)); // using 1000ms timeout + futures.add(ask(actorB, "another request", t)); // using timeout from + // above + + final Future> aggregate = Futures.sequence(futures, + system.dispatcher()); + + final Future transformed = aggregate.map( + new Mapper, Result>() { + public Result apply(Iterable coll) { + final Iterator it = coll.iterator(); + final String x = (String) it.next(); + final String s = (String) it.next(); + return new Result(x, s); + } + }, system.dispatcher()); + + pipe(transformed, system.dispatcher()).to(actorC); + //#ask-pipe + + expectMsgEquals(new Result("request", "another request")); + } + }; } - - public static class MultipleReceiveActor extends AbstractActor { - public MultipleReceiveActor() { - receive(ReceiveBuilder. - match(String.class, s1 -> s1.toLowerCase().equals("become"), s1 -> { - sender().tell(s1.toUpperCase(), self()); - receive(ReceiveBuilder. - match(String.class, s2 -> { - sender().tell(s2.toLowerCase(), self()); - }).build() - ); - }). - match(String.class, s1 -> { - sender().tell(s1.toUpperCase(), self()); - }).build() - ); - } - } - + @Test - public void multipleReceiveActor() { - EventFilter ex1 = new ErrorFilter(IllegalActorStateException.class); - EventFilter[] ignoreExceptions = { ex1 }; - try { - system.eventStream().publish(new TestEvent.Mute(immutableSeq(ignoreExceptions))); - new JavaTestKit(system) {{ - new EventFilter(IllegalActorStateException.class) { - protected Boolean run() { - ActorRef victim = system.actorOf(Props.create(MultipleReceiveActor.class), "victim2"); - victim.tell("Foo", getRef()); - expectMsgEquals("FOO"); - victim.tell("bEcoMe", getRef()); - expectMsgEquals("BECOME"); - victim.tell("Foo", getRef()); - // if it's upper case, then the actor was restarted - expectMsgEquals("FOO"); - return true; - } - }.message("Actor behavior has already been set with receive(...), " + - "use context().become(...) to change it later").occurrences(1).exec(); - }}; - } finally { - system.eventStream().publish(new TestEvent.UnMute(immutableSeq(ignoreExceptions))); - } + public void useKill() { + new JavaTestKit(system) { + { + ActorRef victim = system.actorOf(TestActors.echoActorProps()); + watch(victim); + //#kill + victim.tell(akka.actor.Kill.getInstance(), ActorRef.noSender()); + //#kill + expectTerminated(Duration.create(3, TimeUnit.SECONDS), victim); + } + }; + } + + @Test + public void usePoisonPill() { + new JavaTestKit(system) { + { + ActorRef victim = system.actorOf(TestActors.echoActorProps()); + watch(victim); + //#poison-pill + victim.tell(akka.actor.PoisonPill.getInstance(), ActorRef.noSender()); + //#poison-pill + expectTerminated(Duration.create(3, TimeUnit.SECONDS), victim); + } + }; } @Test @@ -691,7 +831,7 @@ public class ActorDocTest extends AbstractJavaTest { CoordinatedShutdown.get(system).addTask( CoordinatedShutdown.PhaseBeforeServiceUnbind(), "someTaskName", () -> { - return ask(someActor, "stop", new Timeout(5, TimeUnit.SECONDS)) + return akka.pattern.PatternsCS.ask(someActor, "stop", new Timeout(5, TimeUnit.SECONDS)) .thenApply(reply -> Done.getInstance()); }); //#coordinated-shutdown-addTask diff --git a/akka-docs/rst/java/code/docs/actorlambda/DependencyInjectionDocTest.java b/akka-docs/rst/java/code/docs/actorlambda/DependencyInjectionDocTest.java new file mode 100644 index 0000000000..fed5e15dfe --- /dev/null +++ b/akka-docs/rst/java/code/docs/actorlambda/DependencyInjectionDocTest.java @@ -0,0 +1,105 @@ +/** + * Copyright (C) 2009-2017 Lightbend Inc. + */ +package docs.actorlambda; + +import docs.AbstractJavaTest; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import scala.concurrent.Await; +import scala.concurrent.duration.Duration; + +import akka.actor.AbstractActor; +import akka.actor.ActorRef; +import akka.actor.ActorSystem; +import akka.actor.Props; +import akka.testkit.JavaTestKit; + +//#import +import akka.actor.Actor; +import akka.actor.IndirectActorProducer; +//#import + +public class DependencyInjectionDocTest extends AbstractJavaTest { + + public static class TheActor extends AbstractActor { + + final String s; + + public TheActor(String s) { + this.s = s; + } + + @Override + public Receive createReceive() { + return receiveBuilder() + .match(String.class, msg -> { + sender().tell(s, self()); + }) + .build(); + } + } + + static ActorSystem system = null; + + @BeforeClass + public static void beforeClass() { + system = ActorSystem.create("DependencyInjectionDocTest"); + } + + @AfterClass + public static void afterClass() throws Exception { + Await.ready(system.terminate(), Duration.create("5 seconds")); + } + + //this is just to make the test below a tiny fraction nicer + private ActorSystem getContext() { + return system; + } + + static + //#creating-indirectly + class DependencyInjector implements IndirectActorProducer { + final Object applicationContext; + final String beanName; + + public DependencyInjector(Object applicationContext, String beanName) { + this.applicationContext = applicationContext; + this.beanName = beanName; + } + + @Override + public Class actorClass() { + return TheActor.class; + } + + @Override + public TheActor produce() { + TheActor result; + //#obtain-fresh-Actor-instance-from-DI-framework + result = new TheActor((String) applicationContext); + //#obtain-fresh-Actor-instance-from-DI-framework + return result; + } + } + //#creating-indirectly + + @Test + public void indirectActorOf() { + final String applicationContext = "..."; + //#creating-indirectly + + final ActorRef myActor = getContext().actorOf( + Props.create(DependencyInjector.class, applicationContext, "TheActor"), + "TheActor"); + //#creating-indirectly + new JavaTestKit(system) { + { + myActor.tell("hello", getRef()); + expectMsgEquals("..."); + } + }; + } + +} diff --git a/akka-docs/rst/java/code/docs/actorlambda/FaultHandlingTest.java b/akka-docs/rst/java/code/docs/actorlambda/FaultHandlingTest.java index debf3352b2..e7750df9ac 100644 --- a/akka-docs/rst/java/code/docs/actorlambda/FaultHandlingTest.java +++ b/akka-docs/rst/java/code/docs/actorlambda/FaultHandlingTest.java @@ -3,37 +3,41 @@ */ package docs.actorlambda; -//#testkit import akka.actor.*; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import docs.AbstractJavaTest; +import java.util.Optional; + +import static akka.pattern.Patterns.ask; + +//#testkit +import akka.testkit.JavaTestKit; +import akka.testkit.TestProbe; +import akka.testkit.ErrorFilter; +import akka.testkit.EventFilter; +import akka.testkit.TestEvent; +import scala.concurrent.duration.Duration; +import static java.util.concurrent.TimeUnit.SECONDS; +import static akka.japi.Util.immutableSeq; +import scala.concurrent.Await; + +//#testkit + +//#supervisor +import akka.japi.pf.DeciderBuilder; import static akka.actor.SupervisorStrategy.resume; import static akka.actor.SupervisorStrategy.restart; import static akka.actor.SupervisorStrategy.stop; import static akka.actor.SupervisorStrategy.escalate; -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; -import scala.concurrent.duration.Duration; -import akka.testkit.TestProbe; -//#testkit -import akka.testkit.ErrorFilter; -import akka.testkit.EventFilter; -import akka.testkit.TestEvent; -import akka.testkit.JavaTestKit; -import static java.util.concurrent.TimeUnit.SECONDS; -import static akka.japi.Util.immutableSeq; -import scala.Option; +//#supervisor import org.junit.Test; import org.junit.BeforeClass; import org.junit.AfterClass; -import scala.runtime.BoxedUnit; //#testkit public class FaultHandlingTest extends AbstractJavaTest { @@ -65,12 +69,13 @@ public class FaultHandlingTest extends AbstractJavaTest { //#strategy - public Supervisor() { - receive(ReceiveBuilder. - match(Props.class, props -> { - sender().tell(context().actorOf(props), self()); - }).build() - ); + @Override + public Receive createReceive() { + return receiveBuilder() + .match(Props.class, props -> { + sender().tell(getContext().actorOf(props), self()); + }) + .build(); } } @@ -95,16 +100,17 @@ public class FaultHandlingTest extends AbstractJavaTest { //#strategy2 - public Supervisor2() { - receive(ReceiveBuilder. - match(Props.class, props -> { - sender().tell(context().actorOf(props), self()); - }).build() - ); + @Override + public Receive createReceive() { + return receiveBuilder() + .match(Props.class, props -> { + sender().tell(getContext().actorOf(props), self()); + }) + .build(); } @Override - public void preRestart(Throwable cause, Option msg) { + public void preRestart(Throwable cause, Optional msg) { // do not kill all children, which is the default here } } @@ -116,12 +122,13 @@ public class FaultHandlingTest extends AbstractJavaTest { public class Child extends AbstractActor { int state = 0; - public Child() { - receive(ReceiveBuilder. - match(Exception.class, exception -> { throw exception; }). - match(Integer.class, i -> state = i). - matchEquals("get", s -> sender().tell(state, self())).build() - ); + @Override + public Receive createReceive() { + return receiveBuilder() + .match(Exception.class, exception -> { throw exception; }) + .match(Integer.class, i -> state = i) + .matchEquals("get", s -> sender().tell(state, self())) + .build(); } } diff --git a/akka-docs/rst/java/code/docs/actorlambda/GraduallyBuiltActor.java b/akka-docs/rst/java/code/docs/actorlambda/GraduallyBuiltActor.java index 49f0a2f321..720dd8cc04 100644 --- a/akka-docs/rst/java/code/docs/actorlambda/GraduallyBuiltActor.java +++ b/akka-docs/rst/java/code/docs/actorlambda/GraduallyBuiltActor.java @@ -15,10 +15,12 @@ import akka.japi.pf.UnitPFBuilder; //#actor public class GraduallyBuiltActor extends AbstractActor { - private final LoggingAdapter log = Logging.getLogger(context().system(), this); + private final LoggingAdapter log = Logging.getLogger(getContext().system(), this); - public GraduallyBuiltActor() { - UnitPFBuilder builder = ReceiveBuilder.create(); + @Override + public Receive createReceive() { + ReceiveBuilder builder = ReceiveBuilder.create(); + builder.match(String.class, s -> { log.info("Received String message: {}", s); //#actor @@ -27,9 +29,12 @@ public class GraduallyBuiltActor extends AbstractActor { //#reply //#actor }); + // do some other stuff in between + builder.matchAny(o -> log.info("received unknown message")); - receive(builder.build()); + + return builder.build(); } } //#actor diff --git a/akka-docs/rst/java/code/docs/actorlambda/InitializationDocTest.java b/akka-docs/rst/java/code/docs/actorlambda/InitializationDocTest.java index f50c38f5f7..9036c821f2 100644 --- a/akka-docs/rst/java/code/docs/actorlambda/InitializationDocTest.java +++ b/akka-docs/rst/java/code/docs/actorlambda/InitializationDocTest.java @@ -8,7 +8,6 @@ import akka.actor.ActorRef; import akka.actor.ActorSystem; 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; @@ -16,7 +15,7 @@ import org.junit.BeforeClass; import org.junit.Test; import scala.concurrent.Await; import scala.concurrent.duration.Duration; - +import java.util.Optional; import java.util.concurrent.TimeUnit; public class InitializationDocTest extends AbstractJavaTest { @@ -32,24 +31,57 @@ public class InitializationDocTest extends AbstractJavaTest { public static void afterClass() throws Exception { Await.ready(system.terminate(), Duration.create("5 seconds")); } + + static public class PreStartInitExample extends AbstractActor { + + @Override + public Receive createReceive() { + return AbstractActor.emptyBehavior(); + } + + //#preStartInit + @Override + public void preStart() { + // Initialize children here + } + + // Overriding postRestart to disable the call to preStart() + // after restarts + @Override + public void postRestart(Throwable reason) { + } + + // The default implementation of preRestart() stops all the children + // of the actor. To opt-out from stopping the children, we + // have to override preRestart() + @Override + public void preRestart(Throwable reason, Optional message) + throws Exception { + // Keep the call to postStop(), but no stopping of children + postStop(); + } + //#preStartInit + + } public static class MessageInitExample extends AbstractActor { private String initializeMe = null; - public MessageInitExample() { - //#messageInit - receive(ReceiveBuilder. - matchEquals("init", m1 -> { - initializeMe = "Up and running"; - context().become(ReceiveBuilder. - matchEquals("U OK?", m2 -> { - sender().tell(initializeMe, self()); - }).build()); - - }).build() - //#messageInit - ); + //#messageInit + @Override + public Receive createReceive() { + return receiveBuilder() + .matchEquals("init", m1 -> { + initializeMe = "Up and running"; + getContext().become(receiveBuilder() + .matchEquals("U OK?", m2 -> { + sender().tell(initializeMe, self()); + }) + .build()); + }) + .build(); } + //#messageInit } public class GenericMessage { @@ -61,24 +93,27 @@ public class InitializationDocTest extends AbstractJavaTest { } public static class GenericActor extends AbstractActor { - public GenericActor() { - receive(ReceiveBuilder.match(GenericMessage.class, (GenericMessage msg) -> { - GenericMessage message = msg; - sender().tell(message.value.toUpperCase(), self()); - - }).build()); - - + @Override + public Receive createReceive() { + return receiveBuilder() + .matchUnchecked(GenericMessage.class, (GenericMessage msg) -> { + GenericMessage message = msg; + sender().tell(message.value.toUpperCase(), self()); + }) + .build(); } } static class GenericActorWithPredicate extends AbstractActor { - public GenericActorWithPredicate() { + @Override + public Receive createReceive() { FI.TypedPredicate> typedPredicate = s -> !s.value.isEmpty(); - receive(ReceiveBuilder.match(GenericMessage.class, typedPredicate, (GenericMessage msg) -> { - sender().tell(msg.value.toUpperCase(), self()); - }).build()); + return receiveBuilder() + .matchUnchecked(GenericMessage.class, typedPredicate, (GenericMessage msg) -> { + sender().tell(msg.value.toUpperCase(), self()); + }) + .build(); } } diff --git a/akka-docs/rst/java/code/docs/actorlambda/MyActor.java b/akka-docs/rst/java/code/docs/actorlambda/MyActor.java index 54d1d0524b..be93290458 100644 --- a/akka-docs/rst/java/code/docs/actorlambda/MyActor.java +++ b/akka-docs/rst/java/code/docs/actorlambda/MyActor.java @@ -8,26 +8,26 @@ package docs.actorlambda; import akka.actor.AbstractActor; import akka.event.Logging; import akka.event.LoggingAdapter; -import akka.japi.pf.ReceiveBuilder; //#imports //#my-actor public class MyActor extends AbstractActor { - private final LoggingAdapter log = Logging.getLogger(context().system(), this); + private final LoggingAdapter log = Logging.getLogger(getContext().system(), this); - public MyActor() { - receive(ReceiveBuilder. - match(String.class, s -> { + @Override + public Receive createReceive() { + return receiveBuilder() + .match(String.class, s -> { log.info("Received String message: {}", s); //#my-actor //#reply sender().tell(s, self()); //#reply //#my-actor - }). - matchAny(o -> log.info("received unknown message")).build() - ); + }) + .matchAny(o -> log.info("received unknown message")) + .build(); } } //#my-actor diff --git a/akka-docs/rst/java/code/docs/actor/MyBoundedUntypedActor.java b/akka-docs/rst/java/code/docs/actorlambda/MyBoundedActor.java similarity index 79% rename from akka-docs/rst/java/code/docs/actor/MyBoundedUntypedActor.java rename to akka-docs/rst/java/code/docs/actorlambda/MyBoundedActor.java index 506f5aea1e..649395e9f5 100644 --- a/akka-docs/rst/java/code/docs/actor/MyBoundedUntypedActor.java +++ b/akka-docs/rst/java/code/docs/actorlambda/MyBoundedActor.java @@ -2,13 +2,13 @@ * Copyright (C) 2009-2017 Lightbend Inc. */ -package docs.actor; +package docs.actorlambda; //#my-bounded-untyped-actor import akka.dispatch.BoundedMessageQueueSemantics; import akka.dispatch.RequiresMessageQueue; -public class MyBoundedUntypedActor extends MyUntypedActor +public class MyBoundedActor extends MyActor implements RequiresMessageQueue { } //#my-bounded-untyped-actor diff --git a/akka-docs/rst/java/code/docs/actorlambda/MyStoppingActor.java b/akka-docs/rst/java/code/docs/actorlambda/MyStoppingActor.java new file mode 100644 index 0000000000..ec00820d79 --- /dev/null +++ b/akka-docs/rst/java/code/docs/actorlambda/MyStoppingActor.java @@ -0,0 +1,29 @@ +/** + * Copyright (C) 2009-2017 Lightbend Inc. + */ +package docs.actorlambda; + +//#my-stopping-actor +import akka.actor.ActorRef; +import akka.actor.AbstractActor; + +public class MyStoppingActor extends AbstractActor { + + ActorRef child = null; + + // ... creation of child ... + + @Override + public Receive createReceive() { + return receiveBuilder() + .matchEquals("interrupt-child", m -> + getContext().stop(child) + ) + .matchEquals("done", m -> + getContext().stop(self()) + ) + .build(); + } +} +//#my-stopping-actor + diff --git a/akka-docs/rst/java/code/docs/actorlambda/SampleActor.java b/akka-docs/rst/java/code/docs/actorlambda/SampleActor.java index 3b4eeb0ab8..c9f232ee28 100644 --- a/akka-docs/rst/java/code/docs/actorlambda/SampleActor.java +++ b/akka-docs/rst/java/code/docs/actorlambda/SampleActor.java @@ -7,30 +7,30 @@ package docs.actorlambda; //#sample-actor import akka.actor.AbstractActor; import akka.japi.pf.ReceiveBuilder; -import scala.PartialFunction; -import scala.runtime.BoxedUnit; public class SampleActor extends AbstractActor { - private PartialFunction guarded = ReceiveBuilder. - match(String.class, s -> s.contains("guard"), s -> { + private Receive guarded = receiveBuilder() + .match(String.class, s -> s.contains("guard"), s -> { sender().tell("contains(guard): " + s, self()); - context().unbecome(); - }).build(); + getContext().unbecome(); + }) + .build(); - public SampleActor() { - receive(ReceiveBuilder. - match(Double.class, d -> { + @Override + public Receive createReceive() { + return receiveBuilder() + .match(Double.class, d -> { sender().tell(d.isNaN() ? 0 : d, self()); - }). - match(Integer.class, i -> { + }) + .match(Integer.class, i -> { sender().tell(i * 10, self()); - }). - match(String.class, s -> s.startsWith("guard"), s -> { + }) + .match(String.class, s -> s.startsWith("guard"), s -> { sender().tell("startsWith(guard): " + s.toUpperCase(), self()); - context().become(guarded, false); - }).build() - ); + getContext().become(guarded, false); + }) + .build(); } } //#sample-actor 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 3815347e7d..423621a146 100644 --- a/akka-docs/rst/java/code/docs/actorlambda/fsm/FSMDocTest.java +++ b/akka-docs/rst/java/code/docs/actorlambda/fsm/FSMDocTest.java @@ -174,7 +174,7 @@ public class FSMDocTest extends AbstractJavaTest { expectMsgEquals(Active); expectMsgEquals(Data.Foo); String msg = expectMsgClass(String.class); - assertTrue(msg.startsWith("LogEntry(SomeState,Foo,Actor[akka://FSMDocTest/system/")); + assertThat(msg, CoreMatchers.startsWith("LogEntry(SomeState,Foo,Actor[akka://FSMDocTest/system/")); }}; } } diff --git a/akka-docs/rst/java/code/docs/actorlambda/japi/FaultHandlingDocSample.java b/akka-docs/rst/java/code/docs/actorlambda/japi/FaultHandlingDocSample.java index 724fb1297b..e806d86cad 100644 --- a/akka-docs/rst/java/code/docs/actorlambda/japi/FaultHandlingDocSample.java +++ b/akka-docs/rst/java/code/docs/actorlambda/japi/FaultHandlingDocSample.java @@ -67,24 +67,24 @@ public class FaultHandlingDocSample { public void preStart() { // If we don't get any progress within 15 seconds then the service // is unavailable - context().setReceiveTimeout(Duration.create("15 seconds")); + getContext().setReceiveTimeout(Duration.create("15 seconds")); } - public Listener() { - receive(LoggingReceive.create(ReceiveBuilder. + @Override + public Receive createReceive() { + return LoggingReceive.create(receiveBuilder(). match(Progress.class, progress -> { log().info("Current progress: {} %", progress.percent); if (progress.percent >= 100.0) { log().info("That's all, shutting down"); - context().system().terminate(); + getContext().system().terminate(); } }). matchEquals(ReceiveTimeout.getInstance(), x -> { // No progress within 15 seconds, ServiceUnavailable log().error("Shutting down due to unavailable service"); - context().system().terminate(); - }).build(), context() - )); + getContext().system().terminate(); + }).build(), getContext()); } } @@ -119,7 +119,7 @@ public class FaultHandlingDocSample { // The sender of the initial Start message will continuously be notified // about progress ActorRef progressListener; - final ActorRef counterService = context().actorOf( + final ActorRef counterService = getContext().actorOf( Props.create(CounterService.class), "counter"); final int totalCount = 51; @@ -134,13 +134,14 @@ public class FaultHandlingDocSample { return strategy; } - public Worker() { - receive(LoggingReceive.create(ReceiveBuilder. + @Override + public Receive createReceive() { + return LoggingReceive.create(receiveBuilder(). matchEquals(Start, x -> progressListener == null, x -> { progressListener = sender(); - context().system().scheduler().schedule( + getContext().system().scheduler().schedule( Duration.Zero(), Duration.create(1, "second"), self(), Do, - context().dispatcher(), null + getContext().dispatcher(), null ); }). matchEquals(Do, x -> { @@ -154,10 +155,9 @@ public class FaultHandlingDocSample { public Progress apply(CurrentCount c) { return new Progress(100.0 * c.count / totalCount); } - }, context().dispatcher()), context().dispatcher()) + }, getContext().dispatcher()), getContext().dispatcher()) .to(progressListener); - }).build(), context()) - ); + }).build(), getContext()); } } @@ -254,7 +254,7 @@ public class FaultHandlingDocSample { * when it has been terminated. */ void initStorage() { - storage = context().watch(context().actorOf( + storage = getContext().watch(getContext().actorOf( Props.create(Storage.class), "storage")); // Tell the counter, if any, to use the new storage if (counter != null) @@ -263,12 +263,13 @@ public class FaultHandlingDocSample { storage.tell(new Get(key), self()); } - public CounterService() { - receive(LoggingReceive.create(ReceiveBuilder. + @Override + public Receive createReceive() { + return LoggingReceive.create(receiveBuilder(). match(Entry.class, entry -> entry.key.equals(key) && counter == null, entry -> { // Reply from Storage of the initial value, now we can create the Counter final long value = entry.value; - counter = context().actorOf(Props.create(Counter.class, key, value)); + counter = getContext().actorOf(Props.create(Counter.class, key, value)); // Tell the counter to use current storage counter.tell(new UseStorage(storage), self()); // and send the buffered backlog to the counter @@ -290,15 +291,14 @@ public class FaultHandlingDocSample { // Tell the counter that there is no storage for the moment counter.tell(new UseStorage(null), self()); // Try to re-establish storage after while - context().system().scheduler().scheduleOnce( + getContext().system().scheduler().scheduleOnce( Duration.create(10, "seconds"), self(), Reconnect, - context().dispatcher(), null); + getContext().dispatcher(), null); }). matchEquals(Reconnect, o -> { // Re-establish storage after the scheduled delay initStorage(); - }).build(), context()) - ); + }).build(), getContext()); } void forwardOrPlaceInBacklog(Object msg) { @@ -311,7 +311,7 @@ public class FaultHandlingDocSample { " lack of initial value"); backlog.add(new SenderMsgPair(sender(), msg)); } else { - counter.forward(msg, context()); + counter.forward(msg, getContext()); } } } @@ -345,8 +345,11 @@ public class FaultHandlingDocSample { public Counter(String key, long initialValue) { this.key = key; this.count = initialValue; - - receive(LoggingReceive.create(ReceiveBuilder. + } + + @Override + public Receive createReceive() { + return LoggingReceive.create(receiveBuilder(). match(UseStorage.class, useStorage -> { storage = useStorage.storage; storeCount(); @@ -357,8 +360,7 @@ public class FaultHandlingDocSample { }). matchEquals(GetCurrentCount, gcc -> { sender().tell(new CurrentCount(key, count), self()); - }).build(), context()) - ); + }).build(), getContext()); } void storeCount() { @@ -430,8 +432,9 @@ public class FaultHandlingDocSample { final DummyDB db = DummyDB.instance; - public Storage() { - receive(LoggingReceive.create(ReceiveBuilder. + @Override + public Receive createReceive() { + return LoggingReceive.create(receiveBuilder(). match(Store.class, store -> { db.save(store.entry.key, store.entry.value); }). @@ -439,8 +442,7 @@ public class FaultHandlingDocSample { Long value = db.load(get.key); sender().tell(new Entry(get.key, value == null ? Long.valueOf(0L) : value), self()); - }).build(), context()) - ); + }).build(), getContext()); } } diff --git a/akka-docs/rst/java/code/docs/camel/Consumer2.java b/akka-docs/rst/java/code/docs/camel/Consumer2.java index 7f1836a9e1..9ae8528937 100644 --- a/akka-docs/rst/java/code/docs/camel/Consumer2.java +++ b/akka-docs/rst/java/code/docs/camel/Consumer2.java @@ -12,7 +12,7 @@ public class Consumer2 extends UntypedConsumerActor { if (message instanceof CamelMessage) { CamelMessage camelMessage = (CamelMessage) message; String body = camelMessage.getBodyAs(String.class, getCamelContext()); - getSender().tell(String.format("Received message: %s",body), getSelf()); + sender().tell(String.format("Received message: %s",body), self()); } else unhandled(message); } diff --git a/akka-docs/rst/java/code/docs/camel/Consumer3.java b/akka-docs/rst/java/code/docs/camel/Consumer3.java index 95c24f2f82..46516fae5d 100644 --- a/akka-docs/rst/java/code/docs/camel/Consumer3.java +++ b/akka-docs/rst/java/code/docs/camel/Consumer3.java @@ -18,12 +18,12 @@ public class Consumer3 extends UntypedConsumerActor{ public void onReceive(Object message) { if (message instanceof CamelMessage) { - getSender().tell(Ack.getInstance(), getSelf()); + sender().tell(Ack.getInstance(), self()); // on success // .. Exception someException = new Exception("e1"); // on failure - getSender().tell(new Status.Failure(someException), getSelf()); + sender().tell(new Status.Failure(someException), self()); } else unhandled(message); } diff --git a/akka-docs/rst/java/code/docs/camel/Consumer4.java b/akka-docs/rst/java/code/docs/camel/Consumer4.java index a41eba3869..8b1a0ac212 100644 --- a/akka-docs/rst/java/code/docs/camel/Consumer4.java +++ b/akka-docs/rst/java/code/docs/camel/Consumer4.java @@ -24,7 +24,7 @@ public class Consumer4 extends UntypedConsumerActor { if (message instanceof CamelMessage) { CamelMessage camelMessage = (CamelMessage) message; String body = camelMessage.getBodyAs(String.class, getCamelContext()); - getSender().tell(String.format("Hello %s",body), getSelf()); + sender().tell(String.format("Hello %s",body), self()); } else unhandled(message); } diff --git a/akka-docs/rst/java/code/docs/camel/ErrorThrowingConsumer.java b/akka-docs/rst/java/code/docs/camel/ErrorThrowingConsumer.java index 1cec167414..198488d2a6 100644 --- a/akka-docs/rst/java/code/docs/camel/ErrorThrowingConsumer.java +++ b/akka-docs/rst/java/code/docs/camel/ErrorThrowingConsumer.java @@ -47,7 +47,7 @@ public class ErrorThrowingConsumer extends UntypedConsumerActor{ @Override public void preRestart(Throwable reason, Option message) { - getSender().tell(new Status.Failure(reason), getSelf()); + sender().tell(new Status.Failure(reason), self()); } } //#ErrorThrowingConsumer \ No newline at end of file diff --git a/akka-docs/rst/java/code/docs/camel/MyActor.java b/akka-docs/rst/java/code/docs/camel/MyActor.java index cb5432a467..5fbbdb5b28 100644 --- a/akka-docs/rst/java/code/docs/camel/MyActor.java +++ b/akka-docs/rst/java/code/docs/camel/MyActor.java @@ -1,11 +1,11 @@ package docs.camel; //#ProducerTemplate -import akka.actor.UntypedActor; +import akka.actor.UntypedAbstractActor; import akka.camel.Camel; import akka.camel.CamelExtension; import org.apache.camel.ProducerTemplate; -public class MyActor extends UntypedActor { +public class MyActor extends UntypedAbstractActor { public void onReceive(Object message) { Camel camel = CamelExtension.get(getContext().system()); ProducerTemplate template = camel.template(); diff --git a/akka-docs/rst/java/code/docs/camel/RequestBodyActor.java b/akka-docs/rst/java/code/docs/camel/RequestBodyActor.java index dfa5599069..289c59dc80 100644 --- a/akka-docs/rst/java/code/docs/camel/RequestBodyActor.java +++ b/akka-docs/rst/java/code/docs/camel/RequestBodyActor.java @@ -1,15 +1,20 @@ package docs.camel; //#RequestProducerTemplate -import akka.actor.UntypedActor; +import akka.actor.AbstractActor; import akka.camel.Camel; import akka.camel.CamelExtension; import org.apache.camel.ProducerTemplate; -public class RequestBodyActor extends UntypedActor { - public void onReceive(Object message) { - Camel camel = CamelExtension.get(getContext().system()); - ProducerTemplate template = camel.template(); - getSender().tell(template.requestBody("direct:news", message), getSelf()); +public class RequestBodyActor extends AbstractActor { + @Override + public Receive createReceive() { + return receiveBuilder() + .matchAny(message -> { + Camel camel = CamelExtension.get(getContext().system()); + ProducerTemplate template = camel.template(); + sender().tell(template.requestBody("direct:news", message), self()); + }) + .build(); } } //#RequestProducerTemplate \ No newline at end of file diff --git a/akka-docs/rst/java/code/docs/camel/Responder.java b/akka-docs/rst/java/code/docs/camel/Responder.java index 8750aaf700..fddbc247fa 100644 --- a/akka-docs/rst/java/code/docs/camel/Responder.java +++ b/akka-docs/rst/java/code/docs/camel/Responder.java @@ -1,16 +1,16 @@ package docs.camel; //#CustomRoute -import akka.actor.UntypedActor; +import akka.actor.UntypedAbstractActor; import akka.camel.CamelMessage; import akka.dispatch.Mapper; import akka.japi.Function; -public class Responder extends UntypedActor{ +public class Responder extends UntypedAbstractActor{ public void onReceive(Object message) { if (message instanceof CamelMessage) { CamelMessage camelMessage = (CamelMessage) message; - getSender().tell(createResponse(camelMessage), getSelf()); + sender().tell(createResponse(camelMessage), self()); } else unhandled(message); } diff --git a/akka-docs/rst/java/code/docs/camel/ResponseReceiver.java b/akka-docs/rst/java/code/docs/camel/ResponseReceiver.java index d87a731592..e3d7a033ef 100644 --- a/akka-docs/rst/java/code/docs/camel/ResponseReceiver.java +++ b/akka-docs/rst/java/code/docs/camel/ResponseReceiver.java @@ -1,9 +1,9 @@ package docs.camel; //#RouteResponse -import akka.actor.UntypedActor; +import akka.actor.UntypedAbstractActor; import akka.camel.CamelMessage; -public class ResponseReceiver extends UntypedActor{ +public class ResponseReceiver extends UntypedAbstractActor{ public void onReceive(Object message) { if(message instanceof CamelMessage) { // do something with the forwarded response diff --git a/akka-docs/rst/java/code/docs/ddata/DataBot.java b/akka-docs/rst/java/code/docs/ddata/DataBot.java index a8b1f1f08b..49cfade45b 100644 --- a/akka-docs/rst/java/code/docs/ddata/DataBot.java +++ b/akka-docs/rst/java/code/docs/ddata/DataBot.java @@ -5,6 +5,7 @@ package docs.ddata; //#data-bot import static java.util.concurrent.TimeUnit.SECONDS; + import scala.concurrent.duration.Duration; import java.util.concurrent.ThreadLocalRandom; @@ -29,25 +30,26 @@ public class DataBot extends AbstractActor { private static final String TICK = "tick"; - private final LoggingAdapter log = Logging.getLogger(context().system(), this); + private final LoggingAdapter log = Logging.getLogger(getContext().system(), this); private final ActorRef replicator = - DistributedData.get(context().system()).replicator(); - private final Cluster node = Cluster.get(context().system()); + DistributedData.get(getContext().system()).replicator(); + private final Cluster node = Cluster.get(getContext().system()); - private final Cancellable tickTask = context().system().scheduler().schedule( + private final Cancellable tickTask = getContext().system().scheduler().schedule( Duration.create(5, SECONDS), Duration.create(5, SECONDS), self(), TICK, - context().dispatcher(), self()); + getContext().dispatcher(), self()); private final Key> dataKey = ORSetKey.create("key"); @SuppressWarnings("unchecked") - public DataBot() { - receive(ReceiveBuilder + @Override + public Receive createReceive() { + return receiveBuilder() .match(String.class, a -> a.equals(TICK), a -> receiveTick()) .match(Changed.class, c -> c.key().equals(dataKey), c -> receiveChanged((Changed>) c)) .match(UpdateResponse.class, r -> receiveUpdateResoponse()) - .build()); + .build(); } diff --git a/akka-docs/rst/java/code/docs/ddata/DistributedDataDocTest.java b/akka-docs/rst/java/code/docs/ddata/DistributedDataDocTest.java index 372d715c8a..1cb88d4623 100644 --- a/akka-docs/rst/java/code/docs/ddata/DistributedDataDocTest.java +++ b/akka-docs/rst/java/code/docs/ddata/DistributedDataDocTest.java @@ -35,13 +35,11 @@ import akka.testkit.JavaTestKit; import akka.testkit.TestProbe; import akka.serialization.SerializationExtension; +@SuppressWarnings({"unchecked", "unused"}) public class DistributedDataDocTest extends AbstractJavaTest { static ActorSystem system; - void receive(PartialFunction pf) { - } - @BeforeClass public static void setup() { @@ -55,237 +53,261 @@ public class DistributedDataDocTest extends AbstractJavaTest { system = null; } - @Test - public void demonstrateUpdate() { - new JavaTestKit(system) { - { - //#update - final Cluster node = Cluster.get(system); - final ActorRef replicator = DistributedData.get(system).replicator(); + static + //#update + class DemonstrateUpdate extends AbstractActor { + final Cluster node = Cluster.get(getContext().system()); + final ActorRef replicator = + DistributedData.get(getContext().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"); + @Override + public Receive createReceive() { + ReceiveBuilder b = receiveBuilder(); + + b.matchEquals("demonstrate update", msg -> { replicator.tell(new Replicator.Update(counter1Key, PNCounter.create(), - Replicator.writeLocal(), curr -> curr.increment(node, 1)), getTestActor()); + Replicator.writeLocal(), curr -> curr.increment(node, 1)), 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()); + writeTo3, curr -> curr.add("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()); + writeMajority, curr -> curr.add(node, "hello")), self()); final WriteConsistency writeAll = new WriteAll(Duration.create(5, SECONDS)); replicator.tell(new Replicator.Update(activeFlagKey, Flag.create(), - writeAll, curr -> curr.switchOn()), getTestActor()); - //#update - - 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 - }}; + writeAll, curr -> curr.switchOn()), self()); + }); + //#update + + //#update-response1 + b.match(UpdateSuccess.class, a -> a.key().equals(counter1Key), a -> { + // ok + }); + //#update-response1 + + //#update-response2 + b.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 + }); + //#update-response2 + + //#update + return b.build(); + } } + //#update - @Test - public void demonstrateUpdateWithRequestContext() { - new JavaTestKit(system) { - { + static + //#update-request-context + class DemonstrateUpdateWithRequestContext extends AbstractActor { + final Cluster node = Cluster.get(getContext().system()); + final ActorRef replicator = + DistributedData.get(getContext().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"); + + @Override + public Receive createReceive() { + return 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()); + }) - final WriteConsistency writeTwo = new WriteTo(2, Duration.create(3, SECONDS)); - final Key counter1Key = PNCounterKey.create("counter1"); + .match(UpdateSuccess.class, a -> a.key().equals(counter1Key), a -> { + ActorRef replyTo = (ActorRef) a.getRequest().get(); + replyTo.tell("ack", self()); + }) - 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 + .match(UpdateTimeout.class, a -> a.key().equals(counter1Key), a -> { + ActorRef replyTo = (ActorRef) a.getRequest().get(); + replyTo.tell("nack", self()); + }) + + .build(); } - }; } + //#update-request-context - @SuppressWarnings({ "unused", "unchecked" }) - @Test - public void demonstrateGet() { - new JavaTestKit(system) { - { + static + //#get + class DemonstrateGet extends AbstractActor { + final ActorRef replicator = + DistributedData.get(getContext().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"); + + @Override + public Receive createReceive() { + ReceiveBuilder b = receiveBuilder(); + + b.matchEquals("demonstrate get", msg -> { - //#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()), getTestActor()); - - 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), getTestActor()); - - final ReadConsistency readAll = new ReadAll(Duration.create(5, SECONDS)); - replicator.tell(new Replicator.Get(activeFlagKey, - readAll), getTestActor()); + replicator.tell(new Replicator.Get(counter1Key, + Replicator.readLocal()), self()); + + final ReadConsistency readFrom3 = new ReadFrom(3, Duration.create(1, SECONDS)); + replicator.tell(new Replicator.Get>(set1Key, + readFrom3), self()); + + final ReadConsistency readMajority = new ReadMajority(Duration.create(5, SECONDS)); + replicator.tell(new Replicator.Get>(set2Key, + readMajority), self()); + + final ReadConsistency readAll = new ReadAll(Duration.create(5, SECONDS)); + replicator.tell(new Replicator.Get(activeFlagKey, + readAll), self()); + + }); //#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()); + b.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 + }); //#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()); + b.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 + }); //#get-response2 - } - }; + + //#get + return b.build(); + } } + //#get - @SuppressWarnings("unchecked") - @Test - public void demonstrateGetWithRequestContext() { - new JavaTestKit(system) { - { + static + //#get-request-context + class DemonstrateGetWithRequestContext extends AbstractActor { + final ActorRef replicator = + DistributedData.get(getContext().system()).replicator(); + + final ReadConsistency readTwo = new ReadFrom(2, Duration.create(3, SECONDS)); + final Key counter1Key = PNCounterKey.create("counter1"); + + @Override + public Receive createReceive() { + return 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()); + }) - //#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"); + .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()); + }) - 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(GetFailure.class, a -> a.key().equals(counter1Key), a -> { + ActorRef replyTo = (ActorRef) a.getRequest().get(); + replyTo.tell(-1L, 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, 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 + .match(NotFound.class, a -> a.key().equals(counter1Key), a -> { + ActorRef replyTo = (ActorRef) a.getRequest().get(); + replyTo.tell(0L, self()); + }) + + .build(); } - }; } + //#get-request-context - @SuppressWarnings("unchecked") - abstract class MyActor extends AbstractActor { - //#subscribe - final ActorRef replicator = DistributedData.get(system).replicator(); + static +//#subscribe + class DemonstrateSubscribe extends AbstractActor { + final ActorRef replicator = + DistributedData.get(getContext().system()).replicator(); final Key counter1Key = PNCounterKey.create("counter1"); BigInteger currentValue = BigInteger.valueOf(0); - public MyActor() { - receive(ReceiveBuilder. - match(Changed.class, a -> a.key().equals(counter1Key), a -> { + @Override + public Receive createReceive() { + return receiveBuilder() + .match(Changed.class, a -> a.key().equals(counter1Key), a -> { Changed g = a; currentValue = g.dataValue().getValue(); - }). - - match(String.class, a -> a.equals("get-count"), a -> { + }) + .match(String.class, a -> a.equals("get-count"), a -> { // incoming request to retrieve current value of the counter sender().tell(currentValue, sender()); - }).build()); + }) + .build(); } + @Override public void preStart() { // subscribe to changes of the Counter1Key value replicator.tell(new Subscribe(counter1Key, self()), ActorRef.noSender()); } - //#subscribe } + //#subscribe - @Test - public void demonstrateDelete() { - new JavaTestKit(system) { - { - //#delete - final ActorRef replicator = DistributedData.get(system).replicator(); - final Key counter1Key = PNCounterKey.create("counter1"); - final Key> set2Key = ORSetKey.create("set2"); + static + //#delete + class DemonstrateDelete extends AbstractActor { + final ActorRef replicator = + DistributedData.get(getContext().system()).replicator(); + + final Key counter1Key = PNCounterKey.create("counter1"); + final Key> set2Key = ORSetKey.create("set2"); + + @Override + public Receive createReceive() { + return receiveBuilder() + .matchEquals("demonstrate delete", msg -> { - replicator.tell(new Delete(counter1Key, - Replicator.writeLocal()), getTestActor()); - - final WriteConsistency writeMajority = - new WriteMajority(Duration.create(5, SECONDS)); - replicator.tell(new Delete(counter1Key, - writeMajority), getTestActor()); - //#delete - }}; + replicator.tell(new Delete(counter1Key, + Replicator.writeLocal()), self()); + + final WriteConsistency writeMajority = + new WriteMajority(Duration.create(5, SECONDS)); + replicator.tell(new Delete(counter1Key, + writeMajority), self()); + }) + .build(); + } } + //#delete public void demonstratePNCounter() { //#pncounter diff --git a/akka-docs/rst/java/code/docs/dispatcher/DispatcherDocTest.java b/akka-docs/rst/java/code/docs/dispatcher/DispatcherDocTest.java index 1039b67ca2..03354c3209 100644 --- a/akka-docs/rst/java/code/docs/dispatcher/DispatcherDocTest.java +++ b/akka-docs/rst/java/code/docs/dispatcher/DispatcherDocTest.java @@ -8,8 +8,8 @@ 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 docs.actorlambda.MyBoundedActor; +import docs.actorlambda.MyActor; import org.junit.ClassRule; import org.junit.Test; import scala.concurrent.ExecutionContext; @@ -17,7 +17,7 @@ import scala.concurrent.ExecutionContext; //#imports import akka.actor.*; //#imports - +import akka.actor.AbstractActor.Receive; //#imports-prio import akka.event.Logging; import akka.event.LoggingAdapter; @@ -52,7 +52,7 @@ public class DispatcherDocTest extends AbstractJavaTest { public void defineDispatcherInConfig() { //#defining-dispatcher-in-config ActorRef myActor = - system.actorOf(Props.create(MyUntypedActor.class), + system.actorOf(Props.create(MyActor.class), "myactor"); //#defining-dispatcher-in-config } @@ -62,7 +62,7 @@ public class DispatcherDocTest extends AbstractJavaTest { public void defineDispatcherInCode() { //#defining-dispatcher-in-code ActorRef myActor = - system.actorOf(Props.create(MyUntypedActor.class).withDispatcher("my-dispatcher"), + system.actorOf(Props.create(MyActor.class).withDispatcher("my-dispatcher"), "myactor3"); //#defining-dispatcher-in-code } @@ -71,7 +71,7 @@ public class DispatcherDocTest extends AbstractJavaTest { @Test public void defineFixedPoolSizeDispatcher() { //#defining-fixed-pool-size-dispatcher - ActorRef myActor = system.actorOf(Props.create(MyUntypedActor.class) + ActorRef myActor = system.actorOf(Props.create(MyActor.class) .withDispatcher("blocking-io-dispatcher")); //#defining-fixed-pool-size-dispatcher } @@ -80,7 +80,7 @@ public class DispatcherDocTest extends AbstractJavaTest { @Test public void definePinnedDispatcher() { //#defining-pinned-dispatcher - ActorRef myActor = system.actorOf(Props.create(MyUntypedActor.class) + ActorRef myActor = system.actorOf(Props.create(MyActor.class) .withDispatcher("my-pinned-dispatcher")); //#defining-pinned-dispatcher } @@ -99,7 +99,7 @@ public class DispatcherDocTest extends AbstractJavaTest { public void defineMailboxInConfig() { //#defining-mailbox-in-config ActorRef myActor = - system.actorOf(Props.create(MyUntypedActor.class), + system.actorOf(Props.create(MyActor.class), "priomailboxactor"); //#defining-mailbox-in-config } @@ -109,7 +109,7 @@ public class DispatcherDocTest extends AbstractJavaTest { public void defineMailboxInCode() { //#defining-mailbox-in-code ActorRef myActor = - system.actorOf(Props.create(MyUntypedActor.class) + system.actorOf(Props.create(MyActor.class) .withMailbox("prio-mailbox")); //#defining-mailbox-in-code } @@ -118,7 +118,7 @@ public class DispatcherDocTest extends AbstractJavaTest { @Test public void usingARequiredMailbox() { ActorRef myActor = - system.actorOf(Props.create(MyBoundedUntypedActor.class)); + system.actorOf(Props.create(MyBoundedActor.class)); } @Test @@ -126,18 +126,21 @@ public class DispatcherDocTest extends AbstractJavaTest { JavaTestKit probe = new JavaTestKit(system); //#prio-dispatcher - class Demo extends UntypedActor { + class Demo extends AbstractActor { LoggingAdapter log = Logging.getLogger(getContext().system(), this); { for (Object msg : new Object[] { "lowpriority", "lowpriority", "highpriority", "pigdog", "pigdog2", "pigdog3", "highpriority", PoisonPill.getInstance() }) { - getSelf().tell(msg, getSelf()); + self().tell(msg, self()); } } - public void onReceive(Object message) { - log.info(message.toString()); + @Override + public Receive createReceive() { + return receiveBuilder().matchAny(message -> { + log.info(message.toString()); + }).build(); } } @@ -166,17 +169,20 @@ public class DispatcherDocTest extends AbstractJavaTest { JavaTestKit probe = new JavaTestKit(system); //#control-aware-dispatcher - class Demo extends UntypedActor { + class Demo extends AbstractActor { LoggingAdapter log = Logging.getLogger(getContext().system(), this); { for (Object msg : new Object[] { "foo", "bar", new MyControlMessage(), PoisonPill.getInstance() }) { - getSelf().tell(msg, getSelf()); + self().tell(msg, self()); } } - public void onReceive(Object message) { - log.info(message.toString()); + @Override + public Receive createReceive() { + return receiveBuilder().matchAny(message -> { + log.info(message.toString()); + }).build(); } } @@ -226,18 +232,18 @@ public class DispatcherDocTest extends AbstractJavaTest { @Test public void requiredMailboxDispatcher() throws Exception { - ActorRef myActor = system.actorOf(Props.create(MyUntypedActor.class) + ActorRef myActor = system.actorOf(Props.create(MyActor.class) .withDispatcher("custom-dispatcher")); } static //#require-mailbox-on-actor - public class MySpecialActor extends UntypedActor implements + public class MySpecialActor extends AbstractActor implements RequiresMessageQueue { //#require-mailbox-on-actor @Override - public void onReceive(Object message) throws Exception { - unhandled(message); + public Receive createReceive() { + return AbstractActor.emptyBehavior(); } //#require-mailbox-on-actor // ... diff --git a/akka-docs/rst/java/code/docs/event/LoggingDocTest.java b/akka-docs/rst/java/code/docs/event/LoggingDocTest.java index b8e3909159..5379fa756e 100644 --- a/akka-docs/rst/java/code/docs/event/LoggingDocTest.java +++ b/akka-docs/rst/java/code/docs/event/LoggingDocTest.java @@ -22,7 +22,7 @@ import akka.event.Logging.Debug; import docs.AbstractJavaTest; import org.junit.Test; import akka.testkit.JavaTestKit; -import scala.Option; +import java.util.Optional; //#imports-mdc import akka.event.Logging; @@ -80,15 +80,18 @@ public class LoggingDocTest extends AbstractJavaTest { } } - static class Listener extends UntypedActor { + static class Listener extends AbstractActor { @Override - public void onReceive(Object message) throws Exception { - if (message instanceof Jazz) { - System.out.printf("%s is listening to: %s%n", self().path().name(), message); - } else if (message instanceof Electronic) { - System.out.printf("%s is listening to: %s%n", self().path().name(), message); + public Receive createReceive() { + return receiveBuilder() + .match(Jazz.class, msg -> + System.out.printf("%s is listening to: %s%n", self().path().name(), msg) + ) + .match(Electronic.class, msg -> + System.out.printf("%s is listening to: %s%n", self().path().name(), msg) + ) + .build(); } - } } //#superclass-subscription-eventstream @@ -148,7 +151,7 @@ public class LoggingDocTest extends AbstractJavaTest { } //#my-actor - class MyActor extends UntypedActor { + class MyActor extends AbstractActor { LoggingAdapter log = Logging.getLogger(getContext().system(), this); @Override @@ -157,68 +160,86 @@ public class LoggingDocTest extends AbstractJavaTest { } @Override - public void preRestart(Throwable reason, Option message) { + public void preRestart(Throwable reason, Optional message) { log.error(reason, "Restarting due to [{}] when processing [{}]", - reason.getMessage(), message.isDefined() ? message.get() : ""); + reason.getMessage(), message.isPresent() ? message.get() : ""); } - public void onReceive(Object message) { - if (message.equals("test")) { - log.info("Received test"); - } else { - log.warning("Received unknown message: {}", message); - } + @Override + public Receive createReceive() { + return receiveBuilder() + .matchEquals("test", msg -> + log.info("Received test") + ) + .matchAny(msg -> + log.warning("Received unknown message: {}", msg) + ) + .build(); } } //#my-actor //#mdc-actor - class MdcActor extends UntypedActor { + class MdcActor extends AbstractActor { final DiagnosticLoggingAdapter log = Logging.getLogger(this); - public void onReceive(Object message) { + @Override + public Receive createReceive() { + return receiveBuilder() + .matchAny(msg -> { + Map mdc; + mdc = new HashMap(); + mdc.put("requestId", 1234); + mdc.put("visitorId", 5678); + log.setMDC(mdc); - Map mdc; - mdc = new HashMap(); - mdc.put("requestId", 1234); - mdc.put("visitorId", 5678); - log.setMDC(mdc); + log.info("Starting new request"); - log.info("Starting new request"); - - log.clearMDC(); + log.clearMDC(); + }) + .build(); } } //#mdc-actor //#my-event-listener - class MyEventListener extends UntypedActor { - public void onReceive(Object message) { - if (message instanceof InitializeLogger) { - getSender().tell(Logging.loggerInitialized(), getSelf()); - } else if (message instanceof Error) { - // ... - } else if (message instanceof Warning) { - // ... - } else if (message instanceof Info) { - // ... - } else if (message instanceof Debug) { - // ... - } + class MyEventListener extends AbstractActor { + @Override + public Receive createReceive() { + return receiveBuilder() + .match(InitializeLogger.class, msg -> { + sender().tell(Logging.loggerInitialized(), self()); + }) + .match(Error.class, msg -> { + // ... + }) + .match(Warning.class, msg -> { + // ... + }) + .match(Info.class, msg -> { + // ... + }) + .match(Debug.class, msg -> { + // ... + }) + .build(); } } //#my-event-listener static //#deadletter-actor - public class DeadLetterActor extends UntypedActor { - public void onReceive(Object message) { - if (message instanceof DeadLetter) { - System.out.println(message); - } + public class DeadLetterActor extends AbstractActor { + @Override + public Receive createReceive() { + return receiveBuilder() + .match(DeadLetter.class, msg -> { + System.out.println(msg); + }) + .build(); } } //#deadletter-actor diff --git a/akka-docs/rst/java/code/docs/extension/ExtensionDocTest.java b/akka-docs/rst/java/code/docs/extension/ExtensionDocTest.java index e6f48815ea..7f046526f4 100644 --- a/akka-docs/rst/java/code/docs/extension/ExtensionDocTest.java +++ b/akka-docs/rst/java/code/docs/extension/ExtensionDocTest.java @@ -57,11 +57,16 @@ public class ExtensionDocTest extends AbstractJavaTest { static //#extension-usage-actor - public class MyActor extends UntypedActor { - public void onReceive(Object msg) { - // typically you would use static import of the - // CountExtension.CountExtensionProvider field - CountExtension.CountExtensionProvider.get(getContext().system()).increment(); + public class MyActor extends AbstractActor { + @Override + public Receive createReceive() { + return receiveBuilder() + .matchAny(msg -> { + // typically you would use static import of the + // CountExtension.CountExtensionProvider field + CountExtension.CountExtensionProvider.get(getContext().system()).increment(); + }) + .build(); } } diff --git a/akka-docs/rst/java/code/docs/extension/SettingsExtensionDocTest.java b/akka-docs/rst/java/code/docs/extension/SettingsExtensionDocTest.java index a079db4436..605335b819 100644 --- a/akka-docs/rst/java/code/docs/extension/SettingsExtensionDocTest.java +++ b/akka-docs/rst/java/code/docs/extension/SettingsExtensionDocTest.java @@ -9,14 +9,14 @@ 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; //#imports -import akka.actor.UntypedActor; +import docs.AbstractJavaTest; +import akka.actor.AbstractActor; import org.junit.Test; public class SettingsExtensionDocTest extends AbstractJavaTest { @@ -60,7 +60,7 @@ public class SettingsExtensionDocTest extends AbstractJavaTest { static //#extension-usage-actor - public class MyActor extends UntypedActor { + public class MyActor extends AbstractActor { // typically you would use static import of the Settings.SettingsProvider field final SettingsImpl settings = Settings.SettingsProvider.get(getContext().system()); @@ -73,7 +73,9 @@ public class SettingsExtensionDocTest extends AbstractJavaTest { return new Connection(); } - public void onReceive(Object msg) { + @Override + public Receive createReceive() { + return AbstractActor.emptyBehavior(); } //#extension-usage-actor } diff --git a/akka-docs/rst/java/code/docs/future/FutureDocTest.java b/akka-docs/rst/java/code/docs/future/FutureDocTest.java index bcd3750ced..9e42ccdbff 100644 --- a/akka-docs/rst/java/code/docs/future/FutureDocTest.java +++ b/akka-docs/rst/java/code/docs/future/FutureDocTest.java @@ -65,7 +65,7 @@ import org.junit.Test; import akka.testkit.AkkaSpec; import akka.actor.Status.Failure; import akka.actor.ActorSystem; -import akka.actor.UntypedActor; +import akka.actor.AbstractActor; import akka.actor.ActorRef; import akka.actor.Props; import akka.pattern.Patterns; @@ -656,20 +656,21 @@ public class FutureDocTest extends AbstractJavaTest { - public static class MyActor extends UntypedActor { - public void onReceive(Object message) { - if (message instanceof String) { - getSender().tell(((String) message).toUpperCase(), getSelf()); - } else if (message instanceof Integer) { - int i = ((Integer) message).intValue(); - if (i < 0) { - getSender().tell(new Failure(new ArithmeticException("Negative values not supported")), getSelf()); - } else { - getSender().tell(i, getSelf()); - } - } else { - unhandled(message); - } + public static class MyActor extends AbstractActor { + @Override + public Receive createReceive() { + return receiveBuilder() + .match(String.class, msg -> { + sender().tell(msg.toUpperCase(), self()); + }) + .match(Integer.class, i -> { + if (i < 0) { + sender().tell(new Failure(new ArithmeticException("Negative values not supported")), self()); + } else { + sender().tell(i, self()); + } + }) + .build(); } } } diff --git a/akka-docs/rst/java/code/docs/io/IODocTest.java b/akka-docs/rst/java/code/docs/io/IODocTest.java index 5039983f34..ac3cb3a453 100644 --- a/akka-docs/rst/java/code/docs/io/IODocTest.java +++ b/akka-docs/rst/java/code/docs/io/IODocTest.java @@ -8,7 +8,7 @@ import org.junit.BeforeClass; import org.junit.Test; import akka.actor.ActorSystem; -import akka.actor.UntypedActor; +import akka.actor.AbstractActor; //#imports import java.net.InetSocketAddress; import java.util.ArrayList; @@ -24,65 +24,63 @@ import akka.util.ByteString; public class IODocTest { - static public class Demo extends UntypedActor { + static public class Demo extends AbstractActor { ActorRef connectionActor = null; - ActorRef listener = getSelf(); + ActorRef listener = self(); @Override - public void onReceive(Object msg) { - if ("connect".equals(msg)) { - //#manager - final ActorRef tcp = Tcp.get(system).manager(); - //#manager - //#connect - final InetSocketAddress remoteAddr = new InetSocketAddress("127.0.0.1", - 12345); - tcp.tell(TcpMessage.connect(remoteAddr), getSelf()); - //#connect - //#connect-with-options - final InetSocketAddress localAddr = new InetSocketAddress("127.0.0.1", - 1234); - final List options = new ArrayList(); - options.add(TcpSO.keepAlive(true)); - tcp.tell(TcpMessage.connect(remoteAddr, localAddr, options, null, false), getSelf()); - //#connect-with-options - } else - //#connected - if (msg instanceof Tcp.Connected) { - final Tcp.Connected conn = (Tcp.Connected) msg; - connectionActor = getSender(); - connectionActor.tell(TcpMessage.register(listener), getSelf()); - } - //#connected - else - //#received - if (msg instanceof Tcp.Received) { - final Tcp.Received recv = (Tcp.Received) msg; - final ByteString data = recv.data(); - // and do something with the received data ... - } else if (msg instanceof Tcp.CommandFailed) { - final Tcp.CommandFailed failed = (Tcp.CommandFailed) msg; - final Tcp.Command command = failed.cmd(); - // react to failed connect, bind, write, etc. - } else if (msg instanceof Tcp.ConnectionClosed) { - final Tcp.ConnectionClosed closed = (Tcp.ConnectionClosed) msg; - if (closed.isAborted()) { - // handle close reasons like this - } - } - //#received - else - if ("bind".equals(msg)) { - final ActorRef handler = getSelf(); - //#bind - final ActorRef tcp = Tcp.get(system).manager(); - final InetSocketAddress localAddr = new InetSocketAddress("127.0.0.1", - 1234); - final List options = new ArrayList(); - options.add(TcpSO.reuseAddress(true)); - tcp.tell(TcpMessage.bind(handler, localAddr, 10, options, false), getSelf()); - //#bind - } + public Receive createReceive() { + return receiveBuilder() + .matchEquals("connect", msg -> { + //#manager + final ActorRef tcp = Tcp.get(system).manager(); + //#manager + //#connect + final InetSocketAddress remoteAddr = new InetSocketAddress("127.0.0.1", + 12345); + tcp.tell(TcpMessage.connect(remoteAddr), self()); + //#connect + //#connect-with-options + final InetSocketAddress localAddr = new InetSocketAddress("127.0.0.1", + 1234); + final List options = new ArrayList(); + options.add(TcpSO.keepAlive(true)); + tcp.tell(TcpMessage.connect(remoteAddr, localAddr, options, null, false), self()); + //#connect-with-options + }) + //#connected + .match(Tcp.Connected.class, conn -> { + connectionActor = sender(); + connectionActor.tell(TcpMessage.register(listener), self()); + }) + //#connected + //#received + .match(Tcp.Received.class, recv -> { + final ByteString data = recv.data(); + // and do something with the received data ... + }) + .match(Tcp.CommandFailed.class, failed -> { + final Tcp.Command command = failed.cmd(); + // react to failed connect, bind, write, etc. + }) + .match(Tcp.ConnectionClosed.class, closed -> { + if (closed.isAborted()) { + // handle close reasons like this + } + }) + //#received + .matchEquals("bind", msg -> { + final ActorRef handler = self(); + //#bind + final ActorRef tcp = Tcp.get(system).manager(); + final InetSocketAddress localAddr = new InetSocketAddress("127.0.0.1", + 1234); + final List options = new ArrayList(); + options.add(TcpSO.reuseAddress(true)); + tcp.tell(TcpMessage.bind(handler, localAddr, 10, options, false), self()); + //#bind + }) + .build(); } } diff --git a/akka-docs/rst/java/code/docs/io/JavaReadBackPressure.java b/akka-docs/rst/java/code/docs/io/JavaReadBackPressure.java index c8c1b06520..051d22fefe 100644 --- a/akka-docs/rst/java/code/docs/io/JavaReadBackPressure.java +++ b/akka-docs/rst/java/code/docs/io/JavaReadBackPressure.java @@ -25,14 +25,14 @@ public class JavaReadBackPressure { //#pull-accepting public void onReceive(Object message) throws Exception { if (message instanceof Tcp.Bound) { - listener = getSender(); + listener = sender(); // Accept connections one by one - listener.tell(TcpMessage.resumeAccepting(1), getSelf()); + listener.tell(TcpMessage.resumeAccepting(1), self()); } else if (message instanceof Tcp.Connected) { - ActorRef handler = getContext().actorOf(Props.create(PullEcho.class, getSender())); - getSender().tell(TcpMessage.register(handler), getSelf()); + ActorRef handler = getContext().actorOf(Props.create(PullEcho.class, sender())); + sender().tell(TcpMessage.register(handler), self()); // Resume accepting connections - listener.tell(TcpMessage.resumeAccepting(1), getSelf()); + listener.tell(TcpMessage.resumeAccepting(1), self()); } } //#pull-accepting @@ -43,8 +43,8 @@ public class JavaReadBackPressure { tcp = Tcp.get(getContext().system()).manager(); final List options = new ArrayList(); tcp.tell( - TcpMessage.bind(getSelf(), new InetSocketAddress("localhost", 0), 100, options, true), - getSelf() + TcpMessage.bind(self(), new InetSocketAddress("localhost", 0), 100, options, true), + self() ); //#pull-mode-bind } @@ -54,7 +54,7 @@ public class JavaReadBackPressure { final List options = new ArrayList(); tcp.tell( TcpMessage.connect(new InetSocketAddress("localhost", 3000), null, options, null, true), - getSelf() + self() ); //#pull-mode-connect } @@ -73,16 +73,16 @@ public class JavaReadBackPressure { //#pull-reading-echo @Override public void preStart() throws Exception { - connection.tell(TcpMessage.resumeReading(), getSelf()); + connection.tell(TcpMessage.resumeReading(), self()); } @Override public void onReceive(Object message) throws Exception { if (message instanceof Tcp.Received) { ByteString data = ((Tcp.Received) message).data(); - connection.tell(TcpMessage.write(data, new Ack()), getSelf()); + connection.tell(TcpMessage.write(data, new Ack()), self()); } else if (message instanceof Ack) { - connection.tell(TcpMessage.resumeReading(), getSelf()); + connection.tell(TcpMessage.resumeReading(), self()); } } //#pull-reading-echo diff --git a/akka-docs/rst/java/code/docs/io/JavaUdpMulticast.java b/akka-docs/rst/java/code/docs/io/JavaUdpMulticast.java index b5dd9b3a40..2817d33898 100644 --- a/akka-docs/rst/java/code/docs/io/JavaUdpMulticast.java +++ b/akka-docs/rst/java/code/docs/io/JavaUdpMulticast.java @@ -73,7 +73,7 @@ public class JavaUdpMulticast { final ActorRef mgr = Udp.get(getContext().system()).getManager(); // listen for datagrams on this address InetSocketAddress endpoint = new InetSocketAddress(port); - mgr.tell(UdpMessage.bind(getSelf(), endpoint, options), getSelf()); + mgr.tell(UdpMessage.bind(self(), endpoint, options), self()); //#bind } @@ -82,12 +82,12 @@ public class JavaUdpMulticast { if (msg instanceof Udp.Bound) { final Udp.Bound b = (Udp.Bound) msg; log.info("Bound to {}", b.localAddress()); - sink.tell(b, getSelf()); + sink.tell(b, self()); } else if (msg instanceof Udp.Received) { final Udp.Received r = (Udp.Received) msg; final String txt = r.data().decodeString("utf-8"); log.info("Received '{}' from {}", txt, r.sender()); - sink.tell(txt, getSelf()); + sink.tell(txt, self()); } else unhandled(msg); } } @@ -110,7 +110,7 @@ public class JavaUdpMulticast { options.add(new Inet6ProtocolFamily()); final ActorRef mgr = Udp.get(getContext().system()).getManager(); - mgr.tell(UdpMessage.simpleSender(options), getSelf()); + mgr.tell(UdpMessage.simpleSender(options), self()); } @Override @@ -118,7 +118,7 @@ public class JavaUdpMulticast { if (msg instanceof Udp.SimpleSenderReady) { InetSocketAddress remote = new InetSocketAddress(group + "%" + iface, port); log.info("Sending message to " + remote); - getSender().tell(UdpMessage.send(ByteString.fromString(message), remote), getSelf()); + sender().tell(UdpMessage.send(ByteString.fromString(message), remote), self()); } else unhandled(msg); } } diff --git a/akka-docs/rst/java/code/docs/io/UdpConnectedDocTest.java b/akka-docs/rst/java/code/docs/io/UdpConnectedDocTest.java index d2da3cf326..f9ad8d8642 100644 --- a/akka-docs/rst/java/code/docs/io/UdpConnectedDocTest.java +++ b/akka-docs/rst/java/code/docs/io/UdpConnectedDocTest.java @@ -26,8 +26,8 @@ public class UdpConnectedDocTest { static public class Demo extends UntypedActor { ActorRef connectionActor = null; - ActorRef handler = getSelf(); - ActorSystem system = context().system(); + ActorRef handler = self(); + ActorSystem system = getContext().system(); @Override public void onReceive(Object msg) { @@ -38,7 +38,7 @@ public class UdpConnectedDocTest { //#connect final InetSocketAddress remoteAddr = new InetSocketAddress("127.0.0.1", 12345); - udp.tell(UdpConnectedMessage.connect(handler, remoteAddr), getSelf()); + udp.tell(UdpConnectedMessage.connect(handler, remoteAddr), self()); //#connect //#connect-with-options final InetSocketAddress localAddr = @@ -46,13 +46,13 @@ public class UdpConnectedDocTest { final List options = new ArrayList(); options.add(UdpSO.broadcast(true)); - udp.tell(UdpConnectedMessage.connect(handler, remoteAddr, localAddr, options), getSelf()); + udp.tell(UdpConnectedMessage.connect(handler, remoteAddr, localAddr, options), self()); //#connect-with-options } else //#connected if (msg instanceof UdpConnected.Connected) { final UdpConnected.Connected conn = (UdpConnected.Connected) msg; - connectionActor = getSender(); // Save the worker ref for later use + connectionActor = sender(); // Save the worker ref for later use } //#connected else @@ -73,7 +73,7 @@ public class UdpConnectedDocTest { if ("send".equals(msg)) { ByteString data = ByteString.empty(); //#send - connectionActor.tell(UdpConnectedMessage.send(data), getSelf()); + connectionActor.tell(UdpConnectedMessage.send(data), self()); //#send } } diff --git a/akka-docs/rst/java/code/docs/io/UdpDocTest.java b/akka-docs/rst/java/code/docs/io/UdpDocTest.java index b8e76e12de..7b5cd2d540 100644 --- a/akka-docs/rst/java/code/docs/io/UdpDocTest.java +++ b/akka-docs/rst/java/code/docs/io/UdpDocTest.java @@ -29,15 +29,15 @@ public class UdpDocTest { // request creation of a SimpleSender final ActorRef mgr = Udp.get(getContext().system()).getManager(); - mgr.tell(UdpMessage.simpleSender(), getSelf()); + mgr.tell(UdpMessage.simpleSender(), self()); } @Override public void onReceive(Object msg) { if (msg instanceof Udp.SimpleSenderReady) { - getContext().become(ready(getSender())); + getContext().become(ready(sender())); //#sender - getSender().tell(UdpMessage.send(ByteString.fromString("hello"), remote), getSelf()); + sender().tell(UdpMessage.send(ByteString.fromString("hello"), remote), self()); //#sender } else unhandled(msg); } @@ -48,10 +48,10 @@ public class UdpDocTest { public void apply(Object msg) throws Exception { if (msg instanceof String) { final String str = (String) msg; - send.tell(UdpMessage.send(ByteString.fromString(str), remote), getSelf()); + send.tell(UdpMessage.send(ByteString.fromString(str), remote), self()); //#sender if (str.equals("world")) { - send.tell(PoisonPill.getInstance(), getSelf()); + send.tell(PoisonPill.getInstance(), self()); } //#sender @@ -72,8 +72,8 @@ public class UdpDocTest { // request creation of a bound listen socket final ActorRef mgr = Udp.get(getContext().system()).getManager(); mgr.tell( - UdpMessage.bind(getSelf(), new InetSocketAddress("localhost", 0)), - getSelf()); + UdpMessage.bind(self(), new InetSocketAddress("localhost", 0)), + self()); } @Override @@ -81,9 +81,9 @@ public class UdpDocTest { if (msg instanceof Udp.Bound) { final Udp.Bound b = (Udp.Bound) msg; //#listener - nextActor.tell(b.localAddress(), getSender()); + nextActor.tell(b.localAddress(), sender()); //#listener - getContext().become(ready(getSender())); + getContext().become(ready(sender())); } else unhandled(msg); } @@ -94,19 +94,19 @@ public class UdpDocTest { if (msg instanceof Udp.Received) { final Udp.Received r = (Udp.Received) msg; // echo server example: send back the data - socket.tell(UdpMessage.send(r.data(), r.sender()), getSelf()); + socket.tell(UdpMessage.send(r.data(), r.sender()), self()); // or do some processing and forward it on final Object processed = // parse data etc., e.g. using PipelineStage //#listener r.data().utf8String(); //#listener - nextActor.tell(processed, getSelf()); + nextActor.tell(processed, self()); } else if (msg.equals(UdpMessage.unbind())) { - socket.tell(msg, getSelf()); + socket.tell(msg, self()); } else if (msg instanceof Udp.Unbound) { - getContext().stop(getSelf()); + getContext().stop(self()); } else unhandled(msg); } @@ -124,17 +124,17 @@ public class UdpDocTest { // create a restricted a.k.a. “connected” socket final ActorRef mgr = UdpConnected.get(getContext().system()).getManager(); - mgr.tell(UdpConnectedMessage.connect(getSelf(), remote), getSelf()); + mgr.tell(UdpConnectedMessage.connect(self(), remote), self()); } @Override public void onReceive(Object msg) { if (msg instanceof UdpConnected.Connected) { - getContext().become(ready(getSender())); + getContext().become(ready(sender())); //#connected - getSender() + sender() .tell(UdpConnectedMessage.send(ByteString.fromString("hello")), - getSelf()); + self()); //#connected } else unhandled(msg); } @@ -150,7 +150,7 @@ public class UdpDocTest { if (r.data().utf8String().equals("hello")) { connection.tell( UdpConnectedMessage.send(ByteString.fromString("world")), - getSelf()); + self()); } // #connected @@ -158,13 +158,13 @@ public class UdpDocTest { final String str = (String) msg; connection .tell(UdpConnectedMessage.send(ByteString.fromString(str)), - getSelf()); + self()); } else if (msg.equals(UdpConnectedMessage.disconnect())) { - connection.tell(msg, getSelf()); + connection.tell(msg, self()); } else if (msg instanceof UdpConnected.Disconnected) { - getContext().stop(getSelf()); + getContext().stop(self()); } else unhandled(msg); } diff --git a/akka-docs/rst/java/code/docs/io/japi/EchoHandler.java b/akka-docs/rst/java/code/docs/io/japi/EchoHandler.java index 5048ac3486..22aab6762a 100644 --- a/akka-docs/rst/java/code/docs/io/japi/EchoHandler.java +++ b/akka-docs/rst/java/code/docs/io/japi/EchoHandler.java @@ -9,7 +9,7 @@ import java.util.LinkedList; import java.util.Queue; import akka.actor.ActorRef; -import akka.actor.UntypedActor; +import akka.actor.AbstractActor; import akka.event.Logging; import akka.event.LoggingAdapter; import akka.io.Tcp.CommandFailed; @@ -19,14 +19,13 @@ import akka.io.Tcp.Received; import akka.io.Tcp.Write; import akka.io.Tcp.WritingResumed; import akka.io.TcpMessage; -import akka.japi.Procedure; import akka.util.ByteString; //#echo-handler -public class EchoHandler extends UntypedActor { +public class EchoHandler extends AbstractActor { final LoggingAdapter log = Logging - .getLogger(getContext().system(), getSelf()); + .getLogger(getContext().system(), self()); final ActorRef connection; final InetSocketAddress remote; @@ -35,6 +34,13 @@ public class EchoHandler extends UntypedActor { public static final long HIGH_WATERMARK = MAX_STORED * 5 / 10; public static final long LOW_WATERMARK = MAX_STORED * 2 / 10; + private long transferred; + private int storageOffset = 0; + private long stored = 0; + private Queue storage = new LinkedList(); + + private boolean suspended = false; + private static class Ack implements Event { public final int ack; public Ack(int ack) { @@ -45,6 +51,8 @@ public class EchoHandler extends UntypedActor { public EchoHandler(ActorRef connection, InetSocketAddress remote) { this.connection = connection; this.remote = remote; + + writing = writing(); // sign death pact: this actor stops when the connection is closed getContext().watch(connection); @@ -52,138 +60,136 @@ public class EchoHandler extends UntypedActor { // start out in optimistic write-through mode getContext().become(writing); } + + @Override + public Receive createReceive() { + return writing; + } - private final Procedure writing = new Procedure() { - @Override - public void apply(Object msg) throws Exception { - if (msg instanceof Received) { - final ByteString data = ((Received) msg).data(); - connection.tell(TcpMessage.write(data, new Ack(currentOffset())), getSelf()); + private final Receive writing; + + private Receive writing() { + return receiveBuilder() + .match(Received.class, msg -> { + final ByteString data = msg.data(); + connection.tell(TcpMessage.write(data, new Ack(currentOffset())), self()); buffer(data); - } else if (msg instanceof Integer) { - acknowledge((Integer) msg); - - } else if (msg instanceof CommandFailed) { - final Write w = (Write) ((CommandFailed) msg).cmd(); - connection.tell(TcpMessage.resumeWriting(), getSelf()); + }) + .match(Integer.class, msg -> { + acknowledge(msg); + }) + .match(CommandFailed.class, msg -> { + final Write w = (Write) msg.cmd(); + connection.tell(TcpMessage.resumeWriting(), self()); getContext().become(buffering((Ack) w.ack())); - - } else if (msg instanceof ConnectionClosed) { - final ConnectionClosed cl = (ConnectionClosed) msg; - if (cl.isPeerClosed()) { + }) + .match(ConnectionClosed.class, msg -> { + if (msg.isPeerClosed()) { if (storage.isEmpty()) { - getContext().stop(getSelf()); + getContext().stop(self()); } else { - getContext().become(closing); + getContext().become(closing()); } } - } - } - }; + }) + .build(); + } //#buffering - protected Procedure buffering(final Ack nack) { - return new Procedure() { + + final static class BufferingState { + int toAck = 10; + boolean peerClosed = false; + } + + protected Receive buffering(final Ack nack) { + final BufferingState state = new BufferingState(); + + return receiveBuilder() + .match(Received.class, msg -> { + buffer(msg.data()); - private int toAck = 10; - private boolean peerClosed = false; + }) + .match(WritingResumed.class, msg -> { + writeFirst(); - @Override - public void apply(Object msg) throws Exception { - if (msg instanceof Received) { - buffer(((Received) msg).data()); + }) + .match(ConnectionClosed.class, msg -> { + if (msg.isPeerClosed()) + state.peerClosed = true; + else + getContext().stop(self()); - } else if (msg instanceof WritingResumed) { - writeFirst(); + }) + .match(Integer.class, ack -> { + acknowledge(ack); - } else if (msg instanceof ConnectionClosed) { - if (((ConnectionClosed) msg).isPeerClosed()) - peerClosed = true; - else - getContext().stop(getSelf()); + if (ack >= nack.ack) { + // otherwise it was the ack of the last successful write - } else if (msg instanceof Integer) { - final int ack = (Integer) msg; - acknowledge(ack); + if (storage.isEmpty()) { + if (state.peerClosed) + getContext().stop(self()); + else + getContext().become(writing); - if (ack >= nack.ack) { - // otherwise it was the ack of the last successful write - - if (storage.isEmpty()) { - if (peerClosed) - getContext().stop(getSelf()); + } else { + if (state.toAck > 0) { + // stay in ACK-based mode for a short while + writeFirst(); + --state.toAck; + } else { + // then return to NACK-based again + writeAll(); + if (state.peerClosed) + getContext().become(closing()); else getContext().become(writing); - - } else { - if (toAck > 0) { - // stay in ACK-based mode for a short while - writeFirst(); - --toAck; - } else { - // then return to NACK-based again - writeAll(); - if (peerClosed) - getContext().become(closing); - else - getContext().become(writing); - } } } } - } - }; + }) + .build(); } //#buffering //#closing - protected Procedure closing = new Procedure() { - @Override - public void apply(Object msg) throws Exception { - if (msg instanceof CommandFailed) { + protected Receive closing() { + return receiveBuilder() + .match(CommandFailed.class, msg -> { // the command can only have been a Write - connection.tell(TcpMessage.resumeWriting(), getSelf()); - getContext().become(closeResend, false); - } else if (msg instanceof Integer) { - acknowledge((Integer) msg); + connection.tell(TcpMessage.resumeWriting(), self()); + getContext().become(closeResend(), false); + }) + .match(Integer.class, msg -> { + acknowledge(msg); if (storage.isEmpty()) - getContext().stop(getSelf()); - } - } - }; + getContext().stop(self()); + }) + .build(); + } - protected Procedure closeResend = new Procedure() { - @Override - public void apply(Object msg) throws Exception { - if (msg instanceof WritingResumed) { + protected Receive closeResend() { + return receiveBuilder() + .match(WritingResumed.class, msg -> { writeAll(); getContext().unbecome(); - } else if (msg instanceof Integer) { - acknowledge((Integer) msg); - } - } - }; + }) + .match(Integer.class, msg -> { + acknowledge(msg); + }) + .build(); + } //#closing //#storage-omitted - @Override - public void onReceive(Object msg) throws Exception { - // this method is not used due to become() - } @Override public void postStop() { log.info("transferred {} bytes from/to [{}]", transferred, remote); } - private long transferred; - private int storageOffset = 0; - private long stored = 0; - private Queue storage = new LinkedList(); - - private boolean suspended = false; - //#helpers protected void buffer(ByteString data) { storage.add(data); @@ -191,11 +197,11 @@ public class EchoHandler extends UntypedActor { if (stored > MAX_STORED) { log.warning("drop connection to [{}] (buffer overrun)", remote); - getContext().stop(getSelf()); + getContext().stop(self()); } else if (stored > HIGH_WATERMARK) { log.debug("suspending reading at {}", currentOffset()); - connection.tell(TcpMessage.suspendReading(), getSelf()); + connection.tell(TcpMessage.suspendReading(), self()); suspended = true; } } @@ -211,7 +217,7 @@ public class EchoHandler extends UntypedActor { if (suspended && stored < LOW_WATERMARK) { log.debug("resuming reading"); - connection.tell(TcpMessage.resumeReading(), getSelf()); + connection.tell(TcpMessage.resumeReading(), self()); suspended = false; } } @@ -224,12 +230,12 @@ public class EchoHandler extends UntypedActor { protected void writeAll() { int i = 0; for (ByteString data : storage) { - connection.tell(TcpMessage.write(data, new Ack(storageOffset + i++)), getSelf()); + connection.tell(TcpMessage.write(data, new Ack(storageOffset + i++)), self()); } } protected void writeFirst() { - connection.tell(TcpMessage.write(storage.peek(), new Ack(storageOffset)), getSelf()); + connection.tell(TcpMessage.write(storage.peek(), new Ack(storageOffset)), self()); } //#storage-omitted diff --git a/akka-docs/rst/java/code/docs/io/japi/EchoManager.java b/akka-docs/rst/java/code/docs/io/japi/EchoManager.java index b49ed5cd91..c2e032f6d5 100644 --- a/akka-docs/rst/java/code/docs/io/japi/EchoManager.java +++ b/akka-docs/rst/java/code/docs/io/japi/EchoManager.java @@ -9,7 +9,7 @@ import java.net.InetSocketAddress; import akka.actor.ActorRef; import akka.actor.Props; import akka.actor.SupervisorStrategy; -import akka.actor.UntypedActor; +import akka.actor.AbstractActor; import akka.event.Logging; import akka.event.LoggingAdapter; import akka.io.Tcp; @@ -19,10 +19,10 @@ import akka.io.Tcp.CommandFailed; import akka.io.Tcp.Connected; import akka.io.TcpMessage; -public class EchoManager extends UntypedActor { +public class EchoManager extends AbstractActor { final LoggingAdapter log = Logging - .getLogger(getContext().system(), getSelf()); + .getLogger(getContext().system(), self()); final Class handlerClass; @@ -41,41 +41,42 @@ public class EchoManager extends UntypedActor { final ActorRef tcpManager = Tcp.get(getContext().system()).manager(); //#manager tcpManager.tell( - TcpMessage.bind(getSelf(), new InetSocketAddress("localhost", 0), 100), - getSelf()); + TcpMessage.bind(self(), new InetSocketAddress("localhost", 0), 100), + self()); } @Override public void postRestart(Throwable arg0) throws Exception { // do not restart - getContext().stop(getSelf()); + getContext().stop(self()); } @Override - public void onReceive(Object msg) throws Exception { - if (msg instanceof Bound) { - log.info("listening on [{}]", ((Bound) msg).localAddress()); - } else if (msg instanceof Tcp.CommandFailed) { - final CommandFailed failed = (CommandFailed) msg; - if (failed.cmd() instanceof Bind) { - log.warning("cannot bind to [{}]", ((Bind) failed.cmd()).localAddress()); - getContext().stop(getSelf()); - } else { - log.warning("unknown command failed [{}]", failed.cmd()); - } - } else - if (msg instanceof Connected) { - final Connected conn = (Connected) msg; - log.info("received connection from [{}]", conn.remoteAddress()); - final ActorRef connection = getSender(); - final ActorRef handler = getContext().actorOf( - Props.create(handlerClass, connection, conn.remoteAddress())); - //#echo-manager - connection.tell(TcpMessage.register(handler, - true, // <-- keepOpenOnPeerClosed flag - true), getSelf()); - //#echo-manager - } + public Receive createReceive() { + return receiveBuilder() + .match(Bound.class, msg -> { + log.info("listening on [{}]", msg.localAddress()); + }) + .match(Tcp.CommandFailed.class, failed -> { + if (failed.cmd() instanceof Bind) { + log.warning("cannot bind to [{}]", ((Bind) failed.cmd()).localAddress()); + getContext().stop(self()); + } else { + log.warning("unknown command failed [{}]", failed.cmd()); + } + }) + .match(Connected.class, conn -> { + log.info("received connection from [{}]", conn.remoteAddress()); + final ActorRef connection = sender(); + final ActorRef handler = getContext().actorOf( + Props.create(handlerClass, connection, conn.remoteAddress())); + //#echo-manager + connection.tell(TcpMessage.register(handler, + true, // <-- keepOpenOnPeerClosed flag + true), self()); + //#echo-manager + }) + .build(); } } 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 b250a75f84..7a3376f8e5 100644 --- a/akka-docs/rst/java/code/docs/io/japi/IODocTest.java +++ b/akka-docs/rst/java/code/docs/io/japi/IODocTest.java @@ -15,7 +15,7 @@ import java.net.InetSocketAddress; import akka.actor.ActorRef; import akka.actor.ActorSystem; import akka.actor.Props; -import akka.actor.UntypedActor; +import akka.actor.AbstractActor; import akka.io.Tcp; import akka.io.Tcp.Bound; import akka.io.Tcp.CommandFailed; @@ -34,7 +34,7 @@ public class IODocTest extends AbstractJavaTest { static //#server - public class Server extends UntypedActor { + public class Server extends AbstractActor { final ActorRef manager; @@ -49,25 +49,28 @@ public class IODocTest extends AbstractJavaTest { @Override public void preStart() throws Exception { final ActorRef tcp = Tcp.get(getContext().system()).manager(); - tcp.tell(TcpMessage.bind(getSelf(), - new InetSocketAddress("localhost", 0), 100), getSelf()); + tcp.tell(TcpMessage.bind(self(), + new InetSocketAddress("localhost", 0), 100), self()); } @Override - public void onReceive(Object msg) throws Exception { - if (msg instanceof Bound) { - manager.tell(msg, getSelf()); - - } else if (msg instanceof CommandFailed) { - getContext().stop(getSelf()); - - } else if (msg instanceof Connected) { - final Connected conn = (Connected) msg; - manager.tell(conn, getSelf()); - final ActorRef handler = getContext().actorOf( - Props.create(SimplisticHandler.class)); - getSender().tell(TcpMessage.register(handler), getSelf()); - } + public Receive createReceive() { + return receiveBuilder() + .match(Bound.class, msg -> { + manager.tell(msg, self()); + + }) + .match(CommandFailed.class, msg -> { + getContext().stop(self()); + + }) + .match(Connected.class, conn -> { + manager.tell(conn, self()); + final ActorRef handler = getContext().actorOf( + Props.create(SimplisticHandler.class)); + sender().tell(TcpMessage.register(handler), self()); + }) + .build(); } } @@ -75,23 +78,26 @@ public class IODocTest extends AbstractJavaTest { static //#simplistic-handler - public class SimplisticHandler extends UntypedActor { + public class SimplisticHandler extends AbstractActor { @Override - public void onReceive(Object msg) throws Exception { - if (msg instanceof Received) { - final ByteString data = ((Received) msg).data(); - System.out.println(data); - getSender().tell(TcpMessage.write(data), getSelf()); - } else if (msg instanceof ConnectionClosed) { - getContext().stop(getSelf()); - } + public Receive createReceive() { + return receiveBuilder() + .match(Received.class, msg -> { + final ByteString data = msg.data(); + System.out.println(data); + sender().tell(TcpMessage.write(data), self()); + }) + .match(ConnectionClosed.class, msg -> { + getContext().stop(self()); + }) + .build(); } } //#simplistic-handler static //#client - public class Client extends UntypedActor { + public class Client extends AbstractActor { final InetSocketAddress remote; final ActorRef listener; @@ -105,44 +111,43 @@ public class IODocTest extends AbstractJavaTest { this.listener = listener; final ActorRef tcp = Tcp.get(getContext().system()).manager(); - tcp.tell(TcpMessage.connect(remote), getSelf()); + tcp.tell(TcpMessage.connect(remote), self()); } @Override - public void onReceive(Object msg) throws Exception { - if (msg instanceof CommandFailed) { - listener.tell("failed", getSelf()); - getContext().stop(getSelf()); - - } else if (msg instanceof Connected) { - listener.tell(msg, getSelf()); - getSender().tell(TcpMessage.register(getSelf()), getSelf()); - getContext().become(connected(getSender())); - } + public Receive createReceive() { + return receiveBuilder() + .match(CommandFailed.class, msg -> { + listener.tell("failed", self()); + getContext().stop(self()); + + }) + .match(Connected.class, msg -> { + listener.tell(msg, self()); + sender().tell(TcpMessage.register(self()), self()); + getContext().become(connected(sender())); + }) + .build(); } - private Procedure connected(final ActorRef connection) { - return new Procedure() { - @Override - public void apply(Object msg) throws Exception { - - if (msg instanceof ByteString) { - connection.tell(TcpMessage.write((ByteString) msg), getSelf()); - - } else if (msg instanceof CommandFailed) { - // OS kernel socket buffer was full - - } else if (msg instanceof Received) { - listener.tell(((Received) msg).data(), getSelf()); - - } else if (msg.equals("close")) { - connection.tell(TcpMessage.close(), getSelf()); - - } else if (msg instanceof ConnectionClosed) { - getContext().stop(getSelf()); - } - } - }; + private Receive connected(final ActorRef connection) { + return receiveBuilder() + .match(ByteString.class, msg -> { + connection.tell(TcpMessage.write((ByteString) msg), self()); + }) + .match(CommandFailed.class, msg -> { + // OS kernel socket buffer was full + }) + .match(Received.class, msg -> { + listener.tell(msg.data(), self()); + }) + .matchEquals("close", msg -> { + connection.tell(TcpMessage.close(), self()); + }) + .match(ConnectionClosed.class, msg -> { + getContext().stop(self()); + }) + .build(); } } diff --git a/akka-docs/rst/java/code/docs/io/japi/SimpleEchoHandler.java b/akka-docs/rst/java/code/docs/io/japi/SimpleEchoHandler.java index 1da5d26a0c..e759ef41d5 100644 --- a/akka-docs/rst/java/code/docs/io/japi/SimpleEchoHandler.java +++ b/akka-docs/rst/java/code/docs/io/japi/SimpleEchoHandler.java @@ -9,7 +9,7 @@ import java.util.LinkedList; import java.util.Queue; import akka.actor.ActorRef; -import akka.actor.UntypedActor; +import akka.actor.AbstractActor; import akka.event.Logging; import akka.event.LoggingAdapter; import akka.io.Tcp.ConnectionClosed; @@ -20,10 +20,10 @@ import akka.japi.Procedure; import akka.util.ByteString; //#simple-echo-handler -public class SimpleEchoHandler extends UntypedActor { +public class SimpleEchoHandler extends AbstractActor { final LoggingAdapter log = Logging - .getLogger(getContext().system(), getSelf()); + .getLogger(getContext().system(), self()); final ActorRef connection; final InetSocketAddress remote; @@ -41,38 +41,42 @@ public class SimpleEchoHandler extends UntypedActor { } @Override - public void onReceive(Object msg) throws Exception { - if (msg instanceof Received) { - final ByteString data = ((Received) msg).data(); - buffer(data); - connection.tell(TcpMessage.write(data, ACK), getSelf()); - // now switch behavior to “waiting for acknowledgement” - getContext().become(buffering, false); - - } else if (msg instanceof ConnectionClosed) { - getContext().stop(getSelf()); - } + public Receive createReceive() { + return receiveBuilder() + .match(Received.class, msg -> { + final ByteString data = msg.data(); + buffer(data); + connection.tell(TcpMessage.write(data, ACK), self()); + // now switch behavior to “waiting for acknowledgement” + getContext().become(buffering(), false); + + }) + .match(ConnectionClosed.class, msg -> { + getContext().stop(self()); + }) + .build(); } - private final Procedure buffering = new Procedure() { - @Override - public void apply(Object msg) throws Exception { - if (msg instanceof Received) { - buffer(((Received) msg).data()); + private final Receive buffering() { + return receiveBuilder() + .match(Received.class, msg -> { + buffer(msg.data()); - } else if (msg == ACK) { + }) + .match(Event.class, msg -> msg == ACK, msg -> { acknowledge(); - } else if (msg instanceof ConnectionClosed) { - if (((ConnectionClosed) msg).isPeerClosed()) { + }) + .match(ConnectionClosed.class, msg -> { + if (msg.isPeerClosed()) { closing = true; } else { // could also be ErrorClosed, in which case we just give up - getContext().stop(getSelf()); + getContext().stop(self()); } - } - } - }; + }) + .build(); + } //#storage-omitted public void postStop() { @@ -95,11 +99,11 @@ public class SimpleEchoHandler extends UntypedActor { if (stored > maxStored) { log.warning("drop connection to [{}] (buffer overrun)", remote); - getContext().stop(getSelf()); + getContext().stop(self()); } else if (stored > highWatermark) { log.debug("suspending reading"); - connection.tell(TcpMessage.suspendReading(), getSelf()); + connection.tell(TcpMessage.suspendReading(), self()); suspended = true; } } @@ -111,18 +115,18 @@ public class SimpleEchoHandler extends UntypedActor { if (suspended && stored < lowWatermark) { log.debug("resuming reading"); - connection.tell(TcpMessage.resumeReading(), getSelf()); + connection.tell(TcpMessage.resumeReading(), self()); suspended = false; } if (storage.isEmpty()) { if (closing) { - getContext().stop(getSelf()); + getContext().stop(self()); } else { getContext().unbecome(); } } else { - connection.tell(TcpMessage.write(storage.peek(), ACK), getSelf()); + connection.tell(TcpMessage.write(storage.peek(), ACK), self()); } } //#simple-helpers diff --git a/akka-docs/rst/java/code/docs/io/japi/Watcher.java b/akka-docs/rst/java/code/docs/io/japi/Watcher.java index 47941771e5..4bbef03c6f 100644 --- a/akka-docs/rst/java/code/docs/io/japi/Watcher.java +++ b/akka-docs/rst/java/code/docs/io/japi/Watcher.java @@ -4,9 +4,9 @@ import java.util.concurrent.CountDownLatch; import akka.actor.ActorRef; import akka.actor.Terminated; -import akka.actor.UntypedActor; +import akka.actor.AbstractActor; -public class Watcher extends UntypedActor { +public class Watcher extends AbstractActor { static public class Watch { final ActorRef target; @@ -22,13 +22,16 @@ public class Watcher extends UntypedActor { } @Override - public void onReceive(Object msg) throws Exception { - if (msg instanceof Watch) { - getContext().watch(((Watch) msg).target); - } else if (msg instanceof Terminated) { - latch.countDown(); - if (latch.getCount() == 0) getContext().stop(getSelf()); - } + public Receive createReceive() { + return receiveBuilder() + .match(Watch.class, msg -> { + getContext().watch(msg.target); + }) + .match(Terminated.class, msg -> { + latch.countDown(); + if (latch.getCount() == 0) getContext().stop(self()); + }) + .build(); } } diff --git a/akka-docs/rst/java/code/docs/jrouting/ConsistentHashingRouterDocTest.java b/akka-docs/rst/java/code/docs/jrouting/ConsistentHashingRouterDocTest.java index 29bab12096..5eddfa7d6b 100644 --- a/akka-docs/rst/java/code/docs/jrouting/ConsistentHashingRouterDocTest.java +++ b/akka-docs/rst/java/code/docs/jrouting/ConsistentHashingRouterDocTest.java @@ -51,7 +51,7 @@ public class ConsistentHashingRouterDocTest extends AbstractJavaTest { } else if (msg instanceof Get) { Get get = (Get) msg; Object value = cache.get(get.key); - getSender().tell(value == null ? NOT_FOUND : value, + sender().tell(value == null ? NOT_FOUND : value, getContext().self()); } else if (msg instanceof Evict) { Evict evict = (Evict) msg; diff --git a/akka-docs/rst/java/code/docs/jrouting/CustomRouterDocTest.java b/akka-docs/rst/java/code/docs/jrouting/CustomRouterDocTest.java index 61ec7a5df0..fadec0bf2d 100644 --- a/akka-docs/rst/java/code/docs/jrouting/CustomRouterDocTest.java +++ b/akka-docs/rst/java/code/docs/jrouting/CustomRouterDocTest.java @@ -96,7 +96,7 @@ public class CustomRouterDocTest extends AbstractJavaTest { static public class Storage extends UntypedActor { public void onReceive(Object msg) { - getSender().tell(msg, getSelf()); + sender().tell(msg, self()); } } diff --git a/akka-docs/rst/java/code/docs/jrouting/RouterDocTest.java b/akka-docs/rst/java/code/docs/jrouting/RouterDocTest.java index 2c03e142bb..4b3c2c8bd9 100644 --- a/akka-docs/rst/java/code/docs/jrouting/RouterDocTest.java +++ b/akka-docs/rst/java/code/docs/jrouting/RouterDocTest.java @@ -105,7 +105,7 @@ public class RouterDocTest extends AbstractJavaTest { public void onReceive(Object msg) { if (msg instanceof Work) { - router.route(msg, getSender()); + router.route(msg, sender()); } else if (msg instanceof Terminated) { router = router.removeRoutee(((Terminated) msg).actor()); ActorRef r = getContext().actorOf(Props.create(Worker.class)); @@ -123,18 +123,18 @@ public class RouterDocTest extends AbstractJavaTest { static public class Echo extends UntypedActor { public void onReceive(Object msg) { - getSender().tell(msg, getSelf()); + sender().tell(msg, self()); } } static public class Replier extends UntypedActor { public void onReceive(Object msg) { //#reply-with-self - getSender().tell("reply", getSelf()); + sender().tell("reply", self()); //#reply-with-self //#reply-with-parent - getSender().tell("reply", getContext().parent()); + sender().tell("reply", getContext().parent()); //#reply-with-parent } } diff --git a/akka-docs/rst/java/code/docs/pattern/SchedulerPatternTest.java b/akka-docs/rst/java/code/docs/pattern/SchedulerPatternTest.java index 4d784e3e3e..7cc9365554 100644 --- a/akka-docs/rst/java/code/docs/pattern/SchedulerPatternTest.java +++ b/akka-docs/rst/java/code/docs/pattern/SchedulerPatternTest.java @@ -30,7 +30,7 @@ public class SchedulerPatternTest extends AbstractJavaTest { private final Cancellable tick = getContext().system().scheduler().schedule( Duration.create(500, TimeUnit.MILLISECONDS), Duration.create(1, TimeUnit.SECONDS), - getSelf(), "tick", getContext().dispatcher(), null); + self(), "tick", getContext().dispatcher(), null); //#schedule-constructor // this variable and constructor is declared here to not show up in the docs final ActorRef target; @@ -49,7 +49,7 @@ public class SchedulerPatternTest extends AbstractJavaTest { if (message.equals("tick")) { // do something useful here //#schedule-constructor - target.tell(message, getSelf()); + target.tell(message, self()); //#schedule-constructor } //#schedule-constructor @@ -79,7 +79,7 @@ public class SchedulerPatternTest extends AbstractJavaTest { public void preStart() { getContext().system().scheduler().scheduleOnce( Duration.create(500, TimeUnit.MILLISECONDS), - getSelf(), "tick", getContext().dispatcher(), null); + self(), "tick", getContext().dispatcher(), null); } // override postRestart so we don't call preStart and schedule a new message @@ -93,10 +93,10 @@ public class SchedulerPatternTest extends AbstractJavaTest { // send another periodic tick after the specified delay getContext().system().scheduler().scheduleOnce( Duration.create(1, TimeUnit.SECONDS), - getSelf(), "tick", getContext().dispatcher(), null); + self(), "tick", getContext().dispatcher(), null); // do something useful here //#schedule-receive - target.tell(message, getSelf()); + target.tell(message, self()); //#schedule-receive } //#schedule-receive diff --git a/akka-docs/rst/java/code/docs/pattern/SupervisedAsk.java b/akka-docs/rst/java/code/docs/pattern/SupervisedAsk.java index f87d43b2ff..44cb25ce6a 100644 --- a/akka-docs/rst/java/code/docs/pattern/SupervisedAsk.java +++ b/akka-docs/rst/java/code/docs/pattern/SupervisedAsk.java @@ -74,13 +74,13 @@ public class SupervisedAsk { public void onReceive(Object message) throws Exception { if (message instanceof AskParam) { askParam = (AskParam) message; - caller = getSender(); + caller = sender(); targetActor = getContext().actorOf(askParam.props); getContext().watch(targetActor); targetActor.forward(askParam.message, getContext()); Scheduler scheduler = getContext().system().scheduler(); timeoutMessage = scheduler.scheduleOnce(askParam.timeout.duration(), - self(), new AskTimeout(), context().dispatcher(), null); + self(), new AskTimeout(), getContext().dispatcher(), null); } else if (message instanceof Terminated) { Throwable ex = new ActorKilledException("Target actor terminated."); caller.tell(new Status.Failure(ex), self()); diff --git a/akka-docs/rst/java/code/docs/persistence/LambdaPersistenceDocTest.java b/akka-docs/rst/java/code/docs/persistence/LambdaPersistenceDocTest.java index c047a4dbd7..bb9e42c73b 100644 --- a/akka-docs/rst/java/code/docs/persistence/LambdaPersistenceDocTest.java +++ b/akka-docs/rst/java/code/docs/persistence/LambdaPersistenceDocTest.java @@ -10,11 +10,11 @@ import akka.pattern.BackoffSupervisor; import akka.persistence.*; import akka.persistence.journal.EventAdapter; import akka.persistence.journal.EventSeq; -import scala.Option; import scala.concurrent.duration.Duration; import scala.PartialFunction; import scala.runtime.BoxedUnit; import java.io.Serializable; +import java.util.Optional; import java.util.concurrent.TimeUnit; public class LambdaPersistenceDocTest { @@ -42,7 +42,7 @@ public class LambdaPersistenceDocTest { //#recover-on-restart-disabled @Override - public void preRestart(Throwable reason, Option message) {} + public void preRestart(Throwable reason, Optional message) {} //#recover-on-restart-disabled } @@ -65,14 +65,14 @@ public class LambdaPersistenceDocTest { //#persistence-id-override @Override - public PartialFunction receiveCommand() { - return ReceiveBuilder. + public Receive createReceive() { + return receiveBuilder(). match(String.class, cmd -> {/* ... */}).build(); } @Override - public PartialFunction receiveRecover() { - return ReceiveBuilder. + public Receive createReceiveRecover() { + return receiveBuilder(). match(String.class, evt -> {/* ... */}).build(); } @@ -85,8 +85,8 @@ public class LambdaPersistenceDocTest { return "my-stable-persistence-id"; } - @Override public PartialFunction receiveRecover() { - return ReceiveBuilder. + @Override public Receive createReceiveRecover() { + return receiveBuilder(). match(RecoveryCompleted.class, r -> { // perform init after recovery, before any other messages // ... @@ -94,8 +94,8 @@ public class LambdaPersistenceDocTest { match(String.class, this::handleEvent).build(); } - @Override public PartialFunction receiveCommand() { - return ReceiveBuilder. + @Override public Receive createReceive() { + return receiveBuilder(). match(String.class, s -> s.equals("cmd"), s -> persist("evt", this::handleEvent)).build(); } @@ -128,7 +128,7 @@ public class LambdaPersistenceDocTest { Duration.create(3, TimeUnit.SECONDS), Duration.create(30, TimeUnit.SECONDS), 0.2); - context().actorOf(props, "mySupervisor"); + getContext().actorOf(props, "mySupervisor"); super.preStart(); } //#backoff @@ -188,8 +188,8 @@ public class LambdaPersistenceDocTest { } @Override - public PartialFunction receiveCommand() { - return ReceiveBuilder. + public Receive createReceive() { + return receiveBuilder(). match(String.class, s -> { persist(new MsgSent(s), evt -> updateState(evt)); }). @@ -200,8 +200,8 @@ public class LambdaPersistenceDocTest { } @Override - public PartialFunction receiveRecover() { - return ReceiveBuilder. + public Receive createReceiveRecover() { + return receiveBuilder(). match(Object.class, evt -> updateState(evt)).build(); } @@ -217,13 +217,14 @@ public class LambdaPersistenceDocTest { } class MyDestination extends AbstractActor { - public MyDestination() { - receive(ReceiveBuilder. - match(Msg.class, msg -> { + @Override + public Receive createReceive() { + return receiveBuilder() + .match(Msg.class, msg -> { // ... sender().tell(new Confirm(msg.deliveryId), self()); - }).build() - ); + }) + .build(); } } //#at-least-once-example @@ -236,8 +237,8 @@ public class LambdaPersistenceDocTest { //#save-snapshot private Object state; - @Override public PartialFunction receiveCommand() { - return ReceiveBuilder. + @Override public Receive createReceive() { + return receiveBuilder(). match(String.class, s -> s.equals("snap"), s -> saveSnapshot(state)). match(SaveSnapshotSuccess.class, ss -> { @@ -255,8 +256,8 @@ public class LambdaPersistenceDocTest { return "persistence-id"; } - @Override public PartialFunction receiveRecover() { - return ReceiveBuilder. + @Override public Receive createReceiveRecover() { + return receiveBuilder(). match(RecoveryCompleted.class, r -> {/* ...*/}).build(); } @@ -279,8 +280,8 @@ public class LambdaPersistenceDocTest { //#snapshot-offer private Object state; - @Override public PartialFunction receiveRecover() { - return ReceiveBuilder. + @Override public Receive createReceiveRecover() { + return receiveBuilder(). match(SnapshotOffer.class, s -> { state = s.snapshot(); // ... @@ -293,21 +294,22 @@ public class LambdaPersistenceDocTest { return "persistence-id"; } - @Override public PartialFunction receiveCommand() { - return ReceiveBuilder. + @Override public Receive createReceive() { + return receiveBuilder(). match(String.class, s -> {/* ...*/}).build(); } } class MyActor extends AbstractActor { - ActorRef persistentActor; + private final ActorRef persistentActor = + getContext().actorOf(Props.create(MyPersistentActor.class)); - public MyActor() { - persistentActor = context().actorOf(Props.create(MyPersistentActor.class)); - receive(ReceiveBuilder. - match(Object.class, o -> {/* ... */}).build() - ); + @Override + public Receive createReceive() { + return receiveBuilder() + .match(Object.class, o -> {/* ... */}) + .build(); } } }; @@ -331,13 +333,13 @@ public class LambdaPersistenceDocTest { }); } - @Override public PartialFunction receiveRecover() { - return ReceiveBuilder. + @Override public Receive createReceiveRecover() { + return receiveBuilder(). match(String.class, this::handleCommand).build(); } - @Override public PartialFunction receiveCommand() { - return ReceiveBuilder. + @Override public Receive createReceive() { + return receiveBuilder(). match(String.class, this::handleCommand).build(); } } @@ -383,13 +385,13 @@ public class LambdaPersistenceDocTest { }); } - @Override public PartialFunction receiveRecover() { - return ReceiveBuilder. + @Override public Receive createReceiveRecover() { + return receiveBuilder(). match(String.class, this::handleCommand).build(); } - @Override public PartialFunction receiveCommand() { - return ReceiveBuilder. + @Override public Receive createReceive() { + return receiveBuilder(). match(String.class, this::handleCommand).build(); } } @@ -424,15 +426,15 @@ public class LambdaPersistenceDocTest { return "my-stable-persistence-id"; } - @Override public PartialFunction receiveCommand() { - return ReceiveBuilder.matchAny(event -> {}).build(); + @Override public Receive createReceive() { + return receiveBuilder().matchAny(event -> {}).build(); } //#nested-persist-persist - @Override public PartialFunction receiveRecover() { + @Override public Receive createReceiveRecover() { final Procedure replyToSender = event -> sender().tell(event, self()); - return ReceiveBuilder + return receiveBuilder() .match(String.class, msg -> { persist(String.format("%s-outer-1", msg), event -> { sender().tell(event, self()); @@ -477,15 +479,17 @@ public class LambdaPersistenceDocTest { return "my-stable-persistence-id"; } - @Override public PartialFunction receiveCommand() { - return ReceiveBuilder.matchAny(event -> {}).build(); + @Override + public Receive createReceiveRecover() { + return receiveBuilder().matchAny(event -> {}).build(); } //#nested-persistAsync-persistAsync - @Override public PartialFunction receiveRecover() { + @Override + public Receive createReceive() { final Procedure replyToSender = event -> sender().tell(event, self()); - return ReceiveBuilder + return receiveBuilder() .match(String.class, msg -> { persistAsync(String.format("%s-outer-1", msg ), event -> { sender().tell(event, self()); @@ -539,10 +543,10 @@ public class LambdaPersistenceDocTest { } @Override - public PartialFunction receiveCommand() { - return ReceiveBuilder + public Receive createReceive() { + return receiveBuilder() .match(Shutdown.class, shutdown -> { - context().stop(self()); + getContext().stop(self()); }) .match(String.class, msg -> { System.out.println(msg); @@ -552,8 +556,8 @@ public class LambdaPersistenceDocTest { } @Override - public PartialFunction receiveRecover() { - return ReceiveBuilder.matchAny(any -> {}).build(); + public Receive createReceiveRecover() { + return receiveBuilder().matchAny(any -> {}).build(); } } diff --git a/akka-docs/rst/java/code/docs/persistence/LambdaPersistencePluginDocTest.java b/akka-docs/rst/java/code/docs/persistence/LambdaPersistencePluginDocTest.java index debe23a704..d9ebb00c0f 100644 --- a/akka-docs/rst/java/code/docs/persistence/LambdaPersistencePluginDocTest.java +++ b/akka-docs/rst/java/code/docs/persistence/LambdaPersistencePluginDocTest.java @@ -14,12 +14,20 @@ import akka.persistence.snapshot.japi.*; import akka.actor.*; import akka.persistence.journal.leveldb.SharedLeveldbJournal; import akka.persistence.journal.leveldb.SharedLeveldbStore; -import akka.japi.pf.ReceiveBuilder; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import java.io.File; import java.util.ArrayList; +import java.util.List; import scala.concurrent.Future; import java.util.function.Consumer; +import org.iq80.leveldb.util.FileUtils; import java.util.Optional; +import akka.persistence.japi.journal.JavaJournalSpec; +import akka.persistence.japi.snapshot.JavaSnapshotStoreSpec; + public class LambdaPersistencePluginDocTest { @@ -35,21 +43,24 @@ public class LambdaPersistencePluginDocTest { @Override public void preStart() throws Exception { String path = "akka.tcp://example@127.0.0.1:2552/user/store"; - ActorSelection selection = context().actorSelection(path); + ActorSelection selection = getContext().actorSelection(path); selection.tell(new Identify(1), self()); } - public SharedStorageUsage() { - receive(ReceiveBuilder. - match(ActorIdentity.class, ai -> { + @Override + public Receive createReceive() { + return receiveBuilder() + .match(ActorIdentity.class, ai -> { if (ai.correlationId().equals(1)) { - ActorRef store = ai.getRef(); - if (store != null) { - SharedLeveldbJournal.setStore(store, context().system()); + Optional store = ai.getActorRef(); + if (store.isPresent()) { + SharedLeveldbJournal.setStore(store.get(), getContext().system()); + } else { + throw new RuntimeException("Couldn't identify store"); } } - }).build() - ); + }) + .build(); } } //#shared-store-usage @@ -111,4 +122,79 @@ public class LambdaPersistencePluginDocTest { return null; } } + + static Object o2 = new Object() { + //#journal-tck-java + class MyJournalSpecTest extends JavaJournalSpec { + + public MyJournalSpecTest() { + super(ConfigFactory.parseString( + "persistence.journal.plugin = " + + "\"akka.persistence.journal.leveldb-shared\"")); + } + + @Override + public CapabilityFlag supportsRejectingNonSerializableObjects() { + return CapabilityFlag.off(); + } + } + //#journal-tck-java + }; + + static Object o3 = new Object() { + //#snapshot-store-tck-java + class MySnapshotStoreTest extends JavaSnapshotStoreSpec { + + public MySnapshotStoreTest() { + super(ConfigFactory.parseString( + "akka.persistence.snapshot-store.plugin = " + + "\"akka.persistence.snapshot-store.local\"")); + } + } + //#snapshot-store-tck-java + }; + + static Object o4 = new Object() { + //#journal-tck-before-after-java + class MyJournalSpecTest extends JavaJournalSpec { + + List storageLocations = new ArrayList(); + + public MyJournalSpecTest() { + super(ConfigFactory.parseString( + "persistence.journal.plugin = " + + "\"akka.persistence.journal.leveldb-shared\"")); + + Config config = system().settings().config(); + storageLocations.add(new File( + config.getString("akka.persistence.journal.leveldb.dir"))); + storageLocations.add(new File( + config.getString("akka.persistence.snapshot-store.local.dir"))); + } + + + @Override + public CapabilityFlag supportsRejectingNonSerializableObjects() { + return CapabilityFlag.on(); + } + + @Override + public void beforeAll() { + for (File storageLocation : storageLocations) { + FileUtils.deleteRecursively(storageLocation); + } + super.beforeAll(); + } + + @Override + public void afterAll() { + super.afterAll(); + for (File storageLocation : storageLocations) { + FileUtils.deleteRecursively(storageLocation); + } + } + } + //#journal-tck-before-after-java + }; + } diff --git a/akka-docs/rst/java/code/docs/persistence/PersistenceDocTest.java b/akka-docs/rst/java/code/docs/persistence/PersistenceDocTest.java deleted file mode 100644 index bd2e01c024..0000000000 --- a/akka-docs/rst/java/code/docs/persistence/PersistenceDocTest.java +++ /dev/null @@ -1,584 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ - -package docs.persistence; - -import java.util.concurrent.TimeUnit; - -import akka.actor.*; -import akka.pattern.BackoffSupervisor; -import scala.concurrent.duration.Duration; -import akka.japi.Function; -import akka.japi.Procedure; -import akka.persistence.*; - -import java.io.Serializable; - -public class PersistenceDocTest { - - public interface SomeOtherMessage {} - - public interface PersistentActorMethods { - //#persistence-id - public String persistenceId(); - //#persistence-id - //#recovery-status - public boolean recoveryRunning(); - public boolean recoveryFinished(); - //#recovery-status - } - - static Object o2 = new Object() { - abstract class MyPersistentActor1 extends UntypedPersistentActor { - //#recovery-disabled - @Override - public Recovery recovery() { - return Recovery.none(); - } - //#recovery-disabled - } - - abstract class MyPersistentActor2 extends UntypedPersistentActor { - //#recovery-custom - @Override - public Recovery recovery() { - return Recovery.create(457L); - } - //#recovery-custom - } - - class MyPersistentActor4 extends UntypedPersistentActor implements PersistentActorMethods { - //#persistence-id-override - @Override - public String persistenceId() { - return "my-stable-persistence-id"; - } - //#persistence-id-override - @Override - public void onReceiveRecover(Object message) throws Exception {} - @Override - public void onReceiveCommand(Object message) throws Exception {} - } - - class MyPersistentActor5 extends UntypedPersistentActor { - @Override - public String persistenceId() { - return "persistence-id"; - } - - //#recovery-completed - @Override - public void onReceiveRecover(Object message) { - if (message instanceof RecoveryCompleted) { - // perform init after recovery, before any other messages - } - } - - @Override - public void onReceiveCommand(Object message) throws Exception { - if (message instanceof String) { - // ... - } else { - unhandled(message); - } - } - //#recovery-completed - } - - abstract class MyPersistentActor6 extends UntypedPersistentActor { - //#recovery-no-snap - @Override - public Recovery recovery() { - return Recovery.create(SnapshotSelectionCriteria.none()); - } - //#recovery-no-snap - } - - abstract class MyActor extends UntypedPersistentActor { - //#backoff - @Override - public void preStart() throws Exception { - final Props childProps = Props.create(MyPersistentActor1.class); - final Props props = BackoffSupervisor.props( - childProps, - "myActor", - Duration.create(3, TimeUnit.SECONDS), - Duration.create(30, TimeUnit.SECONDS), - 0.2); - getContext().actorOf(props, "mySupervisor"); - super.preStart(); - } - //#backoff - } - }; - - static Object atLeastOnceExample = new Object() { - //#at-least-once-example - - class Msg implements Serializable { - public final long deliveryId; - public final String s; - - public Msg(long deliveryId, String s) { - this.deliveryId = deliveryId; - this.s = s; - } - } - - class Confirm implements Serializable { - public final long deliveryId; - - public Confirm(long deliveryId) { - this.deliveryId = deliveryId; - } - } - - - class MsgSent implements Serializable { - public final String s; - - public MsgSent(String s) { - this.s = s; - } - } - class MsgConfirmed implements Serializable { - public final long deliveryId; - - public MsgConfirmed(long deliveryId) { - this.deliveryId = deliveryId; - } - } - - class MyPersistentActor extends UntypedPersistentActorWithAtLeastOnceDelivery { - private final ActorSelection destination; - - @Override - public String persistenceId() { return "persistence-id"; } - - public MyPersistentActor(ActorSelection destination) { - this.destination = destination; - } - - @Override - public void onReceiveCommand(Object message) { - if (message instanceof String) { - String s = (String) message; - persist(new MsgSent(s), new Procedure() { - public void apply(MsgSent evt) { - updateState(evt); - } - }); - } else if (message instanceof Confirm) { - Confirm confirm = (Confirm) message; - persist(new MsgConfirmed(confirm.deliveryId), new Procedure() { - public void apply(MsgConfirmed evt) { - updateState(evt); - } - }); - } else { - unhandled(message); - } - } - - @Override - public void onReceiveRecover(Object event) { - updateState(event); - } - - void updateState(Object event) { - if (event instanceof MsgSent) { - final MsgSent evt = (MsgSent) event; - deliver(destination, new Function() { - public Object apply(Long deliveryId) { - return new Msg(deliveryId, evt.s); - } - }); - } else if (event instanceof MsgConfirmed) { - final MsgConfirmed evt = (MsgConfirmed) event; - confirmDelivery(evt.deliveryId); - } - } - } - - class MyDestination extends UntypedActor { - public void onReceive(Object message) throws Exception { - if (message instanceof Msg) { - Msg msg = (Msg) message; - // ... - getSender().tell(new Confirm(msg.deliveryId), getSelf()); - } else { - unhandled(message); - } - } - } - //#at-least-once-example - }; - - static Object o4 = new Object() { - class MyPersistentActor extends UntypedPersistentActor { - @Override - public String persistenceId() { return "persistence-id"; } - - //#save-snapshot - private Object state; - - @Override - public void onReceiveCommand(Object message) { - if (message.equals("snap")) { - saveSnapshot(state); - } else if (message instanceof SaveSnapshotSuccess) { - SnapshotMetadata metadata = ((SaveSnapshotSuccess)message).metadata(); - // ... - } else if (message instanceof SaveSnapshotFailure) { - SnapshotMetadata metadata = ((SaveSnapshotFailure)message).metadata(); - // ... - } - } - //#save-snapshot - - @Override - public void onReceiveRecover(Object event) { - } - } - - }; - - static Object o5 = new Object() { - class MyPersistentActor extends UntypedPersistentActor { - - //#snapshot-criteria - @Override - public Recovery recovery() { - return Recovery.create( - SnapshotSelectionCriteria - .create(457L, System.currentTimeMillis())); - } - //#snapshot-criteria - - @Override - public String persistenceId() { return "persistence-id"; } - - //#snapshot-offer - private Object state; - - @Override - public void onReceiveRecover(Object message) { - if (message instanceof SnapshotOffer) { - state = ((SnapshotOffer)message).snapshot(); - // ... - } else if (message instanceof RecoveryCompleted) { - // ... - } else { - // ... - } - } - //#snapshot-offer - - @Override - public void onReceiveCommand(Object message) { - } - } - - }; - - static Object o9 = new Object() { - //#persist-async - class MyPersistentActor extends UntypedPersistentActor { - @Override - public String persistenceId() { return "some-persistence-id"; } - - @Override - public void onReceiveRecover(Object msg) { - // handle recovery here - } - - @Override - public void onReceiveCommand(Object msg) { - sender().tell(msg, self()); - - persistAsync(String.format("evt-%s-1", msg), new Procedure(){ - @Override - public void apply(String event) throws Exception { - sender().tell(event, self()); - } - }); - persistAsync(String.format("evt-%s-2", msg), new Procedure(){ - @Override - public void apply(String event) throws Exception { - sender().tell(event, self()); - } - }); - } - } - //#persist-async - - public void usage() { - final ActorSystem system = ActorSystem.create("example"); - //#persist-async-usage - final ActorRef persistentActor = system.actorOf(Props.create(MyPersistentActor.class)); - persistentActor.tell("a", null); - persistentActor.tell("b", null); - - // possible order of received messages: - // a - // b - // evt-a-1 - // evt-a-2 - // evt-b-1 - // evt-b-2 - //#persist-async-usage - } - }; - - static Object o10 = new Object() { - //#defer - class MyPersistentActor extends UntypedPersistentActor { - @Override - public String persistenceId() { return "some-persistence-id"; } - - @Override - public void onReceiveRecover(Object msg) { - // handle recovery here - } - - @Override - public void onReceiveCommand(Object msg) { - final Procedure replyToSender = new Procedure() { - @Override - public void apply(String event) throws Exception { - sender().tell(event, self()); - } - }; - - persistAsync(String.format("evt-%s-1", msg), replyToSender); - persistAsync(String.format("evt-%s-2", msg), replyToSender); - deferAsync(String.format("evt-%s-3", msg), replyToSender); - } - } - //#defer - - public void usage() { - final ActorSystem system = ActorSystem.create("example"); - //#defer-caller - final ActorRef persistentActor = system.actorOf(Props.create(MyPersistentActor.class)); - persistentActor.tell("a", null); - persistentActor.tell("b", null); - - // order of received messages: - // a - // b - // evt-a-1 - // evt-a-2 - // evt-a-3 - // evt-b-1 - // evt-b-2 - // evt-b-3 - //#defer-caller - } - }; - - static Object o11 = new Object() { - - class MyPersistentActor extends UntypedPersistentActor { - @Override - public String persistenceId() { - return "my-stable-persistence-id"; - } - - @Override - public void onReceiveRecover(Object msg) { - // handle recovery here - } - - //#nested-persist-persist - @Override - public void onReceiveCommand(Object msg) { - final Procedure replyToSender = new Procedure() { - @Override - public void apply(String event) throws Exception { - sender().tell(event, self()); - } - }; - - final Procedure outer1Callback = new Procedure() { - @Override - public void apply(String event) throws Exception { - sender().tell(event, self()); - persist(String.format("%s-inner-1", msg), replyToSender); - } - }; - final Procedure outer2Callback = new Procedure() { - @Override - public void apply(String event) throws Exception { - sender().tell(event, self()); - persist(String.format("%s-inner-2", msg), replyToSender); - } - }; - - persist(String.format("%s-outer-1", msg), outer1Callback); - persist(String.format("%s-outer-2", msg), outer2Callback); - } - //#nested-persist-persist - - void usage(ActorRef persistentActor) { - //#nested-persist-persist-caller - persistentActor.tell("a", self()); - persistentActor.tell("b", self()); - - // order of received messages: - // a - // a-outer-1 - // a-outer-2 - // a-inner-1 - // a-inner-2 - // and only then process "b" - // b - // b-outer-1 - // b-outer-2 - // b-inner-1 - // b-inner-2 - - //#nested-persist-persist-caller - } - } - - - class MyPersistAsyncActor extends UntypedPersistentActor { - @Override - public String persistenceId() { - return "my-stable-persistence-id"; - } - - @Override - public void onReceiveRecover(Object msg) { - // handle recovery here - } - - //#nested-persistAsync-persistAsync - @Override - public void onReceiveCommand(Object msg) { - final Procedure replyToSender = new Procedure() { - @Override - public void apply(String event) throws Exception { - sender().tell(event, self()); - } - }; - - final Procedure outer1Callback = new Procedure() { - @Override - public void apply(String event) throws Exception { - sender().tell(event, self()); - persistAsync(String.format("%s-inner-1", msg), replyToSender); - } - }; - final Procedure outer2Callback = new Procedure() { - @Override - public void apply(String event) throws Exception { - sender().tell(event, self()); - persistAsync(String.format("%s-inner-1", msg), replyToSender); - } - }; - - persistAsync(String.format("%s-outer-1", msg), outer1Callback); - persistAsync(String.format("%s-outer-2", msg), outer2Callback); - } - //#nested-persistAsync-persistAsync - - - void usage(ActorRef persistentActor) { - //#nested-persistAsync-persistAsync-caller - persistentActor.tell("a", ActorRef.noSender()); - persistentActor.tell("b", ActorRef.noSender()); - - // order of received messages: - // a - // b - // a-outer-1 - // a-outer-2 - // b-outer-1 - // b-outer-2 - // a-inner-1 - // a-inner-2 - // b-inner-1 - // b-inner-2 - - // which can be seen as the following causal relationship: - // a -> a-outer-1 -> a-outer-2 -> a-inner-1 -> a-inner-2 - // b -> b-outer-1 -> b-outer-2 -> b-inner-1 -> b-inner-2 - - //#nested-persistAsync-persistAsync-caller - } - } - }; - - static Object o13 = new Object() { - //#safe-shutdown - final class Shutdown {} - - class MyPersistentActor extends UntypedPersistentActor { - @Override - public String persistenceId() { - return "some-persistence-id"; - } - - @Override - public void onReceiveCommand(Object msg) throws Exception { - if (msg instanceof Shutdown) { - context().stop(self()); - } else if (msg instanceof String) { - System.out.println(msg); - persist("handle-" + msg, new Procedure() { - @Override - public void apply(String param) throws Exception { - System.out.println(param); - } - }); - } else unhandled(msg); - } - - @Override - public void onReceiveRecover(Object msg) throws Exception { - // handle recovery... - } - } - //#safe-shutdown - - - public void usage() { - final ActorSystem system = ActorSystem.create("example"); - final ActorRef persistentActor = system.actorOf(Props.create(MyPersistentActor.class)); - //#safe-shutdown-example-bad - // UN-SAFE, due to PersistentActor's command stashing: - persistentActor.tell("a", ActorRef.noSender()); - persistentActor.tell("b", ActorRef.noSender()); - persistentActor.tell(PoisonPill.getInstance(), ActorRef.noSender()); - // order of received messages: - // a - // # b arrives at mailbox, stashing; internal-stash = [b] - // # PoisonPill arrives at mailbox, stashing; internal-stash = [b, Shutdown] - // PoisonPill is an AutoReceivedMessage, is handled automatically - // !! stop !! - // Actor is stopped without handling `b` nor the `a` handler! - //#safe-shutdown-example-bad - - //#safe-shutdown-example-good - // SAFE: - persistentActor.tell("a", ActorRef.noSender()); - persistentActor.tell("b", ActorRef.noSender()); - persistentActor.tell(new Shutdown(), ActorRef.noSender()); - // order of received messages: - // a - // # b arrives at mailbox, stashing; internal-stash = [b] - // # Shutdown arrives at mailbox, stashing; internal-stash = [b, Shutdown] - // handle-a - // # unstashing; internal-stash = [Shutdown] - // b - // handle-b - // # unstashing; internal-stash = [] - // Shutdown - // -- stop -- - //#safe-shutdown-example-good - } - }; -} diff --git a/akka-docs/rst/java/code/docs/persistence/PersistencePluginDocTest.java b/akka-docs/rst/java/code/docs/persistence/PersistencePluginDocTest.java deleted file mode 100644 index 6081558329..0000000000 --- a/akka-docs/rst/java/code/docs/persistence/PersistencePluginDocTest.java +++ /dev/null @@ -1,193 +0,0 @@ -/** - * Copyright (C) 2009-2017 Lightbend Inc. - */ - -package docs.persistence; - -import akka.actor.*; -import akka.dispatch.Futures; -import akka.persistence.japi.journal.JavaJournalSpec; -import akka.persistence.japi.snapshot.JavaSnapshotStoreSpec; -import akka.persistence.journal.leveldb.SharedLeveldbJournal; -import akka.persistence.journal.leveldb.SharedLeveldbStore; -import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; -import org.iq80.leveldb.util.FileUtils; -import scala.concurrent.Future; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.function.Consumer; - -//#plugin-imports -import akka.persistence.*; -import akka.persistence.journal.japi.AsyncWriteJournal; -import akka.persistence.snapshot.japi.SnapshotStore; -//#plugin-imports - -public class PersistencePluginDocTest { - - - static Object o1 = new Object() { - final ActorSystem system = null; - //#shared-store-creation - final ActorRef store = system.actorOf(Props.create(SharedLeveldbStore.class), "store"); - //#shared-store-creation - - //#shared-store-usage - class SharedStorageUsage extends UntypedActor { - @Override - public void preStart() throws Exception { - String path = "akka.tcp://example@127.0.0.1:2552/user/store"; - ActorSelection selection = getContext().actorSelection(path); - selection.tell(new Identify(1), getSelf()); - } - - @Override - public void onReceive(Object message) throws Exception { - if (message instanceof ActorIdentity) { - ActorIdentity identity = (ActorIdentity) message; - if (identity.correlationId().equals(1)) { - ActorRef store = identity.getRef(); - if (store != null) { - SharedLeveldbJournal.setStore(store, getContext().system()); - } - } - } - } - } - //#shared-store-usage - }; - - class MySnapshotStore extends SnapshotStore { - @Override - public Future> doLoadAsync(String persistenceId, SnapshotSelectionCriteria criteria) { - return null; - } - - @Override - public Future doSaveAsync(SnapshotMetadata metadata, Object snapshot) { - return null; - } - - @Override - public Future doDeleteAsync(SnapshotMetadata metadata) { - return Futures.successful(null); - } - - @Override - public Future doDeleteAsync(String persistenceId, SnapshotSelectionCriteria criteria) { - return Futures.successful(null); - } - } - - class MyAsyncJournal extends AsyncWriteJournal { - //#sync-journal-plugin-api - @Override - public Future>> doAsyncWriteMessages( - Iterable messages) { - try { - Iterable> result = new ArrayList>(); - // blocking call here... - // result.add(..) - return Futures.successful(result); - } catch (Exception e) { - return Futures.failed(e); - } - } - //#sync-journal-plugin-api - - @Override - public Future doAsyncDeleteMessagesTo(String persistenceId, long toSequenceNr) { - return null; - } - - @Override - public Future doAsyncReplayMessages(String persistenceId, long fromSequenceNr, - long toSequenceNr, long max, Consumer replayCallback) { - return null; - } - - @Override - public Future doAsyncReadHighestSequenceNr(String persistenceId, long fromSequenceNr) { - return null; - } - } - - - static Object o2 = new Object() { - //#journal-tck-java - class MyJournalSpecTest extends JavaJournalSpec { - - public MyJournalSpecTest() { - super(ConfigFactory.parseString( - "persistence.journal.plugin = " + - "\"akka.persistence.journal.leveldb-shared\"")); - } - - @Override - public CapabilityFlag supportsRejectingNonSerializableObjects() { - return CapabilityFlag.off(); - } - } - //#journal-tck-java - }; - - static Object o3 = new Object() { - //#snapshot-store-tck-java - class MySnapshotStoreTest extends JavaSnapshotStoreSpec { - - public MySnapshotStoreTest() { - super(ConfigFactory.parseString( - "akka.persistence.snapshot-store.plugin = " + - "\"akka.persistence.snapshot-store.local\"")); - } - } - //#snapshot-store-tck-java - }; - - static Object o4 = new Object() { - //#journal-tck-before-after-java - class MyJournalSpecTest extends JavaJournalSpec { - - List storageLocations = new ArrayList(); - - public MyJournalSpecTest() { - super(ConfigFactory.parseString( - "persistence.journal.plugin = " + - "\"akka.persistence.journal.leveldb-shared\"")); - - Config config = system().settings().config(); - storageLocations.add(new File( - config.getString("akka.persistence.journal.leveldb.dir"))); - storageLocations.add(new File( - config.getString("akka.persistence.snapshot-store.local.dir"))); - } - - - @Override - public CapabilityFlag supportsRejectingNonSerializableObjects() { - return CapabilityFlag.on(); - } - - @Override - public void beforeAll() { - for (File storageLocation : storageLocations) { - FileUtils.deleteRecursively(storageLocation); - } - super.beforeAll(); - } - - @Override - public void afterAll() { - super.afterAll(); - for (File storageLocation : storageLocations) { - FileUtils.deleteRecursively(storageLocation); - } - } - } - //#journal-tck-before-after-java - }; -} diff --git a/akka-docs/rst/java/code/docs/persistence/PersistenceQueryDocTest.java b/akka-docs/rst/java/code/docs/persistence/PersistenceQueryDocTest.java index 39d3942382..0c4bdbbad5 100644 --- a/akka-docs/rst/java/code/docs/persistence/PersistenceQueryDocTest.java +++ b/akka-docs/rst/java/code/docs/persistence/PersistenceQueryDocTest.java @@ -451,15 +451,20 @@ public class PersistenceQueryDocTest { public TheOneWhoWritesToQueryJournal() { store = new ExampleStore(); - - receive(ReceiveBuilder.matchAny(message -> { - state = updateState(state, message); - - // example saving logic that requires state to become ready: - if (state.readyToSave()) - store.save(Record.of(state)); - - }).build()); + } + + @Override + public Receive createReceive() { + return receiveBuilder() + .matchAny(message -> { + state = updateState(state, message); + + // example saving logic that requires state to become ready: + if (state.readyToSave()) + store.save(Record.of(state)); + + }) + .build(); } diff --git a/akka-docs/rst/java/code/docs/persistence/query/MyEventsByTagJavaPublisher.java b/akka-docs/rst/java/code/docs/persistence/query/MyEventsByTagJavaPublisher.java index 320fd8eadd..6fcd8ed824 100644 --- a/akka-docs/rst/java/code/docs/persistence/query/MyEventsByTagJavaPublisher.java +++ b/akka-docs/rst/java/code/docs/persistence/query/MyEventsByTagJavaPublisher.java @@ -33,7 +33,7 @@ import static java.util.stream.Collectors.toList; //#events-by-tag-publisher class MyEventsByTagJavaPublisher extends AbstractActorPublisher { private final Serialization serialization = - SerializationExtension.get(context().system()); + SerializationExtension.get(getContext().system()); private final Connection connection; @@ -54,20 +54,23 @@ class MyEventsByTagJavaPublisher extends AbstractActorPublisher { this.tag = tag; this.currentOffset = offset; - final Scheduler scheduler = context().system().scheduler(); + final Scheduler scheduler = getContext().system().scheduler(); this.continueTask = scheduler .schedule(refreshInterval, refreshInterval, self(), CONTINUE, - context().dispatcher(), self()); - - receive(ReceiveBuilder - .matchEquals(CONTINUE, (in) -> { - query(); - deliverBuf(); - }) - .match(Cancel.class, (in) -> { - context().stop(self()); + getContext().dispatcher(), self()); + } + + @Override + public Receive createReceive() { + return receiveBuilder() + .matchEquals(CONTINUE, (in) -> { + query(); + deliverBuf(); }) - .build()); + .match(Cancel.class, (in) -> { + getContext().stop(self()); + }) + .build(); } public static Props props(Connection conn, String tag, Long offset, diff --git a/akka-docs/rst/java/code/docs/remoting/RemoteDeploymentDocTest.java b/akka-docs/rst/java/code/docs/remoting/RemoteDeploymentDocTest.java index 1586c4ab37..834c575cdc 100644 --- a/akka-docs/rst/java/code/docs/remoting/RemoteDeploymentDocTest.java +++ b/akka-docs/rst/java/code/docs/remoting/RemoteDeploymentDocTest.java @@ -25,7 +25,7 @@ public class RemoteDeploymentDocTest { public static class SampleActor extends UntypedActor { public void onReceive(Object message) { - getSender().tell(getSelf(), getSelf()); + sender().tell(self(), self()); } } diff --git a/akka-docs/rst/java/code/docs/stream/ActorPublisherDocTest.java b/akka-docs/rst/java/code/docs/stream/ActorPublisherDocTest.java index 393a7c3ab9..5a2023b1d4 100644 --- a/akka-docs/rst/java/code/docs/stream/ActorPublisherDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/ActorPublisherDocTest.java @@ -67,6 +67,7 @@ public class ActorPublisherDocTest extends AbstractJavaTest { } public static final JobDeniedMessage JobDenied = new JobDeniedMessage(); } + public static class JobManager extends AbstractActorPublisher { public static Props props() { return Props.create(JobManager.class); } @@ -74,12 +75,13 @@ public class ActorPublisherDocTest extends AbstractJavaTest { 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 -> { + @Override + public Receive createReceive() { + return receiveBuilder() + .match(JobManagerProtocol.Job.class, job -> buf.size() == MAX_BUFFER_SIZE, job -> { sender().tell(JobManagerProtocol.JobDenied, self()); - }). - match(JobManagerProtocol.Job.class, job -> { + }) + .match(JobManagerProtocol.Job.class, job -> { sender().tell(JobManagerProtocol.JobAccepted, self()); if (buf.isEmpty() && totalDemand() > 0) @@ -88,10 +90,10 @@ public class ActorPublisherDocTest extends AbstractJavaTest { buf.add(job); deliverBuf(); } - }). - match(ActorPublisherMessage.Request.class, request -> deliverBuf()). - match(ActorPublisherMessage.Cancel.class, cancel -> context().stop(self())). - build()); + }) + .match(ActorPublisherMessage.Request.class, request -> deliverBuf()) + .match(ActorPublisherMessage.Cancel.class, cancel -> getContext().stop(self())) + .build(); } void deliverBuf() { diff --git a/akka-docs/rst/java/code/docs/stream/ActorSubscriberDocTest.java b/akka-docs/rst/java/code/docs/stream/ActorSubscriberDocTest.java index eb6da148bb..8f1af033ee 100644 --- a/akka-docs/rst/java/code/docs/stream/ActorSubscriberDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/ActorSubscriberDocTest.java @@ -161,44 +161,49 @@ public class ActorSubscriberDocTest extends AbstractJavaTest { public WorkerPool() { final List routees = new ArrayList<>(); for (int i = 0; i < 3; i++) - routees.add(new ActorRefRoutee(context().actorOf(Props.create(Worker.class)))); + routees.add(new ActorRefRoutee(getContext().actorOf(Props.create(Worker.class)))); router = new Router(new RoundRobinRoutingLogic(), routees); - - receive(ReceiveBuilder. - match(ActorSubscriberMessage.OnNext.class, on -> on.element() instanceof WorkerPoolProtocol.Msg, - onNext -> { - WorkerPoolProtocol.Msg msg = (WorkerPoolProtocol.Msg) onNext.element(); - queue.put(msg.id, msg.replyTo); - - if (queue.size() > MAX_QUEUE_SIZE) - throw new RuntimeException("queued too many: " + queue.size()); - - router.route(WorkerPoolProtocol.work(msg.id), self()); - }). - match(ActorSubscriberMessage.onCompleteInstance().getClass(), complete -> { - if (queue.isEmpty()) { - context().stop(self()); - } - }). - match(WorkerPoolProtocol.Reply.class, reply -> { - int id = reply.id; - queue.get(id).tell(WorkerPoolProtocol.done(id), self()); - queue.remove(id); - if (canceled() && queue.isEmpty()) { - context().stop(self()); - } - }). - build()); + } + + @Override + public Receive createReceive() { + return receiveBuilder() + .match(ActorSubscriberMessage.OnNext.class, on -> on.element() instanceof WorkerPoolProtocol.Msg, + onNext -> { + WorkerPoolProtocol.Msg msg = (WorkerPoolProtocol.Msg) onNext.element(); + queue.put(msg.id, msg.replyTo); + + if (queue.size() > MAX_QUEUE_SIZE) + throw new RuntimeException("queued too many: " + queue.size()); + + router.route(WorkerPoolProtocol.work(msg.id), self()); + }) + .match(ActorSubscriberMessage.onCompleteInstance().getClass(), complete -> { + if (queue.isEmpty()) { + getContext().stop(self()); + } + }) + .match(WorkerPoolProtocol.Reply.class, reply -> { + int id = reply.id; + queue.get(id).tell(WorkerPoolProtocol.done(id), self()); + queue.remove(id); + if (canceled() && queue.isEmpty()) { + getContext().stop(self()); + } + }) + .build(); } } static class Worker extends AbstractActor { - public Worker() { - receive(ReceiveBuilder. - match(WorkerPoolProtocol.Work.class, work -> { + @Override + public Receive createReceive() { + return receiveBuilder() + .match(WorkerPoolProtocol.Work.class, work -> { // ... sender().tell(WorkerPoolProtocol.reply(work.id), self()); - }).build()); + }) + .build(); } } //#worker-pool diff --git a/akka-docs/rst/java/code/docs/stream/IntegrationDocTest.java b/akka-docs/rst/java/code/docs/stream/IntegrationDocTest.java index 47b488ee91..65de74c2f9 100644 --- a/akka-docs/rst/java/code/docs/stream/IntegrationDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/IntegrationDocTest.java @@ -255,13 +255,18 @@ public class IntegrationDocTest extends AbstractJavaTest { static class DatabaseService extends AbstractActor { public final ActorRef probe; - DatabaseService(ActorRef probe) { + public DatabaseService(ActorRef probe) { this.probe = probe; - - receive(ReceiveBuilder.match(Save.class, s -> { - probe.tell(s.tweet.author.handle, ActorRef.noSender()); - sender().tell(SaveDone.INSTANCE, self()); - }).build()); + } + + @Override + public Receive createReceive() { + return receiveBuilder() + .match(Save.class, s -> { + probe.tell(s.tweet.author.handle, ActorRef.noSender()); + sender().tell(SaveDone.INSTANCE, self()); + }) + .build(); } } @@ -298,7 +303,7 @@ public class IntegrationDocTest extends AbstractJavaTest { // ... process message String reply = word.toUpperCase(); // reply to the ask - getSender().tell(reply, getSelf()); + sender().tell(reply, self()); } else { unhandled(message); } diff --git a/akka-docs/rst/java/code/docs/stream/TwitterStreamQuickstartDocTest.java b/akka-docs/rst/java/code/docs/stream/TwitterStreamQuickstartDocTest.java index 7f64f46a91..afbf1509e1 100644 --- a/akka-docs/rst/java/code/docs/stream/TwitterStreamQuickstartDocTest.java +++ b/akka-docs/rst/java/code/docs/stream/TwitterStreamQuickstartDocTest.java @@ -49,7 +49,7 @@ public class TwitterStreamQuickstartDocTest extends AbstractJavaTest { @BeforeClass public static void setup() { - system = ActorSystem.create("SampleActorTest"); + system = ActorSystem.create("TwitterStreamQuickstartDocTest"); mat = ActorMaterializer.create(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 2277b7e06e..202ea72b45 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 @@ -87,14 +87,17 @@ public class RecipeGlobalRateLimit extends RecipeTest { this.tokenRefreshPeriod, self(), REPLENISH_TOKENS, - context().system().dispatcher(), + getContext().system().dispatcher(), self()); - - receive(open()); + } + + @Override + public Receive createReceive() { + return open(); } - PartialFunction open() { - return ReceiveBuilder + private Receive open() { + return receiveBuilder() .match(ReplenishTokens.class, rt -> { permitTokens = Math.min(permitTokens + tokenRefreshAmount, maxAvailableTokens); }) @@ -102,13 +105,14 @@ public class RecipeGlobalRateLimit extends RecipeTest { permitTokens -= 1; sender().tell(MAY_PASS, self()); if (permitTokens == 0) { - context().become(closed()); + getContext().become(closed()); } - }).build(); + }) + .build(); } - PartialFunction closed() { - return ReceiveBuilder + private Receive closed() { + return receiveBuilder() .match(ReplenishTokens.class, rt -> { permitTokens = Math.min(permitTokens + tokenRefreshAmount, maxAvailableTokens); releaseWaiting(); @@ -128,7 +132,7 @@ public class RecipeGlobalRateLimit extends RecipeTest { permitTokens -= toBeReleased.size(); toBeReleased.stream().forEach(ref -> ref.tell(MAY_PASS, self())); if (permitTokens > 0) { - context().become(open()); + getContext().become(open()); } } diff --git a/akka-docs/rst/java/code/docs/testkit/ParentChildTest.java b/akka-docs/rst/java/code/docs/testkit/ParentChildTest.java index 9cbb4d0643..778146d06d 100644 --- a/akka-docs/rst/java/code/docs/testkit/ParentChildTest.java +++ b/akka-docs/rst/java/code/docs/testkit/ParentChildTest.java @@ -28,7 +28,7 @@ public class ParentChildTest { //#test-example static class Parent extends UntypedActor { - final ActorRef child = context().actorOf(Props.create(Child.class), "child"); + final ActorRef child = getContext().actorOf(Props.create(Child.class), "child"); boolean ponged = false; @Override public void onReceive(Object message) throws Exception { @@ -45,7 +45,7 @@ public class ParentChildTest { static class Child extends UntypedActor { @Override public void onReceive(Object message) throws Exception { if ("ping".equals(message)) { - context().parent().tell("pong", self()); + getContext().parent().tell("pong", self()); } else { unhandled(message); } @@ -79,7 +79,7 @@ public class ParentChildTest { boolean ponged = false; public DependentParent(Props childProps) { - child = context().actorOf(childProps, "child"); + child = getContext().actorOf(childProps, "child"); } @Override public void onReceive(Object message) throws Exception { @@ -102,7 +102,7 @@ public class ParentChildTest { public GenericDependentParent(Function childMaker) throws Exception { - child = childMaker.apply(context()); + child = childMaker.apply(getContext()); } @Override public void onReceive(Object message) throws Exception { @@ -175,13 +175,13 @@ public class ParentChildTest { @Override public Actor create() throws Exception { return new UntypedActor() { - final ActorRef child = context().actorOf(Props.create(Child.class), "child"); + final ActorRef child = getContext().actorOf(Props.create(Child.class), "child"); @Override public void onReceive(Object x) throws Exception { if (sender().equals(child)) { - proxy.ref().forward(x, context()); + proxy.ref().forward(x, getContext()); } else { - child.forward(x, context()); + child.forward(x, getContext()); } } }; diff --git a/akka-docs/rst/java/code/docs/testkit/TestKitDocTest.java b/akka-docs/rst/java/code/docs/testkit/TestKitDocTest.java index 9f14a854f7..93fc1ea871 100644 --- a/akka-docs/rst/java/code/docs/testkit/TestKitDocTest.java +++ b/akka-docs/rst/java/code/docs/testkit/TestKitDocTest.java @@ -37,7 +37,7 @@ public class TestKitDocTest { static class MyActor extends UntypedActor { public void onReceive(Object o) throws Exception { if (o.equals("say42")) { - getSender().tell(42, getSelf()); + sender().tell(42, self()); } else if (o instanceof Exception) { throw (Exception) o; } diff --git a/akka-docs/rst/java/code/docs/testkit/TestKitSampleTest.java b/akka-docs/rst/java/code/docs/testkit/TestKitSampleTest.java index 0f279165df..055d3ca004 100644 --- a/akka-docs/rst/java/code/docs/testkit/TestKitSampleTest.java +++ b/akka-docs/rst/java/code/docs/testkit/TestKitSampleTest.java @@ -24,12 +24,12 @@ public class TestKitSampleTest { public void onReceive(Object msg) { if (msg.equals("hello")) { - getSender().tell("world", getSelf()); + sender().tell("world", self()); if (target != null) target.forward(msg, getContext()); } else if (msg instanceof ActorRef) { target = (ActorRef) msg; - getSender().tell("done", getSelf()); + sender().tell("done", self()); } } } diff --git a/akka-docs/rst/java/fault-tolerance-sample.rst b/akka-docs/rst/java/fault-tolerance-sample.rst deleted file mode 100644 index 4aaadedfc3..0000000000 --- a/akka-docs/rst/java/fault-tolerance-sample.rst +++ /dev/null @@ -1,53 +0,0 @@ -.. _fault-tolerance-sample-java: - -Diagrams of the Fault Tolerance Sample ----------------------------------------------- - -.. image:: ../images/faulttolerancesample-normal-flow.png - -*The above diagram illustrates the normal message flow.* - -**Normal flow:** - -======= ================================================================================== -Step Description -======= ================================================================================== -1 The progress ``Listener`` starts the work. -2 The ``Worker`` schedules work by sending ``Do`` messages periodically to itself -3, 4, 5 When receiving ``Do`` the ``Worker`` tells the ``CounterService`` - to increment the counter, three times. The ``Increment`` message is forwarded - to the ``Counter``, which updates its counter variable and sends current value - to the ``Storage``. -6, 7 The ``Worker`` asks the ``CounterService`` of current value of the counter and pipes - the result back to the ``Listener``. -======= ================================================================================== - - -.. image:: ../images/faulttolerancesample-failure-flow.png - -*The above diagram illustrates what happens in case of storage failure.* - -**Failure flow:** - -=========== ================================================================================== -Step Description -=========== ================================================================================== -1 The ``Storage`` throws ``StorageException``. -2 The ``CounterService`` is supervisor of the ``Storage`` and restarts the - ``Storage`` when ``StorageException`` is thrown. -3, 4, 5, 6 The ``Storage`` continues to fail and is restarted. -7 After 3 failures and restarts within 5 seconds the ``Storage`` is stopped by its - supervisor, i.e. the ``CounterService``. -8 The ``CounterService`` is also watching the ``Storage`` for termination and - receives the ``Terminated`` message when the ``Storage`` has been stopped ... -9, 10, 11 and tells the ``Counter`` that there is no ``Storage``. -12 The ``CounterService`` schedules a ``Reconnect`` message to itself. -13, 14 When it receives the ``Reconnect`` message it creates a new ``Storage`` ... -15, 16 and tells the ``Counter`` to use the new ``Storage`` -=========== ================================================================================== - -Full Source Code of the Fault Tolerance Sample ------------------------------------------------------- - -.. includecode:: code/docs/actor/japi/FaultHandlingDocSample.java#all - diff --git a/akka-docs/rst/java/fault-tolerance.rst b/akka-docs/rst/java/fault-tolerance.rst deleted file mode 100644 index 6e6e15dba3..0000000000 --- a/akka-docs/rst/java/fault-tolerance.rst +++ /dev/null @@ -1,175 +0,0 @@ -.. _fault-tolerance-java: - -Fault Tolerance -====================== - -As explained in :ref:`actor-systems` each actor is the supervisor of its -children, and as such each actor defines fault handling supervisor strategy. -This strategy cannot be changed afterwards as it is an integral part of the -actor system’s structure. - -Fault Handling in Practice --------------------------- - -First, let us look at a sample that illustrates one way to handle data store errors, -which is a typical source of failure in real world applications. Of course it depends -on the actual application what is possible to do when the data store is unavailable, -but in this sample we use a best effort re-connect approach. - -Read the following source code. The inlined comments explain the different pieces of -the fault handling and why they are added. It is also highly recommended to run this -sample as it is easy to follow the log output to understand what is happening at runtime. - -.. toctree:: - - fault-tolerance-sample - -Creating a Supervisor Strategy ------------------------------- - -The following sections explain the fault handling mechanism and alternatives -in more depth. - -For the sake of demonstration let us consider the following strategy: - -.. includecode:: code/docs/actor/FaultHandlingTest.java - :include: strategy - -I have chosen a few well-known exception types in order to demonstrate the -application of the fault handling directives described in :ref:`supervision`. -First off, it is a one-for-one strategy, meaning that each child is treated -separately (an all-for-one strategy works very similarly, the only difference -is that any decision is applied to all children of the supervisor, not only the -failing one). There are limits set on the restart frequency, namely maximum 10 -restarts per minute. ``-1`` and ``Duration.Inf()`` means that the respective limit -does not apply, leaving the possibility to specify an absolute upper limit on the -restarts or to make the restarts work infinitely. -The child actor is stopped if the limit is exceeded. - -.. note:: - - If the strategy is declared inside the supervising actor (as opposed to - a separate class) its decider has access to all internal state of - the actor in a thread-safe fashion, including obtaining a reference to the - currently failed child (available as the ``getSender`` of the failure message). - -Default Supervisor Strategy -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -``Escalate`` is used if the defined strategy doesn't cover the exception that was thrown. - -When the supervisor strategy is not defined for an actor the following -exceptions are handled by default: - -* ``ActorInitializationException`` will stop the failing child actor -* ``ActorKilledException`` will stop the failing child actor -* ``Exception`` will restart the failing child actor -* Other types of ``Throwable`` will be escalated to parent actor - -If the exception escalate all the way up to the root guardian it will handle it -in the same way as the default strategy defined above. - -Stopping Supervisor Strategy -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Closer to the Erlang way is the strategy to just stop children when they fail -and then take corrective action in the supervisor when DeathWatch signals the -loss of the child. This strategy is also provided pre-packaged as -:obj:`SupervisorStrategy.stoppingStrategy` with an accompanying -:class:`StoppingSupervisorStrategy` configurator to be used when you want the -``"/user"`` guardian to apply it. - -Logging of Actor Failures -^^^^^^^^^^^^^^^^^^^^^^^^^ - -By default the ``SupervisorStrategy`` logs failures unless they are escalated. -Escalated failures are supposed to be handled, and potentially logged, at a level -higher in the hierarchy. - -You can mute the default logging of a ``SupervisorStrategy`` by setting -``loggingEnabled`` to ``false`` when instantiating it. Customized logging -can be done inside the ``Decider``. Note that the reference to the currently -failed child is available as the ``getSender`` when the ``SupervisorStrategy`` is -declared inside the supervising actor. - -You may also customize the logging in your own ``SupervisorStrategy`` implementation -by overriding the ``logFailure`` method. - -Supervision of Top-Level Actors -------------------------------- - -Toplevel actors means those which are created using ``system.actorOf()``, and -they are children of the :ref:`User Guardian `. There are no -special rules applied in this case, the guardian simply applies the configured -strategy. - -Test Application ----------------- - -The following section shows the effects of the different directives in practice, -where a test setup is needed. First off, we need a suitable supervisor: - -.. includecode:: code/docs/actor/FaultHandlingTest.java - :include: supervisor - -This supervisor will be used to create a child, with which we can experiment: - -.. includecode:: code/docs/actor/FaultHandlingTest.java - :include: child - -The test is easier by using the utilities described in :ref:`akka-testkit`, -where ``TestProbe`` provides an actor ref useful for receiving and inspecting replies. - -.. includecode:: code/docs/actor/FaultHandlingTest.java - :include: testkit - -Let us create actors: - -.. includecode:: code/docs/actor/FaultHandlingTest.java - :include: create - -The first test shall demonstrate the ``Resume`` directive, so we try it out by -setting some non-initial state in the actor and have it fail: - -.. includecode:: code/docs/actor/FaultHandlingTest.java - :include: resume - -As you can see the value 42 survives the fault handling directive. Now, if we -change the failure to a more serious ``NullPointerException``, that will no -longer be the case: - -.. includecode:: code/docs/actor/FaultHandlingTest.java - :include: restart - -And finally in case of the fatal ``IllegalArgumentException`` the child will be -terminated by the supervisor: - -.. includecode:: code/docs/actor/FaultHandlingTest.java - :include: stop - -Up to now the supervisor was completely unaffected by the child’s failure, -because the directives set did handle it. In case of an ``Exception``, this is not -true anymore and the supervisor escalates the failure. - -.. includecode:: code/docs/actor/FaultHandlingTest.java - :include: escalate-kill - -The supervisor itself is supervised by the top-level actor provided by the -:class:`ActorSystem`, which has the default policy to restart in case of all -``Exception`` cases (with the notable exceptions of -``ActorInitializationException`` and ``ActorKilledException``). Since the -default directive in case of a restart is to kill all children, we expected our poor -child not to survive this failure. - -In case this is not desired (which depends on the use case), we need to use a -different supervisor which overrides this behavior. - -.. includecode:: code/docs/actor/FaultHandlingTest.java - :include: supervisor2 - -With this parent, the child survives the escalated restart, as demonstrated in -the last test: - -.. includecode:: code/docs/actor/FaultHandlingTest.java - :include: escalate-restart - diff --git a/akka-docs/rst/java/fsm.rst b/akka-docs/rst/java/fsm.rst deleted file mode 100644 index 6a5a109d31..0000000000 --- a/akka-docs/rst/java/fsm.rst +++ /dev/null @@ -1,76 +0,0 @@ -.. _fsm-java: - -########################################### -Building Finite State Machine Actors -########################################### - - -Overview -======== - -The FSM (Finite State Machine) pattern is best described in the `Erlang design -principles -`_. -In short, it can be seen as a set of relations of the form: - - **State(S) x Event(E) -> Actions (A), State(S')** - -These relations are interpreted as meaning: - - *If we are in state S and the event E occurs, we should perform the actions A - and make a transition to the state S'.* - -While the Scala programming language enables the formulation of a nice internal -DSL (domain specific language) for formulating finite state machines (see -:ref:`fsm-scala`), Java’s verbosity does not lend itself well to the same -approach. This chapter describes ways to effectively achieve the same -separation of concerns through self-discipline. - -How State should be Handled -=========================== - -All mutable fields (or transitively mutable data structures) referenced by the -FSM actor’s implementation should be collected in one place and only mutated -using a small well-defined set of methods. One way to achieve this is to -assemble all mutable state in a superclass which keeps it private and offers -protected methods for mutating it. - -.. includecode:: code/docs/actor/FSMDocTest.java#imports-data - -.. includecode:: code/docs/actor/FSMDocTest.java#base - -The benefit of this approach is that state changes can be acted upon in one -central place, which makes it impossible to forget inserting code for reacting -to state transitions when adding to the FSM’s machinery. - -Message Buncher Example -======================= - -The base class shown above is designed to support a similar example as for the -Scala FSM documentation: an actor which receives and queues messages, to be -delivered in batches to a configurable target actor. The messages involved are: - -.. includecode:: code/docs/actor/FSMDocTest.java#data - -This actor has only the two states ``IDLE`` and ``ACTIVE``, making their -handling quite straight-forward in the concrete actor derived from the base -class: - -.. includecode:: code/docs/actor/FSMDocTest.java#imports-actor - -.. includecode:: code/docs/actor/FSMDocTest.java#actor - -The trick here is to factor out common functionality like :meth:`whenUnhandled` -and :meth:`transition` in order to obtain a few well-defined points for -reacting to change or insert logging. - -State-Centric vs. Event-Centric -=============================== - -In the example above, the subjective complexity of state and events was roughly -equal, making it a matter of taste whether to choose primary dispatch on -either; in the example a state-based dispatch was chosen. Depending on how -evenly the matrix of possible states and events is populated, it may be more -practical to handle different events first and distinguish the states in the -second tier. An example would be a state machine which has a multitude of -internal states but handles only very few distinct events. diff --git a/akka-docs/rst/java/futures.rst b/akka-docs/rst/java/futures.rst index 4054eb064b..f9af09f782 100644 --- a/akka-docs/rst/java/futures.rst +++ b/akka-docs/rst/java/futures.rst @@ -11,7 +11,7 @@ used to retrieve the result of some concurrent operation. This result can be acc or asynchronously (non-blocking). To be able to use this from Java, Akka provides a java friendly interface in ``akka.dispatch.Futures``. -See also :ref:`actor-java-lambda` for Java compatibility. +See also :ref:`scala-java-compat` for Java compatibility. Execution Contexts ------------------ diff --git a/akka-docs/rst/java/howto.rst b/akka-docs/rst/java/howto.rst index cee355bb9d..e5437cb5a1 100644 --- a/akka-docs/rst/java/howto.rst +++ b/akka-docs/rst/java/howto.rst @@ -66,7 +66,7 @@ Such an actor is unlikely to be reused in a different actor hierarchy and contai This pattern provides a way to encapsulate supervision and error propagation to the temporary actor. Finally the promise returned by Patterns.ask() is fulfilled as a failure, including the exception -(see also :ref:`actor-java-lambda` for Java compatibility). +(see also :ref:`scala-java-compat` for Java compatibility). Let's have a look at the example code: diff --git a/akka-docs/rst/java/index-actors.rst b/akka-docs/rst/java/index-actors.rst index c7afb1f96d..896fb074bf 100644 --- a/akka-docs/rst/java/index-actors.rst +++ b/akka-docs/rst/java/index-actors.rst @@ -4,15 +4,15 @@ Actors .. toctree:: :maxdepth: 2 - untyped-actors - typed-actors - fault-tolerance + lambda-actors + lambda-fault-tolerance dispatchers mailboxes routing - fsm - persistence + lambda-fsm + lambda-persistence persistence-schema-evolution persistence-query persistence-query-leveldb testing + typed-actors diff --git a/akka-docs/rst/java/lambda-actors.rst b/akka-docs/rst/java/lambda-actors.rst index ca451c71f9..9c55fea9c0 100644 --- a/akka-docs/rst/java/lambda-actors.rst +++ b/akka-docs/rst/java/lambda-actors.rst @@ -1,8 +1,8 @@ -.. _lambda-actors-java: +.. _actors-java: -################################### - Actors (Java with Lambda Support) -################################### +######## + Actors +######## The `Actor Model`_ provides a higher level of abstraction for writing concurrent and distributed systems. It alleviates the developer from having to deal with @@ -17,14 +17,6 @@ its syntax from Erlang. .. _Actor Model: http://en.wikipedia.org/wiki/Actor_model -.. warning:: - - The Java with lambda support part of Akka is marked as **“experimental”** as of its introduction in - Akka 2.3.0. We will continue to improve this API based on our users’ feedback, which implies that - while we try to keep incompatible changes to a minimum, but the binary compatibility guarantee for - maintenance releases does not apply to the :class:`akka.actor.AbstractActor`, related classes and - the :class:`akka.japi.pf` package. - Creating Actors =============== @@ -54,12 +46,6 @@ Here is an example: .. includecode:: code/docs/actorlambda/MyActor.java :include: imports,my-actor -In case you want to provide many :meth:`match` cases but want to avoid creating a long call -trail, you can split the creation of the builder into multiple statements as in the example: - -.. includecode:: code/docs/actorlambda/GraduallyBuiltActor.java - :include: imports,actor - Please note that the Akka Actor ``receive`` message loop is exhaustive, which is different compared to Erlang and the late Scala Actors. This means that you need to provide a pattern match for all messages that it can accept and if you @@ -180,20 +166,20 @@ another child to the same parent an `InvalidActorNameException` is thrown. Actors are automatically started asynchronously when created. -.. _actor-create-factory-lambda: +.. _actor-create-factory-java: Dependency Injection -------------------- -If your UntypedActor has a constructor that takes parameters then those need to +If your actor has a constructor that takes parameters then those need to be part of the :class:`Props` as well, as described `above`__. But there are cases when a factory method must be used, for example when the actual constructor arguments are determined by a dependency injection framework. __ Props_ -.. includecode:: code/docs/actor/UntypedActorDocTest.java#import-indirect -.. includecode:: code/docs/actor/UntypedActorDocTest.java +.. includecode:: code/docs/actorlambda/DependencyInjectionDocTest.java#import +.. includecode:: code/docs/actorlambda/DependencyInjectionDocTest.java :include: creating-indirectly :exclude: obtain-fresh-Actor-instance-from-DI-framework @@ -260,19 +246,19 @@ In addition, it offers: occurred within a distant descendant it is still reported one level up at a time). -* :meth:`context()` exposes contextual information for the actor and the current message, such as: +* :meth:`getContext()` exposes contextual information for the actor and the current message, such as: * factory methods to create child actors (:meth:`actorOf`) * system that the actor belongs to * parent supervisor * supervised children * lifecycle monitoring - * hotswap behavior stack as described in :ref:`actor-hotswap-lambda` + * hotswap behavior stack as described in :ref:`actor-hotswap-java` The remaining visible methods are user-overridable life-cycle hooks which are described in the following: -.. includecode:: code/docs/actor/UntypedActorDocTest.java#lifecycle-callbacks +.. includecode:: code/docs/actorlambda/ActorDocTest.java#lifecycle-callbacks The implementations shown above are the defaults provided by the :class:`AbstractActor` class. @@ -317,11 +303,11 @@ occupying it. ``ActorSelection`` cannot be watched for this reason. It is possible to resolve the current incarnation's ``ActorRef`` living under the path by sending an ``Identify`` message to the ``ActorSelection`` which will be replied to with an ``ActorIdentity`` containing the correct reference -(see :ref:`actorSelection-lambda`). This can also be done with the ``resolveOne`` +(see :ref:`actorselection-java`). This can also be done with the ``resolveOne`` method of the :class:`ActorSelection`, which returns a ``Future`` of the matching :class:`ActorRef`. -.. _deathwatch-lambda: +.. _deathwatch-java: Lifecycle Monitoring aka DeathWatch ----------------------------------- @@ -334,6 +320,7 @@ termination (see `Stopping Actors`_). This service is provided by the Registering a monitor is easy: +.. includecode:: code/docs/actorlambda/ActorDocTest.java#import-terminated .. includecode:: code/docs/actorlambda/ActorDocTest.java#watch It should be noted that the :class:`Terminated` message is generated @@ -354,7 +341,7 @@ using ``context.unwatch(target)``. This works even if the :class:`Terminated` message has already been enqueued in the mailbox; after calling :meth:`unwatch` no :class:`Terminated` message for that actor will be processed anymore. -.. _start-hook-lambda: +.. _start-hook-java: Start Hook ---------- @@ -371,7 +358,7 @@ Initialization code which is part of the actor’s constructor will always be called when an instance of the actor class is created, which happens at every restart. -.. _restart-hook-lambda: +.. _restart-hook-java: Restart Hooks ------------- @@ -414,7 +401,7 @@ usual. it has processed the last messages sent by the child before the failure. See :ref:`message-ordering` for details. -.. _stop-hook-lambda: +.. _stop-hook-java: Stop Hook --------- @@ -425,7 +412,7 @@ to run after message queuing has been disabled for this actor, i.e. messages sent to a stopped actor will be redirected to the :obj:`deadLetters` of the :obj:`ActorSystem`. -.. _actorSelection-lambda: +.. _actorselection-java: Identifying Actors via Actor Selection ====================================== @@ -447,7 +434,7 @@ result: It is always preferable to communicate with other Actors using their ActorRef instead of relying upon ActorSelection. Exceptions are - * sending messages using the :ref:`at-least-once-delivery-java-lambda` facility + * sending messages using the :ref:`at-least-once-delivery-java` facility * initiating first contact with a remote system In all other cases ActorRefs can be provided during Actor creation or @@ -488,7 +475,7 @@ of that reply is guaranteed, it still is a normal message. You can also acquire an :class:`ActorRef` for an :class:`ActorSelection` with the ``resolveOne`` method of the :class:`ActorSelection`. It returns a ``Future`` of the matching :class:`ActorRef` if such an actor exists (see also -:ref:`actor-java-lambda` for Java compatibility). It is completed with failure +:ref:`scala-java-compat` for Java compatibility). It is completed with failure [[akka.actor.ActorNotFound]] if no such actor exists or the identification didn't complete within the supplied `timeout`. @@ -532,7 +519,7 @@ In all these methods you have the option of passing along your own ``ActorRef``. Make it a practice of doing so because it will allow the receiver actors to be able to respond to your message, since the sender reference is sent along with the message. -.. _actors-tell-sender-lambda: +.. _actors-tell-sender-java: Tell: Fire-forget ----------------- @@ -551,7 +538,7 @@ different one. Outside of an actor and if no reply is needed the second argument can be ``null``; if a reply is needed outside of an actor you can use the ask-pattern described next.. -.. _actors-ask-lambda: +.. _actors-ask-java: Ask: Send-And-Receive-Future ---------------------------- @@ -559,8 +546,8 @@ Ask: Send-And-Receive-Future The ``ask`` pattern involves actors as well as futures, hence it is offered as a use pattern rather than a method on :class:`ActorRef`: -.. includecode:: code/docs/actor/UntypedActorDocTest.java#import-ask -.. includecode:: code/docs/actor/UntypedActorDocTest.java#ask-pipe +.. includecode:: code/docs/actorlambda/ActorDocTest.java#import-ask +.. includecode:: code/docs/actorlambda/ActorDocTest.java#ask-pipe This example demonstrates ``ask`` together with the ``pipe`` pattern on futures, because this is likely to be a common combination. Please note that @@ -577,6 +564,10 @@ involves creating an internal actor for handling this reply, which needs to have a timeout after which it is destroyed in order not to leak resources; see more below. +.. note:: + A variant of the ``ask`` pattern that returns a ``CompletionStage`` instead of a Scala ``Future`` + is available in the ``akka.pattern.PatternsCS`` object. + .. warning:: To complete the future with an exception you need send a Failure message to the sender. @@ -615,33 +606,60 @@ routers, load-balancers, replicators etc. .. includecode:: code/docs/actorlambda/ActorDocTest.java#forward +.. _actors-receive-java: + Receive messages ================ -An Actor either has to set its initial receive behavior in the constructor by -calling the :meth:`receive` method in the :class:`AbstractActor`: +An actor has to define its initial receive behavior by implementing +the :meth:`createReceive` method in the :class:`AbstractActor`: -.. includecode:: code/docs/actorlambda/ActorDocTest.java - :include: receive-constructor - :exclude: and-some-behavior +.. includecode:: code/docs/actorlambda/ActorDocTest.java#createReceive -or by implementing the :meth:`receive` method in the :class:`Actor` interface: -.. includecode:: code/docs/actorlambda/ActorDocTest.java#receive - -Both the argument to the :class:`AbstractActor` :meth:`receive` method and the return -type of the :class:`Actor` :meth:`receive` method is a ``PartialFunction`` -that defines which messages your Actor can handle, along with the implementation of how the messages -should be processed. - -Don't let the type signature scare you. To allow you to easily build up a partial -function there is a builder named ``ReceiveBuilder`` that you can use. +The return type is :class:`AbstractActor.Receive` that defines which messages your Actor can handle, +along with the implementation of how the messages should be processed. +You can build such behavior with a builder named ``ReceiveBuilder``. Here is an example: .. includecode:: code/docs/actorlambda/MyActor.java :include: imports,my-actor +In case you want to provide many :meth:`match` cases but want to avoid creating a long call +trail, you can split the creation of the builder into multiple statements as in the example: + +.. includecode:: code/docs/actorlambda/GraduallyBuiltActor.java + :include: imports,actor + +Using small methods is a good practice, also in actors. It's recommended to delegate the +actual work of the message processing to methods instead of defining a huge ``ReceiveBuilder`` +with lots of code in each lambda. A well structured actor can look like this: + +.. includecode:: code/docs/actorlambda/ActorDocTest.java#well-structured + +That has benefits such as: + +* easier to see what kind of messages the actor can handle +* readable stack traces in case of exceptions +* works better with performance profiling tools +* Java HotSpot has a better opportunity for making optimizations + +The ``Receive`` can be implemented in other ways than using the ``ReceiveBuilder`` since it in the +end is just a wrapper around a Scala ``PartialFunction``. In Java, you can implement ``PartialFunction`` by +extending ``AbstractPartialFunction``. For example, one could implement an adapter +to `Javaslang Pattern Matching DSL `_. + +If the validation of the ``ReceiveBuilder`` match logic turns out to be a bottleneck for some of your +actors you can consider to implement it at lower level by extending ``UntypedAbstractActor`` instead +of ``AbstractActor``. The partial functions created by the ``ReceiveBuilder`` consist of multiple lambda +expressions for every match statement, where each lambda is referencing the code to be run. This is something +that the JVM can have problems optimizing and the resulting code might not be as performant as the +untyped version. When extending ``UntypedAbstractActor`` each message is received as an untyped +``Object`` and you have to inspect and cast it to the actual message type in other ways, like this: + +.. includecode:: code/docs/actorlambda/ActorDocTest.java#optimized + .. _LambdaActor.Reply: Reply to messages @@ -678,7 +696,7 @@ Messages marked with ``NotInfluenceReceiveTimeout`` will not reset the timer. Th ``ReceiveTimeout`` should be fired by external inactivity but not influenced by internal activity, e.g. scheduled tick messages. -.. _stopping-actors-lambda: +.. _stopping-actors-java: Stopping actors =============== @@ -689,6 +707,8 @@ child actors and the system for stopping top level actors. The actual terminatio the actor is performed asynchronously, i.e. :meth:`stop` may return before the actor is stopped. +.. includecode:: code/docs/actorlambda/MyStoppingActor.java#my-stopping-actor + Processing of the current message, if any, will continue before the actor is stopped, but additional messages in the mailbox will not be processed. By default these messages are sent to the :obj:`deadLetters` of the :obj:`ActorSystem`, but that @@ -698,7 +718,7 @@ Termination of an actor proceeds in two steps: first the actor suspends its mailbox processing and sends a stop command to all its children, then it keeps processing the internal termination notifications from its children until the last one is gone, finally terminating itself (invoking :meth:`postStop`, dumping mailbox, -publishing :class:`Terminated` on the :ref:`DeathWatch `, telling +publishing :class:`Terminated` on the :ref:`DeathWatch `, telling its supervisor). This procedure ensures that actor system sub-trees terminate in an orderly fashion, propagating the stop command to the leaves and collecting their confirmation back to the stopped supervisor. If one of the @@ -724,7 +744,7 @@ enables cleaning up of resources: actor and create its replacement in response to the :class:`Terminated` message which will eventually arrive. -.. _poison-pill-lambda: +.. _poison-pill-java: PoisonPill ---------- @@ -734,15 +754,17 @@ stop the actor when the message is processed. ``PoisonPill`` is enqueued as ordinary messages and will be handled after messages that were already queued in the mailbox. +.. includecode:: code/docs/actorlambda/ActorDocTest.java#poison-pill + Graceful Stop ------------- :meth:`gracefulStop` is useful if you need to wait for termination or compose ordered termination of several actors: -.. includecode:: code/docs/actor/UntypedActorDocTest.java#import-gracefulStop +.. includecode:: code/docs/actorlambda/ActorDocTest.java#import-gracefulStop -.. includecode:: code/docs/actor/UntypedActorDocTest.java#gracefulStop +.. includecode:: code/docs/actorlambda/ActorDocTest.java#gracefulStop .. includecode:: code/docs/actorlambda/ActorDocTest.java#gracefulStop-actor @@ -764,7 +786,7 @@ before stopping the target actor. Simple cleanup tasks can be handled in ``postS within a supervisor you control and only in response to a :class:`Terminated` message, i.e. not for top-level actors. -.. _coordinated-shutdown-lambda: +.. _coordinated-shutdown-java: Coordinated Shutdown -------------------- @@ -842,14 +864,11 @@ used in the test:: akka.coordinated-shutdown.run-by-jvm-shutdown-hook = off akka.cluster.run-coordinated-shutdown-when-down = off -.. _actor-hotswap-lambda: +.. _actor-hotswap-java: Become/Unbecome =============== -Upgrade -------- - Akka supports hotswapping the Actor’s message loop (e.g. its implementation) at runtime: invoke the ``context.become`` method from within the Actor. :meth:`become` takes a ``PartialFunction`` that implements the new @@ -880,7 +899,7 @@ behavior is not the default). .. includecode:: code/docs/actorlambda/ActorDocTest.java#swapper -.. _stash-lambda: +.. _stash-java: Stash ===== @@ -888,7 +907,7 @@ Stash The ``AbstractActorWithStash`` class enables an actor to temporarily stash away messages that can not or should not be handled using the actor's current behavior. Upon changing the actor's message handler, i.e., right -before invoking ``context().become()`` or ``context().unbecome()``, all +before invoking ``getContext().become()`` or ``getContext().unbecome()``, all stashed messages can be "unstashed", thereby prepending them to the actor's mailbox. This way, the stashed messages can be processed in the same order as they have been received originally. An actor that extends @@ -940,7 +959,7 @@ usually the desired behavior. then you should use the ``AbstractActorWithUnboundedStash`` class instead. -.. _killing-actors-lambda: +.. _killing-actors-java: Killing an Actor ================ @@ -953,8 +972,7 @@ See :ref:`supervision-directives` for more information. Use ``Kill`` like this: -.. includecode:: code/docs/actor/UntypedActorDocTest.java - :include: kill +.. includecode:: code/docs/actorlambda/ActorDocTest.java#kill Actors and exceptions ===================== @@ -1021,7 +1039,7 @@ this behavior, and ensure that there is only one call to ``preStart()``. One useful usage of this pattern is to disable creation of new ``ActorRefs`` for children during restarts. This can be achieved by overriding ``preRestart()``: -.. includecode:: code/docs/actor/InitializationDocSpecJava.java#preStartInit +.. includecode:: code/docs/actorlambda/InitializationDocTest.java#preStartInit Please note, that the child actors are *still restarted*, but no new ``ActorRef`` is created. One can recursively apply the same principles for the children, ensuring that their ``preStart()`` method is called only at the creation of their @@ -1049,13 +1067,4 @@ until the initialization finishes, and replaying them after the actor became ini an uninitialized state might lead to the condition that it receives a user message before the initialization has been done. -.. _actor-performance-lambda: -Lambdas and Performance -======================= - -There is one big difference between the optimized partial functions created by the Scala compiler and the ones created by the -``ReceiveBuilder``. The partial functions created by the ``ReceiveBuilder`` consist of multiple lambda expressions for every match -statement, where each lambda is an object referencing the code to be run. This is something that the JVM can have problems -optimizing and the resulting code might not be as performant as the Scala equivalent or the corresponding -:ref:`untyped actor ` version. diff --git a/akka-docs/rst/java/lambda-fault-tolerance-sample.rst b/akka-docs/rst/java/lambda-fault-tolerance-sample.rst index 278d825038..91d0b5f4cf 100644 --- a/akka-docs/rst/java/lambda-fault-tolerance-sample.rst +++ b/akka-docs/rst/java/lambda-fault-tolerance-sample.rst @@ -1,4 +1,4 @@ -.. _lambda-fault-tolerance-sample-java: +.. _fault-tolerance-sample-java: Diagrams of the Fault Tolerance Sample ---------------------------------------------- diff --git a/akka-docs/rst/java/lambda-fault-tolerance.rst b/akka-docs/rst/java/lambda-fault-tolerance.rst index ee8bbd8c1d..8d33d988fa 100644 --- a/akka-docs/rst/java/lambda-fault-tolerance.rst +++ b/akka-docs/rst/java/lambda-fault-tolerance.rst @@ -1,7 +1,7 @@ -.. _lambda-fault-tolerance-java: +.. _fault-tolerance-java: -Fault Tolerance (Java with Lambda Support) -=========================================== +Fault Tolerance +=============== As explained in :ref:`actor-systems` each actor is the supervisor of its children, and as such each actor defines fault handling supervisor strategy. @@ -51,7 +51,7 @@ The child actor is stopped if the limit is exceeded. If the strategy is declared inside the supervising actor (as opposed to a separate class) its decider has access to all internal state of the actor in a thread-safe fashion, including obtaining a reference to the - currently failed child (available as the ``getSender`` of the failure message). + currently failed child (available as the ``sender`` of the failure message). Default Supervisor Strategy ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -89,7 +89,7 @@ higher in the hierarchy. You can mute the default logging of a ``SupervisorStrategy`` by setting ``loggingEnabled`` to ``false`` when instantiating it. Customized logging can be done inside the ``Decider``. Note that the reference to the currently -failed child is available as the ``getSender`` when the ``SupervisorStrategy`` is +failed child is available as the ``sender`` when the ``SupervisorStrategy`` is declared inside the supervising actor. You may also customize the logging in your own ``SupervisorStrategy`` implementation diff --git a/akka-docs/rst/java/lambda-fsm.rst b/akka-docs/rst/java/lambda-fsm.rst index 18a2dc15f0..0ca405c669 100644 --- a/akka-docs/rst/java/lambda-fsm.rst +++ b/akka-docs/rst/java/lambda-fsm.rst @@ -1,8 +1,8 @@ .. _lambda-fsm: -################################ - FSM (Java with Lambda Support) -################################ +##### + FSM +##### Overview diff --git a/akka-docs/rst/java/lambda-persistence.rst b/akka-docs/rst/java/lambda-persistence.rst index e74569162d..900c12f600 100644 --- a/akka-docs/rst/java/lambda-persistence.rst +++ b/akka-docs/rst/java/lambda-persistence.rst @@ -1,8 +1,8 @@ -.. _persistence-lambda-java: +.. _persistence-java: -###################################### -Persistence (Java with Lambda Support) -###################################### +########### +Persistence +########### Akka persistence enables stateful actors to persist their internal state so that it can be recovered when an actor @@ -14,11 +14,6 @@ changes to these actors from which they can rebuild internal state. This can be or starting from a snapshot which can dramatically reduce recovery times. Akka persistence also provides point-to-point communication with at-least-once message delivery semantics. -Akka persistence is inspired by the `eventsourced`_ library. It follows the same concepts and architecture of -`eventsourced`_ but significantly differs on API and implementation level. - -.. _eventsourced: https://github.com/eligosource/eventsourced - Dependencies ============ @@ -68,11 +63,11 @@ Architecture Replicated snapshot stores are available as `Community plugins`_. * *Event sourcing*. Based on the building blocks described above, Akka persistence provides abstractions for the - development of event sourced applications (see section :ref:`event-sourcing-java-lambda`) + development of event sourced applications (see section :ref:`event-sourcing-java`) .. _Community plugins: http://akka.io/community/ -.. _event-sourcing-java-lambda: +.. _event-sourcing-java: Event sourcing ============== @@ -113,7 +108,7 @@ about successful state changes by publishing events. When persisting events with ``persist`` it is guaranteed that the persistent actor will not receive further commands between the ``persist`` call and the execution(s) of the associated event handler. This also holds for multiple ``persist`` -calls in context of a single command. Incoming messages are :ref:`stashed ` until the ``persist`` +calls in context of a single command. Incoming messages are :ref:`stashed ` until the ``persist`` is completed. If persistence of an event fails, ``onPersistFailure`` will be invoked (logging the error by default), @@ -128,7 +123,7 @@ It contains instructions on how to run the ``PersistentActorExample``. .. note:: It's also possible to switch between different command handlers during normal processing and recovery - with ``context().become()`` and ``context().unbecome()``. To get the actor into the same state after + with ``getContext().become()`` and ``getContext().unbecome()``. To get the actor into the same state after recovery you need to take special care to perform the same state transitions with ``become`` and ``unbecome`` in the ``receiveRecover`` method as you would have done in the command handler. Note that when using ``become`` from ``receiveRecover`` it will still only use the ``receiveRecover`` @@ -142,7 +137,13 @@ The identifier must be defined with the ``persistenceId`` method. .. includecode:: code/docs/persistence/LambdaPersistenceDocTest.java#persistence-id-override -.. _recovery-java-lambda: +.. note:: + ``persistenceId`` must be unique to a given entity in the journal (database table/keyspace). + When replaying messages persisted to the journal, you query messages with a ``persistenceId``. + So, if two different entities share the same ``persistenceId``, message-replaying + behavior is corrupted. + +.. _recovery-java: Recovery -------- @@ -156,7 +157,7 @@ only be received by a persistent actor after recovery completes. as the original sender is presumed to be long gone. If you indeed have to notify an actor during recovery in the future, store its ``ActorPath`` explicitly in your persisted events. -.. _recovery-custom-java-lambda: +.. _recovery-custom-java: Recovery customization ^^^^^^^^^^^^^^^^^^^^^^ @@ -178,7 +179,7 @@ events that were previously skipped. .. includecode:: code/docs/persistence/LambdaPersistenceDocTest.java#recovery-custom -Recovery can be disabled by returning ``Recovery.none`` in the ``recovery`` method of a ``PersistentActor``: +Recovery can be disabled by returning ``Recovery.none()`` in the ``recovery`` method of a ``PersistentActor``: .. includecode:: code/docs/persistence/LambdaPersistenceDocTest.java#recovery-disabled @@ -199,13 +200,13 @@ and before any other received messages. If there is a problem with recovering the state of the actor from the journal, ``onRecoveryFailure`` is called (logging the error by default), and the actor will be stopped. -.. _internal-stash-lambda: +.. _internal-stash-java: Internal stash -------------- -The persistent actor has a private :ref:`stash ` for internally caching incoming messages during -:ref:`recovery ` or the ``persist\persistAll`` method persisting events. You can still +The persistent actor has a private :ref:`stash ` for internally caching incoming messages during +:ref:`recovery ` or the ``persist\persistAll`` method persisting events. You can still use/inherit from the ``Stash`` interface. The internal stash cooperates with the normal stash by hooking into ``unstashAll`` method and making sure messages are unstashed properly to the internal stash to maintain ordering guarantees. @@ -234,7 +235,7 @@ The ``DiscardToDeadLetterStrategy`` strategy also has a pre-packaged companion c You can also query default strategy via the Akka persistence extension singleton:: - Persistence.get(context().system()).defaultInternalStashOverflowStrategy(); + Persistence.get(getContext().system()).defaultInternalStashOverflowStrategy(); .. note:: The bounded mailbox should be avoided in the persistent actor, by which the messages come from storage backends may @@ -267,7 +268,7 @@ The ordering between events is still guaranteed ("evt-b-1" will be sent after "e The callback will not be invoked if the actor is restarted (or stopped) in between the call to ``persistAsync`` and the journal has confirmed the write. -.. _defer-java-lambda: +.. _defer-java: Deferring actions until preceding persist handlers have executed ---------------------------------------------------------------- @@ -291,7 +292,7 @@ of the command for which this ``deferAsync`` handler was called. The callback will not be invoked if the actor is restarted (or stopped) in between the call to ``deferAsync`` and the journal has processed and confirmed all preceding writes. -.. _nested-persist-calls-lambda: +.. _nested-persist-calls-java: Nested persist calls -------------------- @@ -326,7 +327,14 @@ In this case no stashing is happening, yet the events are still persisted and ca While it is possible to nest mixed ``persist`` and ``persistAsync`` with keeping their respective semantics it is not a recommended practice, as it may lead to overly complex nesting. -.. _failures-lambda: +.. warning:: + While it is possible to nest ``persist`` calls within one another, + it is *not* legal call ``persist`` from any other Thread than the Actors message processing Thread. + For example, it is not legal to call ``persist`` from Futures! Doing so will break the guarantees + that the persist methods aim to provide. Always call ``persist`` and ``persistAsync`` from within + the Actor's receive block (or methods synchronously invoked from there). + +.. _failures-java: Failures -------- @@ -349,7 +357,7 @@ next message. If there is a problem with recovering the state of the actor from the journal when the actor is started, ``onRecoveryFailure`` is called (logging the error by default), and the actor will be stopped. Note that failure to load snapshot is also treated like this, but you can disable loading of snapshots -if you for example know that serialization format has changed in an incompatible way, see :ref:`recovery-custom-java-lambda`. +if you for example know that serialization format has changed in an incompatible way, see :ref:`recovery-custom-java`. Atomic writes ------------- @@ -386,9 +394,17 @@ Deleting messages in event sourcing based applications is typically either not u up until the sequence number of the data held by that snapshot can be issued to safely delete the previous events while still having access to the accumulated state during replays - by loading the snapshot. +.. warning:: + If you are using :ref:`persistence-query-java`, query results may be missing deleted messages in a journal, + depending on how deletions are implemented in the journal plugin. + Unless you use a plugin which still shows deleted messages in persistence query results, + you have to design your application so that it is not affected by missing messages. + The result of the ``deleteMessages`` request is signaled to the persistent actor with a ``DeleteMessagesSuccess`` message if the delete was successful or a ``DeleteMessagesFailure`` message if it failed. +Message deletion doesn't affect the highest sequence number of the journal, even if all messages were deleted from it after ``deleteMessages`` invocation. + Persistence status handling --------------------------- Persisting, deleting and replaying messages can either succeed or fail. @@ -414,7 +430,7 @@ For critical failures, such as recovery or persisting events failing, the persis handler is invoked. This is because if the underlying journal implementation is signalling persistence failures it is most likely either failing completely or overloaded and restarting right-away and trying to persist the event again will most likely not help the journal recover – as it would likely cause a `Thundering herd problem`_, as many persistent actors -would restart and try to persist their events again. Instead, using a ``BackoffSupervisor`` (as described in :ref:`failures-lambda`) which +would restart and try to persist their events again. Instead, using a ``BackoffSupervisor`` (as described in :ref:`failures-java`) which implements an exponential-backoff strategy which allows for more breathing room for the journal to recover between restarts of the persistent actor. @@ -427,7 +443,7 @@ restarts of the persistent actor. .. _Thundering herd problem: https://en.wikipedia.org/wiki/Thundering_herd_problem -.. _safe-shutdown-lambda: +.. _safe-shutdown-java: Safely shutting down persistent actors -------------------------------------- @@ -453,6 +469,37 @@ mechanism when ``persist()`` is used. Notice the early stop behaviour that occur .. includecode:: code/docs/persistence/LambdaPersistenceDocTest.java#safe-shutdown-example-bad .. includecode:: code/docs/persistence/LambdaPersistenceDocTest.java#safe-shutdown-example-good +.. _replay-filter-java: + +Replay Filter +------------- +There could be cases where event streams are corrupted and multiple writers (i.e. multiple persistent actor instances) +journaled different messages with the same sequence number. +In such a case, you can configure how you filter replayed messages from multiple writers, upon recovery. + +In your configuration, under the ``akka.persistence.journal.xxx.replay-filter`` section (where ``xxx`` is your journal plugin id), +you can select the replay filter ``mode`` from one of the following values: + +* repair-by-discard-old +* fail +* warn +* off + +For example, if you configure the replay filter for leveldb plugin, it looks like this:: + + # The replay filter can detect a corrupt event stream by inspecting + # sequence numbers and writerUuid when replaying events. + akka.persistence.journal.leveldb.replay-filter { + # What the filter should do when detecting invalid events. + # Supported values: + # `repair-by-discard-old` : discard events from old writers, + # warning is logged + # `fail` : fail the replay, error is logged + # `warn` : log warning but emit events untouched + # `off` : disable this feature completely + mode = repair-by-discard-old + } + Snapshots ========= @@ -514,7 +561,7 @@ status messages as illustrated in the following table. ``deleteSnapshots(SnapshotSelectionCriteria)`` ``DeleteSnapshotsSuccess`` ``DeleteSnapshotsFailure`` ============================================== ========================== ============================== -.. _at-least-once-delivery-java-lambda: +.. _at-least-once-delivery-java: At-Least-Once Delivery ====================== @@ -600,6 +647,13 @@ The interval between redelivery attempts is defined by the ``redeliverInterval`` The default value can be configured with the ``akka.persistence.at-least-once-delivery.redeliver-interval`` configuration key. The method can be overridden by implementation classes to return non-default values. +The maximum number of messages that will be sent at each redelivery burst is defined by the +``redeliveryBurstLimit`` method (burst frequency is half of the redelivery interval). If there's a lot of +unconfirmed messages (e.g. if the destination is not available for a long time), this helps to prevent an overwhelming +amount of messages to be sent at once. The default value can be configured with the +``akka.persistence.at-least-once-delivery.redelivery-burst-limit`` configuration key. The method can be overridden +by implementation classes to return non-default values. + After a number of delivery attempts a ``AtLeastOnceDelivery.UnconfirmedWarning`` message will be sent to ``self``. The re-sending will still continue, but you can choose to call ``confirmDelivery`` to cancel the re-sending. The number of delivery attempts before emitting the @@ -614,7 +668,7 @@ not accept more messages and it will throw ``AtLeastOnceDelivery.MaxUnconfirmedM The default value can be configured with the ``akka.persistence.at-least-once-delivery.max-unconfirmed-messages`` configuration key. The method can be overridden by implementation classes to return non-default values. -.. _event-adapters-lambda: +.. _event-adapters-java: Event Adapters ============== @@ -654,7 +708,7 @@ adaptation simply return ``EventSeq.empty``. The adapted events are then deliver .. note:: For more advanced schema evolution techniques refer to the :ref:`persistence-schema-evolution-scala` documentation. -.. _persistent-fsm-java-lambda: +.. _persistent-fsm-java: Persistent FSM ============== @@ -745,8 +799,8 @@ persistence extension will use "default" journal and snapshot-store plugins conf akka.persistence.snapshot-store.plugin = "" However, these entries are provided as empty "", and require explicit user configuration via override in the user ``application.conf``. -For an example of journal plugin which writes messages to LevelDB see :ref:`local-leveldb-journal-java-lambda`. -For an example of snapshot store plugin which writes snapshots as individual files to the local filesystem see :ref:`local-snapshot-store-java-lambda`. +For an example of journal plugin which writes messages to LevelDB see :ref:`local-leveldb-journal-java`. +For an example of snapshot store plugin which writes snapshots as individual files to the local filesystem see :ref:`local-snapshot-store-java`. Applications can provide their own plugins by implementing a plugin API and activate them by configuration. Plugin development requires the following imports: @@ -761,6 +815,8 @@ to start a certain plugin eagerly. In order to do that, you should first add the under the ``akka.extensions`` key. Then, specify the IDs of plugins you wish to start automatically under ``akka.persistence.journal.auto-start-journals`` and ``akka.persistence.snapshot-store.auto-start-snapshot-stores``. +.. _journal-plugin-api-java: + Journal plugin API ------------------ @@ -829,10 +885,48 @@ The ``plugin-dispatcher`` is the dispatcher used for the plugin actor. If not sp Don't run snapshot store tasks/futures on the system default dispatcher, since that might starve other tasks. +Plugin TCK +---------- +In order to help developers build correct and high quality storage plugins, we provide a Technology Compatibility Kit (`TCK `_ for short). + +The TCK is usable from Java as well as Scala projects. For Java you need to include the akka-persistence-tck dependency:: + + + com.typesafe.akka + akka-persistence-tck_${scala.version} + @version@ + test + + +To include the Journal TCK tests in your test suite simply extend the provided ``JavaJournalSpec``: + +.. includecode:: ./code/docs/persistence/LambdaPersistencePluginDocTest.java#journal-tck-java + +Please note that some of the tests are optional, and by overriding the ``supports...`` methods you give the +TCK the needed information about which tests to run. You can implement these methods using the provided +``CapabilityFlag.on`` / ``CapabilityFlag.off`` values. + +We also provide a simple benchmarking class ``JavaJournalPerfSpec`` which includes all the tests that ``JavaJournalSpec`` +has, and also performs some longer operations on the Journal while printing its performance stats. While it is NOT aimed +to provide a proper benchmarking environment it can be used to get a rough feel about your journal's performance in the most +typical scenarios. + +In order to include the ``SnapshotStore`` TCK tests in your test suite simply extend the ``SnapshotStoreSpec``: + +.. includecode:: ./code/docs/persistence/LambdaPersistencePluginDocTest.java#snapshot-store-tck-java + +In case your plugin requires some setting up (starting a mock database, removing temporary files etc.) you can override the +``beforeAll`` and ``afterAll`` methods to hook into the tests lifecycle: + +.. includecode:: ./code/docs/persistence/LambdaPersistencePluginDocTest.java#journal-tck-before-after-java + +We *highly recommend* including these specifications in your test suite, as they cover a broad range of cases you +might have otherwise forgotten to test for when writing a plugin from scratch. + Pre-packaged plugins ==================== -.. _local-leveldb-journal-java-lambda: +.. _local-leveldb-journal-java: Local LevelDB journal --------------------- @@ -862,7 +956,7 @@ directory. This location can be changed by configuration where the specified pat With this plugin, each actor system runs its own private LevelDB instance. -.. _shared-leveldb-journal-java-lambda: +.. _shared-leveldb-journal-java: Shared LevelDB journal ---------------------- @@ -878,11 +972,11 @@ backup node. .. note:: - This plugin has been supplanted by :ref:`Persistence Plugin Proxy`. + This plugin has been supplanted by :ref:`Persistence Plugin Proxy`. A shared LevelDB instance is started by instantiating the ``SharedLeveldbStore`` actor. -.. includecode:: code/docs/persistence/PersistencePluginDocTest.java#shared-store-creation +.. includecode:: code/docs/persistence/LambdaPersistencePluginDocTest.java#shared-store-creation By default, the shared instance writes journaled messages to a local directory named ``journal`` in the current working directory. The storage location can be changed by configuration: @@ -897,12 +991,12 @@ plugin. This plugin must be initialized by injecting the (remote) ``SharedLeveldbStore`` actor reference. Injection is done by calling the ``SharedLeveldbJournal.setStore`` method with the actor reference as argument. -.. includecode:: code/docs/persistence/PersistencePluginDocTest.java#shared-store-usage +.. includecode:: code/docs/persistence/LambdaPersistencePluginDocTest.java#shared-store-usage Internal journal commands (sent by persistent actors) are buffered until injection completes. Injection is idempotent i.e. only the first injection is used. -.. _local-snapshot-store-java-lambda: +.. _local-snapshot-store-java: Local snapshot store -------------------- @@ -920,7 +1014,7 @@ directory. This can be changed by configuration where the specified path can be Note that it is not mandatory to specify a snapshot store plugin. If you don't use snapshots you don't have to configure it. -.. _persistence-plugin-proxy-lambda: +.. _persistence-plugin-proxy-java: Persistence Plugin Proxy ------------------------ @@ -955,7 +1049,7 @@ and ``target-snapshot-store-address`` configuration keys, or programmatically by The proxied persistence plugin can (and should) be configured using its original configuration keys. -.. _custom-serialization-lambda: +.. _custom-serialization-java: Custom serialization ==================== @@ -995,6 +1089,12 @@ in your Akka configuration. The LevelDB Java port is for testing purposes only. When testing Persistence based projects always rely on :ref:`asynchronous messaging using the TestKit `. +Configuration +============= + +There are several configuration properties for the persistence module, please refer +to the :ref:`reference configuration `. + Multiple persistence plugin configurations ========================================== diff --git a/akka-docs/rst/java/mailboxes.rst b/akka-docs/rst/java/mailboxes.rst index aaf257610d..d3c33378c8 100644 --- a/akka-docs/rst/java/mailboxes.rst +++ b/akka-docs/rst/java/mailboxes.rst @@ -17,7 +17,7 @@ It is possible to require a certain type of message queue for a certain type of by having that actor implement the parameterized interface :class:`RequiresMessageQueue`. Here is an example: -.. includecode:: code/docs/actor/MyBoundedUntypedActor.java#my-bounded-untyped-actor +.. includecode:: code/docs/actorlambda/MyBoundedActor.java#my-bounded-untyped-actor The type parameter to the :class:`RequiresMessageQueue` interface needs to be mapped to a mailbox in configuration like this: @@ -25,7 +25,7 @@ configuration like this: .. includecode:: ../scala/code/docs/dispatcher/DispatcherDocSpec.scala :include: bounded-mailbox-config,required-mailbox-config -Now every time you create an actor of type :class:`MyBoundedUntypedActor` it will try to get a bounded +Now every time you create an actor of type :class:`MyBoundedActor` it will try to get a bounded mailbox. If the actor has a different mailbox configured in deployment, either directly or via a dispatcher with a specified mailbox type, then that will override this mapping. diff --git a/akka-docs/rst/java/persistence.rst b/akka-docs/rst/java/persistence.rst deleted file mode 100644 index c821fbd0dc..0000000000 --- a/akka-docs/rst/java/persistence.rst +++ /dev/null @@ -1,1059 +0,0 @@ -.. _persistence-java: - -########### -Persistence -########### - -Akka persistence enables stateful actors to persist their internal state so that it can be recovered when an actor -is started, restarted after a JVM crash or by a supervisor, or migrated in a cluster. The key concept behind Akka -persistence is that only changes to an actor's internal state are persisted but never its current state directly -(except for optional snapshots). These changes are only ever appended to storage, nothing is ever mutated, which -allows for very high transaction rates and efficient replication. Stateful actors are recovered by replaying stored -changes to these actors from which they can rebuild internal state. This can be either the full history of changes -or starting from a snapshot which can dramatically reduce recovery times. Akka persistence also provides point-to-point -communication with at-least-once message delivery semantics. - -.. note:: - - Java 8 lambda expressions are also supported. (See section :ref:`persistence-lambda-java`) - -Akka persistence is inspired by and the official replacement of the `eventsourced`_ library. It follows the same -concepts and architecture of `eventsourced`_ but significantly differs on API and implementation level. See also -:ref:`migration-eventsourced-2.3` - -.. _eventsourced: https://github.com/eligosource/eventsourced - -Dependencies -============ - -Akka persistence is a separate jar file. Make sure that you have the following dependency in your project:: - - - com.typesafe.akka - akka-persistence_@binVersion@ - @version@ - - -The Akka persistence extension comes with few built-in persistence plugins, including -in-memory heap based journal, local file-system based snapshot-store and LevelDB based journal. - -LevelDB based plugins will require the following additional dependency declaration:: - - - org.iq80.leveldb - leveldb - 0.7 - - - org.fusesource.leveldbjni - leveldbjni-all - 1.8 - - -Architecture -============ - -* *UntypedPersistentActor*: Is a persistent, stateful actor. It is able to persist events to a journal and can react to - them in a thread-safe manner. It can be used to implement both *command* as well as *event sourced* actors. - When a persistent actor is started or restarted, journaled messages are replayed to that actor so that it can - recover internal state from these messages. - -* *UntypedPersistentActorAtLeastOnceDelivery*: To send messages with at-least-once delivery semantics to destinations, also in - case of sender and receiver JVM crashes. - -* *AsyncWriteJournal*: A journal stores the sequence of messages sent to a persistent actor. An application can control which messages - are journaled and which are received by the persistent actor without being journaled. Journal maintains *highestSequenceNr* that is increased on each message. - The storage backend of a journal is pluggable. The persistence extension comes with a "leveldb" journal plugin, which writes to the local filesystem. - Replicated journals are available as `Community plugins`_. - -* *Snapshot store*: A snapshot store persists snapshots of a persistent actor's or a view's internal state. Snapshots are - used for optimizing recovery times. The storage backend of a snapshot store is pluggable. - The persistence extension comes with a "local" snapshot storage plugin, which writes to the local filesystem. - Replicated snapshot stores are available as `Community plugins`_. - -.. _Community plugins: http://akka.io/community/ - -.. _event-sourcing-java: - -Event sourcing -============== - -The basic idea behind `Event Sourcing`_ is quite simple. A persistent actor receives a (non-persistent) command -which is first validated if it can be applied to the current state. Here validation can mean anything from simple -inspection of a command message's fields up to a conversation with several external services, for example. -If validation succeeds, events are generated from the command, representing the effect of the command. These events -are then persisted and, after successful persistence, used to change the actor's state. When the persistent actor -needs to be recovered, only the persisted events are replayed of which we know that they can be successfully applied. -In other words, events cannot fail when being replayed to a persistent actor, in contrast to commands. Event sourced -actors may of course also process commands that do not change application state such as query commands for example. - -.. _Event Sourcing: http://martinfowler.com/eaaDev/EventSourcing.html - -Akka persistence supports event sourcing with the ``UntypedPersistentActor`` abstract class. An actor that extends this -class uses the ``persist`` method to persist and handle events. The behavior of an ``UntypedPersistentActor`` -is defined by implementing ``receiveRecover`` and ``receiveCommand``. This is demonstrated in the following example. - -.. includecode:: ../../../akka-samples/akka-sample-persistence-java/src/main/java/sample/persistence/PersistentActorExample.java#persistent-actor-example - -The example defines two data types, ``Cmd`` and ``Evt`` to represent commands and events, respectively. The -``state`` of the ``ExamplePersistentActor`` is a list of persisted event data contained in ``ExampleState``. - -The persistent actor's ``onReceiveRecover`` method defines how ``state`` is updated during recovery by handling ``Evt`` -and ``SnapshotOffer`` messages. The persistent actor's ``onReceiveCommand`` method is a command handler. In this example, -a command is handled by generating two events which are then persisted and handled. Events are persisted by calling -``persist`` with an event (or a sequence of events) as first argument and an event handler as second argument. - -The ``persist`` method persists events asynchronously and the event handler is executed for successfully persisted -events. Successfully persisted events are internally sent back to the persistent actor as individual messages that trigger -event handler executions. An event handler may close over persistent actor state and mutate it. The sender of a persisted -event is the sender of the corresponding command. This allows event handlers to reply to the sender of a command -(not shown). - -The main responsibility of an event handler is changing persistent actor state using event data and notifying others -about successful state changes by publishing events. - -When persisting events with ``persist`` it is guaranteed that the persistent actor will not receive further commands between -the ``persist`` call and the execution(s) of the associated event handler. This also holds for multiple ``persist`` -calls in context of a single command. Incoming messages are :ref:`stashed ` until the ``persist`` -is completed. - -If persistence of an event fails, ``onPersistFailure`` will be invoked (logging the error by default), -and the actor will unconditionally be stopped. If persistence of an event is rejected before it is -stored, e.g. due to serialization error, ``onPersistRejected`` will be invoked (logging a warning -by default), and the actor continues with the next message. - -The easiest way to run this example yourself is to download `Lightbend Activator `_ -and open the tutorial named `Akka Persistence Samples with Java `_. -It contains instructions on how to run the ``PersistentActorExample``. - -.. note:: - - It's also possible to switch between different command handlers during normal processing and recovery - with ``getContext().become()`` and ``getContext().unbecome()``. To get the actor into the same state after - recovery you need to take special care to perform the same state transitions with ``become`` and - ``unbecome`` in the ``receiveRecover`` method as you would have done in the command handler. - Note that when using ``become`` from ``receiveRecover`` it will still only use the ``receiveRecover`` - behavior when replaying the events. When replay is completed it will use the new behavior. - -Identifiers ------------ - -A persistent actor must have an identifier that doesn't change across different actor incarnations. -The identifier must be defined with the ``persistenceId`` method. - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#persistence-id-override - - -.. note:: - ``persistenceId`` must be unique to a given entity in the journal (database table/keyspace). - When replaying messages persisted to the journal, you query messages with a ``persistenceId``. - So, if two different entities share the same ``persistenceId``, message-replaying - behavior is corrupted. - -.. _recovery-java: - -Recovery --------- - -By default, a persistent actor is automatically recovered on start and on restart by replaying journaled messages. -New messages sent to a persistent actor during recovery do not interfere with replayed messages. -They are cached and received by a persistent actor after recovery phase completes. - -.. note:: - Accessing the ``sender()`` for replayed messages will always result in a ``deadLetters`` reference, - as the original sender is presumed to be long gone. If you indeed have to notify an actor during - recovery in the future, store its ``ActorPath`` explicitly in your persisted events. - -.. _recovery-custom-java: - -Recovery customization -^^^^^^^^^^^^^^^^^^^^^^ - -Applications may also customise how recovery is performed by returning a customised ``Recovery`` object -in the ``recovery`` method of a ``UntypedPersistentActor``. - -To skip loading snapshots and replay all events you can use ``SnapshotSelectionCriteria.none()``. -This can be useful if snapshot serialization format has changed in an incompatible way. -It should typically not be used when events have been deleted. - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#recovery-no-snap - -Another example, which can be fun for experiments but probably not in a real application, is setting an -upper bound to the replay which allows the actor to be replayed to a certain point "in the past" -instead to its most up to date state. Note that after that it is a bad idea to persist new -events because a later recovery will probably be confused by the new events that follow the -events that were previously skipped. - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#recovery-custom - -Recovery can be disabled by returning ``Recovery.none()`` in the ``recovery`` method of a ``PersistentActor``: - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#recovery-disabled - -Recovery status -^^^^^^^^^^^^^^^ - -A persistent actor can query its own recovery status via the methods - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#recovery-status - -Sometimes there is a need for performing additional initialization when the -recovery has completed before processing any other message sent to the persistent actor. -The persistent actor will receive a special :class:`RecoveryCompleted` message right after recovery -and before any other received messages. - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#recovery-completed - -The actor will always receive a :class:`RecoveryCompleted` message, even if there are no events -in the journal and the snapshot store is empty, or if it's a new persistent actor with a previously -unused ``persistenceId``. - -If there is a problem with recovering the state of the actor from the journal, ``onRecoveryFailure`` -is called (logging the error by default) and the actor will be stopped. - -.. _internal-stash-java: - -Internal stash --------------- - -The persistent actor has a private :ref:`stash ` for internally caching incoming messages during -:ref:`recovery ` or the ``persist\persistAll`` method persisting events. You can still use/inherit -from the ``Stash`` interface. The internal stash cooperates with the normal stash by hooking into ``unstashAll`` -method and making sure messages are unstashed properly to the internal stash to maintain ordering guarantees. - -You should be careful to not send more messages to a persistent actor than it can keep up with, otherwise the number -of stashed messages will grow without bounds. It can be wise to protect against ``OutOfMemoryError`` by defining a -maximum stash capacity in the mailbox configuration:: - - akka.actor.default-mailbox.stash-capacity=10000 - -Note that the stash capacity is per actor. If you have many persistent actors, e.g. when using cluster sharding, -you may need to define a small stash capacity to ensure that the total number of stashed messages in the system -don't consume too much memory. Additionally, The persistent actor defines three strategies to handle failure when the -internal stash capacity is exceeded. The default overflow strategy is the ``ThrowOverflowExceptionStrategy``, which -discards the current received message and throws a ``StashOverflowException``, causing actor restart if default -supervision strategy is used. you can override the ``internalStashOverflowStrategy`` method to return -``DiscardToDeadLetterStrategy`` or ``ReplyToStrategy`` for any "individual" persistent actor, or define the "default" -for all persistent actors by providing FQCN, which must be a subclass of ``StashOverflowStrategyConfigurator``, in the -persistence configuration:: - - akka.persistence.internal-stash-overflow-strategy= - "akka.persistence.ThrowExceptionConfigurator" - -The ``DiscardToDeadLetterStrategy`` strategy also has a pre-packaged companion configurator -``akka.persistence.DiscardConfigurator``. - -You can also query default strategy via the Akka persistence extension singleton:: - - Persistence.get(context().system()).defaultInternalStashOverflowStrategy(); - -.. note:: - The bounded mailbox should be avoided in the persistent actor, by which the messages come from storage backends may - be discarded. You can use bounded stash instead of it. - - -.. _persist-async-java: - -Relaxed local consistency requirements and high throughput use-cases --------------------------------------------------------------------- - -If faced with relaxed local consistency requirements and high throughput demands sometimes ``PersistentActor`` and its -``persist`` may not be enough in terms of consuming incoming Commands at a high rate, because it has to wait until all -Events related to a given Command are processed in order to start processing the next Command. While this abstraction is -very useful for most cases, sometimes you may be faced with relaxed requirements about consistency – for example you may -want to process commands as fast as you can, assuming that the Event will eventually be persisted and handled properly in -the background, retroactively reacting to persistence failures if needed. - -The ``persistAsync`` method provides a tool for implementing high-throughput persistent actors. It will *not* -stash incoming Commands while the Journal is still working on persisting and/or user code is executing event callbacks. - -In the below example, the event callbacks may be called "at any time", even after the next Command has been processed. -The ordering between events is still guaranteed ("evt-b-1" will be sent after "evt-a-2", which will be sent after "evt-a-1" etc.). - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#persist-async - -.. note:: - In order to implement the pattern known as "*command sourcing*" simply ``persistAsync`` all incoming messages right away - and handle them in the callback. - -.. warning:: - The callback will not be invoked if the actor is restarted (or stopped) in between the call to - ``persistAsync`` and the journal has confirmed the write. - -.. _defer-java: - -Deferring actions until preceding persist handlers have executed ----------------------------------------------------------------- - -Sometimes when working with ``persistAsync`` you may find that it would be nice to define some actions in terms of -''happens-after the previous ``persistAsync`` handlers have been invoked''. ``PersistentActor`` provides an utility method -called ``deferAsync``, which works similarly to ``persistAsync`` yet does not persist the passed in event. It is recommended to -use it for *read* operations, and actions which do not have corresponding events in your domain model. - -Using this method is very similar to the persist family of methods, yet it does **not** persist the passed in event. -It will be kept in memory and used when invoking the handler. - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#defer - -Notice that the ``sender()`` is **safe** to access in the handler callback, and will be pointing to the original sender -of the command for which this ``deferAsync`` handler was called. - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#defer-caller - -.. warning:: - The callback will not be invoked if the actor is restarted (or stopped) in between the call to - ``deferAsync`` and the journal has processed and confirmed all preceding writes. - -.. _nested-persist-calls-java: - -Nested persist calls --------------------- -It is possible to call ``persist`` and ``persistAsync`` inside their respective callback blocks and they will properly -retain both the thread safety (including the right value of ``sender()``) as well as stashing guarantees. - -In general it is encouraged to create command handlers which do not need to resort to nested event persisting, -however there are situations where it may be useful. It is important to understand the ordering of callback execution in -those situations, as well as their implication on the stashing behaviour (that ``persist()`` enforces). In the following -example two persist calls are issued, and each of them issues another persist inside its callback: - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#nested-persist-persist - -When sending two commands to this ``PersistentActor``, the persist handlers will be executed in the following order: - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#nested-persist-persist-caller - -First the "outer layer" of persist calls is issued and their callbacks are applied. After these have successfully completed, -the inner callbacks will be invoked (once the events they are persisting have been confirmed to be persisted by the journal). -Only after all these handlers have been successfully invoked will the next command be delivered to the persistent Actor. -In other words, the stashing of incoming commands that is guaranteed by initially calling ``persist()`` on the outer layer -is extended until all nested ``persist`` callbacks have been handled. - -It is also possible to nest ``persistAsync`` calls, using the same pattern: - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#nested-persistAsync-persistAsync - -In this case no stashing is happening, yet events are still persisted and callbacks are executed in the expected order: - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#nested-persistAsync-persistAsync-caller - -While it is possible to nest mixed ``persist`` and ``persistAsync`` with keeping their respective semantics -it is not a recommended practice, as it may lead to overly complex nesting. - -.. warning:: - While it is possible to nest ``persist`` calls within one another, - it is *not* legal call ``persist`` from any other Thread than the Actors message processing Thread. - For example, it is not legal to call ``persist`` from Futures! Doing so will break the guarantees - that the persist methods aim to provide. Always call ``persist`` and ``persistAsync`` from within - the Actor's receive block (or methods synchronously invoked from there). - -.. _failures-java: - -Failures --------- - -If persistence of an event fails, ``onPersistFailure`` will be invoked (logging the error by default), -and the actor will unconditionally be stopped. - -The reason that it cannot resume when persist fails is that it is unknown if the event was actually -persisted or not, and therefore it is in an inconsistent state. Restarting on persistent failures -will most likely fail anyway since the journal is probably unavailable. It is better to stop the -actor and after a back-off timeout start it again. The ``akka.pattern.BackoffSupervisor`` actor -is provided to support such restarts. - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#backoff - -If persistence of an event is rejected before it is stored, e.g. due to serialization error, -``onPersistRejected`` will be invoked (logging a warning by default), and the actor continues with -next message. - -If there is a problem with recovering the state of the actor from the journal when the actor is -started, ``onRecoveryFailure`` is called (logging the error by default), and the actor will be stopped. -Note that failure to load snapshot is also treated like this, but you can disable loading of snapshots -if you for example know that serialization format has changed in an incompatible way, see :ref:`recovery-custom-java`. - -Atomic writes -------------- - -Each event is of course stored atomically, but it is also possible to store several events atomically by -using the ``persistAll`` or ``persistAllAsync`` method. That means that all events passed to that method -are stored or none of them are stored if there is an error. - -The recovery of a persistent actor will therefore never be done partially with only a subset of events persisted by -`persistAll`. - -Some journals may not support atomic writes of several events and they will then reject the ``persistAll`` -command, i.e. ``onPersistRejected`` is called with an exception (typically ``UnsupportedOperationException``). - -Batch writes ------------- - -In order to optimize throughput when using ``persistAsync``, a persistent actor -internally batches events to be stored under high load before writing them to -the journal (as a single batch). The batch size is dynamically determined by -how many events are emitted during the time of a journal round-trip: after -sending a batch to the journal no further batch can be sent before confirmation -has been received that the previous batch has been written. Batch writes are never -timer-based which keeps latencies at a minimum. - -Message deletion ----------------- - -It is possible to delete all messages (journaled by a single persistent actor) up to a specified sequence number; -Persistent actors may call the ``deleteMessages`` method to this end. - -Deleting messages in event sourcing based applications is typically either not used at all, or used in conjunction with -:ref:`snapshotting `, i.e. after a snapshot has been successfully stored, a ``deleteMessages(toSequenceNr)`` -up until the sequence number of the data held by that snapshot can be issued to safely delete the previous events -while still having access to the accumulated state during replays - by loading the snapshot. - -.. warning:: - If you are using :ref:`persistence-query-java`, query results may be missing deleted messages in a journal, - depending on how deletions are implemented in the journal plugin. - Unless you use a plugin which still shows deleted messages in persistence query results, - you have to design your application so that it is not affected by missing messages. - -The result of the ``deleteMessages`` request is signaled to the persistent actor with a ``DeleteMessagesSuccess`` -message if the delete was successful or a ``DeleteMessagesFailure`` message if it failed. - -Message deletion doesn't affect the highest sequence number of the journal, even if all messages were deleted from it after ``deleteMessages`` invocation. - -Persistence status handling ---------------------------- -Persisting, deleting, and replaying messages can either succeed or fail. - -+---------------------------------+-----------------------------+-------------------------------+-----------------------------------+ -| **Method** | **Success** | **Failure / Rejection** | **After failure handler invoked** | -+---------------------------------+-----------------------------+-------------------------------+-----------------------------------+ -| ``persist`` / ``persistAsync`` | persist handler invoked | ``onPersistFailure`` | Actor is stopped. | -| | +-------------------------------+-----------------------------------+ -| | | ``onPersistRejected`` | No automatic actions. | -+---------------------------------+-----------------------------+-------------------------------+-----------------------------------+ -| ``recovery`` | ``RecoverySuccess`` | ``onRecoveryFailure`` | Actor is stopped. | -+---------------------------------+-----------------------------+-------------------------------+-----------------------------------+ -| ``deleteMessages`` | ``DeleteMessagesSuccess`` | ``DeleteMessagesFailure`` | No automatic actions. | -+---------------------------------+-----------------------------+-------------------------------+-----------------------------------+ - -The most important operations (``persist`` and ``recovery``) have failure handlers modelled as explicit callbacks which -the user can override in the ``PersistentActor``. The default implementations of these handlers emit a log message -(``error`` for persist/recovery failures, and ``warning`` for others), logging the failure cause and information about -which message caused the failure. - -For critical failures such as recovery or persisting events failing the persistent actor will be stopped after the failure -handler is invoked. This is because if the underlying journal implementation is signalling persistence failures it is most -likely either failing completely or overloaded and restarting right-away and trying to persist the event again will most -likely not help the journal recover – as it would likely cause a `Thundering herd problem`_, as many persistent actors -would restart and try to persist their events again. Instead, using a ``BackoffSupervisor`` (as described in :ref:`failures-java`) which -implements an exponential-backoff strategy which allows for more breathing room for the journal to recover between -restarts of the persistent actor. - -.. note:: - Journal implementations may choose to implement a retry mechanism, e.g. such that only after a write fails N number - of times a persistence failure is signalled back to the user. In other words, once a journal returns a failure, - it is considered *fatal* by Akka Persistence, and the persistent actor which caused the failure will be stopped. - - Check the documentation of the journal implementation you are using for details if/how it is using this technique. - -.. _Thundering herd problem: https://en.wikipedia.org/wiki/Thundering_herd_problem - -.. _safe-shutdown-java: - -Safely shutting down persistent actors --------------------------------------- - -Special care should be given when shutting down persistent actors from the outside. -With normal Actors it is often acceptable to use the special :ref:`PoisonPill ` message -to signal to an Actor that it should stop itself once it receives this message – in fact this message is handled -automatically by Akka, leaving the target actor no way to refuse stopping itself when given a poison pill. - -This can be dangerous when used with :class:`PersistentActor` due to the fact that incoming commands are *stashed* while -the persistent actor is awaiting confirmation from the Journal that events have been written when ``persist()`` was used. -Since the incoming commands will be drained from the Actor's mailbox and put into its internal stash while awaiting the -confirmation (thus, before calling the persist handlers) the Actor **may receive and (auto)handle the PoisonPill -before it processes the other messages which have been put into its stash**, causing a pre-mature shutdown of the Actor. - -.. warning:: - Consider using explicit shut-down messages instead of :class:`PoisonPill` when working with persistent actors. - -The example below highlights how messages arrive in the Actor's mailbox and how they interact with its internal stashing -mechanism when ``persist()`` is used. Notice the early stop behaviour that occurs when ``PoisonPill`` is used: - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#safe-shutdown -.. includecode:: code/docs/persistence/PersistenceDocTest.java#safe-shutdown-example-bad -.. includecode:: code/docs/persistence/PersistenceDocTest.java#safe-shutdown-example-good - - -.. _replay-filter-java: - -Replay Filter -------------- -There could be cases where event streams are corrupted and multiple writers (i.e. multiple persistent actor instances) -journaled different messages with the same sequence number. -In such a case, you can configure how you filter replayed messages from multiple writers, upon recovery. - -In your configuration, under the ``akka.persistence.journal.xxx.replay-filter`` section (where ``xxx`` is your journal plugin id), -you can select the replay filter ``mode`` from one of the following values: - -* repair-by-discard-old -* fail -* warn -* off - -For example, if you configure the replay filter for leveldb plugin, it looks like this:: - - # The replay filter can detect a corrupt event stream by inspecting - # sequence numbers and writerUuid when replaying events. - akka.persistence.journal.leveldb.replay-filter { - # What the filter should do when detecting invalid events. - # Supported values: - # `repair-by-discard-old` : discard events from old writers, - # warning is logged - # `fail` : fail the replay, error is logged - # `warn` : log warning but emit events untouched - # `off` : disable this feature completely - mode = repair-by-discard-old - } - - -Snapshots -========= - -Snapshots can dramatically reduce recovery times of persistent actors and views. The following discusses snapshots -in context of persistent actors but this is also applicable to persistent views. - -Persistent actors can save snapshots of internal state by calling the ``saveSnapshot`` method. If saving of a snapshot -succeeds, the persistent actor receives a ``SaveSnapshotSuccess`` message, otherwise a ``SaveSnapshotFailure`` message. - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#save-snapshot - -During recovery, the persistent actor is offered a previously saved snapshot via a ``SnapshotOffer`` message from -which it can initialize internal state. - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#snapshot-offer - -The replayed messages that follow the ``SnapshotOffer`` message, if any, are younger than the offered snapshot. -They finally recover the persistent actor to its current (i.e. latest) state. - -In general, a persistent actor is only offered a snapshot if that persistent actor has previously saved one or more snapshots -and at least one of these snapshots matches the ``SnapshotSelectionCriteria`` that can be specified for recovery. - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#snapshot-criteria - -If not specified, they default to ``SnapshotSelectionCriteria.latest()`` which selects the latest (= youngest) snapshot. -To disable snapshot-based recovery, applications should use ``SnapshotSelectionCriteria.none()``. A recovery where no -saved snapshot matches the specified ``SnapshotSelectionCriteria`` will replay all journaled messages. - -.. note:: - In order to use snapshots, a default snapshot-store (``akka.persistence.snapshot-store.plugin``) must be configured, - or the persistent actor can pick a snapshot store explicitly by overriding ``String snapshotPluginId()``. - - Since it is acceptable for some applications to not use any snapshotting, it is legal to not configure a snapshot store. - However, Akka will log a warning message when this situation is detected and then continue to operate until - an actor tries to store a snapshot, at which point the operation will fail (by replying with an ``SaveSnapshotFailure`` for example). - - Note that :ref:`cluster_sharding_java` is using snapshots, so if you use Cluster Sharding you need to define a snapshot store plugin. - -Snapshot deletion ------------------ - -A persistent actor can delete individual snapshots by calling the ``deleteSnapshot`` method with the sequence number of -when the snapshot was taken. - -To bulk-delete a range of snapshots matching ``SnapshotSelectionCriteria``, -persistent actors should use the ``deleteSnapshots`` method. - -Snapshot status handling ------------------------- - -Saving or deleting snapshots can either succeed or fail – this information is reported back to the persistent actor via -status messages as illustrated in the following table. - -============================================== ========================== ============================== -**Method** **Success** **Failure message** -============================================== ========================== ============================== -``saveSnapshot(Any)`` ``SaveSnapshotSuccess`` ``SaveSnapshotFailure`` -``deleteSnapshot(Long)`` ``DeleteSnapshotSuccess`` ``DeleteSnapshotFailure`` -``deleteSnapshots(SnapshotSelectionCriteria)`` ``DeleteSnapshotsSuccess`` ``DeleteSnapshotsFailure`` -============================================== ========================== ============================== - -.. _at-least-once-delivery-java: - -At-Least-Once Delivery -====================== - -To send messages with at-least-once delivery semantics to destinations you can extend the ``UntypedPersistentActorWithAtLeastOnceDelivery`` -class instead of ``UntypedPersistentActor`` on the sending side. It takes care of re-sending messages when they -have not been confirmed within a configurable timeout. - -The state of the sending actor, including which messages have been sent that have not been -confirmed by the recipient must be persistent so that it can survive a crash of the sending actor -or JVM. The ``UntypedPersistentActorWithAtLeastOnceDelivery`` class does not persist anything by itself. -It is your responsibility to persist the intent that a message is sent and that a confirmation has been -received. - -.. note:: - - At-least-once delivery implies that original message sending order is not always preserved, - and the destination may receive duplicate messages. Semantics do not match those of a normal :class:`ActorRef` send operation: - - * it is not at-most-once delivery - - * message order for the same sender–receiver pair is not preserved due to - possible resends - - * after a crash and restart of the destination messages are still - delivered to the new actor incarnation - - These semantics are similar to what an :class:`ActorPath` represents (see - :ref:`actor-lifecycle-scala`), therefore you need to supply a path and not a - reference when delivering messages. The messages are sent to the path with - an actor selection. - -Use the ``deliver`` method to send a message to a destination. Call the ``confirmDelivery`` method -when the destination has replied with a confirmation message. - -Relationship between deliver and confirmDelivery ------------------------------------------------- - -To send messages to the destination path, use the ``deliver`` method after you have persisted the intent -to send the message. - -The destination actor must send back a confirmation message. When the sending actor receives this -confirmation message you should persist the fact that the message was delivered successfully and then call -the ``confirmDelivery`` method. - -If the persistent actor is not currently recovering, the ``deliver`` method will send the message to -the destination actor. When recovering, messages will be buffered until they have been confirmed using ``confirmDelivery``. -Once recovery has completed, if there are outstanding messages that have not been confirmed (during the message replay), -the persistent actor will resend these before sending any other messages. - -Deliver requires a ``deliveryIdToMessage`` function to pass the provided ``deliveryId`` into the message so that the correlation -between ``deliver`` and ``confirmDelivery`` is possible. The ``deliveryId`` must do the round trip. Upon receipt -of the message, the destination actor will send the same``deliveryId`` wrapped in a confirmation message back to the sender. -The sender will then use it to call the ``confirmDelivery`` method to complete the delivery routine. - -.. includecode:: code/docs/persistence/PersistenceDocTest.java#at-least-once-example - -The ``deliveryId`` generated by the persistence module is a strictly monotonically increasing sequence number -without gaps. The same sequence is used for all destinations of the actor, i.e. when sending to multiple -destinations the destinations will see gaps in the sequence. It is not possible to use custom ``deliveryId``. -However, you can send a custom correlation identifier in the message to the destination. You must then retain -a mapping between the internal ``deliveryId`` (passed into the ``deliveryIdToMessage`` function) and your custom -correlation id (passed into the message). You can do this by storing such mapping in a ``Map(correlationId -> deliveryId)`` -from which you can retrieve the ``deliveryId`` to be passed into the ``confirmDelivery`` method once the receiver -of your message has replied with your custom correlation id. - -The ``UntypedPersistentActorWithAtLeastOnceDelivery`` class has a state consisting of unconfirmed messages and a -sequence number. It does not store this state itself. You must persist events corresponding to the -``deliver`` and ``confirmDelivery`` invocations from your ``PersistentActor`` so that the state can -be restored by calling the same methods during the recovery phase of the ``PersistentActor``. Sometimes -these events can be derived from other business level events, and sometimes you must create separate events. -During recovery, calls to ``deliver`` will not send out messages, those will be sent later -if no matching ``confirmDelivery`` will have been performed. - -Support for snapshots is provided by ``getDeliverySnapshot`` and ``setDeliverySnapshot``. -The ``AtLeastOnceDeliverySnapshot`` contains the full delivery state, including unconfirmed messages. -If you need a custom snapshot for other parts of the actor state you must also include the -``AtLeastOnceDeliverySnapshot``. It is serialized using protobuf with the ordinary Akka -serialization mechanism. It is easiest to include the bytes of the ``AtLeastOnceDeliverySnapshot`` -as a blob in your custom snapshot. - -The interval between redelivery attempts is defined by the ``redeliverInterval`` method. -The default value can be configured with the ``akka.persistence.at-least-once-delivery.redeliver-interval`` -configuration key. The method can be overridden by implementation classes to return non-default values. - -The maximum number of messages that will be sent at each redelivery burst is defined by the -``redeliveryBurstLimit`` method (burst frequency is half of the redelivery interval). If there's a lot of -unconfirmed messages (e.g. if the destination is not available for a long time), this helps to prevent an overwhelming -amount of messages to be sent at once. The default value can be configured with the -``akka.persistence.at-least-once-delivery.redelivery-burst-limit`` configuration key. The method can be overridden -by implementation classes to return non-default values. - -After a number of delivery attempts a ``AtLeastOnceDelivery.UnconfirmedWarning`` message -will be sent to ``self``. The re-sending will still continue, but you can choose to call -``confirmDelivery`` to cancel the re-sending. The number of delivery attempts before emitting the -warning is defined by the ``warnAfterNumberOfUnconfirmedAttempts`` method. The default value can be -configured with the ``akka.persistence.at-least-once-delivery.warn-after-number-of-unconfirmed-attempts`` -configuration key. The method can be overridden by implementation classes to return non-default values. - -The ``UntypedPersistentActorWithAtLeastOnceDelivery`` class holds messages in memory until their successful delivery has been confirmed. -The maximum number of unconfirmed messages that the actor is allowed to hold in memory -is defined by the ``maxUnconfirmedMessages`` method. If this limit is exceed the ``deliver`` method will -not accept more messages and it will throw ``AtLeastOnceDelivery.MaxUnconfirmedMessagesExceededException``. -The default value can be configured with the ``akka.persistence.at-least-once-delivery.max-unconfirmed-messages`` -configuration key. The method can be overridden by implementation classes to return non-default values. - -.. _event-adapters-java: - -Event Adapters -============== - -In long running projects using event sourcing sometimes the need arises to detach the data model from the domain model -completely. - -Event Adapters help in situations where: - -- **Version Migrations** – existing events stored in *Version 1* should be "upcasted" to a new *Version 2* representation, - and the process of doing so involves actual code, not just changes on the serialization layer. For these scenarios - the ``toJournal`` function is usually an identity function, however the ``fromJournal`` is implemented as - ``v1.Event=>v2.Event``, performing the neccessary mapping inside the fromJournal method. - This technique is sometimes refered to as "upcasting" in other CQRS libraries. -- **Separating Domain and Data models** – thanks to EventAdapters it is possible to completely separate the domain model - from the model used to persist data in the Journals. For example one may want to use case classes in the - domain model, however persist their protocol-buffer (or any other binary serialization format) counter-parts to the Journal. - A simple ``toJournal:MyModel=>MyDataModel`` and ``fromJournal:MyDataModel=>MyModel`` adapter can be used to implement this feature. -- **Journal Specialized Data Types** – exposing data types understood by the underlying Journal, for example for data stores which - understand JSON it is possible to write an EventAdapter ``toJournal:Any=>JSON`` such that the Journal can *directly* store the - json instead of serializing the object to its binary representation. - -Implementing an EventAdapter is rather stright forward: - -.. includecode:: code/docs/persistence/PersistenceEventAdapterDocTest.java#identity-event-adapter - -Then in order for it to be used on events coming to and from the journal you must bind it using the below configuration syntax: - -.. includecode:: ../scala/code/docs/persistence/PersistenceEventAdapterDocSpec.scala#event-adapters-config - -It is possible to bind multiple adapters to one class *for recovery*, in which case the ``fromJournal`` methods of all -bound adapters will be applied to a given matching event (in order of definition in the configuration). Since each adapter may -return from ``0`` to ``n`` adapted events (called as ``EventSeq``), each adapter can investigate the event and if it should -indeed adapt it return the adapted event(s) for it. Other adapters which do not have anything to contribute during this -adaptation simply return ``EventSeq.empty``. The adapted events are then delivered in-order to the ``PersistentActor`` during replay. - -.. note:: - For more advanced schema evolution techniques refer to the :ref:`persistence-schema-evolution-scala` documentation. - -Storage plugins -=============== - -Storage backends for journals and snapshot stores are pluggable in the Akka persistence extension. - -A directory of persistence journal and snapshot store plugins is available at the Akka Community Projects page, see `Community plugins`_ - -Plugins can be selected either by "default", for all persistent actors and views, -or "individually", when a persistent actor or view defines its own set of plugins. - -When a persistent actor or view does NOT override the ``journalPluginId`` and ``snapshotPluginId`` methods, -the persistence extension will use the "default" journal and snapshot-store plugins configured in the ``reference.conf``:: - - akka.persistence.journal.plugin = "" - akka.persistence.snapshot-store.plugin = "" - -However, these entries are provided as empty "", and require explicit user configuration via override in the user ``application.conf``. -For an example of a journal plugin which writes messages to LevelDB see :ref:`local-leveldb-journal-java`. -For an example of a snapshot store plugin which writes snapshots as individual files to the local filesystem see :ref:`local-snapshot-store-java`. - -Applications can provide their own plugins by implementing a plugin API and activating them by configuration. -Plugin development requires the following imports: - -.. includecode:: code/docs/persistence/PersistencePluginDocTest.java#plugin-imports - -Eager initialization of persistence plugin ------------------------------------------- - -By default, persistence plugins are started on-demand, as they are used. In some case, however, it might be beneficial -to start a certain plugin eagerly. In order to do that, you should first add the ``akka.persistence.Persistence`` -under the ``akka.extensions`` key. Then, specify the IDs of plugins you wish to start automatically under -``akka.persistence.journal.auto-start-journals`` and ``akka.persistence.snapshot-store.auto-start-snapshot-stores``. - -.. _journal-plugin-api-java: - -Journal plugin API ------------------- - -A journal plugin extends ``AsyncWriteJournal``. - -``AsyncWriteJournal`` is an actor and the methods to be implemented are: - -.. includecode:: ../../../akka-persistence/src/main/java/akka/persistence/journal/japi/AsyncWritePlugin.java#async-write-plugin-api - -If the storage backend API only supports synchronous, blocking writes, the methods should be implemented as: - -.. includecode:: code/docs/persistence/PersistencePluginDocTest.java#sync-journal-plugin-api - -A journal plugin must also implement the methods defined in ``AsyncRecovery`` for replays and sequence number recovery: - -.. includecode:: ../../../akka-persistence/src/main/java/akka/persistence/journal/japi/AsyncRecoveryPlugin.java#async-replay-plugin-api - -A journal plugin can be activated with the following minimal configuration: - -.. includecode:: ../scala/code/docs/persistence/PersistencePluginDocSpec.scala#journal-plugin-config - -The specified plugin ``class`` must have a no-arg constructor. The ``plugin-dispatcher`` is the dispatcher -used for the plugin actor. If not specified, it defaults to ``akka.persistence.dispatchers.default-plugin-dispatcher``. - -The journal plugin instance is an actor so the methods corresponding to requests from persistent actors -are executed sequentially. It may delegate to asynchronous libraries, spawn futures, or delegate to other -actors to achive parallelism. - -The journal plugin class must have a constructor with one of these signatures: - -* constructor with one ``com.typesafe.config.Config`` parameter and a ``String`` parameter for the config path -* constructor with one ``com.typesafe.config.Config`` parameter -* constructor without parameters - -The plugin section of the actor system's config will be passed in the config constructor parameter. The config path -of the plugin is passed in the ``String`` parameter. - -Don't run journal tasks/futures on the system default dispatcher, since that might starve other tasks. - -Snapshot store plugin API -------------------------- - -A snapshot store plugin must extend the ``SnapshotStore`` actor and implement the following methods: - -.. includecode:: ../../../akka-persistence/src/main/java/akka/persistence/snapshot/japi/SnapshotStorePlugin.java#snapshot-store-plugin-api - -A snapshot store plugin can be activated with the following minimal configuration: - -.. includecode:: ../scala/code/docs/persistence/PersistencePluginDocSpec.scala#snapshot-store-plugin-config - -The snapshot store instance is an actor so the methods corresponding to requests from persistent actors -are executed sequentially. It may delegate to asynchronous libraries, spawn futures, or delegate to other -actors to achive parallelism. - -The snapshot store plugin class must have a constructor with one of these signatures: - -* constructor with one ``com.typesafe.config.Config`` parameter and a ``String`` parameter for the config path -* constructor with one ``com.typesafe.config.Config`` parameter -* constructor without parameters - -The plugin section of the actor system's config will be passed in the config constructor parameter. The config path -of the plugin is passed in the ``String`` parameter. - -The ``plugin-dispatcher`` is the dispatcher used for the plugin actor. If not specified, it defaults to -``akka.persistence.dispatchers.default-plugin-dispatcher``. - -Don't run snapshot store tasks/futures on the system default dispatcher, since that might starve other tasks. - -Plugin TCK ----------- -In order to help developers build correct and high quality storage plugins, we provide a Technology Compatibility Kit (`TCK `_ for short). - -The TCK is usable from Java as well as Scala projects. For Java you need to include the akka-persistence-tck dependency:: - - - com.typesafe.akka - akka-persistence-tck_${scala.version} - @version@ - test - - -To include the Journal TCK tests in your test suite simply extend the provided ``JavaJournalSpec``: - -.. includecode:: ./code/docs/persistence/PersistencePluginDocTest.java#journal-tck-java - -Please note that some of the tests are optional, and by overriding the ``supports...`` methods you give the -TCK the needed information about which tests to run. You can implement these methods using the provided -``CapabilityFlag.on`` / ``CapabilityFlag.off`` values. - -We also provide a simple benchmarking class ``JavaJournalPerfSpec`` which includes all the tests that ``JavaJournalSpec`` -has, and also performs some longer operations on the Journal while printing its performance stats. While it is NOT aimed -to provide a proper benchmarking environment it can be used to get a rough feel about your journal's performance in the most -typical scenarios. - -In order to include the ``SnapshotStore`` TCK tests in your test suite simply extend the ``SnapshotStoreSpec``: - -.. includecode:: ./code/docs/persistence/PersistencePluginDocTest.java#snapshot-store-tck-java - -In case your plugin requires some setting up (starting a mock database, removing temporary files etc.) you can override the -``beforeAll`` and ``afterAll`` methods to hook into the tests lifecycle: - -.. includecode:: ./code/docs/persistence/PersistencePluginDocTest.java#journal-tck-before-after-java - -We *highly recommend* including these specifications in your test suite, as they cover a broad range of cases you -might have otherwise forgotten to test for when writing a plugin from scratch. - -Pre-packaged plugins -==================== - -.. _local-leveldb-journal-java: - -Local LevelDB journal ---------------------- - -The LevelDB journal plugin config entry is ``akka.persistence.journal.leveldb``. It writes messages to a local LevelDB -instance. Enable this plugin by defining config property: - -.. includecode:: ../scala/code/docs/persistence/PersistencePluginDocSpec.scala#leveldb-plugin-config - -LevelDB based plugins will also require the following additional dependency declaration:: - - - org.iq80.leveldb - leveldb - 0.7 - - - org.fusesource.leveldbjni - leveldbjni-all - 1.8 - - -The default location of the LevelDB files is a directory named ``journal`` in the current working -directory. This location can be changed by configuration where the specified path can be relative or absolute: - -.. includecode:: ../scala/code/docs/persistence/PersistencePluginDocSpec.scala#journal-config - -With this plugin, each actor system runs its own private LevelDB instance. - -.. _shared-leveldb-journal-java: - -Shared LevelDB journal ----------------------- - -A LevelDB instance can also be shared by multiple actor systems (on the same or on different nodes). This, for -example, allows persistent actors to failover to a backup node and continue using the shared journal instance from the -backup node. - -.. warning:: - - A shared LevelDB instance is a single point of failure and should therefore only be used for testing - purposes. Highly-available, replicated journals are available as `Community plugins`_. - -.. note:: - - This plugin has been supplanted by :ref:`Persistence Plugin Proxy`. - -A shared LevelDB instance is started by instantiating the ``SharedLeveldbStore`` actor. - -.. includecode:: code/docs/persistence/PersistencePluginDocTest.java#shared-store-creation - -By default, the shared instance writes journaled messages to a local directory named ``journal`` in the current -working directory. The storage location can be changed by configuration: - -.. includecode:: ../scala/code/docs/persistence/PersistencePluginDocSpec.scala#shared-store-config - -Actor systems that use a shared LevelDB store must activate the ``akka.persistence.journal.leveldb-shared`` -plugin. - -.. includecode:: ../scala/code/docs/persistence/PersistencePluginDocSpec.scala#shared-journal-config - -This plugin must be initialized by injecting the (remote) ``SharedLeveldbStore`` actor reference. Injection is -done by calling the ``SharedLeveldbJournal.setStore`` method with the actor reference as argument. - -.. includecode:: code/docs/persistence/PersistencePluginDocTest.java#shared-store-usage - -Internal journal commands (sent by persistent actors) are buffered until injection completes. Injection is idempotent -i.e. only the first injection is used. - -.. _local-snapshot-store-java: - -Local snapshot store --------------------- - -The local snapshot store plugin config entry is ``akka.persistence.snapshot-store.local``. It writes snapshot files to -the local filesystem. Enable this plugin by defining config property: - -.. includecode:: ../scala/code/docs/persistence/PersistencePluginDocSpec.scala#leveldb-snapshot-plugin-config - -The default storage location is a directory named ``snapshots`` in the current working -directory. This can be changed by configuration where the specified path can be relative or absolute: - -.. includecode:: ../scala/code/docs/persistence/PersistencePluginDocSpec.scala#snapshot-config - -Note that it is not mandatory to specify a snapshot store plugin. If you don't use snapshots -you don't have to configure it. - -.. _persistence-plugin-proxy-java: - -Persistence Plugin Proxy ------------------------- - -A persistence plugin proxy allows sharing of journals and snapshot stores across multiple actor systems (on the same or -on different nodes). This, for example, allows persistent actors to failover to a backup node and continue using the -shared journal instance from the backup node. The proxy works by forwarding all the journal/snapshot store messages to a -single, shared, persistence plugin instance, and therefor supports any use case supported by the proxied plugin. - -.. warning:: - - A shared journal/snapshot store is a single point of failure and should therefore only be used for testing - purposes. Highly-available, replicated persistence plugins are available as `Community plugins`_. - -The journal and snapshot store proxies are controlled via the ``akka.persistence.journal.proxy`` and -``akka.persistence.snapshot-store.proxy`` configuration entries, respectively. Set the ``target-journal-plugin`` or -``target-snapshot-store-plugin`` keys to the underlying plugin you wish to use (for example: -``akka.persistence.journal.leveldb``). The ``start-target-journal`` and ``start-target-snapshot-store`` keys should be -set to ``on`` in exactly one actor system - this is the system that will instantiate the shared persistence plugin. -Next, the proxy needs to be told how to find the shared plugin. This can be done by setting the ``target-journal-address`` -and ``target-snapshot-store-address`` configuration keys, or programmatically by calling the -``PersistencePluginProxy.setTargetLocation`` method. - -.. note:: - - Akka starts extensions lazily when they are required, and this includes the proxy. This means that in order for the - proxy to work, the persistence plugin on the target node must be instantiated. This can be done by instantiating the - ``PersistencePluginProxyExtension`` :ref:`extension`, or by calling the ``PersistencePluginProxy.start`` method. - -.. note:: - - The proxied persistence plugin can (and should) be configured using its original configuration keys. - - -.. _custom-serialization-java: - -Custom serialization -==================== - -Serialization of snapshots and payloads of ``Persistent`` messages is configurable with Akka's -:ref:`serialization-java` infrastructure. For example, if an application wants to serialize - -* payloads of type ``MyPayload`` with a custom ``MyPayloadSerializer`` and -* snapshots of type ``MySnapshot`` with a custom ``MySnapshotSerializer`` - -it must add - -.. includecode:: ../scala/code/docs/persistence/PersistenceSerializerDocSpec.scala#custom-serializer-config - -to the application configuration. If not specified, a default serializer is used. - -For more advanced schema evolution techniques refer to the :ref:`persistence-schema-evolution-scala` documentation. - -Testing -======= - -When running tests with LevelDB default settings in ``sbt``, make sure to set ``fork := true`` in your sbt project. -Otherwise, you'll see an ``UnsatisfiedLinkError``. Alternatively, you can switch to a LevelDB Java port by setting - -.. includecode:: ../scala/code/docs/persistence/PersistencePluginDocSpec.scala#native-config - -or - -.. includecode:: ../scala/code/docs/persistence/PersistencePluginDocSpec.scala#shared-store-native-config - -in your Akka configuration. The LevelDB Java port is for testing purposes only. - -.. warning:: - It is not possible to test persistence provided classes (i.e. :ref:`PersistentActor ` - and :ref:`AtLeastOnceDelivery `) using ``TestActorRef`` due to its *synchronous* nature. - These traits need to be able to perform asynchronous tasks in the background in order to handle internal persistence - related events. - - When testing Persistence based projects always rely on :ref:`asynchronous messaging using the TestKit `. - -Configuration -============= - -There are several configuration properties for the persistence module, please refer -to the :ref:`reference configuration `. - -Multiple persistence plugin configurations -========================================== - -By default, a persistent actor or view will use the "default" journal and snapshot store plugins -configured in the following sections of the ``reference.conf`` configuration resource: - -.. includecode:: ../scala/code/docs/persistence/PersistenceMultiDocSpec.scala#default-config - -Note that in this case the actor or view overrides only ``persistenceId`` method: - -.. includecode:: ../java/code/docs/persistence/PersistenceMultiDocTest.java#default-plugins - -When a persistent actor or view overrides the ``journalPluginId`` and ``snapshotPluginId`` methods, -the actor or view will be serviced by these specific persistence plugins instead of the defaults: - -.. includecode:: ../java/code/docs/persistence/PersistenceMultiDocTest.java#override-plugins - -Note that ``journalPluginId`` and ``snapshotPluginId`` must refer to properly configured ``reference.conf`` -plugin entries with a standard ``class`` property as well as settings which are specific for those plugins, i.e.: - -.. includecode:: ../scala/code/docs/persistence/PersistenceMultiDocSpec.scala#override-config diff --git a/akka-docs/rst/java/remoting.rst b/akka-docs/rst/java/remoting.rst index 4a1702d85a..20c9132ee1 100644 --- a/akka-docs/rst/java/remoting.rst +++ b/akka-docs/rst/java/remoting.rst @@ -73,10 +73,10 @@ As you can see from the example above the following pattern is used to find an a Once you obtained a selection to the actor you can interact with it they same way you would with a local actor, e.g.:: - selection.tell("Pretty awesome feature", getSelf()); + selection.tell("Pretty awesome feature", self()); To acquire an :class:`ActorRef` for an :class:`ActorSelection` you need to -send a message to the selection and use the ``getSender`` reference of the reply from +send a message to the selection and use the ``sender`` reference of the reply from the actor. There is a built-in ``Identify`` message that all Actors will understand and automatically reply to with a ``ActorIdentity`` message containing the :class:`ActorRef`. This can also be done with the ``resolveOneCS`` method of diff --git a/akka-docs/rst/java/lambda-index-actors.rst b/akka-docs/rst/java/scala-compat.rst similarity index 83% rename from akka-docs/rst/java/lambda-index-actors.rst rename to akka-docs/rst/java/scala-compat.rst index 8dfd29222c..884114a4ba 100644 --- a/akka-docs/rst/java/lambda-index-actors.rst +++ b/akka-docs/rst/java/scala-compat.rst @@ -1,7 +1,7 @@ -.. _actor-java-lambda: +.. _scala-java-compat: -Actors (Java with Lambda Support) -================================= +Java 8 and Scala Compatibility +============================== Starting with Akka 2.4.2 we have begun to introduce Java 8 types (most prominently ``java.util.concurrent.CompletionStage`` and @@ -22,11 +22,3 @@ we can rely on Scala 2.12 to provide full interoperability—this will mean that Scala users can directly implement Java Functional Interfaces using lambda syntax as well as that Java users can directly implement Scala functions using lambda syntax. - -.. toctree:: - :maxdepth: 2 - - lambda-actors - lambda-fault-tolerance - lambda-fsm - lambda-persistence diff --git a/akka-docs/rst/java/stream/stream-integrations.rst b/akka-docs/rst/java/stream/stream-integrations.rst index d23e616e91..bdc7645c50 100644 --- a/akka-docs/rst/java/stream/stream-integrations.rst +++ b/akka-docs/rst/java/stream/stream-integrations.rst @@ -30,7 +30,7 @@ even though the actor will only process one message at a time because then there is already a message in the mailbox when the actor has completed previous message. -The actor must reply to the ``getSender()`` for each message from the stream. That +The actor must reply to the ``sender()`` for each message from the stream. That reply will complete the ``CompletionStage`` of the ``ask`` and it will be the element that is emitted downstreams from ``mapAsync``. diff --git a/akka-docs/rst/java/testing.rst b/akka-docs/rst/java/testing.rst index e42e58093d..bac90883bc 100644 --- a/akka-docs/rst/java/testing.rst +++ b/akka-docs/rst/java/testing.rst @@ -497,7 +497,7 @@ Create the child using JavaTestKit ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The ``JavaTestKit`` class can in fact create actors that will run with the test probe as parent. -This will cause any messages the child actor sends to `context().getParent()` to +This will cause any messages the child actor sends to `getContext().getParent()` to end up in the test probe. .. includecode:: code/docs/testkit/ParentChildTest.java#test-TestProbe-parent diff --git a/akka-docs/rst/java/untyped-actors.rst b/akka-docs/rst/java/untyped-actors.rst deleted file mode 100644 index 7f22f40026..0000000000 --- a/akka-docs/rst/java/untyped-actors.rst +++ /dev/null @@ -1,991 +0,0 @@ -.. _untyped-actors-java: - -################ - Actors -################ - - -The `Actor Model`_ provides a higher level of abstraction for writing concurrent -and distributed systems. It alleviates the developer from having to deal with -explicit locking and thread management, making it easier to write correct -concurrent and parallel systems. Actors were defined in the 1973 paper by Carl -Hewitt but have been popularized by the Erlang language, and used for example at -Ericsson with great success to build highly concurrent and reliable telecom -systems. - -The API of Akka’s Actors is similar to Scala Actors which has borrowed some of -its syntax from Erlang. - -.. _Actor Model: http://en.wikipedia.org/wiki/Actor_model - - -Creating Actors -=============== - -.. note:: - - Since Akka enforces parental supervision every actor is supervised and - (potentially) the supervisor of its children, it is advisable that you - familiarize yourself with :ref:`actor-systems` and :ref:`supervision` and it - may also help to read :ref:`addressing`. - -Defining an Actor class ------------------------ - -Actors in Java are implemented by extending the ``UntypedActor`` class and implementing the -:meth:`onReceive` method. This method takes the message as a parameter. - -Here is an example: - -.. includecode:: code/docs/actor/MyUntypedActor.java#my-untyped-actor - -Props ------ - -:class:`Props` is a configuration class to specify options for the creation -of actors, think of it as an immutable and thus freely shareable recipe for -creating an actor including associated deployment information (e.g. which -dispatcher to use, see more below). Here are some examples of how to create a -:class:`Props` instance. - -.. includecode:: code/docs/actor/UntypedActorDocTest.java#import-props -.. includecode:: code/docs/actor/UntypedActorDocTest.java#creating-props-config - -The second line shows how to pass constructor arguments to the :class:`Actor` -being created. The presence of a matching constructor is verified during -construction of the :class:`Props` object, resulting in an -:class:`IllegalArgumentException` if no or multiple matching constructors are -found. - -The third line demonstrates the use of a :class:`Creator`. The -creator class must be static, which is verified during :class:`Props` -construction. The type parameter’s upper bound is used to determine the -produced actor class, falling back to :class:`Actor` if fully erased. An -example of a parametric factory could be: - -.. includecode:: code/docs/actor/UntypedActorDocTest.java#parametric-creator - -.. note:: - - In order for mailbox requirements—like using a deque-based mailbox for actors - using the stash—to be picked up, the actor type needs to be known before - creating it, which is what the :class:`Creator` type argument allows. - Therefore make sure to use the specific type for your actors wherever - possible. - -Recommended Practices -^^^^^^^^^^^^^^^^^^^^^ - -It is a good idea to provide static factory methods on the -:class:`UntypedActor` which help keeping the creation of suitable -:class:`Props` as close to the actor definition as possible. This also allows -usage of the :class:`Creator`-based methods which statically verify that the -used constructor actually exists instead relying only on a runtime check. - -.. includecode:: code/docs/actor/UntypedActorDocTest.java#props-factory - -Another good practice is to declare what messages an Actor can receive -as close to the actor definition as possible (e.g. as static classes -inside the Actor or using other suitable class), which makes it easier to know -what it can receive. - -.. includecode:: code/docs/actor/UntypedActorDocTest.java#messages-in-companion - -Creating Actors with Props --------------------------- - -Actors are created by passing a :class:`Props` instance into the -:meth:`actorOf` factory method which is available on :class:`ActorSystem` and -:class:`ActorContext`. - -.. includecode:: code/docs/actor/UntypedActorDocTest.java#import-actorRef -.. includecode:: code/docs/actor/UntypedActorDocTest.java#system-actorOf - -Using the :class:`ActorSystem` will create top-level actors, supervised by the -actor system’s provided guardian actor, while using an actor’s context will -create a child actor. - -.. includecode:: code/docs/actor/UntypedActorDocTest.java#context-actorOf - :exclude: plus-some-behavior - -It is recommended to create a hierarchy of children, grand-children and so on -such that it fits the logical failure-handling structure of the application, -see :ref:`actor-systems`. - -The call to :meth:`actorOf` returns an instance of :class:`ActorRef`. This is a -handle to the actor instance and the only way to interact with it. The -:class:`ActorRef` is immutable and has a one to one relationship with the Actor -it represents. The :class:`ActorRef` is also serializable and network-aware. -This means that you can serialize it, send it over the wire and use it on a -remote host and it will still be representing the same Actor on the original -node, across the network. - -The name parameter is optional, but you should preferably name your actors, -since that is used in log messages and for identifying actors. The name must -not be empty or start with ``$``, but it may contain URL encoded characters -(eg. ``%20`` for a blank space). If the given name is already in use by -another child to the same parent an `InvalidActorNameException` is thrown. - -Actors are automatically started asynchronously when created. - -.. _actor-create-factory: - -Dependency Injection --------------------- - -If your UntypedActor has a constructor that takes parameters then those need to -be part of the :class:`Props` as well, as described `above`__. But there -are cases when a factory method must be used, for example when the actual -constructor arguments are determined by a dependency injection framework. - -__ Props_ - -.. includecode:: code/docs/actor/UntypedActorDocTest.java#import-indirect -.. includecode:: code/docs/actor/UntypedActorDocTest.java - :include: creating-indirectly - :exclude: obtain-fresh-Actor-instance-from-DI-framework - -.. warning:: - - You might be tempted at times to offer an :class:`IndirectActorProducer` - which always returns the same instance, e.g. by using a static field. This is - not supported, as it goes against the meaning of an actor restart, which is - described here: :ref:`supervision-restart`. - - When using a dependency injection framework, actor beans *MUST NOT* have - singleton scope. - -Techniques for dependency injection and integration with dependency injection frameworks -are described in more depth in the -`Using Akka with Dependency Injection `_ -guideline and the `Akka Java Spring `_ tutorial -in Lightbend Activator. - -The Inbox ---------- - -When writing code outside of actors which shall communicate with actors, the -``ask`` pattern can be a solution (see below), but there are two things it -cannot do: receiving multiple replies (e.g. by subscribing an :class:`ActorRef` -to a notification service) and watching other actors’ lifecycle. For these -purposes there is the :class:`Inbox` class: - -.. includecode:: code/docs/actor/InboxDocTest.java#inbox - -The :meth:`send` method wraps a normal :meth:`tell` and supplies the internal -actor’s reference as the sender. This allows the reply to be received on the -last line. Watching an actor is quite simple as well: - -.. includecode:: code/docs/actor/InboxDocTest.java#watch - -UntypedActor API -================ - -The :class:`UntypedActor` class defines only one abstract method, the above mentioned -:meth:`onReceive(Object message)`, which implements the behavior of the actor. - -If the current actor behavior does not match a received message, it's recommended that -you call the :meth:`unhandled` method, which by default publishes a ``new -akka.actor.UnhandledMessage(message, sender, recipient)`` on the actor system’s -event stream (set configuration item ``akka.actor.debug.unhandled`` to ``on`` -to have them converted into actual Debug messages). - -In addition, it offers: - -* :meth:`getSelf()` reference to the :class:`ActorRef` of the actor - -* :meth:`getSender()` reference sender Actor of the last received message, typically used as described in :ref:`UntypedActor.Reply` - -* :meth:`supervisorStrategy()` user overridable definition the strategy to use for supervising child actors - - This strategy is typically declared inside the actor in order to have access - to the actor’s internal state within the decider function: since failure is - communicated as a message sent to the supervisor and processed like other - messages (albeit outside of the normal behavior), all values and variables - within the actor are available, as is the ``getSender()`` reference (which will - be the immediate child reporting the failure; if the original failure - occurred within a distant descendant it is still reported one level up at a - time). - -* :meth:`getContext()` exposes contextual information for the actor and the current message, such as: - - * factory methods to create child actors (:meth:`actorOf`) - * system that the actor belongs to - * parent supervisor - * supervised children - * lifecycle monitoring - * hotswap behavior stack as described in :ref:`UntypedActor.HotSwap` - -The remaining visible methods are user-overridable life-cycle hooks which are -described in the following: - -.. includecode:: code/docs/actor/UntypedActorDocTest.java#lifecycle-callbacks - -The implementations shown above are the defaults provided by the :class:`UntypedActor` -class. - -.. _actor-lifecycle-java: - -Actor Lifecycle ---------------- - -.. image:: ../images/actor_lifecycle.png - :align: center - :width: 680 - -A path in an actor system represents a "place" which might be occupied -by a living actor. Initially (apart from system initialized actors) a path is -empty. When ``actorOf()`` is called it assigns an *incarnation* of the actor -described by the passed ``Props`` to the given path. An actor incarnation is -identified by the path *and a UID*. A restart only swaps the ``Actor`` -instance defined by the ``Props`` but the incarnation and hence the UID remains -the same. - -The lifecycle of an incarnation ends when the actor is stopped. At -that point the appropriate lifecycle events are called and watching actors -are notified of the termination. After the incarnation is stopped, the path can -be reused again by creating an actor with ``actorOf()``. In this case the -name of the new incarnation will be the same as the previous one but the -UIDs will differ. An actor can be stopped by the actor itself, another actor -or the ``ActorSystem`` (see :ref:`stopping-actors-java`). - -.. note:: - - It is important to note that Actors do not stop automatically when no longer - referenced, every Actor that is created must also explicitly be destroyed. - The only simplification is that stopping a parent Actor will also recursively - stop all the child Actors that this parent has created. - -An ``ActorRef`` always represents an incarnation (path and UID) not just a -given path. Therefore if an actor is stopped and a new one with the same -name is created an ``ActorRef`` of the old incarnation will not point -to the new one. - -``ActorSelection`` on the other hand points to the path (or multiple paths -if wildcards are used) and is completely oblivious to which incarnation is currently -occupying it. ``ActorSelection`` cannot be watched for this reason. It is -possible to resolve the current incarnation's ``ActorRef`` living under the -path by sending an ``Identify`` message to the ``ActorSelection`` which -will be replied to with an ``ActorIdentity`` containing the correct reference -(see :ref:`actorSelection-java`). This can also be done with the ``resolveOne`` -method of the :class:`ActorSelection`, which returns a ``Future`` of the matching -:class:`ActorRef`. - -.. _deathwatch-java: - -Lifecycle Monitoring aka DeathWatch ------------------------------------ - -In order to be notified when another actor terminates (i.e. stops permanently, -not temporary failure and restart), an actor may register itself for reception -of the :class:`Terminated` message dispatched by the other actor upon -termination (see `Stopping Actors`_). This service is provided by the -:class:`DeathWatch` component of the actor system. - -Registering a monitor is easy (see fourth line, the rest is for demonstrating -the whole functionality): - -.. includecode:: code/docs/actor/UntypedActorDocTest.java#import-terminated -.. includecode:: code/docs/actor/UntypedActorDocTest.java#watch - -It should be noted that the :class:`Terminated` message is generated -independent of the order in which registration and termination occur. -In particular, the watching actor will receive a :class:`Terminated` message -even if the watched actor has already been terminated at the time of registration. - -Registering multiple times does not necessarily lead to multiple messages being -generated, but there is no guarantee that only exactly one such message is -received: if termination of the watched actor has generated and queued the -message, and another registration is done before this message has been -processed, then a second message will be queued, because registering for -monitoring of an already terminated actor leads to the immediate generation of -the :class:`Terminated` message. - -It is also possible to deregister from watching another actor’s liveliness -using ``getContext().unwatch(target)``. This works even if the -:class:`Terminated` message has already been enqueued in the mailbox; after -calling :meth:`unwatch` no :class:`Terminated` message for that actor will be -processed anymore. - -Start Hook ----------- - -Right after starting the actor, its :meth:`preStart` method is invoked. - -.. includecode:: code/docs/actor/UntypedActorDocTest.java#preStart - -This method is called when the actor is first created. During restarts it is -called by the default implementation of :meth:`postRestart`, which means that -by overriding that method you can choose whether the initialization code in -this method is called only exactly once for this actor or for every restart. -Initialization code which is part of the actor’s constructor will always be -called when an instance of the actor class is created, which happens at every -restart. - -Restart Hooks -------------- - -All actors are supervised, i.e. linked to another actor with a fault -handling strategy. Actors may be restarted in case an exception is thrown while -processing a message (see :ref:`supervision`). This restart involves the hooks -mentioned above: - -1. The old actor is informed by calling :meth:`preRestart` with the exception - which caused the restart and the message which triggered that exception; the - latter may be ``None`` if the restart was not caused by processing a - message, e.g. when a supervisor does not trap the exception and is restarted - in turn by its supervisor, or if an actor is restarted due to a sibling’s - failure. If the message is available, then that message’s sender is also - accessible in the usual way (i.e. by calling ``getSender()``). - - This method is the best place for cleaning up, preparing hand-over to the - fresh actor instance, etc. By default it stops all children and calls - :meth:`postStop`. - -2. The initial factory from the ``actorOf`` call is used - to produce the fresh instance. - -3. The new actor’s :meth:`postRestart` method is invoked with the exception - which caused the restart. By default the :meth:`preStart` - is called, just as in the normal start-up case. - -An actor restart replaces only the actual actor object; the contents of the -mailbox is unaffected by the restart, so processing of messages will resume -after the :meth:`postRestart` hook returns. The message -that triggered the exception will not be received again. Any message -sent to an actor while it is being restarted will be queued to its mailbox as -usual. - -.. warning:: - - Be aware that the ordering of failure notifications relative to user messages - is not deterministic. In particular, a parent might restart its child before - it has processed the last messages sent by the child before the failure. - See :ref:`message-ordering` for details. - - -Stop Hook ---------- - -After stopping an actor, its :meth:`postStop` hook is called, which may be used -e.g. for deregistering this actor from other services. This hook is guaranteed -to run after message queuing has been disabled for this actor, i.e. messages -sent to a stopped actor will be redirected to the :obj:`deadLetters` of the -:obj:`ActorSystem`. - - -.. _actorSelection-java: - -Identifying Actors via Actor Selection -====================================== - -As described in :ref:`addressing`, each actor has a unique logical path, which -is obtained by following the chain of actors from child to parent until -reaching the root of the actor system, and it has a physical path, which may -differ if the supervision chain includes any remote supervisors. These paths -are used by the system to look up actors, e.g. when a remote message is -received and the recipient is searched, but they are also useful more directly: -actors may look up other actors by specifying absolute or relative -paths—logical or physical—and receive back an :class:`ActorSelection` with the -result: - -.. includecode:: code/docs/actor/UntypedActorDocTest.java#selection-local - -.. note:: - - It is always preferable to communicate with other Actors using their ActorRef - instead of relying upon ActorSelection. Exceptions are - - * sending messages using the :ref:`at-least-once-delivery-java` facility - * initiating first contact with a remote system - - In all other cases ActorRefs can be provided during Actor creation or - initialization, passing them from parent to child or introducing Actors by - sending their ActorRefs to other Actors within messages. - -The supplied path is parsed as a :class:`java.net.URI`, which basically means -that it is split on ``/`` into path elements. If the path starts with ``/``, it -is absolute and the look-up starts at the root guardian (which is the parent of -``"/user"``); otherwise it starts at the current actor. If a path element equals -``..``, the look-up will take a step “up” towards the supervisor of the -currently traversed actor, otherwise it will step “down” to the named child. -It should be noted that the ``..`` in actor paths here always means the logical -structure, i.e. the supervisor. - -The path elements of an actor selection may contain wildcard patterns allowing for -broadcasting of messages to that section: - -.. includecode:: code/docs/actor/UntypedActorDocTest.java#selection-wildcard - -Messages can be sent via the :class:`ActorSelection` and the path of the -:class:`ActorSelection` is looked up when delivering each message. If the selection -does not match any actors the message will be dropped. - -To acquire an :class:`ActorRef` for an :class:`ActorSelection` you need to send -a message to the selection and use the ``getSender`` reference of the reply -from the actor. There is a built-in ``Identify`` message that all Actors will -understand and automatically reply to with a ``ActorIdentity`` message -containing the :class:`ActorRef`. This message is handled specially by the -actors which are traversed in the sense that if a concrete name lookup fails -(i.e. a non-wildcard path element does not correspond to a live actor) then a -negative result is generated. Please note that this does not mean that delivery -of that reply is guaranteed, it still is a normal message. - -.. includecode:: code/docs/actor/UntypedActorDocTest.java#import-identify -.. includecode:: code/docs/actor/UntypedActorDocTest.java#identify - -You can also acquire an :class:`ActorRef` for an :class:`ActorSelection` with -the ``resolveOne`` method of the :class:`ActorSelection`. It returns a ``Future`` -of the matching :class:`ActorRef` if such an actor exists. It is completed with -failure [[akka.actor.ActorNotFound]] if no such actor exists or the identification -didn't complete within the supplied `timeout`. - -Remote actor addresses may also be looked up, if :ref:`remoting ` is enabled: - -.. includecode:: code/docs/actor/UntypedActorDocTest.java#selection-remote - -An example demonstrating remote actor look-up is given in :ref:`remote-sample-java`. - -Messages and immutability -========================= - -**IMPORTANT**: Messages can be any kind of object but have to be -immutable. Akka can’t enforce immutability (yet) so this has to be by -convention. - -Here is an example of an immutable message: - -.. includecode:: code/docs/actor/ImmutableMessage.java#immutable-message - - -Send messages -============= - -Messages are sent to an Actor through one of the following methods. - -* ``tell`` means “fire-and-forget”, e.g. send a message asynchronously and return - immediately. -* ``ask`` sends a message asynchronously and returns a :class:`Future` - representing a possible reply. - -Message ordering is guaranteed on a per-sender basis. - -.. note:: - - There are performance implications of using ``ask`` since something needs to - keep track of when it times out, there needs to be something that bridges - a ``Promise`` into an ``ActorRef`` and it also needs to be reachable through - remoting. So always prefer ``tell`` for performance, and only ``ask`` if you must. - -In all these methods you have the option of passing along your own ``ActorRef``. -Make it a practice of doing so because it will allow the receiver actors to be able to respond -to your message, since the sender reference is sent along with the message. - -.. _actors-tell-sender-java: - -Tell: Fire-forget ------------------ - -This is the preferred way of sending messages. No blocking waiting for a -message. This gives the best concurrency and scalability characteristics. - -.. includecode:: code/docs/actor/UntypedActorDocTest.java#tell - -The sender reference is passed along with the message and available within the -receiving actor via its :meth:`getSender()` method while processing this -message. Inside of an actor it is usually :meth:`getSelf` who shall be the -sender, but there can be cases where replies shall be routed to some other -actor—e.g. the parent—in which the second argument to :meth:`tell` would be a -different one. Outside of an actor and if no reply is needed the second -argument can be ``null``; if a reply is needed outside of an actor you can use -the ask-pattern described next.. - -.. _actors-ask-java: - -Ask: Send-And-Receive-Future ----------------------------- - -The ``ask`` pattern involves actors as well as futures, hence it is offered as -a use pattern rather than a method on :class:`ActorRef`: - -.. includecode:: code/docs/actor/UntypedActorDocTest.java#import-ask -.. includecode:: code/docs/actor/UntypedActorDocTest.java#ask-pipe - -This example demonstrates ``ask`` together with the ``pipe`` pattern on -futures, because this is likely to be a common combination. Please note that -all of the above is completely non-blocking and asynchronous: ``ask`` produces -a :class:`Future`, two of which are composed into a new future using the -:meth:`Futures.sequence` and :meth:`map` methods and then ``pipe`` installs -an ``onComplete``-handler on the future to effect the submission of the -aggregated :class:`Result` to another actor. - -Using ``ask`` will send a message to the receiving Actor as with ``tell``, and -the receiving actor must reply with ``getSender().tell(reply, getSelf())`` in order to -complete the returned :class:`Future` with a value. The ``ask`` operation -involves creating an internal actor for handling this reply, which needs to -have a timeout after which it is destroyed in order not to leak resources; see -more below. - -.. note:: - A Java 8 variant of the ``ask`` pattern that returns a ``CompletionStage`` instead of a Scala ``Future`` - is available in the ``akka.pattern.PatternsCS`` object. - -.. warning:: - - To complete the future with an exception you need send a Failure message to the sender. - This is *not done automatically* when an actor throws an exception while processing a message. - -.. includecode:: code/docs/actor/UntypedActorDocTest.java#reply-exception - -If the actor does not complete the future, it will expire after the timeout period, -specified as parameter to the ``ask`` method; this will complete the -:class:`Future` with an :class:`AskTimeoutException`. - -See :ref:`futures-java` for more information on how to await or query a -future. - -The ``onComplete``, ``onSuccess``, or ``onFailure`` methods of the ``Future`` can be -used to register a callback to get a notification when the Future completes. -Gives you a way to avoid blocking. - -.. warning:: - - When using future callbacks, inside actors you need to carefully avoid closing over - the containing actor’s reference, i.e. do not call methods or access mutable state - on the enclosing actor from within the callback. This would break the actor - encapsulation and may introduce synchronization bugs and race conditions because - the callback will be scheduled concurrently to the enclosing actor. Unfortunately - there is not yet a way to detect these illegal accesses at compile time. See also: - :ref:`jmm-shared-state` - -Forward message ---------------- - -You can forward a message from one actor to another. This means that the -original sender address/reference is maintained even though the message is going -through a 'mediator'. This can be useful when writing actors that work as -routers, load-balancers, replicators etc. -You need to pass along your context variable as well. - -.. includecode:: code/docs/actor/UntypedActorDocTest.java#forward - -Receive messages -================ - -When an actor receives a message it is passed into the ``onReceive`` method, this is -an abstract method on the ``UntypedActor`` base class that needs to be defined. - -Here is an example: - -.. includecode:: code/docs/actor/MyUntypedActor.java#my-untyped-actor - -An alternative to using if-instanceof checks is to use `Apache Commons MethodUtils -`_ -to invoke a named method whose parameter type matches the message type. - -.. _UntypedActor.Reply: - -Reply to messages -================= - -If you want to have a handle for replying to a message, you can use -``getSender()``, which gives you an ActorRef. You can reply by sending to -that ActorRef with ``getSender().tell(replyMsg, getSelf())``. You can also store the ActorRef -for replying later, or passing on to other actors. If there is no sender (a -message was sent without an actor or future context) then the sender -defaults to a 'dead-letter' actor ref. - -.. includecode:: code/docs/actor/UntypedActorDocTest.java#reply - :exclude: calculate-result - -Receive timeout -=============== - -The `UntypedActorContext` :meth:`setReceiveTimeout` defines the inactivity timeout after which -the sending of a `ReceiveTimeout` message is triggered. -When specified, the receive function should be able to handle an `akka.actor.ReceiveTimeout` message. -1 millisecond is the minimum supported timeout. - -Please note that the receive timeout might fire and enqueue the `ReceiveTimeout` message right after -another message was enqueued; hence it is **not guaranteed** that upon reception of the receive -timeout there must have been an idle period beforehand as configured via this method. - -Once set, the receive timeout stays in effect (i.e. continues firing repeatedly after inactivity -periods). Pass in `Duration.Undefined` to switch off this feature. - -.. includecode:: code/docs/actor/MyReceiveTimeoutUntypedActor.java#receive-timeout - -Messages marked with ``NotInfluenceReceiveTimeout`` will not reset the timer. This can be useful when -``ReceiveTimeout`` should be fired by external inactivity but not influenced by internal activity, -e.g. scheduled tick messages. - -.. _stopping-actors-java: - -Stopping actors -=============== - -Actors are stopped by invoking the :meth:`stop` method of a ``ActorRefFactory``, -i.e. ``ActorContext`` or ``ActorSystem``. Typically the context is used for stopping -the actor itself or child actors and the system for stopping top level actors. The actual -termination of the actor is performed asynchronously, i.e. :meth:`stop` may return before -the actor is stopped. - -.. includecode:: code/docs/actor/MyStoppingActor.java#my-stopping-actor - -Processing of the current message, if any, will continue before the actor is stopped, -but additional messages in the mailbox will not be processed. By default these -messages are sent to the :obj:`deadLetters` of the :obj:`ActorSystem`, but that -depends on the mailbox implementation. - -Termination of an actor proceeds in two steps: first the actor suspends its -mailbox processing and sends a stop command to all its children, then it keeps -processing the internal termination notifications from its children until the last one is -gone, finally terminating itself (invoking :meth:`postStop`, dumping mailbox, -publishing :class:`Terminated` on the :ref:`DeathWatch `, telling -its supervisor). This procedure ensures that actor system sub-trees terminate -in an orderly fashion, propagating the stop command to the leaves and -collecting their confirmation back to the stopped supervisor. If one of the -actors does not respond (i.e. processing a message for extended periods of time -and therefore not receiving the stop command), this whole process will be -stuck. - -Upon :meth:`ActorSystem.terminate()`, the system guardian actors will be -stopped, and the aforementioned process will ensure proper termination of the -whole system. - -The :meth:`postStop()` hook is invoked after an actor is fully stopped. This -enables cleaning up of resources: - -.. includecode:: code/docs/actor/UntypedActorDocTest.java#postStop - :exclude: clean-up-resources-here - -.. note:: - - Since stopping an actor is asynchronous, you cannot immediately reuse the - name of the child you just stopped; this will result in an - :class:`InvalidActorNameException`. Instead, :meth:`watch()` the terminating - actor and create its replacement in response to the :class:`Terminated` - message which will eventually arrive. - -.. _poison-pill-java: - -PoisonPill ----------- - -You can also send an actor the ``akka.actor.PoisonPill`` message, which will -stop the actor when the message is processed. ``PoisonPill`` is enqueued as -ordinary messages and will be handled after messages that were already queued -in the mailbox. - -Use it like this: - -.. includecode:: code/docs/actor/UntypedActorDocTest.java - :include: poison-pill - -Graceful Stop -------------- - -:meth:`gracefulStop` is useful if you need to wait for termination or compose ordered -termination of several actors: - -.. includecode:: code/docs/actor/UntypedActorDocTest.java - :include: import-gracefulStop - -.. includecode:: code/docs/actor/UntypedActorDocTest.java - :include: gracefulStop - -.. includecode:: code/docs/actor/UntypedActorDocTest.java - :include: gracefulStop-actor - -When ``gracefulStop()`` returns successfully, the actor’s ``postStop()`` hook -will have been executed: there exists a happens-before edge between the end of -``postStop()`` and the return of ``gracefulStop()``. - -In the above example a custom ``Manager.SHUTDOWN`` message is sent to the target -actor to initiate the process of stopping the actor. You can use ``PoisonPill`` for -this, but then you have limited possibilities to perform interactions with other actors -before stopping the target actor. Simple cleanup tasks can be handled in ``postStop``. - -.. warning:: - - Keep in mind that an actor stopping and its name being deregistered are - separate events which happen asynchronously from each other. Therefore it may - be that you will find the name still in use after ``gracefulStop()`` - returned. In order to guarantee proper deregistration, only reuse names from - within a supervisor you control and only in response to a :class:`Terminated` - message, i.e. not for top-level actors. - -Coordinated Shutdown --------------------- - -There is an extension named ``CoordinatedShutdown`` that will stop certain actors and -services in a specific order and perform registered tasks during the shutdown process. - -The order of the shutdown phases is defined in configuration ``akka.coordinated-shutdown.phases``. -The default phases are defined as: - -.. includecode:: ../../../akka-actor/src/main/resources/reference.conf#coordinated-shutdown-phases - -More phases can be be added in the application's configuration if needed by overriding a phase with an -additional ``depends-on``. Especially the phases ``before-service-unbind``, ``before-cluster-shutdown`` and -``before-actor-system-terminate`` are intended for application specific phases or tasks. - -The default phases are defined in a single linear order, but the phases can be ordered as a -directed acyclic graph (DAG) by defining the dependencies between the phases. -The phases are ordered with `topological `_ sort of the DAG. - -Tasks can be added to a phase with: - -.. includecode:: code/docs/actorlambda/ActorDocTest.java#coordinated-shutdown-addTask - -The returned ``CompletionStage`` should be completed when the task is completed. The task name parameter -is only used for debugging/logging. - -Tasks added to the same phase are executed in parallel without any ordering assumptions. -Next phase will not start until all tasks of previous phase have been completed. - -If tasks are not completed within a configured timeout (see :ref:`reference.conf `) -the next phase will be started anyway. It is possible to configure ``recover=off`` for a phase -to abort the rest of the shutdown process if a task fails or is not completed within the timeout. - -Tasks should typically be registered as early as possible after system startup. When running -the coordinated shutdown tasks that have been registered will be performed but tasks that are -added too late will not be run. - -To start the coordinated shutdown process you can invoke ``runAll`` on the ``CoordinatedShutdown`` -extension: - -.. includecode:: code/docs/actorlambda/ActorDocTest.java#coordinated-shutdown-run - -It's safe to call the ``runAll`` method multiple times. It will only run once. - -That also means that the ``ActorSystem`` will be terminated in the last phase. By default, the -JVM is not forcefully stopped (it will be stopped if all non-daemon threads have been terminated). -To enable a hard ``System.exit`` as a final action you can configure:: - - akka.coordinated-shutdown.exit-jvm = on - -When using :ref:`Akka Cluster ` the ``CoordinatedShutdown`` will automatically run -when the cluster node sees itself as ``Exiting``, i.e. leaving from another node will trigger -the shutdown process on the leaving node. Tasks for graceful leaving of cluster including graceful -shutdown of Cluster Singletons and Cluster Sharding are added automatically when Akka Cluster is used, -i.e. running the shutdown process will also trigger the graceful leaving if it's not already in progress. - -By default, the ``CoordinatedShutdown`` will be run when the JVM process exits, e.g. -via ``kill SIGTERM`` signal (``SIGINT`` ctrl-c doesn't work). This behavior can be disabled with:: - - akka.coordinated-shutdown.run-by-jvm-shutdown-hook=off - -If you have application specific JVM shutdown hooks it's recommended that you register them via the -``CoordinatedShutdown`` so that they are running before Akka internal shutdown hooks, e.g. -those shutting down Akka Remoting (Artery). - -.. includecode:: code/docs/actorlambda/ActorDocTest.java#coordinated-shutdown-jvm-hook - -.. _UntypedActor.HotSwap: - -HotSwap -======= - -Upgrade -------- - -Akka supports hotswapping the Actor’s message loop (e.g. its implementation) at -runtime. Use the ``getContext().become`` method from within the Actor. -The hotswapped code is kept in a Stack which can be pushed (replacing or adding -at the top) and popped. - -.. warning:: - - Please note that the actor will revert to its original behavior when restarted by its Supervisor. - -To hotswap the Actor using ``getContext().become``: - -.. includecode:: code/docs/actor/UntypedActorDocTest.java - :include: import-procedure - -.. includecode:: code/docs/actor/UntypedActorDocTest.java - :include: hot-swap-actor - -This variant of the :meth:`become` method is useful for many different things, -such as to implement a Finite State Machine (FSM). It will replace the current -behavior (i.e. the top of the behavior stack), which means that you do not use -:meth:`unbecome`, instead always the next behavior is explicitly installed. - -The other way of using :meth:`become` does not replace but add to the top of -the behavior stack. In this case care must be taken to ensure that the number -of “pop” operations (i.e. :meth:`unbecome`) matches the number of “push” ones -in the long run, otherwise this amounts to a memory leak (which is why this -behavior is not the default). - -.. includecode:: code/docs/actor/UntypedActorSwapper.java#swapper - -.. _stash-java: - -Stash -===== - -The ``UntypedActorWithStash`` class enables an actor to temporarily stash away messages -that can not or should not be handled using the actor's current -behavior. Upon changing the actor's message handler, i.e., right -before invoking ``getContext().become()`` or ``getContext().unbecome()``, all -stashed messages can be "unstashed", thereby prepending them to the actor's -mailbox. This way, the stashed messages can be processed in the same -order as they have been received originally. An actor that extends -``UntypedActorWithStash`` will automatically get a deque-based mailbox. - -.. note:: - - The abstract class ``UntypedActorWithStash`` implements the marker - interface ``RequiresMessageQueue`` - which requests the system to automatically choose a deque based - mailbox implementation for the actor. If you want more - control over the mailbox, see the documentation on mailboxes: :ref:`mailboxes-java`. - -Here is an example of the ``UntypedActorWithStash`` class in action: - -.. includecode:: code/docs/actor/UntypedActorDocTest.java#import-stash -.. includecode:: code/docs/actor/UntypedActorDocTest.java#stash - -Invoking ``stash()`` adds the current message (the message that the -actor received last) to the actor's stash. It is typically invoked -when handling the default case in the actor's message handler to stash -messages that aren't handled by the other cases. It is illegal to -stash the same message twice; to do so results in an -``IllegalStateException`` being thrown. The stash may also be bounded -in which case invoking ``stash()`` may lead to a capacity violation, -which results in a ``StashOverflowException``. The capacity of the -stash can be configured using the ``stash-capacity`` setting (an ``Int``) of the -mailbox's configuration. - -Invoking ``unstashAll()`` enqueues messages from the stash to the -actor's mailbox until the capacity of the mailbox (if any) has been -reached (note that messages from the stash are prepended to the -mailbox). In case a bounded mailbox overflows, a -``MessageQueueAppendFailedException`` is thrown. -The stash is guaranteed to be empty after calling ``unstashAll()``. - -The stash is backed by a ``scala.collection.immutable.Vector``. As a -result, even a very large number of messages may be stashed without a -major impact on performance. - -Note that the stash is part of the ephemeral actor state, unlike the -mailbox. Therefore, it should be managed like other parts of the -actor's state which have the same property. The :class:`UntypedActorWithStash` -implementation of :meth:`preRestart` will call ``unstashAll()``, which is -usually the desired behavior. - -.. note:: - - If you want to enforce that your actor can only work with an unbounded stash, - then you should use the ``UntypedActorWithUnboundedStash`` class instead. - - -.. _killing-actors-java: - -Killing an Actor -================ - -You can kill an actor by sending a ``Kill`` message. This will cause the actor -to throw a :class:`ActorKilledException`, triggering a failure. The actor will -suspend operation and its supervisor will be asked how to handle the failure, -which may mean resuming the actor, restarting it or terminating it completely. -See :ref:`supervision-directives` for more information. - -Use ``Kill`` like this: - -.. includecode:: code/docs/actor/UntypedActorDocTest.java - :include: kill - -Actors and exceptions -===================== - -It can happen that while a message is being processed by an actor, that some -kind of exception is thrown, e.g. a database exception. - -What happens to the Message ---------------------------- - -If an exception is thrown while a message is being processed (i.e. taken out of -its mailbox and handed over to the current behavior), then this message will be -lost. It is important to understand that it is not put back on the mailbox. So -if you want to retry processing of a message, you need to deal with it yourself -by catching the exception and retry your flow. Make sure that you put a bound -on the number of retries since you don't want a system to livelock (so -consuming a lot of cpu cycles without making progress). Another possibility -would be to have a look at the :ref:`PeekMailbox pattern `. - -What happens to the mailbox ---------------------------- - -If an exception is thrown while a message is being processed, nothing happens to -the mailbox. If the actor is restarted, the same mailbox will be there. So all -messages on that mailbox will be there as well. - -What happens to the actor -------------------------- - -If code within an actor throws an exception, that actor is suspended and the -supervision process is started (see :ref:`supervision`). Depending on the -supervisor’s decision the actor is resumed (as if nothing happened), restarted -(wiping out its internal state and starting from scratch) or terminated. - -Initialization patterns -======================= - -The rich lifecycle hooks of Actors provide a useful toolkit to implement various initialization patterns. During the -lifetime of an ``ActorRef``, an actor can potentially go through several restarts, where the old instance is replaced by -a fresh one, invisibly to the outside observer who only sees the ``ActorRef``. - -One may think about the new instances as "incarnations". Initialization might be necessary for every incarnation -of an actor, but sometimes one needs initialization to happen only at the birth of the first instance when the -``ActorRef`` is created. The following sections provide patterns for different initialization needs. - -Initialization via constructor ------------------------------- - -Using the constructor for initialization has various benefits. First of all, it makes it possible to use ``val`` fields to store -any state that does not change during the life of the actor instance, making the implementation of the actor more robust. -The constructor is invoked for every incarnation of the actor, therefore the internals of the actor can always assume -that proper initialization happened. This is also the drawback of this approach, as there are cases when one would -like to avoid reinitializing internals on restart. For example, it is often useful to preserve child actors across -restarts. The following section provides a pattern for this case. - -Initialization via preStart ---------------------------- - -The method ``preStart()`` of an actor is only called once directly during the initialization of the first instance, that -is, at creation of its ``ActorRef``. In the case of restarts, ``preStart()`` is called from ``postRestart()``, therefore -if not overridden, ``preStart()`` is called on every incarnation. However, overriding ``postRestart()`` one can disable -this behavior, and ensure that there is only one call to ``preStart()``. - -One useful usage of this pattern is to disable creation of new ``ActorRefs`` for children during restarts. This can be -achieved by overriding ``preRestart()``: - -.. includecode:: code/docs/actor/InitializationDocSpecJava.java#preStartInit - -Please note, that the child actors are *still restarted*, but no new ``ActorRef`` is created. One can recursively apply -the same principles for the children, ensuring that their ``preStart()`` method is called only at the creation of their -refs. - -For more information see :ref:`supervision-restart`. - -Initialization via message passing ----------------------------------- - -There are cases when it is impossible to pass all the information needed for actor initialization in the constructor, -for example in the presence of circular dependencies. In this case the actor should listen for an initialization message, -and use ``become()`` or a finite state-machine state transition to encode the initialized and uninitialized states -of the actor. - -.. includecode:: code/docs/actor/InitializationDocSpecJava.java#messageInit - -If the actor may receive messages before it has been initialized, a useful tool can be the ``Stash`` to save messages -until the initialization finishes, and replaying them after the actor became initialized. - -.. warning:: - - This pattern should be used with care, and applied only when none of the patterns above are applicable. One of - the potential issues is that messages might be lost when sent to remote actors. Also, publishing an ``ActorRef`` in - an uninitialized state might lead to the condition that it receives a user message before the initialization has been - done. diff --git a/akka-docs/rst/project/migration-guide-2.3.x-2.4.x.rst b/akka-docs/rst/project/migration-guide-2.3.x-2.4.x.rst index 8e040ad434..f2eb1fb44b 100644 --- a/akka-docs/rst/project/migration-guide-2.3.x-2.4.x.rst +++ b/akka-docs/rst/project/migration-guide-2.3.x-2.4.x.rst @@ -6,3 +6,4 @@ Migration Guide 2.3.x to 2.4.x Migration from 2.3.x to 2.4.x is described in the `documentation of 2.4 `_. + diff --git a/akka-docs/rst/project/migration-guide-2.4.x-2.5.x.rst b/akka-docs/rst/project/migration-guide-2.4.x-2.5.x.rst index 935e0c09ca..38ee25f4fd 100644 --- a/akka-docs/rst/project/migration-guide-2.4.x-2.5.x.rst +++ b/akka-docs/rst/project/migration-guide-2.4.x-2.5.x.rst @@ -4,8 +4,211 @@ Migration Guide 2.4.x to 2.5.x ############################## -Actor -===== +Actor (Java) +============ + +AbstractActor +------------- + +``AbstractActor`` has been promoted from its experimental state and while doing this we +did some small, but important, improvements to the API that will require some mechanical +changes of your source code. + +Previously the receive behavior was set with the ``receive`` method, but now an actor has +to define its initial receive behavior by implementing the ``createReceive`` method in +the ``AbstractActor``. This has the advantages: + +* It gives a clear entry point of what to implement. The compiler tells you that the + abstract method must be implemented. +* It's impossible to forget to set the receive behavior. +* It's not possible to define the receive behavior more than once. + +The return type of ``createReceive`` is ``AbstractActor.Receive``. It defines which messages +your Actor can handle, along with the implementation of how the messages should be processed. +You can build such behavior with a builder named ``ReceiveBuilder``. + +``AbstractActor.Receive`` can also be used in ``getContext().become``. + +The old ``receive`` method exposed Scala's ``PartialFunction`` and ``BoxedUnit`` in the signature, +which are unnecessary concepts for newcomers to learn. The new ``createReceive`` requires no +additional imports. + +Note that The ``Receive`` can still be implemented in other ways than using the ``ReceiveBuilder`` +since it in the end is just a wrapper around a Scala ``PartialFunction``. For example, one could +implement an adapter to `Javaslang Pattern Matching DSL `_. + +The mechanical source code change for migration to the new ``AbstractActor`` is to implement the +``createReceive`` instead of calling ``receive`` (compiler will tell that this is missing). + +Old:: + + import akka.actor.AbstractActor; + import akka.japi.pf.ReceiveBuilder; + import scala.PartialFunction; + import scala.runtime.BoxedUnit; + + public class SomeActor extends AbstractActor { + public SomeActor() { + receive(ReceiveBuilder + .match(String.class, s -> System.out.println(s.toLowerCase())). + .build()); + } + } + +New:: + + import akka.actor.AbstractActor; + + public class SomeActor extends AbstractActor { + @Override + public Receive createReceive() { + return receiveBuilder() + .match(String.class, s -> System.out.println(s.toLowerCase())) + .build(); + } + } + +See :ref:`actors-receive-java` documentation for more advice about how to implement +``createReceive``. + +A few new methods have been added with deprecation of the old. Worth noting is ``preRestart``. + +Old:: + + @Override + public void preRestart(Throwable reason, scala.Option message) { + super.preRestart(reason, message); + } + +New:: + + @Override + public void preRestart(Throwable reason, java.util.Optional message) { + super.preRestart(reason, message); + } + +AbstractPersistentActor +----------------------- + +Similar change as described above for ``AbstractActor`` is needed for ``AbstractPersistentActor``. Implement ``createReceiveRecover`` +instead of ``receiveRecover``, and ``createReceive`` instead of ``receiveCommand``. + +Old:: + + @Override + public PartialFunction receiveCommand() { + return ReceiveBuilder. + match(String.class, cmd -> {/* ... */}).build(); + } + + @Override + public PartialFunction receiveRecover() { + return ReceiveBuilder. + match(String.class, evt -> {/* ... */}).build(); + } + +New:: + + @Override + public Receive createReceive() { + return receiveBuilder(). + match(String.class, cmd -> {/* ... */}).build(); + } + + @Override + public Receive createReceiveRecover() { + return receiveBuilder(). + match(String.class, evt -> {/* ... */}).build(); + } + +UntypedActor +------------ + +``UntypedActor`` has been deprecated in favor of ``AbstractActor``. As a migration path you can extend +``UntypedAbstractActor`` instead of ``UntypedActor``. + +Old:: + + import akka.actor.UntypedActor; + + public class SomeActor extends UntypedActor { + + public static class Msg1 {} + + @Override + public void onReceive(Object msg) throws Exception { + if (msg instanceof Msg1) { + Msg1 msg1 = (Msg1) msg; + // actual work + } else { + unhandled(msg); + } + } + } + + +New:: + + import akka.actor.UntypedAbstractActor; + + public class SomeActor extends UntypedAbstractActor { + + public static class Msg1 {} + + @Override + public void onReceive(Object msg) throws Exception { + if (msg instanceof Msg1) { + Msg1 msg1 = (Msg1) msg; + // actual work + } else { + unhandled(msg); + } + } + } + +It's recommended to migrate ``UntypedActor`` to ``AbstractActor`` by implementing +``createReceive`` instead of ``onMessage``. + +Old:: + + import akka.actor.UntypedActor; + + public class SomeActor extends UntypedActor { + + @Override + public void onReceive(Object msg) throws Exception { + if (msg instanceof String) { + String s = (String) msg; + System.out.println(s.toLowerCase()); + } else { + unhandled(msg); + } + } + } + +New:: + + import akka.actor.AbstractActor; + + public class SomeActor extends AbstractActor { + @Override + public Receive createReceive() { + return receiveBuilder() + .match(String.class, s -> { + System.out.println(s.toLowerCase()); + }) + .build(); + } + } + +See :ref:`actors-receive-java` documentation for more advice about how to implement +``createReceive``. + +Similar with ``UntypedActorWithStash``, ``UntypedPersistentActor``, and +``UntypedPersistentActorWithAtLeastOnceDelivery``. + +Actor (Scala) +============= Actor DSL deprecation --------------------- @@ -77,10 +280,8 @@ It is still possible to make a rolling upgrade from a version < 2.4.12 by doing and do a first rolling upgrade * second, turn the setting to ``on`` and do another rolling upgrade -For more information see the documentation for the ``akka.remote.netty.ssl.require-mutual-authentication` configuration setting -in akka-remote's `reference.conf`_. - -.. _reference.conf: https://github.com/akka/akka/blob/master/akka-remote/src/main/resources/reference.conf +For more information see the documentation for the ``akka.remote.netty.ssl.require-mutual-authentication`` configuration setting +in :ref:`akka-remote's reference.conf `. Cluster ======= @@ -102,7 +303,7 @@ read the documentation for the Coordinated Shutdown and revisit your own impleme Most likely your implementation will not be needed any more or it can be simplified. More information can be found in the :ref:`documentation for Scala ` or -:ref:`documentation for Java ` +:ref:`documentation for Java ` For some tests it might be undesired to terminate the ``ActorSystem`` via ``CoordinatedShutdown``. You can disable that by adding the following to the configuration of the ``ActorSystem`` that is diff --git a/akka-docs/rst/additional/code/docs/osgi/Activator.scala b/akka-osgi/src/test/scala/docs/osgi/Activator.scala similarity index 93% rename from akka-docs/rst/additional/code/docs/osgi/Activator.scala rename to akka-osgi/src/test/scala/docs/osgi/Activator.scala index 8ae0c7a2cc..4f432452c3 100644 --- a/akka-docs/rst/additional/code/docs/osgi/Activator.scala +++ b/akka-osgi/src/test/scala/docs/osgi/Activator.scala @@ -3,7 +3,7 @@ package docs.osgi case object SomeMessage class SomeActor extends akka.actor.Actor { - def receive = { case SomeMessage => } + def receive = { case SomeMessage ⇒ } } //#Activator diff --git a/akka-docs/rst/additional/code/docs/osgi/blueprint.xml b/akka-osgi/src/test/scala/docs/osgi/blueprint.xml similarity index 100% rename from akka-docs/rst/additional/code/docs/osgi/blueprint.xml rename to akka-osgi/src/test/scala/docs/osgi/blueprint.xml diff --git a/akka-persistence/src/main/scala/akka/persistence/AtLeastOnceDelivery.scala b/akka-persistence/src/main/scala/akka/persistence/AtLeastOnceDelivery.scala index f7fe002df2..4e58b5de01 100644 --- a/akka-persistence/src/main/scala/akka/persistence/AtLeastOnceDelivery.scala +++ b/akka-persistence/src/main/scala/akka/persistence/AtLeastOnceDelivery.scala @@ -366,6 +366,7 @@ trait AtLeastOnceDeliveryLike extends Eventsourced { * @see [[AtLeastOnceDelivery]] * @see [[AtLeastOnceDeliveryLike]] */ +@deprecated("Use AbstractPersistentActorWithAtLeastOnceDelivery instead.", since = "2.5.0") abstract class UntypedPersistentActorWithAtLeastOnceDelivery extends UntypedPersistentActor with AtLeastOnceDeliveryLike { /** * Java API: Send the message created by the `deliveryIdToMessage` function to diff --git a/akka-persistence/src/main/scala/akka/persistence/Eventsourced.scala b/akka-persistence/src/main/scala/akka/persistence/Eventsourced.scala index 772b615a1a..f2abba6607 100644 --- a/akka-persistence/src/main/scala/akka/persistence/Eventsourced.scala +++ b/akka-persistence/src/main/scala/akka/persistence/Eventsourced.scala @@ -39,8 +39,7 @@ private[persistence] object Eventsourced { /** * INTERNAL API. * - * Scala API and implementation details of [[PersistentActor]], [[AbstractPersistentActor]] and - * [[UntypedPersistentActor]]. + * Scala API and implementation details of [[PersistentActor]] and [[AbstractPersistentActor]]. */ private[persistence] trait Eventsourced extends Snapshotter with PersistenceStash with PersistenceIdentity with PersistenceRecovery { import JournalProtocol._ diff --git a/akka-persistence/src/main/scala/akka/persistence/PersistentActor.scala b/akka-persistence/src/main/scala/akka/persistence/PersistentActor.scala index 2f04dbbe16..43d3485024 100644 --- a/akka-persistence/src/main/scala/akka/persistence/PersistentActor.scala +++ b/akka-persistence/src/main/scala/akka/persistence/PersistentActor.scala @@ -165,6 +165,7 @@ trait PersistentActor extends Eventsourced with PersistenceIdentity { /** * Java API: an persistent actor - can be used to implement command or event sourcing. */ +@deprecated("Use AbstractPersistentActor instead of UntypedPersistentActor.", since = "2.5.0") abstract class UntypedPersistentActor extends UntypedActor with Eventsourced with PersistenceIdentity { final def onReceive(message: Any) = onReceiveCommand(message) @@ -308,6 +309,35 @@ abstract class UntypedPersistentActor extends UntypedActor with Eventsourced wit */ abstract class AbstractPersistentActor extends AbstractActor with PersistentActor with Eventsourced { + /** + * Recovery handler that receives persisted events during recovery. If a state snapshot + * has been captured and saved, this handler will receive a [[SnapshotOffer]] message + * followed by events that are younger than the offered snapshot. + * + * This handler must not have side-effects other than changing persistent actor state i.e. it + * should not perform actions that may fail, such as interacting with external services, + * for example. + * + * If there is a problem with recovering the state of the actor from the journal, the error + * will be logged and the actor will be stopped. + * + * @see [[Recovery]] + */ + def createReceiveRecover(): AbstractActor.Receive + + override final def receiveRecover: Receive = createReceiveRecover().onMessage.asInstanceOf[Receive] + + /** + * An persistent actor has to define its initial receive behavior by implementing + * the `createReceive` method, also known as the command handler. Typically + * validates commands against current state (and/or by communication with other actors). + * On successful validation, one or more events are derived from a command and + * these events are then persisted by calling `persist`. + */ + override def createReceive(): AbstractActor.Receive + + override final def receiveCommand: Receive = createReceive().onMessage.asInstanceOf[Receive] + /** * Java API: asynchronously persists `event`. On successful persistence, `handler` is called with the * persisted event. It is guaranteed that no new commands will be received by a persistent actor diff --git a/akka-stream/src/main/scala/akka/stream/stage/GraphStage.scala b/akka-stream/src/main/scala/akka/stream/stage/GraphStage.scala index 909cadc2fd..26eda32d0e 100644 --- a/akka-stream/src/main/scala/akka/stream/stage/GraphStage.scala +++ b/akka-stream/src/main/scala/akka/stream/stage/GraphStage.scala @@ -563,8 +563,7 @@ abstract class GraphStageLogic private[stream] (val inCount: Int, val outCount: pos += 1 if (pos == n) andThen(result) }, - () ⇒ onClose(result.take(pos))) - ) + () ⇒ onClose(result.take(pos)))) } else andThen(result) } diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index 86693dea17..7d0e446531 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -74,7 +74,6 @@ object AkkaBuild extends Build { protobuf, remote, remoteTests, - samples, slf4j, stream, streamTestkit, @@ -95,7 +94,7 @@ object AkkaBuild extends Build { base = file("akka-scala-nightly"), // remove dependencies that we have to build ourselves (Scala STM) // samples don't work with dbuild right now - aggregate = aggregatedProjects diff List[ProjectReference](agent, docs, samples) + aggregate = aggregatedProjects diff List[ProjectReference](agent, docs) ).disablePlugins(ValidatePullRequest, MimaPlugin) lazy val actor = Project( diff --git a/project/MiMa.scala b/project/MiMa.scala index 83adab3687..3cf1ab73d6 100644 --- a/project/MiMa.scala +++ b/project/MiMa.scala @@ -172,6 +172,20 @@ object MiMa extends AutoPlugin { ProblemFilters.exclude[DirectMissingMethodProblem]("akka.cluster.ClusterCoreDaemon.removed"), ProblemFilters.exclude[DirectMissingMethodProblem]("akka.cluster.Gossip.convergence"), + //#21717 Improvements to AbstractActor API + FilterAnyProblemStartingWith("akka.japi.pf.ReceiveBuilder"), + ProblemFilters.exclude[DirectMissingMethodProblem]("akka.actor.AbstractActor.receive"), + ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.actor.AbstractActor.createReceive"), + ProblemFilters.exclude[MissingClassProblem]("akka.actor.AbstractActorContext"), + ProblemFilters.exclude[IncompatibleResultTypeProblem]("akka.actor.AbstractActor.getContext"), + ProblemFilters.exclude[IncompatibleResultTypeProblem]("akka.actor.AbstractActor.emptyBehavior"), + ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.actor.dungeon.Children.findChild"), + ProblemFilters.exclude[MissingTypesProblem]("akka.actor.ActorCell"), + ProblemFilters.exclude[MissingTypesProblem]("akka.routing.RoutedActorCell"), + ProblemFilters.exclude[MissingTypesProblem]("akka.routing.ResizablePoolCell"), + ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.persistence.AbstractPersistentActor.createReceiveRecover"), + ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.persistence.AbstractPersistentActor.createReceive"), + // #21423 removal of deprecated stages (in 2.5.x) ProblemFilters.exclude[DirectMissingMethodProblem]("akka.stream.javadsl.Source.transform"), ProblemFilters.exclude[DirectMissingMethodProblem]("akka.stream.javadsl.SubSource.transform"), diff --git a/project/Sample.scala b/project/Sample.scala index 08b84934ce..3df2e85e3f 100644 --- a/project/Sample.scala +++ b/project/Sample.scala @@ -16,7 +16,7 @@ object Sample { * * Default: true */ - val aggregateSamples = sys.props.getOrElse("akka.build.aggregateSamples", "true").toBoolean + val aggregateSamples = sys.props.getOrElse("akka.build.aggregateSamples", "false").toBoolean } final val akkaOrganization = "com.typesafe.akka"