move code to src/test

* so that it compiles and tests pass
* fix some additional snip references in getting started
This commit is contained in:
Patrik Nordwall 2017-05-11 15:11:25 +02:00
parent 413df8e0f4
commit 59f53e1a22
289 changed files with 45 additions and 45 deletions

View file

@ -0,0 +1,837 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.actor;
import akka.actor.*;
import akka.testkit.javadsl.TestKit;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import jdocs.AbstractJavaTest;
import static jdocs.actor.Messages.Swap.Swap;
import static jdocs.actor.Messages.*;
import akka.actor.CoordinatedShutdown;
import akka.util.Timeout;
import akka.Done;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import akka.testkit.TestActors;
import scala.concurrent.Await;
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-ask
import static akka.pattern.PatternsCS.ask;
import static akka.pattern.PatternsCS.pipe;
import java.util.concurrent.CompletableFuture;
//#import-ask
//#import-gracefulStop
import static akka.pattern.PatternsCS.gracefulStop;
import akka.pattern.AskTimeoutException;
import scala.concurrent.duration.Duration;
import java.util.concurrent.CompletionStage;
//#import-gracefulStop
//#import-terminated
import akka.actor.Terminated;
//#import-terminated
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.ready(system.terminate(), Duration.create(5, TimeUnit.SECONDS));
}
static
//#context-actorOf
public class FirstActor extends AbstractActor {
final ActorRef child = getContext().actorOf(Props.create(MyActor.class), "myChild");
//#plus-some-behavior
@Override
public Receive createReceive() {
return receiveBuilder()
.matchAny(x -> getSender().tell(x, getSelf()))
.build();
}
//#plus-some-behavior
}
//#context-actorOf
static public class SomeActor extends AbstractActor {
//#createReceive
@Override
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 Msg2)
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;
public ActorWithArgs(String args) {
this.args = args;
}
@Override
public Receive createReceive() {
return 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 actors 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;
public DemoActor(Integer magicNumber) {
this.magicNumber = magicNumber;
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Integer.class, i -> {
getSender().tell(i + magicNumber, getSelf());
})
.build();
}
}
//#props-factory
static
//#props-factory
public class SomeOtherActor extends AbstractActor {
// Props(new DemoActor(42)) would not be safe
ActorRef demoActor = getContext().actorOf(DemoActor.props(42), "demo");
// ...
//#props-factory
@Override
public Receive createReceive() {
return AbstractActor.emptyBehavior();
}
//#props-factory
}
//#props-factory
static
//#messages-in-companion
public class DemoMessagesActor extends AbstractLoggingActor {
static public class Greeting {
private final String from;
public Greeting(String from) {
this.from = from;
}
public String getGreeter() {
return from;
}
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Greeting.class, g -> {
log().info("I was greeted by {}", g.getGreeter());
})
.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<Object> 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;
@Override
public Receive createReceive() {
return AbstractActor.emptyBehavior();
}
//#preStart
@Override
public void preStart() {
target = getContext().actorOf(Props.create(MyActor.class, "target"));
}
//#preStart
//#postStop
@Override
public void postStop() {
//#clean-up-some-resources
final String message = "stopped";
//#tell
// dont forget to think about who is the sender (2nd argument)
target.tell(message, getSelf());
//#tell
final Object result = "";
//#forward
target.forward(result, getContext());
//#forward
target = null;
//#clean-up-some-resources
}
//#postStop
// 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
}
}
public static class ReplyException extends AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder()
.matchAny(x -> {
//#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
})
.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 =
getContext().watch(getContext().actorOf(Props.create(Cruncher.class), "worker"));
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("job", s -> {
worker.tell("crunch", getSelf());
})
.matchEquals(SHUTDOWN, x -> {
worker.tell(PoisonPill.getInstance(), getSelf());
getContext().become(shuttingDown());
})
.build();
}
private AbstractActor.Receive shuttingDown() {
return receiveBuilder()
.matchEquals("job", s ->
getSender().tell("service unavailable, shutting down", getSelf())
)
.match(Terminated.class, t -> t.actor().equals(worker), t ->
getContext().stop(getSelf())
)
.build();
}
}
//#gracefulStop-actor
@Test
public void usePatternsGracefulStop() throws Exception {
ActorRef actorRef = system.actorOf(Props.create(Manager.class));
//#gracefulStop
try {
CompletionStage<Boolean> stopped =
gracefulStop(actorRef, Duration.create(5, TimeUnit.SECONDS), Manager.SHUTDOWN);
stopped.toCompletableFuture().get(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 {
@Override
public Receive createReceive() {
return receiveBuilder().matchEquals("crunch", s -> { }).build();
}
}
static
//#swapper
public class Swapper extends AbstractLoggingActor {
@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();
}
}
//#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(MyActor.class), "myactor");
//#system-actorOf
try {
new TestKit(system) {
{
myActor.tell("hello", getRef());
expectMsgEquals("hello");
}
};
} finally {
TestKit.shutdownActorSystem(system);
}
}
@Test
public void creatingGraduallyBuiltActorWithSystemActorOf() {
final ActorSystem system = ActorSystem.create("MySystem", config);
final ActorRef actor = system.actorOf(Props.create(GraduallyBuiltActor.class), "graduallyBuiltActor");
try {
new TestKit(system) {
{
actor.tell("hello", getRef());
expectMsgEquals("hello");
}
};
} finally {
TestKit.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 = getContext().getSystem().deadLetters();
//#receive-timeout
public ReceiveTimeoutActor() {
// To set an initial delay
getContext().setReceiveTimeout(Duration.create(10, TimeUnit.SECONDS));
}
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("Hello", s -> {
// To set in a response to a message
getContext().setReceiveTimeout(Duration.create(1, TimeUnit.SECONDS));
//#receive-timeout
target = getSender();
target.tell("Hello world", getSelf());
//#receive-timeout
})
.match(ReceiveTimeout.class, r -> {
// To turn it off
getContext().setReceiveTimeout(Duration.Undefined());
//#receive-timeout
target.tell("timeout", getSelf());
//#receive-timeout
})
.build();
}
}
//#receive-timeout
@Test
public void using_receiveTimeout() {
final ActorRef myActor = system.actorOf(Props.create(ReceiveTimeoutActor.class));
new TestKit(system) {
{
myActor.tell("Hello", getRef());
expectMsgEquals("Hello world");
expectMsgEquals("timeout");
}
};
}
static
//#hot-swap-actor
public class HotSwapActor extends AbstractActor {
private AbstractActor.Receive angry;
private AbstractActor.Receive happy;
public HotSwapActor() {
angry =
receiveBuilder()
.matchEquals("foo", s -> {
getSender().tell("I am already angry?", getSelf());
})
.matchEquals("bar", s -> {
getContext().become(happy);
})
.build();
happy = receiveBuilder()
.matchEquals("bar", s -> {
getSender().tell("I am already happy :-)", getSelf());
})
.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
@Test
public void using_hot_swap() {
final ActorRef actor = system.actorOf(Props.create(HotSwapActor.class), "hot");
new TestKit(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 {
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("open", s -> {
getContext().become(receiveBuilder()
.matchEquals("write", ws -> { /* do writing */ })
.matchEquals("close", cs -> {
unstashAll();
getContext().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 = getContext().actorOf(Props.empty(), "target");
private ActorRef lastSender = system.deadLetters();
public WatchActor() {
getContext().watch(child); // <-- this is the only call needed for registration
}
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("kill", s -> {
getContext().stop(child);
lastSender = getSender();
})
.match(Terminated.class, t -> t.actor().equals(child), t -> {
lastSender.tell("finished", getSelf());
})
.build();
}
}
//#watch
@Test
public void using_watch() {
ActorRef actor = system.actorOf(Props.create(WatchActor.class));
new TestKit(system) {
{
actor.tell("kill", getRef());
expectMsgEquals("finished");
}
};
}
static
//#identify
public class Follower extends AbstractActor {
final Integer identifyId = 1;
public Follower(){
ActorSelection selection = getContext().actorSelection("/user/another");
selection.tell(new Identify(identifyId), getSelf());
}
@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(getSelf());
})
.build();
}
final AbstractActor.Receive active(final ActorRef another) {
return receiveBuilder()
.match(Terminated.class, t -> t.actor().equals(another), t ->
getContext().stop(getSelf())
)
.build();
}
}
//#identify
@Test
public void using_Identify() {
ActorRef a = system.actorOf(Props.empty());
ActorRef b = system.actorOf(Props.create(Follower.class));
new TestKit(system) {
{
watch(b);
system.stop(a);
assertEquals(expectMsgClass(Duration.create(2, TimeUnit.SECONDS), Terminated.class).actor(), b);
}
};
}
@Test
public void usePatternsAskPipe() {
new TestKit(system) {
{
ActorRef actorA = system.actorOf(TestActors.echoActorProps());
ActorRef actorB = system.actorOf(TestActors.echoActorProps());
ActorRef actorC = getRef();
//#ask-pipe
Timeout t = new Timeout(Duration.create(5, TimeUnit.SECONDS));
// using 1000ms timeout
CompletableFuture<Object> future1 =
ask(actorA, "request", 1000).toCompletableFuture();
// using timeout from above
CompletableFuture<Object> future2 =
ask(actorB, "another request", t).toCompletableFuture();
CompletableFuture<Result> transformed =
CompletableFuture.allOf(future1, future2)
.thenApply(v -> {
String x = (String) future1.join();
String s = (String) future2.join();
return new Result(x, s);
});
pipe(transformed, system.dispatcher()).to(actorC);
//#ask-pipe
expectMsgEquals(new Result("request", "another request"));
}
};
}
@Test
public void useKill() {
new TestKit(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 TestKit(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
public void coordinatedShutdown() {
final ActorRef someActor = system.actorOf(Props.create(FirstActor.class));
//#coordinated-shutdown-addTask
CoordinatedShutdown.get(system).addTask(
CoordinatedShutdown.PhaseBeforeServiceUnbind(), "someTaskName",
() -> {
return akka.pattern.PatternsCS.ask(someActor, "stop", new Timeout(5, TimeUnit.SECONDS))
.thenApply(reply -> Done.getInstance());
});
//#coordinated-shutdown-addTask
//#coordinated-shutdown-jvm-hook
CoordinatedShutdown.get(system).addJvmShutdownHook(() ->
System.out.println("custom JVM shutdown hook...")
);
//#coordinated-shutdown-jvm-hook
// don't run this
if (false) {
//#coordinated-shutdown-run
CompletionStage<Done> done = CoordinatedShutdown.get(system).runAll();
//#coordinated-shutdown-run
}
}
}

View file

@ -0,0 +1,79 @@
/*
* Copyright (C) 2016-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.actor;
//#bytebufserializer-with-manifest
import akka.serialization.ByteBufferSerializer;
import akka.serialization.SerializerWithStringManifest;
//#bytebufserializer-with-manifest
import java.nio.ByteBuffer;
public class ByteBufferSerializerDocTest {
static //#bytebufserializer-with-manifest
class ExampleByteBufSerializer extends SerializerWithStringManifest
implements ByteBufferSerializer {
@Override
public int identifier() {
return 1337;
}
@Override
public String manifest(Object o) {
return "serialized-" + o.getClass().getSimpleName();
}
@Override
public byte[] toBinary(Object o) {
// in production code, aquire this from a BufferPool
final ByteBuffer buf = ByteBuffer.allocate(256);
toBinary(o, buf);
buf.flip();
final byte[] bytes = new byte[buf.remaining()];
buf.get(bytes);
return bytes;
}
@Override
public Object fromBinary(byte[] bytes, String manifest) {
return fromBinary(ByteBuffer.wrap(bytes), manifest);
}
@Override
public void toBinary(Object o, ByteBuffer buf) {
// Implement actual serialization here
}
@Override
public Object fromBinary(ByteBuffer buf, String manifest) {
// Implement actual deserialization here
return null;
}
}
//#bytebufserializer-with-manifest
static class OnlyForDocInclude {
static
//#ByteBufferSerializer-interface
interface ByteBufferSerializer {
/**
* Serializes the given object into the `ByteBuffer`.
*/
void toBinary(Object o, ByteBuffer buf);
/**
* Produces an object from a `ByteBuffer`, with an optional type-hint;
* the class should be loaded using ActorSystem.dynamicAccess.
*/
Object fromBinary(ByteBuffer buf, String manifest);
}
//#ByteBufferSerializer-interface
}
}

View file

@ -0,0 +1,105 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.actor;
import jdocs.AbstractJavaTest;
import akka.testkit.javadsl.TestKit;
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
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 -> {
getSender().tell(s, getSelf());
})
.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<? extends Actor> 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 TestKit(system) {
{
myActor.tell("hello", getRef());
expectMsgEquals("...");
}
};
}
}

View file

@ -0,0 +1,469 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.actor;
//#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.pattern.Patterns;
import akka.util.Timeout;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import scala.concurrent.duration.Duration;
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 static akka.pattern.Patterns.ask;
import static akka.pattern.Patterns.pipe;
import static jdocs.actor.FaultHandlingDocSample.WorkerApi.*;
import static jdocs.actor.FaultHandlingDocSample.CounterServiceApi.*;
import static jdocs.actor.FaultHandlingDocSample.CounterApi.*;
import static jdocs.actor.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 {\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
getContext().setReceiveTimeout(Duration.create("15 seconds"));
}
@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");
getContext().getSystem().terminate();
}
}).
matchEquals(ReceiveTimeout.getInstance(), x -> {
// No progress within 15 seconds, ServiceUnavailable
log().error("Shutting down due to unavailable service");
getContext().getSystem().terminate();
}).build(), getContext());
}
}
//#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 = getContext().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;
}
@Override
public Receive createReceive() {
return LoggingReceive.create(receiveBuilder().
matchEquals(Start, x -> progressListener == null, x -> {
progressListener = getSender();
getContext().getSystem().scheduler().schedule(
Duration.Zero(), Duration.create(1, "second"), getSelf(), Do,
getContext().dispatcher(), null
);
}).
matchEquals(Do, x -> {
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(Patterns.ask(counterService, GetCurrentCount, askTimeout)
.mapTo(classTag(CurrentCount.class))
.map(new Mapper<CurrentCount, Progress>() {
public Progress apply(CurrentCount c) {
return new Progress(100.0 * c.count / totalCount);
}
}, getContext().dispatcher()), getContext().dispatcher())
.to(progressListener);
}).build(), getContext());
}
}
//#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 = getSelf().path().name();
ActorRef storage;
ActorRef counter;
final List<SenderMsgPair> 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 = 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 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 = 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();
}).
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), getSelf());
// Try to re-establish storage after while
getContext().getSystem().scheduler().scheduleOnce(
Duration.create(10, "seconds"), getSelf(), Reconnect,
getContext().dispatcher(), null);
}).
matchEquals(Reconnect, o -> {
// Re-establish storage after the scheduled delay
initStorage();
}).build(), getContext());
}
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 AbstractLoggingActor {
final String key;
long count;
ActorRef storage;
public Counter(String key, long initialValue) {
this.key = key;
this.count = initialValue;
}
@Override
public Receive createReceive() {
return LoggingReceive.create(receiveBuilder().
match(UseStorage.class, useStorage -> {
storage = useStorage.storage;
storeCount();
}).
match(Increment.class, increment -> {
count += increment.n;
storeCount();
}).
matchEquals(GetCurrentCount, gcc -> {
getSender().tell(new CurrentCount(key, count), getSelf());
}).build(), getContext());
}
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 AbstractLoggingActor {
final DummyDB db = DummyDB.instance;
@Override
public Receive createReceive() {
return 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);
getSender().tell(new Entry(get.key, value == null ?
Long.valueOf(0L) : value), getSelf());
}).build(), getContext());
}
}
//#dummydb
public static class DummyDB {
public static final DummyDB instance = new DummyDB();
private final Map<String, Long> db = new HashMap<String, Long>();
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

View file

@ -0,0 +1,212 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.actor;
import akka.actor.*;
import akka.testkit.javadsl.TestKit;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import jdocs.AbstractJavaTest;
import java.util.Optional;
import static akka.pattern.Patterns.ask;
//#testkit
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;
//#supervisor
import org.junit.Test;
import org.junit.BeforeClass;
import org.junit.AfterClass;
//#testkit
public class FaultHandlingTest extends AbstractJavaTest {
//#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
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Props.class, props -> {
getSender().tell(getContext().actorOf(props), getSelf());
})
.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
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Props.class, props -> {
getSender().tell(getContext().actorOf(props), getSelf());
})
.build();
}
@Override
public void preRestart(Throwable cause, Optional<Object> msg) {
// do not kill all children, which is the default here
}
}
//#supervisor2
static
//#child
public class Child extends AbstractActor {
int state = 0;
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Exception.class, exception -> { throw exception; })
.match(Integer.class, i -> state = i)
.matchEquals("get", s -> getSender().tell(state, getSelf()))
.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() {
TestKit.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

View file

@ -0,0 +1,39 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.actor;
//#imports
import akka.actor.AbstractActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.japi.pf.ReceiveBuilder;
//#imports
//#actor
public class GraduallyBuiltActor extends AbstractActor {
private final LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
@Override
public Receive createReceive() {
ReceiveBuilder builder = ReceiveBuilder.create();
builder.match(String.class, s -> {
log.info("Received String message: {}", s);
//#actor
//#reply
getSender().tell(s, getSelf());
//#reply
//#actor
});
// do some other stuff in between
builder.matchAny(o -> log.info("received unknown message"));
return builder.build();
}
}
//#actor

View file

@ -0,0 +1,28 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.actor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
//#immutable-message
public class ImmutableMessage {
private final int sequenceNumber;
private final List<String> values;
public ImmutableMessage(int sequenceNumber, List<String> values) {
this.sequenceNumber = sequenceNumber;
this.values = Collections.unmodifiableList(new ArrayList<String>(values));
}
public int getSequenceNumber() {
return sequenceNumber;
}
public List<String> getValues() {
return values;
}
}
//#immutable-message

View file

@ -0,0 +1,66 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.actor;
import java.util.concurrent.TimeUnit;
import akka.testkit.AkkaJUnitActorSystemResource;
import jdocs.AbstractJavaTest;
import akka.testkit.javadsl.TestKit;
import org.junit.ClassRule;
import org.junit.Test;
import scala.concurrent.duration.Duration;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Inbox;
import akka.actor.PoisonPill;
import akka.actor.Terminated;
import akka.testkit.AkkaSpec;
public class InboxDocTest extends AbstractJavaTest {
@ClassRule
public static AkkaJUnitActorSystemResource actorSystemResource =
new AkkaJUnitActorSystemResource("InboxDocTest", AkkaSpec.testConf());
private final ActorSystem system = actorSystemResource.getSystem();
@Test
public void demonstrateInbox() {
final TestKit probe = new TestKit(system);
final ActorRef target = probe.getRef();
//#inbox
final Inbox inbox = Inbox.create(system);
inbox.send(target, "hello");
//#inbox
probe.expectMsgEquals("hello");
probe.send(probe.getLastSender(), "world");
//#inbox
try {
assert inbox.receive(Duration.create(1, TimeUnit.SECONDS)).equals("world");
} catch (java.util.concurrent.TimeoutException e) {
// timeout
}
//#inbox
}
@Test
public void demonstrateWatch() {
final TestKit probe = new TestKit(system);
final ActorRef target = probe.getRef();
//#watch
final Inbox inbox = Inbox.create(system);
inbox.watch(target);
target.tell(PoisonPill.getInstance(), ActorRef.noSender());
try {
assert inbox.receive(Duration.create(1, TimeUnit.SECONDS)) instanceof Terminated;
} catch (java.util.concurrent.TimeoutException e) {
// timeout
}
//#watch
}
}

View file

@ -0,0 +1,161 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.actor;
import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.japi.pf.FI;
import jdocs.AbstractJavaTest;
import akka.testkit.javadsl.TestKit;
import org.junit.AfterClass;
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 {
static ActorSystem system = null;
@BeforeClass
public static void beforeClass() {
system = ActorSystem.create("InitializationDocTest");
}
@AfterClass
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<Object> 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;
//#messageInit
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("init", m1 -> {
initializeMe = "Up and running";
getContext().become(receiveBuilder()
.matchEquals("U OK?", m2 -> {
getSender().tell(initializeMe, getSelf());
})
.build());
})
.build();
}
//#messageInit
}
public class GenericMessage<T> {
T value;
public GenericMessage(T value) {
this.value = value;
}
}
public static class GenericActor extends AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder()
.matchUnchecked(GenericMessage.class, (GenericMessage<String> msg) -> {
GenericMessage<String> message = msg;
getSender().tell(message.value.toUpperCase(), getSelf());
})
.build();
}
}
static class GenericActorWithPredicate extends AbstractActor {
@Override
public Receive createReceive() {
FI.TypedPredicate<GenericMessage<String>> typedPredicate = s -> !s.value.isEmpty();
return receiveBuilder()
.matchUnchecked(GenericMessage.class, typedPredicate, (GenericMessage<String> msg) -> {
getSender().tell(msg.value.toUpperCase(), getSelf());
})
.build();
}
}
@Test
public void testIt() {
new TestKit(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");
}};
}
@Test
public void testGenericActor() {
new TestKit(system) {{
ActorRef genericTestActor = system.actorOf(Props.create(GenericActor.class), "genericActor");
GenericMessage<String> genericMessage = new GenericMessage<String>("a");
genericTestActor.tell(genericMessage, getRef());
expectMsgEquals("A");
}};
}
@Test
public void actorShouldNotRespondForEmptyMessage() {
new TestKit(system) {{
ActorRef genericTestActor = system.actorOf(Props.create(GenericActorWithPredicate.class), "genericActorWithPredicate");
GenericMessage<String> emptyGenericMessage = new GenericMessage<String>("");
GenericMessage<String> nonEmptyGenericMessage = new GenericMessage<String>("a");
genericTestActor.tell(emptyGenericMessage, getRef());
expectNoMsg();
genericTestActor.tell(nonEmptyGenericMessage, getRef());
expectMsgEquals("A");
}};
}
}

View file

@ -0,0 +1,149 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.actor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Messages {
static
//#immutable-message
public class ImmutableMessage {
private final int sequenceNumber;
private final List<String> values;
public ImmutableMessage(int sequenceNumber, List<String> values) {
this.sequenceNumber = sequenceNumber;
this.values = Collections.unmodifiableList(new ArrayList<String>(values));
}
public int getSequenceNumber() {
return sequenceNumber;
}
public List<String> getValues() {
return values;
}
}
//#immutable-message
public static class DoIt {
private final ImmutableMessage msg;
DoIt(ImmutableMessage msg) {
this.msg = msg;
}
public ImmutableMessage getMsg() {
return msg;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DoIt doIt = (DoIt) o;
if (!msg.equals(doIt.msg)) return false;
return true;
}
@Override
public int hashCode() {
return msg.hashCode();
}
@Override
public String toString() {
return "DoIt{" +
"msg=" + msg +
'}';
}
}
public static class Message {
final String str;
Message(String str) {
this.str = str;
}
public String getStr() {
return str;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Message message = (Message) o;
if (!str.equals(message.str)) return false;
return true;
}
@Override
public int hashCode() {
return str.hashCode();
}
@Override
public String toString() {
return "Message{" +
"str='" + str + '\'' +
'}';
}
}
public static enum Swap {
Swap
}
public static 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;
}
}
}

View file

@ -0,0 +1,33 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.actor;
//#imports
import akka.actor.AbstractActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
//#imports
//#my-actor
public class MyActor extends AbstractActor {
private final LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
@Override
public Receive createReceive() {
return receiveBuilder()
.match(String.class, s -> {
log.info("Received String message: {}", s);
//#my-actor
//#reply
getSender().tell(s, getSelf());
//#reply
//#my-actor
})
.matchAny(o -> log.info("received unknown message"))
.build();
}
}
//#my-actor

View file

@ -0,0 +1,14 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.actor;
//#my-bounded-untyped-actor
import akka.dispatch.BoundedMessageQueueSemantics;
import akka.dispatch.RequiresMessageQueue;
public class MyBoundedActor extends MyActor
implements RequiresMessageQueue<BoundedMessageQueueSemantics> {
}
//#my-bounded-untyped-actor

View file

@ -0,0 +1,29 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.actor;
//#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(getSelf())
)
.build();
}
}
//#my-stopping-actor

View file

@ -0,0 +1,35 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.actor;
//#sample-actor
import akka.actor.AbstractActor;
public class SampleActor extends AbstractActor {
private Receive guarded = receiveBuilder()
.match(String.class, s -> s.contains("guard"), s -> {
getSender().tell("contains(guard): " + s, getSelf());
getContext().unbecome();
})
.build();
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Double.class, d -> {
getSender().tell(d.isNaN() ? 0 : d, getSelf());
})
.match(Integer.class, i -> {
getSender().tell(i * 10, getSelf());
})
.match(String.class, s -> s.startsWith("guard"), s -> {
getSender().tell("startsWith(guard): " + s.toUpperCase(), getSelf());
getContext().become(guarded, false);
})
.build();
}
}
//#sample-actor

View file

@ -0,0 +1,56 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.actor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import jdocs.AbstractJavaTest;
import akka.testkit.javadsl.TestKit;
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() {
TestKit.shutdownActorSystem(system);
system = null;
}
@Test
public void testSampleActor()
{
new TestKit(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();
}};
}
}

View file

@ -0,0 +1,78 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.actor;
//#imports1
import akka.actor.Props;
import jdocs.AbstractJavaTest;
import scala.concurrent.duration.Duration;
import java.util.concurrent.TimeUnit;
//#imports1
//#imports2
import akka.actor.Cancellable;
//#imports2
import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.testkit.AkkaSpec;
import akka.testkit.AkkaJUnitActorSystemResource;
import org.junit.*;
public class SchedulerDocTest extends AbstractJavaTest {
@ClassRule
public static AkkaJUnitActorSystemResource actorSystemResource = new AkkaJUnitActorSystemResource("SchedulerDocTest",
AkkaSpec.testConf());
private final ActorSystem system = actorSystemResource.getSystem();
private ActorRef testActor = system.actorOf(Props.create(MyActor.class));
@Test
public void scheduleOneOffTask() {
//#schedule-one-off-message
system.scheduler().scheduleOnce(Duration.create(50, TimeUnit.MILLISECONDS),
testActor, "foo", system.dispatcher(), null);
//#schedule-one-off-message
//#schedule-one-off-thunk
system.scheduler().scheduleOnce(Duration.create(50, TimeUnit.MILLISECONDS),
new Runnable() {
@Override
public void run() {
testActor.tell(System.currentTimeMillis(), ActorRef.noSender());
}
}, system.dispatcher());
//#schedule-one-off-thunk
}
@Test
public void scheduleRecurringTask() {
//#schedule-recurring
class Ticker extends AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("Tick", m -> {
// Do someting
})
.build();
}
}
ActorRef tickActor = system.actorOf(Props.create(Ticker.class, this));
//This will schedule to send the Tick-message
//to the tickActor after 0ms repeating every 50ms
Cancellable cancellable = system.scheduler().schedule(Duration.Zero(),
Duration.create(50, TimeUnit.MILLISECONDS), tickActor, "Tick",
system.dispatcher(), null);
//This cancels further Ticks to be sent
cancellable.cancel();
//#schedule-recurring
system.stop(tickActor);
}
}

View file

@ -0,0 +1,252 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.actor;
//#imports
import akka.actor.TypedActor;
import akka.actor.*;
import akka.japi.*;
import akka.dispatch.Futures;
import jdocs.AbstractJavaTest;
import scala.concurrent.Await;
import scala.concurrent.Future;
import scala.concurrent.duration.Duration;
import java.util.concurrent.TimeUnit;
import java.util.List;
import java.util.ArrayList;
import java.util.Random;
import akka.routing.RoundRobinGroup;
//#imports
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class TypedActorDocTest extends AbstractJavaTest {
Object someReference = null;
ActorSystem system = null;
static
//#typed-actor-iface
public interface Squarer {
//#typed-actor-iface-methods
void squareDontCare(int i); //fire-forget
Future<Integer> square(int i); //non-blocking send-request-reply
Option<Integer> squareNowPlease(int i);//blocking send-request-reply
int squareNow(int i); //blocking send-request-reply
//#typed-actor-iface-methods
}
//#typed-actor-iface
static
//#typed-actor-impl
class SquarerImpl implements Squarer {
private String name;
public SquarerImpl() {
this.name = "default";
}
public SquarerImpl(String name) {
this.name = name;
}
//#typed-actor-impl-methods
public void squareDontCare(int i) {
int sq = i * i; //Nobody cares :(
}
public Future<Integer> square(int i) {
return Futures.successful(i * i);
}
public Option<Integer> squareNowPlease(int i) {
return Option.some(i * i);
}
public int squareNow(int i) {
return i * i;
}
//#typed-actor-impl-methods
}
//#typed-actor-impl
@Test public void mustGetTheTypedActorExtension() {
try {
//#typed-actor-extension-tools
//Returns the Typed Actor Extension
TypedActorExtension extension =
TypedActor.get(system); //system is an instance of ActorSystem
//Returns whether the reference is a Typed Actor Proxy or not
TypedActor.get(system).isTypedActor(someReference);
//Returns the backing Akka Actor behind an external Typed Actor Proxy
TypedActor.get(system).getActorRefFor(someReference);
//Returns the current ActorContext,
// method only valid within methods of a TypedActor implementation
ActorContext context = TypedActor.context();
//Returns the external proxy of the current Typed Actor,
// method only valid within methods of a TypedActor implementation
Squarer sq = TypedActor.<Squarer>self();
//Returns a contextual instance of the Typed Actor Extension
//this means that if you create other Typed Actors with this,
//they will become children to the current Typed Actor.
TypedActor.get(TypedActor.context());
//#typed-actor-extension-tools
} catch (Exception e) {
//dun care
}
}
@Test public void createATypedActor() {
try {
//#typed-actor-create1
Squarer mySquarer =
TypedActor.get(system).typedActorOf(
new TypedProps<SquarerImpl>(Squarer.class, SquarerImpl.class));
//#typed-actor-create1
//#typed-actor-create2
Squarer otherSquarer =
TypedActor.get(system).typedActorOf(
new TypedProps<SquarerImpl>(Squarer.class,
new Creator<SquarerImpl>() {
public SquarerImpl create() { return new SquarerImpl("foo"); }
}),
"name");
//#typed-actor-create2
//#typed-actor-calls
//#typed-actor-call-oneway
mySquarer.squareDontCare(10);
//#typed-actor-call-oneway
//#typed-actor-call-future
Future<Integer> fSquare = mySquarer.square(10); //A Future[Int]
//#typed-actor-call-future
//#typed-actor-call-option
Option<Integer> oSquare = mySquarer.squareNowPlease(10); //Option[Int]
//#typed-actor-call-option
//#typed-actor-call-strict
int iSquare = mySquarer.squareNow(10); //Int
//#typed-actor-call-strict
//#typed-actor-calls
assertEquals(100, Await.result(fSquare,
Duration.create(3, TimeUnit.SECONDS)).intValue());
assertEquals(100, oSquare.get().intValue());
assertEquals(100, iSquare);
//#typed-actor-stop
TypedActor.get(system).stop(mySquarer);
//#typed-actor-stop
//#typed-actor-poisonpill
TypedActor.get(system).poisonPill(otherSquarer);
//#typed-actor-poisonpill
} catch(Exception e) {
//Ignore
}
}
@Test public void createHierarchies() {
try {
//#typed-actor-hierarchy
Squarer childSquarer =
TypedActor.get(TypedActor.context()).
typedActorOf(
new TypedProps<SquarerImpl>(Squarer.class, SquarerImpl.class)
);
//Use "childSquarer" as a Squarer
//#typed-actor-hierarchy
} catch (Exception e) {
//dun care
}
}
@Test public void proxyAnyActorRef() {
try {
final ActorRef actorRefToRemoteActor = system.deadLetters();
//#typed-actor-remote
Squarer typedActor =
TypedActor.get(system).
typedActorOf(
new TypedProps<Squarer>(Squarer.class),
actorRefToRemoteActor
);
//Use "typedActor" as a FooBar
//#typed-actor-remote
} catch (Exception e) {
//dun care
}
}
//#typed-router-types
interface HasName {
String name();
}
class Named implements HasName {
private int id = new Random().nextInt(1024);
@Override public String name() { return "name-" + id; }
}
//#typed-router-types
@Test public void typedRouterPattern() {
try {
//#typed-router
// prepare routees
TypedActorExtension typed = TypedActor.get(system);
Named named1 =
typed.typedActorOf(new TypedProps<Named>(Named.class));
Named named2 =
typed.typedActorOf(new TypedProps<Named>(Named.class));
List<Named> routees = new ArrayList<Named>();
routees.add(named1);
routees.add(named2);
List<String> routeePaths = new ArrayList<String>();
routeePaths.add(typed.getActorRefFor(named1).path().toStringWithoutAddress());
routeePaths.add(typed.getActorRefFor(named2).path().toStringWithoutAddress());
// prepare untyped router
ActorRef router = system.actorOf(new RoundRobinGroup(routeePaths).props(), "router");
// prepare typed proxy, forwarding MethodCall messages to `router`
Named typedRouter = typed.typedActorOf(new TypedProps<Named>(Named.class), router);
System.out.println("actor was: " + typedRouter.name()); // name-243
System.out.println("actor was: " + typedRouter.name()); // name-614
System.out.println("actor was: " + typedRouter.name()); // name-243
System.out.println("actor was: " + typedRouter.name()); // name-614
//#typed-router
typed.poisonPill(named1);
typed.poisonPill(named2);
typed.poisonPill(typedRouter);
} catch (Exception e) {
//dun care
}
}
}

View file

@ -0,0 +1,136 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.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 jdocs.actor.fsm.Buncher.Data;
import static jdocs.actor.fsm.Buncher.State.*;
import static jdocs.actor.fsm.Buncher.State;
import static jdocs.actor.fsm.Buncher.Uninitialized.*;
import static jdocs.actor.fsm.Events.*;
//#simple-fsm
public class Buncher extends AbstractFSM<State, Data> {
{
//#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<Data> m = UnitMatch.create(
matchData(Todo.class,
todo -> todo.getTarget().tell(new Batch(todo.getQueue()), getSelf())));
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<Object> queue;
public Todo(ActorRef target, List<Object> queue) {
this.target = target;
this.queue = queue;
}
public ActorRef getTarget() {
return target;
}
public List<Object> getQueue() {
return queue;
}
//#boilerplate
@Override
public String toString() {
return "Todo{" +
"target=" + target +
", queue=" + queue +
'}';
}
public Todo addElement(Object element) {
List<Object> nQueue = new LinkedList<>(queue);
nQueue.add(element);
return new Todo(this.target, nQueue);
}
public Todo copy(List<Object> 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

View file

@ -0,0 +1,78 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.actor.fsm;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import jdocs.AbstractJavaTest;
import akka.testkit.javadsl.TestKit;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.LinkedList;
import static jdocs.actor.fsm.Events.Batch;
import static jdocs.actor.fsm.Events.Queue;
import static jdocs.actor.fsm.Events.SetTarget;
import static jdocs.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() {
TestKit.shutdownActorSystem(system);
system = null;
}
@Test
public void testBuncherActorBatchesCorrectly() {
new TestKit(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<Object> 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<Object> list2 = new LinkedList<>();
list2.add(44);
expectMsgEquals(new Batch(list2));
LinkedList<Object> list3 = new LinkedList<>();
list3.add(45);
expectMsgEquals(new Batch(list3));
system.stop(buncher);
}};
}
@Test
public void testBuncherActorDoesntBatchUninitialized() {
new TestKit(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

View file

@ -0,0 +1,108 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.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<Object> list;
public Batch(List<Object> list) {
this.list = list;
}
public List<Object> 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
}

View file

@ -0,0 +1,179 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.actor.fsm;
import akka.actor.*;
import jdocs.AbstractJavaTest;
import akka.testkit.javadsl.TestKit;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import scala.concurrent.duration.Duration;
import static org.junit.Assert.*;
import static jdocs.actor.fsm.FSMDocTest.StateType.*;
import static jdocs.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() {
TestKit.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<StateType, Integer> {
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<StateType, Data> {
//#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(), getSelf());
target.tell(state, getSelf());
target.tell(data, getSelf());
target.tell(lastEvents, getSelf());
//#logging-fsm
})
);
//...
//#logging-fsm
startWith(SomeState, Data.Foo);
when(SomeState, matchEvent(ActorRef.class, Data.class, (ref, data) -> {
target = ref;
target.tell("going active", getSelf());
return goTo(Active);
}));
when(Active, matchEventEquals("stop", (event, data) -> {
target.tell("stopping", getSelf());
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 TestKit(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);
assertTrue(msg.startsWith("LogEntry(SomeState,Foo,Actor[akka://FSMDocTest/system/"));
}};
}
}