Merge pull request #22003 from akka/wip-lambda-patriknw
Improvements of AbstractActor API, #21717
This commit is contained in:
commit
04846f4528
158 changed files with 3751 additions and 9345 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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] {
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
||||
|
|
|
|||
|
|
@ -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>>();
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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<String> 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<String> 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<X, Y> matcher = Matcher.create(...);
|
||||
*
|
||||
*
|
||||
* Y someY = matcher.match(obj);
|
||||
* </code></pre>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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<String> 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<String> 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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<String> 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<String> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<X> matcher = UnitMatcher.create(...);
|
||||
*
|
||||
*
|
||||
* matcher.match(obj);
|
||||
* </code></pre>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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<String> 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<String> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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/>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
* }
|
||||
* }
|
||||
* }}}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ package akka.actor
|
|||
* }
|
||||
* }}}
|
||||
*/
|
||||
@deprecated("Use AbstractActor instead of UntypedActor.", since = "2.5.0")
|
||||
abstract class UntypedActor extends Actor {
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---------------
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
.. _lambda-actors-java:
|
||||
.. _actors-java:
|
||||
|
||||
###################################
|
||||
Actors (Java with Lambda Support)
|
||||
###################################
|
||||
########
|
||||
Actors
|
||||
########
|
||||
|
||||
The `Actor Model`_ provides a higher level of abstraction for writing concurrent
|
||||
and distributed systems. It alleviates the developer from having to deal with
|
||||
|
|
@ -17,14 +17,6 @@ its syntax from Erlang.
|
|||
|
||||
.. _Actor Model: http://en.wikipedia.org/wiki/Actor_model
|
||||
|
||||
.. warning::
|
||||
|
||||
The Java with lambda support part of Akka is marked as **“experimental”** as of its introduction in
|
||||
Akka 2.3.0. We will continue to improve this API based on our users’ feedback, which implies that
|
||||
while we try to keep incompatible changes to a minimum, but the binary compatibility guarantee for
|
||||
maintenance releases does not apply to the :class:`akka.actor.AbstractActor`, related classes and
|
||||
the :class:`akka.japi.pf` package.
|
||||
|
||||
Creating Actors
|
||||
===============
|
||||
|
||||
|
|
@ -54,12 +46,6 @@ Here is an example:
|
|||
.. includecode:: code/docs/actorlambda/MyActor.java
|
||||
:include: imports,my-actor
|
||||
|
||||
In case you want to provide many :meth:`match` cases but want to avoid creating a long call
|
||||
trail, you can split the creation of the builder into multiple statements as in the example:
|
||||
|
||||
.. includecode:: code/docs/actorlambda/GraduallyBuiltActor.java
|
||||
:include: imports,actor
|
||||
|
||||
Please note that the Akka Actor ``receive`` message loop is exhaustive, which
|
||||
is different compared to Erlang and the late Scala Actors. This means that you
|
||||
need to provide a pattern match for all messages that it can accept and if you
|
||||
|
|
@ -180,20 +166,20 @@ another child to the same parent an `InvalidActorNameException` is thrown.
|
|||
|
||||
Actors are automatically started asynchronously when created.
|
||||
|
||||
.. _actor-create-factory-lambda:
|
||||
.. _actor-create-factory-java:
|
||||
|
||||
Dependency Injection
|
||||
--------------------
|
||||
|
||||
If your UntypedActor has a constructor that takes parameters then those need to
|
||||
If your actor has a constructor that takes parameters then those need to
|
||||
be part of the :class:`Props` as well, as described `above`__. But there
|
||||
are cases when a factory method must be used, for example when the actual
|
||||
constructor arguments are determined by a dependency injection framework.
|
||||
|
||||
__ Props_
|
||||
|
||||
.. includecode:: code/docs/actor/UntypedActorDocTest.java#import-indirect
|
||||
.. includecode:: code/docs/actor/UntypedActorDocTest.java
|
||||
.. includecode:: code/docs/actorlambda/DependencyInjectionDocTest.java#import
|
||||
.. includecode:: code/docs/actorlambda/DependencyInjectionDocTest.java
|
||||
:include: creating-indirectly
|
||||
:exclude: obtain-fresh-Actor-instance-from-DI-framework
|
||||
|
||||
|
|
@ -260,19 +246,19 @@ In addition, it offers:
|
|||
occurred within a distant descendant it is still reported one level up at a
|
||||
time).
|
||||
|
||||
* :meth:`context()` exposes contextual information for the actor and the current message, such as:
|
||||
* :meth:`getContext()` exposes contextual information for the actor and the current message, such as:
|
||||
|
||||
* factory methods to create child actors (:meth:`actorOf`)
|
||||
* system that the actor belongs to
|
||||
* parent supervisor
|
||||
* supervised children
|
||||
* lifecycle monitoring
|
||||
* hotswap behavior stack as described in :ref:`actor-hotswap-lambda`
|
||||
* hotswap behavior stack as described in :ref:`actor-hotswap-java`
|
||||
|
||||
The remaining visible methods are user-overridable life-cycle hooks which are
|
||||
described in the following:
|
||||
|
||||
.. includecode:: code/docs/actor/UntypedActorDocTest.java#lifecycle-callbacks
|
||||
.. includecode:: code/docs/actorlambda/ActorDocTest.java#lifecycle-callbacks
|
||||
|
||||
The implementations shown above are the defaults provided by the :class:`AbstractActor`
|
||||
class.
|
||||
|
|
@ -317,11 +303,11 @@ occupying it. ``ActorSelection`` cannot be watched for this reason. It is
|
|||
possible to resolve the current incarnation's ``ActorRef`` living under the
|
||||
path by sending an ``Identify`` message to the ``ActorSelection`` which
|
||||
will be replied to with an ``ActorIdentity`` containing the correct reference
|
||||
(see :ref:`actorSelection-lambda`). This can also be done with the ``resolveOne``
|
||||
(see :ref:`actorselection-java`). This can also be done with the ``resolveOne``
|
||||
method of the :class:`ActorSelection`, which returns a ``Future`` of the matching
|
||||
:class:`ActorRef`.
|
||||
|
||||
.. _deathwatch-lambda:
|
||||
.. _deathwatch-java:
|
||||
|
||||
Lifecycle Monitoring aka DeathWatch
|
||||
-----------------------------------
|
||||
|
|
@ -334,6 +320,7 @@ termination (see `Stopping Actors`_). This service is provided by the
|
|||
|
||||
Registering a monitor is easy:
|
||||
|
||||
.. includecode:: code/docs/actorlambda/ActorDocTest.java#import-terminated
|
||||
.. includecode:: code/docs/actorlambda/ActorDocTest.java#watch
|
||||
|
||||
It should be noted that the :class:`Terminated` message is generated
|
||||
|
|
@ -354,7 +341,7 @@ using ``context.unwatch(target)``. This works even if the :class:`Terminated`
|
|||
message has already been enqueued in the mailbox; after calling :meth:`unwatch`
|
||||
no :class:`Terminated` message for that actor will be processed anymore.
|
||||
|
||||
.. _start-hook-lambda:
|
||||
.. _start-hook-java:
|
||||
|
||||
Start Hook
|
||||
----------
|
||||
|
|
@ -371,7 +358,7 @@ Initialization code which is part of the actor’s constructor will always be
|
|||
called when an instance of the actor class is created, which happens at every
|
||||
restart.
|
||||
|
||||
.. _restart-hook-lambda:
|
||||
.. _restart-hook-java:
|
||||
|
||||
Restart Hooks
|
||||
-------------
|
||||
|
|
@ -414,7 +401,7 @@ usual.
|
|||
it has processed the last messages sent by the child before the failure.
|
||||
See :ref:`message-ordering` for details.
|
||||
|
||||
.. _stop-hook-lambda:
|
||||
.. _stop-hook-java:
|
||||
|
||||
Stop Hook
|
||||
---------
|
||||
|
|
@ -425,7 +412,7 @@ to run after message queuing has been disabled for this actor, i.e. messages
|
|||
sent to a stopped actor will be redirected to the :obj:`deadLetters` of the
|
||||
:obj:`ActorSystem`.
|
||||
|
||||
.. _actorSelection-lambda:
|
||||
.. _actorselection-java:
|
||||
|
||||
Identifying Actors via Actor Selection
|
||||
======================================
|
||||
|
|
@ -447,7 +434,7 @@ result:
|
|||
It is always preferable to communicate with other Actors using their ActorRef
|
||||
instead of relying upon ActorSelection. Exceptions are
|
||||
|
||||
* sending messages using the :ref:`at-least-once-delivery-java-lambda` facility
|
||||
* sending messages using the :ref:`at-least-once-delivery-java` facility
|
||||
* initiating first contact with a remote system
|
||||
|
||||
In all other cases ActorRefs can be provided during Actor creation or
|
||||
|
|
@ -488,7 +475,7 @@ of that reply is guaranteed, it still is a normal message.
|
|||
You can also acquire an :class:`ActorRef` for an :class:`ActorSelection` with
|
||||
the ``resolveOne`` method of the :class:`ActorSelection`. It returns a
|
||||
``Future`` of the matching :class:`ActorRef` if such an actor exists (see also
|
||||
:ref:`actor-java-lambda` for Java compatibility). It is completed with failure
|
||||
:ref:`scala-java-compat` for Java compatibility). It is completed with failure
|
||||
[[akka.actor.ActorNotFound]] if no such actor exists or the identification
|
||||
didn't complete within the supplied `timeout`.
|
||||
|
||||
|
|
@ -532,7 +519,7 @@ In all these methods you have the option of passing along your own ``ActorRef``.
|
|||
Make it a practice of doing so because it will allow the receiver actors to be able to respond
|
||||
to your message, since the sender reference is sent along with the message.
|
||||
|
||||
.. _actors-tell-sender-lambda:
|
||||
.. _actors-tell-sender-java:
|
||||
|
||||
Tell: Fire-forget
|
||||
-----------------
|
||||
|
|
@ -551,7 +538,7 @@ different one. Outside of an actor and if no reply is needed the second
|
|||
argument can be ``null``; if a reply is needed outside of an actor you can use
|
||||
the ask-pattern described next..
|
||||
|
||||
.. _actors-ask-lambda:
|
||||
.. _actors-ask-java:
|
||||
|
||||
Ask: Send-And-Receive-Future
|
||||
----------------------------
|
||||
|
|
@ -559,8 +546,8 @@ Ask: Send-And-Receive-Future
|
|||
The ``ask`` pattern involves actors as well as futures, hence it is offered as
|
||||
a use pattern rather than a method on :class:`ActorRef`:
|
||||
|
||||
.. includecode:: code/docs/actor/UntypedActorDocTest.java#import-ask
|
||||
.. includecode:: code/docs/actor/UntypedActorDocTest.java#ask-pipe
|
||||
.. includecode:: code/docs/actorlambda/ActorDocTest.java#import-ask
|
||||
.. includecode:: code/docs/actorlambda/ActorDocTest.java#ask-pipe
|
||||
|
||||
This example demonstrates ``ask`` together with the ``pipe`` pattern on
|
||||
futures, because this is likely to be a common combination. Please note that
|
||||
|
|
@ -577,6 +564,10 @@ involves creating an internal actor for handling this reply, which needs to
|
|||
have a timeout after which it is destroyed in order not to leak resources; see
|
||||
more below.
|
||||
|
||||
.. note::
|
||||
A variant of the ``ask`` pattern that returns a ``CompletionStage`` instead of a Scala ``Future``
|
||||
is available in the ``akka.pattern.PatternsCS`` object.
|
||||
|
||||
.. warning::
|
||||
|
||||
To complete the future with an exception you need send a Failure message to the sender.
|
||||
|
|
@ -615,33 +606,60 @@ routers, load-balancers, replicators etc.
|
|||
|
||||
.. includecode:: code/docs/actorlambda/ActorDocTest.java#forward
|
||||
|
||||
.. _actors-receive-java:
|
||||
|
||||
Receive messages
|
||||
================
|
||||
|
||||
An Actor either has to set its initial receive behavior in the constructor by
|
||||
calling the :meth:`receive` method in the :class:`AbstractActor`:
|
||||
An actor has to define its initial receive behavior by implementing
|
||||
the :meth:`createReceive` method in the :class:`AbstractActor`:
|
||||
|
||||
.. includecode:: code/docs/actorlambda/ActorDocTest.java
|
||||
:include: receive-constructor
|
||||
:exclude: and-some-behavior
|
||||
.. includecode:: code/docs/actorlambda/ActorDocTest.java#createReceive
|
||||
|
||||
or by implementing the :meth:`receive` method in the :class:`Actor` interface:
|
||||
|
||||
.. includecode:: code/docs/actorlambda/ActorDocTest.java#receive
|
||||
|
||||
Both the argument to the :class:`AbstractActor` :meth:`receive` method and the return
|
||||
type of the :class:`Actor` :meth:`receive` method is a ``PartialFunction<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 Actor’s 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.
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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``
|
||||
|
|
|
|||
|
|
@ -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 actor’s constructor.
|
||||
* @return a Props for creating this actor, which can then be further configured
|
||||
* (e.g. calling `.withDispatcher()` on it)
|
||||
*/
|
||||
static Props props(Integer magicNumber) {
|
||||
// You need to specify the actual type of the returned actor
|
||||
// since Java 8 lambdas have some runtime type information erased
|
||||
return Props.create(DemoActor.class, () -> new DemoActor(magicNumber));
|
||||
}
|
||||
|
||||
private final Integer magicNumber;
|
||||
|
||||
DemoActor(Integer magicNumber) {
|
||||
this.magicNumber = magicNumber;
|
||||
receive(ReceiveBuilder.
|
||||
match(Integer.class, i -> {
|
||||
sender().tell(i + magicNumber, self());
|
||||
}).build()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//#props-factory
|
||||
static
|
||||
//#props-factory
|
||||
public class SomeOtherActor extends AbstractActor {
|
||||
// Props(new DemoActor(42)) would not be safe
|
||||
ActorRef demoActor = context().actorOf(DemoActor.props(42), "demo");
|
||||
// ...
|
||||
//#props-factory
|
||||
public SomeOtherActor() {
|
||||
receive(emptyBehavior());
|
||||
}
|
||||
//#props-factory
|
||||
}
|
||||
//#props-factory
|
||||
|
||||
public static class Hook extends AbstractActor {
|
||||
ActorRef target = null;
|
||||
public Hook() {
|
||||
receive(emptyBehavior());
|
||||
}
|
||||
//#preStart
|
||||
@Override
|
||||
public void preStart() {
|
||||
target = context().actorOf(Props.create(MyActor.class, "target"));
|
||||
}
|
||||
//#preStart
|
||||
//#postStop
|
||||
@Override
|
||||
public void postStop() {
|
||||
//#clean-up-some-resources
|
||||
final String message = "stopped";
|
||||
//#tell
|
||||
// don’t forget to think about who is the sender (2nd argument)
|
||||
target.tell(message, self());
|
||||
//#tell
|
||||
final Object result = "";
|
||||
//#forward
|
||||
target.forward(result, context());
|
||||
//#forward
|
||||
target = null;
|
||||
//#clean-up-some-resources
|
||||
}
|
||||
//#postStop
|
||||
|
||||
// compilation test only
|
||||
public void compileSelections() {
|
||||
//#selection-local
|
||||
// will look up this absolute path
|
||||
context().actorSelection("/user/serviceA/actor");
|
||||
// will look up sibling beneath same supervisor
|
||||
context().actorSelection("../joe");
|
||||
//#selection-local
|
||||
|
||||
//#selection-wildcard
|
||||
// will look all children to serviceB with names starting with worker
|
||||
context().actorSelection("/user/serviceB/worker*");
|
||||
// will look up all siblings beneath same supervisor
|
||||
context().actorSelection("../*");
|
||||
//#selection-wildcard
|
||||
|
||||
//#selection-remote
|
||||
context().actorSelection("akka.tcp://app@otherhost:1234/user/serviceB");
|
||||
//#selection-remote
|
||||
}
|
||||
}
|
||||
|
||||
public static class ReplyException extends AbstractActor {
|
||||
public ReplyException() {
|
||||
receive(ReceiveBuilder.
|
||||
matchAny(x -> {
|
||||
//#reply-exception
|
||||
try {
|
||||
String result = operation();
|
||||
sender().tell(result, self());
|
||||
} catch (Exception e) {
|
||||
sender().tell(new akka.actor.Status.Failure(e), self());
|
||||
throw e;
|
||||
}
|
||||
//#reply-exception
|
||||
}).build()
|
||||
);
|
||||
}
|
||||
|
||||
private String operation() {
|
||||
return "Hi";
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
//#gracefulStop-actor
|
||||
public class Manager extends AbstractActor {
|
||||
private static enum Shutdown {
|
||||
Shutdown
|
||||
}
|
||||
public static final Shutdown SHUTDOWN = Shutdown.Shutdown;
|
||||
|
||||
private ActorRef worker =
|
||||
context().watch(context().actorOf(Props.create(Cruncher.class), "worker"));
|
||||
|
||||
public Manager() {
|
||||
receive(ReceiveBuilder.
|
||||
matchEquals("job", s -> {
|
||||
worker.tell("crunch", self());
|
||||
}).
|
||||
matchEquals(SHUTDOWN, x -> {
|
||||
worker.tell(PoisonPill.getInstance(), self());
|
||||
context().become(shuttingDown);
|
||||
}).build()
|
||||
);
|
||||
}
|
||||
|
||||
public PartialFunction<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)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// don’t forget to think about who is the sender (2nd argument)
|
||||
target.tell(message, getSelf());
|
||||
//#tell
|
||||
//#clean-up-resources-here
|
||||
}
|
||||
//#postStop
|
||||
}
|
||||
final ActorRef master = system.actorOf(Props.create(Master.class, this, getRef()));
|
||||
expectMsgEquals("");
|
||||
master.tell("", getRef());
|
||||
final ActorRef victim = expectMsgClass(ActorRef.class);
|
||||
//#kill
|
||||
victim.tell(akka.actor.Kill.getInstance(), ActorRef.noSender());
|
||||
//#kill
|
||||
expectMsgEquals("killed");
|
||||
expectMsgEquals("stopped");
|
||||
assert getLastSender().equals(master);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void useBecome() {
|
||||
new JavaTestKit(system) {
|
||||
{
|
||||
ActorRef myActor = system.actorOf(Props.create(HotSwapActor.class));
|
||||
myActor.tell("foo", getRef());
|
||||
myActor.tell("bar", getRef());
|
||||
expectMsgEquals("I am already happy :-)");
|
||||
myActor.tell("bar", getRef());
|
||||
expectMsgEquals("I am already happy :-)");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void useWatch() throws Exception {
|
||||
ActorRef myActor = system.actorOf(Props.create(WatchActor.class));
|
||||
Future<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 actor’s constructor.
|
||||
* @return a Props for creating this actor, which can then be further configured
|
||||
* (e.g. calling `.withDispatcher()` on it)
|
||||
*/
|
||||
public static Props props(final int magicNumber) {
|
||||
return Props.create(new Creator<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
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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/"));
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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("...");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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/"));
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// ...
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue