Merge pull request #22003 from akka/wip-lambda-patriknw

Improvements of AbstractActor API, #21717
This commit is contained in:
Patrik Nordwall 2017-01-23 19:59:19 +01:00 committed by GitHub
commit 04846f4528
158 changed files with 3751 additions and 9345 deletions

View file

@ -21,9 +21,9 @@ import org.scalatest.junit.JUnitSuite;
public class ActorCreationTest extends JUnitSuite {
static class C implements Creator<UntypedActor> {
static class C implements Creator<AbstractActor> {
@Override
public UntypedActor create() throws Exception {
public AbstractActor create() throws Exception {
return null;
}
}
@ -35,19 +35,19 @@ public class ActorCreationTest extends JUnitSuite {
}
}
static class E<T extends UntypedActor> implements Creator<T> {
static class E<T extends AbstractActor> implements Creator<T> {
@Override
public T create() {
return null;
}
}
static interface I<T> extends Creator<UntypedActor> {
static interface I<T> extends Creator<AbstractActor> {
}
static class F implements I<Object> {
@Override
public UntypedActor create() {
public AbstractActor create() {
return null;
}
}
@ -59,12 +59,12 @@ public class ActorCreationTest extends JUnitSuite {
}
}
abstract class H extends UntypedActor {
abstract class H extends AbstractActor {
public H(String a) {
}
}
static class P implements Creator<UntypedActor> {
static class P implements Creator<AbstractActor> {
final String value;
public P(String value) {
@ -72,7 +72,7 @@ public class ActorCreationTest extends JUnitSuite {
}
@Override
public UntypedActor create() throws Exception {
public AbstractActor create() throws Exception {
return null;
}
}
@ -94,40 +94,46 @@ public class ActorCreationTest extends JUnitSuite {
TestActor(Integer magicNumber) {
this.magicNumber = magicNumber;
}
@Override
public Receive createReceive() {
return receiveBuilder().build();
}
}
public static class UntypedTestActor extends UntypedActor {
public static class TestActor2 extends UntypedAbstractActor {
public static Props propsUsingCreator(final int magicNumber) {
// You need to specify the actual type of the returned actor
// since runtime type information erased
return Props.create(UntypedTestActor.class, new Creator<UntypedTestActor>() {
return Props.create(TestActor2.class, new Creator<TestActor2>() {
private static final long serialVersionUID = 1L;
@Override
public UntypedTestActor create() throws Exception {
return new UntypedTestActor(magicNumber);
public TestActor2 create() throws Exception {
return new TestActor2(magicNumber);
}
});
}
public static Props propsUsingCreatorWithoutClass(final int magicNumber) {
return Props.create(new Creator<UntypedTestActor>() {
return Props.create(new Creator<TestActor2>() {
private static final long serialVersionUID = 1L;
@Override
public UntypedTestActor create() throws Exception {
return new UntypedTestActor(magicNumber);
public TestActor2 create() throws Exception {
return new TestActor2(magicNumber);
}
});
}
private static final Creator<UntypedTestActor> staticCreator = new Creator<UntypedTestActor>() {
private static final Creator<TestActor2> staticCreator = new Creator<TestActor2>() {
private static final long serialVersionUID = 1L;
@Override
public UntypedTestActor create() throws Exception {
return new UntypedTestActor(12);
public TestActor2 create() throws Exception {
return new TestActor2(12);
}
};
@ -140,7 +146,7 @@ public class ActorCreationTest extends JUnitSuite {
final int magicNumber;
UntypedTestActor(int magicNumber) {
TestActor2(int magicNumber) {
this.magicNumber = magicNumber;
}
@ -149,7 +155,7 @@ public class ActorCreationTest extends JUnitSuite {
}
}
public static class Issue20537Reproducer extends UntypedActor {
public static class Issue20537Reproducer extends UntypedAbstractActor {
static final class ReproducerCreator implements Creator<Issue20537Reproducer> {
@ -197,13 +203,13 @@ public class ActorCreationTest extends JUnitSuite {
} catch (IllegalArgumentException e) {
assertEquals("erased Creator types are unsupported, use Props.create(actorClass, creator) instead", e.getMessage());
}
Props.create(UntypedActor.class, new G());
Props.create(AbstractActor.class, new G());
}
@Test
public void testRightStaticCreator() {
final Props p = Props.create(new C());
assertEquals(UntypedActor.class, p.actorClass());
assertEquals(AbstractActor.class, p.actorClass());
}
@Test
@ -218,27 +224,27 @@ public class ActorCreationTest extends JUnitSuite {
@Test
public void testRightTopLevelNonStaticCreator() {
final Creator<UntypedActor> nonStatic = new NonStaticCreator();
final Creator<UntypedAbstractActor> nonStatic = new NonStaticCreator();
final Props p = Props.create(nonStatic);
assertEquals(UntypedActor.class, p.actorClass());
assertEquals(UntypedAbstractActor.class, p.actorClass());
}
@Test
public void testRightStaticParametricCreator() {
final Props p = Props.create(new D<UntypedActor>());
final Props p = Props.create(new D<AbstractActor>());
assertEquals(Actor.class, p.actorClass());
}
@Test
public void testRightStaticBoundedCreator() {
final Props p = Props.create(new E<UntypedActor>());
assertEquals(UntypedActor.class, p.actorClass());
final Props p = Props.create(new E<AbstractActor>());
assertEquals(AbstractActor.class, p.actorClass());
}
@Test
public void testRightStaticSuperinterface() {
final Props p = Props.create(new F());
assertEquals(UntypedActor.class, p.actorClass());
assertEquals(AbstractActor.class, p.actorClass());
}
@Test
@ -251,10 +257,10 @@ public class ActorCreationTest extends JUnitSuite {
}
}
private static Creator<UntypedActor> createAnonymousCreatorInStaticMethod() {
return new Creator<UntypedActor>() {
private static Creator<AbstractActor> createAnonymousCreatorInStaticMethod() {
return new Creator<AbstractActor>() {
@Override
public UntypedActor create() throws Exception {
public AbstractActor create() throws Exception {
return null;
}
};
@ -262,20 +268,20 @@ public class ActorCreationTest extends JUnitSuite {
@Test
public void testAnonymousClassCreatedInStaticMethodCreator() {
final Creator<UntypedActor> anonymousCreatorFromStaticMethod = createAnonymousCreatorInStaticMethod();
final Creator<AbstractActor> anonymousCreatorFromStaticMethod = createAnonymousCreatorInStaticMethod();
Props.create(anonymousCreatorFromStaticMethod);
}
@Test
public void testClassCreatorWithArguments() {
final Creator<UntypedActor> anonymousCreatorFromStaticMethod = new P("hello");
final Creator<AbstractActor> anonymousCreatorFromStaticMethod = new P("hello");
Props.create(anonymousCreatorFromStaticMethod);
}
@Test
public void testAnonymousClassCreatorWithArguments() {
try {
final Creator<UntypedActor> anonymousCreatorFromStaticMethod = new P("hello") {
final Creator<AbstractActor> anonymousCreatorFromStaticMethod = new P("hello") {
// captures enclosing class
};
Props.create(anonymousCreatorFromStaticMethod);
@ -306,14 +312,14 @@ public class ActorCreationTest extends JUnitSuite {
@Test
public void testRightPropsUsingCreator() {
final Props p = UntypedTestActor.propsUsingCreator(17);
assertEquals(UntypedTestActor.class, p.actorClass());
final Props p = TestActor2.propsUsingCreator(17);
assertEquals(TestActor2.class, p.actorClass());
}
@Test
public void testPropsUsingCreatorWithoutClass() {
final Props p = UntypedTestActor.propsUsingCreatorWithoutClass(17);
assertEquals(UntypedTestActor.class, p.actorClass());
final Props p = TestActor2.propsUsingCreatorWithoutClass(17);
assertEquals(TestActor2.class, p.actorClass());
}
@Test

View file

@ -137,7 +137,7 @@ public class JavaAPI extends JUnitSuite {
final Tuple4<Integer, String, Integer, Long> t4 = Tuple4.create(1, "2", 3, 4L);
Tuple22.create(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22);
}
@Test
public void mustBeAbleToCreateOptionFromOptional() {
Option<Object> empty = Util.option(Optional.ofNullable(null));
@ -147,7 +147,7 @@ public class JavaAPI extends JUnitSuite {
assertTrue(full.isDefined());
}
public static class ActorWithConstructorParams extends UntypedActor {
public static class ActorWithConstructorParams extends UntypedAbstractActor {
private final String a;
private final String b;
@ -189,7 +189,6 @@ public class JavaAPI extends JUnitSuite {
this.d = d;
}
@Override
public void onReceive(Object msg) {
String reply = String.valueOf(a) + "-" + String.valueOf(b) + "-" + String.valueOf(c) + "-" + String.valueOf(d);
getSender().tell(reply, getSelf());

View file

@ -1,6 +1,6 @@
package akka.actor;
public class JavaAPITestActor extends UntypedActor {
public class JavaAPITestActor extends UntypedAbstractActor {
public static String ANSWER = "got it!";
public void onReceive(Object msg) {

View file

@ -10,7 +10,7 @@ public class NonPublicClass {
}
}
class MyNonPublicActorClass extends UntypedActor {
class MyNonPublicActorClass extends UntypedAbstractActor {
@Override public void onReceive(Object msg) {
getSender().tell(msg, getSelf());
}

View file

@ -2,9 +2,9 @@ package akka.actor;
import akka.japi.Creator;
public class NonStaticCreator implements Creator<UntypedActor> {
public class NonStaticCreator implements Creator<UntypedAbstractActor> {
@Override
public UntypedActor create() throws Exception {
public UntypedAbstractActor create() throws Exception {
return null;
}
}

View file

@ -5,9 +5,9 @@ import static org.junit.Assert.*;
public class StashJavaAPITestActors {
/*
* Helper method to make the tests of UntypedActorWithStash, UntypedActorWithUnboundedStash and
* UntypedActorWithUnrestrictedStash more DRY since mixin is not possible.
*/
* Helper method to make the tests of AbstractActorWithStash, AbstractActorWithUnboundedStash and
* AbstractActorWithUnrestrictedStash more DRY since mixin is not possible.
*/
private static int testReceive(Object msg, int count, ActorRef sender, ActorRef self, UnrestrictedStash stash) {
if (msg instanceof String) {
if (count < 0) {
@ -26,28 +26,43 @@ public class StashJavaAPITestActors {
return count;
}
public static class WithStash extends UntypedActorWithStash {
int count = 0;
public static class WithStash extends AbstractActorWithStash {
int count = 0;
public void onReceive(Object msg) {
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Object.class, msg -> {
count = testReceive(msg, count, getSender(), getSelf(), this);
}
})
.build();
}
}
public static class WithUnboundedStash extends UntypedActorWithUnboundedStash {
int count = 0;
public static class WithUnboundedStash extends AbstractActorWithUnboundedStash {
int count = 0;
public void onReceive(Object msg) {
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Object.class, msg -> {
count = testReceive(msg, count, getSender(), getSelf(), this);
}
})
.build();
}
}
public static class WithUnrestrictedStash extends UntypedActorWithUnrestrictedStash {
int count = 0;
public static class WithUnrestrictedStash extends AbstractActorWithUnrestrictedStash {
int count = 0;
public void onReceive(Object msg) {
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Object.class, msg -> {
count = testReceive(msg, count, getSender(), getSelf(), this);
}
})
.build();
}
}
}

View file

@ -1,47 +1,50 @@
package akka.event;
import akka.actor.UntypedActor;
import akka.actor.AbstractActor;
import akka.japi.pf.ReceiveBuilder;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
public class ActorWithMDC extends UntypedActor {
public class ActorWithMDC extends AbstractActor {
private final DiagnosticLoggingAdapter logger = Logging.getLogger(this);
@Override
public void onReceive(Object message) throws Exception {
Log log = (Log) message;
@Override
public Receive createReceive() {
return receiveBuilder().match(Log.class, this::receiveLog).build();
}
Map<String, Object> mdc;
if(log.message.startsWith("No MDC")) {
mdc = Collections.emptyMap();
} else if(log.message.equals("Null MDC")) {
mdc = null;
} else {
mdc = new LinkedHashMap<String, Object>();
mdc.put("messageLength", log.message.length());
}
logger.setMDC(mdc);
switch (log.level()) {
case 1:
logger.error(log.message);
break;
case 2:
logger.warning(log.message);
break;
case 3:
logger.info(log.message);
break;
default:
logger.debug(log.message);
break;
}
logger.clearMDC();
private void receiveLog(Log log) {
Map<String, Object> mdc;
if (log.message.startsWith("No MDC")) {
mdc = Collections.emptyMap();
} else if (log.message.equals("Null MDC")) {
mdc = null;
} else {
mdc = new LinkedHashMap<String, Object>();
mdc.put("messageLength", log.message.length());
}
logger.setMDC(mdc);
switch (log.level()) {
case 1:
logger.error(log.message);
break;
case 2:
logger.warning(log.message);
break;
case 3:
logger.info(log.message);
break;
default:
logger.debug(log.message);
break;
}
logger.clearMDC();
}
public static class Log {
private final Object level;

View file

@ -51,7 +51,7 @@ public class MatchBuilderTest extends JUnitSuite {
@Test
public void shouldHandleMatchOnGenericClass() {
Match<Object, String> pf = Match.create(Match.match(GenericClass.class, new FI.Apply<GenericClass<String>, String>() {
Match<Object, String> pf = Match.create(Match.matchUnchecked(GenericClass.class, new FI.Apply<GenericClass<String>, String>() {
@Override
public String apply(GenericClass<String> stringGenericClass) {
return stringGenericClass.val;
@ -64,7 +64,7 @@ public class MatchBuilderTest extends JUnitSuite {
@Test
public void shouldHandleMatchWithPredicateOnGenericClass() {
Match<Object, String> pf = Match.create(Match.match(GenericClass.class, new FI.TypedPredicate<GenericClass<String>>() {
Match<Object, String> pf = Match.create(Match.matchUnchecked(GenericClass.class, new FI.TypedPredicate<GenericClass<String>>() {
@Override
public boolean defined(GenericClass<String> genericClass) {
return !genericClass.val.isEmpty();

View file

@ -10,6 +10,7 @@ import scala.PartialFunction;
import static org.junit.Assert.*;
@SuppressWarnings("serial")
public class PFBuilderTest extends JUnitSuite {
@Test
@ -18,7 +19,7 @@ public class PFBuilderTest extends JUnitSuite {
.matchEquals("hello", s -> 1)
.matchAny(s -> Integer.valueOf(s))
.build();
assertTrue(pf.isDefinedAt("hello"));
assertTrue(pf.isDefinedAt("42"));
assertEquals(42, pf.apply("42").intValue());

View file

@ -0,0 +1,261 @@
/**
* Copyright (C) 2017 Lightbend Inc. <http://www.lightbend.com>
*/
package akka.japi.pf;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.junit.Before;
import org.scalatest.junit.JUnitSuite;
import akka.actor.AbstractActor.Receive;
import static org.junit.Assert.*;
@SuppressWarnings("serial")
public class ReceiveBuilderTest extends JUnitSuite {
public static interface Msg {}
public static class Msg1 implements Msg {
@Override
public String toString() {
return "Msg1";
}
}
public static class Msg2 implements Msg {
public final String value;
public Msg2(String value) {
this.value = value;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((value == null) ? 0 : value.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Msg2 other = (Msg2) obj;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
@Override
public String toString() {
return "Msg2 [value=" + value + "]";
}
}
// using instance variable, because lambdas can only modify final fields
private String result = "";
private String result() {
String r = result;
result = "";
return r;
}
private void result(String r) {
result = r;
}
@Before
public void beforeEach() {
result = "";
}
@Test
public void shouldNotMatchWhenEmpty() {
Receive rcv = ReceiveBuilder.create().build();
assertFalse(rcv.onMessage().isDefinedAt("hello"));
assertFalse(rcv.onMessage().isDefinedAt(42));
}
@Test
public void shouldMatchByClass() {
Receive rcv = ReceiveBuilder.create()
.match(Msg1.class, m -> result("match Msg1"))
.build();
assertTrue(rcv.onMessage().isDefinedAt(new Msg1()));
rcv.onMessage().apply(new Msg1());
assertEquals("match Msg1", result());
assertFalse(rcv.onMessage().isDefinedAt(new Msg2("foo")));
assertFalse(rcv.onMessage().isDefinedAt("hello"));
assertFalse(rcv.onMessage().isDefinedAt(42));
}
@Test
public void shouldMatchBySubclass() {
Receive rcv = ReceiveBuilder.create()
.match(Msg.class, m -> result("match Msg"))
.build();
assertTrue(rcv.onMessage().isDefinedAt(new Msg1()));
rcv.onMessage().apply(new Msg1());
assertEquals("match Msg", result());
assertTrue(rcv.onMessage().isDefinedAt(new Msg2("foo")));
rcv.onMessage().apply(new Msg2("foo"));
assertEquals("match Msg", result());
assertFalse(rcv.onMessage().isDefinedAt("hello"));
assertFalse(rcv.onMessage().isDefinedAt(42));
}
private void handleMsg(Msg msg) {
result("match Msg");
}
private void handleMsg(Msg1 msg) {
result("match Msg1");
}
private void handleMsg(Msg2 msg) {
result("match Msg2");
}
@Test
public void shouldMatchDelegatingToSpecificMethod() {
Receive rcv = ReceiveBuilder.create()
.match(Msg1.class, this::handleMsg)
.match(Msg2.class, this::handleMsg)
.match(Msg.class, this::handleMsg)
.build();
assertTrue(rcv.onMessage().isDefinedAt(new Msg1()));
rcv.onMessage().apply(new Msg1());
assertEquals("match Msg1", result());
assertTrue(rcv.onMessage().isDefinedAt(new Msg2("foo")));
rcv.onMessage().apply(new Msg2("foo"));
assertEquals("match Msg2", result());
}
private void anotherHandleMsg(Msg msg) {
result("match Msg");
}
private void anotherHandleMsg(Msg1 msg) {
result("match Msg1");
}
@Test
public void shouldMatchDelegatingToGeneralMethod() {
Receive rcv = ReceiveBuilder.create()
.match(Msg1.class, this::anotherHandleMsg)
.match(Msg2.class, this::anotherHandleMsg)
.build();
assertTrue(rcv.onMessage().isDefinedAt(new Msg1()));
rcv.onMessage().apply(new Msg1());
assertEquals("match Msg1", result());
assertTrue(rcv.onMessage().isDefinedAt(new Msg2("foo")));
rcv.onMessage().apply(new Msg2("foo"));
assertEquals("match Msg", result());
}
@Test
public void shouldMatchByPredicate() {
Receive rcv = ReceiveBuilder.create()
.match(Msg1.class, m -> true, m -> result("match Msg1"))
.match(Msg2.class, m -> m.value.equals("foo"), m -> result("match Msg2"))
.build();
assertTrue(rcv.onMessage().isDefinedAt(new Msg1()));
rcv.onMessage().apply(new Msg1());
assertEquals("match Msg1", result());
assertTrue(rcv.onMessage().isDefinedAt(new Msg2("foo")));
rcv.onMessage().apply(new Msg2("foo"));
assertEquals("match Msg2", result());
assertFalse(rcv.onMessage().isDefinedAt(new Msg2("bar")));
assertFalse(rcv.onMessage().isDefinedAt("hello"));
assertFalse(rcv.onMessage().isDefinedAt(42));
}
@Test
public void shouldMatchEquals() {
Msg2 msg2 = new Msg2("foo");
Receive rcv = ReceiveBuilder.create()
.matchEquals(msg2, m -> result("match msg2"))
.matchEquals("foo", m -> result("match foo"))
.matchEquals(17, m -> result("match 17"))
.build();
assertTrue(rcv.onMessage().isDefinedAt(new Msg2("foo")));
rcv.onMessage().apply(new Msg2("foo"));
assertEquals("match msg2", result());
assertTrue(rcv.onMessage().isDefinedAt("foo"));
rcv.onMessage().apply("foo");
assertEquals("match foo", result());
assertTrue(rcv.onMessage().isDefinedAt(17));
rcv.onMessage().apply(17);
assertEquals("match 17", result());
assertFalse(rcv.onMessage().isDefinedAt(new Msg2("bar")));
assertFalse(rcv.onMessage().isDefinedAt("hello"));
assertFalse(rcv.onMessage().isDefinedAt(42));
}
@Test
public void shouldMatchAny() {
Receive rcv = ReceiveBuilder.create()
.match(Msg1.class, m -> result("match Msg1"))
.matchAny(m -> result("match any"))
.build();
assertTrue(rcv.onMessage().isDefinedAt(new Msg1()));
rcv.onMessage().apply(new Msg1());
assertEquals("match Msg1", result());
assertTrue(rcv.onMessage().isDefinedAt(new Msg2("foo")));
rcv.onMessage().apply(new Msg2("foo"));
assertEquals("match any", result());
assertTrue(rcv.onMessage().isDefinedAt("hello"));
assertTrue(rcv.onMessage().isDefinedAt(42));
}
@Test
public void shouldMatchUnchecked() {
Receive rcv = ReceiveBuilder.create()
.matchUnchecked(List.class, (List<String> list) -> {
result("match List");
})
.build();
List<String> list = Arrays.asList("foo");
assertTrue(rcv.onMessage().isDefinedAt(list));
rcv.onMessage().apply(list);
assertEquals("match List", result());
}
@Test(expected = ClassCastException.class)
public void shouldThrowWhenUncheckedWithWrongTypes() {
// note that this doesn't compile with ordinary match
Receive rcv = ReceiveBuilder.create()
.matchUnchecked(String.class, (Integer i) -> {
result(String.valueOf(i + 2));
})
.build();
assertTrue(rcv.onMessage().isDefinedAt("foo"));
rcv.onMessage().apply("foo");
}
}

View file

@ -39,7 +39,7 @@ public class PatternsTest extends JUnitSuite {
ActorRef testActor = system.actorOf(Props.create(JavaAPITestActor.class), "test2");
ActorSelection selection = system.actorSelection("/user/test2");
ActorIdentity id = (ActorIdentity) Await.result(ask(selection, new Identify("yo!"), 3000), Duration.create(3, "seconds"));
assertEquals("Ask (Identify) should return the proper ActorIdentity", testActor, id.getRef());
assertEquals("Ask (Identify) should return the proper ActorIdentity", testActor, id.getActorRef().get());
}
@Test

View file

@ -51,15 +51,15 @@ object LoggerSpec {
}
""").withFallback(AkkaSpec.testConf)
val ticket3165Config = ConfigFactory.parseString("""
val ticket3165Config = ConfigFactory.parseString(s"""
akka {
stdout-loglevel = "WARNING"
loglevel = "DEBUG"
loggers = ["akka.event.LoggerSpec$TestLogger1"]
loggers = ["akka.event.LoggerSpec$$TestLogger1"]
actor {
serialize-messages = on
serialization-bindings {
"akka.event.Logging$LogEvent" = bytes
"akka.event.Logging$$LogEvent" = bytes
"java.io.Serializable" = java
}
}

View file

@ -88,8 +88,9 @@ object SerializationTests {
}
}
class FooUntypedActor extends UntypedActor {
def onReceive(message: Any) {}
class FooAbstractActor extends AbstractActor {
override def createReceive(): AbstractActor.Receive =
receiveBuilder().build()
}
class NonSerializableActor(system: ActorSystem) extends Actor {
@ -286,7 +287,7 @@ class VerifySerializabilitySpec extends AkkaSpec(SerializationTests.verifySerial
val a = system.actorOf(Props[FooActor])
system stop a
val b = system.actorOf(Props(new FooActor))
val b = system.actorOf(Props(new FooAbstractActor))
system stop b
intercept[IllegalArgumentException] {

View file

@ -12,8 +12,6 @@ import scala.PartialFunction;
*
* @param <I> the input type, that this PartialFunction will be applied to
* @param <R> the return type, that the results of the application will have
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
class AbstractMatch<I, R> {

View file

@ -12,7 +12,6 @@ import scala.PartialFunction;
* @param <F> the input type, that this PartialFunction will be applied to
* @param <T> the return type, that the results of the application will have
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
abstract class AbstractPFBuilder<F, T> {

View file

@ -28,7 +28,6 @@ import static akka.actor.SupervisorStrategy.Directive;
* }
* </pre>
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
public class DeciderBuilder {
private DeciderBuilder() {

View file

@ -8,7 +8,6 @@ package akka.japi.pf;
* Class that encapsulates all the Functional Interfaces
* used for creating partial functions.
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
public final class FI {
private FI() {

View file

@ -14,7 +14,6 @@ import java.util.List;
* @param <S> the state type
* @param <D> the data type
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
@SuppressWarnings("rawtypes")
public class FSMStateFunctionBuilder<S, D> {

View file

@ -14,7 +14,6 @@ import scala.runtime.BoxedUnit;
* @param <S> the state type
* @param <D> the data type
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
public class FSMStopBuilder<S, D> {

View file

@ -13,11 +13,10 @@ import scala.Tuple2;
*
* @param <S> the state type
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
public class FSMTransitionHandlerBuilder<S> {
private UnitPFBuilder<Tuple2<S, S>> builder =
private final UnitPFBuilder<Tuple2<S, S>> builder =
new UnitPFBuilder<Tuple2<S, S>>();
/**

View file

@ -14,7 +14,6 @@ import scala.PartialFunction;
* @param <I> the input type, that this PartialFunction will be applied to
* @param <R> the return type, that the results of the application will have
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
public class Match<I, R> extends AbstractMatch<I, R> {
@ -27,11 +26,25 @@ public class Match<I, R> extends AbstractMatch<I, R> {
* @return a builder with the case statement added
* @see PFBuilder#match(Class, FI.Apply)
*/
public static <F, T, P> PFBuilder<F, T> match(final Class<? extends P> type,
final FI.Apply<? extends P, T> apply) {
public static <F, T, P> PFBuilder<F, T> match(final Class<P> type,
final FI.Apply<P, T> apply) {
return new PFBuilder<F, T>().match(type, apply);
}
/**
* Convenience function to create a {@link PFBuilder} with the first
* case statement added without compile time type check of the parameters.
* Should normally not be used, but when matching on class with generic type
* argument it can be useful, e.g. <code>List.class</code> and
* <code>(List&lt;String&gt; list) -> {}</code>.
*
* @see PFBuilder#matchUnchecked(Class, FI.Apply)
*/
public static <F, T> PFBuilder<F, T> matchUnchecked(final Class<?> type,
final FI.Apply<?, T> apply) {
return new PFBuilder<F, T>().matchUnchecked(type, apply);
}
/**
* Convenience function to create a {@link PFBuilder} with the first
* case statement added.
@ -42,12 +55,27 @@ public class Match<I, R> extends AbstractMatch<I, R> {
* @return a builder with the case statement added
* @see PFBuilder#match(Class, FI.TypedPredicate, FI.Apply)
*/
public static <F, T, P> PFBuilder<F, T> match(final Class<? extends P> type,
final FI.TypedPredicate<? extends P> predicate,
final FI.Apply<? extends P, T> apply) {
public static <F, T, P> PFBuilder<F, T> match(final Class<P> type,
final FI.TypedPredicate<P> predicate,
final FI.Apply<P, T> apply) {
return new PFBuilder<F, T>().match(type, predicate, apply);
}
/**
* Convenience function to create a {@link PFBuilder} with the first
* case statement added without compile time type check of the parameters.
* Should normally not be used, but when matching on class with generic type
* argument it can be useful, e.g. <code>List.class</code> and
* <code>(List&lt;String&gt; list) -> {}</code>.
*
* @see PFBuilder#matchUnchecked(Class, FI.TypedPredicate, FI.Apply)
*/
public static <F, T> PFBuilder<F, T> matchUnchecked(final Class<?> type,
final FI.TypedPredicate<?> predicate,
final FI.Apply<?, T> apply) {
return new PFBuilder<F, T>().matchUnchecked(type, predicate, apply);
}
/**
* Convenience function to create a {@link PFBuilder} with the first
* case statement added.
@ -91,10 +119,10 @@ public class Match<I, R> extends AbstractMatch<I, R> {
/**
* Convenience function to make the Java code more readable.
* <p></p>
*
*
* <pre><code>
* Matcher&lt;X, Y&gt; matcher = Matcher.create(...);
*
*
* Y someY = matcher.match(obj);
* </code></pre>
*

View file

@ -10,7 +10,6 @@ package akka.japi.pf;
* @param <I> the input type, that this PartialFunction will be applied to
* @param <R> the return type, that the results of the application will have
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
public final class PFBuilder<I, R> extends AbstractPFBuilder<I, R> {
@ -27,8 +26,22 @@ public final class PFBuilder<I, R> extends AbstractPFBuilder<I, R> {
* @param apply an action to apply to the argument if the type matches
* @return a builder with the case statement added
*/
public <P> PFBuilder<I, R> match(final Class<P> type, FI.Apply<P, R> apply) {
return matchUnchecked(type, apply);
}
/**
* Add a new case statement to this builder without compile time type check of the parameters.
* Should normally not be used, but when matching on class with generic type
* argument it can be useful, e.g. <code>List.class</code> and
* <code>(List&lt;String&gt; list) -> {}</code>.
*
* @param type a type to match the argument against
* @param apply an action to apply to the argument if the type matches
* @return a builder with the case statement added
*/
@SuppressWarnings("unchecked")
public <P> PFBuilder<I, R> match(final Class<? extends P> type, FI.Apply<? extends P, R> apply) {
public PFBuilder<I, R> matchUnchecked(final Class<?> type, FI.Apply<?, R> apply) {
FI.Predicate predicate = new FI.Predicate() {
@Override
@ -37,7 +50,7 @@ public final class PFBuilder<I, R> extends AbstractPFBuilder<I, R> {
}
};
addStatement(new CaseStatement<I, P, R>(predicate, (FI.Apply<P, R>) apply));
addStatement(new CaseStatement<I, Object, R>(predicate, (FI.Apply<Object, R>) apply));
return this;
}
@ -49,23 +62,37 @@ public final class PFBuilder<I, R> extends AbstractPFBuilder<I, R> {
* @param apply an action to apply to the argument if the type matches and the predicate returns true
* @return a builder with the case statement added
*/
public <P> PFBuilder<I, R> match(final Class<P> type,
final FI.TypedPredicate<P> predicate,
final FI.Apply<P, R> apply) {
return matchUnchecked(type, predicate, apply);
}
/**
* Add a new case statement to this builder without compile time type check of the parameters.
* Should normally not be used, but when matching on class with generic type
* argument it can be useful, e.g. <code>List.class</code> and
* <code>(List&lt;String&gt; list) -> {}</code>.
*
* @param type a type to match the argument against
* @param predicate a predicate that will be evaluated on the argument if the type matches
* @param apply an action to apply to the argument if the type matches and the predicate returns true
* @return a builder with the case statement added
*/
@SuppressWarnings("unchecked")
public <P> PFBuilder<I, R> match(final Class<? extends P> type,
final FI.TypedPredicate<? extends P> predicate,
final FI.Apply<? extends P, R> apply) {
public PFBuilder<I, R> matchUnchecked(final Class<?> type,
final FI.TypedPredicate<?> predicate,
final FI.Apply<?, R> apply) {
FI.Predicate fiPredicate = new FI.Predicate() {
@Override
public boolean defined(Object o) {
if (!type.isInstance(o))
return false;
else {
@SuppressWarnings("unchecked")
P p = (P) o;
return ((FI.TypedPredicate<P>) predicate).defined(p);
}
else
return ((FI.TypedPredicate<Object>) predicate).defined(o);
}
};
addStatement(new CaseStatement<I, P, R>(fiPredicate, (FI.Apply<P, R>) apply));
addStatement(new CaseStatement<I, Object, R>(fiPredicate, (FI.Apply<Object, R>) apply));
return this;
}

View file

@ -4,6 +4,11 @@
package akka.japi.pf;
import scala.PartialFunction;
import scala.runtime.BoxedUnit;
import akka.actor.AbstractActor;
import akka.actor.AbstractActor.Receive;
/**
* Used for building a partial function for {@link akka.actor.Actor#receive() Actor.receive()}.
*
@ -30,80 +35,198 @@ package akka.japi.pf;
* }
* </pre>
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
public class ReceiveBuilder {
private ReceiveBuilder() {
private PartialFunction<Object, BoxedUnit> statements = null;
protected void addStatement(PartialFunction<Object, BoxedUnit> statement) {
if (statements == null)
statements = statement;
else
statements = statements.orElse(statement);
}
/**
* Return a new {@link UnitPFBuilder} with no case statements. They can be added later as the returned {@link
* UnitPFBuilder} is a mutable object.
* Build a {@link scala.PartialFunction} from this builder. After this call
* the builder will be reset.
*
* @return a PartialFunction for this builder.
*/
public Receive build() {
PartialFunction<Object, BoxedUnit> empty = CaseStatement.empty();
if (statements == null)
return new Receive(empty);
else
return new Receive(statements.orElse(empty)); // FIXME why no new Receive(statements)?
}
/**
* Return a new {@link ReceiveBuilder} with no case statements. They can be
* added later as the returned {@link ReceiveBuilder} is a mutable object.
*
* @return a builder with no case statements
*/
public static UnitPFBuilder<Object> create() {
return new UnitPFBuilder<>();
public static ReceiveBuilder create() {
return new ReceiveBuilder();
}
/**
* Return a new {@link UnitPFBuilder} with a case statement added.
* Add a new case statement to this builder.
*
* @param type a type to match the argument against
* @param apply an action to apply to the argument if the type matches
* @param type
* a type to match the argument against
* @param apply
* an action to apply to the argument if the type matches
* @return a builder with the case statement added
*/
public static <P> UnitPFBuilder<Object> match(final Class<? extends P> type, FI.UnitApply<? extends P> apply) {
return UnitMatch.match(type, apply);
public <P> ReceiveBuilder match(final Class<P> type, final FI.UnitApply<P> apply) {
return matchUnchecked(type, apply);
}
/**
* Return a new {@link UnitPFBuilder} with a case statement added.
* Add a new case statement to this builder without compile time type check.
* Should normally not be used, but when matching on class with generic type
* argument it can be useful, e.g. <code>List.class</code> and
* <code>(List&lt;String&gt; list) -> {}</code>.
*
* @param type a type to match the argument against
* @param predicate a predicate that will be evaluated on the argument if the type matches
* @param apply an action to apply to the argument if the type matches and the predicate returns true
* @param type
* a type to match the argument against
* @param apply
* an action to apply to the argument if the type matches
* @return a builder with the case statement added
*/
public static <P> UnitPFBuilder<Object> match(final Class<? extends P> type,
FI.TypedPredicate<? extends P> predicate,
FI.UnitApply<? extends P> apply) {
return UnitMatch.match(type, predicate, apply);
@SuppressWarnings("unchecked")
public ReceiveBuilder matchUnchecked(final Class<?> type, final FI.UnitApply<?> apply) {
FI.Predicate predicate = new FI.Predicate() {
@Override
public boolean defined(Object o) {
return type.isInstance(o);
}
};
addStatement(new UnitCaseStatement<Object, Object>(predicate, (FI.UnitApply<Object>) apply));
return this;
}
/**
* Return a new {@link UnitPFBuilder} with a case statement added.
* Add a new case statement to this builder.
*
* @param object the object to compare equals with
* @param apply an action to apply to the argument if the object compares equal
* @param type
* a type to match the argument against
* @param predicate
* a predicate that will be evaluated on the argument if the type
* matches
* @param apply
* an action to apply to the argument if the type matches and the
* predicate returns true
* @return a builder with the case statement added
*/
public static <P> UnitPFBuilder<Object> matchEquals(P object, FI.UnitApply<P> apply) {
return UnitMatch.matchEquals(object, apply);
public <P> ReceiveBuilder match(final Class<P> type, final FI.TypedPredicate<P> predicate,
final FI.UnitApply<P> apply) {
return matchUnchecked(type, predicate, apply);
}
/**
* Add a new case statement to this builder without compile time type check.
* Should normally not be used, but when matching on class with generic type
* argument it can be useful, e.g. <code>List.class</code> and
* <code>(List&lt;String&gt; list) -> {}</code>.
*
* @param type
* a type to match the argument against
* @param predicate
* a predicate that will be evaluated on the argument if the type
* matches
* @param apply
* an action to apply to the argument if the type matches and the
* predicate returns true
* @return a builder with the case statement added
*/
@SuppressWarnings("unchecked")
public <P> ReceiveBuilder matchUnchecked(final Class<?> type, final FI.TypedPredicate<?> predicate,
final FI.UnitApply<P> apply) {
FI.Predicate fiPredicate = new FI.Predicate() {
@Override
public boolean defined(Object o) {
if (!type.isInstance(o))
return false;
else
return ((FI.TypedPredicate<Object>) predicate).defined(o);
}
};
addStatement(new UnitCaseStatement<Object, Object>(fiPredicate, (FI.UnitApply<Object>) apply));
return this;
}
/**
* Return a new {@link UnitPFBuilder} with a case statement added.
* Add a new case statement to this builder.
*
* @param object the object to compare equals with
* @param predicate a predicate that will be evaluated on the argument if the object compares equal
* @param apply an action to apply to the argument if the object compares equal
* @param object
* the object to compare equals with
* @param apply
* an action to apply to the argument if the object compares equal
* @return a builder with the case statement added
*/
public static <P> UnitPFBuilder<Object> matchEquals(P object,
FI.TypedPredicate<P> predicate,
FI.UnitApply<P> apply) {
return UnitMatch.matchEquals(object, predicate, apply);
public <P> ReceiveBuilder matchEquals(final P object, final FI.UnitApply<P> apply) {
addStatement(new UnitCaseStatement<Object, P>(new FI.Predicate() {
@Override
public boolean defined(Object o) {
return object.equals(o);
}
}, apply));
return this;
}
/**
* Return a new {@link UnitPFBuilder} with a case statement added.
* Add a new case statement to this builder.
*
* @param apply an action to apply to the argument
* @param object
* the object to compare equals with
* @param predicate
* a predicate that will be evaluated on the argument if the object
* compares equal
* @param apply
* an action to apply to the argument if the object compares equal
* @return a builder with the case statement added
*/
public static UnitPFBuilder<Object> matchAny(FI.UnitApply<Object> apply) {
return UnitMatch.matchAny(apply);
public <P> ReceiveBuilder matchEquals(final P object, final FI.TypedPredicate<P> predicate,
final FI.UnitApply<P> apply) {
addStatement(new UnitCaseStatement<Object, P>(new FI.Predicate() {
@Override
public boolean defined(Object o) {
if (!object.equals(o))
return false;
else {
@SuppressWarnings("unchecked")
P p = (P) o;
return predicate.defined(p);
}
}
}, apply));
return this;
}
/**
* Add a new case statement to this builder, that matches any argument.
*
* @param apply
* an action to apply to the argument
* @return a builder with the case statement added
*/
public ReceiveBuilder matchAny(final FI.UnitApply<Object> apply) {
addStatement(new UnitCaseStatement<Object, Object>(new FI.Predicate() {
@Override
public boolean defined(Object o) {
return true;
}
}, apply));
return this;
}
}

View file

@ -16,7 +16,6 @@ import scala.runtime.BoxedUnit;
*
* @param <I> the input type, that this PartialFunction will be applied to
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
public class UnitMatch<I> extends AbstractMatch<I, BoxedUnit> {
@ -29,10 +28,20 @@ public class UnitMatch<I> extends AbstractMatch<I, BoxedUnit> {
* @return a builder with the case statement added
* @see UnitPFBuilder#match(Class, FI.UnitApply)
*/
public static <F, P> UnitPFBuilder<F> match(final Class<? extends P> type, FI.UnitApply<? extends P> apply) {
public static <F, P> UnitPFBuilder<F> match(final Class<P> type, FI.UnitApply<P> apply) {
return new UnitPFBuilder<F>().match(type, apply);
}
/**
* Convenience function to create a {@link UnitPFBuilder} with the first
* case statement added. Should normally not be used.
*
* @see UnitPFBuilder#matchUnchecked(Class, FI.UnitApply)
*/
public static UnitPFBuilder<Object> matchUnchecked(final Class<?> type, final FI.UnitApply<?> apply) {
return new UnitPFBuilder<Object>().matchUnchecked(type, apply);
}
/**
* Convenience function to create a {@link UnitPFBuilder} with the first
* case statement added.
@ -43,12 +52,24 @@ public class UnitMatch<I> extends AbstractMatch<I, BoxedUnit> {
* @return a builder with the case statement added
* @see UnitPFBuilder#match(Class, FI.TypedPredicate, FI.UnitApply)
*/
public static <F, P> UnitPFBuilder<F> match(final Class<? extends P> type,
final FI.TypedPredicate<? extends P> predicate,
final FI.UnitApply<? extends P> apply) {
public static <F, P> UnitPFBuilder<F> match(final Class<P> type,
final FI.TypedPredicate<P> predicate,
final FI.UnitApply<P> apply) {
return new UnitPFBuilder<F>().match(type, predicate, apply);
}
/**
* Convenience function to create a {@link UnitPFBuilder} with the first
* case statement added. Should normally not be used.
*
* @see UnitPFBuilder#matchUnchecked(Class, FI.TypedPredicate, FI.UnitApply)
*/
public static <F, P> UnitPFBuilder<F> matchUnchecked(final Class<?> type,
final FI.TypedPredicate<?> predicate,
final FI.UnitApply<?> apply) {
return new UnitPFBuilder<F>().matchUnchecked(type, predicate, apply);
}
/**
* Convenience function to create a {@link UnitPFBuilder} with the first
* case statement added.
@ -110,7 +131,7 @@ public class UnitMatch<I> extends AbstractMatch<I, BoxedUnit> {
* <p>
* <pre><code>
* UnitMatcher&lt;X&gt; matcher = UnitMatcher.create(...);
*
*
* matcher.match(obj);
* </code></pre>
*

View file

@ -13,7 +13,6 @@ import scala.runtime.BoxedUnit;
*
* @param <I> the input type, that this PartialFunction to be applied to
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
public final class UnitPFBuilder<I> extends AbstractPFBuilder<I, BoxedUnit> {
@ -30,9 +29,25 @@ public final class UnitPFBuilder<I> extends AbstractPFBuilder<I, BoxedUnit> {
* @param apply an action to apply to the argument if the type matches
* @return a builder with the case statement added
*/
public <P> UnitPFBuilder<I> match(final Class<P> type,
final FI.UnitApply<P> apply) {
return matchUnchecked(type, apply);
}
/**
* Add a new case statement to this builder without compile time type check.
* Should normally not be used, but when matching on class with generic type
* argument it can be useful, e.g. <code>List.class</code> and
* <code>(List&lt;String&gt; list) -> {}</code>.
*
* @param type
* a type to match the argument against
* @param apply
* an action to apply to the argument if the type matches
* @return a builder with the case statement added
*/
@SuppressWarnings("unchecked")
public <P> UnitPFBuilder<I> match(final Class<? extends P> type,
final FI.UnitApply<? extends P> apply) {
public UnitPFBuilder<I> matchUnchecked(final Class<?> type, final FI.UnitApply<?> apply) {
FI.Predicate predicate = new FI.Predicate() {
@Override
@ -41,7 +56,7 @@ public final class UnitPFBuilder<I> extends AbstractPFBuilder<I, BoxedUnit> {
}
};
addStatement(new UnitCaseStatement<I, P>(predicate, (FI.UnitApply<P>) apply));
addStatement(new UnitCaseStatement<I, Object>(predicate, (FI.UnitApply<Object>) apply));
return this;
}
@ -54,24 +69,39 @@ public final class UnitPFBuilder<I> extends AbstractPFBuilder<I, BoxedUnit> {
* @param apply an action to apply to the argument if the type matches and the predicate returns true
* @return a builder with the case statement added
*/
public <P> UnitPFBuilder<I> match(final Class<P> type,
final FI.TypedPredicate<P> predicate,
final FI.UnitApply<P> apply) {
return matchUnchecked(type, predicate, apply);
}
/**
* Add a new case statement to this builder without compile time type check.
* Should normally not be used, but when matching on class with generic type
* argument it can be useful, e.g. <code>List.class</code> and
* <code>(List&lt;String&gt; list) -> {}</code>.
*
* @param type a type to match the argument against
* @param predicate a predicate that will be evaluated on the argument if the type matches
* @param apply an action to apply to the argument if the type matches and the predicate returns true
* @return a builder with the case statement added
*/
@SuppressWarnings("unchecked")
public <P> UnitPFBuilder<I> match(final Class<? extends P> type,
final FI.TypedPredicate<? extends P> predicate,
final FI.UnitApply<? extends P> apply) {
public UnitPFBuilder<I> matchUnchecked(final Class<?> type,
final FI.TypedPredicate<?> predicate,
final FI.UnitApply<?> apply) {
FI.Predicate fiPredicate = new FI.Predicate() {
@Override
public boolean defined(Object o) {
if (!type.isInstance(o))
return false;
else {
@SuppressWarnings("unchecked")
P p = (P) o;
return ((FI.TypedPredicate<P>) predicate).defined(p);
return ((FI.TypedPredicate<Object>) predicate).defined(o);
}
}
};
addStatement(new UnitCaseStatement<I, P>(fiPredicate, (FI.UnitApply<P>) apply));
addStatement(new UnitCaseStatement<I, Object>(fiPredicate, (FI.UnitApply<Object>) apply));
return this;
}

View file

@ -5,17 +5,82 @@
package akka.actor
import akka.annotation.ApiMayChange
import akka.japi.pf.ReceiveBuilder
import akka.japi.pf.UnitPFBuilder
import scala.runtime.BoxedUnit
import java.util.Optional
/**
* Java API: compatible with lambda expressions
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
object AbstractActor {
/**
* Defines which messages the Actor can handle, along with the implementation of
* how the messages should be processed. You can build such behavior with the
* [[akka.japi.pf.receivebuilder]] but it can be implemented in other ways than
* using the `ReceiveBuilder` since it in the end is just a wrapper around a
* Scala `PartialFunction`. In Java, you can implement `PartialFunction` by
* extending `AbstractPartialFunction`.
*/
final class Receive(val onMessage: PartialFunction[Any, BoxedUnit]) {
/**
* Composes this `Receive` with a fallback which gets applied
* where this partial function is not defined.
*/
def orElse(other: Receive): Receive = new Receive(onMessage.orElse(other.onMessage))
}
/**
* emptyBehavior is a Receive-expression that matches no messages at all, ever.
*/
final val emptyBehavior = Actor.emptyBehavior
final val emptyBehavior: Receive = new Receive(PartialFunction.empty)
/**
* The actor context - the view of the actor cell from the actor.
* Exposes contextual information for the actor and the current message.
*/
trait ActorContext extends akka.actor.ActorContext {
/**
* Returns an unmodifiable Java Collection containing the linked actors,
* please note that the backing map is thread-safe but not immutable
*/
def getChildren(): java.lang.Iterable[ActorRef]
/**
* Returns a reference to the named child or null if no child with
* that name exists.
*/
@deprecated("Use findChild instead", "2.5.0")
def getChild(name: String): ActorRef
/**
* Returns a reference to the named child if it exists.
*/
def findChild(name: String): Optional[ActorRef]
/**
* Changes the Actor's behavior to become the new 'Receive' handler.
* Replaces the current behavior on the top of the behavior stack.
*/
def become(behavior: Receive): Unit =
become(behavior, discardOld = true)
/**
* Changes the Actor's behavior to become the new 'Receive' handler.
* This method acts upon the behavior stack as follows:
*
* - if `discardOld = true` it will replace the top element (i.e. the current behavior)
* - if `discardOld = false` it will keep the current behavior and push the given one atop
*
* The default of replacing the current behavior on the stack has been chosen to avoid memory
* leaks in case client code is written without consulting this documentation first (i.e.
* always pushing new behaviors and never issuing an `unbecome()`)
*/
def become(behavior: Receive, discardOld: Boolean): Unit =
become(behavior.onMessage.asInstanceOf[PartialFunction[Any, Unit]], discardOld)
}
}
/**
@ -29,49 +94,151 @@ object AbstractActor {
* int count = 0;
*
* public MyActor() {
* receive(ReceiveBuilder.
* match(Double.class, d -> {
* receive(receiveBuilder()
* .match(Double.class, d -> {
* sender().tell(d.isNaN() ? 0 : d, self());
* }).
* match(Integer.class, i -> {
* })
* .match(Integer.class, i -> {
* sender().tell(i * 10, self());
* }).
* match(String.class, s -> s.startsWith("foo"), s -> {
* })
* .match(String.class, s -> s.startsWith("foo"), s -> {
* sender().tell(s.toUpperCase(), self());
* }).build()
* })
* .build();
* );
* }
* }
* </pre>
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
@ApiMayChange
abstract class AbstractActor extends Actor {
private var _receive: Receive = null
/**
* Set up the initial receive behavior of the Actor.
*
* @param receive The receive behavior.
*/
@throws(classOf[IllegalActorStateException])
protected def receive(receive: Receive): Unit =
if (_receive == null) _receive = receive
else throw IllegalActorStateException("Actor behavior has already been set with receive(...), " +
"use context().become(...) to change it later")
/**
* Returns this AbstractActor's AbstractActorContext
* The AbstractActorContext is not thread safe so do not expose it outside of the
* Returns this AbstractActor's ActorContext
* The ActorContext is not thread safe so do not expose it outside of the
* AbstractActor.
*/
def getContext(): AbstractActorContext = context.asInstanceOf[AbstractActorContext]
def getContext(): AbstractActor.ActorContext = context.asInstanceOf[AbstractActor.ActorContext]
override def receive =
if (_receive != null) _receive
else throw IllegalActorStateException("Actor behavior has not been set with receive(...)")
/**
* Returns the ActorRef for this actor.
*
* Same as `self()`.
*/
def getSelf(): ActorRef = self
/**
* The reference sender Actor of the currently processed message. This is
* always a legal destination to send to, even if there is no logical recipient
* for the reply, in which case it will be sent to the dead letter mailbox.
*
* Same as `sender()`.
*
* WARNING: Only valid within the Actor itself, so do not close over it and
* publish it to other threads!
*/
def getSender(): ActorRef = sender()
/**
* User overridable definition the strategy to use for supervising
* child actors.
*/
override def supervisorStrategy: SupervisorStrategy = super.supervisorStrategy
/**
* User overridable callback.
* <p/>
* Is called when an Actor is started.
* Actor are automatically started asynchronously when created.
* Empty default implementation.
*/
@throws(classOf[Exception])
override def preStart(): Unit = super.preStart()
/**
* User overridable callback.
* <p/>
* Is called asynchronously after `getContext().stop()` is invoked.
* Empty default implementation.
*/
@throws(classOf[Exception])
override def postStop(): Unit = super.postStop()
// TODO In 2.6.0 we can remove deprecation and make the method final
@deprecated("Override preRestart with message parameter with Optional type instead", "2.5.0")
@throws(classOf[Exception])
override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
import scala.compat.java8.OptionConverters._
preRestart(reason, message.asJava)
}
/**
* User overridable callback: '''By default it disposes of all children and then calls `postStop()`.'''
* <p/>
* Is called on a crashed Actor right BEFORE it is restarted to allow clean
* up of resources before Actor is terminated.
*/
@throws(classOf[Exception])
def preRestart(reason: Throwable, message: Optional[Any]): Unit = {
import scala.compat.java8.OptionConverters._
super.preRestart(reason, message.asScala)
}
/**
* User overridable callback: By default it calls `preStart()`.
* <p/>
* Is called right AFTER restart on the newly created Actor to allow reinitialization after an Actor crash.
*/
@throws(classOf[Exception])
override def postRestart(reason: Throwable): Unit = super.postRestart(reason)
/**
* An actor has to define its initial receive behavior by implementing
* the `createReceive` method.
*/
def createReceive(): AbstractActor.Receive
override def receive: PartialFunction[Any, Unit] =
createReceive().onMessage.asInstanceOf[PartialFunction[Any, Unit]]
/**
* Convenience factory of the `ReceiveBuilder`.
* Creates a new empty `ReceiveBuilder`.
*/
final def receiveBuilder(): ReceiveBuilder = ReceiveBuilder.create()
}
/**
* If the validation of the `ReceiveBuilder` match logic turns out to be a bottleneck for some of your
* actors you can consider to implement it at lower level by extending `UntypedAbstractActor` instead
* of `AbstractActor`. The partial functions created by the `ReceiveBuilder` consist of multiple lambda
* expressions for every match statement, where each lambda is referencing the code to be run. This is something
* that the JVM can have problems optimizing and the resulting code might not be as performant as the
* untyped version. When extending `UntypedAbstractActor` each message is received as an untyped
* `Object` and you have to inspect and cast it to the actual message type in other ways (instanceof checks).
*/
abstract class UntypedAbstractActor extends AbstractActor {
final override def createReceive(): AbstractActor.Receive =
throw new UnsupportedOperationException("createReceive should not be used by UntypedAbstractActor")
override def receive: PartialFunction[Any, Unit] = { case msg onReceive(msg) }
/**
* To be implemented by concrete UntypedAbstractActor, this defines the behavior of the
* actor.
*/
@throws(classOf[Throwable])
def onReceive(message: Any): Unit
/**
* Recommended convention is to call this method if the message
* isn't handled in [[#onReceive]] (e.g. unknown message type).
* By default it fails with either a [[akka.actor.DeathPactException]] (in
* case of an unhandled [[akka.actor.Terminated]] message) or publishes an [[akka.actor.UnhandledMessage]]
* to the actor's system's [[akka.event.EventStream]].
*/
override def unhandled(message: Any): Unit = super.unhandled(message)
}
/**
@ -79,7 +246,6 @@ abstract class AbstractActor extends Actor {
*
* Actor base class that mixes in logging into the Actor.
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
abstract class AbstractLoggingActor extends AbstractActor with ActorLogging
@ -126,7 +292,6 @@ abstract class AbstractLoggingActor extends AbstractActor with ActorLogging
* There is also an unrestricted version [[akka.actor.AbstractActorWithUnrestrictedStash]] that does not
* enforce the mailbox type.
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
abstract class AbstractActorWithStash extends AbstractActor with Stash
@ -137,7 +302,6 @@ abstract class AbstractActorWithStash extends AbstractActor with Stash
* manually, and the mailbox should extend the [[akka.dispatch.DequeBasedMessageQueueSemantics]] marker trait.
* See [[akka.actor.AbstractActorWithStash]] for details on how `Stash` works.
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
abstract class AbstractActorWithUnboundedStash extends AbstractActor with UnboundedStash
@ -147,6 +311,5 @@ abstract class AbstractActorWithUnboundedStash extends AbstractActor with Unboun
* Actor base class with `Stash` that does not enforce any mailbox type. The mailbox of the actor has to be configured
* manually. See [[akka.actor.AbstractActorWithStash]] for details on how `Stash` works.
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
abstract class AbstractActorWithUnrestrictedStash extends AbstractActor with UnrestrictedStash

View file

@ -11,9 +11,7 @@ import scala.concurrent.duration.FiniteDuration
/**
* Java API: compatible with lambda expressions
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
@ApiMayChange
object AbstractFSM {
/**
* A partial function value which does not match anything and can be used to
@ -31,7 +29,6 @@ object AbstractFSM {
*
* Finite State Machine actor abstract base class.
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
abstract class AbstractFSM[S, D] extends FSM[S, D] {
import akka.japi.pf._
@ -382,7 +379,6 @@ abstract class AbstractFSM[S, D] extends FSM[S, D] {
*
* Finite State Machine actor abstract base class.
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
abstract class AbstractLoggingFSM[S, D] extends AbstractFSM[S, D] with LoggingFSM[S, D]
@ -391,6 +387,5 @@ abstract class AbstractLoggingFSM[S, D] extends AbstractFSM[S, D] with LoggingFS
*
* Finite State Machine actor abstract base class with Stash support.
*
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
abstract class AbstractFSMWithStash[S, D] extends AbstractFSM[S, D] with Stash

View file

@ -10,6 +10,7 @@ import akka.event.LoggingAdapter
import scala.annotation.tailrec
import scala.beans.BeanProperty
import scala.util.control.NoStackTrace
import java.util.Optional
/**
* INTERNAL API
@ -76,7 +77,17 @@ final case class ActorIdentity(correlationId: Any, ref: Option[ActorRef]) {
* Java API: `ActorRef` of the actor replying to the request or
* null if no actor matched the request.
*/
@deprecated("Use getActorRef instead", "2.5.0")
def getRef: ActorRef = ref.orNull
/**
* Java API: `ActorRef` of the actor replying to the request or
* not defined if no actor matched the request.
*/
def getActorRef: Optional[ActorRef] = {
import scala.compat.java8.OptionConverters._
ref.asJava
}
}
/**
@ -390,7 +401,7 @@ object Actor {
* initial behavior of the actor as a partial function (behavior can be changed
* using `context.become` and `context.unbecome`).
*
* This is the Scala API (hence the Scala code below), for the Java API see [[akka.actor.UntypedActor]].
* This is the Scala API (hence the Scala code below), for the Java API see [[akka.actor.AbstractActor]].
*
* {{{
* class ExampleActor extends Actor {
@ -434,14 +445,14 @@ trait Actor {
type Receive = Actor.Receive
/**
* Stores the context for this actor, including self, and sender.
* Scala API: Stores the context for this actor, including self, and sender.
* It is implicit to support operations such as `forward`.
*
* WARNING: Only valid within the Actor itself, so do not close over it and
* publish it to other threads!
*
* [[akka.actor.ActorContext]] is the Scala API. `getContext` returns a
* [[akka.actor.UntypedActorContext]], which is the Java API of the actor
* [[akka.actor.AbstractActor.ActorContext]], which is the Java API of the actor
* context.
*/
implicit val context: ActorContext = {
@ -476,7 +487,7 @@ trait Actor {
final def sender(): ActorRef = context.sender()
/**
* This defines the initial actor behavior, it must return a partial function
* Scala API: This defines the initial actor behavior, it must return a partial function
* with the actor logic.
*/
//#receive
@ -550,7 +561,7 @@ trait Actor {
//#lifecycle-hooks
/**
* User overridable callback: '''By default it disposes of all children and then calls `postStop()`.'''
* Scala API: User overridable callback: '''By default it disposes of all children and then calls `postStop()`.'''
* @param reason the Throwable that caused the restart to happen
* @param message optionally the current message the actor processed when failing, if applicable
* <p/>

View file

@ -18,6 +18,8 @@ import java.util.concurrent.ThreadLocalRandom
import scala.util.control.NonFatal
import akka.dispatch.MessageDispatcher
import akka.util.Reflect
import akka.japi.pf.ReceiveBuilder
import akka.actor.AbstractActor.Receive
/**
* The actor context - the view of the actor cell from the actor.
@ -160,29 +162,11 @@ trait ActorContext extends ActorRefFactory {
throw new NotSerializableException("ActorContext is not serializable!")
}
/**
* AbstractActorContext is the AbstractActor equivalent of ActorContext,
* containing the Java API
*/
trait AbstractActorContext extends ActorContext {
/**
* Returns an unmodifiable Java Collection containing the linked actors,
* please note that the backing map is thread-safe but not immutable
*/
def getChildren(): java.lang.Iterable[ActorRef]
/**
* Returns a reference to the named child or null if no child with
* that name exists.
*/
def getChild(name: String): ActorRef
}
/**
* UntypedActorContext is the UntypedActor equivalent of ActorContext,
* containing the Java API
*/
@deprecated("Use AbstractActor instead of UntypedActor.", since = "2.5.0")
trait UntypedActorContext extends ActorContext {
/**
@ -377,7 +361,7 @@ private[akka] class ActorCell(
final val props: Props, // Must be final so that it can be properly cleared in clearActorCellFields
val dispatcher: MessageDispatcher,
val parent: InternalActorRef)
extends UntypedActorContext with AbstractActorContext with Cell
extends UntypedActorContext with AbstractActor.ActorContext with Cell
with dungeon.ReceiveTimeout
with dungeon.Children
with dungeon.Dispatch

View file

@ -65,29 +65,23 @@ object ActorRef {
* import static akka.pattern.Patterns.ask;
* import static akka.pattern.Patterns.pipe;
*
* public class ExampleActor extends UntypedActor {
* public class ExampleActor extends AbstractActor {
* // this child will be destroyed and re-created upon restart by default
* final ActorRef other = getContext().actorOf(Props.create(OtherActor.class), "childName");
*
* @Override
* public void onReceive(Object o) {
* if (o instanceof Request1) {
* Msg msg = ((Request1) o).getMsg();
* other.tell(msg, getSelf()); // uses this actor as sender reference, reply goes to us
*
* } else if (o instanceof Request2) {
* Msg msg = ((Request2) o).getMsg();
* other.tell(msg, getSender()); // forward sender reference, enabling direct reply
*
* } else if (o instanceof Request3) {
* Msg msg = ((Request3) o).getMsg();
* pipe(ask(other, msg, 5000), context().dispatcher()).to(getSender());
* // the ask call will get a future from other's reply
* // when the future is complete, send its value to the original sender
*
* } else {
* unhandled(o);
* }
* public Receive createReceive() {
* return receiveBuilder()
* .match(Request1.class, msg ->
* // uses this actor as sender reference, reply goes to us
* other.tell(msg, getSelf()))
* .match(Request2.class, msg ->
* // forward sender reference, enabling direct reply
* other.tell(msg, getSender()))
* .match(Request3.class, msg ->
* // the ask call will get a future from other's reply
* // when the future is complete, send its value to the original sender
* pipe(ask(other, msg, 5000), context().dispatcher()).to(getSender()))
* .build();
* }
* }
* }}}

View file

@ -316,21 +316,6 @@ trait ActorRefFactory {
* Java API: Look-up an actor by applying the given path elements, starting from the
* current context, where `".."` signifies the parent of an actor.
*
* Example:
* {{{
* public class MyActor extends UntypedActor {
* public void onReceive(Object msg) throws Exception {
* ...
* final List<String> path = new ArrayList<String>();
* path.add("..");
* path.add("myBrother");
* path.add("myNephew");
* final ActorRef target = getContext().actorFor(path);
* ...
* }
* }
* }}}
*
* For maximum performance use a collection with efficient head & tail operations.
*
* Actor references acquired with `actorFor` do not always include the full information

View file

@ -411,21 +411,18 @@ case class AllForOneStrategy(
/**
* Java API: compatible with lambda expressions
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
def this(maxNrOfRetries: Int, withinTimeRange: Duration, decider: SupervisorStrategy.Decider) =
this(maxNrOfRetries = maxNrOfRetries, withinTimeRange = withinTimeRange)(decider)
/**
* Java API: compatible with lambda expressions
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
def this(loggingEnabled: Boolean, decider: SupervisorStrategy.Decider) =
this(loggingEnabled = loggingEnabled)(decider)
/**
* Java API: compatible with lambda expressions
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
def this(decider: SupervisorStrategy.Decider) =
this()(decider)
@ -487,21 +484,15 @@ case class OneForOneStrategy(
/**
* Java API: compatible with lambda expressions
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
def this(maxNrOfRetries: Int, withinTimeRange: Duration, decider: SupervisorStrategy.Decider) =
this(maxNrOfRetries = maxNrOfRetries, withinTimeRange = withinTimeRange)(decider)
/**
* Java API: compatible with lambda expressions
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
def this(loggingEnabled: Boolean, decider: SupervisorStrategy.Decider) =
this(loggingEnabled = loggingEnabled)(decider)
/**
* Java API: compatible with lambda expressions
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
def this(decider: SupervisorStrategy.Decider) =
this()(decider)

View file

@ -92,6 +92,7 @@ package akka.actor
* }
* }}}
*/
@deprecated("Use AbstractActor instead of UntypedActor.", since = "2.5.0")
abstract class UntypedActor extends Actor {
/**

View file

@ -44,12 +44,14 @@ package akka.actor
* There is also an unrestricted version [[akka.actor.UntypedActorWithUnrestrictedStash]] that does not
* enforce the mailbox type.
*/
@deprecated("Use AbstractActor instead of UntypedActor.", since = "2.5.0")
abstract class UntypedActorWithStash extends UntypedActor with Stash
/**
* Actor base class with `Stash` that enforces an unbounded deque for the actor.
* See [[akka.actor.UntypedActorWithStash]] for details on how `Stash` works.
*/
@deprecated("Use AbstractActor instead of UntypedActor.", since = "2.5.0")
abstract class UntypedActorWithUnboundedStash extends UntypedActor with UnboundedStash
/**
@ -57,4 +59,5 @@ abstract class UntypedActorWithUnboundedStash extends UntypedActor with Unbounde
* manually, and the mailbox should extend the [[akka.dispatch.DequeBasedMessageQueueSemantics]] marker trait.
* See [[akka.actor.UntypedActorWithStash]] for details on how `Stash` works.
*/
@deprecated("Use AbstractActor instead of UntypedActor.", since = "2.5.0")
abstract class UntypedActorWithUnrestrictedStash extends UntypedActor with UnrestrictedStash

View file

@ -11,6 +11,7 @@ import akka.actor._
import akka.serialization.SerializationExtension
import akka.util.{ Unsafe, Helpers }
import akka.serialization.SerializerWithStringManifest
import java.util.Optional
private[akka] object Children {
val GetNobody = () Nobody
@ -35,6 +36,7 @@ private[akka] trait Children { this: ActorCell ⇒
case Some(s: ChildRestartStats) s.child
case _ null
}
def findChild(name: String): Optional[ActorRef] = Optional.ofNullable(getChild(name))
def actorOf(props: Props): ActorRef =
makeChild(this, props, randomName(), async = false, systemService = false)

View file

@ -642,6 +642,7 @@ object Logging {
* Obtain LoggingAdapter with MDC support for the given actor.
* Don't use it outside its specific Actor as it isn't thread safe
*/
@deprecated("Use AbstractActor instead of UntypedActor.", since = "2.5.0")
def getLogger(logSource: UntypedActor): DiagnosticLoggingAdapter = {
val (str, clazz) = LogSource.fromAnyRef(logSource)
val system = logSource.getContext().system.asInstanceOf[ExtendedActorSystem]

View file

@ -9,6 +9,8 @@ import akka.actor.ActorContext
import akka.actor.ActorCell
import akka.actor.DiagnosticActorLogging
import akka.event.Logging.{ LogEvent, LogLevel }
import akka.actor.AbstractActor
import scala.runtime.BoxedUnit
object LoggingReceive {
@ -37,10 +39,17 @@ object LoggingReceive {
/**
* Java API: compatible with lambda expressions
* This is an EXPERIMENTAL feature and is subject to change until it has received more real world testing.
*/
@deprecated("Use the create method with `AbstractActor.Receive` parameter instead.", since = "2.5.0")
def create(r: Receive, context: ActorContext): Receive = apply(r)(context)
/**
* Java API: compatible with lambda expressions
*/
def create(r: AbstractActor.Receive, context: AbstractActor.ActorContext): AbstractActor.Receive =
new AbstractActor.Receive(apply(r.onMessage.asInstanceOf[PartialFunction[Any, Unit]])(context)
.asInstanceOf[PartialFunction[Any, BoxedUnit]])
/**
* Create a decorated logger which will append `" in state " + label` to each message it logs.
*/

View file

@ -5,6 +5,7 @@
package akka.cluster.sharding;
import static java.util.concurrent.TimeUnit.SECONDS;
import scala.concurrent.duration.Duration;
import akka.actor.AbstractActor;
@ -17,13 +18,10 @@ import akka.actor.Props;
import akka.actor.SupervisorStrategy;
import akka.actor.Terminated;
import akka.actor.ReceiveTimeout;
import akka.actor.UntypedActor;
import akka.japi.Procedure;
import akka.japi.Option;
import akka.persistence.UntypedPersistentActor;
import akka.persistence.AbstractPersistentActor;
import akka.cluster.Cluster;
import akka.japi.pf.DeciderBuilder;
import akka.japi.pf.ReceiveBuilder;
// Doc code, compile only
public class ClusterShardingTest {
@ -96,7 +94,7 @@ public class ClusterShardingTest {
}
static//#counter-actor
public class Counter extends UntypedPersistentActor {
public class Counter extends AbstractPersistentActor {
public static enum CounterOp {
INCREMENT, DECREMENT
@ -139,7 +137,7 @@ public class ClusterShardingTest {
@Override
public void preStart() throws Exception {
super.preStart();
context().setReceiveTimeout(Duration.create(120, SECONDS));
getContext().setReceiveTimeout(Duration.create(120, SECONDS));
}
void updateState(CounterChanged event) {
@ -147,45 +145,45 @@ public class ClusterShardingTest {
}
@Override
public void onReceiveRecover(Object msg) {
if (msg instanceof CounterChanged)
updateState((CounterChanged) msg);
else
unhandled(msg);
public Receive createReceiveRecover() {
return receiveBuilder()
.match(CounterChanged.class, this::updateState)
.build();
}
@Override
public void onReceiveCommand(Object msg) {
if (msg instanceof Get)
getSender().tell(count, getSelf());
else if (msg == CounterOp.INCREMENT)
persist(new CounterChanged(+1), new Procedure<CounterChanged>() {
public void apply(CounterChanged evt) {
updateState(evt);
}
});
else if (msg == CounterOp.DECREMENT)
persist(new CounterChanged(-1), new Procedure<CounterChanged>() {
public void apply(CounterChanged evt) {
updateState(evt);
}
});
else if (msg.equals(ReceiveTimeout.getInstance()))
getContext().parent().tell(
new ShardRegion.Passivate(PoisonPill.getInstance()), getSelf());
else
unhandled(msg);
public Receive createReceive() {
return receiveBuilder()
.match(Get.class, this::receiveGet)
.matchEquals(CounterOp.INCREMENT, msg -> receiveIncrement())
.matchEquals(CounterOp.DECREMENT, msg -> receiveDecrement())
.matchEquals(ReceiveTimeout.getInstance(), msg -> passivate())
.build();
}
private void receiveGet(Get msg) {
getSender().tell(count, getSelf());
}
private void receiveIncrement() {
persist(new CounterChanged(+1), this::updateState);
}
private void receiveDecrement() {
persist(new CounterChanged(-1), this::updateState);
}
private void passivate() {
getContext().parent().tell(
new ShardRegion.Passivate(PoisonPill.getInstance()), getSelf());
}
}
//#counter-actor
static//#supervisor
public class CounterSupervisor extends UntypedActor {
public class CounterSupervisor extends AbstractActor {
private final ActorRef counter = getContext().actorOf(
Props.create(Counter.class), "theCounter");
@ -203,9 +201,12 @@ public class ClusterShardingTest {
}
@Override
public void onReceive(Object msg) {
counter.forward(msg, getContext());
public Receive createReceive() {
return receiveBuilder()
.match(Object.class, msg -> counter.forward(msg, getContext()))
.build();
}
}
//#supervisor

View file

@ -58,13 +58,13 @@ public class ClusterClientTest extends JUnitSuite {
system.actorOf(Props.create(ReceptionistListener.class, ClusterClientReceptionist.get(system).underlying()));
}
static public class Service extends UntypedActor {
static public class Service extends UntypedAbstractActor {
public void onReceive(Object msg) {
}
}
//#clientEventsListener
static public class ClientListener extends UntypedActor {
static public class ClientListener extends AbstractActor {
private final ActorRef targetClient;
private final Set<ActorPath> contactPoints = new HashSet<>();
@ -78,26 +78,28 @@ public class ClusterClientTest extends JUnitSuite {
}
@Override
public void onReceive(Object message) {
if (message instanceof ContactPoints) {
ContactPoints msg = (ContactPoints)message;
contactPoints.addAll(msg.getContactPoints());
// Now do something with an up-to-date "contactPoints"
} else if (message instanceof ContactPointAdded) {
ContactPointAdded msg = (ContactPointAdded) message;
contactPoints.add(msg.contactPoint());
// Now do something with an up-to-date "contactPoints"
} else if (message instanceof ContactPointRemoved) {
ContactPointRemoved msg = (ContactPointRemoved)message;
contactPoints.remove(msg.contactPoint());
// Now do something with an up-to-date "contactPoints"
}
public Receive createReceive() {
return receiveBuilder()
.match(ContactPoints.class, msg -> {
contactPoints.addAll(msg.getContactPoints());
// Now do something with an up-to-date "contactPoints"
})
.match(ContactPointAdded.class, msg -> {
contactPoints.add(msg.contactPoint());
// Now do something with an up-to-date "contactPoints"
})
.match(ContactPointRemoved.class, msg -> {
contactPoints.remove(msg.contactPoint());
// Now do something with an up-to-date "contactPoints"
})
.build();
}
}
//#clientEventsListener
//#receptionistEventsListener
static public class ReceptionistListener extends UntypedActor {
static public class ReceptionistListener extends AbstractActor {
private final ActorRef targetReceptionist;
private final Set<ActorRef> clusterClients = new HashSet<>();
@ -111,21 +113,23 @@ public class ClusterClientTest extends JUnitSuite {
}
@Override
public void onReceive(Object message) {
if (message instanceof ClusterClients) {
ClusterClients msg = (ClusterClients) message;
clusterClients.addAll(msg.getClusterClients());
// Now do something with an up-to-date "clusterClients"
} else if (message instanceof ClusterClientUp) {
ClusterClientUp msg = (ClusterClientUp) message;
clusterClients.add(msg.clusterClient());
// Now do something with an up-to-date "clusterClients"
} else if (message instanceof ClusterClientUnreachable) {
ClusterClientUnreachable msg = (ClusterClientUnreachable) message;
clusterClients.remove(msg.clusterClient());
// Now do something with an up-to-date "clusterClients"
}
public Receive createReceive() {
return receiveBuilder()
.match(ClusterClients.class, msg -> {
clusterClients.addAll(msg.getClusterClients());
// Now do something with an up-to-date "clusterClients"
})
.match(ClusterClientUp.class, msg -> {
clusterClients.add(msg.clusterClient());
// Now do something with an up-to-date "clusterClients"
})
.match(ClusterClientUnreachable.class, msg -> {
clusterClients.remove(msg.clusterClient());
// Now do something with an up-to-date "clusterClients"
})
.build();
}
}
//#receptionistEventsListener

View file

@ -14,7 +14,7 @@ import org.junit.Test;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.actor.AbstractActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import org.scalatest.junit.JUnitSuite;
@ -65,7 +65,7 @@ public class DistributedPubSubMediatorTest extends JUnitSuite {
}
static//#subscriber
public class Subscriber extends UntypedActor {
public class Subscriber extends AbstractActor {
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
public Subscriber() {
@ -76,40 +76,42 @@ public class DistributedPubSubMediatorTest extends JUnitSuite {
getSelf());
}
public void onReceive(Object msg) {
if (msg instanceof String)
log.info("Got: {}", msg);
else if (msg instanceof DistributedPubSubMediator.SubscribeAck)
log.info("subscribing");
else
unhandled(msg);
@Override
public Receive createReceive() {
return receiveBuilder()
.match(String.class, msg ->
log.info("Got: {}", msg))
.match(DistributedPubSubMediator.SubscribeAck.class, msg ->
log.info("subscribing"))
.build();
}
}
//#subscriber
static//#publisher
public class Publisher extends UntypedActor {
public class Publisher extends AbstractActor {
// activate the extension
ActorRef mediator =
DistributedPubSub.get(getContext().system()).mediator();
public void onReceive(Object msg) {
if (msg instanceof String) {
String in = (String) msg;
String out = in.toUpperCase();
mediator.tell(new DistributedPubSubMediator.Publish("content", out),
getSelf());
} else {
unhandled(msg);
@Override
public Receive createReceive() {
return receiveBuilder()
.match(String.class, in -> {
String out = in.toUpperCase();
mediator.tell(new DistributedPubSubMediator.Publish("content", out),
getSelf());
})
.build();
}
}
}
//#publisher
static//#send-destination
public class Destination extends UntypedActor {
public class Destination extends AbstractActor {
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
public Destination() {
@ -119,36 +121,39 @@ public class DistributedPubSubMediatorTest extends JUnitSuite {
mediator.tell(new DistributedPubSubMediator.Put(getSelf()), getSelf());
}
public void onReceive(Object msg) {
if (msg instanceof String)
log.info("Got: {}", msg);
else if (msg instanceof DistributedPubSubMediator.SubscribeAck)
log.info("subscribing");
else
unhandled(msg);
@Override
public Receive createReceive() {
return receiveBuilder()
.match(String.class, msg ->
log.info("Got: {}", msg))
.match(DistributedPubSubMediator.SubscribeAck.class, msg ->
log.info("subscribing"))
.build();
}
}
//#send-destination
static//#sender
public class Sender extends UntypedActor {
public class Sender extends AbstractActor {
// activate the extension
ActorRef mediator =
DistributedPubSub.get(getContext().system()).mediator();
public void onReceive(Object msg) {
if (msg instanceof String) {
String in = (String) msg;
String out = in.toUpperCase();
boolean localAffinity = true;
mediator.tell(new DistributedPubSubMediator.Send("/user/destination", out,
localAffinity), getSelf());
} else {
unhandled(msg);
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(String.class, in -> {
String out = in.toUpperCase();
boolean localAffinity = true;
mediator.tell(new DistributedPubSubMediator.Send("/user/destination", out,
localAffinity), getSelf());
})
.build();
}
}
//#sender
}

View file

@ -114,8 +114,7 @@ Activator
To bootstrap Akka inside an OSGi environment, you can use the ``akka.osgi.ActorSystemActivator`` class
to conveniently set up the ActorSystem.
.. includecode:: code/docs/osgi/Activator.scala#Activator
.. includecode:: ../../../akka-osgi/src/test/scala/docs/osgi/Activator.scala#Activator
The goal here is to map the OSGi lifecycle more directly to the Akka lifecycle. The ``ActorSystemActivator`` creates
the actor system with a class loader that finds resources (``application.conf`` and ``reference.conf`` files) and classes

View file

@ -5,7 +5,7 @@ package docs.circuitbreaker;
//#imports1
import akka.actor.UntypedActor;
import akka.actor.AbstractActor;
import scala.concurrent.Future;
import akka.event.LoggingAdapter;
import scala.concurrent.duration.Duration;
@ -20,7 +20,7 @@ import java.util.concurrent.Callable;
//#imports1
//#circuit-breaker-initialization
public class DangerousJavaActor extends UntypedActor {
public class DangerousJavaActor extends AbstractActor {
private final CircuitBreaker breaker;
private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
@ -47,22 +47,21 @@ public class DangerousJavaActor extends UntypedActor {
}
@Override
public void onReceive(Object message) {
if (message instanceof String) {
String m = (String) message;
if ("is my middle name".equals(m)) {
public Receive createReceive() {
return receiveBuilder().
match(String.class, m -> "is my middle name".equals(m), m -> {
pipe(
breaker.callWithCircuitBreaker(() ->
future(() -> dangerousCall(), getContext().dispatcher())
), getContext().dispatcher()
).to(getSender());
}
if ("block for me".equals(m)) {
getSender().tell(breaker
).to(sender());
})
.match(String.class, m -> "block for me".equals(m), m -> {
sender().tell(breaker
.callWithSyncCircuitBreaker(
() -> dangerousCall()), getSelf());
}
}
() -> dangerousCall()), self());
})
.build();
}
//#circuit-breaker-usage

View file

@ -5,13 +5,14 @@ package docs.circuitbreaker;
import akka.actor.ActorRef;
import akka.actor.ReceiveTimeout;
import akka.actor.UntypedActor;
import akka.actor.AbstractActor.Receive;
import akka.actor.AbstractActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.pattern.CircuitBreaker;
import scala.concurrent.duration.Duration;
public class TellPatternJavaActor extends UntypedActor {
public class TellPatternJavaActor extends AbstractActor {
private final ActorRef target;
private final CircuitBreaker breaker;
@ -35,16 +36,21 @@ public class TellPatternJavaActor extends UntypedActor {
//#circuit-breaker-tell-pattern
@Override
public void onReceive(Object payload) {
if ( "call".equals(payload) && breaker.isClosed() ) {
target.tell("message", getSelf());
} else if ( "response".equals(payload) ) {
breaker.succeed();
} else if ( payload instanceof Throwable ) {
breaker.fail();
} else if ( payload instanceof ReceiveTimeout ) {
breaker.fail();
}
public Receive createReceive() {
return receiveBuilder()
.match(String.class, payload -> "call".equals(payload) && breaker.isClosed(), payload ->
target.tell("message", self())
)
.matchEquals("response", payload ->
breaker.succeed()
)
.match(Throwable.class, t ->
breaker.fail()
)
.match(ReceiveTimeout.class, t ->
breaker.fail()
)
.build();
}
//#circuit-breaker-tell-pattern

View file

@ -20,8 +20,6 @@ prior deprecation.
:maxdepth: 1
../dev/multi-node-testing
../java/lambda-actors
../java/lambda-fsm
Another reason for marking a module as experimental is that it's too early
to tell if the module has a maintainer that can take the responsibility

View file

@ -20,8 +20,6 @@ prior deprecation.
:maxdepth: 1
../dev/multi-node-testing
../java/lambda-actors
../java/lambda-fsm
../scala/typed
Another reason for marking a module as experimental is that it's too early

View file

@ -8,7 +8,7 @@ hierarchies and are the smallest unit when building an application. This
section looks at one such actor in isolation, explaining the concepts you
encounter while implementing it. For a more in depth reference with all the
details please refer to
:ref:`Actors (Scala) <actors-scala>` and :ref:`Untyped Actors (Java) <untyped-actors-java>`.
:ref:`Actors (Scala) <actors-scala>` and :ref:`Actors (Java) <actors-java>`.
An actor is a container for `State`_, `Behavior`_, a `Mailbox`_, `Child Actors`_
and a `Supervisor Strategy`_. All of this is encapsulated behind an `Actor

View file

@ -38,7 +38,7 @@ Actors give you:
- Asynchronous, non-blocking and highly performant message-driven programming model.
- Very lightweight event-driven processes (several million actors per GB of heap memory).
See the chapter for :ref:`Scala <actors-scala>` or :ref:`Java <untyped-actors-java>`.
See the chapter for :ref:`Scala <actors-scala>` or :ref:`Java <actors-java>`.
Fault Tolerance
---------------

View file

@ -9,13 +9,13 @@ Java Documentation
intro/index-java
general/index
java/index-actors
java/lambda-index-actors
java/index-futures
java/index-network
java/index-utilities
java/stream/index
java/http/index
java/howto
java/scala-compat
experimental/index-java
dev/index
project/index

View file

@ -1,8 +1,8 @@
.. _lambda-actors-java:
.. _actors-java:
###################################
Actors (Java with Lambda Support)
###################################
########
Actors
########
The `Actor Model`_ provides a higher level of abstraction for writing concurrent
and distributed systems. It alleviates the developer from having to deal with
@ -17,14 +17,6 @@ its syntax from Erlang.
.. _Actor Model: http://en.wikipedia.org/wiki/Actor_model
.. warning::
The Java with lambda support part of Akka is marked as **“experimental”** as of its introduction in
Akka 2.3.0. We will continue to improve this API based on our users feedback, which implies that
while we try to keep incompatible changes to a minimum, but the binary compatibility guarantee for
maintenance releases does not apply to the :class:`akka.actor.AbstractActor`, related classes and
the :class:`akka.japi.pf` package.
Creating Actors
===============
@ -54,12 +46,6 @@ Here is an example:
.. includecode:: code/docs/actorlambda/MyActor.java
:include: imports,my-actor
In case you want to provide many :meth:`match` cases but want to avoid creating a long call
trail, you can split the creation of the builder into multiple statements as in the example:
.. includecode:: code/docs/actorlambda/GraduallyBuiltActor.java
:include: imports,actor
Please note that the Akka Actor ``receive`` message loop is exhaustive, which
is different compared to Erlang and the late Scala Actors. This means that you
need to provide a pattern match for all messages that it can accept and if you
@ -180,20 +166,20 @@ another child to the same parent an `InvalidActorNameException` is thrown.
Actors are automatically started asynchronously when created.
.. _actor-create-factory-lambda:
.. _actor-create-factory-java:
Dependency Injection
--------------------
If your UntypedActor has a constructor that takes parameters then those need to
If your actor has a constructor that takes parameters then those need to
be part of the :class:`Props` as well, as described `above`__. But there
are cases when a factory method must be used, for example when the actual
constructor arguments are determined by a dependency injection framework.
__ Props_
.. includecode:: code/docs/actor/UntypedActorDocTest.java#import-indirect
.. includecode:: code/docs/actor/UntypedActorDocTest.java
.. includecode:: code/docs/actorlambda/DependencyInjectionDocTest.java#import
.. includecode:: code/docs/actorlambda/DependencyInjectionDocTest.java
:include: creating-indirectly
:exclude: obtain-fresh-Actor-instance-from-DI-framework
@ -260,19 +246,19 @@ In addition, it offers:
occurred within a distant descendant it is still reported one level up at a
time).
* :meth:`context()` exposes contextual information for the actor and the current message, such as:
* :meth:`getContext()` exposes contextual information for the actor and the current message, such as:
* factory methods to create child actors (:meth:`actorOf`)
* system that the actor belongs to
* parent supervisor
* supervised children
* lifecycle monitoring
* hotswap behavior stack as described in :ref:`actor-hotswap-lambda`
* hotswap behavior stack as described in :ref:`actor-hotswap-java`
The remaining visible methods are user-overridable life-cycle hooks which are
described in the following:
.. includecode:: code/docs/actor/UntypedActorDocTest.java#lifecycle-callbacks
.. includecode:: code/docs/actorlambda/ActorDocTest.java#lifecycle-callbacks
The implementations shown above are the defaults provided by the :class:`AbstractActor`
class.
@ -317,11 +303,11 @@ occupying it. ``ActorSelection`` cannot be watched for this reason. It is
possible to resolve the current incarnation's ``ActorRef`` living under the
path by sending an ``Identify`` message to the ``ActorSelection`` which
will be replied to with an ``ActorIdentity`` containing the correct reference
(see :ref:`actorSelection-lambda`). This can also be done with the ``resolveOne``
(see :ref:`actorselection-java`). This can also be done with the ``resolveOne``
method of the :class:`ActorSelection`, which returns a ``Future`` of the matching
:class:`ActorRef`.
.. _deathwatch-lambda:
.. _deathwatch-java:
Lifecycle Monitoring aka DeathWatch
-----------------------------------
@ -334,6 +320,7 @@ termination (see `Stopping Actors`_). This service is provided by the
Registering a monitor is easy:
.. includecode:: code/docs/actorlambda/ActorDocTest.java#import-terminated
.. includecode:: code/docs/actorlambda/ActorDocTest.java#watch
It should be noted that the :class:`Terminated` message is generated
@ -354,7 +341,7 @@ using ``context.unwatch(target)``. This works even if the :class:`Terminated`
message has already been enqueued in the mailbox; after calling :meth:`unwatch`
no :class:`Terminated` message for that actor will be processed anymore.
.. _start-hook-lambda:
.. _start-hook-java:
Start Hook
----------
@ -371,7 +358,7 @@ Initialization code which is part of the actors constructor will always be
called when an instance of the actor class is created, which happens at every
restart.
.. _restart-hook-lambda:
.. _restart-hook-java:
Restart Hooks
-------------
@ -414,7 +401,7 @@ usual.
it has processed the last messages sent by the child before the failure.
See :ref:`message-ordering` for details.
.. _stop-hook-lambda:
.. _stop-hook-java:
Stop Hook
---------
@ -425,7 +412,7 @@ to run after message queuing has been disabled for this actor, i.e. messages
sent to a stopped actor will be redirected to the :obj:`deadLetters` of the
:obj:`ActorSystem`.
.. _actorSelection-lambda:
.. _actorselection-java:
Identifying Actors via Actor Selection
======================================
@ -447,7 +434,7 @@ result:
It is always preferable to communicate with other Actors using their ActorRef
instead of relying upon ActorSelection. Exceptions are
* sending messages using the :ref:`at-least-once-delivery-java-lambda` facility
* sending messages using the :ref:`at-least-once-delivery-java` facility
* initiating first contact with a remote system
In all other cases ActorRefs can be provided during Actor creation or
@ -488,7 +475,7 @@ of that reply is guaranteed, it still is a normal message.
You can also acquire an :class:`ActorRef` for an :class:`ActorSelection` with
the ``resolveOne`` method of the :class:`ActorSelection`. It returns a
``Future`` of the matching :class:`ActorRef` if such an actor exists (see also
:ref:`actor-java-lambda` for Java compatibility). It is completed with failure
:ref:`scala-java-compat` for Java compatibility). It is completed with failure
[[akka.actor.ActorNotFound]] if no such actor exists or the identification
didn't complete within the supplied `timeout`.
@ -532,7 +519,7 @@ In all these methods you have the option of passing along your own ``ActorRef``.
Make it a practice of doing so because it will allow the receiver actors to be able to respond
to your message, since the sender reference is sent along with the message.
.. _actors-tell-sender-lambda:
.. _actors-tell-sender-java:
Tell: Fire-forget
-----------------
@ -551,7 +538,7 @@ different one. Outside of an actor and if no reply is needed the second
argument can be ``null``; if a reply is needed outside of an actor you can use
the ask-pattern described next..
.. _actors-ask-lambda:
.. _actors-ask-java:
Ask: Send-And-Receive-Future
----------------------------
@ -559,8 +546,8 @@ Ask: Send-And-Receive-Future
The ``ask`` pattern involves actors as well as futures, hence it is offered as
a use pattern rather than a method on :class:`ActorRef`:
.. includecode:: code/docs/actor/UntypedActorDocTest.java#import-ask
.. includecode:: code/docs/actor/UntypedActorDocTest.java#ask-pipe
.. includecode:: code/docs/actorlambda/ActorDocTest.java#import-ask
.. includecode:: code/docs/actorlambda/ActorDocTest.java#ask-pipe
This example demonstrates ``ask`` together with the ``pipe`` pattern on
futures, because this is likely to be a common combination. Please note that
@ -577,6 +564,10 @@ involves creating an internal actor for handling this reply, which needs to
have a timeout after which it is destroyed in order not to leak resources; see
more below.
.. note::
A variant of the ``ask`` pattern that returns a ``CompletionStage`` instead of a Scala ``Future``
is available in the ``akka.pattern.PatternsCS`` object.
.. warning::
To complete the future with an exception you need send a Failure message to the sender.
@ -615,33 +606,60 @@ routers, load-balancers, replicators etc.
.. includecode:: code/docs/actorlambda/ActorDocTest.java#forward
.. _actors-receive-java:
Receive messages
================
An Actor either has to set its initial receive behavior in the constructor by
calling the :meth:`receive` method in the :class:`AbstractActor`:
An actor has to define its initial receive behavior by implementing
the :meth:`createReceive` method in the :class:`AbstractActor`:
.. includecode:: code/docs/actorlambda/ActorDocTest.java
:include: receive-constructor
:exclude: and-some-behavior
.. includecode:: code/docs/actorlambda/ActorDocTest.java#createReceive
or by implementing the :meth:`receive` method in the :class:`Actor` interface:
.. includecode:: code/docs/actorlambda/ActorDocTest.java#receive
Both the argument to the :class:`AbstractActor` :meth:`receive` method and the return
type of the :class:`Actor` :meth:`receive` method is a ``PartialFunction<Object, BoxedUnit>``
that defines which messages your Actor can handle, along with the implementation of how the messages
should be processed.
Don't let the type signature scare you. To allow you to easily build up a partial
function there is a builder named ``ReceiveBuilder`` that you can use.
The return type is :class:`AbstractActor.Receive` that defines which messages your Actor can handle,
along with the implementation of how the messages should be processed.
You can build such behavior with a builder named ``ReceiveBuilder``.
Here is an example:
.. includecode:: code/docs/actorlambda/MyActor.java
:include: imports,my-actor
In case you want to provide many :meth:`match` cases but want to avoid creating a long call
trail, you can split the creation of the builder into multiple statements as in the example:
.. includecode:: code/docs/actorlambda/GraduallyBuiltActor.java
:include: imports,actor
Using small methods is a good practice, also in actors. It's recommended to delegate the
actual work of the message processing to methods instead of defining a huge ``ReceiveBuilder``
with lots of code in each lambda. A well structured actor can look like this:
.. includecode:: code/docs/actorlambda/ActorDocTest.java#well-structured
That has benefits such as:
* easier to see what kind of messages the actor can handle
* readable stack traces in case of exceptions
* works better with performance profiling tools
* Java HotSpot has a better opportunity for making optimizations
The ``Receive`` can be implemented in other ways than using the ``ReceiveBuilder`` since it in the
end is just a wrapper around a Scala ``PartialFunction``. In Java, you can implement ``PartialFunction`` by
extending ``AbstractPartialFunction``. For example, one could implement an adapter
to `Javaslang Pattern Matching DSL <http://www.javaslang.io/javaslang-docs/#_pattern_matching>`_.
If the validation of the ``ReceiveBuilder`` match logic turns out to be a bottleneck for some of your
actors you can consider to implement it at lower level by extending ``UntypedAbstractActor`` instead
of ``AbstractActor``. The partial functions created by the ``ReceiveBuilder`` consist of multiple lambda
expressions for every match statement, where each lambda is referencing the code to be run. This is something
that the JVM can have problems optimizing and the resulting code might not be as performant as the
untyped version. When extending ``UntypedAbstractActor`` each message is received as an untyped
``Object`` and you have to inspect and cast it to the actual message type in other ways, like this:
.. includecode:: code/docs/actorlambda/ActorDocTest.java#optimized
.. _LambdaActor.Reply:
Reply to messages
@ -678,7 +696,7 @@ Messages marked with ``NotInfluenceReceiveTimeout`` will not reset the timer. Th
``ReceiveTimeout`` should be fired by external inactivity but not influenced by internal activity,
e.g. scheduled tick messages.
.. _stopping-actors-lambda:
.. _stopping-actors-java:
Stopping actors
===============
@ -689,6 +707,8 @@ child actors and the system for stopping top level actors. The actual terminatio
the actor is performed asynchronously, i.e. :meth:`stop` may return before the actor is
stopped.
.. includecode:: code/docs/actorlambda/MyStoppingActor.java#my-stopping-actor
Processing of the current message, if any, will continue before the actor is stopped,
but additional messages in the mailbox will not be processed. By default these
messages are sent to the :obj:`deadLetters` of the :obj:`ActorSystem`, but that
@ -698,7 +718,7 @@ Termination of an actor proceeds in two steps: first the actor suspends its
mailbox processing and sends a stop command to all its children, then it keeps
processing the internal termination notifications from its children until the last one is
gone, finally terminating itself (invoking :meth:`postStop`, dumping mailbox,
publishing :class:`Terminated` on the :ref:`DeathWatch <deathwatch-lambda>`, telling
publishing :class:`Terminated` on the :ref:`DeathWatch <deathwatch-java>`, telling
its supervisor). This procedure ensures that actor system sub-trees terminate
in an orderly fashion, propagating the stop command to the leaves and
collecting their confirmation back to the stopped supervisor. If one of the
@ -724,7 +744,7 @@ enables cleaning up of resources:
actor and create its replacement in response to the :class:`Terminated`
message which will eventually arrive.
.. _poison-pill-lambda:
.. _poison-pill-java:
PoisonPill
----------
@ -734,15 +754,17 @@ stop the actor when the message is processed. ``PoisonPill`` is enqueued as
ordinary messages and will be handled after messages that were already queued
in the mailbox.
.. includecode:: code/docs/actorlambda/ActorDocTest.java#poison-pill
Graceful Stop
-------------
:meth:`gracefulStop` is useful if you need to wait for termination or compose ordered
termination of several actors:
.. includecode:: code/docs/actor/UntypedActorDocTest.java#import-gracefulStop
.. includecode:: code/docs/actorlambda/ActorDocTest.java#import-gracefulStop
.. includecode:: code/docs/actor/UntypedActorDocTest.java#gracefulStop
.. includecode:: code/docs/actorlambda/ActorDocTest.java#gracefulStop
.. includecode:: code/docs/actorlambda/ActorDocTest.java#gracefulStop-actor
@ -764,7 +786,7 @@ before stopping the target actor. Simple cleanup tasks can be handled in ``postS
within a supervisor you control and only in response to a :class:`Terminated`
message, i.e. not for top-level actors.
.. _coordinated-shutdown-lambda:
.. _coordinated-shutdown-java:
Coordinated Shutdown
--------------------
@ -842,14 +864,11 @@ used in the test::
akka.coordinated-shutdown.run-by-jvm-shutdown-hook = off
akka.cluster.run-coordinated-shutdown-when-down = off
.. _actor-hotswap-lambda:
.. _actor-hotswap-java:
Become/Unbecome
===============
Upgrade
-------
Akka supports hotswapping the Actors message loop (e.g. its implementation) at
runtime: invoke the ``context.become`` method from within the Actor.
:meth:`become` takes a ``PartialFunction<Object, BoxedUnit>`` that implements the new
@ -880,7 +899,7 @@ behavior is not the default).
.. includecode:: code/docs/actorlambda/ActorDocTest.java#swapper
.. _stash-lambda:
.. _stash-java:
Stash
=====
@ -888,7 +907,7 @@ Stash
The ``AbstractActorWithStash`` class enables an actor to temporarily stash away messages
that can not or should not be handled using the actor's current
behavior. Upon changing the actor's message handler, i.e., right
before invoking ``context().become()`` or ``context().unbecome()``, all
before invoking ``getContext().become()`` or ``getContext().unbecome()``, all
stashed messages can be "unstashed", thereby prepending them to the actor's
mailbox. This way, the stashed messages can be processed in the same
order as they have been received originally. An actor that extends
@ -940,7 +959,7 @@ usually the desired behavior.
then you should use the ``AbstractActorWithUnboundedStash`` class instead.
.. _killing-actors-lambda:
.. _killing-actors-java:
Killing an Actor
================
@ -953,8 +972,7 @@ See :ref:`supervision-directives` for more information.
Use ``Kill`` like this:
.. includecode:: code/docs/actor/UntypedActorDocTest.java
:include: kill
.. includecode:: code/docs/actorlambda/ActorDocTest.java#kill
Actors and exceptions
=====================
@ -1021,7 +1039,7 @@ this behavior, and ensure that there is only one call to ``preStart()``.
One useful usage of this pattern is to disable creation of new ``ActorRefs`` for children during restarts. This can be
achieved by overriding ``preRestart()``:
.. includecode:: code/docs/actor/InitializationDocSpecJava.java#preStartInit
.. includecode:: code/docs/actorlambda/InitializationDocTest.java#preStartInit
Please note, that the child actors are *still restarted*, but no new ``ActorRef`` is created. One can recursively apply
the same principles for the children, ensuring that their ``preStart()`` method is called only at the creation of their
@ -1049,13 +1067,4 @@ until the initialization finishes, and replaying them after the actor became ini
an uninitialized state might lead to the condition that it receives a user message before the initialization has been
done.
.. _actor-performance-lambda:
Lambdas and Performance
=======================
There is one big difference between the optimized partial functions created by the Scala compiler and the ones created by the
``ReceiveBuilder``. The partial functions created by the ``ReceiveBuilder`` consist of multiple lambda expressions for every match
statement, where each lambda is an object referencing the code to be run. This is something that the JVM can have problems
optimizing and the resulting code might not be as performant as the Scala equivalent or the corresponding
:ref:`untyped actor <untyped-actors-java>` version.

View file

@ -166,7 +166,7 @@ from localhost on port 8877.
After starting the actor, clients can send messages to that actor by POSTing to
``http://localhost:8877/camel/default``. The actor sends a response by using the
getSender().tell method. For returning a message body and headers to the HTTP
sender().tell method. For returning a message body and headers to the HTTP
client the response type should be `CamelMessage`_. For any other response type, a
new CamelMessage object is created by akka-camel with the actor response as message
body.
@ -304,7 +304,7 @@ designed to be asynchronous. This is the case for both, consumer and producer
actors.
* A consumer endpoint sends request messages to its consumer actor using the ``tell``
method and the actor returns responses with ``getSender().tell`` once they are
method and the actor returns responses with ``sender().tell`` once they are
ready.
* A producer actor sends request messages to its endpoint using Camel's

View file

@ -301,7 +301,7 @@ to the ``ShardRegion`` actor to handoff all shards that are hosted by that ``Sha
During this period other regions will buffer messages for those shards in the same way as when a rebalance is
triggered by the coordinator. When the shards have been stopped the coordinator will allocate these shards elsewhere.
This is performed automatically by the :ref:`coordinated-shutdown-lambda` and is therefore part of the
This is performed automatically by the :ref:`coordinated-shutdown-java` and is therefore part of the
graceful leaving process of a cluster member.
.. _RemoveInternalClusterShardingData-java:

View file

@ -188,7 +188,7 @@ It can also be performed programmatically with:
Note that this command can be issued to any member in the cluster, not necessarily the
one that is leaving.
The :ref:`coordinated-shutdown-lambda` will automatically run when the cluster node sees itself as
The :ref:`coordinated-shutdown-java` will automatically run when the cluster node sees itself as
``Exiting``, i.e. leaving from another node will trigger the shutdown process on the leaving node.
Tasks for graceful leaving of cluster including graceful shutdown of Cluster Singletons and
Cluster Sharding are added automatically when Akka Cluster is used, i.e. running the shutdown
@ -362,7 +362,7 @@ How To Cleanup when Member is Removed
You can do some clean up in a ``registerOnMemberRemoved`` callback, which will
be invoked when the current member status is changed to 'Removed' or the cluster have been shutdown.
An alternative is to register tasks to the :ref:`coordinated-shutdown-lambda`.
An alternative is to register tasks to the :ref:`coordinated-shutdown-java`.
.. note::
Register a OnMemberRemoved callback on a cluster that have been shutdown, the callback will be invoked immediately on
@ -532,7 +532,7 @@ That is not done by the router. The configuration for a group looks like this:
The actor paths without address information that are defined in ``routees.paths`` are used for selecting the
actors to which the messages will be forwarded to by the router.
Messages will be forwarded to the routees using :ref:`ActorSelection <actorSelection-java>`, so the same delivery semantics should be expected.
Messages will be forwarded to the routees using :ref:`ActorSelection <actorselection-java>`, so the same delivery semantics should be expected.
It is possible to limit the lookup of routees to member nodes tagged with a certain role by specifying ``use-role``.
``max-total-nr-of-instances`` defines total number of routees in the cluster. By default ``max-total-nr-of-instances``

View file

@ -1,639 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor;
import akka.actor.*;
import akka.japi.pf.ReceiveBuilder;
import akka.testkit.ErrorFilter;
import akka.testkit.EventFilter;
import akka.testkit.TestEvent;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import docs.AbstractJavaTest;
import org.scalatest.junit.JUnitSuite;
import scala.PartialFunction;
import scala.runtime.BoxedUnit;
import static docs.actor.Messages.Swap.Swap;
import static akka.japi.Util.immutableSeq;
import java.util.concurrent.TimeUnit;
import akka.testkit.JavaTestKit;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
//#import-props
import akka.actor.Props;
//#import-props
//#import-actorRef
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
//#import-actorRef
//#import-identify
import akka.actor.ActorIdentity;
import akka.actor.ActorSelection;
import akka.actor.Identify;
//#import-identify
//#import-graceFulStop
import akka.pattern.AskTimeoutException;
import scala.concurrent.Await;
import scala.concurrent.duration.Duration;
import scala.concurrent.Future;
import static akka.pattern.Patterns.gracefulStop;
//#import-graceFulStop
public class ActorDocTest extends AbstractJavaTest {
public static Config config = ConfigFactory.parseString(
"akka {\n" +
" loggers = [\"akka.testkit.TestEventListener\"]\n" +
" loglevel = \"WARNING\"\n" +
" stdout-loglevel = \"WARNING\"\n" +
"}\n"
);
static ActorSystem system = null;
@BeforeClass
public static void beforeClass() {
system = ActorSystem.create("ActorDocTest", config);
}
@AfterClass
public static void afterClass() throws Exception {
Await.result(system.terminate(), Duration.create("5 seconds"));
}
static
//#context-actorOf
public class FirstActor extends AbstractActor {
final ActorRef child = context().actorOf(Props.create(MyActor.class), "myChild");
//#plus-some-behavior
public FirstActor() {
receive(ReceiveBuilder.
matchAny(x -> {
sender().tell(x, self());
}).build()
);
}
//#plus-some-behavior
}
//#context-actorOf
static public abstract class SomeActor extends AbstractActor {
//#receive-constructor
public SomeActor() {
receive(ReceiveBuilder.
//#and-some-behavior
match(String.class, s -> System.out.println(s.toLowerCase())).
//#and-some-behavior
build());
}
//#receive-constructor
@Override
//#receive
public abstract PartialFunction<Object, BoxedUnit> receive();
//#receive
}
static public class ActorWithArgs extends AbstractActor {
private final String args;
ActorWithArgs(String args) {
this.args = args;
receive(ReceiveBuilder.
matchAny(x -> { }).build()
);
}
}
static
//#props-factory
public class DemoActor extends AbstractActor {
/**
* Create Props for an actor of this type.
* @param magicNumber The magic number to be passed to this 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;
DemoActor(Integer magicNumber) {
this.magicNumber = magicNumber;
receive(ReceiveBuilder.
match(Integer.class, i -> {
sender().tell(i + magicNumber, self());
}).build()
);
}
}
//#props-factory
static
//#props-factory
public class SomeOtherActor extends AbstractActor {
// Props(new DemoActor(42)) would not be safe
ActorRef demoActor = context().actorOf(DemoActor.props(42), "demo");
// ...
//#props-factory
public SomeOtherActor() {
receive(emptyBehavior());
}
//#props-factory
}
//#props-factory
public static class Hook extends AbstractActor {
ActorRef target = null;
public Hook() {
receive(emptyBehavior());
}
//#preStart
@Override
public void preStart() {
target = context().actorOf(Props.create(MyActor.class, "target"));
}
//#preStart
//#postStop
@Override
public void postStop() {
//#clean-up-some-resources
final String message = "stopped";
//#tell
// dont forget to think about who is the sender (2nd argument)
target.tell(message, self());
//#tell
final Object result = "";
//#forward
target.forward(result, context());
//#forward
target = null;
//#clean-up-some-resources
}
//#postStop
// compilation test only
public void compileSelections() {
//#selection-local
// will look up this absolute path
context().actorSelection("/user/serviceA/actor");
// will look up sibling beneath same supervisor
context().actorSelection("../joe");
//#selection-local
//#selection-wildcard
// will look all children to serviceB with names starting with worker
context().actorSelection("/user/serviceB/worker*");
// will look up all siblings beneath same supervisor
context().actorSelection("../*");
//#selection-wildcard
//#selection-remote
context().actorSelection("akka.tcp://app@otherhost:1234/user/serviceB");
//#selection-remote
}
}
public static class ReplyException extends AbstractActor {
public ReplyException() {
receive(ReceiveBuilder.
matchAny(x -> {
//#reply-exception
try {
String result = operation();
sender().tell(result, self());
} catch (Exception e) {
sender().tell(new akka.actor.Status.Failure(e), self());
throw e;
}
//#reply-exception
}).build()
);
}
private String operation() {
return "Hi";
}
}
static
//#gracefulStop-actor
public class Manager extends AbstractActor {
private static enum Shutdown {
Shutdown
}
public static final Shutdown SHUTDOWN = Shutdown.Shutdown;
private ActorRef worker =
context().watch(context().actorOf(Props.create(Cruncher.class), "worker"));
public Manager() {
receive(ReceiveBuilder.
matchEquals("job", s -> {
worker.tell("crunch", self());
}).
matchEquals(SHUTDOWN, x -> {
worker.tell(PoisonPill.getInstance(), self());
context().become(shuttingDown);
}).build()
);
}
public PartialFunction<Object, BoxedUnit> shuttingDown =
ReceiveBuilder.
matchEquals("job", s -> {
sender().tell("service unavailable, shutting down", self());
}).
match(Terminated.class, t -> t.actor().equals(worker), t -> {
context().stop(self());
}).build();
}
//#gracefulStop-actor
@Test
public void usePatternsGracefulStop() throws Exception {
ActorRef actorRef = system.actorOf(Props.create(Manager.class));
//#gracefulStop
try {
Future<Boolean> stopped =
gracefulStop(actorRef, Duration.create(5, TimeUnit.SECONDS), Manager.SHUTDOWN);
Await.result(stopped, Duration.create(6, TimeUnit.SECONDS));
// the actor has been stopped
} catch (AskTimeoutException e) {
// the actor wasn't stopped within 5 seconds
}
//#gracefulStop
}
public static class Cruncher extends AbstractActor {
public Cruncher() {
receive(ReceiveBuilder.
matchEquals("crunch", s -> { }).build()
);
}
}
static
//#swapper
public class Swapper extends AbstractLoggingActor {
public Swapper() {
receive(ReceiveBuilder.
matchEquals(Swap, s -> {
log().info("Hi");
context().become(ReceiveBuilder.
matchEquals(Swap, x -> {
log().info("Ho");
context().unbecome(); // resets the latest 'become' (just for fun)
}).build(), false); // push on top instead of replace
}).build()
);
}
}
//#swapper
static
//#swapper
public class SwapperApp {
public static void main(String[] args) {
ActorSystem system = ActorSystem.create("SwapperSystem");
ActorRef swapper = system.actorOf(Props.create(Swapper.class), "swapper");
swapper.tell(Swap, ActorRef.noSender()); // logs Hi
swapper.tell(Swap, ActorRef.noSender()); // logs Ho
swapper.tell(Swap, ActorRef.noSender()); // logs Hi
swapper.tell(Swap, ActorRef.noSender()); // logs Ho
swapper.tell(Swap, ActorRef.noSender()); // logs Hi
swapper.tell(Swap, ActorRef.noSender()); // logs Ho
system.terminate();
}
}
//#swapper
@Test
public void creatingActorWithSystemActorOf() {
//#system-actorOf
// ActorSystem is a heavy object: create only one per application
final ActorSystem system = ActorSystem.create("MySystem", config);
final ActorRef myActor = system.actorOf(Props.create(FirstActor.class), "myactor");
//#system-actorOf
try {
new JavaTestKit(system) {
{
myActor.tell("hello", getRef());
expectMsgEquals("hello");
}
};
} finally {
JavaTestKit.shutdownActorSystem(system);
}
}
@Test
public void creatingPropsConfig() {
//#creating-props
Props props1 = Props.create(MyActor.class);
Props props2 = Props.create(ActorWithArgs.class,
() -> new ActorWithArgs("arg")); // careful, see below
Props props3 = Props.create(ActorWithArgs.class, "arg");
//#creating-props
//#creating-props-deprecated
// NOT RECOMMENDED within another actor:
// encourages to close over enclosing class
Props props7 = Props.create(ActorWithArgs.class,
() -> new ActorWithArgs("arg"));
//#creating-props-deprecated
}
@Test(expected=IllegalArgumentException.class)
public void creatingPropsIllegal() {
//#creating-props-illegal
// This will throw an IllegalArgumentException since some runtime
// type information of the lambda is erased.
// Use Props.create(actorClass, Creator) instead.
Props props = Props.create(() -> new ActorWithArgs("arg"));
//#creating-props-illegal
}
static
//#receive-timeout
public class ReceiveTimeoutActor extends AbstractActor {
//#receive-timeout
ActorRef target = context().system().deadLetters();
//#receive-timeout
public ReceiveTimeoutActor() {
// To set an initial delay
context().setReceiveTimeout(Duration.create("10 seconds"));
receive(ReceiveBuilder.
matchEquals("Hello", s -> {
// To set in a response to a message
context().setReceiveTimeout(Duration.create("1 second"));
//#receive-timeout
target = sender();
target.tell("Hello world", self());
//#receive-timeout
}).
match(ReceiveTimeout.class, r -> {
// To turn it off
context().setReceiveTimeout(Duration.Undefined());
//#receive-timeout
target.tell("timeout", self());
//#receive-timeout
}).build()
);
}
}
//#receive-timeout
@Test
public void using_receiveTimeout() {
final ActorRef myActor = system.actorOf(Props.create(ReceiveTimeoutActor.class));
new JavaTestKit(system) {
{
myActor.tell("Hello", getRef());
expectMsgEquals("Hello world");
expectMsgEquals("timeout");
}
};
}
static
//#hot-swap-actor
public class HotSwapActor extends AbstractActor {
private PartialFunction<Object, BoxedUnit> angry;
private PartialFunction<Object, BoxedUnit> happy;
public HotSwapActor() {
angry =
ReceiveBuilder.
matchEquals("foo", s -> {
sender().tell("I am already angry?", self());
}).
matchEquals("bar", s -> {
context().become(happy);
}).build();
happy = ReceiveBuilder.
matchEquals("bar", s -> {
sender().tell("I am already happy :-)", self());
}).
matchEquals("foo", s -> {
context().become(angry);
}).build();
receive(ReceiveBuilder.
matchEquals("foo", s -> {
context().become(angry);
}).
matchEquals("bar", s -> {
context().become(happy);
}).build()
);
}
}
//#hot-swap-actor
@Test
public void using_hot_swap() {
final ActorRef actor = system.actorOf(Props.create(HotSwapActor.class), "hot");
new JavaTestKit(system) {
{
actor.tell("foo", getRef());
actor.tell("foo", getRef());
expectMsgEquals("I am already angry?");
actor.tell("bar", getRef());
actor.tell("bar", getRef());
expectMsgEquals("I am already happy :-)");
actor.tell("foo", getRef());
actor.tell("foo", getRef());
expectMsgEquals("I am already angry?");
expectNoMsg(Duration.create(1, TimeUnit.SECONDS));
}
};
}
static
//#stash
public class ActorWithProtocol extends AbstractActorWithStash {
public ActorWithProtocol() {
receive(ReceiveBuilder.
matchEquals("open", s -> {
context().become(ReceiveBuilder.
matchEquals("write", ws -> { /* do writing */ }).
matchEquals("close", cs -> {
unstashAll();
context().unbecome();
}).
matchAny(msg -> stash()).build(), false);
}).
matchAny(msg -> stash()).build()
);
}
}
//#stash
@Test
public void using_Stash() {
final ActorRef actor = system.actorOf(Props.create(ActorWithProtocol.class), "stash");
}
static
//#watch
public class WatchActor extends AbstractActor {
private final ActorRef child = context().actorOf(Props.empty(), "target");
private ActorRef lastSender = system.deadLetters();
public WatchActor() {
context().watch(child); // <-- this is the only call needed for registration
receive(ReceiveBuilder.
matchEquals("kill", s -> {
context().stop(child);
lastSender = sender();
}).
match(Terminated.class, t -> t.actor().equals(child), t -> {
lastSender.tell("finished", self());
}).build()
);
}
}
//#watch
@Test
public void using_watch() {
ActorRef actor = system.actorOf(Props.create(WatchActor.class));
new JavaTestKit(system) {
{
actor.tell("kill", getRef());
expectMsgEquals("finished");
}
};
}
static
//#identify
public class Follower extends AbstractActor {
final Integer identifyId = 1;
public Follower(){
ActorSelection selection = context().actorSelection("/user/another");
selection.tell(new Identify(identifyId), self());
receive(ReceiveBuilder.
match(ActorIdentity.class, id -> id.getRef() != null, id -> {
ActorRef ref = id.getRef();
context().watch(ref);
context().become(active(ref));
}).
match(ActorIdentity.class, id -> id.getRef() == null, id -> {
context().stop(self());
}).build()
);
}
final PartialFunction<Object, BoxedUnit> active(final ActorRef another) {
return ReceiveBuilder.
match(Terminated.class, t -> t.actor().equals(another), t -> {
context().stop(self());
}).build();
}
}
//#identify
@Test
public void using_Identify() {
ActorRef a = system.actorOf(Props.empty());
ActorRef b = system.actorOf(Props.create(Follower.class));
new JavaTestKit(system) {
{
watch(b);
system.stop(a);
assertEquals(expectMsgClass(Duration.create(2, TimeUnit.SECONDS), Terminated.class).actor(), b);
}
};
}
public static class NoReceiveActor extends AbstractActor {
}
@Test
public void noReceiveActor() {
EventFilter ex1 = new ErrorFilter(ActorInitializationException.class);
EventFilter[] ignoreExceptions = { ex1 };
try {
system.eventStream().publish(new TestEvent.Mute(immutableSeq(ignoreExceptions)));
new JavaTestKit(system) {{
final ActorRef victim = new EventFilter<ActorRef>(ActorInitializationException.class) {
protected ActorRef run() {
return system.actorOf(Props.create(NoReceiveActor.class), "victim");
}
}.message("Actor behavior has not been set with receive(...)").occurrences(1).exec();
assertEquals(true, victim.isTerminated());
}};
} finally {
system.eventStream().publish(new TestEvent.UnMute(immutableSeq(ignoreExceptions)));
}
}
public static class MultipleReceiveActor extends AbstractActor {
public MultipleReceiveActor() {
receive(ReceiveBuilder.
match(String.class, s1 -> s1.toLowerCase().equals("become"), s1 -> {
sender().tell(s1.toUpperCase(), self());
receive(ReceiveBuilder.
match(String.class, s2 -> {
sender().tell(s2.toLowerCase(), self());
}).build()
);
}).
match(String.class, s1 -> {
sender().tell(s1.toUpperCase(), self());
}).build()
);
}
}
@Test
public void multipleReceiveActor() {
EventFilter ex1 = new ErrorFilter(IllegalActorStateException.class);
EventFilter[] ignoreExceptions = { ex1 };
try {
system.eventStream().publish(new TestEvent.Mute(immutableSeq(ignoreExceptions)));
new JavaTestKit(system) {{
new EventFilter<Boolean>(IllegalActorStateException.class) {
protected Boolean run() {
ActorRef victim = system.actorOf(Props.create(MultipleReceiveActor.class), "victim2");
victim.tell("Foo", getRef());
expectMsgEquals("FOO");
victim.tell("bEcoMe", getRef());
expectMsgEquals("BECOME");
victim.tell("Foo", getRef());
// if it's upper case, then the actor was restarted
expectMsgEquals("FOO");
return true;
}
}.message("Actor behavior has already been set with receive(...), " +
"use context().become(...) to change it later").occurrences(1).exec();
}};
} finally {
system.eventStream().publish(new TestEvent.UnMute(immutableSeq(ignoreExceptions)));
}
}
}

View file

@ -1,209 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor;
//#imports-data
import java.util.ArrayList;
import java.util.List;
import akka.actor.ActorRef;
//#imports-data
//#imports-actor
import akka.event.LoggingAdapter;
import akka.event.Logging;
import akka.actor.UntypedActor;
//#imports-actor
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.testkit.JavaTestKit;
import akka.testkit.TestProbe;
import akka.testkit.AkkaSpec;
import docs.AbstractJavaTest;
public class FSMDocTest extends AbstractJavaTest {
static
//#data
public final class SetTarget {
final ActorRef ref;
public SetTarget(ActorRef ref) {
this.ref = ref;
}
}
//#data
static
//#data
public final class Queue {
final Object o;
public Queue(Object o) {
this.o = o;
}
}
//#data
static
//#data
public final Object flush = new Object();
//#data
static
//#data
public final class Batch {
final List<Object> objects;
public Batch(List<Object> objects) {
this.objects = objects;
}
}
//#data
static
//#base
public abstract class MyFSMBase extends UntypedActor {
/*
* This is the mutable state of this state machine.
*/
protected enum State {
IDLE, ACTIVE;
}
private State state = State.IDLE;
private ActorRef target;
private List<Object> queue;
/*
* Then come all the mutator methods:
*/
protected void init(ActorRef target) {
this.target = target;
queue = new ArrayList<Object>();
}
protected void setState(State s) {
if (state != s) {
transition(state, s);
state = s;
}
}
protected void enqueue(Object o) {
if (queue != null)
queue.add(o);
}
protected List<Object> drainQueue() {
final List<Object> q = queue;
if (q == null)
throw new IllegalStateException("drainQueue(): not yet initialized");
queue = new ArrayList<Object>();
return q;
}
/*
* Here are the interrogation methods:
*/
protected boolean isInitialized() {
return target != null;
}
protected State getState() {
return state;
}
protected ActorRef getTarget() {
if (target == null)
throw new IllegalStateException("getTarget(): not yet initialized");
return target;
}
/*
* And finally the callbacks (only one in this example: react to state change)
*/
abstract protected void transition(State old, State next);
}
//#base
static
//#actor
public class MyFSM extends MyFSMBase {
private final LoggingAdapter log =
Logging.getLogger(getContext().system(), this);
@Override
public void onReceive(Object o) {
if (getState() == State.IDLE) {
if (o instanceof SetTarget)
init(((SetTarget) o).ref);
else
whenUnhandled(o);
} else if (getState() == State.ACTIVE) {
if (o == flush)
setState(State.IDLE);
else
whenUnhandled(o);
}
}
@Override
public void transition(State old, State next) {
if (old == State.ACTIVE) {
getTarget().tell(new Batch(drainQueue()), getSelf());
}
}
private void whenUnhandled(Object o) {
if (o instanceof Queue && isInitialized()) {
enqueue(((Queue) o).o);
setState(State.ACTIVE);
} else {
log.warning("received unknown message {} in state {}", o, getState());
}
}
}
//#actor
ActorSystem system;
@org.junit.Before
public void setUp() {
system = ActorSystem.create("FSMSystem", AkkaSpec.testConf());
}
@org.junit.Test
public void mustBunch() {
final ActorRef buncher = system.actorOf(Props.create(MyFSM.class));
final TestProbe probe = new TestProbe(system);
buncher.tell(new SetTarget(probe.ref()), ActorRef.noSender());
buncher.tell(new Queue(1), ActorRef.noSender());
buncher.tell(new Queue(2), ActorRef.noSender());
buncher.tell(flush, ActorRef.noSender());
buncher.tell(new Queue(3), ActorRef.noSender());
final Batch b = probe.expectMsgClass(Batch.class);
assert b.objects.size() == 2;
assert b.objects.contains(1);
assert b.objects.contains(2);
}
@org.junit.After
public void cleanup() {
JavaTestKit.shutdownActorSystem(system);
}
}

View file

@ -1,224 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor;
//#testkit
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.SupervisorStrategy;
import static akka.actor.SupervisorStrategy.resume;
import static akka.actor.SupervisorStrategy.restart;
import static akka.actor.SupervisorStrategy.stop;
import static akka.actor.SupervisorStrategy.escalate;
import akka.actor.SupervisorStrategy.Directive;
import akka.actor.OneForOneStrategy;
import akka.actor.Props;
import akka.actor.Terminated;
import akka.actor.UntypedActor;
import docs.AbstractJavaTest;
import scala.collection.immutable.Seq;
import scala.concurrent.Await;
import static akka.pattern.Patterns.ask;
import scala.concurrent.duration.Duration;
import akka.testkit.TestProbe;
//#testkit
import akka.testkit.ErrorFilter;
import akka.testkit.EventFilter;
import akka.testkit.TestEvent;
import akka.testkit.JavaTestKit;
import static java.util.concurrent.TimeUnit.SECONDS;
import static akka.japi.Util.immutableSeq;
import akka.japi.Function;
import scala.Option;
import org.junit.Test;
import org.junit.BeforeClass;
import org.junit.AfterClass;
//#testkit
public class FaultHandlingTest extends AbstractJavaTest {
//#testkit
static
//#supervisor
public class Supervisor extends UntypedActor {
//#strategy
private static SupervisorStrategy strategy =
new OneForOneStrategy(10, Duration.create("1 minute"),
new Function<Throwable, Directive>() {
@Override
public Directive apply(Throwable t) {
if (t instanceof ArithmeticException) {
return resume();
} else if (t instanceof NullPointerException) {
return restart();
} else if (t instanceof IllegalArgumentException) {
return stop();
} else {
return escalate();
}
}
});
@Override
public SupervisorStrategy supervisorStrategy() {
return strategy;
}
//#strategy
public void onReceive(Object o) {
if (o instanceof Props) {
getSender().tell(getContext().actorOf((Props) o), getSelf());
} else {
unhandled(o);
}
}
}
//#supervisor
static
//#supervisor2
public class Supervisor2 extends UntypedActor {
//#strategy2
private static SupervisorStrategy strategy = new OneForOneStrategy(10,
Duration.create("1 minute"),
new Function<Throwable, Directive>() {
@Override
public Directive apply(Throwable t) {
if (t instanceof ArithmeticException) {
return resume();
} else if (t instanceof NullPointerException) {
return restart();
} else if (t instanceof IllegalArgumentException) {
return stop();
} else {
return escalate();
}
}
});
@Override
public SupervisorStrategy supervisorStrategy() {
return strategy;
}
//#strategy2
public void onReceive(Object o) {
if (o instanceof Props) {
getSender().tell(getContext().actorOf((Props) o), getSelf());
} else {
unhandled(o);
}
}
@Override
public void preRestart(Throwable cause, Option<Object> msg) {
// do not kill all children, which is the default here
}
}
//#supervisor2
static
//#child
public class Child extends UntypedActor {
int state = 0;
public void onReceive(Object o) throws Exception {
if (o instanceof Exception) {
throw (Exception) o;
} else if (o instanceof Integer) {
state = (Integer) o;
} else if (o.equals("get")) {
getSender().tell(state, getSelf());
} else {
unhandled(o);
}
}
}
//#child
//#testkit
static ActorSystem system;
Duration timeout = Duration.create(5, SECONDS);
@BeforeClass
public static void start() {
system = ActorSystem.create("FaultHandlingTest");
}
@AfterClass
public static void cleanup() {
JavaTestKit.shutdownActorSystem(system);
system = null;
}
@Test
public void mustEmploySupervisorStrategy() throws Exception {
// code here
//#testkit
EventFilter ex1 = new ErrorFilter(ArithmeticException.class);
EventFilter ex2 = new ErrorFilter(NullPointerException.class);
EventFilter ex3 = new ErrorFilter(IllegalArgumentException.class);
EventFilter ex4 = new ErrorFilter(Exception.class);
EventFilter[] ignoreExceptions = { ex1, ex2, ex3, ex4 };
Seq<EventFilter> seq = immutableSeq(ignoreExceptions);
system.eventStream().publish(new TestEvent.Mute(seq));
//#create
Props superprops = Props.create(Supervisor.class);
ActorRef supervisor = system.actorOf(superprops, "supervisor");
ActorRef child = (ActorRef) Await.result(ask(supervisor,
Props.create(Child.class), 5000), timeout);
//#create
//#resume
child.tell(42, ActorRef.noSender());
assert Await.result(ask(child, "get", 5000), timeout).equals(42);
child.tell(new ArithmeticException(), ActorRef.noSender());
assert Await.result(ask(child, "get", 5000), timeout).equals(42);
//#resume
//#restart
child.tell(new NullPointerException(), ActorRef.noSender());
assert Await.result(ask(child, "get", 5000), timeout).equals(0);
//#restart
//#stop
final TestProbe probe = new TestProbe(system);
probe.watch(child);
child.tell(new IllegalArgumentException(), ActorRef.noSender());
probe.expectMsgClass(Terminated.class);
//#stop
//#escalate-kill
child = (ActorRef) Await.result(ask(supervisor,
Props.create(Child.class), 5000), timeout);
probe.watch(child);
assert Await.result(ask(child, "get", 5000), timeout).equals(0);
child.tell(new Exception(), ActorRef.noSender());
probe.expectMsgClass(Terminated.class);
//#escalate-kill
//#escalate-restart
superprops = Props.create(Supervisor2.class);
supervisor = system.actorOf(superprops);
child = (ActorRef) Await.result(ask(supervisor,
Props.create(Child.class), 5000), timeout);
child.tell(23, ActorRef.noSender());
assert Await.result(ask(child, "get", 5000), timeout).equals(23);
child.tell(new Exception(), ActorRef.noSender());
assert Await.result(ask(child, "get", 5000), timeout).equals(0);
//#escalate-restart
//#testkit
}
}
//#testkit

View file

@ -1,205 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor;
//#testkit
import akka.actor.*;
import static akka.actor.SupervisorStrategy.resume;
import static akka.actor.SupervisorStrategy.restart;
import static akka.actor.SupervisorStrategy.stop;
import static akka.actor.SupervisorStrategy.escalate;
import akka.japi.pf.DeciderBuilder;
import akka.japi.pf.ReceiveBuilder;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import org.scalatest.junit.JUnitSuite;
import scala.PartialFunction;
import scala.concurrent.Await;
import static akka.pattern.Patterns.ask;
import scala.concurrent.duration.Duration;
import akka.testkit.TestProbe;
//#testkit
import akka.testkit.ErrorFilter;
import akka.testkit.EventFilter;
import akka.testkit.TestEvent;
import akka.testkit.JavaTestKit;
import static java.util.concurrent.TimeUnit.SECONDS;
import static akka.japi.Util.immutableSeq;
import scala.Option;
import org.junit.Test;
import org.junit.BeforeClass;
import org.junit.AfterClass;
import scala.runtime.BoxedUnit;
//#testkit
public class FaultHandlingTestJava8 extends JUnitSuite {
//#testkit
public static Config config = ConfigFactory.parseString(
"akka {\n" +
" loggers = [\"akka.testkit.TestEventListener\"]\n" +
" loglevel = \"WARNING\"\n" +
" stdout-loglevel = \"WARNING\"\n" +
"}\n");
static
//#supervisor
public class Supervisor extends AbstractActor {
//#strategy
private static SupervisorStrategy strategy =
new OneForOneStrategy(10, Duration.create("1 minute"), DeciderBuilder.
match(ArithmeticException.class, e -> resume()).
match(NullPointerException.class, e -> restart()).
match(IllegalArgumentException.class, e -> stop()).
matchAny(o -> escalate()).build());
@Override
public SupervisorStrategy supervisorStrategy() {
return strategy;
}
//#strategy
public Supervisor() {
receive(ReceiveBuilder.
match(Props.class, props -> {
sender().tell(context().actorOf(props), self());
}).build()
);
}
}
//#supervisor
static
//#supervisor2
public class Supervisor2 extends AbstractActor {
//#strategy2
private static SupervisorStrategy strategy =
new OneForOneStrategy(10, Duration.create("1 minute"), DeciderBuilder.
match(ArithmeticException.class, e -> resume()).
match(NullPointerException.class, e -> restart()).
match(IllegalArgumentException.class, e -> stop()).
matchAny(o -> escalate()).build());
@Override
public SupervisorStrategy supervisorStrategy() {
return strategy;
}
//#strategy2
public Supervisor2() {
receive(ReceiveBuilder.
match(Props.class, props -> {
sender().tell(context().actorOf(props), self());
}).build()
);
}
@Override
public void preRestart(Throwable cause, Option<Object> msg) {
// do not kill all children, which is the default here
}
}
//#supervisor2
static
//#child
public class Child extends AbstractActor {
int state = 0;
public Child() {
receive(ReceiveBuilder.
match(Exception.class, exception -> { throw exception; }).
match(Integer.class, i -> state = i).
matchEquals("get", s -> sender().tell(state, self())).build()
);
}
}
//#child
//#testkit
static ActorSystem system;
Duration timeout = Duration.create(5, SECONDS);
@BeforeClass
public static void start() {
system = ActorSystem.create("FaultHandlingTest", config);
}
@AfterClass
public static void cleanup() {
JavaTestKit.shutdownActorSystem(system);
system = null;
}
@Test
public void mustEmploySupervisorStrategy() throws Exception {
// code here
//#testkit
EventFilter ex1 = new ErrorFilter(ArithmeticException.class);
EventFilter ex2 = new ErrorFilter(NullPointerException.class);
EventFilter ex3 = new ErrorFilter(IllegalArgumentException.class);
EventFilter ex4 = new ErrorFilter(Exception.class);
EventFilter[] ignoreExceptions = { ex1, ex2, ex3, ex4 };
system.eventStream().publish(new TestEvent.Mute(immutableSeq(ignoreExceptions)));
//#create
Props superprops = Props.create(Supervisor.class);
ActorRef supervisor = system.actorOf(superprops, "supervisor");
ActorRef child = (ActorRef) Await.result(ask(supervisor,
Props.create(Child.class), 5000), timeout);
//#create
//#resume
child.tell(42, ActorRef.noSender());
assert Await.result(ask(child, "get", 5000), timeout).equals(42);
child.tell(new ArithmeticException(), ActorRef.noSender());
assert Await.result(ask(child, "get", 5000), timeout).equals(42);
//#resume
//#restart
child.tell(new NullPointerException(), ActorRef.noSender());
assert Await.result(ask(child, "get", 5000), timeout).equals(0);
//#restart
//#stop
final TestProbe probe = new TestProbe(system);
probe.watch(child);
child.tell(new IllegalArgumentException(), ActorRef.noSender());
probe.expectMsgClass(Terminated.class);
//#stop
//#escalate-kill
child = (ActorRef) Await.result(ask(supervisor,
Props.create(Child.class), 5000), timeout);
probe.watch(child);
assert Await.result(ask(child, "get", 5000), timeout).equals(0);
child.tell(new Exception(), ActorRef.noSender());
probe.expectMsgClass(Terminated.class);
//#escalate-kill
//#escalate-restart
superprops = Props.create(Supervisor2.class);
supervisor = system.actorOf(superprops);
child = (ActorRef) Await.result(ask(supervisor,
Props.create(Child.class), 5000), timeout);
child.tell(23, ActorRef.noSender());
assert Await.result(ask(child, "get", 5000), timeout).equals(23);
child.tell(new Exception(), ActorRef.noSender());
assert Await.result(ask(child, "get", 5000), timeout).equals(0);
//#escalate-restart
//#testkit
}
}
//#testkit

View file

@ -1,21 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor;
import akka.actor.ActorRef;
import akka.actor.Props;
import akka.actor.PoisonPill;
import akka.actor.UntypedActor;
//#context-actorOf
public class FirstUntypedActor extends UntypedActor {
ActorRef myActor = getContext().actorOf(Props.create(MyActor.class), "myactor");
//#context-actorOf
public void onReceive(Object message) {
myActor.forward(message, getContext());
myActor.tell(PoisonPill.getInstance(), ActorRef.noSender());
}
}

View file

@ -1,87 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor;
import akka.actor.*;
import akka.japi.Procedure;
import akka.testkit.AkkaJUnitActorSystemResource;
import akka.testkit.JavaTestKit;
import docs.AbstractJavaTest;
import org.junit.ClassRule;
import org.junit.Test;
import scala.Option;
public class InitializationDocSpecJava extends AbstractJavaTest {
static public class PreStartInitExample extends UntypedActor {
public void onReceive(Object message) throws Exception {}
//#preStartInit
@Override
public void preStart() {
// Initialize children here
}
// Overriding postRestart to disable the call to preStart()
// after restarts
@Override
public void postRestart(Throwable reason) {
}
// The default implementation of preRestart() stops all the children
// of the actor. To opt-out from stopping the children, we
// have to override preRestart()
@Override
public void preRestart(Throwable reason, Option<Object> message)
throws Exception {
// Keep the call to postStop(), but no stopping of children
postStop();
}
//#preStartInit
}
public static class MessageInitExample extends UntypedActor {
//#messageInit
private String initializeMe = null;
@Override
public void onReceive(Object message) throws Exception {
if (message.equals("init")) {
initializeMe = "Up and running";
getContext().become(new Procedure<Object>() {
@Override
public void apply(Object message) throws Exception {
if (message.equals("U OK?"))
getSender().tell(initializeMe, getSelf());
}
});
}
}
//#messageInit
}
@ClassRule
public static AkkaJUnitActorSystemResource actorSystemResource =
new AkkaJUnitActorSystemResource("InitializationDocSpecJava");
private final ActorSystem system = actorSystemResource.getSystem();
@Test
public void testIt() {
new JavaTestKit(system) {{
ActorRef testactor = system.actorOf(Props.create(MessageInitExample.class), "testactor");
String probe = "U OK?";
testactor.tell(probe, getRef());
expectNoMsg();
testactor.tell("init", getRef());
testactor.tell(probe, getRef());
expectMsgEquals("Up and running");
}};
}
}

View file

@ -1,68 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor;
import akka.actor.*;
import akka.japi.pf.ReceiveBuilder;
import akka.testkit.JavaTestKit;
import docs.AbstractJavaTest;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import scala.PartialFunction;
import scala.concurrent.duration.Duration;
import scala.runtime.BoxedUnit;
import scala.concurrent.Await;
import java.util.concurrent.TimeUnit;
public class InitializationDocTest extends AbstractJavaTest {
static ActorSystem system = null;
@BeforeClass
public static void beforeClass() {
system = ActorSystem.create("InitializationDocTest");
}
@AfterClass
public static void afterClass() throws Exception {
Await.result(system.terminate(), Duration.create("5 seconds"));
}
public static class MessageInitExample extends AbstractActor {
private String initializeMe = null;
public MessageInitExample() {
//#messageInit
receive(ReceiveBuilder.
matchEquals("init", m1 -> {
initializeMe = "Up and running";
context().become(ReceiveBuilder.
matchEquals("U OK?", m2 -> {
sender().tell(initializeMe, self());
}).build());
}).build()
//#messageInit
);
}
}
@Test
public void testIt() {
new JavaTestKit(system) {{
ActorRef testactor = system.actorOf(Props.create(MessageInitExample.class), "testactor");
String msg = "U OK?";
testactor.tell(msg, getRef());
expectNoMsg(Duration.create(1, TimeUnit.SECONDS));
testactor.tell("init", getRef());
testactor.tell(msg, getRef());
expectMsgEquals("Up and running");
}};
}
}

View file

@ -1,33 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor;
//#imports
import akka.actor.AbstractActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.japi.pf.ReceiveBuilder;
//#imports
//#my-actor
public class MyJavaActor extends AbstractActor {
private final LoggingAdapter log = Logging.getLogger(context().system(), this);
public MyJavaActor() {
receive(ReceiveBuilder.
match(String.class, s -> {
log.info("Received String message: {}", s);
//#my-actor
//#reply
sender().tell(s, self());
//#reply
//#my-actor
}).
matchAny(o -> log.info("received unknown message")).build()
);
}
}
//#my-actor

View file

@ -1,41 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor;
//#receive-timeout
import akka.actor.ActorRef;
import akka.actor.ReceiveTimeout;
import akka.actor.UntypedActor;
import scala.concurrent.duration.Duration;
public class MyReceiveTimeoutUntypedActor extends UntypedActor {
//#receive-timeout
ActorRef target = getContext().system().deadLetters();
//#receive-timeout
public MyReceiveTimeoutUntypedActor() {
// To set an initial delay
getContext().setReceiveTimeout(Duration.create("30 seconds"));
}
public void onReceive(Object message) {
if (message.equals("Hello")) {
// To set in a response to a message
getContext().setReceiveTimeout(Duration.create("1 second"));
//#receive-timeout
target = getSender();
target.tell("Hello world", getSelf());
//#receive-timeout
} else if (message instanceof ReceiveTimeout) {
// To turn it off
getContext().setReceiveTimeout(Duration.Undefined());
//#receive-timeout
target.tell("timeout", getSelf());
//#receive-timeout
} else {
unhandled(message);
}
}
}
//#receive-timeout

View file

@ -1,29 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor;
//#my-stopping-actor
import akka.actor.ActorRef;
import akka.actor.UntypedActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
public class MyStoppingActor extends UntypedActor {
ActorRef child = null;
// ... creation of child ...
public void onReceive(Object message) throws Exception {
if (message.equals("interrupt-child")) {
context().stop(child);
} else if (message.equals("done")) {
context().stop(getSelf());
} else {
unhandled(message);
}
}
}
//#my-stopping-actor

View file

@ -1,23 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor;
//#my-untyped-actor
import akka.actor.UntypedActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
public class MyUntypedActor extends UntypedActor {
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
public void onReceive(Object message) throws Exception {
if (message instanceof String) {
log.info("Received String message: {}", message);
getSender().tell(message, getSelf());
} else
unhandled(message);
}
}
//#my-untyped-actor

View file

@ -1,36 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor;
//#sample-actor
import akka.actor.AbstractActor;
import akka.japi.pf.ReceiveBuilder;
import scala.PartialFunction;
import scala.runtime.BoxedUnit;
public class SampleActor extends AbstractActor {
private PartialFunction<Object, BoxedUnit> guarded = ReceiveBuilder.
match(String.class, s -> s.contains("guard"), s -> {
sender().tell("contains(guard): " + s, self());
context().unbecome();
}).build();
public SampleActor() {
receive(ReceiveBuilder.
match(Double.class, d -> {
sender().tell(d.isNaN() ? 0 : d, self());
}).
match(Integer.class, i -> {
sender().tell(i * 10, self());
}).
match(String.class, s -> s.startsWith("guard"), s -> {
sender().tell("startsWith(guard): " + s.toUpperCase(), self());
context().become(guarded, false);
}).build()
);
}
}
//#sample-actor

View file

@ -1,56 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.testkit.JavaTestKit;
import docs.AbstractJavaTest;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
public class SampleActorTest extends AbstractJavaTest {
static ActorSystem system;
@BeforeClass
public static void setup() {
system = ActorSystem.create("SampleActorTest");
}
@AfterClass
public static void tearDown() {
JavaTestKit.shutdownActorSystem(system);
system = null;
}
@Test
public void testSampleActor()
{
new JavaTestKit(system) {{
final ActorRef subject = system.actorOf(Props.create(SampleActor.class), "sample-actor");
final ActorRef probeRef = getRef();
subject.tell(47.11, probeRef);
subject.tell("and no guard in the beginning", probeRef);
subject.tell("guard is a good thing", probeRef);
subject.tell(47.11, probeRef);
subject.tell(4711, probeRef);
subject.tell("and no guard in the beginning", probeRef);
subject.tell(4711, probeRef);
subject.tell("and an unmatched message", probeRef);
expectMsgEquals(47.11);
assertTrue(expectMsgClass(String.class).startsWith("startsWith(guard):"));
assertTrue(expectMsgClass(String.class).startsWith("contains(guard):"));
expectMsgEquals(47110);
expectNoMsg();
}};
}
}

View file

@ -11,10 +11,11 @@ import java.util.concurrent.TimeUnit;
//#imports1
//#imports2
import akka.actor.UntypedActor;
import akka.actor.Cancellable;
//#imports2
import docs.actorlambda.MyActor;
import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.testkit.AkkaSpec;
@ -28,7 +29,7 @@ public class SchedulerDocTest extends AbstractJavaTest {
AkkaSpec.testConf());
private final ActorSystem system = actorSystemResource.getSystem();
private ActorRef testActor = system.actorOf(Props.create(MyUntypedActor.class));
private ActorRef testActor = system.actorOf(Props.create(MyActor.class));
@Test
public void scheduleOneOffTask() {
@ -51,14 +52,14 @@ public class SchedulerDocTest extends AbstractJavaTest {
@Test
public void scheduleRecurringTask() {
//#schedule-recurring
class Ticker extends UntypedActor {
class Ticker extends AbstractActor {
@Override
public void onReceive(Object message) {
if (message.equals("Tick")) {
// Do someting
} else {
unhandled(message);
}
public Receive createReceive() {
return receiveBuilder()
.matchEquals("Tick", m -> {
// Do someting
})
.build();
}
}

View file

@ -1,794 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor;
//#import-ask
import static akka.pattern.Patterns.ask;
import static akka.pattern.Patterns.pipe;
//#import-ask
//#import-gracefulStop
import static akka.pattern.Patterns.gracefulStop;
//#import-gracefulStop
import akka.actor.PoisonPill;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;
import akka.testkit.AkkaJUnitActorSystemResource;
import docs.AbstractJavaTest;
import org.junit.ClassRule;
import org.junit.Test;
//#import-gracefulStop
import scala.concurrent.Await;
//#import-ask
import scala.concurrent.Future;
import scala.concurrent.duration.Duration;
//#import-ask
//#import-gracefulStop
//#import-indirect
import akka.actor.Actor;
//#import-indirect
//#import-identify
import akka.actor.ActorIdentity;
//#import-identify
import akka.actor.ActorKilledException;
//#import-identify
import akka.actor.ActorSelection;
//#import-identify
//#import-actorRef
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
//#import-actorRef
//#import-identify
import akka.actor.Identify;
//#import-identify
//#import-indirect
import akka.actor.IndirectActorProducer;
//#import-indirect
import akka.actor.OneForOneStrategy;
//#import-props
import akka.actor.Props;
import akka.japi.Creator;
//#import-props
import akka.actor.SupervisorStrategy;
import akka.actor.SupervisorStrategy.Directive;
//#import-terminated
import akka.actor.Terminated;
//#import-terminated
//#import-untypedActor
import akka.actor.UntypedActor;
//#import-untypedActor
//#import-stash
import akka.actor.UntypedActorWithStash;
//#import-stash
//#import-ask
import akka.dispatch.Futures;
import akka.dispatch.Mapper;
//#import-ask
import akka.japi.Function;
//#import-procedure
import akka.japi.Procedure;
//#import-procedure
//#import-gracefulStop
import akka.pattern.AskTimeoutException;
//#import-gracefulStop
import akka.pattern.Patterns;
import akka.testkit.AkkaSpec;
import akka.testkit.JavaTestKit;
//#import-ask
import akka.util.Timeout;
//#import-ask
public class UntypedActorDocTest extends AbstractJavaTest {
@ClassRule
public static AkkaJUnitActorSystemResource actorSystemResource =
new AkkaJUnitActorSystemResource("UntypedActorDocTest", AkkaSpec.testConf());
private final ActorSystem system = actorSystemResource.getSystem();
//#creating-props-config
static class MyActorC implements Creator<MyActor> {
@Override public MyActor create() {
return new MyActor("...");
}
}
//#creating-props-config
@SuppressWarnings("unused")
@Test
public void createProps() {
//#creating-props-config
Props props1 = Props.create(MyUntypedActor.class);
Props props2 = Props.create(MyActor.class, "...");
Props props3 = Props.create(new MyActorC());
//#creating-props-config
}
//#parametric-creator
static class ParametricCreator<T extends MyActor> implements Creator<T> {
@Override public T create() {
// ... fabricate actor here
//#parametric-creator
return null;
//#parametric-creator
}
}
//#parametric-creator
@Test
public void systemActorOf() {
//#system-actorOf
// ActorSystem is a heavy object: create only one per application
final ActorSystem system = ActorSystem.create("MySystem");
final ActorRef myActor = system.actorOf(Props.create(MyUntypedActor.class),
"myactor");
//#system-actorOf
try {
new JavaTestKit(system) {
{
myActor.tell("hello", getRef());
expectMsgEquals("hello");
}
};
} finally {
JavaTestKit.shutdownActorSystem(system);
}
}
@Test
public void contextActorOf() {
new JavaTestKit(system) {
{
//#context-actorOf
class A extends UntypedActor {
final ActorRef child =
getContext().actorOf(Props.create(MyUntypedActor.class), "myChild");
//#plus-some-behavior
@Override
public void onReceive(Object msg) {
getSender().tell(child, getSelf());
}
//#plus-some-behavior
}
//#context-actorOf
final ActorRef top = system.actorOf(Props.create(A.class, this));
top.tell("hello", getRef());
final ActorRef child = expectMsgClass(ActorRef.class);
child.tell("hello", getRef());
expectMsgEquals("hello");
}
};
}
// this is just to make the test below a tiny fraction nicer
private ActorSystem getContext() {
return system;
}
static
//#creating-indirectly
class DependencyInjector implements IndirectActorProducer {
final Object applicationContext;
final String beanName;
public DependencyInjector(Object applicationContext, String beanName) {
this.applicationContext = applicationContext;
this.beanName = beanName;
}
@Override
public Class<? extends Actor> actorClass() {
return MyActor.class;
}
@Override
public MyActor produce() {
MyActor result;
//#obtain-fresh-Actor-instance-from-DI-framework
result = new MyActor((String) applicationContext);
//#obtain-fresh-Actor-instance-from-DI-framework
return result;
}
}
//#creating-indirectly
@Test
public void indirectActorOf() {
final String applicationContext = "...";
//#creating-indirectly
final ActorRef myActor = getContext().actorOf(
Props.create(DependencyInjector.class, applicationContext, "MyActor"),
"myactor3");
//#creating-indirectly
new JavaTestKit(system) {
{
myActor.tell("hello", getRef());
expectMsgEquals("...");
}
};
}
@SuppressWarnings("unused")
@Test
public void usingAsk() throws Exception {
ActorRef myActor = system.actorOf(Props.create(MyAskActor.class, this), "myactor5");
//#using-ask
Future<Object> future = Patterns.ask(myActor, "Hello", 1000);
Object result = Await.result(future, Duration.create(1, TimeUnit.SECONDS));
//#using-ask
}
@Test
public void receiveTimeout() {
final ActorRef myActor = system.actorOf(Props.create(MyReceiveTimeoutUntypedActor.class));
new JavaTestKit(system) {
{
new Within(Duration.create(1, TimeUnit.SECONDS), Duration.create(1500,
TimeUnit.MILLISECONDS)) {
@Override
protected void run() {
myActor.tell("Hello", getRef());
expectMsgEquals("Hello world");
expectMsgEquals("timeout");
}
};
}
};
}
@Test
public void usePoisonPill() {
final ActorRef myActor = system.actorOf(Props.create(MyUntypedActor.class));
new JavaTestKit(system) {
{
final ActorRef sender = getRef();
//#poison-pill
myActor.tell(akka.actor.PoisonPill.getInstance(), sender);
//#poison-pill
watch(myActor);
expectTerminated(myActor);
}
};
}
@Test
public void useKill() {
new JavaTestKit(system) {
{
class Master extends UntypedActor {
private SupervisorStrategy strategy = new OneForOneStrategy(-1,
Duration.Undefined(), new Function<Throwable, Directive>() {
@Override
public Directive apply(Throwable thr) {
if (thr instanceof ActorKilledException) {
target.tell("killed", getSelf());
getContext().stop(getSelf());
return SupervisorStrategy.stop();
}
return SupervisorStrategy.escalate();
}
});
final ActorRef target;
ActorRef child;
//#preStart
@Override
public void preStart() {
child = getContext().actorOf(Props.empty());
}
//#preStart
@SuppressWarnings("unused")
public Master(ActorRef target) {
this.target = target;
/*
* Only compilation of `forward` is verified here.
*/
final Object result = "";
//#forward
target.forward(result, getContext());
//#forward
}
@Override
public SupervisorStrategy supervisorStrategy() {
return strategy;
}
//#reply
@Override
public void onReceive(Object msg) {
Object result =
//#calculate-result
child;
//#calculate-result
// do not forget the second argument!
getSender().tell(result, getSelf());
}
//#reply
//#postStop
@Override
public void postStop() {
//#clean-up-resources-here
final String message = "stopped";
//#tell
// dont forget to think about who is the sender (2nd argument)
target.tell(message, getSelf());
//#tell
//#clean-up-resources-here
}
//#postStop
}
final ActorRef master = system.actorOf(Props.create(Master.class, this, getRef()));
expectMsgEquals("");
master.tell("", getRef());
final ActorRef victim = expectMsgClass(ActorRef.class);
//#kill
victim.tell(akka.actor.Kill.getInstance(), ActorRef.noSender());
//#kill
expectMsgEquals("killed");
expectMsgEquals("stopped");
assert getLastSender().equals(master);
}
};
}
@Test
public void useBecome() {
new JavaTestKit(system) {
{
ActorRef myActor = system.actorOf(Props.create(HotSwapActor.class));
myActor.tell("foo", getRef());
myActor.tell("bar", getRef());
expectMsgEquals("I am already happy :-)");
myActor.tell("bar", getRef());
expectMsgEquals("I am already happy :-)");
}
};
}
@Test
public void useWatch() throws Exception {
ActorRef myActor = system.actorOf(Props.create(WatchActor.class));
Future<Object> future = Patterns.ask(myActor, "kill", 1000);
assert Await.result(future, Duration.create("1 second")).equals("finished");
}
// compilation test only
public void compileSelections() {
//#selection-local
// will look up this absolute path
getContext().actorSelection("/user/serviceA/actor");
// will look up sibling beneath same supervisor
getContext().actorSelection("../joe");
//#selection-local
//#selection-wildcard
// will look all children to serviceB with names starting with worker
getContext().actorSelection("/user/serviceB/worker*");
// will look up all siblings beneath same supervisor
getContext().actorSelection("../*");
//#selection-wildcard
//#selection-remote
getContext().actorSelection("akka.tcp://app@otherhost:1234/user/serviceB");
//#selection-remote
}
@Test
public void useIdentify() throws Exception {
new JavaTestKit(system) {
{
ActorRef a = system.actorOf(Props.create(MyUntypedActor.class), "another");
ActorRef b = system.actorOf(Props.create(Follower.class, getRef()));
expectMsgEquals(a);
system.stop(a);
watch(b);
expectTerminated(b);
}
};
}
@Test
public void usePatternsGracefulStop() throws Exception {
ActorRef actorRef = system.actorOf(Props.create(Manager.class));
//#gracefulStop
try {
Future<Boolean> stopped =
gracefulStop(actorRef, Duration.create(5, TimeUnit.SECONDS), Manager.SHUTDOWN);
Await.result(stopped, Duration.create(6, TimeUnit.SECONDS));
// the actor has been stopped
} catch (AskTimeoutException e) {
// the actor wasn't stopped within 5 seconds
}
//#gracefulStop
}
class Result {
final String x;
final String s;
public Result(String x, String s) {
this.x = x;
this.s = s;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((s == null) ? 0 : s.hashCode());
result = prime * result + ((x == null) ? 0 : x.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Result other = (Result) obj;
if (s == null) {
if (other.s != null)
return false;
} else if (!s.equals(other.s))
return false;
if (x == null) {
if (other.x != null)
return false;
} else if (!x.equals(other.x))
return false;
return true;
}
}
@Test
public void usePatternsAskPipe() {
new JavaTestKit(system) {
{
ActorRef actorA = system.actorOf(Props.create(MyUntypedActor.class));
ActorRef actorB = system.actorOf(Props.create(MyUntypedActor.class));
ActorRef actorC = getRef();
//#ask-pipe
final Timeout t = new Timeout(Duration.create(5, TimeUnit.SECONDS));
final ArrayList<Future<Object>> futures = new ArrayList<Future<Object>>();
futures.add(ask(actorA, "request", 1000)); // using 1000ms timeout
futures.add(ask(actorB, "another request", t)); // using timeout from
// above
final Future<Iterable<Object>> aggregate = Futures.sequence(futures,
system.dispatcher());
final Future<Result> transformed = aggregate.map(
new Mapper<Iterable<Object>, Result>() {
public Result apply(Iterable<Object> coll) {
final Iterator<Object> it = coll.iterator();
final String x = (String) it.next();
final String s = (String) it.next();
return new Result(x, s);
}
}, system.dispatcher());
pipe(transformed, system.dispatcher()).to(actorC);
//#ask-pipe
expectMsgEquals(new Result("request", "another request"));
}
};
}
static
//#props-factory
public class DemoActor extends UntypedActor {
/**
* Create Props for an actor of this type.
* @param magicNumber The magic number to be passed to this actors constructor.
* @return a Props for creating this actor, which can then be further configured
* (e.g. calling `.withDispatcher()` on it)
*/
public static Props props(final int magicNumber) {
return Props.create(new Creator<DemoActor>() {
private static final long serialVersionUID = 1L;
@Override
public DemoActor create() throws Exception {
return new DemoActor(magicNumber);
}
});
}
final int magicNumber;
public DemoActor(int magicNumber) {
this.magicNumber = magicNumber;
}
@Override
public void onReceive(Object msg) {
// some behavior here
}
}
//#props-factory
@Test
public void demoActor() {
//#props-factory
system.actorOf(DemoActor.props(42), "demo");
//#props-factory
}
static
//#messages-in-companion
public class DemoMessagesActor extends UntypedActor {
static public class Greeting {
private final String from;
public Greeting(String from) {
this.from = from;
}
public String getGreeter() {
return from;
}
}
public void onReceive(Object message) throws Exception {
if (message instanceof Greeting) {
getSender().tell("Hello " + ((Greeting) message).getGreeter(), getSelf());
} else
unhandled(message);
}
}
//#messages-in-companion
public static class MyActor extends UntypedActor {
final String s;
public MyActor(String s) {
this.s = s;
}
public void onReceive(Object message) {
getSender().tell(s, getSelf());
}
/*
* This section must be kept in sync with the actual Actor trait.
*
* BOYSCOUT RULE: whenever you read this, verify that!
*/
//#lifecycle-callbacks
public void preStart() {
}
public void preRestart(Throwable reason, scala.Option<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 class MyAskActor extends UntypedActor {
public void onReceive(Object message) throws Exception {
//#reply-exception
try {
String result = operation();
getSender().tell(result, getSelf());
} catch (Exception e) {
getSender().tell(new akka.actor.Status.Failure(e), getSelf());
throw e;
}
//#reply-exception
}
private String operation() {
return "Hi";
}
}
static
//#gracefulStop-actor
public class Manager extends UntypedActor {
public static final String SHUTDOWN = "shutdown";
ActorRef worker = getContext().watch(getContext().actorOf(
Props.create(Cruncher.class), "worker"));
public void onReceive(Object message) {
if (message.equals("job")) {
worker.tell("crunch", getSelf());
} else if (message.equals(SHUTDOWN)) {
worker.tell(PoisonPill.getInstance(), getSelf());
getContext().become(shuttingDown);
}
}
Procedure<Object> shuttingDown = new Procedure<Object>() {
@Override
public void apply(Object message) {
if (message.equals("job")) {
getSender().tell("service unavailable, shutting down", getSelf());
} else if (message instanceof Terminated) {
getContext().stop(getSelf());
}
}
};
}
//#gracefulStop-actor
static class Cruncher extends UntypedActor {
public void onReceive(Object message) {
// crunch...
}
}
static
//#hot-swap-actor
public class HotSwapActor extends UntypedActor {
Procedure<Object> angry = new Procedure<Object>() {
@Override
public void apply(Object message) {
if (message.equals("bar")) {
getSender().tell("I am already angry?", getSelf());
} else if (message.equals("foo")) {
getContext().become(happy);
}
}
};
Procedure<Object> happy = new Procedure<Object>() {
@Override
public void apply(Object message) {
if (message.equals("bar")) {
getSender().tell("I am already happy :-)", getSelf());
} else if (message.equals("foo")) {
getContext().become(angry);
}
}
};
public void onReceive(Object message) {
if (message.equals("bar")) {
getContext().become(angry);
} else if (message.equals("foo")) {
getContext().become(happy);
} else {
unhandled(message);
}
}
}
//#hot-swap-actor
static
//#stash
public class ActorWithProtocol extends UntypedActorWithStash {
public void onReceive(Object msg) {
if (msg.equals("open")) {
unstashAll();
getContext().become(new Procedure<Object>() {
public void apply(Object msg) throws Exception {
if (msg.equals("write")) {
// do writing...
} else if (msg.equals("close")) {
unstashAll();
getContext().unbecome();
} else {
stash();
}
}
}, false); // add behavior on top instead of replacing
} else {
stash();
}
}
}
//#stash
static
//#watch
public class WatchActor extends UntypedActor {
final ActorRef child = this.getContext().actorOf(Props.empty(), "child");
{
this.getContext().watch(child); // <-- the only call needed for registration
}
ActorRef lastSender = getContext().system().deadLetters();
@Override
public void onReceive(Object message) {
if (message.equals("kill")) {
getContext().stop(child);
lastSender = getSender();
} else if (message instanceof Terminated) {
final Terminated t = (Terminated) message;
if (t.getActor() == child) {
lastSender.tell("finished", getSelf());
}
} else {
unhandled(message);
}
}
}
//#watch
static
//#identify
public class Follower extends UntypedActor {
final String identifyId = "1";
{
ActorSelection selection =
getContext().actorSelection("/user/another");
selection.tell(new Identify(identifyId), getSelf());
}
ActorRef another;
//#test-omitted
final ActorRef probe;
public Follower(ActorRef probe) {
this.probe = probe;
}
//#test-omitted
@Override
public void onReceive(Object message) {
if (message instanceof ActorIdentity) {
ActorIdentity identity = (ActorIdentity) message;
if (identity.correlationId().equals(identifyId)) {
ActorRef ref = identity.getRef();
if (ref == null)
getContext().stop(getSelf());
else {
another = ref;
getContext().watch(another);
//#test-omitted
probe.tell(ref, getSelf());
//#test-omitted
}
}
} else if (message instanceof Terminated) {
final Terminated t = (Terminated) message;
if (t.getActor().equals(another)) {
getContext().stop(getSelf());
}
} else {
unhandled(message);
}
}
}
//#identify
}

View file

@ -1,56 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor;
import static docs.actor.UntypedActorSwapper.Swap.SWAP;
import akka.actor.ActorRef;
import akka.actor.Props;
import akka.actor.ActorSystem;
import akka.actor.UntypedActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.japi.Procedure;
//#swapper
public class UntypedActorSwapper {
public static class Swap {
public static Swap SWAP = new Swap();
private Swap() {
}
}
public static class Swapper extends UntypedActor {
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
public void onReceive(Object message) {
if (message == SWAP) {
log.info("Hi");
getContext().become(new Procedure<Object>() {
@Override
public void apply(Object message) {
log.info("Ho");
getContext().unbecome(); // resets the latest 'become'
}
}, false); // this signals stacking of the new behavior
} else {
unhandled(message);
}
}
}
public static void main(String... args) {
ActorSystem system = ActorSystem.create("MySystem");
ActorRef swap = system.actorOf(Props.create(Swapper.class));
swap.tell(SWAP, ActorRef.noSender()); // logs Hi
swap.tell(SWAP, ActorRef.noSender()); // logs Ho
swap.tell(SWAP, ActorRef.noSender()); // logs Hi
swap.tell(SWAP, ActorRef.noSender()); // logs Ho
swap.tell(SWAP, ActorRef.noSender()); // logs Hi
swap.tell(SWAP, ActorRef.noSender()); // logs Ho
}
}
//#swapper

View file

@ -1,136 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor.fsm;
//#simple-imports
import akka.actor.AbstractFSM;
import akka.actor.ActorRef;
import akka.japi.pf.UnitMatch;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import scala.concurrent.duration.Duration;
//#simple-imports
import static docs.actor.fsm.Buncher.Data;
import static docs.actor.fsm.Buncher.State.*;
import static docs.actor.fsm.Buncher.State;
import static docs.actor.fsm.Buncher.Uninitialized.*;
import static docs.actor.fsm.Events.*;
//#simple-fsm
public class Buncher extends AbstractFSM<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()), self())));
m.match(stateData());
}).
state(Idle, Active, () -> {/* Do something here */}));
//#transition-elided
when(Active, Duration.create(1, "second"),
matchEvent(Arrays.asList(Flush.class, StateTimeout()), Todo.class,
(event, todo) -> goTo(Idle).using(todo.copy(new LinkedList<>()))));
//#unhandled-elided
whenUnhandled(
matchEvent(Queue.class, Todo.class,
(queue, todo) -> goTo(Active).using(todo.addElement(queue.getObj()))).
anyEvent((event, state) -> {
log().warning("received unhandled request {} in state {}/{}",
event, stateName(), state);
return stay();
}));
//#unhandled-elided
initialize();
//#fsm-body
}
//#simple-fsm
static
//#simple-state
// states
enum State {
Idle, Active
}
//#simple-state
static
//#simple-state
// state data
interface Data {
}
//#simple-state
static
//#simple-state
enum Uninitialized implements Data {
Uninitialized
}
//#simple-state
static
//#simple-state
final class Todo implements Data {
private final ActorRef target;
private final List<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

@ -1,79 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor.fsm;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.testkit.JavaTestKit;
import docs.AbstractJavaTest;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.LinkedList;
import docs.actor.fsm.*;
import static docs.actor.fsm.Events.Batch;
import static docs.actor.fsm.Events.Queue;
import static docs.actor.fsm.Events.SetTarget;
import static docs.actor.fsm.Events.Flush.Flush;
//#test-code
public class BuncherTest extends AbstractJavaTest {
static ActorSystem system;
@BeforeClass
public static void setup() {
system = ActorSystem.create("BuncherTest");
}
@AfterClass
public static void tearDown() {
JavaTestKit.shutdownActorSystem(system);
system = null;
}
@Test
public void testBuncherActorBatchesCorrectly() {
new JavaTestKit(system) {{
final ActorRef buncher =
system.actorOf(Props.create(Buncher.class));
final ActorRef probe = getRef();
buncher.tell(new SetTarget(probe), probe);
buncher.tell(new Queue(42), probe);
buncher.tell(new Queue(43), probe);
LinkedList<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 JavaTestKit(system) {{
final ActorRef buncher =
system.actorOf(Props.create(Buncher.class));
final ActorRef probe = getRef();
buncher.tell(new Queue(42), probe);
expectNoMsg();
system.stop(buncher);
}};
}
}
//#test-code

View file

@ -1,108 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor.fsm;
import akka.actor.ActorRef;
import java.util.List;
public class Events {
static
//#simple-events
public final class SetTarget {
private final ActorRef ref;
public SetTarget(ActorRef ref) {
this.ref = ref;
}
public ActorRef getRef() {
return ref;
}
//#boilerplate
@Override
public String toString() {
return "SetTarget{" +
"ref=" + ref +
'}';
}
//#boilerplate
}
//#simple-events
static
//#simple-events
public final class Queue {
private final Object obj;
public Queue(Object obj) {
this.obj = obj;
}
public Object getObj() {
return obj;
}
//#boilerplate
@Override
public String toString() {
return "Queue{" +
"obj=" + obj +
'}';
}
//#boilerplate
}
//#simple-events
static
//#simple-events
public final class Batch {
private final List<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

@ -1,180 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor.fsm;
import akka.actor.*;
import akka.testkit.JavaTestKit;
import docs.AbstractJavaTest;
import org.hamcrest.CoreMatchers;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import scala.concurrent.duration.Duration;
import static org.junit.Assert.*;
import static docs.actor.fsm.FSMDocTest.StateType.*;
import static docs.actor.fsm.FSMDocTest.Messages.*;
import static java.util.concurrent.TimeUnit.*;
public class FSMDocTest extends AbstractJavaTest {
static ActorSystem system;
@BeforeClass
public static void setup() {
system = ActorSystem.create("FSMDocTest");
}
@AfterClass
public static void tearDown() {
JavaTestKit.shutdownActorSystem(system);
system = null;
}
public static enum StateType {
SomeState,
Processing,
Idle,
Active,
Error
}
public static enum Messages {
WillDo,
Tick
}
public static enum Data {
Foo,
Bar
};
public static interface X {};
public static class DummyFSM extends AbstractFSM<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(), self());
target.tell(state, self());
target.tell(data, self());
target.tell(lastEvents, self());
//#logging-fsm
})
);
//...
//#logging-fsm
startWith(SomeState, Data.Foo);
when(SomeState, matchEvent(ActorRef.class, Data.class, (ref, data) -> {
target = ref;
target.tell("going active", self());
return goTo(Active);
}));
when(Active, matchEventEquals("stop", (event, data) -> {
target.tell("stopping", self());
return stop(new Failure("This is not the error you're looking for"));
}));
initialize();
//#logging-fsm
}
//#body-elided
}
//#logging-fsm
@Test
public void testLoggingFSM()
{
new JavaTestKit(system) {{
final ActorRef logger =
system.actorOf(Props.create(MyFSM.class));
final ActorRef probe = getRef();
logger.tell(probe, probe);
expectMsgEquals("going active");
logger.tell("stop", probe);
expectMsgEquals("stopping");
expectMsgEquals("This is not the error you're looking for");
expectMsgEquals(Active);
expectMsgEquals(Data.Foo);
String msg = expectMsgClass(String.class);
assertThat(msg, CoreMatchers.startsWith("LogEntry(SomeState,Foo,Actor[akka://FSMDocTest/system/"));
}};
}
}

View file

@ -1,489 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor.japi;
//#all
//#imports
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import akka.actor.*;
import akka.dispatch.Mapper;
import akka.japi.Function;
import scala.concurrent.duration.Duration;
import akka.util.Timeout;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import static akka.japi.Util.classTag;
import static akka.actor.SupervisorStrategy.restart;
import static akka.actor.SupervisorStrategy.stop;
import static akka.actor.SupervisorStrategy.escalate;
import akka.actor.SupervisorStrategy.Directive;
import static akka.pattern.Patterns.ask;
import static akka.pattern.Patterns.pipe;
import static docs.actor.japi.FaultHandlingDocSample.WorkerApi.*;
import static docs.actor.japi.FaultHandlingDocSample.CounterServiceApi.*;
import static docs.actor.japi.FaultHandlingDocSample.CounterApi.*;
import static docs.actor.japi.FaultHandlingDocSample.StorageApi.*;
//#imports
public class FaultHandlingDocSample {
/**
* Runs the sample
*/
public static void main(String[] args) {
Config config = ConfigFactory.parseString("akka.loglevel = DEBUG \n" +
"akka.actor.debug.lifecycle = on");
ActorSystem system = ActorSystem.create("FaultToleranceSample", config);
ActorRef worker = system.actorOf(Props.create(Worker.class), "worker");
ActorRef listener = system.actorOf(Props.create(Listener.class), "listener");
// start the work and listen on progress
// note that the listener is used as sender of the tell,
// i.e. it will receive replies from the worker
worker.tell(Start, listener);
}
/**
* Listens on progress from the worker and shuts down the system when enough
* work has been done.
*/
public static class Listener extends UntypedActor {
final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
@Override
public void preStart() {
// If we don't get any progress within 15 seconds then the service
// is unavailable
getContext().setReceiveTimeout(Duration.create("15 seconds"));
}
public void onReceive(Object msg) {
log.debug("received message {}", msg);
if (msg instanceof Progress) {
Progress progress = (Progress) msg;
log.info("Current progress: {} %", progress.percent);
if (progress.percent >= 100.0) {
log.info("That's all, shutting down");
getContext().system().terminate();
}
} else if (msg == ReceiveTimeout.getInstance()) {
// No progress within 15 seconds, ServiceUnavailable
log.error("Shutting down due to unavailable service");
getContext().system().terminate();
} else {
unhandled(msg);
}
}
}
//#messages
public interface WorkerApi {
public static final Object Start = "Start";
public static final Object Do = "Do";
public static class Progress {
public final double percent;
public Progress(double percent) {
this.percent = percent;
}
public String toString() {
return String.format("%s(%s)", getClass().getSimpleName(), percent);
}
}
}
//#messages
/**
* Worker performs some work when it receives the Start message. It will
* continuously notify the sender of the Start message of current Progress.
* The Worker supervise the CounterService.
*/
public static class Worker extends UntypedActor {
final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
final Timeout askTimeout = new Timeout(Duration.create(5, "seconds"));
// The sender of the initial Start message will continuously be notified
// about progress
ActorRef progressListener;
final ActorRef counterService = getContext().actorOf(
Props.create(CounterService.class), "counter");
final int totalCount = 51;
// Stop the CounterService child if it throws ServiceUnavailable
private static SupervisorStrategy strategy = new OneForOneStrategy(-1,
Duration.Inf(), new Function<Throwable, Directive>() {
@Override
public Directive apply(Throwable t) {
if (t instanceof ServiceUnavailable) {
return stop();
} else {
return escalate();
}
}
});
@Override
public SupervisorStrategy supervisorStrategy() {
return strategy;
}
public void onReceive(Object msg) {
log.debug("received message {}", msg);
if (msg.equals(Start) && progressListener == null) {
progressListener = getSender();
getContext().system().scheduler().schedule(
Duration.Zero(), Duration.create(1, "second"), getSelf(), Do,
getContext().dispatcher(), null
);
} else if (msg.equals(Do)) {
counterService.tell(new Increment(1), getSelf());
counterService.tell(new Increment(1), getSelf());
counterService.tell(new Increment(1), getSelf());
// Send current progress to the initial sender
pipe(ask(counterService, GetCurrentCount, askTimeout)
.mapTo(classTag(CurrentCount.class))
.map(new Mapper<CurrentCount, Progress>() {
public Progress apply(CurrentCount c) {
return new Progress(100.0 * c.count / totalCount);
}
}, getContext().dispatcher()), getContext().dispatcher())
.to(progressListener);
} else {
unhandled(msg);
}
}
}
//#messages
public interface CounterServiceApi {
public static final Object GetCurrentCount = "GetCurrentCount";
public static class CurrentCount {
public final String key;
public final long count;
public CurrentCount(String key, long count) {
this.key = key;
this.count = count;
}
public String toString() {
return String.format("%s(%s, %s)", getClass().getSimpleName(), key, count);
}
}
public static class Increment {
public final long n;
public Increment(long n) {
this.n = n;
}
public String toString() {
return String.format("%s(%s)", getClass().getSimpleName(), n);
}
}
public static class ServiceUnavailable extends RuntimeException {
private static final long serialVersionUID = 1L;
public ServiceUnavailable(String msg) {
super(msg);
}
}
}
//#messages
/**
* Adds the value received in Increment message to a persistent counter.
* Replies with CurrentCount when it is asked for CurrentCount. CounterService
* supervise Storage and Counter.
*/
public static class CounterService extends UntypedActor {
// Reconnect message
static final Object Reconnect = "Reconnect";
private static class SenderMsgPair {
final ActorRef sender;
final Object msg;
SenderMsgPair(ActorRef sender, Object msg) {
this.msg = msg;
this.sender = sender;
}
}
final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
final String key = getSelf().path().name();
ActorRef storage;
ActorRef counter;
final List<SenderMsgPair> backlog = new ArrayList<SenderMsgPair>();
final int MAX_BACKLOG = 10000;
// Restart the storage child when StorageException is thrown.
// After 3 restarts within 5 seconds it will be stopped.
private static SupervisorStrategy strategy = new OneForOneStrategy(3,
Duration.create("5 seconds"), new Function<Throwable, Directive>() {
@Override
public Directive apply(Throwable t) {
if (t instanceof StorageException) {
return restart();
} else {
return escalate();
}
}
});
@Override
public SupervisorStrategy supervisorStrategy() {
return strategy;
}
@Override
public void preStart() {
initStorage();
}
/**
* The child storage is restarted in case of failure, but after 3 restarts,
* and still failing it will be stopped. Better to back-off than
* continuously failing. When it has been stopped we will schedule a
* Reconnect after a delay. Watch the child so we receive Terminated message
* when it has been terminated.
*/
void initStorage() {
storage = getContext().watch(getContext().actorOf(
Props.create(Storage.class), "storage"));
// Tell the counter, if any, to use the new storage
if (counter != null)
counter.tell(new UseStorage(storage), getSelf());
// We need the initial value to be able to operate
storage.tell(new Get(key), getSelf());
}
@Override
public void onReceive(Object msg) {
log.debug("received message {}", msg);
if (msg instanceof Entry && ((Entry) msg).key.equals(key) &&
counter == null) {
// Reply from Storage of the initial value, now we can create the Counter
final long value = ((Entry) msg).value;
counter = getContext().actorOf(Props.create(Counter.class, key, value));
// Tell the counter to use current storage
counter.tell(new UseStorage(storage), getSelf());
// and send the buffered backlog to the counter
for (SenderMsgPair each : backlog) {
counter.tell(each.msg, each.sender);
}
backlog.clear();
} else if (msg instanceof Increment) {
forwardOrPlaceInBacklog(msg);
} else if (msg.equals(GetCurrentCount)) {
forwardOrPlaceInBacklog(msg);
} else if (msg instanceof Terminated) {
// After 3 restarts the storage child is stopped.
// We receive Terminated because we watch the child, see initStorage.
storage = null;
// Tell the counter that there is no storage for the moment
counter.tell(new UseStorage(null), getSelf());
// Try to re-establish storage after while
getContext().system().scheduler().scheduleOnce(
Duration.create(10, "seconds"), getSelf(), Reconnect,
getContext().dispatcher(), null);
} else if (msg.equals(Reconnect)) {
// Re-establish storage after the scheduled delay
initStorage();
} else {
unhandled(msg);
}
}
void forwardOrPlaceInBacklog(Object msg) {
// We need the initial value from storage before we can start delegate to
// the counter. Before that we place the messages in a backlog, to be sent
// to the counter when it is initialized.
if (counter == null) {
if (backlog.size() >= MAX_BACKLOG)
throw new ServiceUnavailable("CounterService not available," +
" lack of initial value");
backlog.add(new SenderMsgPair(getSender(), msg));
} else {
counter.forward(msg, getContext());
}
}
}
//#messages
public interface CounterApi {
public static class UseStorage {
public final ActorRef storage;
public UseStorage(ActorRef storage) {
this.storage = storage;
}
public String toString() {
return String.format("%s(%s)", getClass().getSimpleName(), storage);
}
}
}
//#messages
/**
* The in memory count variable that will send current value to the Storage,
* if there is any storage available at the moment.
*/
public static class Counter extends UntypedActor {
final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
final String key;
long count;
ActorRef storage;
public Counter(String key, long initialValue) {
this.key = key;
this.count = initialValue;
}
@Override
public void onReceive(Object msg) {
log.debug("received message {}", msg);
if (msg instanceof UseStorage) {
storage = ((UseStorage) msg).storage;
storeCount();
} else if (msg instanceof Increment) {
count += ((Increment) msg).n;
storeCount();
} else if (msg.equals(GetCurrentCount)) {
getSender().tell(new CurrentCount(key, count), getSelf());
} else {
unhandled(msg);
}
}
void storeCount() {
// Delegate dangerous work, to protect our valuable state.
// We can continue without storage.
if (storage != null) {
storage.tell(new Store(new Entry(key, count)), getSelf());
}
}
}
//#messages
public interface StorageApi {
public static class Store {
public final Entry entry;
public Store(Entry entry) {
this.entry = entry;
}
public String toString() {
return String.format("%s(%s)", getClass().getSimpleName(), entry);
}
}
public static class Entry {
public final String key;
public final long value;
public Entry(String key, long value) {
this.key = key;
this.value = value;
}
public String toString() {
return String.format("%s(%s, %s)", getClass().getSimpleName(), key, value);
}
}
public static class Get {
public final String key;
public Get(String key) {
this.key = key;
}
public String toString() {
return String.format("%s(%s)", getClass().getSimpleName(), key);
}
}
public static class StorageException extends RuntimeException {
private static final long serialVersionUID = 1L;
public StorageException(String msg) {
super(msg);
}
}
}
//#messages
/**
* Saves key/value pairs to persistent storage when receiving Store message.
* Replies with current value when receiving Get message. Will throw
* StorageException if the underlying data store is out of order.
*/
public static class Storage extends UntypedActor {
final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
final DummyDB db = DummyDB.instance;
@Override
public void onReceive(Object msg) {
log.debug("received message {}", msg);
if (msg instanceof Store) {
Store store = (Store) msg;
db.save(store.entry.key, store.entry.value);
} else if (msg instanceof Get) {
Get get = (Get) msg;
Long value = db.load(get.key);
getSender().tell(new Entry(get.key, value == null ?
Long.valueOf(0L) : value), getSelf());
} else {
unhandled(msg);
}
}
}
//#dummydb
public static class DummyDB {
public static final DummyDB instance = new DummyDB();
private final Map<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

@ -1,470 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor.japi;
//#all
//#imports
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import akka.actor.*;
import akka.dispatch.Mapper;
import akka.event.LoggingReceive;
import akka.japi.pf.DeciderBuilder;
import akka.japi.pf.ReceiveBuilder;
import akka.util.Timeout;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import scala.concurrent.duration.Duration;
import scala.PartialFunction;
import scala.runtime.BoxedUnit;
import static akka.japi.Util.classTag;
import static akka.actor.SupervisorStrategy.resume;
import static akka.actor.SupervisorStrategy.restart;
import static akka.actor.SupervisorStrategy.stop;
import static akka.actor.SupervisorStrategy.escalate;
import static akka.pattern.Patterns.ask;
import static akka.pattern.Patterns.pipe;
import static docs.actor.japi.FaultHandlingDocSample.WorkerApi.*;
import static docs.actor.japi.FaultHandlingDocSample.CounterServiceApi.*;
import static docs.actor.japi.FaultHandlingDocSample.CounterApi.*;
import static docs.actor.japi.FaultHandlingDocSample.StorageApi.*;
//#imports
public class FaultHandlingDocSampleJava8 {
/**
* Runs the sample
*/
public static void main(String[] args) {
Config config = ConfigFactory.parseString(
"akka.loglevel = \"DEBUG\"\n" +
"akka.actor.debug {\n" +
" receive = on\n" +
" lifecycle = on\n" +
"}\n");
ActorSystem system = ActorSystem.create("FaultToleranceSample", config);
ActorRef worker = system.actorOf(Props.create(Worker.class), "worker");
ActorRef listener = system.actorOf(Props.create(Listener.class), "listener");
// start the work and listen on progress
// note that the listener is used as sender of the tell,
// i.e. it will receive replies from the worker
worker.tell(Start, listener);
}
/**
* Listens on progress from the worker and shuts down the system when enough
* work has been done.
*/
public static class Listener extends AbstractLoggingActor {
@Override
public void preStart() {
// If we don't get any progress within 15 seconds then the service
// is unavailable
context().setReceiveTimeout(Duration.create("15 seconds"));
}
public Listener() {
receive(LoggingReceive.create(ReceiveBuilder.
match(Progress.class, progress -> {
log().info("Current progress: {} %", progress.percent);
if (progress.percent >= 100.0) {
log().info("That's all, shutting down");
context().system().shutdown();
}
}).
matchEquals(ReceiveTimeout.getInstance(), x -> {
// No progress within 15 seconds, ServiceUnavailable
log().error("Shutting down due to unavailable service");
context().system().shutdown();
}).build(), context()
));
}
}
//#messages
public interface WorkerApi {
public static final Object Start = "Start";
public static final Object Do = "Do";
public static class Progress {
public final double percent;
public Progress(double percent) {
this.percent = percent;
}
public String toString() {
return String.format("%s(%s)", getClass().getSimpleName(), percent);
}
}
}
//#messages
/**
* Worker performs some work when it receives the Start message. It will
* continuously notify the sender of the Start message of current Progress.
* The Worker supervise the CounterService.
*/
public static class Worker extends AbstractLoggingActor {
final Timeout askTimeout = new Timeout(Duration.create(5, "seconds"));
// The sender of the initial Start message will continuously be notified
// about progress
ActorRef progressListener;
final ActorRef counterService = context().actorOf(
Props.create(CounterService.class), "counter");
final int totalCount = 51;
// Stop the CounterService child if it throws ServiceUnavailable
private static final SupervisorStrategy strategy =
new OneForOneStrategy(DeciderBuilder.
match(ServiceUnavailable.class, e -> stop()).
matchAny(o -> escalate()).build());
@Override
public SupervisorStrategy supervisorStrategy() {
return strategy;
}
public Worker() {
receive(LoggingReceive.create(ReceiveBuilder.
matchEquals(Start, x -> progressListener == null, x -> {
progressListener = sender();
context().system().scheduler().schedule(
Duration.Zero(), Duration.create(1, "second"), self(), Do,
context().dispatcher(), null
);
}).
matchEquals(Do, x -> {
counterService.tell(new Increment(1), self());
counterService.tell(new Increment(1), self());
counterService.tell(new Increment(1), self());
// Send current progress to the initial sender
pipe(ask(counterService, GetCurrentCount, askTimeout)
.mapTo(classTag(CurrentCount.class))
.map(new Mapper<CurrentCount, Progress>() {
public Progress apply(CurrentCount c) {
return new Progress(100.0 * c.count / totalCount);
}
}, context().dispatcher()), context().dispatcher())
.to(progressListener);
}).build(), context())
);
}
}
//#messages
public interface CounterServiceApi {
public static final Object GetCurrentCount = "GetCurrentCount";
public static class CurrentCount {
public final String key;
public final long count;
public CurrentCount(String key, long count) {
this.key = key;
this.count = count;
}
public String toString() {
return String.format("%s(%s, %s)", getClass().getSimpleName(), key, count);
}
}
public static class Increment {
public final long n;
public Increment(long n) {
this.n = n;
}
public String toString() {
return String.format("%s(%s)", getClass().getSimpleName(), n);
}
}
public static class ServiceUnavailable extends RuntimeException {
private static final long serialVersionUID = 1L;
public ServiceUnavailable(String msg) {
super(msg);
}
}
}
//#messages
/**
* Adds the value received in Increment message to a persistent counter.
* Replies with CurrentCount when it is asked for CurrentCount. CounterService
* supervise Storage and Counter.
*/
public static class CounterService extends AbstractLoggingActor {
// Reconnect message
static final Object Reconnect = "Reconnect";
private static class SenderMsgPair {
final ActorRef sender;
final Object msg;
SenderMsgPair(ActorRef sender, Object msg) {
this.msg = msg;
this.sender = sender;
}
}
final String key = self().path().name();
ActorRef storage;
ActorRef counter;
final List<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 = context().watch(context().actorOf(
Props.create(Storage.class), "storage"));
// Tell the counter, if any, to use the new storage
if (counter != null)
counter.tell(new UseStorage(storage), self());
// We need the initial value to be able to operate
storage.tell(new Get(key), self());
}
public CounterService() {
receive(LoggingReceive.create(ReceiveBuilder.
match(Entry.class, entry -> entry.key.equals(key) && counter == null, entry -> {
// Reply from Storage of the initial value, now we can create the Counter
final long value = entry.value;
counter = context().actorOf(Props.create(Counter.class, key, value));
// Tell the counter to use current storage
counter.tell(new UseStorage(storage), self());
// and send the buffered backlog to the counter
for (SenderMsgPair each : backlog) {
counter.tell(each.msg, each.sender);
}
backlog.clear();
}).
match(Increment.class, increment -> {
forwardOrPlaceInBacklog(increment);
}).
matchEquals(GetCurrentCount, gcc -> {
forwardOrPlaceInBacklog(gcc);
}).
match(Terminated.class, o -> {
// After 3 restarts the storage child is stopped.
// We receive Terminated because we watch the child, see initStorage.
storage = null;
// Tell the counter that there is no storage for the moment
counter.tell(new UseStorage(null), self());
// Try to re-establish storage after while
context().system().scheduler().scheduleOnce(
Duration.create(10, "seconds"), self(), Reconnect,
context().dispatcher(), null);
}).
matchEquals(Reconnect, o -> {
// Re-establish storage after the scheduled delay
initStorage();
}).build(), context())
);
}
void forwardOrPlaceInBacklog(Object msg) {
// We need the initial value from storage before we can start delegate to
// the counter. Before that we place the messages in a backlog, to be sent
// to the counter when it is initialized.
if (counter == null) {
if (backlog.size() >= MAX_BACKLOG)
throw new ServiceUnavailable("CounterService not available," +
" lack of initial value");
backlog.add(new SenderMsgPair(sender(), msg));
} else {
counter.forward(msg, context());
}
}
}
//#messages
public interface CounterApi {
public static class UseStorage {
public final ActorRef storage;
public UseStorage(ActorRef storage) {
this.storage = storage;
}
public String toString() {
return String.format("%s(%s)", getClass().getSimpleName(), storage);
}
}
}
//#messages
/**
* The in memory count variable that will send current value to the Storage,
* if there is any storage available at the moment.
*/
public static class Counter extends AbstractLoggingActor {
final String key;
long count;
ActorRef storage;
public Counter(String key, long initialValue) {
this.key = key;
this.count = initialValue;
receive(LoggingReceive.create(ReceiveBuilder.
match(UseStorage.class, useStorage -> {
storage = useStorage.storage;
storeCount();
}).
match(Increment.class, increment -> {
count += increment.n;
storeCount();
}).
matchEquals(GetCurrentCount, gcc -> {
sender().tell(new CurrentCount(key, count), self());
}).build(), context())
);
}
void storeCount() {
// Delegate dangerous work, to protect our valuable state.
// We can continue without storage.
if (storage != null) {
storage.tell(new Store(new Entry(key, count)), self());
}
}
}
//#messages
public interface StorageApi {
public static class Store {
public final Entry entry;
public Store(Entry entry) {
this.entry = entry;
}
public String toString() {
return String.format("%s(%s)", getClass().getSimpleName(), entry);
}
}
public static class Entry {
public final String key;
public final long value;
public Entry(String key, long value) {
this.key = key;
this.value = value;
}
public String toString() {
return String.format("%s(%s, %s)", getClass().getSimpleName(), key, value);
}
}
public static class Get {
public final String key;
public Get(String key) {
this.key = key;
}
public String toString() {
return String.format("%s(%s)", getClass().getSimpleName(), key);
}
}
public static class StorageException extends RuntimeException {
private static final long serialVersionUID = 1L;
public StorageException(String msg) {
super(msg);
}
}
}
//#messages
/**
* Saves key/value pairs to persistent storage when receiving Store message.
* Replies with current value when receiving Get message. Will throw
* StorageException if the underlying data store is out of order.
*/
public static class Storage extends AbstractLoggingActor {
final DummyDB db = DummyDB.instance;
public Storage() {
receive(LoggingReceive.create(ReceiveBuilder.
match(Store.class, store -> {
db.save(store.entry.key, store.entry.value);
}).
match(Get.class, get -> {
Long value = db.load(get.key);
sender().tell(new Entry(get.key, value == null ?
Long.valueOf(0L) : value), self());
}).build(), context())
);
}
}
//#dummydb
public static class DummyDB {
public static final DummyDB instance = new DummyDB();
private final Map<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

@ -12,19 +12,23 @@ import akka.testkit.TestEvent;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import docs.AbstractJavaTest;
import docs.actor.ActorDocTest.FirstActor;
import scala.PartialFunction;
import scala.runtime.BoxedUnit;
import static docs.actorlambda.Messages.Swap.Swap;
import static docs.actorlambda.Messages.*;
import static akka.japi.Util.immutableSeq;
import akka.actor.CoordinatedShutdown;
import static akka.pattern.PatternsCS.ask;
import akka.util.Timeout;
import akka.Done;
import java.util.concurrent.CompletionStage;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import akka.testkit.TestActors;
import akka.dispatch.Mapper;
import akka.dispatch.Futures;
import akka.util.Timeout;
import akka.testkit.JavaTestKit;
import org.junit.AfterClass;
@ -44,13 +48,25 @@ import akka.actor.ActorIdentity;
import akka.actor.ActorSelection;
import akka.actor.Identify;
//#import-identify
//#import-graceFulStop
//#import-ask
import static akka.pattern.Patterns.ask;
import static akka.pattern.Patterns.pipe;
import akka.dispatch.Futures;
import akka.dispatch.Mapper;
import akka.util.Timeout;
//#import-ask
//#import-gracefulStop
import akka.pattern.AskTimeoutException;
import scala.concurrent.Await;
import scala.concurrent.duration.Duration;
//#import-ask
import scala.concurrent.Future;
import static akka.pattern.Patterns.gracefulStop;
//#import-graceFulStop
//#import-ask
//#import-gracefulStop
//#import-terminated
import akka.actor.Terminated;
//#import-terminated
public class ActorDocTest extends AbstractJavaTest {
@ -71,49 +87,111 @@ public class ActorDocTest extends AbstractJavaTest {
@AfterClass
public static void afterClass() throws Exception {
Await.ready(system.terminate(), Duration.create("5 seconds"));
Await.ready(system.terminate(), Duration.create(5, TimeUnit.SECONDS));
}
static
//#context-actorOf
public class FirstActor extends AbstractActor {
final ActorRef child = context().actorOf(Props.create(MyActor.class), "myChild");
final ActorRef child = getContext().actorOf(Props.create(MyActor.class), "myChild");
//#plus-some-behavior
public FirstActor() {
receive(ReceiveBuilder.
matchAny(x -> {
sender().tell(x, self());
}).build()
);
@Override
public Receive createReceive() {
return receiveBuilder()
.matchAny(x -> sender().tell(x, self()))
.build();
}
//#plus-some-behavior
}
//#context-actorOf
static public abstract class SomeActor extends AbstractActor {
//#receive-constructor
public SomeActor() {
receive(ReceiveBuilder.
//#and-some-behavior
match(String.class, s -> System.out.println(s.toLowerCase())).
//#and-some-behavior
build());
}
//#receive-constructor
static public class SomeActor extends AbstractActor {
//#createReceive
@Override
//#receive
public abstract PartialFunction<Object, BoxedUnit> receive();
//#receive
public Receive createReceive() {
return receiveBuilder()
.match(String.class, s -> System.out.println(s.toLowerCase()))
.build();
}
//#createReceive
}
static
//#well-structured
public class WellStructuredActor extends AbstractActor {
public static class Msg1 {}
public static class Msg2 {}
public static class Msg3 {}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Msg1.class, this::receiveMsg1)
.match(Msg2.class, this::receiveMsg2)
.match(Msg3.class, this::receiveMsg3)
.build();
}
private void receiveMsg1(Msg1 msg) {
// actual work
}
private void receiveMsg2(Msg2 msg) {
// actual work
}
private void receiveMsg3(Msg3 msg) {
// actual work
}
}
//#well-structured
static
//#optimized
public class OptimizedActor extends UntypedAbstractActor {
public static class Msg1 {}
public static class Msg2 {}
public static class Msg3 {}
@Override
public void onReceive(Object msg) throws Exception {
if (msg instanceof Msg1)
receiveMsg1((Msg1) msg);
else if (msg instanceof Msg1)
receiveMsg2((Msg2) msg);
else if (msg instanceof Msg3)
receiveMsg3((Msg3) msg);
else
unhandled(msg);
}
private void receiveMsg1(Msg1 msg) {
// actual work
}
private void receiveMsg2(Msg2 msg) {
// actual work
}
private void receiveMsg3(Msg3 msg) {
// actual work
}
}
//#optimized
static public class ActorWithArgs extends AbstractActor {
private final String args;
ActorWithArgs(String args) {
public ActorWithArgs(String args) {
this.args = args;
receive(ReceiveBuilder.
matchAny(x -> { }).build()
);
}
@Override
public Receive createReceive() {
return receiveBuilder().matchAny(x -> { }).build();
}
}
@ -133,14 +211,18 @@ public class ActorDocTest extends AbstractJavaTest {
}
private final Integer magicNumber;
DemoActor(Integer magicNumber) {
public DemoActor(Integer magicNumber) {
this.magicNumber = magicNumber;
receive(ReceiveBuilder.
match(Integer.class, i -> {
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Integer.class, i -> {
sender().tell(i + magicNumber, self());
}).build()
);
})
.build();
}
}
@ -149,11 +231,12 @@ public class ActorDocTest extends AbstractJavaTest {
//#props-factory
public class SomeOtherActor extends AbstractActor {
// Props(new DemoActor(42)) would not be safe
ActorRef demoActor = context().actorOf(DemoActor.props(42), "demo");
ActorRef demoActor = getContext().actorOf(DemoActor.props(42), "demo");
// ...
//#props-factory
public SomeOtherActor() {
receive(emptyBehavior());
@Override
public Receive createReceive() {
return AbstractActor.emptyBehavior();
}
//#props-factory
}
@ -174,26 +257,65 @@ public class ActorDocTest extends AbstractJavaTest {
return from;
}
}
DemoMessagesActor() {
receive(ReceiveBuilder.
match(Greeting.class, g -> {
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Greeting.class, g -> {
log().info("I was greeted by {}", g.getGreeter());
}).build()
);
};
})
.build();
}
}
//#messages-in-companion
public static class LifecycleMethods extends AbstractActor {
@Override
public Receive createReceive() {
return AbstractActor.emptyBehavior();
}
/*
* This section must be kept in sync with the actual Actor trait.
*
* BOYSCOUT RULE: whenever you read this, verify that!
*/
//#lifecycle-callbacks
public void preStart() {
}
public void preRestart(Throwable reason, Optional<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;
public Hook() {
receive(emptyBehavior());
@Override
public Receive createReceive() {
return AbstractActor.emptyBehavior();
}
//#preStart
@Override
public void preStart() {
target = context().actorOf(Props.create(MyActor.class, "target"));
target = getContext().actorOf(Props.create(MyActor.class, "target"));
}
//#preStart
//#postStop
@ -207,7 +329,7 @@ public class ActorDocTest extends AbstractJavaTest {
//#tell
final Object result = "";
//#forward
target.forward(result, context());
target.forward(result, getContext());
//#forward
target = null;
//#clean-up-some-resources
@ -218,28 +340,30 @@ public class ActorDocTest extends AbstractJavaTest {
public void compileSelections() {
//#selection-local
// will look up this absolute path
context().actorSelection("/user/serviceA/actor");
getContext().actorSelection("/user/serviceA/actor");
// will look up sibling beneath same supervisor
context().actorSelection("../joe");
getContext().actorSelection("../joe");
//#selection-local
//#selection-wildcard
// will look all children to serviceB with names starting with worker
context().actorSelection("/user/serviceB/worker*");
getContext().actorSelection("/user/serviceB/worker*");
// will look up all siblings beneath same supervisor
context().actorSelection("../*");
getContext().actorSelection("../*");
//#selection-wildcard
//#selection-remote
context().actorSelection("akka.tcp://app@otherhost:1234/user/serviceB");
getContext().actorSelection("akka.tcp://app@otherhost:1234/user/serviceB");
//#selection-remote
}
}
public static class ReplyException extends AbstractActor {
public ReplyException() {
receive(ReceiveBuilder.
matchAny(x -> {
@Override
public Receive createReceive() {
return receiveBuilder()
.matchAny(x -> {
//#reply-exception
try {
String result = operation();
@ -249,8 +373,8 @@ public class ActorDocTest extends AbstractJavaTest {
throw e;
}
//#reply-exception
}).build()
);
})
.build();
}
private String operation() {
@ -267,28 +391,31 @@ public class ActorDocTest extends AbstractJavaTest {
public static final Shutdown SHUTDOWN = Shutdown.Shutdown;
private ActorRef worker =
context().watch(context().actorOf(Props.create(Cruncher.class), "worker"));
getContext().watch(getContext().actorOf(Props.create(Cruncher.class), "worker"));
public Manager() {
receive(ReceiveBuilder.
matchEquals("job", s -> {
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("job", s -> {
worker.tell("crunch", self());
}).
matchEquals(SHUTDOWN, x -> {
})
.matchEquals(SHUTDOWN, x -> {
worker.tell(PoisonPill.getInstance(), self());
context().become(shuttingDown);
}).build()
);
getContext().become(shuttingDown());
})
.build();
}
public PartialFunction<Object, BoxedUnit> shuttingDown =
ReceiveBuilder.
matchEquals("job", s -> {
sender().tell("service unavailable, shutting down", self());
}).
match(Terminated.class, t -> t.actor().equals(worker), t -> {
context().stop(self());
}).build();
private AbstractActor.Receive shuttingDown() {
return receiveBuilder()
.matchEquals("job", s ->
sender().tell("service unavailable, shutting down", self())
)
.match(Terminated.class, t -> t.actor().equals(worker), t ->
getContext().stop(self())
)
.build();
}
}
//#gracefulStop-actor
@ -309,27 +436,26 @@ public class ActorDocTest extends AbstractJavaTest {
public static class Cruncher extends AbstractActor {
public Cruncher() {
receive(ReceiveBuilder.
matchEquals("crunch", s -> { }).build()
);
@Override
public Receive createReceive() {
return receiveBuilder().matchEquals("crunch", s -> { }).build();
}
}
static
//#swapper
public class Swapper extends AbstractLoggingActor {
public Swapper() {
receive(ReceiveBuilder.
matchEquals(Swap, s -> {
log().info("Hi");
context().become(ReceiveBuilder.
matchEquals(Swap, x -> {
log().info("Ho");
context().unbecome(); // resets the latest 'become' (just for fun)
}).build(), false); // push on top instead of replace
}).build()
);
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals(Swap, s -> {
log().info("Hi");
getContext().become(receiveBuilder().
matchEquals(Swap, x -> {
log().info("Ho");
getContext().unbecome(); // resets the latest 'become' (just for fun)
}).build(), false); // push on top instead of replace
}).build();
}
}
@ -418,29 +544,32 @@ public class ActorDocTest extends AbstractJavaTest {
//#receive-timeout
public class ReceiveTimeoutActor extends AbstractActor {
//#receive-timeout
ActorRef target = context().system().deadLetters();
ActorRef target = getContext().system().deadLetters();
//#receive-timeout
public ReceiveTimeoutActor() {
// To set an initial delay
context().setReceiveTimeout(Duration.create("10 seconds"));
receive(ReceiveBuilder.
matchEquals("Hello", s -> {
getContext().setReceiveTimeout(Duration.create(10, TimeUnit.SECONDS));
}
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("Hello", s -> {
// To set in a response to a message
context().setReceiveTimeout(Duration.create("1 second"));
getContext().setReceiveTimeout(Duration.create(1, TimeUnit.SECONDS));
//#receive-timeout
target = sender();
target.tell("Hello world", self());
//#receive-timeout
}).
match(ReceiveTimeout.class, r -> {
})
.match(ReceiveTimeout.class, r -> {
// To turn it off
context().setReceiveTimeout(Duration.Undefined());
getContext().setReceiveTimeout(Duration.Undefined());
//#receive-timeout
target.tell("timeout", self());
//#receive-timeout
}).build()
);
})
.build();
}
}
//#receive-timeout
@ -460,35 +589,40 @@ public class ActorDocTest extends AbstractJavaTest {
static
//#hot-swap-actor
public class HotSwapActor extends AbstractActor {
private PartialFunction<Object, BoxedUnit> angry;
private PartialFunction<Object, BoxedUnit> happy;
private AbstractActor.Receive angry;
private AbstractActor.Receive happy;
public HotSwapActor() {
angry =
ReceiveBuilder.
matchEquals("foo", s -> {
receiveBuilder()
.matchEquals("foo", s -> {
sender().tell("I am already angry?", self());
}).
matchEquals("bar", s -> {
context().become(happy);
}).build();
})
.matchEquals("bar", s -> {
getContext().become(happy);
})
.build();
happy = ReceiveBuilder.
matchEquals("bar", s -> {
happy = receiveBuilder()
.matchEquals("bar", s -> {
sender().tell("I am already happy :-)", self());
}).
matchEquals("foo", s -> {
context().become(angry);
}).build();
receive(ReceiveBuilder.
matchEquals("foo", s -> {
context().become(angry);
}).
matchEquals("bar", s -> {
context().become(happy);
}).build()
);
})
.matchEquals("foo", s -> {
getContext().become(angry);
})
.build();
}
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("foo", s ->
getContext().become(angry)
)
.matchEquals("bar", s ->
getContext().become(happy)
)
.build();
}
}
//#hot-swap-actor
@ -516,19 +650,21 @@ public class ActorDocTest extends AbstractJavaTest {
static
//#stash
public class ActorWithProtocol extends AbstractActorWithStash {
public ActorWithProtocol() {
receive(ReceiveBuilder.
matchEquals("open", s -> {
context().become(ReceiveBuilder.
matchEquals("write", ws -> { /* do writing */ }).
matchEquals("close", cs -> {
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("open", s -> {
getContext().become(receiveBuilder()
.matchEquals("write", ws -> { /* do writing */ })
.matchEquals("close", cs -> {
unstashAll();
context().unbecome();
}).
matchAny(msg -> stash()).build(), false);
}).
matchAny(msg -> stash()).build()
);
getContext().unbecome();
})
.matchAny(msg -> stash())
.build(), false);
})
.matchAny(msg -> stash())
.build();
}
}
//#stash
@ -541,21 +677,24 @@ public class ActorDocTest extends AbstractJavaTest {
static
//#watch
public class WatchActor extends AbstractActor {
private final ActorRef child = context().actorOf(Props.empty(), "target");
private final ActorRef child = getContext().actorOf(Props.empty(), "target");
private ActorRef lastSender = system.deadLetters();
public WatchActor() {
context().watch(child); // <-- this is the only call needed for registration
receive(ReceiveBuilder.
matchEquals("kill", s -> {
context().stop(child);
getContext().watch(child); // <-- this is the only call needed for registration
}
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("kill", s -> {
getContext().stop(child);
lastSender = sender();
}).
match(Terminated.class, t -> t.actor().equals(child), t -> {
})
.match(Terminated.class, t -> t.actor().equals(child), t -> {
lastSender.tell("finished", self());
}).build()
);
})
.build();
}
}
//#watch
@ -578,26 +717,30 @@ public class ActorDocTest extends AbstractJavaTest {
final Integer identifyId = 1;
public Follower(){
ActorSelection selection = context().actorSelection("/user/another");
ActorSelection selection = getContext().actorSelection("/user/another");
selection.tell(new Identify(identifyId), self());
receive(ReceiveBuilder.
match(ActorIdentity.class, id -> id.getRef() != null, id -> {
ActorRef ref = id.getRef();
context().watch(ref);
context().become(active(ref));
}).
match(ActorIdentity.class, id -> id.getRef() == null, id -> {
context().stop(self());
}).build()
);
}
final PartialFunction<Object, BoxedUnit> active(final ActorRef another) {
return ReceiveBuilder.
match(Terminated.class, t -> t.actor().equals(another), t -> {
context().stop(self());
}).build();
@Override
public Receive createReceive() {
return receiveBuilder()
.match(ActorIdentity.class, id -> id.getActorRef().isPresent(), id -> {
ActorRef ref = id.getActorRef().get();
getContext().watch(ref);
getContext().become(active(ref));
})
.match(ActorIdentity.class, id -> !id.getActorRef().isPresent(), id -> {
getContext().stop(self());
})
.build();
}
final AbstractActor.Receive active(final ActorRef another) {
return receiveBuilder()
.match(Terminated.class, t -> t.actor().equals(another), t ->
getContext().stop(self())
)
.build();
}
}
//#identify
@ -615,73 +758,70 @@ public class ActorDocTest extends AbstractJavaTest {
}
};
}
public static class NoReceiveActor extends AbstractActor {
}
@Test
public void noReceiveActor() {
EventFilter ex1 = new ErrorFilter(ActorInitializationException.class);
EventFilter[] ignoreExceptions = { ex1 };
try {
system.eventStream().publish(new TestEvent.Mute(immutableSeq(ignoreExceptions)));
new JavaTestKit(system) {{
final ActorRef victim = new EventFilter<ActorRef>(ActorInitializationException.class) {
protected ActorRef run() {
return system.actorOf(Props.create(NoReceiveActor.class), "victim");
}
}.message("Actor behavior has not been set with receive(...)").occurrences(1).exec();
public void usePatternsAskPipe() {
new JavaTestKit(system) {
{
ActorRef actorA = system.actorOf(TestActors.echoActorProps());
ActorRef actorB = system.actorOf(TestActors.echoActorProps());
ActorRef actorC = getRef();
assertEquals(true, victim.isTerminated());
}};
} finally {
system.eventStream().publish(new TestEvent.UnMute(immutableSeq(ignoreExceptions)));
}
//#ask-pipe
final Timeout t = new Timeout(Duration.create(5, TimeUnit.SECONDS));
final ArrayList<Future<Object>> futures = new ArrayList<Future<Object>>();
futures.add(ask(actorA, "request", 1000)); // using 1000ms timeout
futures.add(ask(actorB, "another request", t)); // using timeout from
// above
final Future<Iterable<Object>> aggregate = Futures.sequence(futures,
system.dispatcher());
final Future<Result> transformed = aggregate.map(
new Mapper<Iterable<Object>, Result>() {
public Result apply(Iterable<Object> coll) {
final Iterator<Object> it = coll.iterator();
final String x = (String) it.next();
final String s = (String) it.next();
return new Result(x, s);
}
}, system.dispatcher());
pipe(transformed, system.dispatcher()).to(actorC);
//#ask-pipe
expectMsgEquals(new Result("request", "another request"));
}
};
}
public static class MultipleReceiveActor extends AbstractActor {
public MultipleReceiveActor() {
receive(ReceiveBuilder.
match(String.class, s1 -> s1.toLowerCase().equals("become"), s1 -> {
sender().tell(s1.toUpperCase(), self());
receive(ReceiveBuilder.
match(String.class, s2 -> {
sender().tell(s2.toLowerCase(), self());
}).build()
);
}).
match(String.class, s1 -> {
sender().tell(s1.toUpperCase(), self());
}).build()
);
}
}
@Test
public void multipleReceiveActor() {
EventFilter ex1 = new ErrorFilter(IllegalActorStateException.class);
EventFilter[] ignoreExceptions = { ex1 };
try {
system.eventStream().publish(new TestEvent.Mute(immutableSeq(ignoreExceptions)));
new JavaTestKit(system) {{
new EventFilter<Boolean>(IllegalActorStateException.class) {
protected Boolean run() {
ActorRef victim = system.actorOf(Props.create(MultipleReceiveActor.class), "victim2");
victim.tell("Foo", getRef());
expectMsgEquals("FOO");
victim.tell("bEcoMe", getRef());
expectMsgEquals("BECOME");
victim.tell("Foo", getRef());
// if it's upper case, then the actor was restarted
expectMsgEquals("FOO");
return true;
}
}.message("Actor behavior has already been set with receive(...), " +
"use context().become(...) to change it later").occurrences(1).exec();
}};
} finally {
system.eventStream().publish(new TestEvent.UnMute(immutableSeq(ignoreExceptions)));
}
public void useKill() {
new JavaTestKit(system) {
{
ActorRef victim = system.actorOf(TestActors.echoActorProps());
watch(victim);
//#kill
victim.tell(akka.actor.Kill.getInstance(), ActorRef.noSender());
//#kill
expectTerminated(Duration.create(3, TimeUnit.SECONDS), victim);
}
};
}
@Test
public void usePoisonPill() {
new JavaTestKit(system) {
{
ActorRef victim = system.actorOf(TestActors.echoActorProps());
watch(victim);
//#poison-pill
victim.tell(akka.actor.PoisonPill.getInstance(), ActorRef.noSender());
//#poison-pill
expectTerminated(Duration.create(3, TimeUnit.SECONDS), victim);
}
};
}
@Test
@ -691,7 +831,7 @@ public class ActorDocTest extends AbstractJavaTest {
CoordinatedShutdown.get(system).addTask(
CoordinatedShutdown.PhaseBeforeServiceUnbind(), "someTaskName",
() -> {
return ask(someActor, "stop", new Timeout(5, TimeUnit.SECONDS))
return akka.pattern.PatternsCS.ask(someActor, "stop", new Timeout(5, TimeUnit.SECONDS))
.thenApply(reply -> Done.getInstance());
});
//#coordinated-shutdown-addTask

View file

@ -0,0 +1,105 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actorlambda;
import docs.AbstractJavaTest;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import scala.concurrent.Await;
import scala.concurrent.duration.Duration;
import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.testkit.JavaTestKit;
//#import
import akka.actor.Actor;
import akka.actor.IndirectActorProducer;
//#import
public class DependencyInjectionDocTest extends AbstractJavaTest {
public static class TheActor extends AbstractActor {
final String s;
public TheActor(String s) {
this.s = s;
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(String.class, msg -> {
sender().tell(s, self());
})
.build();
}
}
static ActorSystem system = null;
@BeforeClass
public static void beforeClass() {
system = ActorSystem.create("DependencyInjectionDocTest");
}
@AfterClass
public static void afterClass() throws Exception {
Await.ready(system.terminate(), Duration.create("5 seconds"));
}
//this is just to make the test below a tiny fraction nicer
private ActorSystem getContext() {
return system;
}
static
//#creating-indirectly
class DependencyInjector implements IndirectActorProducer {
final Object applicationContext;
final String beanName;
public DependencyInjector(Object applicationContext, String beanName) {
this.applicationContext = applicationContext;
this.beanName = beanName;
}
@Override
public Class<? 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 JavaTestKit(system) {
{
myActor.tell("hello", getRef());
expectMsgEquals("...");
}
};
}
}

View file

@ -3,37 +3,41 @@
*/
package docs.actorlambda;
//#testkit
import akka.actor.*;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import docs.AbstractJavaTest;
import java.util.Optional;
import static akka.pattern.Patterns.ask;
//#testkit
import akka.testkit.JavaTestKit;
import akka.testkit.TestProbe;
import akka.testkit.ErrorFilter;
import akka.testkit.EventFilter;
import akka.testkit.TestEvent;
import scala.concurrent.duration.Duration;
import static java.util.concurrent.TimeUnit.SECONDS;
import static akka.japi.Util.immutableSeq;
import scala.concurrent.Await;
//#testkit
//#supervisor
import akka.japi.pf.DeciderBuilder;
import static akka.actor.SupervisorStrategy.resume;
import static akka.actor.SupervisorStrategy.restart;
import static akka.actor.SupervisorStrategy.stop;
import static akka.actor.SupervisorStrategy.escalate;
import akka.japi.pf.DeciderBuilder;
import akka.japi.pf.ReceiveBuilder;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import docs.AbstractJavaTest;
import scala.PartialFunction;
import scala.concurrent.Await;
import static akka.pattern.Patterns.ask;
import scala.concurrent.duration.Duration;
import akka.testkit.TestProbe;
//#testkit
import akka.testkit.ErrorFilter;
import akka.testkit.EventFilter;
import akka.testkit.TestEvent;
import akka.testkit.JavaTestKit;
import static java.util.concurrent.TimeUnit.SECONDS;
import static akka.japi.Util.immutableSeq;
import scala.Option;
//#supervisor
import org.junit.Test;
import org.junit.BeforeClass;
import org.junit.AfterClass;
import scala.runtime.BoxedUnit;
//#testkit
public class FaultHandlingTest extends AbstractJavaTest {
@ -65,12 +69,13 @@ public class FaultHandlingTest extends AbstractJavaTest {
//#strategy
public Supervisor() {
receive(ReceiveBuilder.
match(Props.class, props -> {
sender().tell(context().actorOf(props), self());
}).build()
);
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Props.class, props -> {
sender().tell(getContext().actorOf(props), self());
})
.build();
}
}
@ -95,16 +100,17 @@ public class FaultHandlingTest extends AbstractJavaTest {
//#strategy2
public Supervisor2() {
receive(ReceiveBuilder.
match(Props.class, props -> {
sender().tell(context().actorOf(props), self());
}).build()
);
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Props.class, props -> {
sender().tell(getContext().actorOf(props), self());
})
.build();
}
@Override
public void preRestart(Throwable cause, Option<Object> msg) {
public void preRestart(Throwable cause, Optional<Object> msg) {
// do not kill all children, which is the default here
}
}
@ -116,12 +122,13 @@ public class FaultHandlingTest extends AbstractJavaTest {
public class Child extends AbstractActor {
int state = 0;
public Child() {
receive(ReceiveBuilder.
match(Exception.class, exception -> { throw exception; }).
match(Integer.class, i -> state = i).
matchEquals("get", s -> sender().tell(state, self())).build()
);
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Exception.class, exception -> { throw exception; })
.match(Integer.class, i -> state = i)
.matchEquals("get", s -> sender().tell(state, self()))
.build();
}
}

View file

@ -15,10 +15,12 @@ import akka.japi.pf.UnitPFBuilder;
//#actor
public class GraduallyBuiltActor extends AbstractActor {
private final LoggingAdapter log = Logging.getLogger(context().system(), this);
private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
public GraduallyBuiltActor() {
UnitPFBuilder<Object> builder = ReceiveBuilder.create();
@Override
public Receive createReceive() {
ReceiveBuilder builder = ReceiveBuilder.create();
builder.match(String.class, s -> {
log.info("Received String message: {}", s);
//#actor
@ -27,9 +29,12 @@ public class GraduallyBuiltActor extends AbstractActor {
//#reply
//#actor
});
// do some other stuff in between
builder.matchAny(o -> log.info("received unknown message"));
receive(builder.build());
return builder.build();
}
}
//#actor

View file

@ -8,7 +8,6 @@ import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.japi.pf.FI;
import akka.japi.pf.ReceiveBuilder;
import akka.testkit.JavaTestKit;
import docs.AbstractJavaTest;
import org.junit.AfterClass;
@ -16,7 +15,7 @@ import org.junit.BeforeClass;
import org.junit.Test;
import scala.concurrent.Await;
import scala.concurrent.duration.Duration;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
public class InitializationDocTest extends AbstractJavaTest {
@ -32,24 +31,57 @@ public class InitializationDocTest extends AbstractJavaTest {
public static void afterClass() throws Exception {
Await.ready(system.terminate(), Duration.create("5 seconds"));
}
static public class PreStartInitExample extends AbstractActor {
@Override
public Receive createReceive() {
return AbstractActor.emptyBehavior();
}
//#preStartInit
@Override
public void preStart() {
// Initialize children here
}
// Overriding postRestart to disable the call to preStart()
// after restarts
@Override
public void postRestart(Throwable reason) {
}
// The default implementation of preRestart() stops all the children
// of the actor. To opt-out from stopping the children, we
// have to override preRestart()
@Override
public void preRestart(Throwable reason, Optional<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;
public MessageInitExample() {
//#messageInit
receive(ReceiveBuilder.
matchEquals("init", m1 -> {
initializeMe = "Up and running";
context().become(ReceiveBuilder.
matchEquals("U OK?", m2 -> {
sender().tell(initializeMe, self());
}).build());
}).build()
//#messageInit
);
//#messageInit
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("init", m1 -> {
initializeMe = "Up and running";
getContext().become(receiveBuilder()
.matchEquals("U OK?", m2 -> {
sender().tell(initializeMe, self());
})
.build());
})
.build();
}
//#messageInit
}
public class GenericMessage<T> {
@ -61,24 +93,27 @@ public class InitializationDocTest extends AbstractJavaTest {
}
public static class GenericActor extends AbstractActor {
public GenericActor() {
receive(ReceiveBuilder.match(GenericMessage.class, (GenericMessage<String> msg) -> {
GenericMessage<String> message = msg;
sender().tell(message.value.toUpperCase(), self());
}).build());
@Override
public Receive createReceive() {
return receiveBuilder()
.matchUnchecked(GenericMessage.class, (GenericMessage<String> msg) -> {
GenericMessage<String> message = msg;
sender().tell(message.value.toUpperCase(), self());
})
.build();
}
}
static class GenericActorWithPredicate extends AbstractActor {
public GenericActorWithPredicate() {
@Override
public Receive createReceive() {
FI.TypedPredicate<GenericMessage<String>> typedPredicate = s -> !s.value.isEmpty();
receive(ReceiveBuilder.match(GenericMessage.class, typedPredicate, (GenericMessage<String> msg) -> {
sender().tell(msg.value.toUpperCase(), self());
}).build());
return receiveBuilder()
.matchUnchecked(GenericMessage.class, typedPredicate, (GenericMessage<String> msg) -> {
sender().tell(msg.value.toUpperCase(), self());
})
.build();
}
}

View file

@ -8,26 +8,26 @@ package docs.actorlambda;
import akka.actor.AbstractActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.japi.pf.ReceiveBuilder;
//#imports
//#my-actor
public class MyActor extends AbstractActor {
private final LoggingAdapter log = Logging.getLogger(context().system(), this);
private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
public MyActor() {
receive(ReceiveBuilder.
match(String.class, s -> {
@Override
public Receive createReceive() {
return receiveBuilder()
.match(String.class, s -> {
log.info("Received String message: {}", s);
//#my-actor
//#reply
sender().tell(s, self());
//#reply
//#my-actor
}).
matchAny(o -> log.info("received unknown message")).build()
);
})
.matchAny(o -> log.info("received unknown message"))
.build();
}
}
//#my-actor

View file

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

View file

@ -0,0 +1,29 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actorlambda;
//#my-stopping-actor
import akka.actor.ActorRef;
import akka.actor.AbstractActor;
public class MyStoppingActor extends AbstractActor {
ActorRef child = null;
// ... creation of child ...
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("interrupt-child", m ->
getContext().stop(child)
)
.matchEquals("done", m ->
getContext().stop(self())
)
.build();
}
}
//#my-stopping-actor

View file

@ -7,30 +7,30 @@ package docs.actorlambda;
//#sample-actor
import akka.actor.AbstractActor;
import akka.japi.pf.ReceiveBuilder;
import scala.PartialFunction;
import scala.runtime.BoxedUnit;
public class SampleActor extends AbstractActor {
private PartialFunction<Object, BoxedUnit> guarded = ReceiveBuilder.
match(String.class, s -> s.contains("guard"), s -> {
private Receive guarded = receiveBuilder()
.match(String.class, s -> s.contains("guard"), s -> {
sender().tell("contains(guard): " + s, self());
context().unbecome();
}).build();
getContext().unbecome();
})
.build();
public SampleActor() {
receive(ReceiveBuilder.
match(Double.class, d -> {
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Double.class, d -> {
sender().tell(d.isNaN() ? 0 : d, self());
}).
match(Integer.class, i -> {
})
.match(Integer.class, i -> {
sender().tell(i * 10, self());
}).
match(String.class, s -> s.startsWith("guard"), s -> {
})
.match(String.class, s -> s.startsWith("guard"), s -> {
sender().tell("startsWith(guard): " + s.toUpperCase(), self());
context().become(guarded, false);
}).build()
);
getContext().become(guarded, false);
})
.build();
}
}
//#sample-actor

View file

@ -174,7 +174,7 @@ public class FSMDocTest extends AbstractJavaTest {
expectMsgEquals(Active);
expectMsgEquals(Data.Foo);
String msg = expectMsgClass(String.class);
assertTrue(msg.startsWith("LogEntry(SomeState,Foo,Actor[akka://FSMDocTest/system/"));
assertThat(msg, CoreMatchers.startsWith("LogEntry(SomeState,Foo,Actor[akka://FSMDocTest/system/"));
}};
}
}

View file

@ -67,24 +67,24 @@ public class FaultHandlingDocSample {
public void preStart() {
// If we don't get any progress within 15 seconds then the service
// is unavailable
context().setReceiveTimeout(Duration.create("15 seconds"));
getContext().setReceiveTimeout(Duration.create("15 seconds"));
}
public Listener() {
receive(LoggingReceive.create(ReceiveBuilder.
@Override
public Receive createReceive() {
return LoggingReceive.create(receiveBuilder().
match(Progress.class, progress -> {
log().info("Current progress: {} %", progress.percent);
if (progress.percent >= 100.0) {
log().info("That's all, shutting down");
context().system().terminate();
getContext().system().terminate();
}
}).
matchEquals(ReceiveTimeout.getInstance(), x -> {
// No progress within 15 seconds, ServiceUnavailable
log().error("Shutting down due to unavailable service");
context().system().terminate();
}).build(), context()
));
getContext().system().terminate();
}).build(), getContext());
}
}
@ -119,7 +119,7 @@ public class FaultHandlingDocSample {
// The sender of the initial Start message will continuously be notified
// about progress
ActorRef progressListener;
final ActorRef counterService = context().actorOf(
final ActorRef counterService = getContext().actorOf(
Props.create(CounterService.class), "counter");
final int totalCount = 51;
@ -134,13 +134,14 @@ public class FaultHandlingDocSample {
return strategy;
}
public Worker() {
receive(LoggingReceive.create(ReceiveBuilder.
@Override
public Receive createReceive() {
return LoggingReceive.create(receiveBuilder().
matchEquals(Start, x -> progressListener == null, x -> {
progressListener = sender();
context().system().scheduler().schedule(
getContext().system().scheduler().schedule(
Duration.Zero(), Duration.create(1, "second"), self(), Do,
context().dispatcher(), null
getContext().dispatcher(), null
);
}).
matchEquals(Do, x -> {
@ -154,10 +155,9 @@ public class FaultHandlingDocSample {
public Progress apply(CurrentCount c) {
return new Progress(100.0 * c.count / totalCount);
}
}, context().dispatcher()), context().dispatcher())
}, getContext().dispatcher()), getContext().dispatcher())
.to(progressListener);
}).build(), context())
);
}).build(), getContext());
}
}
@ -254,7 +254,7 @@ public class FaultHandlingDocSample {
* when it has been terminated.
*/
void initStorage() {
storage = context().watch(context().actorOf(
storage = getContext().watch(getContext().actorOf(
Props.create(Storage.class), "storage"));
// Tell the counter, if any, to use the new storage
if (counter != null)
@ -263,12 +263,13 @@ public class FaultHandlingDocSample {
storage.tell(new Get(key), self());
}
public CounterService() {
receive(LoggingReceive.create(ReceiveBuilder.
@Override
public Receive createReceive() {
return LoggingReceive.create(receiveBuilder().
match(Entry.class, entry -> entry.key.equals(key) && counter == null, entry -> {
// Reply from Storage of the initial value, now we can create the Counter
final long value = entry.value;
counter = context().actorOf(Props.create(Counter.class, key, value));
counter = getContext().actorOf(Props.create(Counter.class, key, value));
// Tell the counter to use current storage
counter.tell(new UseStorage(storage), self());
// and send the buffered backlog to the counter
@ -290,15 +291,14 @@ public class FaultHandlingDocSample {
// Tell the counter that there is no storage for the moment
counter.tell(new UseStorage(null), self());
// Try to re-establish storage after while
context().system().scheduler().scheduleOnce(
getContext().system().scheduler().scheduleOnce(
Duration.create(10, "seconds"), self(), Reconnect,
context().dispatcher(), null);
getContext().dispatcher(), null);
}).
matchEquals(Reconnect, o -> {
// Re-establish storage after the scheduled delay
initStorage();
}).build(), context())
);
}).build(), getContext());
}
void forwardOrPlaceInBacklog(Object msg) {
@ -311,7 +311,7 @@ public class FaultHandlingDocSample {
" lack of initial value");
backlog.add(new SenderMsgPair(sender(), msg));
} else {
counter.forward(msg, context());
counter.forward(msg, getContext());
}
}
}
@ -345,8 +345,11 @@ public class FaultHandlingDocSample {
public Counter(String key, long initialValue) {
this.key = key;
this.count = initialValue;
receive(LoggingReceive.create(ReceiveBuilder.
}
@Override
public Receive createReceive() {
return LoggingReceive.create(receiveBuilder().
match(UseStorage.class, useStorage -> {
storage = useStorage.storage;
storeCount();
@ -357,8 +360,7 @@ public class FaultHandlingDocSample {
}).
matchEquals(GetCurrentCount, gcc -> {
sender().tell(new CurrentCount(key, count), self());
}).build(), context())
);
}).build(), getContext());
}
void storeCount() {
@ -430,8 +432,9 @@ public class FaultHandlingDocSample {
final DummyDB db = DummyDB.instance;
public Storage() {
receive(LoggingReceive.create(ReceiveBuilder.
@Override
public Receive createReceive() {
return LoggingReceive.create(receiveBuilder().
match(Store.class, store -> {
db.save(store.entry.key, store.entry.value);
}).
@ -439,8 +442,7 @@ public class FaultHandlingDocSample {
Long value = db.load(get.key);
sender().tell(new Entry(get.key, value == null ?
Long.valueOf(0L) : value), self());
}).build(), context())
);
}).build(), getContext());
}
}

View file

@ -12,7 +12,7 @@ public class Consumer2 extends UntypedConsumerActor {
if (message instanceof CamelMessage) {
CamelMessage camelMessage = (CamelMessage) message;
String body = camelMessage.getBodyAs(String.class, getCamelContext());
getSender().tell(String.format("Received message: %s",body), getSelf());
sender().tell(String.format("Received message: %s",body), self());
} else
unhandled(message);
}

View file

@ -18,12 +18,12 @@ public class Consumer3 extends UntypedConsumerActor{
public void onReceive(Object message) {
if (message instanceof CamelMessage) {
getSender().tell(Ack.getInstance(), getSelf());
sender().tell(Ack.getInstance(), self());
// on success
// ..
Exception someException = new Exception("e1");
// on failure
getSender().tell(new Status.Failure(someException), getSelf());
sender().tell(new Status.Failure(someException), self());
} else
unhandled(message);
}

View file

@ -24,7 +24,7 @@ public class Consumer4 extends UntypedConsumerActor {
if (message instanceof CamelMessage) {
CamelMessage camelMessage = (CamelMessage) message;
String body = camelMessage.getBodyAs(String.class, getCamelContext());
getSender().tell(String.format("Hello %s",body), getSelf());
sender().tell(String.format("Hello %s",body), self());
} else
unhandled(message);
}

View file

@ -47,7 +47,7 @@ public class ErrorThrowingConsumer extends UntypedConsumerActor{
@Override
public void preRestart(Throwable reason, Option<Object> message) {
getSender().tell(new Status.Failure(reason), getSelf());
sender().tell(new Status.Failure(reason), self());
}
}
//#ErrorThrowingConsumer

View file

@ -1,11 +1,11 @@
package docs.camel;
//#ProducerTemplate
import akka.actor.UntypedActor;
import akka.actor.UntypedAbstractActor;
import akka.camel.Camel;
import akka.camel.CamelExtension;
import org.apache.camel.ProducerTemplate;
public class MyActor extends UntypedActor {
public class MyActor extends UntypedAbstractActor {
public void onReceive(Object message) {
Camel camel = CamelExtension.get(getContext().system());
ProducerTemplate template = camel.template();

View file

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

View file

@ -1,16 +1,16 @@
package docs.camel;
//#CustomRoute
import akka.actor.UntypedActor;
import akka.actor.UntypedAbstractActor;
import akka.camel.CamelMessage;
import akka.dispatch.Mapper;
import akka.japi.Function;
public class Responder extends UntypedActor{
public class Responder extends UntypedAbstractActor{
public void onReceive(Object message) {
if (message instanceof CamelMessage) {
CamelMessage camelMessage = (CamelMessage) message;
getSender().tell(createResponse(camelMessage), getSelf());
sender().tell(createResponse(camelMessage), self());
} else
unhandled(message);
}

View file

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

View file

@ -5,6 +5,7 @@ package docs.ddata;
//#data-bot
import static java.util.concurrent.TimeUnit.SECONDS;
import scala.concurrent.duration.Duration;
import java.util.concurrent.ThreadLocalRandom;
@ -29,25 +30,26 @@ public class DataBot extends AbstractActor {
private static final String TICK = "tick";
private final LoggingAdapter log = Logging.getLogger(context().system(), this);
private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
private final ActorRef replicator =
DistributedData.get(context().system()).replicator();
private final Cluster node = Cluster.get(context().system());
DistributedData.get(getContext().system()).replicator();
private final Cluster node = Cluster.get(getContext().system());
private final Cancellable tickTask = context().system().scheduler().schedule(
private final Cancellable tickTask = getContext().system().scheduler().schedule(
Duration.create(5, SECONDS), Duration.create(5, SECONDS), self(), TICK,
context().dispatcher(), self());
getContext().dispatcher(), self());
private final Key<ORSet<String>> dataKey = ORSetKey.create("key");
@SuppressWarnings("unchecked")
public DataBot() {
receive(ReceiveBuilder
@Override
public Receive createReceive() {
return receiveBuilder()
.match(String.class, a -> a.equals(TICK), a -> receiveTick())
.match(Changed.class, c -> c.key().equals(dataKey), c -> receiveChanged((Changed<ORSet<String>>) c))
.match(UpdateResponse.class, r -> receiveUpdateResoponse())
.build());
.build();
}

View file

@ -35,13 +35,11 @@ import akka.testkit.JavaTestKit;
import akka.testkit.TestProbe;
import akka.serialization.SerializationExtension;
@SuppressWarnings({"unchecked", "unused"})
public class DistributedDataDocTest extends AbstractJavaTest {
static ActorSystem system;
void receive(PartialFunction<Object, BoxedUnit> pf) {
}
@BeforeClass
public static void setup() {
@ -55,237 +53,261 @@ public class DistributedDataDocTest extends AbstractJavaTest {
system = null;
}
@Test
public void demonstrateUpdate() {
new JavaTestKit(system) {
{
//#update
final Cluster node = Cluster.get(system);
final ActorRef replicator = DistributedData.get(system).replicator();
static
//#update
class DemonstrateUpdate extends AbstractActor {
final Cluster node = Cluster.get(getContext().system());
final ActorRef replicator =
DistributedData.get(getContext().system()).replicator();
final Key<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");
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)), getTestActor());
Replicator.writeLocal(), curr -> curr.increment(node, 1)), self());
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")), getTestActor());
writeTo3, curr -> curr.add("hello")), self());
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")), getTestActor());
writeMajority, curr -> curr.add(node, "hello")), self());
final WriteConsistency writeAll = new WriteAll(Duration.create(5, SECONDS));
replicator.tell(new Replicator.Update<Flag>(activeFlagKey, Flag.create(),
writeAll, curr -> curr.switchOn()), getTestActor());
//#update
expectMsgClass(UpdateSuccess.class);
//#update-response1
receive(ReceiveBuilder.
match(UpdateSuccess.class, a -> a.key().equals(counter1Key), a -> {
// ok
}).build());
//#update-response1
//#update-response2
receive(ReceiveBuilder.
match(UpdateSuccess.class, a -> a.key().equals(set1Key), a -> {
// ok
}).
match(UpdateTimeout.class, a -> a.key().equals(set1Key), a -> {
// write to 3 nodes failed within 1.second
}).build());
//#update-response2
}};
writeAll, curr -> curr.switchOn()), self());
});
//#update
//#update-response1
b.match(UpdateSuccess.class, a -> a.key().equals(counter1Key), a -> {
// ok
});
//#update-response1
//#update-response2
b.match(UpdateSuccess.class, a -> a.key().equals(set1Key), a -> {
// ok
})
.match(UpdateTimeout.class, a -> a.key().equals(set1Key), a -> {
// write to 3 nodes failed within 1.second
});
//#update-response2
//#update
return b.build();
}
}
//#update
@Test
public void demonstrateUpdateWithRequestContext() {
new JavaTestKit(system) {
{
static
//#update-request-context
class DemonstrateUpdateWithRequestContext extends AbstractActor {
final Cluster node = Cluster.get(getContext().system());
final ActorRef replicator =
DistributedData.get(getContext().system()).replicator();
//#update-request-context
final Cluster node = Cluster.get(system);
final ActorRef replicator = DistributedData.get(system).replicator();
final WriteConsistency writeTwo = new WriteTo(2, Duration.create(3, SECONDS));
final Key<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(sender());
Replicator.Update<PNCounter> upd = new Replicator.Update<PNCounter>(counter1Key,
PNCounter.create(), writeTwo, reqContext, curr -> curr.increment(node, 1));
replicator.tell(upd, self());
})
final WriteConsistency writeTwo = new WriteTo(2, Duration.create(3, SECONDS));
final Key<PNCounter> counter1Key = PNCounterKey.create("counter1");
.match(UpdateSuccess.class, a -> a.key().equals(counter1Key), a -> {
ActorRef replyTo = (ActorRef) a.getRequest().get();
replyTo.tell("ack", self());
})
receive(ReceiveBuilder.
match(String.class, a -> a.equals("increment"), a -> {
// incoming command to increase the counter
Optional<Object> reqContext = Optional.of(getRef());
Replicator.Update<PNCounter> upd = new Replicator.Update<PNCounter>(counter1Key,
PNCounter.create(), writeTwo, reqContext, curr -> curr.increment(node, 1));
replicator.tell(upd, getTestActor());
}).
match(UpdateSuccess.class, a -> a.key().equals(counter1Key), a -> {
ActorRef replyTo = (ActorRef) a.getRequest().get();
replyTo.tell("ack", getTestActor());
}).
match(UpdateTimeout.class, a -> a.key().equals(counter1Key), a -> {
ActorRef replyTo = (ActorRef) a.getRequest().get();
replyTo.tell("nack", getTestActor());
}).build());
//#update-request-context
.match(UpdateTimeout.class, a -> a.key().equals(counter1Key), a -> {
ActorRef replyTo = (ActorRef) a.getRequest().get();
replyTo.tell("nack", self());
})
.build();
}
};
}
//#update-request-context
@SuppressWarnings({ "unused", "unchecked" })
@Test
public void demonstrateGet() {
new JavaTestKit(system) {
{
static
//#get
class DemonstrateGet extends AbstractActor {
final ActorRef replicator =
DistributedData.get(getContext().system()).replicator();
final Key<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 -> {
//#get
final ActorRef replicator = DistributedData.get(system).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");
replicator.tell(new Replicator.Get<PNCounter>(counter1Key,
Replicator.readLocal()), getTestActor());
final ReadConsistency readFrom3 = new ReadFrom(3, Duration.create(1, SECONDS));
replicator.tell(new Replicator.Get<GSet<String>>(set1Key,
readFrom3), getTestActor());
final ReadConsistency readMajority = new ReadMajority(Duration.create(5, SECONDS));
replicator.tell(new Replicator.Get<ORSet<String>>(set2Key,
readMajority), getTestActor());
final ReadConsistency readAll = new ReadAll(Duration.create(5, SECONDS));
replicator.tell(new Replicator.Get<Flag>(activeFlagKey,
readAll), getTestActor());
replicator.tell(new Replicator.Get<PNCounter>(counter1Key,
Replicator.readLocal()), self());
final ReadConsistency readFrom3 = new ReadFrom(3, Duration.create(1, SECONDS));
replicator.tell(new Replicator.Get<GSet<String>>(set1Key,
readFrom3), self());
final ReadConsistency readMajority = new ReadMajority(Duration.create(5, SECONDS));
replicator.tell(new Replicator.Get<ORSet<String>>(set2Key,
readMajority), self());
final ReadConsistency readAll = new ReadAll(Duration.create(5, SECONDS));
replicator.tell(new Replicator.Get<Flag>(activeFlagKey,
readAll), self());
});
//#get
//#get-response1
receive(ReceiveBuilder.
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
}).build());
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
receive(ReceiveBuilder.
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
}).build());
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
@SuppressWarnings("unchecked")
@Test
public void demonstrateGetWithRequestContext() {
new JavaTestKit(system) {
{
static
//#get-request-context
class DemonstrateGetWithRequestContext extends AbstractActor {
final ActorRef replicator =
DistributedData.get(getContext().system()).replicator();
final ReadConsistency readTwo = new ReadFrom(2, Duration.create(3, SECONDS));
final Key<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(sender());
replicator.tell(new Replicator.Get<PNCounter>(counter1Key,
readTwo), self());
})
//#get-request-context
final ActorRef replicator = DistributedData.get(system).replicator();
final ReadConsistency readTwo = new ReadFrom(2, Duration.create(3, SECONDS));
final Key<PNCounter> counter1Key = PNCounterKey.create("counter1");
.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, self());
})
receive(ReceiveBuilder.
match(String.class, a -> a.equals("get-count"), a -> {
// incoming request to retrieve current value of the counter
Optional<Object> reqContext = Optional.of(getTestActor());
replicator.tell(new Replicator.Get<PNCounter>(counter1Key,
readTwo), getTestActor());
}).
.match(GetFailure.class, a -> a.key().equals(counter1Key), a -> {
ActorRef replyTo = (ActorRef) a.getRequest().get();
replyTo.tell(-1L, self());
})
match(GetSuccess.class, a -> a.key().equals(counter1Key), a -> {
ActorRef replyTo = (ActorRef) a.getRequest().get();
GetSuccess<PNCounter> g = a;
long value = g.dataValue().getValue().longValue();
replyTo.tell(value, getTestActor());
}).
match(GetFailure.class, a -> a.key().equals(counter1Key), a -> {
ActorRef replyTo = (ActorRef) a.getRequest().get();
replyTo.tell(-1L, getTestActor());
}).
match(NotFound.class, a -> a.key().equals(counter1Key), a -> {
ActorRef replyTo = (ActorRef) a.getRequest().get();
replyTo.tell(0L, getTestActor());
}).build());
//#get-request-context
.match(NotFound.class, a -> a.key().equals(counter1Key), a -> {
ActorRef replyTo = (ActorRef) a.getRequest().get();
replyTo.tell(0L, self());
})
.build();
}
};
}
//#get-request-context
@SuppressWarnings("unchecked")
abstract class MyActor extends AbstractActor {
//#subscribe
final ActorRef replicator = DistributedData.get(system).replicator();
static
//#subscribe
class DemonstrateSubscribe extends AbstractActor {
final ActorRef replicator =
DistributedData.get(getContext().system()).replicator();
final Key<PNCounter> counter1Key = PNCounterKey.create("counter1");
BigInteger currentValue = BigInteger.valueOf(0);
public MyActor() {
receive(ReceiveBuilder.
match(Changed.class, a -> a.key().equals(counter1Key), a -> {
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Changed.class, a -> a.key().equals(counter1Key), a -> {
Changed<PNCounter> g = a;
currentValue = g.dataValue().getValue();
}).
match(String.class, a -> a.equals("get-count"), a -> {
})
.match(String.class, a -> a.equals("get-count"), a -> {
// incoming request to retrieve current value of the counter
sender().tell(currentValue, sender());
}).build());
})
.build();
}
@Override
public void preStart() {
// subscribe to changes of the Counter1Key value
replicator.tell(new Subscribe<PNCounter>(counter1Key, self()), ActorRef.noSender());
}
//#subscribe
}
//#subscribe
@Test
public void demonstrateDelete() {
new JavaTestKit(system) {
{
//#delete
final ActorRef replicator = DistributedData.get(system).replicator();
final Key<PNCounter> counter1Key = PNCounterKey.create("counter1");
final Key<ORSet<String>> set2Key = ORSetKey.create("set2");
static
//#delete
class DemonstrateDelete extends AbstractActor {
final ActorRef replicator =
DistributedData.get(getContext().system()).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()), getTestActor());
final WriteConsistency writeMajority =
new WriteMajority(Duration.create(5, SECONDS));
replicator.tell(new Delete<PNCounter>(counter1Key,
writeMajority), getTestActor());
//#delete
}};
replicator.tell(new Delete<PNCounter>(counter1Key,
Replicator.writeLocal()), self());
final WriteConsistency writeMajority =
new WriteMajority(Duration.create(5, SECONDS));
replicator.tell(new Delete<PNCounter>(counter1Key,
writeMajority), self());
})
.build();
}
}
//#delete
public void demonstratePNCounter() {
//#pncounter

View file

@ -8,8 +8,8 @@ import akka.dispatch.RequiresMessageQueue;
import akka.testkit.AkkaSpec;
import com.typesafe.config.ConfigFactory;
import docs.AbstractJavaTest;
import docs.actor.MyBoundedUntypedActor;
import docs.actor.MyUntypedActor;
import docs.actorlambda.MyBoundedActor;
import docs.actorlambda.MyActor;
import org.junit.ClassRule;
import org.junit.Test;
import scala.concurrent.ExecutionContext;
@ -17,7 +17,7 @@ import scala.concurrent.ExecutionContext;
//#imports
import akka.actor.*;
//#imports
import akka.actor.AbstractActor.Receive;
//#imports-prio
import akka.event.Logging;
import akka.event.LoggingAdapter;
@ -52,7 +52,7 @@ public class DispatcherDocTest extends AbstractJavaTest {
public void defineDispatcherInConfig() {
//#defining-dispatcher-in-config
ActorRef myActor =
system.actorOf(Props.create(MyUntypedActor.class),
system.actorOf(Props.create(MyActor.class),
"myactor");
//#defining-dispatcher-in-config
}
@ -62,7 +62,7 @@ public class DispatcherDocTest extends AbstractJavaTest {
public void defineDispatcherInCode() {
//#defining-dispatcher-in-code
ActorRef myActor =
system.actorOf(Props.create(MyUntypedActor.class).withDispatcher("my-dispatcher"),
system.actorOf(Props.create(MyActor.class).withDispatcher("my-dispatcher"),
"myactor3");
//#defining-dispatcher-in-code
}
@ -71,7 +71,7 @@ public class DispatcherDocTest extends AbstractJavaTest {
@Test
public void defineFixedPoolSizeDispatcher() {
//#defining-fixed-pool-size-dispatcher
ActorRef myActor = system.actorOf(Props.create(MyUntypedActor.class)
ActorRef myActor = system.actorOf(Props.create(MyActor.class)
.withDispatcher("blocking-io-dispatcher"));
//#defining-fixed-pool-size-dispatcher
}
@ -80,7 +80,7 @@ public class DispatcherDocTest extends AbstractJavaTest {
@Test
public void definePinnedDispatcher() {
//#defining-pinned-dispatcher
ActorRef myActor = system.actorOf(Props.create(MyUntypedActor.class)
ActorRef myActor = system.actorOf(Props.create(MyActor.class)
.withDispatcher("my-pinned-dispatcher"));
//#defining-pinned-dispatcher
}
@ -99,7 +99,7 @@ public class DispatcherDocTest extends AbstractJavaTest {
public void defineMailboxInConfig() {
//#defining-mailbox-in-config
ActorRef myActor =
system.actorOf(Props.create(MyUntypedActor.class),
system.actorOf(Props.create(MyActor.class),
"priomailboxactor");
//#defining-mailbox-in-config
}
@ -109,7 +109,7 @@ public class DispatcherDocTest extends AbstractJavaTest {
public void defineMailboxInCode() {
//#defining-mailbox-in-code
ActorRef myActor =
system.actorOf(Props.create(MyUntypedActor.class)
system.actorOf(Props.create(MyActor.class)
.withMailbox("prio-mailbox"));
//#defining-mailbox-in-code
}
@ -118,7 +118,7 @@ public class DispatcherDocTest extends AbstractJavaTest {
@Test
public void usingARequiredMailbox() {
ActorRef myActor =
system.actorOf(Props.create(MyBoundedUntypedActor.class));
system.actorOf(Props.create(MyBoundedActor.class));
}
@Test
@ -126,18 +126,21 @@ public class DispatcherDocTest extends AbstractJavaTest {
JavaTestKit probe = new JavaTestKit(system);
//#prio-dispatcher
class Demo extends UntypedActor {
class Demo extends AbstractActor {
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
{
for (Object msg : new Object[] { "lowpriority", "lowpriority",
"highpriority", "pigdog", "pigdog2", "pigdog3", "highpriority",
PoisonPill.getInstance() }) {
getSelf().tell(msg, getSelf());
self().tell(msg, self());
}
}
public void onReceive(Object message) {
log.info(message.toString());
@Override
public Receive createReceive() {
return receiveBuilder().matchAny(message -> {
log.info(message.toString());
}).build();
}
}
@ -166,17 +169,20 @@ public class DispatcherDocTest extends AbstractJavaTest {
JavaTestKit probe = new JavaTestKit(system);
//#control-aware-dispatcher
class Demo extends UntypedActor {
class Demo extends AbstractActor {
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
{
for (Object msg : new Object[] { "foo", "bar", new MyControlMessage(),
PoisonPill.getInstance() }) {
getSelf().tell(msg, getSelf());
self().tell(msg, self());
}
}
public void onReceive(Object message) {
log.info(message.toString());
@Override
public Receive createReceive() {
return receiveBuilder().matchAny(message -> {
log.info(message.toString());
}).build();
}
}
@ -226,18 +232,18 @@ public class DispatcherDocTest extends AbstractJavaTest {
@Test
public void requiredMailboxDispatcher() throws Exception {
ActorRef myActor = system.actorOf(Props.create(MyUntypedActor.class)
ActorRef myActor = system.actorOf(Props.create(MyActor.class)
.withDispatcher("custom-dispatcher"));
}
static
//#require-mailbox-on-actor
public class MySpecialActor extends UntypedActor implements
public class MySpecialActor extends AbstractActor implements
RequiresMessageQueue<MyUnboundedJMessageQueueSemantics> {
//#require-mailbox-on-actor
@Override
public void onReceive(Object message) throws Exception {
unhandled(message);
public Receive createReceive() {
return AbstractActor.emptyBehavior();
}
//#require-mailbox-on-actor
// ...

View file

@ -22,7 +22,7 @@ import akka.event.Logging.Debug;
import docs.AbstractJavaTest;
import org.junit.Test;
import akka.testkit.JavaTestKit;
import scala.Option;
import java.util.Optional;
//#imports-mdc
import akka.event.Logging;
@ -80,15 +80,18 @@ public class LoggingDocTest extends AbstractJavaTest {
}
}
static class Listener extends UntypedActor {
static class Listener extends AbstractActor {
@Override
public void onReceive(Object message) throws Exception {
if (message instanceof Jazz) {
System.out.printf("%s is listening to: %s%n", self().path().name(), message);
} else if (message instanceof Electronic) {
System.out.printf("%s is listening to: %s%n", self().path().name(), message);
public Receive createReceive() {
return receiveBuilder()
.match(Jazz.class, msg ->
System.out.printf("%s is listening to: %s%n", self().path().name(), msg)
)
.match(Electronic.class, msg ->
System.out.printf("%s is listening to: %s%n", self().path().name(), msg)
)
.build();
}
}
}
//#superclass-subscription-eventstream
@ -148,7 +151,7 @@ public class LoggingDocTest extends AbstractJavaTest {
}
//#my-actor
class MyActor extends UntypedActor {
class MyActor extends AbstractActor {
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
@Override
@ -157,68 +160,86 @@ public class LoggingDocTest extends AbstractJavaTest {
}
@Override
public void preRestart(Throwable reason, Option<Object> message) {
public void preRestart(Throwable reason, Optional<Object> message) {
log.error(reason, "Restarting due to [{}] when processing [{}]",
reason.getMessage(), message.isDefined() ? message.get() : "");
reason.getMessage(), message.isPresent() ? message.get() : "");
}
public void onReceive(Object message) {
if (message.equals("test")) {
log.info("Received test");
} else {
log.warning("Received unknown message: {}", message);
}
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("test", msg ->
log.info("Received test")
)
.matchAny(msg ->
log.warning("Received unknown message: {}", msg)
)
.build();
}
}
//#my-actor
//#mdc-actor
class MdcActor extends UntypedActor {
class MdcActor extends AbstractActor {
final DiagnosticLoggingAdapter log = Logging.getLogger(this);
public void onReceive(Object message) {
@Override
public Receive createReceive() {
return receiveBuilder()
.matchAny(msg -> {
Map<String, Object> mdc;
mdc = new HashMap<String, Object>();
mdc.put("requestId", 1234);
mdc.put("visitorId", 5678);
log.setMDC(mdc);
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.info("Starting new request");
log.clearMDC();
log.clearMDC();
})
.build();
}
}
//#mdc-actor
//#my-event-listener
class MyEventListener extends UntypedActor {
public void onReceive(Object message) {
if (message instanceof InitializeLogger) {
getSender().tell(Logging.loggerInitialized(), getSelf());
} else if (message instanceof Error) {
// ...
} else if (message instanceof Warning) {
// ...
} else if (message instanceof Info) {
// ...
} else if (message instanceof Debug) {
// ...
}
class MyEventListener extends AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder()
.match(InitializeLogger.class, msg -> {
sender().tell(Logging.loggerInitialized(), self());
})
.match(Error.class, msg -> {
// ...
})
.match(Warning.class, msg -> {
// ...
})
.match(Info.class, msg -> {
// ...
})
.match(Debug.class, msg -> {
// ...
})
.build();
}
}
//#my-event-listener
static
//#deadletter-actor
public class DeadLetterActor extends UntypedActor {
public void onReceive(Object message) {
if (message instanceof DeadLetter) {
System.out.println(message);
}
public class DeadLetterActor extends AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder()
.match(DeadLetter.class, msg -> {
System.out.println(msg);
})
.build();
}
}
//#deadletter-actor

View file

@ -57,11 +57,16 @@ public class ExtensionDocTest extends AbstractJavaTest {
static
//#extension-usage-actor
public class MyActor extends UntypedActor {
public void onReceive(Object msg) {
// typically you would use static import of the
// CountExtension.CountExtensionProvider field
CountExtension.CountExtensionProvider.get(getContext().system()).increment();
public class MyActor extends AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder()
.matchAny(msg -> {
// typically you would use static import of the
// CountExtension.CountExtensionProvider field
CountExtension.CountExtensionProvider.get(getContext().system()).increment();
})
.build();
}
}

View file

@ -9,14 +9,14 @@ import akka.actor.AbstractExtensionId;
import akka.actor.ExtensionIdProvider;
import akka.actor.ActorSystem;
import akka.actor.ExtendedActorSystem;
import docs.AbstractJavaTest;
import scala.concurrent.duration.Duration;
import com.typesafe.config.Config;
import java.util.concurrent.TimeUnit;
//#imports
import akka.actor.UntypedActor;
import docs.AbstractJavaTest;
import akka.actor.AbstractActor;
import org.junit.Test;
public class SettingsExtensionDocTest extends AbstractJavaTest {
@ -60,7 +60,7 @@ public class SettingsExtensionDocTest extends AbstractJavaTest {
static
//#extension-usage-actor
public class MyActor extends UntypedActor {
public class MyActor extends AbstractActor {
// typically you would use static import of the Settings.SettingsProvider field
final SettingsImpl settings =
Settings.SettingsProvider.get(getContext().system());
@ -73,7 +73,9 @@ public class SettingsExtensionDocTest extends AbstractJavaTest {
return new Connection();
}
public void onReceive(Object msg) {
@Override
public Receive createReceive() {
return AbstractActor.emptyBehavior();
}
//#extension-usage-actor
}

View file

@ -65,7 +65,7 @@ import org.junit.Test;
import akka.testkit.AkkaSpec;
import akka.actor.Status.Failure;
import akka.actor.ActorSystem;
import akka.actor.UntypedActor;
import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.Props;
import akka.pattern.Patterns;
@ -656,20 +656,21 @@ public class FutureDocTest extends AbstractJavaTest {
public static class MyActor extends UntypedActor {
public void onReceive(Object message) {
if (message instanceof String) {
getSender().tell(((String) message).toUpperCase(), getSelf());
} else if (message instanceof Integer) {
int i = ((Integer) message).intValue();
if (i < 0) {
getSender().tell(new Failure(new ArithmeticException("Negative values not supported")), getSelf());
} else {
getSender().tell(i, getSelf());
}
} else {
unhandled(message);
}
public static class MyActor extends AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder()
.match(String.class, msg -> {
sender().tell(msg.toUpperCase(), self());
})
.match(Integer.class, i -> {
if (i < 0) {
sender().tell(new Failure(new ArithmeticException("Negative values not supported")), self());
} else {
sender().tell(i, self());
}
})
.build();
}
}
}

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