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,13 @@
/*
* Copyright (C) 2016-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs
import org.scalatest.junit.JUnitSuite
/**
* Base class for all runnable example tests written in Java
*/
abstract class AbstractJavaTest extends JUnitSuite {
}

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/"));
}};
}
}

View file

@ -0,0 +1,115 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.agent;
import static org.junit.Assert.*;
import org.junit.Test;
import scala.concurrent.Await;
import scala.concurrent.duration.Duration;
//#import-agent
import scala.concurrent.ExecutionContext;
import akka.agent.Agent;
import akka.dispatch.ExecutionContexts;
//#import-agent
//#import-function
import akka.dispatch.Mapper;
//#import-function
//#import-future
import scala.concurrent.Future;
//#import-future
public class AgentDocTest extends jdocs.AbstractJavaTest {
private static ExecutionContext ec = ExecutionContexts.global();
@Test
public void createAndRead() throws Exception {
//#create
ExecutionContext ec = ExecutionContexts.global();
Agent<Integer> agent = Agent.create(5, ec);
//#create
//#read-get
Integer result = agent.get();
//#read-get
//#read-future
Future<Integer> future = agent.future();
//#read-future
assertEquals(result, new Integer(5));
assertEquals(Await.result(future, Duration.create(5,"s")), new Integer(5));
}
@Test
public void sendAndSendOffAndReadAwait() throws Exception {
Agent<Integer> agent = Agent.create(5, ec);
//#send
// send a value, enqueues this change
// of the value of the Agent
agent.send(7);
// send a Mapper, enqueues this change
// to the value of the Agent
agent.send(new Mapper<Integer, Integer>() {
public Integer apply(Integer i) {
return i * 2;
}
});
//#send
Mapper<Integer, Integer> longRunningOrBlockingFunction = new Mapper<Integer, Integer>() {
public Integer apply(Integer i) {
return i * 1;
}
};
ExecutionContext theExecutionContextToExecuteItIn = ec;
//#send-off
// sendOff a function
agent.sendOff(longRunningOrBlockingFunction,
theExecutionContextToExecuteItIn);
//#send-off
assertEquals(Await.result(agent.future(), Duration.create(5,"s")), new Integer(14));
}
@Test
public void alterAndAlterOff() throws Exception {
Agent<Integer> agent = Agent.create(5, ec);
//#alter
// alter a value
Future<Integer> f1 = agent.alter(7);
// alter a function (Mapper)
Future<Integer> f2 = agent.alter(new Mapper<Integer, Integer>() {
public Integer apply(Integer i) {
return i * 2;
}
});
//#alter
Mapper<Integer, Integer> longRunningOrBlockingFunction = new Mapper<Integer, Integer>() {
public Integer apply(Integer i) {
return i * 1;
}
};
ExecutionContext theExecutionContextToExecuteItIn = ec;
//#alter-off
// alterOff a function (Mapper)
Future<Integer> f3 = agent.alterOff(longRunningOrBlockingFunction,
theExecutionContextToExecuteItIn);
//#alter-off
assertEquals(Await.result(f3, Duration.create(5,"s")), new Integer(14));
}
}

View file

@ -0,0 +1,54 @@
package jdocs.camel;
//#CamelActivation
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.camel.Camel;
import akka.camel.CamelExtension;
import akka.camel.javaapi.UntypedConsumerActor;
import akka.testkit.javadsl.TestKit;
import akka.util.Timeout;
import jdocs.AbstractJavaTest;
import scala.concurrent.Future;
import scala.concurrent.duration.Duration;
import static java.util.concurrent.TimeUnit.SECONDS;
//#CamelActivation
import org.junit.Test;
public class ActivationTestBase extends AbstractJavaTest {
@SuppressWarnings("unused")
@Test
public void testActivation() {
//#CamelActivation
// ..
ActorSystem system = ActorSystem.create("some-system");
Props props = Props.create(MyConsumer.class);
ActorRef producer = system.actorOf(props,"myproducer");
Camel camel = CamelExtension.get(system);
// get a future reference to the activation of the endpoint of the Consumer Actor
Timeout timeout = new Timeout(Duration.create(10, SECONDS));
Future<ActorRef> activationFuture = camel.activationFutureFor(producer,
timeout, system.dispatcher());
//#CamelActivation
//#CamelDeactivation
// ..
system.stop(producer);
// get a future reference to the deactivation of the endpoint of the Consumer Actor
Future<ActorRef> deactivationFuture = camel.deactivationFutureFor(producer,
timeout, system.dispatcher());
//#CamelDeactivation
TestKit.shutdownActorSystem(system);
}
public static class MyConsumer extends UntypedConsumerActor {
public String getEndpointUri() {
return "direct:test";
}
public void onReceive(Object message) {
}
}
}

View file

@ -0,0 +1,34 @@
package jdocs.camel;
import akka.actor.ActorSystem;
import akka.camel.Camel;
import akka.camel.CamelExtension;
import jdocs.AbstractJavaTest;
import akka.testkit.javadsl.TestKit;
import org.apache.camel.CamelContext;
import org.apache.camel.ProducerTemplate;
import org.junit.Test;
public class CamelExtensionTest extends AbstractJavaTest {
@Test
public void getCamelExtension() {
//#CamelExtension
ActorSystem system = ActorSystem.create("some-system");
Camel camel = CamelExtension.get(system);
CamelContext camelContext = camel.context();
ProducerTemplate producerTemplate = camel.template();
//#CamelExtension
TestKit.shutdownActorSystem(system);
}
public void addActiveMQComponent() {
//#CamelExtensionAddComponent
ActorSystem system = ActorSystem.create("some-system");
Camel camel = CamelExtension.get(system);
CamelContext camelContext = camel.context();
// camelContext.addComponent("activemq", ActiveMQComponent.activeMQComponent(
// "vm://localhost?broker.persistent=false"));
//#CamelExtensionAddComponent
TestKit.shutdownActorSystem(system);
}
}

View file

@ -0,0 +1,24 @@
package jdocs.camel;
//#Consumer1
import akka.camel.CamelMessage;
import akka.camel.javaapi.UntypedConsumerActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
public class Consumer1 extends UntypedConsumerActor {
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
public String getEndpointUri() {
return "file:data/input/actor";
}
public void onReceive(Object message) {
if (message instanceof CamelMessage) {
CamelMessage camelMessage = (CamelMessage) message;
String body = camelMessage.getBodyAs(String.class, getCamelContext());
log.info("Received message: {}", body);
} else
unhandled(message);
}
}
//#Consumer1

View file

@ -0,0 +1,20 @@
package jdocs.camel;
//#Consumer2
import akka.camel.CamelMessage;
import akka.camel.javaapi.UntypedConsumerActor;
public class Consumer2 extends UntypedConsumerActor {
public String getEndpointUri() {
return "jetty:http://localhost:8877/camel/default";
}
public void onReceive(Object message) {
if (message instanceof CamelMessage) {
CamelMessage camelMessage = (CamelMessage) message;
String body = camelMessage.getBodyAs(String.class, getCamelContext());
getSender().tell(String.format("Received message: %s",body), getSelf());
} else
unhandled(message);
}
}
//#Consumer2

View file

@ -0,0 +1,31 @@
package jdocs.camel;
//#Consumer3
import akka.actor.Status;
import akka.camel.Ack;
import akka.camel.CamelMessage;
import akka.camel.javaapi.UntypedConsumerActor;
public class Consumer3 extends UntypedConsumerActor{
@Override
public boolean autoAck() {
return false;
}
public String getEndpointUri() {
return "jms:queue:test";
}
public void onReceive(Object message) {
if (message instanceof CamelMessage) {
getSender().tell(Ack.getInstance(), getSelf());
// on success
// ..
Exception someException = new Exception("e1");
// on failure
getSender().tell(new Status.Failure(someException), getSelf());
} else
unhandled(message);
}
}
//#Consumer3

View file

@ -0,0 +1,32 @@
package jdocs.camel;
//#Consumer4
import akka.camel.CamelMessage;
import akka.camel.javaapi.UntypedConsumerActor;
import scala.concurrent.duration.Duration;
import scala.concurrent.duration.FiniteDuration;
import java.util.concurrent.TimeUnit;
public class Consumer4 extends UntypedConsumerActor {
private final static FiniteDuration timeout =
Duration.create(500, TimeUnit.MILLISECONDS);
@Override
public FiniteDuration replyTimeout() {
return timeout;
}
public String getEndpointUri() {
return "jetty:http://localhost:8877/camel/default";
}
public void onReceive(Object message) {
if (message instanceof CamelMessage) {
CamelMessage camelMessage = (CamelMessage) message;
String body = camelMessage.getBodyAs(String.class, getCamelContext());
getSender().tell(String.format("Hello %s",body), getSelf());
} else
unhandled(message);
}
}
//#Consumer4

View file

@ -0,0 +1,18 @@
package jdocs.camel;
//#CustomRoute
import akka.actor.ActorRef;
import akka.camel.internal.component.CamelPath;
import org.apache.camel.builder.RouteBuilder;
public class CustomRouteBuilder extends RouteBuilder{
private String uri;
public CustomRouteBuilder(ActorRef responder) {
uri = CamelPath.toUri(responder);
}
public void configure() throws Exception {
from("jetty:http://localhost:8877/camel/custom").to(uri);
}
}
//#CustomRoute

View file

@ -0,0 +1,23 @@
package jdocs.camel;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.camel.Camel;
import akka.camel.CamelExtension;
import akka.testkit.javadsl.TestKit;
public class CustomRouteTestBase {
public void customRoute() throws Exception{
//#CustomRoute
ActorSystem system = ActorSystem.create("some-system");
try {
Camel camel = CamelExtension.get(system);
ActorRef responder = system.actorOf(Props.create(Responder.class), "TestResponder");
camel.context().addRoutes(new CustomRouteBuilder(responder));
//#CustomRoute
} finally {
TestKit.shutdownActorSystem(system);
}
}
}

View file

@ -0,0 +1,53 @@
package jdocs.camel;
//#ErrorThrowingConsumer
import akka.actor.Status;
import akka.camel.CamelMessage;
import akka.camel.javaapi.UntypedConsumerActor;
import akka.dispatch.Mapper;
import org.apache.camel.builder.Builder;
import org.apache.camel.model.ProcessorDefinition;
import org.apache.camel.model.RouteDefinition;
import scala.Option;
public class ErrorThrowingConsumer extends UntypedConsumerActor{
private String uri;
private static Mapper<RouteDefinition, ProcessorDefinition<?>> mapper =
new Mapper<RouteDefinition, ProcessorDefinition<?>>() {
public ProcessorDefinition<?> apply(RouteDefinition rd) {
// Catch any exception and handle it by returning the exception message
// as response
return rd.onException(Exception.class).handled(true).
transform(Builder.exceptionMessage()).end();
}
};
public ErrorThrowingConsumer(String uri){
this.uri = uri;
}
public String getEndpointUri() {
return uri;
}
public void onReceive(Object message) throws Exception{
if (message instanceof CamelMessage) {
CamelMessage camelMessage = (CamelMessage) message;
String body = camelMessage.getBodyAs(String.class, getCamelContext());
throw new Exception(String.format("error: %s",body));
} else
unhandled(message);
}
@Override
public Mapper<RouteDefinition,
ProcessorDefinition<?>> getRouteDefinitionHandler() {
return mapper;
}
@Override
public void preRestart(Throwable reason, Option<Object> message) {
getSender().tell(new Status.Failure(reason), getSelf());
}
}
//#ErrorThrowingConsumer

View file

@ -0,0 +1,10 @@
package jdocs.camel;
//#Producer1
import akka.camel.javaapi.UntypedProducerActor;
public class FirstProducer extends UntypedProducerActor {
public String getEndpointUri() {
return "http://localhost:8080/news";
}
}
//#Producer1

View file

@ -0,0 +1,24 @@
package jdocs.camel;
//#RouteResponse
import akka.actor.ActorRef;
import akka.camel.javaapi.UntypedProducerActor;
public class Forwarder extends UntypedProducerActor {
private String uri;
private ActorRef target;
public Forwarder(String uri, ActorRef target) {
this.uri = uri;
this.target = target;
}
public String getEndpointUri() {
return uri;
}
@Override
public void onRouteResponse(Object message) {
target.forward(message, getContext());
}
}
//#RouteResponse

View file

@ -0,0 +1,15 @@
package jdocs.camel;
//#ProducerTemplate
import akka.actor.UntypedAbstractActor;
import akka.camel.Camel;
import akka.camel.CamelExtension;
import org.apache.camel.ProducerTemplate;
public class MyActor extends UntypedAbstractActor {
public void onReceive(Object message) {
Camel camel = CamelExtension.get(getContext().getSystem());
ProducerTemplate template = camel.template();
template.sendBody("direct:news", message);
}
}
//#ProducerTemplate

View file

@ -0,0 +1,31 @@
package jdocs.camel;
//#Consumer-mina
import akka.camel.CamelMessage;
import akka.camel.javaapi.UntypedConsumerActor;
public class MyEndpoint extends UntypedConsumerActor{
private String uri;
public String getEndpointUri() {
return uri;
}
public void onReceive(Object message) throws Exception {
if (message instanceof CamelMessage) {
/* ... */
} else
unhandled(message);
}
// Extra constructor to change the default uri,
// for instance to "jetty:http://localhost:8877/example"
public MyEndpoint(String uri) {
this.uri = uri;
}
public MyEndpoint() {
this.uri = "mina2:tcp://localhost:6200?textline=true";
}
}
//#Consumer-mina

View file

@ -0,0 +1,23 @@
package jdocs.camel;
import akka.actor.*;
import akka.testkit.javadsl.TestKit;
public class OnRouteResponseTestBase {
public void onRouteResponse(){
//#RouteResponse
ActorSystem system = ActorSystem.create("some-system");
Props receiverProps = Props.create(ResponseReceiver.class);
final ActorRef receiver = system.actorOf(receiverProps,"responseReceiver");
ActorRef forwardResponse = system.actorOf(Props.create(
Forwarder.class, "http://localhost:8080/news/akka", receiver));
// the Forwarder sends out a request to the web page and forwards the response to
// the ResponseReceiver
forwardResponse.tell("some request", ActorRef.noSender());
//#RouteResponse
system.stop(receiver);
system.stop(forwardResponse);
TestKit.shutdownActorSystem(system);
}
}

View file

@ -0,0 +1,20 @@
package jdocs.camel;
//#Oneway
import akka.camel.javaapi.UntypedProducerActor;
public class OnewaySender extends UntypedProducerActor{
private String uri;
public OnewaySender(String uri) {
this.uri = uri;
}
public String getEndpointUri() {
return uri;
}
@Override
public boolean isOneway() {
return true;
}
}
//#Oneway

View file

@ -0,0 +1,10 @@
package jdocs.camel;
//#Producer
import akka.camel.javaapi.UntypedProducerActor;
public class Orders extends UntypedProducerActor {
public String getEndpointUri() {
return "jms:queue:Orders";
}
}
//#Producer

View file

@ -0,0 +1,10 @@
package jdocs.camel;
//#Producer1
import akka.camel.javaapi.UntypedProducerActor;
public class Producer1 extends UntypedProducerActor {
public String getEndpointUri() {
return "http://localhost:8080/news";
}
}
//#Producer1

View file

@ -0,0 +1,51 @@
package jdocs.camel;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletionStage;
import akka.testkit.javadsl.TestKit;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.camel.CamelMessage;
import akka.pattern.PatternsCS;
public class ProducerTestBase {
public void tellJmsProducer() {
//#TellProducer
ActorSystem system = ActorSystem.create("some-system");
Props props = Props.create(Orders.class);
ActorRef producer = system.actorOf(props, "jmsproducer");
producer.tell("<order amount=\"100\" currency=\"PLN\" itemId=\"12345\"/>",
ActorRef.noSender());
//#TellProducer
TestKit.shutdownActorSystem(system);
}
@SuppressWarnings("unused")
public void askProducer() {
//#AskProducer
ActorSystem system = ActorSystem.create("some-system");
Props props = Props.create(FirstProducer.class);
ActorRef producer = system.actorOf(props,"myproducer");
CompletionStage<Object> future = PatternsCS.ask(producer, "some request", 1000);
//#AskProducer
system.stop(producer);
TestKit.shutdownActorSystem(system);
}
public void correlate(){
//#Correlate
ActorSystem system = ActorSystem.create("some-system");
Props props = Props.create(Orders.class);
ActorRef producer = system.actorOf(props,"jmsproducer");
Map<String,Object> headers = new HashMap<String, Object>();
headers.put(CamelMessage.MessageExchangeId(),"123");
producer.tell(new CamelMessage("<order amount=\"100\" currency=\"PLN\" " +
"itemId=\"12345\"/>",headers), ActorRef.noSender());
//#Correlate
system.stop(producer);
TestKit.shutdownActorSystem(system);
}
}

View file

@ -0,0 +1,20 @@
package jdocs.camel;
//#RequestProducerTemplate
import akka.actor.AbstractActor;
import akka.camel.Camel;
import akka.camel.CamelExtension;
import org.apache.camel.ProducerTemplate;
public class RequestBodyActor extends AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder()
.matchAny(message -> {
Camel camel = CamelExtension.get(getContext().getSystem());
ProducerTemplate template = camel.template();
getSender().tell(template.requestBody("direct:news", message), getSelf());
})
.build();
}
}
//#RequestProducerTemplate

View file

@ -0,0 +1,26 @@
package jdocs.camel;
//#CustomRoute
import akka.actor.UntypedAbstractActor;
import akka.camel.CamelMessage;
import akka.dispatch.Mapper;
public class Responder extends UntypedAbstractActor{
public void onReceive(Object message) {
if (message instanceof CamelMessage) {
CamelMessage camelMessage = (CamelMessage) message;
getSender().tell(createResponse(camelMessage), getSelf());
} else
unhandled(message);
}
private CamelMessage createResponse(CamelMessage msg) {
return msg.mapBody(new Mapper<String,String>() {
@Override
public String apply(String body) {
return String.format("received %s", body);
}
});
}
}
//#CustomRoute

View file

@ -0,0 +1,13 @@
package jdocs.camel;
//#RouteResponse
import akka.actor.UntypedAbstractActor;
import akka.camel.CamelMessage;
public class ResponseReceiver extends UntypedAbstractActor{
public void onReceive(Object message) {
if(message instanceof CamelMessage) {
// do something with the forwarded response
}
}
}
//#RouteResponse

View file

@ -0,0 +1,37 @@
package jdocs.camel;
//#TransformOutgoingMessage
import akka.camel.CamelMessage;
import akka.camel.javaapi.UntypedProducerActor;
import akka.dispatch.Mapper;
public class Transformer extends UntypedProducerActor{
private String uri;
public Transformer(String uri) {
this.uri = uri;
}
public String getEndpointUri() {
return uri;
}
private CamelMessage upperCase(CamelMessage msg) {
return msg.mapBody(new Mapper<String,String>() {
@Override
public String apply(String body) {
return body.toUpperCase();
}
});
}
@Override
public Object onTransformOutgoingMessage(Object message) {
if(message instanceof CamelMessage) {
CamelMessage camelMessage = (CamelMessage) message;
return upperCase(camelMessage);
} else {
return message;
}
}
}
//#TransformOutgoingMessage

View file

@ -0,0 +1,59 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.circuitbreaker;
//#imports1
import akka.actor.AbstractActor;
import akka.event.LoggingAdapter;
import scala.concurrent.duration.Duration;
import akka.pattern.CircuitBreaker;
import akka.event.Logging;
import static akka.pattern.PatternsCS.pipe;
import java.util.concurrent.CompletableFuture;
//#imports1
//#circuit-breaker-initialization
public class DangerousJavaActor extends AbstractActor {
private final CircuitBreaker breaker;
private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
public DangerousJavaActor() {
this.breaker = new CircuitBreaker(
getContext().dispatcher(), getContext().system().scheduler(),
5, Duration.create(10, "s"), Duration.create(1, "m"))
.onOpen(this::notifyMeOnOpen);
}
public void notifyMeOnOpen() {
log.warning("My CircuitBreaker is now open, and will not close for one minute");
}
//#circuit-breaker-initialization
//#circuit-breaker-usage
public String dangerousCall() {
return "This really isn't that dangerous of a call after all";
}
@Override
public Receive createReceive() {
return receiveBuilder().
match(String.class, "is my middle name"::equals, m -> pipe(
breaker.callWithCircuitBreakerCS(() ->
CompletableFuture.supplyAsync(this::dangerousCall)
), getContext().dispatcher()
).to(sender()))
.match(String.class, "block for me"::equals, m -> {
sender().tell(breaker
.callWithSyncCircuitBreaker(this::dangerousCall), self());
})
.build();
}
//#circuit-breaker-usage
}

View file

@ -0,0 +1,33 @@
package jdocs.circuitbreaker;
import akka.actor.AbstractActor;
import akka.pattern.CircuitBreaker;
import scala.concurrent.duration.Duration;
import java.util.Optional;
import java.util.function.BiFunction;
public class EvenNoFailureJavaExample extends AbstractActor {
//#even-no-as-failure
private final CircuitBreaker breaker;
public EvenNoFailureJavaExample() {
this.breaker = new CircuitBreaker(
getContext().dispatcher(), getContext().system().scheduler(),
5, Duration.create(10, "s"), Duration.create(1, "m"));
}
public int luckyNumber() {
BiFunction<Optional<Integer>, Optional<Throwable>, Boolean> evenNoAsFailure =
(result, err) -> (result.isPresent() && result.get() % 2 == 0);
// this will return 8888 and increase failure count at the same time
return this.breaker.callWithSyncCircuitBreaker(() -> 8888, evenNoAsFailure);
}
//#even-no-as-failure
@Override
public Receive createReceive() {
return null;
}
}

View file

@ -0,0 +1,57 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.circuitbreaker;
import akka.actor.ActorRef;
import akka.actor.ReceiveTimeout;
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 AbstractActor {
private final ActorRef target;
private final CircuitBreaker breaker;
private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
public TellPatternJavaActor(ActorRef targetActor) {
this.target = targetActor;
this.breaker = new CircuitBreaker(
getContext().dispatcher(), getContext().system().scheduler(),
5, Duration.create(10, "s"), Duration.create(1, "m"))
.onOpen(new Runnable() {
public void run() {
notifyMeOnOpen();
}
});
}
public void notifyMeOnOpen() {
log.warning("My CircuitBreaker is now open, and will not close for one minute");
}
//#circuit-breaker-tell-pattern
@Override
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
}

View file

@ -0,0 +1,42 @@
/**
* Copyright (C) 2015-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.cluster;
import akka.testkit.javadsl.TestKit;
import com.typesafe.config.ConfigFactory;
import jdocs.AbstractJavaTest;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import akka.actor.ActorSystem;
import akka.cluster.Cluster;
public class ClusterDocTest extends AbstractJavaTest {
static ActorSystem system;
@BeforeClass
public static void setup() {
system = ActorSystem.create("ClusterDocTest",
ConfigFactory.parseString(scala.docs.cluster.ClusterDocSpec.config()));
}
@AfterClass
public static void tearDown() {
TestKit.shutdownActorSystem(system);
system = null;
}
@Test
public void demonstrateLeave() {
//#leave
final Cluster cluster = Cluster.get(system);
cluster.leave(cluster.selfAddress());
//#leave
}
}

View file

@ -0,0 +1,36 @@
package jdocs.cluster;
import java.math.BigInteger;
import java.util.concurrent.CompletableFuture;
import akka.actor.AbstractActor;
import static akka.pattern.PatternsCS.pipe;
//#backend
public class FactorialBackend extends AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Integer.class, n -> {
CompletableFuture<FactorialResult> result =
CompletableFuture.supplyAsync(() -> factorial(n))
.thenApply((factorial) -> new FactorialResult(n, factorial));
pipe(result, getContext().dispatcher()).to(getSender());
})
.build();
}
BigInteger factorial(int n) {
BigInteger acc = BigInteger.ONE;
for (int i = 1; i <= n; ++i) {
acc = acc.multiply(BigInteger.valueOf(i));
}
return acc;
}
}
//#backend

View file

@ -0,0 +1,103 @@
package jdocs.cluster;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import akka.actor.Props;
import akka.cluster.metrics.AdaptiveLoadBalancingGroup;
import akka.cluster.metrics.AdaptiveLoadBalancingPool;
import akka.cluster.metrics.HeapMetricsSelector;
import akka.cluster.metrics.SystemLoadAverageMetricsSelector;
import akka.cluster.routing.ClusterRouterGroup;
import akka.cluster.routing.ClusterRouterGroupSettings;
import akka.cluster.routing.ClusterRouterPool;
import akka.cluster.routing.ClusterRouterPoolSettings;
import scala.concurrent.duration.Duration;
import akka.actor.ActorRef;
import akka.actor.ReceiveTimeout;
import akka.actor.AbstractActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.routing.FromConfig;
//#frontend
public class FactorialFrontend extends AbstractActor {
final int upToN;
final boolean repeat;
LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
ActorRef backend = getContext().actorOf(FromConfig.getInstance().props(),
"factorialBackendRouter");
public FactorialFrontend(int upToN, boolean repeat) {
this.upToN = upToN;
this.repeat = repeat;
}
@Override
public void preStart() {
sendJobs();
getContext().setReceiveTimeout(Duration.create(10, TimeUnit.SECONDS));
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(FactorialResult.class, result -> {
if (result.n == upToN) {
log.debug("{}! = {}", result.n, result.factorial);
if (repeat)
sendJobs();
else
getContext().stop(getSelf());
}
})
.match(ReceiveTimeout.class, x -> {
log.info("Timeout");
sendJobs();
})
.build();
}
void sendJobs() {
log.info("Starting batch of factorials up to [{}]", upToN);
for (int n = 1; n <= upToN; n++) {
backend.tell(n, getSelf());
}
}
}
//#frontend
//not used, only for documentation
abstract class FactorialFrontend2 extends AbstractActor {
//#router-lookup-in-code
int totalInstances = 100;
Iterable<String> routeesPaths = Arrays.asList("/user/factorialBackend", "");
boolean allowLocalRoutees = true;
String useRole = "backend";
ActorRef backend = getContext().actorOf(
new ClusterRouterGroup(new AdaptiveLoadBalancingGroup(
HeapMetricsSelector.getInstance(), Collections.<String> emptyList()),
new ClusterRouterGroupSettings(totalInstances, routeesPaths,
allowLocalRoutees, useRole)).props(), "factorialBackendRouter2");
//#router-lookup-in-code
}
//not used, only for documentation
abstract class FactorialFrontend3 extends AbstractActor {
//#router-deploy-in-code
int totalInstances = 100;
int maxInstancesPerNode = 3;
boolean allowLocalRoutees = false;
String useRole = "backend";
ActorRef backend = getContext().actorOf(
new ClusterRouterPool(new AdaptiveLoadBalancingPool(
SystemLoadAverageMetricsSelector.getInstance(), 0),
new ClusterRouterPoolSettings(totalInstances, maxInstancesPerNode,
allowLocalRoutees, useRole)).props(Props
.create(FactorialBackend.class)), "factorialBackendRouter3");
//#router-deploy-in-code
}

View file

@ -0,0 +1,71 @@
package jdocs.cluster;
import java.util.concurrent.TimeUnit;
import scala.concurrent.Await;
import scala.concurrent.duration.Duration;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.cluster.Cluster;
public class FactorialFrontendMain {
public static void main(String[] args) {
final int upToN = 200;
final Config config = ConfigFactory.parseString(
"akka.cluster.roles = [frontend]").withFallback(
ConfigFactory.load("factorial"));
final ActorSystem system = ActorSystem.create("ClusterSystem", config);
system.log().info(
"Factorials will start when 2 backend members in the cluster.");
//#registerOnUp
Cluster.get(system).registerOnMemberUp(new Runnable() {
@Override
public void run() {
system.actorOf(Props.create(FactorialFrontend.class, upToN, true),
"factorialFrontend");
}
});
//#registerOnUp
//#registerOnRemoved
Cluster.get(system).registerOnMemberRemoved(new Runnable() {
@Override
public void run() {
// exit JVM when ActorSystem has been terminated
final Runnable exit = new Runnable() {
@Override public void run() {
System.exit(0);
}
};
system.registerOnTermination(exit);
// shut down ActorSystem
system.terminate();
// In case ActorSystem shutdown takes longer than 10 seconds,
// exit the JVM forcefully anyway.
// We must spawn a separate thread to not block current thread,
// since that would have blocked the shutdown of the ActorSystem.
new Thread() {
@Override public void run(){
try {
Await.ready(system.whenTerminated(), Duration.create(10, TimeUnit.SECONDS));
} catch (Exception e) {
System.exit(-1);
}
}
}.start();
}
});
//#registerOnRemoved
}
}

View file

@ -0,0 +1,15 @@
package jdocs.cluster;
import java.math.BigInteger;
import java.io.Serializable;
public class FactorialResult implements Serializable {
private static final long serialVersionUID = 1L;
public final int n;
public final BigInteger factorial;
FactorialResult(int n, BigInteger factorial) {
this.n = n;
this.factorial = factorial;
}
}

View file

@ -0,0 +1,69 @@
package jdocs.cluster;
//#metrics-listener
import akka.actor.AbstractActor;
import akka.cluster.Cluster;
import akka.cluster.ClusterEvent.CurrentClusterState;
import akka.cluster.metrics.ClusterMetricsChanged;
import akka.cluster.metrics.NodeMetrics;
import akka.cluster.metrics.StandardMetrics;
import akka.cluster.metrics.StandardMetrics.HeapMemory;
import akka.cluster.metrics.StandardMetrics.Cpu;
import akka.cluster.metrics.ClusterMetricsExtension;
import akka.event.Logging;
import akka.event.LoggingAdapter;
public class MetricsListener extends AbstractActor {
LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
Cluster cluster = Cluster.get(getContext().getSystem());
ClusterMetricsExtension extension = ClusterMetricsExtension.get(getContext().getSystem());
// Subscribe unto ClusterMetricsEvent events.
@Override
public void preStart() {
extension.subscribe(getSelf());
}
// Unsubscribe from ClusterMetricsEvent events.
@Override
public void postStop() {
extension.unsubscribe(getSelf());
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(ClusterMetricsChanged.class, clusterMetrics -> {
for (NodeMetrics nodeMetrics : clusterMetrics.getNodeMetrics()) {
if (nodeMetrics.address().equals(cluster.selfAddress())) {
logHeap(nodeMetrics);
logCpu(nodeMetrics);
}
}
})
.match(CurrentClusterState.class, message -> {
// Ignore.
})
.build();
}
void logHeap(NodeMetrics nodeMetrics) {
HeapMemory heap = StandardMetrics.extractHeapMemory(nodeMetrics);
if (heap != null) {
log.info("Used heap: {} MB", ((double) heap.used()) / 1024 / 1024);
}
}
void logCpu(NodeMetrics nodeMetrics) {
Cpu cpu = StandardMetrics.extractCpu(nodeMetrics);
if (cpu != null && cpu.systemLoadAverage().isDefined()) {
log.info("Load: {} ({} processors)", cpu.systemLoadAverage().get(),
cpu.processors());
}
}
}
//#metrics-listener

View file

@ -0,0 +1,49 @@
package jdocs.cluster;
import akka.actor.AbstractActor;
import akka.cluster.Cluster;
import akka.cluster.ClusterEvent;
import akka.cluster.ClusterEvent.MemberEvent;
import akka.cluster.ClusterEvent.MemberUp;
import akka.cluster.ClusterEvent.MemberRemoved;
import akka.cluster.ClusterEvent.UnreachableMember;
import akka.event.Logging;
import akka.event.LoggingAdapter;
public class SimpleClusterListener extends AbstractActor {
LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
Cluster cluster = Cluster.get(getContext().getSystem());
//subscribe to cluster changes
@Override
public void preStart() {
//#subscribe
cluster.subscribe(getSelf(), ClusterEvent.initialStateAsEvents(),
MemberEvent.class, UnreachableMember.class);
//#subscribe
}
//re-subscribe when restart
@Override
public void postStop() {
cluster.unsubscribe(getSelf());
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(MemberUp.class, mUp -> {
log.info("Member is Up: {}", mUp.member());
})
.match(UnreachableMember.class, mUnreachable -> {
log.info("Member detected as unreachable: {}", mUnreachable.member());
})
.match(MemberRemoved.class, mRemoved -> {
log.info("Member is Removed: {}", mRemoved.member());
})
.match(MemberEvent.class, message -> {
// ignore
})
.build();
}
}

View file

@ -0,0 +1,51 @@
package jdocs.cluster;
import akka.actor.AbstractActor;
import akka.cluster.Cluster;
import akka.cluster.ClusterEvent.CurrentClusterState;
import akka.cluster.ClusterEvent.MemberEvent;
import akka.cluster.ClusterEvent.MemberUp;
import akka.cluster.ClusterEvent.MemberRemoved;
import akka.cluster.ClusterEvent.UnreachableMember;
import akka.event.Logging;
import akka.event.LoggingAdapter;
public class SimpleClusterListener2 extends AbstractActor {
LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
Cluster cluster = Cluster.get(getContext().getSystem());
//subscribe to cluster changes
@Override
public void preStart() {
//#subscribe
cluster.subscribe(getSelf(), MemberEvent.class, UnreachableMember.class);
//#subscribe
}
//re-subscribe when restart
@Override
public void postStop() {
cluster.unsubscribe(getSelf());
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(CurrentClusterState.class, state -> {
log.info("Current members: {}", state.members());
})
.match(MemberUp.class, mUp -> {
log.info("Member is Up: {}", mUp.member());
})
.match(UnreachableMember.class, mUnreachable -> {
log.info("Member detected as unreachable: {}", mUnreachable.member());
})
.match(MemberRemoved.class, mRemoved -> {
log.info("Member is Removed: {}", mRemoved.member());
})
.match(MemberEvent.class, event -> {
// ignore
})
.build();
}
}

View file

@ -0,0 +1,55 @@
package jdocs.cluster;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import jdocs.cluster.StatsMessages.JobFailed;
import jdocs.cluster.StatsMessages.StatsResult;
import scala.concurrent.duration.Duration;
import akka.actor.ActorRef;
import akka.actor.ReceiveTimeout;
import akka.actor.AbstractActor;
//#aggregator
public class StatsAggregator extends AbstractActor {
final int expectedResults;
final ActorRef replyTo;
final List<Integer> results = new ArrayList<Integer>();
public StatsAggregator(int expectedResults, ActorRef replyTo) {
this.expectedResults = expectedResults;
this.replyTo = replyTo;
}
@Override
public void preStart() {
getContext().setReceiveTimeout(Duration.create(3, TimeUnit.SECONDS));
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Integer.class, wordCount -> {
results.add(wordCount);
if (results.size() == expectedResults) {
int sum = 0;
for (int c : results) {
sum += c;
}
double meanWordLength = ((double) sum) / results.size();
replyTo.tell(new StatsResult(meanWordLength), getSelf());
getContext().stop(getSelf());
}
})
.match(ReceiveTimeout.class, x -> {
replyTo.tell(new JobFailed("Service unavailable, try again later"),
getSelf());
getContext().stop(getSelf());
})
.build();
}
}
//#aggregator

View file

@ -0,0 +1,55 @@
package jdocs.cluster;
import java.io.Serializable;
//#messages
public interface StatsMessages {
public static class StatsJob implements Serializable {
private final String text;
public StatsJob(String text) {
this.text = text;
}
public String getText() {
return text;
}
}
public static class StatsResult implements Serializable {
private final double meanWordLength;
public StatsResult(double meanWordLength) {
this.meanWordLength = meanWordLength;
}
public double getMeanWordLength() {
return meanWordLength;
}
@Override
public String toString() {
return "meanWordLength: " + meanWordLength;
}
}
public static class JobFailed implements Serializable {
private final String reason;
public JobFailed(String reason) {
this.reason = reason;
}
public String getReason() {
return reason;
}
@Override
public String toString() {
return "JobFailed(" + reason + ")";
}
}
}
//#messages

View file

@ -0,0 +1,100 @@
package jdocs.cluster;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import jdocs.cluster.StatsMessages.JobFailed;
import jdocs.cluster.StatsMessages.StatsJob;
import jdocs.cluster.StatsMessages.StatsResult;
import java.util.concurrent.ThreadLocalRandom;
import scala.concurrent.duration.Duration;
import scala.concurrent.duration.FiniteDuration;
import akka.actor.ActorSelection;
import akka.actor.Address;
import akka.actor.Cancellable;
import akka.actor.AbstractActor;
import akka.cluster.Cluster;
import akka.cluster.ClusterEvent.UnreachableMember;
import akka.cluster.ClusterEvent.ReachableMember;
import akka.cluster.ClusterEvent.CurrentClusterState;
import akka.cluster.ClusterEvent.MemberEvent;
import akka.cluster.ClusterEvent.MemberUp;
import akka.cluster.ClusterEvent.ReachabilityEvent;
import akka.cluster.Member;
import akka.cluster.MemberStatus;
public class StatsSampleClient extends AbstractActor {
final String servicePath;
final Cancellable tickTask;
final Set<Address> nodes = new HashSet<Address>();
Cluster cluster = Cluster.get(getContext().getSystem());
public StatsSampleClient(String servicePath) {
this.servicePath = servicePath;
FiniteDuration interval = Duration.create(2, TimeUnit.SECONDS);
tickTask = getContext()
.getSystem()
.scheduler()
.schedule(interval, interval, getSelf(), "tick",
getContext().dispatcher(), null);
}
//subscribe to cluster changes, MemberEvent
@Override
public void preStart() {
cluster.subscribe(getSelf(), MemberEvent.class, ReachabilityEvent.class);
}
//re-subscribe when restart
@Override
public void postStop() {
cluster.unsubscribe(getSelf());
tickTask.cancel();
}
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("tick", x -> !nodes.isEmpty(), x -> {
// just pick any one
List<Address> nodesList = new ArrayList<Address>(nodes);
Address address = nodesList.get(ThreadLocalRandom.current().nextInt(
nodesList.size()));
ActorSelection service = getContext().actorSelection(address + servicePath);
service.tell(new StatsJob("this is the text that will be analyzed"),
getSelf());
})
.match(StatsResult.class, System.out::println)
.match(JobFailed.class, System.out::println)
.match(CurrentClusterState.class, state -> {
nodes.clear();
for (Member member : state.getMembers()) {
if (member.hasRole("compute") && member.status().equals(MemberStatus.up())) {
nodes.add(member.address());
}
}
})
.match(MemberUp.class, mUp -> {
if (mUp.member().hasRole("compute"))
nodes.add(mUp.member().address());
})
.match(MemberEvent.class, event -> {
nodes.remove(event.member().address());
})
.match(UnreachableMember.class, unreachable -> {
nodes.remove(unreachable.member().address());
})
.match(ReachableMember.class, reachable -> {
if (reachable.member().hasRole("compute"))
nodes.add(reachable.member().address());
})
.build();
}
}

View file

@ -0,0 +1,19 @@
package jdocs.cluster;
import com.typesafe.config.ConfigFactory;
import akka.actor.ActorSystem;
import akka.actor.Props;
public class StatsSampleOneMasterClientMain {
public static void main(String[] args) {
// note that client is not a compute node, role not defined
ActorSystem system = ActorSystem.create("ClusterSystem",
ConfigFactory.load("stats2"));
system.actorOf(Props.create(StatsSampleClient.class, "/user/statsServiceProxy"),
"client");
}
}

View file

@ -0,0 +1,53 @@
package jdocs.cluster;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import akka.actor.ActorSystem;
import akka.actor.PoisonPill;
import akka.actor.Props;
import akka.cluster.singleton.ClusterSingletonManager;
import akka.cluster.singleton.ClusterSingletonManagerSettings;
import akka.cluster.singleton.ClusterSingletonProxy;
import akka.cluster.singleton.ClusterSingletonProxySettings;
public class StatsSampleOneMasterMain {
public static void main(String[] args) {
if (args.length == 0) {
startup(new String[] { "2551", "2552", "0" });
StatsSampleOneMasterClientMain.main(new String[0]);
} else {
startup(args);
}
}
public static void startup(String[] ports) {
for (String port : ports) {
// Override the configuration of the port
Config config = ConfigFactory
.parseString("akka.remote.netty.tcp.port=" + port)
.withFallback(
ConfigFactory.parseString("akka.cluster.roles = [compute]"))
.withFallback(ConfigFactory.load("stats2"));
ActorSystem system = ActorSystem.create("ClusterSystem", config);
//#create-singleton-manager
ClusterSingletonManagerSettings settings = ClusterSingletonManagerSettings.create(system)
.withRole("compute");
system.actorOf(ClusterSingletonManager.props(
Props.create(StatsService.class), PoisonPill.getInstance(), settings),
"statsService");
//#create-singleton-manager
//#singleton-proxy
ClusterSingletonProxySettings proxySettings =
ClusterSingletonProxySettings.create(system).withRole("compute");
system.actorOf(ClusterSingletonProxy.props("/user/statsService",
proxySettings), "statsServiceProxy");
//#singleton-proxy
}
}
}

View file

@ -0,0 +1,80 @@
package jdocs.cluster;
import akka.cluster.routing.ClusterRouterGroup;
import akka.cluster.routing.ClusterRouterGroupSettings;
import akka.cluster.routing.ClusterRouterPool;
import akka.cluster.routing.ClusterRouterPoolSettings;
import akka.routing.ConsistentHashingGroup;
import akka.routing.ConsistentHashingPool;
import jdocs.cluster.StatsMessages.StatsJob;
import akka.actor.ActorRef;
import akka.actor.Props;
import akka.actor.AbstractActor;
import akka.routing.ConsistentHashingRouter.ConsistentHashableEnvelope;
import akka.routing.FromConfig;
import java.util.Collections;
//#service
public class StatsService extends AbstractActor {
// This router is used both with lookup and deploy of routees. If you
// have a router with only lookup of routees you can use Props.empty()
// instead of Props.create(StatsWorker.class).
ActorRef workerRouter = getContext().actorOf(
FromConfig.getInstance().props(Props.create(StatsWorker.class)),
"workerRouter");
@Override
public Receive createReceive() {
return receiveBuilder()
.match(StatsJob.class, job -> !job.getText().isEmpty(), job -> {
String[] words = job.getText().split(" ");
ActorRef replyTo = getSender();
// create actor that collects replies from workers
ActorRef aggregator = getContext().actorOf(
Props.create(StatsAggregator.class, words.length, replyTo));
// send each word to a worker
for (String word : words) {
workerRouter.tell(new ConsistentHashableEnvelope(word, word),
aggregator);
}
})
.build();
}
}
//#service
//not used, only for documentation
abstract class StatsService2 extends AbstractActor {
//#router-lookup-in-code
int totalInstances = 100;
Iterable<String> routeesPaths = Collections
.singletonList("/user/statsWorker");
boolean allowLocalRoutees = true;
String useRole = "compute";
ActorRef workerRouter = getContext().actorOf(
new ClusterRouterGroup(new ConsistentHashingGroup(routeesPaths),
new ClusterRouterGroupSettings(totalInstances, routeesPaths,
allowLocalRoutees, useRole)).props(), "workerRouter2");
//#router-lookup-in-code
}
//not used, only for documentation
abstract class StatsService3 extends AbstractActor {
//#router-deploy-in-code
int totalInstances = 100;
int maxInstancesPerNode = 3;
boolean allowLocalRoutees = false;
String useRole = "compute";
ActorRef workerRouter = getContext().actorOf(
new ClusterRouterPool(new ConsistentHashingPool(0),
new ClusterRouterPoolSettings(totalInstances, maxInstancesPerNode,
allowLocalRoutees, useRole)).props(Props
.create(StatsWorker.class)), "workerRouter3");
//#router-deploy-in-code
}

View file

@ -0,0 +1,27 @@
package jdocs.cluster;
import java.util.HashMap;
import java.util.Map;
import akka.actor.AbstractActor;
//#worker
public class StatsWorker extends AbstractActor {
Map<String, Integer> cache = new HashMap<String, Integer>();
@Override
public Receive createReceive() {
return receiveBuilder()
.match(String.class, word -> {
Integer length = cache.get(word);
if (length == null) {
length = word.length();
cache.put(word, length);
}
getSender().tell(length, getSelf());
})
.build();
}
}
//#worker

View file

@ -0,0 +1,56 @@
package jdocs.cluster;
import static jdocs.cluster.TransformationMessages.BACKEND_REGISTRATION;
import jdocs.cluster.TransformationMessages.TransformationJob;
import jdocs.cluster.TransformationMessages.TransformationResult;
import akka.actor.AbstractActor;
import akka.cluster.Cluster;
import akka.cluster.ClusterEvent.CurrentClusterState;
import akka.cluster.ClusterEvent.MemberUp;
import akka.cluster.Member;
import akka.cluster.MemberStatus;
//#backend
public class TransformationBackend extends AbstractActor {
Cluster cluster = Cluster.get(getContext().getSystem());
//subscribe to cluster changes, MemberUp
@Override
public void preStart() {
cluster.subscribe(getSelf(), MemberUp.class);
}
//re-subscribe when restart
@Override
public void postStop() {
cluster.unsubscribe(getSelf());
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(TransformationJob.class, job -> {
getSender().tell(new TransformationResult(job.getText().toUpperCase()),
getSelf());
})
.match(CurrentClusterState.class, state -> {
for (Member member : state.getMembers()) {
if (member.status().equals(MemberStatus.up())) {
register(member);
}
}
})
.match(MemberUp.class, mUp -> {
register(mUp.member());
})
.build();
}
void register(Member member) {
if (member.hasRole("frontend"))
getContext().actorSelection(member.address() + "/user/frontend").tell(
BACKEND_REGISTRATION, getSelf());
}
}
//#backend

View file

@ -0,0 +1,44 @@
package jdocs.cluster;
import static jdocs.cluster.TransformationMessages.BACKEND_REGISTRATION;
import java.util.ArrayList;
import java.util.List;
import jdocs.cluster.TransformationMessages.JobFailed;
import jdocs.cluster.TransformationMessages.TransformationJob;
import akka.actor.ActorRef;
import akka.actor.Terminated;
import akka.actor.AbstractActor;
//#frontend
public class TransformationFrontend extends AbstractActor {
List<ActorRef> backends = new ArrayList<ActorRef>();
int jobCounter = 0;
@Override
public Receive createReceive() {
return receiveBuilder()
.match(TransformationJob.class, job -> backends.isEmpty(), job -> {
getSender().tell(
new JobFailed("Service unavailable, try again later", job),
getSender());
})
.match(TransformationJob.class, job -> {
jobCounter++;
backends.get(jobCounter % backends.size())
.forward(job, getContext());
})
.matchEquals(BACKEND_REGISTRATION, x -> {
getContext().watch(getSender());
backends.add(getSender());
})
.match(Terminated.class, terminated -> {
backends.remove(terminated.getActor());
})
.build();
}
}
//#frontend

View file

@ -0,0 +1,63 @@
package jdocs.cluster;
import java.io.Serializable;
//#messages
public interface TransformationMessages {
public static class TransformationJob implements Serializable {
private final String text;
public TransformationJob(String text) {
this.text = text;
}
public String getText() {
return text;
}
}
public static class TransformationResult implements Serializable {
private final String text;
public TransformationResult(String text) {
this.text = text;
}
public String getText() {
return text;
}
@Override
public String toString() {
return "TransformationResult(" + text + ")";
}
}
public static class JobFailed implements Serializable {
private final String reason;
private final TransformationJob job;
public JobFailed(String reason, TransformationJob job) {
this.reason = reason;
this.job = job;
}
public String getReason() {
return reason;
}
public TransformationJob getJob() {
return job;
}
@Override
public String toString() {
return "JobFailed(" + reason + ")";
}
}
public static final String BACKEND_REGISTRATION = "BackendRegistration";
}
//#messages

View file

@ -0,0 +1,33 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.config;
import akka.actor.ActorSystem;
import com.typesafe.config.*;
public class ConfigDoc {
public ActorSystem createConfiguredSystem() {
//#java-custom-config
// make a Config with just your special setting
Config myConfig =
ConfigFactory.parseString("something=somethingElse");
// load the normal config stack (system props,
// then application.conf, then reference.conf)
Config regularConfig =
ConfigFactory.load();
// override regular stack with myConfig
Config combined =
myConfig.withFallback(regularConfig);
// put the result in between the overrides
// (system props) and defaults again
Config complete =
ConfigFactory.load(combined);
// create ActorSystem
ActorSystem system =
ActorSystem.create("myname", complete);
//#java-custom-config
return system;
}
}

View file

@ -0,0 +1,101 @@
/**
* Copyright (C) 2015-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.ddata;
//#data-bot
import static java.util.concurrent.TimeUnit.SECONDS;
import scala.concurrent.duration.Duration;
import java.util.concurrent.ThreadLocalRandom;
import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.Cancellable;
import akka.cluster.Cluster;
import akka.cluster.ddata.DistributedData;
import akka.cluster.ddata.Key;
import akka.cluster.ddata.ORSet;
import akka.cluster.ddata.ORSetKey;
import akka.cluster.ddata.Replicator;
import akka.cluster.ddata.Replicator.Changed;
import akka.cluster.ddata.Replicator.Subscribe;
import akka.cluster.ddata.Replicator.Update;
import akka.cluster.ddata.Replicator.UpdateResponse;
import akka.event.Logging;
import akka.event.LoggingAdapter;
public class DataBot extends AbstractActor {
private static final String TICK = "tick";
private final LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
private final ActorRef replicator =
DistributedData.get(getContext().getSystem()).replicator();
private final Cluster node = Cluster.get(getContext().getSystem());
private final Cancellable tickTask = getContext().getSystem().scheduler().schedule(
Duration.create(5, SECONDS), Duration.create(5, SECONDS), getSelf(), TICK,
getContext().dispatcher(), getSelf());
private final Key<ORSet<String>> dataKey = ORSetKey.create("key");
@SuppressWarnings("unchecked")
@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<ORSet<String>>) c))
.match(UpdateResponse.class, r -> receiveUpdateResoponse())
.build();
}
private void receiveTick() {
String s = String.valueOf((char) ThreadLocalRandom.current().nextInt(97, 123));
if (ThreadLocalRandom.current().nextBoolean()) {
// add
log.info("Adding: {}", s);
Update<ORSet<String>> update = new Update<>(
dataKey,
ORSet.create(),
Replicator.writeLocal(),
curr -> curr.add(node, s));
replicator.tell(update, getSelf());
} else {
// remove
log.info("Removing: {}", s);
Update<ORSet<String>> update = new Update<>(
dataKey,
ORSet.create(),
Replicator.writeLocal(),
curr -> curr.remove(node, s));
replicator.tell(update, getSelf());
}
}
private void receiveChanged(Changed<ORSet<String>> c) {
ORSet<String> data = c.dataValue();
log.info("Current elements: {}", data.getElements());
}
private void receiveUpdateResoponse() {
// ignore
}
@Override
public void preStart() {
Subscribe<ORSet<String>> subscribe = new Subscribe<>(dataKey, getSelf());
replicator.tell(subscribe, ActorRef.noSender());
}
@Override
public void postStop(){
tickTask.cancel();
}
}
//#data-bot

View file

@ -0,0 +1,422 @@
/**
* Copyright (C) 2015-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.ddata;
import java.util.HashSet;
import java.util.Arrays;
import java.util.Set;
import java.math.BigInteger;
import java.util.Optional;
import akka.actor.*;
import akka.testkit.javadsl.TestKit;
import com.typesafe.config.ConfigFactory;
import docs.ddata.DistributedDataDocSpec;
import jdocs.AbstractJavaTest;
import scala.concurrent.duration.Duration;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import akka.cluster.Cluster;
import akka.cluster.ddata.*;
import akka.japi.pf.ReceiveBuilder;
import static akka.cluster.ddata.Replicator.*;
@SuppressWarnings({"unchecked", "unused"})
public class DistributedDataDocTest extends AbstractJavaTest {
static ActorSystem system;
@BeforeClass
public static void setup() {
system = ActorSystem.create("DistributedDataDocTest",
ConfigFactory.parseString(DistributedDataDocSpec.config()));
}
@AfterClass
public static void tearDown() {
TestKit.shutdownActorSystem(system);
system = null;
}
static
//#update
class DemonstrateUpdate extends AbstractActor {
final Cluster node = Cluster.get(getContext().getSystem());
final ActorRef replicator =
DistributedData.get(getContext().getSystem()).replicator();
final Key<PNCounter> counter1Key = PNCounterKey.create("counter1");
final Key<GSet<String>> set1Key = GSetKey.create("set1");
final Key<ORSet<String>> set2Key = ORSetKey.create("set2");
final Key<Flag> activeFlagKey = FlagKey.create("active");
@Override
public Receive createReceive() {
ReceiveBuilder b = receiveBuilder();
b.matchEquals("demonstrate update", msg -> {
replicator.tell(new Replicator.Update<PNCounter>(counter1Key, PNCounter.create(),
Replicator.writeLocal(), curr -> curr.increment(node, 1)), getSelf());
final WriteConsistency writeTo3 = new WriteTo(3, Duration.create(1, SECONDS));
replicator.tell(new Replicator.Update<GSet<String>>(set1Key, GSet.create(),
writeTo3, curr -> curr.add("hello")), getSelf());
final WriteConsistency writeMajority =
new WriteMajority(Duration.create(5, SECONDS));
replicator.tell(new Replicator.Update<ORSet<String>>(set2Key, ORSet.create(),
writeMajority, curr -> curr.add(node, "hello")), getSelf());
final WriteConsistency writeAll = new WriteAll(Duration.create(5, SECONDS));
replicator.tell(new Replicator.Update<Flag>(activeFlagKey, Flag.create(),
writeAll, curr -> curr.switchOn()), getSelf());
});
//#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
static
//#update-request-context
class DemonstrateUpdateWithRequestContext extends AbstractActor {
final Cluster node = Cluster.get(getContext().getSystem());
final ActorRef replicator =
DistributedData.get(getContext().getSystem()).replicator();
final WriteConsistency writeTwo = new WriteTo(2, Duration.create(3, SECONDS));
final Key<PNCounter> 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<Object> reqContext = Optional.of(getSender());
Replicator.Update<PNCounter> upd = new Replicator.Update<PNCounter>(counter1Key,
PNCounter.create(), writeTwo, reqContext, curr -> curr.increment(node, 1));
replicator.tell(upd, getSelf());
})
.match(UpdateSuccess.class, a -> a.key().equals(counter1Key), a -> {
ActorRef replyTo = (ActorRef) a.getRequest().get();
replyTo.tell("ack", getSelf());
})
.match(UpdateTimeout.class, a -> a.key().equals(counter1Key), a -> {
ActorRef replyTo = (ActorRef) a.getRequest().get();
replyTo.tell("nack", getSelf());
})
.build();
}
}
//#update-request-context
static
//#get
class DemonstrateGet extends AbstractActor {
final ActorRef replicator =
DistributedData.get(getContext().getSystem()).replicator();
final Key<PNCounter> counter1Key = PNCounterKey.create("counter1");
final Key<GSet<String>> set1Key = GSetKey.create("set1");
final Key<ORSet<String>> set2Key = ORSetKey.create("set2");
final Key<Flag> activeFlagKey = FlagKey.create("active");
@Override
public Receive createReceive() {
ReceiveBuilder b = receiveBuilder();
b.matchEquals("demonstrate get", msg -> {
replicator.tell(new Replicator.Get<PNCounter>(counter1Key,
Replicator.readLocal()), getSelf());
final ReadConsistency readFrom3 = new ReadFrom(3, Duration.create(1, SECONDS));
replicator.tell(new Replicator.Get<GSet<String>>(set1Key,
readFrom3), getSelf());
final ReadConsistency readMajority = new ReadMajority(Duration.create(5, SECONDS));
replicator.tell(new Replicator.Get<ORSet<String>>(set2Key,
readMajority), getSelf());
final ReadConsistency readAll = new ReadAll(Duration.create(5, SECONDS));
replicator.tell(new Replicator.Get<Flag>(activeFlagKey,
readAll), getSelf());
});
//#get
//#get-response1
b.match(GetSuccess.class, a -> a.key().equals(counter1Key), a -> {
GetSuccess<PNCounter> 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
b.match(GetSuccess.class, a -> a.key().equals(set1Key), a -> {
GetSuccess<GSet<String>> g = a;
Set<String> 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
static
//#get-request-context
class DemonstrateGetWithRequestContext extends AbstractActor {
final ActorRef replicator =
DistributedData.get(getContext().getSystem()).replicator();
final ReadConsistency readTwo = new ReadFrom(2, Duration.create(3, SECONDS));
final Key<PNCounter> 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<Object> reqContext = Optional.of(getSender());
replicator.tell(new Replicator.Get<PNCounter>(counter1Key,
readTwo), getSelf());
})
.match(GetSuccess.class, a -> a.key().equals(counter1Key), a -> {
ActorRef replyTo = (ActorRef) a.getRequest().get();
GetSuccess<PNCounter> g = a;
long value = g.dataValue().getValue().longValue();
replyTo.tell(value, getSelf());
})
.match(GetFailure.class, a -> a.key().equals(counter1Key), a -> {
ActorRef replyTo = (ActorRef) a.getRequest().get();
replyTo.tell(-1L, getSelf());
})
.match(NotFound.class, a -> a.key().equals(counter1Key), a -> {
ActorRef replyTo = (ActorRef) a.getRequest().get();
replyTo.tell(0L, getSelf());
})
.build();
}
}
//#get-request-context
static
//#subscribe
class DemonstrateSubscribe extends AbstractActor {
final ActorRef replicator =
DistributedData.get(getContext().getSystem()).replicator();
final Key<PNCounter> counter1Key = PNCounterKey.create("counter1");
BigInteger currentValue = BigInteger.valueOf(0);
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Changed.class, a -> a.key().equals(counter1Key), a -> {
Changed<PNCounter> g = a;
currentValue = g.dataValue().getValue();
})
.match(String.class, a -> a.equals("get-count"), a -> {
// incoming request to retrieve current value of the counter
getSender().tell(currentValue, getSender());
})
.build();
}
@Override
public void preStart() {
// subscribe to changes of the Counter1Key value
replicator.tell(new Subscribe<PNCounter>(counter1Key, getSelf()), ActorRef.noSender());
}
}
//#subscribe
static
//#delete
class DemonstrateDelete extends AbstractActor {
final ActorRef replicator =
DistributedData.get(getContext().getSystem()).replicator();
final Key<PNCounter> counter1Key = PNCounterKey.create("counter1");
final Key<ORSet<String>> set2Key = ORSetKey.create("set2");
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("demonstrate delete", msg -> {
replicator.tell(new Delete<PNCounter>(counter1Key,
Replicator.writeLocal()), getSelf());
final WriteConsistency writeMajority =
new WriteMajority(Duration.create(5, SECONDS));
replicator.tell(new Delete<PNCounter>(counter1Key,
writeMajority), getSelf());
})
.build();
}
}
//#delete
public void demonstratePNCounter() {
//#pncounter
final Cluster node = Cluster.get(system);
final PNCounter c0 = PNCounter.create();
final PNCounter c1 = c0.increment(node, 1);
final PNCounter c2 = c1.increment(node, 7);
final PNCounter c3 = c2.decrement(node, 2);
System.out.println(c3.value()); // 6
//#pncounter
}
public void demonstratePNCounterMap() {
//#pncountermap
final Cluster node = Cluster.get(system);
final PNCounterMap<String> m0 = PNCounterMap.create();
final PNCounterMap<String> m1 = m0.increment(node, "a", 7);
final PNCounterMap<String> m2 = m1.decrement(node, "a", 2);
final PNCounterMap<String> m3 = m2.increment(node, "b", 1);
System.out.println(m3.get("a")); // 5
System.out.println(m3.getEntries());
//#pncountermap
}
public void demonstrateGSet() {
//#gset
final GSet<String> s0 = GSet.create();
final GSet<String> s1 = s0.add("a");
final GSet<String> s2 = s1.add("b").add("c");
if (s2.contains("a"))
System.out.println(s2.getElements()); // a, b, c
//#gset
}
public void demonstrateORSet() {
//#orset
final Cluster node = Cluster.get(system);
final ORSet<String> s0 = ORSet.create();
final ORSet<String> s1 = s0.add(node, "a");
final ORSet<String> s2 = s1.add(node, "b");
final ORSet<String> s3 = s2.remove(node, "a");
System.out.println(s3.getElements()); // b
//#orset
}
public void demonstrateORMultiMap() {
//#ormultimap
final Cluster node = Cluster.get(system);
final ORMultiMap<String, Integer> m0 = ORMultiMap.create();
final ORMultiMap<String, Integer> m1 = m0.put(node, "a",
new HashSet<>(Arrays.asList(1, 2, 3)));
final ORMultiMap<String, Integer> m2 = m1.addBinding(node, "a", 4);
final ORMultiMap<String, Integer> m3 = m2.removeBinding(node, "a", 2);
final ORMultiMap<String, Integer> m4 = m3.addBinding(node, "b", 1);
System.out.println(m4.getEntries());
//#ormultimap
}
public void demonstrateFlag() {
//#flag
final Flag f0 = Flag.create();
final Flag f1 = f0.switchOn();
System.out.println(f1.enabled());
//#flag
}
@Test
public void demonstrateLWWRegister() {
//#lwwregister
final Cluster node = Cluster.get(system);
final LWWRegister<String> r1 = LWWRegister.create(node, "Hello");
final LWWRegister<String> r2 = r1.withValue(node, "Hi");
System.out.println(r1.value() + " by " + r1.updatedBy() + " at " + r1.timestamp());
//#lwwregister
assertEquals("Hi", r2.value());
}
static
//#lwwregister-custom-clock
class Record {
public final int version;
public final String name;
public final String address;
public Record(int version, String name, String address) {
this.version = version;
this.name = name;
this.address = address;
}
}
//#lwwregister-custom-clock
public void demonstrateLWWRegisterWithCustomClock() {
//#lwwregister-custom-clock
final Cluster node = Cluster.get(system);
final LWWRegister.Clock<Record> recordClock = new LWWRegister.Clock<Record>() {
@Override
public long apply(long currentTimestamp, Record value) {
return value.version;
}
};
final Record record1 = new Record(1, "Alice", "Union Square");
final LWWRegister<Record> r1 = LWWRegister.create(node, record1);
final Record record2 = new Record(2, "Alice", "Madison Square");
final LWWRegister<Record> r2 = LWWRegister.create(node, record2);
final LWWRegister<Record> r3 = r1.merge(r2);
System.out.println(r3.value());
//#lwwregister-custom-clock
assertEquals("Madison Square", r3.value().address);
}
}

View file

@ -0,0 +1,273 @@
package jdocs.ddata;
import static java.util.concurrent.TimeUnit.SECONDS;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import scala.concurrent.duration.Duration;
import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.Props;
import akka.cluster.Cluster;
import akka.cluster.ddata.DistributedData;
import akka.cluster.ddata.Key;
import akka.cluster.ddata.LWWMap;
import akka.cluster.ddata.LWWMapKey;
import akka.cluster.ddata.Replicator;
import akka.cluster.ddata.Replicator.GetFailure;
import akka.cluster.ddata.Replicator.GetResponse;
import akka.cluster.ddata.Replicator.GetSuccess;
import akka.cluster.ddata.Replicator.NotFound;
import akka.cluster.ddata.Replicator.ReadConsistency;
import akka.cluster.ddata.Replicator.ReadMajority;
import akka.cluster.ddata.Replicator.Update;
import akka.cluster.ddata.Replicator.UpdateFailure;
import akka.cluster.ddata.Replicator.UpdateSuccess;
import akka.cluster.ddata.Replicator.UpdateTimeout;
import akka.cluster.ddata.Replicator.WriteConsistency;
import akka.cluster.ddata.Replicator.WriteMajority;
@SuppressWarnings("unchecked")
public class ShoppingCart extends AbstractActor {
//#read-write-majority
private final WriteConsistency writeMajority =
new WriteMajority(Duration.create(3, SECONDS));
private final static ReadConsistency readMajority =
new ReadMajority(Duration.create(3, SECONDS));
//#read-write-majority
public static final String GET_CART = "getCart";
public static class AddItem {
public final LineItem item;
public AddItem(LineItem item) {
this.item = item;
}
}
public static class RemoveItem {
public final String productId;
public RemoveItem(String productId) {
this.productId = productId;
}
}
public static class Cart {
public final Set<LineItem> items;
public Cart(Set<LineItem> items) {
this.items = items;
}
}
public static class LineItem implements Serializable {
private static final long serialVersionUID = 1L;
public final String productId;
public final String title;
public final int quantity;
public LineItem(String productId, String title, int quantity) {
this.productId = productId;
this.title = title;
this.quantity = quantity;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((productId == null) ? 0 : productId.hashCode());
result = prime * result + quantity;
result = prime * result + ((title == null) ? 0 : title.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;
LineItem other = (LineItem) obj;
if (productId == null) {
if (other.productId != null)
return false;
} else if (!productId.equals(other.productId))
return false;
if (quantity != other.quantity)
return false;
if (title == null) {
if (other.title != null)
return false;
} else if (!title.equals(other.title))
return false;
return true;
}
@Override
public String toString() {
return "LineItem [productId=" + productId + ", title=" + title + ", quantity=" + quantity + "]";
}
}
public static Props props(String userId) {
return Props.create(ShoppingCart.class, userId);
}
private final ActorRef replicator = DistributedData.get(getContext().getSystem()).replicator();
private final Cluster node = Cluster.get(getContext().getSystem());
@SuppressWarnings("unused")
private final String userId;
private final Key<LWWMap<String, LineItem>> dataKey;
public ShoppingCart(String userId) {
this.userId = userId;
this.dataKey = LWWMapKey.create("cart-" + userId);
}
@Override
public Receive createReceive() {
return matchGetCart()
.orElse(matchAddItem())
.orElse(matchRemoveItem())
.orElse(matchOther());
}
//#get-cart
private Receive matchGetCart() {
return receiveBuilder()
.matchEquals(GET_CART, s -> receiveGetCart())
.match(GetSuccess.class, this::isResponseToGetCart,
g -> receiveGetSuccess((GetSuccess<LWWMap<String, LineItem>>) g))
.match(NotFound.class, this::isResponseToGetCart,
n -> receiveNotFound((NotFound<LWWMap<String, LineItem>>) n))
.match(GetFailure.class, this::isResponseToGetCart,
f -> receiveGetFailure((GetFailure<LWWMap<String, LineItem>>) f))
.build();
}
private void receiveGetCart() {
Optional<Object> ctx = Optional.of(getSender());
replicator.tell(new Replicator.Get<LWWMap<String, LineItem>>(dataKey, readMajority, ctx),
getSelf());
}
private boolean isResponseToGetCart(GetResponse<?> response) {
return response.key().equals(dataKey) &&
(response.getRequest().orElse(null) instanceof ActorRef);
}
private void receiveGetSuccess(GetSuccess<LWWMap<String, LineItem>> g) {
Set<LineItem> items = new HashSet<>(g.dataValue().getEntries().values());
ActorRef replyTo = (ActorRef) g.getRequest().get();
replyTo.tell(new Cart(items), getSelf());
}
private void receiveNotFound(NotFound<LWWMap<String, LineItem>> n) {
ActorRef replyTo = (ActorRef) n.getRequest().get();
replyTo.tell(new Cart(new HashSet<>()), getSelf());
}
private void receiveGetFailure(GetFailure<LWWMap<String, LineItem>> f) {
// ReadMajority failure, try again with local read
Optional<Object> ctx = Optional.of(getSender());
replicator.tell(new Replicator.Get<LWWMap<String, LineItem>>(dataKey, Replicator.readLocal(),
ctx), getSelf());
}
//#get-cart
//#add-item
private Receive matchAddItem() {
return receiveBuilder()
.match(AddItem.class, this::receiveAddItem)
.build();
}
private void receiveAddItem(AddItem add) {
Update<LWWMap<String, LineItem>> update = new Update<>(dataKey, LWWMap.create(), writeMajority,
cart -> updateCart(cart, add.item));
replicator.tell(update, getSelf());
}
//#add-item
private LWWMap<String, LineItem> updateCart(LWWMap<String, LineItem> data, LineItem item) {
if (data.contains(item.productId)) {
LineItem existingItem = data.get(item.productId).get();
int newQuantity = existingItem.quantity + item.quantity;
LineItem newItem = new LineItem(item.productId, item.title, newQuantity);
return data.put(node, item.productId, newItem);
} else {
return data.put(node, item.productId, item);
}
}
private Receive matchRemoveItem() {
return receiveBuilder()
.match(RemoveItem.class, this::receiveRemoveItem)
.match(GetSuccess.class, this::isResponseToRemoveItem,
g -> receiveRemoveItemGetSuccess((GetSuccess<LWWMap<String, LineItem>>) g))
.match(GetFailure.class, this::isResponseToRemoveItem,
f -> receiveRemoveItemGetFailure((GetFailure<LWWMap<String, LineItem>>) f))
.match(NotFound.class, this::isResponseToRemoveItem, n -> {/* nothing to remove */})
.build();
}
//#remove-item
private void receiveRemoveItem(RemoveItem rm) {
// Try to fetch latest from a majority of nodes first, since ORMap
// remove must have seen the item to be able to remove it.
Optional<Object> ctx = Optional.of(rm);
replicator.tell(new Replicator.Get<LWWMap<String, LineItem>>(dataKey, readMajority, ctx),
getSelf());
}
private void receiveRemoveItemGetSuccess(GetSuccess<LWWMap<String, LineItem>> g) {
RemoveItem rm = (RemoveItem) g.getRequest().get();
removeItem(rm.productId);
}
private void receiveRemoveItemGetFailure(GetFailure<LWWMap<String, LineItem>> f) {
// ReadMajority failed, fall back to best effort local value
RemoveItem rm = (RemoveItem) f.getRequest().get();
removeItem(rm.productId);
}
private void removeItem(String productId) {
Update<LWWMap<String, LineItem>> update = new Update<>(dataKey, LWWMap.create(), writeMajority,
cart -> cart.remove(node, productId));
replicator.tell(update, getSelf());
}
private boolean isResponseToRemoveItem(GetResponse<?> response) {
return response.key().equals(dataKey) &&
(response.getRequest().orElse(null) instanceof RemoveItem);
}
//#remove-item
private Receive matchOther() {
return receiveBuilder()
.match(UpdateSuccess.class, u -> {
// ok
})
.match(UpdateTimeout.class, t -> {
// will eventually be replicated
})
.match(UpdateFailure.class, f -> {
throw new IllegalStateException("Unexpected failure: " + f);
})
.build();
}
}

View file

@ -0,0 +1,48 @@
/**
* Copyright (C) 2015-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.ddata;
import java.util.HashSet;
import java.util.Set;
import akka.cluster.ddata.AbstractReplicatedData;
import akka.cluster.ddata.GSet;
//#twophaseset
public class TwoPhaseSet extends AbstractReplicatedData<TwoPhaseSet> {
public final GSet<String> adds;
public final GSet<String> removals;
public TwoPhaseSet(GSet<String> adds, GSet<String> removals) {
this.adds = adds;
this.removals = removals;
}
public static TwoPhaseSet create() {
return new TwoPhaseSet(GSet.create(), GSet.create());
}
public TwoPhaseSet add(String element) {
return new TwoPhaseSet(adds.add(element), removals);
}
public TwoPhaseSet remove(String element) {
return new TwoPhaseSet(adds, removals.add(element));
}
public Set<String> getElements() {
Set<String> result = new HashSet<>(adds.getElements());
result.removeAll(removals.getElements());
return result;
}
@Override
public TwoPhaseSet mergeData(TwoPhaseSet that) {
return new TwoPhaseSet(this.adds.merge(that.adds),
this.removals.merge(that.removals));
}
}
//#twophaseset

View file

@ -0,0 +1,92 @@
/**
* Copyright (C) 2015-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.ddata.protobuf;
//#serializer
import jdocs.ddata.TwoPhaseSet;
import docs.ddata.protobuf.msg.TwoPhaseSetMessages;
import docs.ddata.protobuf.msg.TwoPhaseSetMessages.TwoPhaseSet.Builder;
import java.util.ArrayList;
import java.util.Collections;
import akka.actor.ExtendedActorSystem;
import akka.cluster.ddata.GSet;
import akka.cluster.ddata.protobuf.AbstractSerializationSupport;
public class TwoPhaseSetSerializer extends AbstractSerializationSupport {
private final ExtendedActorSystem system;
public TwoPhaseSetSerializer(ExtendedActorSystem system) {
this.system = system;
}
@Override
public ExtendedActorSystem system() {
return this.system;
}
@Override
public boolean includeManifest() {
return false;
}
@Override
public int identifier() {
return 99998;
}
@Override
public byte[] toBinary(Object obj) {
if (obj instanceof TwoPhaseSet) {
return twoPhaseSetToProto((TwoPhaseSet) obj).toByteArray();
} else {
throw new IllegalArgumentException(
"Can't serialize object of type " + obj.getClass());
}
}
@Override
public Object fromBinaryJava(byte[] bytes, Class<?> manifest) {
return twoPhaseSetFromBinary(bytes);
}
protected TwoPhaseSetMessages.TwoPhaseSet twoPhaseSetToProto(TwoPhaseSet twoPhaseSet) {
Builder b = TwoPhaseSetMessages.TwoPhaseSet.newBuilder();
ArrayList<String> adds = new ArrayList<>(twoPhaseSet.adds.getElements());
if (!adds.isEmpty()) {
Collections.sort(adds);
b.addAllAdds(adds);
}
ArrayList<String> removals = new ArrayList<>(twoPhaseSet.removals.getElements());
if (!removals.isEmpty()) {
Collections.sort(removals);
b.addAllRemovals(removals);
}
return b.build();
}
protected TwoPhaseSet twoPhaseSetFromBinary(byte[] bytes) {
try {
TwoPhaseSetMessages.TwoPhaseSet msg =
TwoPhaseSetMessages.TwoPhaseSet.parseFrom(bytes);
GSet<String> adds = GSet.create();
for (String elem : msg.getAddsList()) {
adds = adds.add(elem);
}
GSet<String> removals = GSet.create();
for (String elem : msg.getRemovalsList()) {
removals = removals.add(elem);
}
// GSet will accumulate deltas when adding elements,
// but those are not of interest in the result of the deserialization
return new TwoPhaseSet(adds.resetDelta(), removals.resetDelta());
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
//#serializer

View file

@ -0,0 +1,87 @@
/**
* Copyright (C) 2015-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.ddata.protobuf;
//#serializer
import jdocs.ddata.TwoPhaseSet;
import docs.ddata.protobuf.msg.TwoPhaseSetMessages;
import docs.ddata.protobuf.msg.TwoPhaseSetMessages.TwoPhaseSet2.Builder;
import akka.actor.ExtendedActorSystem;
import akka.cluster.ddata.GSet;
import akka.cluster.ddata.protobuf.AbstractSerializationSupport;
import akka.cluster.ddata.protobuf.ReplicatedDataSerializer;
public class TwoPhaseSetSerializer2 extends AbstractSerializationSupport {
private final ExtendedActorSystem system;
private final ReplicatedDataSerializer replicatedDataSerializer;
public TwoPhaseSetSerializer2(ExtendedActorSystem system) {
this.system = system;
this.replicatedDataSerializer = new ReplicatedDataSerializer(system);
}
@Override
public ExtendedActorSystem system() {
return this.system;
}
@Override
public boolean includeManifest() {
return false;
}
@Override
public int identifier() {
return 99998;
}
@Override
public byte[] toBinary(Object obj) {
if (obj instanceof TwoPhaseSet) {
return twoPhaseSetToProto((TwoPhaseSet) obj).toByteArray();
} else {
throw new IllegalArgumentException(
"Can't serialize object of type " + obj.getClass());
}
}
@Override
public Object fromBinaryJava(byte[] bytes, Class<?> manifest) {
return twoPhaseSetFromBinary(bytes);
}
protected TwoPhaseSetMessages.TwoPhaseSet2 twoPhaseSetToProto(TwoPhaseSet twoPhaseSet) {
Builder b = TwoPhaseSetMessages.TwoPhaseSet2.newBuilder();
if (!twoPhaseSet.adds.isEmpty())
b.setAdds(otherMessageToProto(twoPhaseSet.adds).toByteString());
if (!twoPhaseSet.removals.isEmpty())
b.setRemovals(otherMessageToProto(twoPhaseSet.removals).toByteString());
return b.build();
}
@SuppressWarnings("unchecked")
protected TwoPhaseSet twoPhaseSetFromBinary(byte[] bytes) {
try {
TwoPhaseSetMessages.TwoPhaseSet2 msg =
TwoPhaseSetMessages.TwoPhaseSet2.parseFrom(bytes);
GSet<String> adds = GSet.create();
if (msg.hasAdds())
adds = (GSet<String>) otherMessageFromBinary(msg.getAdds().toByteArray());
GSet<String> removals = GSet.create();
if (msg.hasRemovals())
adds = (GSet<String>) otherMessageFromBinary(msg.getRemovals().toByteArray());
return new TwoPhaseSet(adds, removals);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
//#serializer

View file

@ -0,0 +1,32 @@
/**
* Copyright (C) 2015-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.ddata.protobuf;
import jdocs.ddata.TwoPhaseSet;
import akka.actor.ExtendedActorSystem;
public class TwoPhaseSetSerializerWithCompression extends TwoPhaseSetSerializer {
public TwoPhaseSetSerializerWithCompression(ExtendedActorSystem system) {
super(system);
}
//#compression
@Override
public byte[] toBinary(Object obj) {
if (obj instanceof TwoPhaseSet) {
return compress(twoPhaseSetToProto((TwoPhaseSet) obj));
} else {
throw new IllegalArgumentException(
"Can't serialize object of type " + obj.getClass());
}
}
@Override
public Object fromBinaryJava(byte[] bytes, Class<?> manifest) {
return twoPhaseSetFromBinary(decompress(bytes));
}
//#compression
}

View file

@ -0,0 +1,258 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.dispatcher;
import akka.dispatch.ControlMessage;
import akka.dispatch.RequiresMessageQueue;
import akka.testkit.AkkaSpec;
import akka.testkit.javadsl.TestKit;
import com.typesafe.config.ConfigFactory;
import docs.dispatcher.DispatcherDocSpec;
import jdocs.AbstractJavaTest;
import jdocs.actor.MyBoundedActor;
import jdocs.actor.MyActor;
import org.junit.ClassRule;
import org.junit.Test;
import scala.concurrent.ExecutionContext;
//#imports
import akka.actor.*;
//#imports
//#imports-prio
import akka.event.Logging;
import akka.event.LoggingAdapter;
//#imports-prio
//#imports-prio-mailbox
import akka.dispatch.PriorityGenerator;
import akka.dispatch.UnboundedStablePriorityMailbox;
import akka.testkit.AkkaJUnitActorSystemResource;
import com.typesafe.config.Config;
//#imports-prio-mailbox
//#imports-required-mailbox
//#imports-required-mailbox
public class DispatcherDocTest extends AbstractJavaTest {
@ClassRule
public static AkkaJUnitActorSystemResource actorSystemResource =
new AkkaJUnitActorSystemResource("DispatcherDocTest", ConfigFactory.parseString(
DispatcherDocSpec.javaConfig()).withFallback(ConfigFactory.parseString(
DispatcherDocSpec.config())).withFallback(AkkaSpec.testConf()));
private final ActorSystem system = actorSystemResource.getSystem();
@SuppressWarnings("unused")
@Test
public void defineDispatcherInConfig() {
//#defining-dispatcher-in-config
ActorRef myActor =
system.actorOf(Props.create(MyActor.class),
"myactor");
//#defining-dispatcher-in-config
}
@SuppressWarnings("unused")
@Test
public void defineDispatcherInCode() {
//#defining-dispatcher-in-code
ActorRef myActor =
system.actorOf(Props.create(MyActor.class).withDispatcher("my-dispatcher"),
"myactor3");
//#defining-dispatcher-in-code
}
@SuppressWarnings("unused")
@Test
public void defineFixedPoolSizeDispatcher() {
//#defining-fixed-pool-size-dispatcher
ActorRef myActor = system.actorOf(Props.create(MyActor.class)
.withDispatcher("blocking-io-dispatcher"));
//#defining-fixed-pool-size-dispatcher
}
@SuppressWarnings("unused")
@Test
public void definePinnedDispatcher() {
//#defining-pinned-dispatcher
ActorRef myActor = system.actorOf(Props.create(MyActor.class)
.withDispatcher("my-pinned-dispatcher"));
//#defining-pinned-dispatcher
}
@SuppressWarnings("unused")
public void compileLookup() {
//#lookup
// this is scala.concurrent.ExecutionContext
// for use with Futures, Scheduler, etc.
final ExecutionContext ex = system.dispatchers().lookup("my-dispatcher");
//#lookup
}
@SuppressWarnings("unused")
@Test
public void defineMailboxInConfig() {
//#defining-mailbox-in-config
ActorRef myActor =
system.actorOf(Props.create(MyActor.class),
"priomailboxactor");
//#defining-mailbox-in-config
}
@SuppressWarnings("unused")
@Test
public void defineMailboxInCode() {
//#defining-mailbox-in-code
ActorRef myActor =
system.actorOf(Props.create(MyActor.class)
.withMailbox("prio-mailbox"));
//#defining-mailbox-in-code
}
@SuppressWarnings("unused")
@Test
public void usingARequiredMailbox() {
ActorRef myActor =
system.actorOf(Props.create(MyBoundedActor.class));
}
@Test
public void priorityDispatcher() throws Exception {
TestKit probe = new TestKit(system);
//#prio-dispatcher
class Demo extends AbstractActor {
LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
{
for (Object msg : new Object[] { "lowpriority", "lowpriority",
"highpriority", "pigdog", "pigdog2", "pigdog3", "highpriority",
PoisonPill.getInstance() }) {
getSelf().tell(msg, getSelf());
}
}
@Override
public Receive createReceive() {
return receiveBuilder().matchAny(message -> {
log.info(message.toString());
}).build();
}
}
// We create a new Actor that just prints out what it processes
ActorRef myActor = system.actorOf(Props.create(Demo.class, this)
.withDispatcher("prio-dispatcher"));
/*
Logs:
'highpriority
'highpriority
'pigdog
'pigdog2
'pigdog3
'lowpriority
'lowpriority
*/
//#prio-dispatcher
probe.watch(myActor);
probe.expectMsgClass(Terminated.class);
}
@Test
public void controlAwareDispatcher() throws Exception {
TestKit probe = new TestKit(system);
//#control-aware-dispatcher
class Demo extends AbstractActor {
LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
{
for (Object msg : new Object[] { "foo", "bar", new MyControlMessage(),
PoisonPill.getInstance() }) {
getSelf().tell(msg, getSelf());
}
}
@Override
public Receive createReceive() {
return receiveBuilder().matchAny(message -> {
log.info(message.toString());
}).build();
}
}
// We create a new Actor that just prints out what it processes
ActorRef myActor = system.actorOf(Props.create(Demo.class, this)
.withDispatcher("control-aware-dispatcher"));
/*
Logs:
'MyControlMessage
'foo
'bar
*/
//#control-aware-dispatcher
probe.watch(myActor);
probe.expectMsgClass(Terminated.class);
}
static
//#prio-mailbox
public class MyPrioMailbox extends UnboundedStablePriorityMailbox {
// needed for reflective instantiation
public MyPrioMailbox(ActorSystem.Settings settings, Config config) {
// Create a new PriorityGenerator, lower prio means more important
super(new PriorityGenerator() {
@Override
public int gen(Object message) {
if (message.equals("highpriority"))
return 0; // 'highpriority messages should be treated first if possible
else if (message.equals("lowpriority"))
return 2; // 'lowpriority messages should be treated last if possible
else if (message.equals(PoisonPill.getInstance()))
return 3; // PoisonPill when no other left
else
return 1; // By default they go between high and low prio
}
});
}
}
//#prio-mailbox
static
//#control-aware-mailbox-messages
public class MyControlMessage implements ControlMessage {}
//#control-aware-mailbox-messages
@Test
public void requiredMailboxDispatcher() throws Exception {
ActorRef myActor = system.actorOf(Props.create(MyActor.class)
.withDispatcher("custom-dispatcher"));
}
static
//#require-mailbox-on-actor
public class MySpecialActor extends AbstractActor implements
RequiresMessageQueue<MyUnboundedMessageQueueSemantics> {
//#require-mailbox-on-actor
@Override
public Receive createReceive() {
return AbstractActor.emptyBehavior();
}
//#require-mailbox-on-actor
// ...
}
//#require-mailbox-on-actor
@Test
public void requiredMailboxActor() throws Exception {
ActorRef myActor = system.actorOf(Props.create(MySpecialActor.class));
}
}

View file

@ -0,0 +1,51 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.dispatcher;
//#mailbox-implementation-example
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.dispatch.Envelope;
import akka.dispatch.MailboxType;
import akka.dispatch.MessageQueue;
import akka.dispatch.ProducesMessageQueue;
import com.typesafe.config.Config;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.Queue;
import scala.Option;
public class MyUnboundedMailbox implements MailboxType,
ProducesMessageQueue<MyUnboundedMailbox.MyMessageQueue> {
// This is the MessageQueue implementation
public static class MyMessageQueue implements MessageQueue,
MyUnboundedMessageQueueSemantics {
private final Queue<Envelope> queue =
new ConcurrentLinkedQueue<Envelope>();
// these must be implemented; queue used as example
public void enqueue(ActorRef receiver, Envelope handle) {
queue.offer(handle);
}
public Envelope dequeue() { return queue.poll(); }
public int numberOfMessages() { return queue.size(); }
public boolean hasMessages() { return !queue.isEmpty(); }
public void cleanUp(ActorRef owner, MessageQueue deadLetters) {
for (Envelope handle: queue) {
deadLetters.enqueue(owner, handle);
}
}
}
// This constructor signature must exist, it will be called by Akka
public MyUnboundedMailbox(ActorSystem.Settings settings, Config config) {
// put your initialization code here
}
// The create method is called to create the MessageQueue
public MessageQueue create(Option<ActorRef> owner, Option<ActorSystem> system) {
return new MyMessageQueue();
}
}
//#mailbox-implementation-example

View file

@ -0,0 +1,11 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.dispatcher;
//#mailbox-implementation-example
// Marker interface used for mailbox requirements mapping
public interface MyUnboundedMessageQueueSemantics {
}
//#mailbox-implementation-example

View file

@ -0,0 +1,27 @@
/**
* Copyright (C) 2013-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.duration;
//#import
import scala.concurrent.duration.Duration;
import scala.concurrent.duration.Deadline;
//#import
class Java {
public void demo() {
//#dsl
final Duration fivesec = Duration.create(5, "seconds");
final Duration threemillis = Duration.create("3 millis");
final Duration diff = fivesec.minus(threemillis);
assert diff.lt(fivesec);
assert Duration.Zero().lt(Duration.Inf());
//#dsl
//#deadline
final Deadline deadline = Duration.create(10, "seconds").fromNow();
final Duration rest = deadline.timeLeft();
//#deadline
rest.toString();
}
}

View file

@ -0,0 +1,325 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.event;
import akka.event.japi.EventBus;
import java.util.concurrent.TimeUnit;
import jdocs.AbstractJavaTest;
import akka.testkit.javadsl.TestKit;
import org.junit.ClassRule;
import org.junit.Test;
import akka.actor.ActorSystem;
import akka.actor.ActorRef;
import akka.testkit.AkkaJUnitActorSystemResource;
import akka.util.Subclassification;
import scala.concurrent.duration.FiniteDuration;
//#lookup-bus
import akka.event.japi.LookupEventBus;
//#lookup-bus
//#subchannel-bus
import akka.event.japi.SubchannelEventBus;
//#subchannel-bus
//#scanning-bus
import akka.event.japi.ScanningEventBus;
//#scanning-bus
//#actor-bus
import akka.event.japi.ManagedActorEventBus;
//#actor-bus
public class EventBusDocTest extends AbstractJavaTest {
public static class Event {}
public static class Subscriber {}
public static class Classifier {}
static public interface EventBusApi extends EventBus<Event, Subscriber, Classifier> {
@Override
//#event-bus-api
/**
* Attempts to register the subscriber to the specified Classifier
* @return true if successful and false if not (because it was already
* subscribed to that Classifier, or otherwise)
*/
public boolean subscribe(Subscriber subscriber, Classifier to);
//#event-bus-api
@Override
//#event-bus-api
/**
* Attempts to deregister the subscriber from the specified Classifier
* @return true if successful and false if not (because it wasn't subscribed
* to that Classifier, or otherwise)
*/
public boolean unsubscribe(Subscriber subscriber, Classifier from);
//#event-bus-api
@Override
//#event-bus-api
/**
* Attempts to deregister the subscriber from all Classifiers it may be subscribed to
*/
public void unsubscribe(Subscriber subscriber);
//#event-bus-api
@Override
//#event-bus-api
/**
* Publishes the specified Event to this bus
*/
public void publish(Event event);
//#event-bus-api
}
static
//#lookup-bus
public class MsgEnvelope {
public final String topic;
public final Object payload;
public MsgEnvelope(String topic, Object payload) {
this.topic = topic;
this.payload = payload;
}
}
//#lookup-bus
static
//#lookup-bus
/**
* Publishes the payload of the MsgEnvelope when the topic of the
* MsgEnvelope equals the String specified when subscribing.
*/
public class LookupBusImpl extends LookupEventBus<MsgEnvelope, ActorRef, String> {
// is used for extracting the classifier from the incoming events
@Override public String classify(MsgEnvelope event) {
return event.topic;
}
// will be invoked for each event for all subscribers which registered themselves
// for the events classifier
@Override public void publish(MsgEnvelope event, ActorRef subscriber) {
subscriber.tell(event.payload, ActorRef.noSender());
}
// must define a full order over the subscribers, expressed as expected from
// `java.lang.Comparable.compare`
@Override public int compareSubscribers(ActorRef a, ActorRef b) {
return a.compareTo(b);
}
// determines the initial size of the index data structure
// used internally (i.e. the expected number of different classifiers)
@Override public int mapSize() {
return 128;
}
}
//#lookup-bus
static
//#subchannel-bus
public class StartsWithSubclassification implements Subclassification<String> {
@Override public boolean isEqual(String x, String y) {
return x.equals(y);
}
@Override public boolean isSubclass(String x, String y) {
return x.startsWith(y);
}
}
//#subchannel-bus
static
//#subchannel-bus
/**
* Publishes the payload of the MsgEnvelope when the topic of the
* MsgEnvelope starts with the String specified when subscribing.
*/
public class SubchannelBusImpl extends SubchannelEventBus<MsgEnvelope, ActorRef, String> {
// Subclassification is an object providing `isEqual` and `isSubclass`
// to be consumed by the other methods of this classifier
@Override public Subclassification<String> subclassification() {
return new StartsWithSubclassification();
}
// is used for extracting the classifier from the incoming events
@Override public String classify(MsgEnvelope event) {
return event.topic;
}
// will be invoked for each event for all subscribers which registered themselves
// for the events classifier
@Override public void publish(MsgEnvelope event, ActorRef subscriber) {
subscriber.tell(event.payload, ActorRef.noSender());
}
}
//#subchannel-bus
static
//#scanning-bus
/**
* Publishes String messages with length less than or equal to the length
* specified when subscribing.
*/
public class ScanningBusImpl extends ScanningEventBus<String, ActorRef, Integer> {
// is needed for determining matching classifiers and storing them in an
// ordered collection
@Override public int compareClassifiers(Integer a, Integer b) {
return a.compareTo(b);
}
// is needed for storing subscribers in an ordered collection
@Override public int compareSubscribers(ActorRef a, ActorRef b) {
return a.compareTo(b);
}
// determines whether a given classifier shall match a given event; it is invoked
// for each subscription for all received events, hence the name of the classifier
@Override public boolean matches(Integer classifier, String event) {
return event.length() <= classifier;
}
// will be invoked for each event for all subscribers which registered themselves
// for the events classifier
@Override public void publish(String event, ActorRef subscriber) {
subscriber.tell(event, ActorRef.noSender());
}
}
//#scanning-bus
static
//#actor-bus
public class Notification {
public final ActorRef ref;
public final int id;
public Notification(ActorRef ref, int id) {
this.ref = ref;
this.id = id;
}
}
//#actor-bus
static
//#actor-bus
public class ActorBusImpl extends ManagedActorEventBus<Notification> {
// the ActorSystem will be used for book-keeping operations, such as subscribers terminating
public ActorBusImpl(ActorSystem system) {
super(system);
}
// is used for extracting the classifier from the incoming events
@Override public ActorRef classify(Notification event) {
return event.ref;
}
// determines the initial size of the index data structure
// used internally (i.e. the expected number of different classifiers)
@Override public int mapSize() {
return 128;
}
}
//#actor-bus
@ClassRule
public static AkkaJUnitActorSystemResource actorSystemResource =
new AkkaJUnitActorSystemResource("EventBusDocTest");
private final ActorSystem system = actorSystemResource.getSystem();
@Test
public void demonstrateLookupClassification() {
new TestKit(system) {{
//#lookup-bus-test
LookupBusImpl lookupBus = new LookupBusImpl();
lookupBus.subscribe(getTestActor(), "greetings");
lookupBus.publish(new MsgEnvelope("time", System.currentTimeMillis()));
lookupBus.publish(new MsgEnvelope("greetings", "hello"));
expectMsgEquals("hello");
//#lookup-bus-test
}};
}
@Test
public void demonstrateSubchannelClassification() {
new TestKit(system) {{
//#subchannel-bus-test
SubchannelBusImpl subchannelBus = new SubchannelBusImpl();
subchannelBus.subscribe(getTestActor(), "abc");
subchannelBus.publish(new MsgEnvelope("xyzabc", "x"));
subchannelBus.publish(new MsgEnvelope("bcdef", "b"));
subchannelBus.publish(new MsgEnvelope("abc", "c"));
expectMsgEquals("c");
subchannelBus.publish(new MsgEnvelope("abcdef", "d"));
expectMsgEquals("d");
//#subchannel-bus-test
}};
}
@Test
public void demonstrateScanningClassification() {
new TestKit(system) {{
//#scanning-bus-test
ScanningBusImpl scanningBus = new ScanningBusImpl();
scanningBus.subscribe(getTestActor(), 3);
scanningBus.publish("xyzabc");
scanningBus.publish("ab");
expectMsgEquals("ab");
scanningBus.publish("abc");
expectMsgEquals("abc");
//#scanning-bus-test
}};
}
@Test
public void demonstrateManagedActorClassification() {
//#actor-bus-test
ActorRef observer1 = new TestKit(system).getRef();
ActorRef observer2 = new TestKit(system).getRef();
TestKit probe1 = new TestKit(system);
TestKit probe2 = new TestKit(system);
ActorRef subscriber1 = probe1.getRef();
ActorRef subscriber2 = probe2.getRef();
ActorBusImpl actorBus = new ActorBusImpl(system);
actorBus.subscribe(subscriber1, observer1);
actorBus.subscribe(subscriber2, observer1);
actorBus.subscribe(subscriber2, observer2);
Notification n1 = new Notification(observer1, 100);
actorBus.publish(n1);
probe1.expectMsgEquals(n1);
probe2.expectMsgEquals(n1);
Notification n2 = new Notification(observer2, 101);
actorBus.publish(n2);
probe2.expectMsgEquals(n2);
probe1.expectNoMsg(FiniteDuration.create(500, TimeUnit.MILLISECONDS));
//#actor-bus-test
}
}

View file

@ -0,0 +1,245 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.event;
//#imports
import akka.actor.*;
import akka.event.Logging;
import akka.event.LoggingAdapter;
//#imports
//#imports-listener
import akka.event.Logging.InitializeLogger;
import akka.event.Logging.Error;
import akka.event.Logging.Warning;
import akka.event.Logging.Info;
import akka.event.Logging.Debug;
//#imports-listener
import jdocs.AbstractJavaTest;
import akka.testkit.javadsl.TestKit;
import org.junit.Test;
import java.util.Optional;
//#imports-mdc
import akka.event.DiagnosticLoggingAdapter;
import java.util.HashMap;
import java.util.Map;
//#imports-mdc
//#imports-deadletter
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
//#imports-deadletter
public class LoggingDocTest extends AbstractJavaTest {
@Test
public void useLoggingActor() {
ActorSystem system = ActorSystem.create("MySystem");
ActorRef myActor = system.actorOf(Props.create(MyActor.class, this));
myActor.tell("test", ActorRef.noSender());
TestKit.shutdownActorSystem(system);
}
@Test
public void useLoggingActorWithMDC() {
ActorSystem system = ActorSystem.create("MyDiagnosticSystem");
ActorRef mdcActor = system.actorOf(Props.create(MdcActor.class, this));
mdcActor.tell("some request", ActorRef.noSender());
TestKit.shutdownActorSystem(system);
}
@Test
public void subscribeToDeadLetters() {
//#deadletters
final ActorSystem system = ActorSystem.create("DeadLetters");
final ActorRef actor = system.actorOf(Props.create(DeadLetterActor.class));
system.eventStream().subscribe(actor, DeadLetter.class);
//#deadletters
TestKit.shutdownActorSystem(system);
}
//#superclass-subscription-eventstream
interface AllKindsOfMusic { }
class Jazz implements AllKindsOfMusic {
final public String artist;
public Jazz(String artist) {
this.artist = artist;
}
}
class Electronic implements AllKindsOfMusic {
final public String artist;
public Electronic(String artist) {
this.artist = artist;
}
}
static class Listener extends AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Jazz.class, msg ->
System.out.printf("%s is listening to: %s%n", getSelf().path().name(), msg)
)
.match(Electronic.class, msg ->
System.out.printf("%s is listening to: %s%n", getSelf().path().name(), msg)
)
.build();
}
}
//#superclass-subscription-eventstream
@Test
public void subscribeBySubclassification() {
final ActorSystem system = ActorSystem.create("DeadLetters");
//#superclass-subscription-eventstream
final ActorRef actor = system.actorOf(Props.create(DeadLetterActor.class));
system.eventStream().subscribe(actor, DeadLetter.class);
final ActorRef jazzListener = system.actorOf(Props.create(Listener.class));
final ActorRef musicListener = system.actorOf(Props.create(Listener.class));
system.eventStream().subscribe(jazzListener, Jazz.class);
system.eventStream().subscribe(musicListener, AllKindsOfMusic.class);
// only musicListener gets this message, since it listens to *all* kinds of music:
system.eventStream().publish(new Electronic("Parov Stelar"));
// jazzListener and musicListener will be notified about Jazz:
system.eventStream().publish(new Jazz("Sonny Rollins"));
//#superclass-subscription-eventstream
TestKit.shutdownActorSystem(system);
}
@Test
public void subscribeToSuppressedDeadLetters() {
final ActorSystem system = ActorSystem.create("SuppressedDeadLetters");
final ActorRef actor = system.actorOf(Props.create(DeadLetterActor.class));
//#suppressed-deadletters
system.eventStream().subscribe(actor, SuppressedDeadLetter.class);
//#suppressed-deadletters
TestKit.shutdownActorSystem(system);
}
@Test
public void subscribeToAllDeadLetters() {
final ActorSystem system = ActorSystem.create("AllDeadLetters");
final ActorRef actor = system.actorOf(Props.create(DeadLetterActor.class));
//#all-deadletters
system.eventStream().subscribe(actor, AllDeadLetters.class);
//#all-deadletters
TestKit.shutdownActorSystem(system);
}
@Test
public void demonstrateMultipleArgs() {
final ActorSystem system = ActorSystem.create("multiArg");
//#array
final Object[] args = new Object[] { "The", "brown", "fox", "jumps", 42 };
system.log().debug("five parameters: {}, {}, {}, {}, {}", args);
//#array
TestKit.shutdownActorSystem(system);
}
//#my-actor
class MyActor extends AbstractActor {
LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
@Override
public void preStart() {
log.debug("Starting");
}
@Override
public void preRestart(Throwable reason, Optional<Object> message) {
log.error(reason, "Restarting due to [{}] when processing [{}]",
reason.getMessage(), message.isPresent() ? message.get() : "");
}
@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 AbstractActor {
final DiagnosticLoggingAdapter log = Logging.getLogger(this);
@Override
public Receive createReceive() {
return receiveBuilder()
.matchAny(msg -> {
Map<String, Object> mdc;
mdc = new HashMap<String, Object>();
mdc.put("requestId", 1234);
mdc.put("visitorId", 5678);
log.setMDC(mdc);
log.info("Starting new request");
log.clearMDC();
})
.build();
}
}
//#mdc-actor
//#my-event-listener
class MyEventListener extends AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder()
.match(InitializeLogger.class, msg -> {
getSender().tell(Logging.loggerInitialized(), getSelf());
})
.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 AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder()
.match(DeadLetter.class, msg -> {
System.out.println(msg);
})
.build();
}
}
//#deadletter-actor
}

View file

@ -0,0 +1,89 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.extension;
//#imports
import akka.actor.*;
import java.util.concurrent.atomic.AtomicLong;
//#imports
import jdocs.AbstractJavaTest;
import org.junit.Test;
public class ExtensionDocTest extends AbstractJavaTest {
static
//#extension
public class CountExtensionImpl implements Extension {
//Since this Extension is a shared instance
// per ActorSystem we need to be threadsafe
private final AtomicLong counter = new AtomicLong(0);
//This is the operation this Extension provides
public long increment() {
return counter.incrementAndGet();
}
}
//#extension
static
//#extensionid
public class CountExtension extends AbstractExtensionId<CountExtensionImpl>
implements ExtensionIdProvider {
//This will be the identifier of our CountExtension
public final static CountExtension CountExtensionProvider = new CountExtension();
private CountExtension() {}
//The lookup method is required by ExtensionIdProvider,
// so we return ourselves here, this allows us
// to configure our extension to be loaded when
// the ActorSystem starts up
public CountExtension lookup() {
return CountExtension.CountExtensionProvider; //The public static final
}
//This method will be called by Akka
// to instantiate our Extension
public CountExtensionImpl createExtension(ExtendedActorSystem system) {
return new CountExtensionImpl();
}
}
//#extensionid
static
//#extension-usage-actor
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().getSystem()).increment();
})
.build();
}
}
//#extension-usage-actor
@Test
public void demonstrateHowToCreateAndUseAnAkkaExtensionInJava() {
final ActorSystem system = null;
try {
//#extension-usage
// typically you would use static import of the
// CountExtension.CountExtensionProvider field
CountExtension.CountExtensionProvider.get(system).increment();
//#extension-usage
} catch (Exception e) {
//do nothing
}
}
}

View file

@ -0,0 +1,100 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.extension;
//#imports
import akka.actor.Extension;
import akka.actor.AbstractExtensionId;
import akka.actor.ExtensionIdProvider;
import akka.actor.ActorSystem;
import akka.actor.ExtendedActorSystem;
import scala.concurrent.duration.Duration;
import com.typesafe.config.Config;
import java.util.concurrent.TimeUnit;
//#imports
import jdocs.AbstractJavaTest;
import akka.actor.AbstractActor;
import org.junit.Test;
public class SettingsExtensionDocTest extends AbstractJavaTest {
static
//#extension
public class SettingsImpl implements Extension {
public final String DB_URI;
public final Duration CIRCUIT_BREAKER_TIMEOUT;
public SettingsImpl(Config config) {
DB_URI = config.getString("myapp.db.uri");
CIRCUIT_BREAKER_TIMEOUT =
Duration.create(config.getDuration("myapp.circuit-breaker.timeout",
TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS);
}
}
//#extension
static
//#extensionid
public class Settings extends AbstractExtensionId<SettingsImpl>
implements ExtensionIdProvider {
public final static Settings SettingsProvider = new Settings();
private Settings() {}
public Settings lookup() {
return Settings.SettingsProvider;
}
public SettingsImpl createExtension(ExtendedActorSystem system) {
return new SettingsImpl(system.settings().config());
}
}
//#extensionid
static
//#extension-usage-actor
public class MyActor extends AbstractActor {
// typically you would use static import of the Settings.SettingsProvider field
final SettingsImpl settings =
Settings.SettingsProvider.get(getContext().getSystem());
Connection connection =
connect(settings.DB_URI, settings.CIRCUIT_BREAKER_TIMEOUT);
//#extension-usage-actor
public Connection connect(String dbUri, Duration circuitBreakerTimeout) {
return new Connection();
}
@Override
public Receive createReceive() {
return AbstractActor.emptyBehavior();
}
//#extension-usage-actor
}
//#extension-usage-actor
public static class Connection {
}
@Test
public void demonstrateHowToCreateAndUseAnAkkaExtensionInJava() {
final ActorSystem system = null;
try {
//#extension-usage
// typically you would use static import of the Settings.SettingsProvider field
String dbUri = Settings.SettingsProvider.get(system).DB_URI;
//#extension-usage
} catch (Exception e) {
//do nothing
}
}
}

View file

@ -0,0 +1,671 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.future;
//#imports1
import akka.dispatch.*;
import jdocs.AbstractJavaTest;
import scala.concurrent.ExecutionContext;
import scala.concurrent.Future;
import scala.concurrent.Await;
import scala.concurrent.Promise;
import akka.util.Timeout;
//#imports1
//#imports2
import scala.concurrent.duration.Duration;
import akka.japi.Function;
import java.util.concurrent.Callable;
import static akka.dispatch.Futures.future;
import static java.util.concurrent.TimeUnit.SECONDS;
//#imports2
//#imports3
import static akka.dispatch.Futures.sequence;
//#imports3
//#imports4
import static akka.dispatch.Futures.traverse;
//#imports4
//#imports5
import akka.japi.Function2;
import static akka.dispatch.Futures.fold;
//#imports5
//#imports6
import static akka.dispatch.Futures.reduce;
//#imports6
//#imports7
import static akka.pattern.Patterns.after;
import java.util.Arrays;
//#imports7
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.CountDownLatch;
import scala.compat.java8.FutureConverters;
import akka.testkit.AkkaJUnitActorSystemResource;
import org.junit.ClassRule;
import org.junit.Test;
import akka.testkit.AkkaSpec;
import akka.actor.Status.Failure;
import akka.actor.ActorSystem;
import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.Props;
import akka.pattern.Patterns;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.*;
public class FutureDocTest extends AbstractJavaTest {
@ClassRule
public static AkkaJUnitActorSystemResource actorSystemResource =
new AkkaJUnitActorSystemResource("FutureDocTest", AkkaSpec.testConf());
private final ActorSystem system = actorSystemResource.getSystem();
public final static class PrintResult<T> extends OnSuccess<T> {
@Override public final void onSuccess(T t) {
// print t
}
}
public final static class Demo {
//#print-result
public final static class PrintResult<T> extends OnSuccess<T> {
@Override public final void onSuccess(T t) {
System.out.println(t);
}
}
//#print-result
}
@SuppressWarnings("unchecked") @Test public void useCustomExecutionContext() throws Exception {
ExecutorService yourExecutorServiceGoesHere = Executors.newSingleThreadExecutor();
//#diy-execution-context
ExecutionContext ec =
ExecutionContexts.fromExecutorService(yourExecutorServiceGoesHere);
//Use ec with your Futures
Future<String> f1 = Futures.successful("foo");
// Then you shut down the ExecutorService at the end of your application.
yourExecutorServiceGoesHere.shutdown();
//#diy-execution-context
}
@Test
public void useBlockingFromActor() throws Exception {
ActorRef actor = system.actorOf(Props.create(MyActor.class));
String msg = "hello";
//#ask-blocking
Timeout timeout = new Timeout(Duration.create(5, "seconds"));
Future<Object> future = Patterns.ask(actor, msg, timeout);
String result = (String) Await.result(future, timeout.duration());
//#ask-blocking
//#pipe-to
akka.pattern.Patterns.pipe(future, system.dispatcher()).to(actor);
//#pipe-to
assertEquals("HELLO", result);
}
@Test
public void useFutureEval() throws Exception {
//#future-eval
Future<String> f = future(new Callable<String>() {
public String call() {
return "Hello" + "World";
}
}, system.dispatcher());
f.onSuccess(new PrintResult<String>(), system.dispatcher());
//#future-eval
String result = (String) Await.result(f, Duration.create(5, SECONDS));
assertEquals("HelloWorld", result);
}
@Test
public void useMap() throws Exception {
//#map
final ExecutionContext ec = system.dispatcher();
Future<String> f1 = future(new Callable<String>() {
public String call() {
return "Hello" + "World";
}
}, ec);
Future<Integer> f2 = f1.map(new Mapper<String, Integer>() {
public Integer apply(String s) {
return s.length();
}
}, ec);
f2.onSuccess(new PrintResult<Integer>(), system.dispatcher());
//#map
int result = Await.result(f2, Duration.create(5, SECONDS));
assertEquals(10, result);
}
@Test
public void useFlatMap() throws Exception {
//#flat-map
final ExecutionContext ec = system.dispatcher();
Future<String> f1 = future(new Callable<String>() {
public String call() {
return "Hello" + "World";
}
}, ec);
Future<Integer> f2 = f1.flatMap(new Mapper<String, Future<Integer>>() {
public Future<Integer> apply(final String s) {
return future(new Callable<Integer>() {
public Integer call() {
return s.length();
}
}, ec);
}
}, ec);
f2.onSuccess(new PrintResult<Integer>(), system.dispatcher());
//#flat-map
int result = Await.result(f2, Duration.create(5, SECONDS));
assertEquals(10, result);
}
@Test
public void useSequence() throws Exception {
List<Future<Integer>> source = new ArrayList<Future<Integer>>();
source.add(Futures.successful(1));
source.add(Futures.successful(2));
//#sequence
final ExecutionContext ec = system.dispatcher();
//Some source generating a sequence of Future<Integer>:s
Iterable<Future<Integer>> listOfFutureInts = source;
// now we have a Future[Iterable[Integer]]
Future<Iterable<Integer>> futureListOfInts = sequence(listOfFutureInts, ec);
// Find the sum of the odd numbers
Future<Long> futureSum = futureListOfInts.map(
new Mapper<Iterable<Integer>, Long>() {
public Long apply(Iterable<Integer> ints) {
long sum = 0;
for (Integer i : ints)
sum += i;
return sum;
}
}, ec);
futureSum.onSuccess(new PrintResult<Long>(), system.dispatcher());
//#sequence
long result = Await.result(futureSum, Duration.create(5, SECONDS));
assertEquals(3L, result);
}
@Test
public void useTraverse() throws Exception {
//#traverse
final ExecutionContext ec = system.dispatcher();
//Just a sequence of Strings
Iterable<String> listStrings = Arrays.asList("a", "b", "c");
Future<Iterable<String>> futureResult = traverse(listStrings,
new Function<String, Future<String>>() {
public Future<String> apply(final String r) {
return future(new Callable<String>() {
public String call() {
return r.toUpperCase();
}
}, ec);
}
}, ec);
//Returns the sequence of strings as upper case
futureResult.onSuccess(new PrintResult<Iterable<String>>(), system.dispatcher());
//#traverse
Iterable<String> result = Await.result(futureResult, Duration.create(5, SECONDS));
assertEquals(Arrays.asList("A", "B", "C"), result);
}
@Test
public void useFold() throws Exception {
List<Future<String>> source = new ArrayList<Future<String>>();
source.add(Futures.successful("a"));
source.add(Futures.successful("b"));
//#fold
final ExecutionContext ec = system.dispatcher();
//A sequence of Futures, in this case Strings
Iterable<Future<String>> futures = source;
//Start value is the empty string
Future<String> resultFuture = fold("", futures,
new Function2<String, String, String>() {
public String apply(String r, String t) {
return r + t; //Just concatenate
}
}, ec);
resultFuture.onSuccess(new PrintResult<String>(), system.dispatcher());
//#fold
String result = Await.result(resultFuture, Duration.create(5, SECONDS));
assertEquals("ab", result);
}
@Test
public void useReduce() throws Exception {
List<Future<String>> source = new ArrayList<Future<String>>();
source.add(Futures.successful("a"));
source.add(Futures.successful("b"));
//#reduce
final ExecutionContext ec = system.dispatcher();
//A sequence of Futures, in this case Strings
Iterable<Future<String>> futures = source;
Future<Object> resultFuture = reduce(futures,
new Function2<Object, String, Object>() {
public Object apply(Object r, String t) {
return r + t; //Just concatenate
}
}, ec);
resultFuture.onSuccess(new PrintResult<Object>(), system.dispatcher());
//#reduce
Object result = Await.result(resultFuture, Duration.create(5, SECONDS));
assertEquals("ab", result);
}
@Test
public void useSuccessfulAndFailedAndPromise() throws Exception {
final ExecutionContext ec = system.dispatcher();
//#successful
Future<String> future = Futures.successful("Yay!");
//#successful
//#failed
Future<String> otherFuture = Futures.failed(
new IllegalArgumentException("Bang!"));
//#failed
//#promise
Promise<String> promise = Futures.promise();
Future<String> theFuture = promise.future();
promise.success("hello");
//#promise
Object result = Await.result(future, Duration.create(5, SECONDS));
assertEquals("Yay!", result);
Throwable result2 = Await.result(otherFuture.failed(),
Duration.create(5, SECONDS));
assertEquals("Bang!", result2.getMessage());
String out = Await.result(theFuture, Duration.create(5, SECONDS));
assertEquals("hello", out);
}
@Test
public void useFilter() throws Exception {
//#filter
final ExecutionContext ec = system.dispatcher();
Future<Integer> future1 = Futures.successful(4);
Future<Integer> successfulFilter = future1.filter(Filter.filterOf(
new Function<Integer, Boolean>() {
public Boolean apply(Integer i) {
return i % 2 == 0;
}
}), ec);
Future<Integer> failedFilter = future1.filter(Filter.filterOf(
new Function<Integer, Boolean>() {
public Boolean apply(Integer i) {
return i % 2 != 0;
}
}), ec);
//When filter fails, the returned Future will be failed with a scala.MatchError
//#filter
}
public void sendToTheInternetz(String s) {
}
public void sendToIssueTracker(Throwable t) {
}
@Test
public void useAndThen() {
//#and-then
final ExecutionContext ec = system.dispatcher();
Future<String> future1 = Futures.successful("value").andThen(
new OnComplete<String>() {
public void onComplete(Throwable failure, String result) {
if (failure != null)
sendToIssueTracker(failure);
}
}, ec).andThen(new OnComplete<String>() {
public void onComplete(Throwable failure, String result) {
if (result != null)
sendToTheInternetz(result);
}
}, ec);
//#and-then
}
@Test
public void useRecover() throws Exception {
//#recover
final ExecutionContext ec = system.dispatcher();
Future<Integer> future = future(new Callable<Integer>() {
public Integer call() {
return 1 / 0;
}
}, ec).recover(new Recover<Integer>() {
public Integer recover(Throwable problem) throws Throwable {
if (problem instanceof ArithmeticException)
return 0;
else
throw problem;
}
}, ec);
future.onSuccess(new PrintResult<Integer>(), system.dispatcher());
//#recover
int result = Await.result(future, Duration.create(5, SECONDS));
assertEquals(result, 0);
}
@Test
public void useTryRecover() throws Exception {
//#try-recover
final ExecutionContext ec = system.dispatcher();
Future<Integer> future = future(new Callable<Integer>() {
public Integer call() {
return 1 / 0;
}
}, ec).recoverWith(new Recover<Future<Integer>>() {
public Future<Integer> recover(Throwable problem) throws Throwable {
if (problem instanceof ArithmeticException) {
return future(new Callable<Integer>() {
public Integer call() {
return 0;
}
}, ec);
} else
throw problem;
}
}, ec);
future.onSuccess(new PrintResult<Integer>(), system.dispatcher());
//#try-recover
int result = Await.result(future, Duration.create(5, SECONDS));
assertEquals(result, 0);
}
@Test
public void useOnSuccessOnFailureAndOnComplete() throws Exception {
{
Future<String> future = Futures.successful("foo");
//#onSuccess
final ExecutionContext ec = system.dispatcher();
future.onSuccess(new OnSuccess<String>() {
public void onSuccess(String result) {
if ("bar" == result) {
//Do something if it resulted in "bar"
} else {
//Do something if it was some other String
}
}
}, ec);
//#onSuccess
}
{
Future<String> future = Futures.failed(new IllegalStateException("OHNOES"));
//#onFailure
final ExecutionContext ec = system.dispatcher();
future.onFailure(new OnFailure() {
public void onFailure(Throwable failure) {
if (failure instanceof IllegalStateException) {
//Do something if it was this particular failure
} else {
//Do something if it was some other failure
}
}
}, ec);
//#onFailure
}
{
Future<String> future = Futures.successful("foo");
//#onComplete
final ExecutionContext ec = system.dispatcher();
future.onComplete(new OnComplete<String>() {
public void onComplete(Throwable failure, String result) {
if (failure != null) {
//We got a failure, handle it here
} else {
// We got a result, do something with it
}
}
}, ec);
//#onComplete
}
}
@Test
public void useOrAndZip() throws Exception {
{
//#zip
final ExecutionContext ec = system.dispatcher();
Future<String> future1 = Futures.successful("foo");
Future<String> future2 = Futures.successful("bar");
Future<String> future3 = future1.zip(future2).map(
new Mapper<scala.Tuple2<String, String>, String>() {
public String apply(scala.Tuple2<String, String> zipped) {
return zipped._1() + " " + zipped._2();
}
}, ec);
future3.onSuccess(new PrintResult<String>(), system.dispatcher());
//#zip
String result = Await.result(future3, Duration.create(5, SECONDS));
assertEquals("foo bar", result);
}
{
//#fallback-to
Future<String> future1 = Futures.failed(new IllegalStateException("OHNOES1"));
Future<String> future2 = Futures.failed(new IllegalStateException("OHNOES2"));
Future<String> future3 = Futures.successful("bar");
// Will have "bar" in this case
Future<String> future4 = future1.fallbackTo(future2).fallbackTo(future3);
future4.onSuccess(new PrintResult<String>(), system.dispatcher());
//#fallback-to
String result = Await.result(future4, Duration.create(5, SECONDS));
assertEquals("bar", result);
}
}
@Test(expected = IllegalStateException.class)
@SuppressWarnings("unchecked")
public void useAfter() throws Exception {
//#after
final ExecutionContext ec = system.dispatcher();
Future<String> failExc = Futures.failed(new IllegalStateException("OHNOES1"));
Future<String> delayed = Patterns.after(Duration.create(200, "millis"),
system.scheduler(), ec, failExc);
Future<String> future = future(new Callable<String>() {
public String call() throws InterruptedException {
Thread.sleep(1000);
return "foo";
}
}, ec);
Future<String> result = Futures.firstCompletedOf(
Arrays.<Future<String>>asList(future, delayed), ec);
//#after
Await.result(result, Duration.create(2, SECONDS));
}
@Test
public void thenApplyCompletionThread() throws Exception {
//#apply-completion-thread
final ExecutionContext ec = system.dispatcher();
final CountDownLatch countDownLatch = new CountDownLatch(1);
Future<String> scalaFuture = Futures.future(() -> {
assertThat(Thread.currentThread().getName(), containsString("akka.actor.default-dispatcher"));
countDownLatch.await(); // do not complete yet
return "hello";
}, ec);
CompletionStage<String> fromScalaFuture = FutureConverters.toJava(scalaFuture)
.thenApply(s -> { // 1
assertThat(Thread.currentThread().getName(), containsString("ForkJoinPool.commonPool"));
return s;
})
.thenApply(s -> { // 2
assertThat(Thread.currentThread().getName(), containsString("ForkJoinPool.commonPool"));
return s;
})
.thenApply(s -> { // 3
assertThat(Thread.currentThread().getName(), containsString("ForkJoinPool.commonPool"));
return s;
});
countDownLatch.countDown(); // complete scalaFuture
//#apply-completion-thread
fromScalaFuture.toCompletableFuture().get(2, SECONDS);
}
@Test
public void thenApplyMainThread() throws Exception {
final ExecutionContext ec = system.dispatcher();
//#apply-main-thread
Future<String> scalaFuture = Futures.future(() -> {
assertThat(Thread.currentThread().getName(), containsString("akka.actor.default-dispatcher"));
return "hello";
}, ec);
CompletionStage<String> completedStage = FutureConverters.toJava(scalaFuture)
.thenApply(s -> { // 1
assertThat(Thread.currentThread().getName(), containsString("ForkJoinPool.commonPool"));
return s;
});
completedStage.toCompletableFuture().get(2, SECONDS); // complete current CompletionStage
final String currentThread = Thread.currentThread().getName();
CompletionStage<String> stage2 = completedStage
.thenApply(s -> { // 2
assertThat(Thread.currentThread().getName(), is(currentThread));
return s;
})
.thenApply(s -> { // 3
assertThat(Thread.currentThread().getName(), is(currentThread));
return s;
});
//#apply-main-thread
stage2.toCompletableFuture().get(2, SECONDS);
}
@Test
public void thenApplyAsyncDefault() throws Exception {
final ExecutionContext ec = system.dispatcher();
Future<String> scalaFuture = Futures.future(() -> {
assertThat(Thread.currentThread().getName(), containsString("akka.actor.default-dispatcher"));
return "hello";
}, ec);
//#apply-async-default
CompletionStage<String> fromScalaFuture = FutureConverters.toJava(scalaFuture)
.thenApplyAsync(s -> { // 1
assertThat(Thread.currentThread().getName(), containsString("ForkJoinPool.commonPool"));
return s;
})
.thenApplyAsync(s -> { // 2
assertThat(Thread.currentThread().getName(), containsString("ForkJoinPool.commonPool"));
return s;
})
.thenApplyAsync(s -> { // 3
assertThat(Thread.currentThread().getName(), containsString("ForkJoinPool.commonPool"));
return s;
});
//#apply-async-default
fromScalaFuture.toCompletableFuture().get(2, SECONDS);
}
@Test
public void thenApplyAsyncExecutor() throws Exception {
final ExecutionContext ec = system.dispatcher();
Future<String> scalaFuture = Futures.future(() -> {
assertThat(Thread.currentThread().getName(), containsString("akka.actor.default-dispatcher"));
return "hello";
}, ec);
//#apply-async-executor
final Executor ex = system.dispatcher();
CompletionStage<String> fromScalaFuture = FutureConverters.toJava(scalaFuture)
.thenApplyAsync(s -> {
assertThat(Thread.currentThread().getName(), containsString("akka.actor.default-dispatcher"));
return s;
}, ex)
.thenApplyAsync(s -> {
assertThat(Thread.currentThread().getName(), containsString("akka.actor.default-dispatcher"));
return s;
}, ex)
.thenApplyAsync(s -> {
assertThat(Thread.currentThread().getName(), containsString("akka.actor.default-dispatcher"));
return s;
}, ex);
//#apply-async-executor
fromScalaFuture.toCompletableFuture().get(2, SECONDS);
}
public static class MyActor extends AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder()
.match(String.class, msg -> {
getSender().tell(msg.toUpperCase(), getSelf());
})
.match(Integer.class, i -> {
if (i < 0) {
getSender().tell(new Failure(new ArithmeticException("Negative values not supported")), getSelf());
} else {
getSender().tell(i, getSelf());
}
})
.build();
}
}
}

View file

@ -0,0 +1,86 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.io;
import akka.actor.ActorSystem;
import akka.actor.AbstractActor;
//#imports
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import akka.actor.ActorRef;
import akka.io.Inet;
import akka.io.Tcp;
import akka.io.TcpMessage;
import akka.io.TcpSO;
import akka.util.ByteString;
//#imports
public class IODocTest {
static public class Demo extends AbstractActor {
ActorRef connectionActor = null;
ActorRef listener = getSelf();
@Override
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), getSelf());
//#connect
//#connect-with-options
final InetSocketAddress localAddr = new InetSocketAddress("127.0.0.1",
1234);
final List<Inet.SocketOption> options = new ArrayList<Inet.SocketOption>();
options.add(TcpSO.keepAlive(true));
tcp.tell(TcpMessage.connect(remoteAddr, localAddr, options, null, false), getSelf());
//#connect-with-options
})
//#connected
.match(Tcp.Connected.class, conn -> {
connectionActor = getSender();
connectionActor.tell(TcpMessage.register(listener), getSelf());
})
//#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 = getSelf();
//#bind
final ActorRef tcp = Tcp.get(system).manager();
final InetSocketAddress localAddr = new InetSocketAddress("127.0.0.1",
1234);
final List<Inet.SocketOption> options = new ArrayList<Inet.SocketOption>();
options.add(TcpSO.reuseAddress(true));
tcp.tell(TcpMessage.bind(handler, localAddr, 10, options, false), getSelf());
//#bind
})
.build();
}
}
static ActorSystem system;
// This is currently only a compilation test, nothing is run
}

View file

@ -0,0 +1,97 @@
package jdocs.io;
import akka.actor.ActorRef;
import akka.actor.Props;
import akka.actor.AbstractActor;
import akka.io.Inet;
import akka.io.Tcp;
import akka.io.TcpMessage;
import akka.util.ByteString;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
public class JavaReadBackPressure {
static public class Listener extends AbstractActor {
ActorRef tcp;
ActorRef listener;
@Override
//#pull-accepting
public Receive createReceive() {
return receiveBuilder()
.match(Tcp.Bound.class, x -> {
listener = getSender();
// Accept connections one by one
listener.tell(TcpMessage.resumeAccepting(1), getSelf());
})
.match(Tcp.Connected.class, x -> {
ActorRef handler = getContext().actorOf(Props.create(PullEcho.class, getSender()));
getSender().tell(TcpMessage.register(handler), getSelf());
// Resume accepting connections
listener.tell(TcpMessage.resumeAccepting(1), getSelf());
})
.build();
}
//#pull-accepting
@Override
public void preStart() throws Exception {
//#pull-mode-bind
tcp = Tcp.get(getContext().getSystem()).manager();
final List<Inet.SocketOption> options = new ArrayList<Inet.SocketOption>();
tcp.tell(
TcpMessage.bind(getSelf(), new InetSocketAddress("localhost", 0), 100, options, true),
getSelf()
);
//#pull-mode-bind
}
private void demonstrateConnect() {
//#pull-mode-connect
final List<Inet.SocketOption> options = new ArrayList<Inet.SocketOption>();
tcp.tell(
TcpMessage.connect(new InetSocketAddress("localhost", 3000), null, options, null, true),
getSelf()
);
//#pull-mode-connect
}
}
static public class Ack implements Tcp.Event {
}
static public class PullEcho extends AbstractActor {
final ActorRef connection;
public PullEcho(ActorRef connection) {
this.connection = connection;
}
//#pull-reading-echo
@Override
public void preStart() throws Exception {
connection.tell(TcpMessage.resumeReading(), getSelf());
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Tcp.Received.class, message -> {
ByteString data = message.data();
connection.tell(TcpMessage.write(data, new Ack()), getSelf());
})
.match(Ack.class, message -> {
connection.tell(TcpMessage.resumeReading(), getSelf());
})
.build();
}
//#pull-reading-echo
}
}

View file

@ -0,0 +1,128 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.io;
//#imports
import akka.actor.ActorRef;
import akka.actor.AbstractActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.io.Inet;
import akka.io.Udp;
import akka.io.UdpMessage;
import akka.util.ByteString;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.StandardProtocolFamily;
import java.net.DatagramSocket;
import java.nio.channels.DatagramChannel;
import java.util.ArrayList;
import java.util.List;
//#imports
public class JavaUdpMulticast {
//#inet6-protocol-family
public static class Inet6ProtocolFamily extends Inet.DatagramChannelCreator {
@Override
public DatagramChannel create() throws Exception {
return DatagramChannel.open(StandardProtocolFamily.INET6);
}
}
//#inet6-protocol-family
//#multicast-group
public static class MulticastGroup extends Inet.AbstractSocketOptionV2 {
private String address;
private String interf;
public MulticastGroup(String address, String interf) {
this.address = address;
this.interf = interf;
}
@Override
public void afterBind(DatagramSocket s) {
try {
InetAddress group = InetAddress.getByName(address);
NetworkInterface networkInterface = NetworkInterface.getByName(interf);
s.getChannel().join(group, networkInterface);
} catch (Exception ex) {
System.out.println("Unable to join multicast group.");
}
}
}
//#multicast-group
public static class Listener extends AbstractActor {
LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
ActorRef sink;
public Listener(String iface, String group, Integer port, ActorRef sink) {
this.sink = sink;
//#bind
List<Inet.SocketOption> options = new ArrayList<>();
options.add(new Inet6ProtocolFamily());
options.add(new MulticastGroup(group, iface));
final ActorRef mgr = Udp.get(getContext().getSystem()).getManager();
// listen for datagrams on this address
InetSocketAddress endpoint = new InetSocketAddress(port);
mgr.tell(UdpMessage.bind(getSelf(), endpoint, options), getSelf());
//#bind
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Udp.Bound.class, bound -> {
log.info("Bound to {}", bound.localAddress());
sink.tell(bound, getSelf());
})
.match(Udp.Received.class, received -> {
final String txt = received.data().decodeString("utf-8");
log.info("Received '{}' from {}", txt, received.sender());
sink.tell(txt, getSelf());
})
.build();
}
}
public static class Sender extends AbstractActor {
LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
String iface;
String group;
Integer port;
String message;
public Sender(String iface, String group, Integer port, String msg) {
this.iface = iface;
this.group = group;
this.port = port;
this.message = msg;
List<Inet.SocketOption> options = new ArrayList<>();
options.add(new Inet6ProtocolFamily());
final ActorRef mgr = Udp.get(getContext().getSystem()).getManager();
mgr.tell(UdpMessage.simpleSender(options), getSelf());
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Udp.SimpleSenderReady.class, x -> {
InetSocketAddress remote = new InetSocketAddress(group + "%" + iface, port);
log.info("Sending message to " + remote);
getSender().tell(UdpMessage.send(ByteString.fromString(message), remote), getSelf());
})
.build();
}
}
}

View file

@ -0,0 +1,100 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.io;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.io.Udp;
import akka.testkit.SocketUtil;
import jdocs.AbstractJavaTest;
import akka.testkit.javadsl.TestKit;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.*;
public class JavaUdpMulticastTest extends AbstractJavaTest {
static ActorSystem system;
@BeforeClass
public static void setup() {
system = ActorSystem.create("JavaUdpMulticastTest");
}
@Test
public void testUdpMulticast() throws Exception {
new TestKit(system) {{
List<NetworkInterface> ipv6Ifaces = new ArrayList<>();
for (Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); interfaces.hasMoreElements(); ) {
NetworkInterface interf = interfaces.nextElement();
if (interf.isUp() && interf.supportsMulticast()) {
for (Enumeration<InetAddress> addresses = interf.getInetAddresses(); addresses.hasMoreElements(); ) {
InetAddress address = addresses.nextElement();
if (address instanceof Inet6Address) {
ipv6Ifaces.add(interf);
}
}
}
}
if (ipv6Ifaces.isEmpty()) {
system.log().info("JavaUdpMulticastTest skipped since no ipv6 interface supporting multicast could be found");
} else {
// lots of problems with choosing the wrong interface for this test depending
// on the platform (awsdl0 can't be used on OSX, docker[0-9] can't be used in a docker machine etc.)
// therefore: try hard to find an interface that _does_ work, and only fail if there was any potentially
// working interfaces but all failed
for (Iterator<NetworkInterface> interfaceIterator = ipv6Ifaces.iterator(); interfaceIterator.hasNext(); ) {
NetworkInterface ipv6Iface = interfaceIterator.next();
// host assigned link local multicast address http://tools.ietf.org/html/rfc3307#section-4.3.2
// generate a random 32 bit multicast address with the high order bit set
final String randomAddress = Long.toHexString(((long) Math.abs(new Random().nextInt())) | (1L << 31)).toUpperCase();
final StringBuilder groupBuilder = new StringBuilder("FF02:");
for (int i = 0; i < 2; i += 1) {
groupBuilder.append(":");
groupBuilder.append(randomAddress.subSequence(i * 4, i * 4 + 4));
}
final String group = groupBuilder.toString();
final Integer port = SocketUtil.temporaryUdpIpv6Port(ipv6Iface);
final String msg = "ohi";
final ActorRef sink = getRef();
final String iface = ipv6Iface.getName();
final ActorRef listener = system.actorOf(Props.create(JavaUdpMulticast.Listener.class, iface, group, port, sink));
try {
expectMsgClass(Udp.Bound.class);
final ActorRef sender = system.actorOf(Props.create(JavaUdpMulticast.Sender.class, iface, group, port, msg));
expectMsgEquals(msg);
// success with one interface is enough
break;
} catch (AssertionError ex) {
if (!interfaceIterator.hasNext()) throw ex;
else {
system.log().info("Failed to run test on interface {}", ipv6Iface.getDisplayName());
}
} finally {
// unbind
system.stop(listener);
}
}
}
}};
}
@AfterClass
public static void tearDown() {
TestKit.shutdownActorSystem(system);
system = null;
}
}

View file

@ -0,0 +1,85 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.io;
import akka.japi.pf.ReceiveBuilder;
import org.junit.Test;
import akka.actor.ActorSystem;
import akka.actor.AbstractActor;
//#imports
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import akka.actor.ActorRef;
import akka.io.Inet;
import akka.io.UdpConnected;
import akka.io.UdpConnectedMessage;
import akka.io.UdpSO;
import akka.util.ByteString;
//#imports
public class UdpConnectedDocTest {
static public class Demo extends AbstractActor {
ActorRef connectionActor = null;
ActorRef handler = getSelf();
ActorSystem system = getContext().getSystem();
@Override
public Receive createReceive() {
ReceiveBuilder builder = receiveBuilder();
builder.matchEquals("connect", message -> {
//#manager
final ActorRef udp = UdpConnected.get(system).manager();
//#manager
//#connect
final InetSocketAddress remoteAddr =
new InetSocketAddress("127.0.0.1", 12345);
udp.tell(UdpConnectedMessage.connect(handler, remoteAddr), getSelf());
//#connect
//#connect-with-options
final InetSocketAddress localAddr =
new InetSocketAddress("127.0.0.1", 1234);
final List<Inet.SocketOption> options =
new ArrayList<Inet.SocketOption>();
options.add(UdpSO.broadcast(true));
udp.tell(UdpConnectedMessage.connect(handler, remoteAddr, localAddr, options), getSelf());
//#connect-with-options
});
//#connected
builder.match(UdpConnected.Connected.class, conn -> {
connectionActor = getSender(); // Save the worker ref for later use
});
//#connected
//#received
builder
.match(UdpConnected.Received.class, recv -> {
final ByteString data = recv.data();
// and do something with the received data ...
})
.match(UdpConnected.CommandFailed.class, failed -> {
final UdpConnected.Command command = failed.cmd();
// react to failed connect, etc.
})
.match(UdpConnected.Disconnected.class, x -> {
// do something on disconnect
});
//#received
builder.matchEquals("send", x -> {
ByteString data = ByteString.empty();
//#send
connectionActor.tell(UdpConnectedMessage.send(data), getSelf());
//#send
});
return builder.build();
}
}
@Test
public void demonstrateConnect() {
}
}

View file

@ -0,0 +1,164 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.io;
//#imports
import akka.actor.ActorRef;
import akka.actor.PoisonPill;
import akka.actor.AbstractActor;
import akka.io.Udp;
import akka.io.UdpConnected;
import akka.io.UdpConnectedMessage;
import akka.io.UdpMessage;
import akka.util.ByteString;
import java.net.InetSocketAddress;
//#imports
public class UdpDocTest {
//#sender
public static class SimpleSender extends AbstractActor {
final InetSocketAddress remote;
public SimpleSender(InetSocketAddress remote) {
this.remote = remote;
// request creation of a SimpleSender
final ActorRef mgr = Udp.get(getContext().getSystem()).getManager();
mgr.tell(UdpMessage.simpleSender(), getSelf());
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Udp.SimpleSenderReady.class, message -> {
getContext().become(ready(getSender()));
//#sender
getSender().tell(UdpMessage.send(ByteString.fromString("hello"), remote), getSelf());
//#sender
})
.build();
}
private Receive ready(final ActorRef send) {
return receiveBuilder()
.match(String.class, message -> {
send.tell(UdpMessage.send(ByteString.fromString(message), remote), getSelf());
//#sender
if (message.equals("world")) {
send.tell(PoisonPill.getInstance(), getSelf());
}
//#sender
})
.build();
}
}
//#sender
//#listener
public static class Listener extends AbstractActor {
final ActorRef nextActor;
public Listener(ActorRef nextActor) {
this.nextActor = nextActor;
// request creation of a bound listen socket
final ActorRef mgr = Udp.get(getContext().getSystem()).getManager();
mgr.tell(
UdpMessage.bind(getSelf(), new InetSocketAddress("localhost", 0)),
getSelf());
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Udp.Bound.class, bound -> {
//#listener
nextActor.tell(bound.localAddress(), getSender());
//#listener
getContext().become(ready(getSender()));
})
.build();
}
private Receive ready(final ActorRef socket) {
return receiveBuilder()
.match(Udp.Received.class, r -> {
// echo server example: send back the data
socket.tell(UdpMessage.send(r.data(), r.sender()), getSelf());
// 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());
})
.matchEquals(UdpMessage.unbind(), message -> {
socket.tell(message, getSelf());
})
.match(Udp.Unbound.class, message -> {
getContext().stop(getSelf());
})
.build();
}
}
//#listener
//#connected
public static class Connected extends AbstractActor {
final InetSocketAddress remote;
public Connected(InetSocketAddress remote) {
this.remote = remote;
// create a restricted a.k.a. connected socket
final ActorRef mgr = UdpConnected.get(getContext().getSystem()).getManager();
mgr.tell(UdpConnectedMessage.connect(getSelf(), remote), getSelf());
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(UdpConnected.Connected.class, message -> {
getContext().become(ready(getSender()));
//#connected
getSender()
.tell(UdpConnectedMessage.send(ByteString.fromString("hello")),
getSelf());
//#connected
})
.build();
}
private Receive ready(final ActorRef connection) {
return receiveBuilder()
.match(UdpConnected.Received.class, r -> {
// process data, send it on, etc.
// #connected
if (r.data().utf8String().equals("hello")) {
connection.tell(
UdpConnectedMessage.send(ByteString.fromString("world")),
getSelf());
}
// #connected
})
.match(String.class, str -> {
connection
.tell(UdpConnectedMessage.send(ByteString.fromString(str)),
getSelf());
})
.matchEquals(UdpConnectedMessage.disconnect(), message -> {
connection.tell(message, getSelf());
})
.match(UdpConnected.Disconnected.class, x -> {
getContext().stop(getSelf());
})
.build();
}
}
//#connected
}

View file

@ -0,0 +1,243 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.io.japi;
import java.net.InetSocketAddress;
import java.util.LinkedList;
import java.util.Queue;
import akka.actor.ActorRef;
import akka.actor.AbstractActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.io.Tcp.CommandFailed;
import akka.io.Tcp.ConnectionClosed;
import akka.io.Tcp.Event;
import akka.io.Tcp.Received;
import akka.io.Tcp.Write;
import akka.io.Tcp.WritingResumed;
import akka.io.TcpMessage;
import akka.util.ByteString;
//#echo-handler
public class EchoHandler extends AbstractActor {
final LoggingAdapter log = Logging
.getLogger(getContext().getSystem(), getSelf());
final ActorRef connection;
final InetSocketAddress remote;
public static final long MAX_STORED = 100000000;
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<ByteString> storage = new LinkedList<ByteString>();
private boolean suspended = false;
private static class Ack implements Event {
public final int ack;
public Ack(int ack) {
this.ack = ack;
}
}
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);
// start out in optimistic write-through mode
getContext().become(writing);
}
@Override
public Receive createReceive() {
return writing;
}
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())), getSelf());
buffer(data);
})
.match(Integer.class, msg -> {
acknowledge(msg);
})
.match(CommandFailed.class, msg -> {
final Write w = (Write) msg.cmd();
connection.tell(TcpMessage.resumeWriting(), getSelf());
getContext().become(buffering((Ack) w.ack()));
})
.match(ConnectionClosed.class, msg -> {
if (msg.isPeerClosed()) {
if (storage.isEmpty()) {
getContext().stop(getSelf());
} else {
getContext().become(closing());
}
}
})
.build();
}
//#buffering
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());
})
.match(WritingResumed.class, msg -> {
writeFirst();
})
.match(ConnectionClosed.class, msg -> {
if (msg.isPeerClosed())
state.peerClosed = true;
else
getContext().stop(getSelf());
})
.match(Integer.class, ack -> {
acknowledge(ack);
if (ack >= nack.ack) {
// otherwise it was the ack of the last successful write
if (storage.isEmpty()) {
if (state.peerClosed)
getContext().stop(getSelf());
else
getContext().become(writing);
} 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);
}
}
}
})
.build();
}
//#buffering
//#closing
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);
})
.match(Integer.class, msg -> {
acknowledge(msg);
if (storage.isEmpty())
getContext().stop(getSelf());
})
.build();
}
protected Receive closeResend() {
return receiveBuilder()
.match(WritingResumed.class, msg -> {
writeAll();
getContext().unbecome();
})
.match(Integer.class, msg -> {
acknowledge(msg);
})
.build();
}
//#closing
//#storage-omitted
@Override
public void postStop() {
log.info("transferred {} bytes from/to [{}]", transferred, remote);
}
//#helpers
protected void buffer(ByteString data) {
storage.add(data);
stored += data.size();
if (stored > MAX_STORED) {
log.warning("drop connection to [{}] (buffer overrun)", remote);
getContext().stop(getSelf());
} else if (stored > HIGH_WATERMARK) {
log.debug("suspending reading at {}", currentOffset());
connection.tell(TcpMessage.suspendReading(), getSelf());
suspended = true;
}
}
protected void acknowledge(int ack) {
assert ack == storageOffset;
assert !storage.isEmpty();
final ByteString acked = storage.remove();
stored -= acked.size();
transferred += acked.size();
storageOffset += 1;
if (suspended && stored < LOW_WATERMARK) {
log.debug("resuming reading");
connection.tell(TcpMessage.resumeReading(), getSelf());
suspended = false;
}
}
//#helpers
protected int currentOffset() {
return storageOffset + storage.size();
}
protected void writeAll() {
int i = 0;
for (ByteString data : storage) {
connection.tell(TcpMessage.write(data, new Ack(storageOffset + i++)), getSelf());
}
}
protected void writeFirst() {
connection.tell(TcpMessage.write(storage.peek(), new Ack(storageOffset)), getSelf());
}
//#storage-omitted
}
//#echo-handler

View file

@ -0,0 +1,81 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.io.japi;
import java.net.InetSocketAddress;
import akka.actor.ActorRef;
import akka.actor.Props;
import akka.actor.SupervisorStrategy;
import akka.actor.AbstractActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.io.Tcp;
import akka.io.Tcp.Bind;
import akka.io.Tcp.Bound;
import akka.io.Tcp.Connected;
import akka.io.TcpMessage;
public class EchoManager extends AbstractActor {
final LoggingAdapter log = Logging
.getLogger(getContext().getSystem(), getSelf());
final Class<?> handlerClass;
public EchoManager(Class<?> handlerClass) {
this.handlerClass = handlerClass;
}
@Override
public SupervisorStrategy supervisorStrategy() {
return SupervisorStrategy.stoppingStrategy();
}
@Override
public void preStart() throws Exception {
//#manager
final ActorRef tcpManager = Tcp.get(getContext().getSystem()).manager();
//#manager
tcpManager.tell(
TcpMessage.bind(getSelf(), new InetSocketAddress("localhost", 0), 100),
getSelf());
}
@Override
public void postRestart(Throwable arg0) throws Exception {
// do not restart
getContext().stop(getSelf());
}
@Override
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(getSelf());
} else {
log.warning("unknown command failed [{}]", failed.cmd());
}
})
.match(Connected.class, conn -> {
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
})
.build();
}
}

View file

@ -0,0 +1,35 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.io.japi;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
public class EchoServer {
public static void main(String[] args) throws InterruptedException {
final Config config = ConfigFactory.parseString("akka.loglevel=DEBUG");
final ActorSystem system = ActorSystem.create("EchoServer", config);
try {
final CountDownLatch latch = new CountDownLatch(1);
final ActorRef watcher = system.actorOf(Props.create(Watcher.class, latch), "watcher");
final ActorRef nackServer = system.actorOf(Props.create(EchoManager.class, EchoHandler.class), "nack");
final ActorRef ackServer = system.actorOf(Props.create(EchoManager.class, SimpleEchoHandler.class), "ack");
watcher.tell(nackServer, ActorRef.noSender());
watcher.tell(ackServer, ActorRef.noSender());
latch.await(10, TimeUnit.MINUTES);
} finally {
system.terminate();
}
}
}

View file

@ -0,0 +1,186 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.io.japi;
import akka.testkit.AkkaJUnitActorSystemResource;
import jdocs.AbstractJavaTest;
import akka.testkit.javadsl.TestKit;
import org.junit.ClassRule;
import org.junit.Test;
//#imports
import java.net.InetSocketAddress;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.actor.AbstractActor;
import akka.io.Tcp;
import akka.io.Tcp.Bound;
import akka.io.Tcp.CommandFailed;
import akka.io.Tcp.Connected;
import akka.io.Tcp.ConnectionClosed;
import akka.io.Tcp.Received;
import akka.io.TcpMessage;
import akka.util.ByteString;
//#imports
import akka.testkit.AkkaSpec;
public class IODocTest extends AbstractJavaTest {
static
//#server
public class Server extends AbstractActor {
final ActorRef manager;
public Server(ActorRef manager) {
this.manager = manager;
}
public static Props props(ActorRef manager) {
return Props.create(Server.class, manager);
}
@Override
public void preStart() throws Exception {
final ActorRef tcp = Tcp.get(getContext().getSystem()).manager();
tcp.tell(TcpMessage.bind(getSelf(),
new InetSocketAddress("localhost", 0), 100), getSelf());
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Bound.class, msg -> {
manager.tell(msg, getSelf());
})
.match(CommandFailed.class, msg -> {
getContext().stop(getSelf());
})
.match(Connected.class, conn -> {
manager.tell(conn, getSelf());
final ActorRef handler = getContext().actorOf(
Props.create(SimplisticHandler.class));
getSender().tell(TcpMessage.register(handler), getSelf());
})
.build();
}
}
//#server
static
//#simplistic-handler
public class SimplisticHandler extends AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Received.class, msg -> {
final ByteString data = msg.data();
System.out.println(data);
getSender().tell(TcpMessage.write(data), getSelf());
})
.match(ConnectionClosed.class, msg -> {
getContext().stop(getSelf());
})
.build();
}
}
//#simplistic-handler
static
//#client
public class Client extends AbstractActor {
final InetSocketAddress remote;
final ActorRef listener;
public static Props props(InetSocketAddress remote, ActorRef listener) {
return Props.create(Client.class, remote, listener);
}
public Client(InetSocketAddress remote, ActorRef listener) {
this.remote = remote;
this.listener = listener;
final ActorRef tcp = Tcp.get(getContext().getSystem()).manager();
tcp.tell(TcpMessage.connect(remote), getSelf());
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(CommandFailed.class, msg -> {
listener.tell("failed", getSelf());
getContext().stop(getSelf());
})
.match(Connected.class, msg -> {
listener.tell(msg, getSelf());
getSender().tell(TcpMessage.register(getSelf()), getSelf());
getContext().become(connected(getSender()));
})
.build();
}
private Receive connected(final ActorRef connection) {
return receiveBuilder()
.match(ByteString.class, msg -> {
connection.tell(TcpMessage.write((ByteString) msg), getSelf());
})
.match(CommandFailed.class, msg -> {
// OS kernel socket buffer was full
})
.match(Received.class, msg -> {
listener.tell(msg.data(), getSelf());
})
.matchEquals("close", msg -> {
connection.tell(TcpMessage.close(), getSelf());
})
.match(ConnectionClosed.class, msg -> {
getContext().stop(getSelf());
})
.build();
}
}
//#client
@ClassRule
public static AkkaJUnitActorSystemResource actorSystemResource =
new AkkaJUnitActorSystemResource("IODocTest", AkkaSpec.testConf());
private final ActorSystem system = actorSystemResource.getSystem();
@Test
public void testConnection() {
new TestKit(system) {
{
@SuppressWarnings("unused")
final ActorRef server = system.actorOf(Server.props(getRef()), "server1");
final InetSocketAddress listen = expectMsgClass(Bound.class).localAddress();
final ActorRef client = system.actorOf(Client.props(listen, getRef()), "client1");
final Connected c1 = expectMsgClass(Connected.class);
final Connected c2 = expectMsgClass(Connected.class);
assert c1.localAddress().equals(c2.remoteAddress());
assert c2.localAddress().equals(c1.remoteAddress());
client.tell(ByteString.fromString("hello"), getRef());
final ByteString reply = expectMsgClass(ByteString.class);
assert reply.utf8String().equals("hello");
watch(client);
client.tell("close", getRef());
expectTerminated(client);
}
};
}
}

View file

@ -0,0 +1,45 @@
/**
* Copyright (C) 2013-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.io.japi;
//#message
public class Message {
static public class Person {
private final String first;
private final String last;
public Person(String first, String last) {
this.first = first;
this.last = last;
}
public String getFirst() {
return first;
}
public String getLast() {
return last;
}
}
private final Person[] persons;
private final double[] happinessCurve;
public Message(Person[] persons, double[] happinessCurve) {
this.persons = persons;
this.happinessCurve = happinessCurve;
}
public Person[] getPersons() {
return persons;
}
public double[] getHappinessCurve() {
return happinessCurve;
}
}
//#message

View file

@ -0,0 +1,134 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.io.japi;
import java.net.InetSocketAddress;
import java.util.LinkedList;
import java.util.Queue;
import akka.actor.ActorRef;
import akka.actor.AbstractActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.io.Tcp.ConnectionClosed;
import akka.io.Tcp.Event;
import akka.io.Tcp.Received;
import akka.io.TcpMessage;
import akka.util.ByteString;
//#simple-echo-handler
public class SimpleEchoHandler extends AbstractActor {
final LoggingAdapter log = Logging
.getLogger(getContext().getSystem(), getSelf());
final ActorRef connection;
final InetSocketAddress remote;
public static final long maxStored = 100000000;
public static final long highWatermark = maxStored * 5 / 10;
public static final long lowWatermark = maxStored * 2 / 10;
public SimpleEchoHandler(ActorRef connection, InetSocketAddress remote) {
this.connection = connection;
this.remote = remote;
// sign death pact: this actor stops when the connection is closed
getContext().watch(connection);
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Received.class, msg -> {
final ByteString data = msg.data();
buffer(data);
connection.tell(TcpMessage.write(data, ACK), getSelf());
// now switch behavior to waiting for acknowledgement
getContext().become(buffering(), false);
})
.match(ConnectionClosed.class, msg -> {
getContext().stop(getSelf());
})
.build();
}
private final Receive buffering() {
return receiveBuilder()
.match(Received.class, msg -> {
buffer(msg.data());
})
.match(Event.class, msg -> msg == ACK, msg -> {
acknowledge();
})
.match(ConnectionClosed.class, msg -> {
if (msg.isPeerClosed()) {
closing = true;
} else {
// could also be ErrorClosed, in which case we just give up
getContext().stop(getSelf());
}
})
.build();
}
//#storage-omitted
public void postStop() {
log.info("transferred {} bytes from/to [{}]", transferred, remote);
}
private long transferred;
private long stored = 0;
private Queue<ByteString> storage = new LinkedList<ByteString>();
private boolean suspended = false;
private boolean closing = false;
private final Event ACK = new Event() {};
//#simple-helpers
protected void buffer(ByteString data) {
storage.add(data);
stored += data.size();
if (stored > maxStored) {
log.warning("drop connection to [{}] (buffer overrun)", remote);
getContext().stop(getSelf());
} else if (stored > highWatermark) {
log.debug("suspending reading");
connection.tell(TcpMessage.suspendReading(), getSelf());
suspended = true;
}
}
protected void acknowledge() {
final ByteString acked = storage.remove();
stored -= acked.size();
transferred += acked.size();
if (suspended && stored < lowWatermark) {
log.debug("resuming reading");
connection.tell(TcpMessage.resumeReading(), getSelf());
suspended = false;
}
if (storage.isEmpty()) {
if (closing) {
getContext().stop(getSelf());
} else {
getContext().unbecome();
}
} else {
connection.tell(TcpMessage.write(storage.peek(), ACK), getSelf());
}
}
//#simple-helpers
//#storage-omitted
}
//#simple-echo-handler

View file

@ -0,0 +1,37 @@
package jdocs.io.japi;
import java.util.concurrent.CountDownLatch;
import akka.actor.ActorRef;
import akka.actor.Terminated;
import akka.actor.AbstractActor;
public class Watcher extends AbstractActor {
static public class Watch {
final ActorRef target;
public Watch(ActorRef target) {
this.target = target;
}
}
final CountDownLatch latch;
public Watcher(CountDownLatch latch) {
this.latch = latch;
}
@Override
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(getSelf());
})
.build();
}
}

View file

@ -0,0 +1,49 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.pattern;
import akka.actor.*;
import akka.pattern.Backoff;
import akka.pattern.BackoffSupervisor;
import akka.testkit.TestActors.EchoActor;
//#backoff-imports
import scala.concurrent.duration.Duration;
//#backoff-imports
import java.util.concurrent.TimeUnit;
public class BackoffSupervisorDocTest {
void exampleStop (ActorSystem system) {
//#backoff-stop
final Props childProps = Props.create(EchoActor.class);
final Props supervisorProps = BackoffSupervisor.props(
Backoff.onStop(
childProps,
"myEcho",
Duration.create(3, TimeUnit.SECONDS),
Duration.create(30, TimeUnit.SECONDS),
0.2)); // adds 20% "noise" to vary the intervals slightly
system.actorOf(supervisorProps, "echoSupervisor");
//#backoff-stop
}
void exampleFailure (ActorSystem system) {
//#backoff-fail
final Props childProps = Props.create(EchoActor.class);
final Props supervisorProps = BackoffSupervisor.props(
Backoff.onFailure(
childProps,
"myEcho",
Duration.create(3, TimeUnit.SECONDS),
Duration.create(30, TimeUnit.SECONDS),
0.2)); // adds 20% "noise" to vary the intervals slightly
system.actorOf(supervisorProps, "echoSupervisor");
//#backoff-fail
}
}

View file

@ -0,0 +1,173 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.pattern;
import akka.actor.*;
import akka.testkit.*;
import akka.testkit.TestEvent.Mute;
import akka.testkit.TestEvent.UnMute;
import jdocs.AbstractJavaTest;
import akka.testkit.javadsl.TestKit;
import org.junit.*;
import scala.concurrent.duration.Duration;
import scala.concurrent.duration.FiniteDuration;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
public class SchedulerPatternTest extends AbstractJavaTest {
@ClassRule
public static AkkaJUnitActorSystemResource actorSystemResource =
new AkkaJUnitActorSystemResource("SchedulerPatternTest", AkkaSpec.testConf());
private final ActorSystem system = actorSystemResource.getSystem();
static
//#schedule-constructor
public class ScheduleInConstructor extends AbstractActor {
private final Cancellable tick = getContext().getSystem().scheduler().schedule(
Duration.create(500, TimeUnit.MILLISECONDS),
Duration.create(1, TimeUnit.SECONDS),
getSelf(), "tick", getContext().dispatcher(), null);
//#schedule-constructor
// this variable and constructor is declared here to not show up in the docs
final ActorRef target;
public ScheduleInConstructor(ActorRef target) {
this.target = target;
}
//#schedule-constructor
@Override
public void postStop() {
tick.cancel();
}
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("tick", message -> {
// do something useful here
//#schedule-constructor
target.tell(message, getSelf());
//#schedule-constructor
})
.matchEquals("restart", message -> {
throw new ArithmeticException();
})
.build();
}
}
//#schedule-constructor
static
//#schedule-receive
public class ScheduleInReceive extends AbstractActor {
//#schedule-receive
// this variable and constructor is declared here to not show up in the docs
final ActorRef target;
public ScheduleInReceive(ActorRef target) {
this.target = target;
}
//#schedule-receive
@Override
public void preStart() {
getContext().getSystem().scheduler().scheduleOnce(
Duration.create(500, TimeUnit.MILLISECONDS),
getSelf(), "tick", getContext().dispatcher(), null);
}
// override postRestart so we don't call preStart and schedule a new message
@Override
public void postRestart(Throwable reason) {
}
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("tick", message -> {
// send another periodic tick after the specified delay
getContext().getSystem().scheduler().scheduleOnce(
Duration.create(1, TimeUnit.SECONDS),
getSelf(), "tick", getContext().dispatcher(), null);
// do something useful here
//#schedule-receive
target.tell(message, getSelf());
//#schedule-receive
})
.matchEquals("restart", message -> {
throw new ArithmeticException();
})
.build();
}
}
//#schedule-receive
@Test
@Ignore // no way to tag this as timing sensitive
public void scheduleInConstructor() {
new TestSchedule(system) {{
final TestKit probe = new TestKit(system);
final Props props = Props.create(ScheduleInConstructor.class, probe.getRef());
testSchedule(probe, props, duration("3000 millis"), duration("2000 millis"));
}};
}
@Test
@Ignore // no way to tag this as timing sensitive
public void scheduleInReceive() {
new TestSchedule(system) {{
final TestKit probe = new TestKit(system);
final Props props = Props.create(ScheduleInReceive.class, probe.getRef());
testSchedule(probe, props, duration("3000 millis"), duration("2500 millis"));
}};
}
@Test
public void doNothing() {
// actorSystemResource.after is not called when all tests are ignored
}
public static class TestSchedule extends TestKit {
private ActorSystem system;
public TestSchedule(ActorSystem system) {
super(system);
this.system = system;
}
public void testSchedule(final TestKit probe, Props props,
FiniteDuration startDuration,
FiniteDuration afterRestartDuration) {
Iterable<akka.testkit.EventFilter> filter =
Arrays.asList(new akka.testkit.EventFilter[]{
(akka.testkit.EventFilter) new ErrorFilter(ArithmeticException.class)});
try {
system.eventStream().publish(new Mute(filter));
final ActorRef actor = system.actorOf(props);
within(startDuration, () -> {
probe.expectMsgEquals("tick");
probe.expectMsgEquals("tick");
probe.expectMsgEquals("tick");
return null;
});
actor.tell("restart", getRef());
within(afterRestartDuration, () -> {
probe.expectMsgEquals("tick");
probe.expectMsgEquals("tick");
return null;
});
system.stop(actor);
}
finally {
system.eventStream().publish(new UnMute(filter));
}
}
}
}

View file

@ -0,0 +1,106 @@
package jdocs.pattern;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeoutException;
import scala.concurrent.duration.Duration;
import akka.actor.ActorKilledException;
import akka.actor.ActorRef;
import akka.actor.ActorRefFactory;
import akka.actor.Cancellable;
import akka.actor.OneForOneStrategy;
import akka.actor.Props;
import akka.actor.Scheduler;
import akka.actor.Status;
import akka.actor.SupervisorStrategy;
import akka.actor.Terminated;
import akka.actor.AbstractActor;
import akka.pattern.PatternsCS;
import akka.util.Timeout;
public class SupervisedAsk {
private static class AskParam {
Props props;
Object message;
Timeout timeout;
AskParam(Props props, Object message, Timeout timeout) {
this.props = props;
this.message = message;
this.timeout = timeout;
}
}
private static class AskTimeout {
}
public static class AskSupervisorCreator extends AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder()
.match(AskParam.class, message -> {
ActorRef supervisor = getContext().actorOf(
Props.create(AskSupervisor.class));
supervisor.forward(message, getContext());
})
.build();
}
}
public static class AskSupervisor extends AbstractActor {
private ActorRef targetActor;
private ActorRef caller;
private AskParam askParam;
private Cancellable timeoutMessage;
@Override
public SupervisorStrategy supervisorStrategy() {
return new OneForOneStrategy(0, Duration.Zero(), cause -> {
caller.tell(new Status.Failure(cause), getSelf());
return SupervisorStrategy.stop();
});
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(AskParam.class, message -> {
askParam = message;
caller = getSender();
targetActor = getContext().actorOf(askParam.props);
getContext().watch(targetActor);
targetActor.forward(askParam.message, getContext());
Scheduler scheduler = getContext().getSystem().scheduler();
timeoutMessage = scheduler.scheduleOnce(askParam.timeout.duration(),
getSelf(), new AskTimeout(), getContext().dispatcher(), null);
})
.match(Terminated.class, message -> {
Throwable ex = new ActorKilledException("Target actor terminated.");
caller.tell(new Status.Failure(ex), getSelf());
timeoutMessage.cancel();
getContext().stop(getSelf());
})
.match(AskTimeout.class, message -> {
Throwable ex = new TimeoutException("Target actor timed out after "
+ askParam.timeout.toString());
caller.tell(new Status.Failure(ex), getSelf());
getContext().stop(getSelf());
})
.build();
}
}
public static CompletionStage<Object> askOf(ActorRef supervisorCreator, Props props,
Object message, Timeout timeout) {
AskParam param = new AskParam(props, message, timeout);
return PatternsCS.ask(supervisorCreator, param, timeout);
}
synchronized public static ActorRef createSupervisorCreator(
ActorRefFactory factory) {
return factory.actorOf(Props.create(AskSupervisorCreator.class));
}
}

View file

@ -0,0 +1,30 @@
package jdocs.pattern;
import akka.actor.ActorRef;
import akka.actor.ActorRefFactory;
import akka.actor.Props;
import akka.actor.AbstractActor;
import akka.util.Timeout;
import scala.concurrent.duration.FiniteDuration;
import java.util.concurrent.CompletionStage;
public class SupervisedAskSpec {
public Object execute(Class<? extends AbstractActor> someActor,
Object message, Timeout timeout, ActorRefFactory actorSystem)
throws Exception {
// example usage
try {
ActorRef supervisorCreator = SupervisedAsk
.createSupervisorCreator(actorSystem);
CompletionStage<Object> finished = SupervisedAsk.askOf(supervisorCreator,
Props.create(someActor), message, timeout);
FiniteDuration d = timeout.duration();
return finished.toCompletableFuture().get(d.length(), d.unit());
} catch (Exception e) {
// exception propagated by supervision
throw e;
}
}
}

Some files were not shown because too many files have changed in this diff Show more