Merge branch 'master' into wip-multi-dc-merge-master-patriknw
This commit is contained in:
commit
6ed3295acd
393 changed files with 11343 additions and 9108 deletions
|
|
@ -49,6 +49,7 @@ import static akka.pattern.PatternsCS.gracefulStop;
|
|||
import akka.pattern.AskTimeoutException;
|
||||
import scala.concurrent.duration.Duration;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
|
||||
//#import-gracefulStop
|
||||
//#import-terminated
|
||||
import akka.actor.Terminated;
|
||||
|
|
|
|||
46
akka-docs/src/test/java/jdocs/actor/TimerDocTest.java
Normal file
46
akka-docs/src/test/java/jdocs/actor/TimerDocTest.java
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* Copyright (C) 2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
|
||||
package jdocs.actor;
|
||||
|
||||
//#timers
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import scala.concurrent.duration.Duration;
|
||||
import akka.actor.AbstractActorWithTimers;
|
||||
|
||||
//#timers
|
||||
|
||||
public class TimerDocTest {
|
||||
|
||||
static
|
||||
//#timers
|
||||
public class MyActor extends AbstractActorWithTimers {
|
||||
|
||||
private static Object TICK_KEY = "TickKey";
|
||||
private static final class FirstTick {
|
||||
}
|
||||
private static final class Tick {
|
||||
}
|
||||
|
||||
public MyActor() {
|
||||
getTimers().startSingleTimer(TICK_KEY, new FirstTick(),
|
||||
Duration.create(500, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return receiveBuilder()
|
||||
.match(FirstTick.class, message -> {
|
||||
// do something useful here
|
||||
getTimers().startPeriodicTimer(TICK_KEY, new Tick(),
|
||||
Duration.create(1, TimeUnit.SECONDS));
|
||||
})
|
||||
.match(Tick.class, message -> {
|
||||
// do something useful here
|
||||
})
|
||||
.build();
|
||||
}
|
||||
}
|
||||
//#timers
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@ package jdocs.cluster;
|
|||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import akka.actor.Props;
|
||||
|
|
@ -77,12 +79,12 @@ abstract class FactorialFrontend2 extends AbstractActor {
|
|||
int totalInstances = 100;
|
||||
Iterable<String> routeesPaths = Arrays.asList("/user/factorialBackend", "");
|
||||
boolean allowLocalRoutees = true;
|
||||
String useRole = "backend";
|
||||
Set<String> useRoles = new HashSet<>(Arrays.asList("backend"));
|
||||
ActorRef backend = getContext().actorOf(
|
||||
new ClusterRouterGroup(new AdaptiveLoadBalancingGroup(
|
||||
HeapMetricsSelector.getInstance(), Collections.<String> emptyList()),
|
||||
new ClusterRouterGroupSettings(totalInstances, routeesPaths,
|
||||
allowLocalRoutees, useRole)).props(), "factorialBackendRouter2");
|
||||
allowLocalRoutees, useRoles)).props(), "factorialBackendRouter2");
|
||||
|
||||
//#router-lookup-in-code
|
||||
}
|
||||
|
|
@ -93,12 +95,12 @@ abstract class FactorialFrontend3 extends AbstractActor {
|
|||
int totalInstances = 100;
|
||||
int maxInstancesPerNode = 3;
|
||||
boolean allowLocalRoutees = false;
|
||||
String useRole = "backend";
|
||||
Set<String> useRoles = new HashSet<>(Arrays.asList("backend"));
|
||||
ActorRef backend = getContext().actorOf(
|
||||
new ClusterRouterPool(new AdaptiveLoadBalancingPool(
|
||||
SystemLoadAverageMetricsSelector.getInstance(), 0),
|
||||
new ClusterRouterPoolSettings(totalInstances, maxInstancesPerNode,
|
||||
allowLocalRoutees, useRole)).props(Props
|
||||
allowLocalRoutees, useRoles)).props(Props
|
||||
.create(FactorialBackend.class)), "factorialBackendRouter3");
|
||||
//#router-deploy-in-code
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,10 @@ import akka.actor.AbstractActor;
|
|||
import akka.routing.ConsistentHashingRouter.ConsistentHashableEnvelope;
|
||||
import akka.routing.FromConfig;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
//#service
|
||||
public class StatsService extends AbstractActor {
|
||||
|
|
@ -55,11 +58,11 @@ abstract class StatsService2 extends AbstractActor {
|
|||
Iterable<String> routeesPaths = Collections
|
||||
.singletonList("/user/statsWorker");
|
||||
boolean allowLocalRoutees = true;
|
||||
String useRole = "compute";
|
||||
Set<String> useRoles = new HashSet<>(Arrays.asList("compute"));
|
||||
ActorRef workerRouter = getContext().actorOf(
|
||||
new ClusterRouterGroup(new ConsistentHashingGroup(routeesPaths),
|
||||
new ClusterRouterGroupSettings(totalInstances, routeesPaths,
|
||||
allowLocalRoutees, useRole)).props(), "workerRouter2");
|
||||
allowLocalRoutees, useRoles)).props(), "workerRouter2");
|
||||
//#router-lookup-in-code
|
||||
}
|
||||
|
||||
|
|
@ -69,11 +72,11 @@ abstract class StatsService3 extends AbstractActor {
|
|||
int totalInstances = 100;
|
||||
int maxInstancesPerNode = 3;
|
||||
boolean allowLocalRoutees = false;
|
||||
String useRole = "compute";
|
||||
Set<String> useRoles = new HashSet<>(Arrays.asList("compute"));
|
||||
ActorRef workerRouter = getContext().actorOf(
|
||||
new ClusterRouterPool(new ConsistentHashingPool(0),
|
||||
new ClusterRouterPoolSettings(totalInstances, maxInstancesPerNode,
|
||||
allowLocalRoutees, useRole)).props(Props
|
||||
allowLocalRoutees, useRoles)).props(Props
|
||||
.create(StatsWorker.class)), "workerRouter3");
|
||||
//#router-deploy-in-code
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
package jdocs.dispatcher;
|
||||
|
||||
//#mailbox-implementation-example
|
||||
//#mailbox-marker-interface
|
||||
// Marker interface used for mailbox requirements mapping
|
||||
public interface MyUnboundedMessageQueueSemantics {
|
||||
}
|
||||
//#mailbox-implementation-example
|
||||
//#mailbox-marker-interface
|
||||
|
|
|
|||
17
akka-docs/src/test/java/jdocs/future/ActorWithFuture.java
Normal file
17
akka-docs/src/test/java/jdocs/future/ActorWithFuture.java
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package jdocs.future;
|
||||
|
||||
//#context-dispatcher
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.dispatch.Futures;
|
||||
|
||||
public class ActorWithFuture extends AbstractActor {
|
||||
ActorWithFuture(){
|
||||
Futures.future(() -> "hello", getContext().dispatcher());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return AbstractActor.emptyBehavior();
|
||||
}
|
||||
}
|
||||
// #context-dispatcher
|
||||
|
|
@ -11,6 +11,8 @@ import scala.concurrent.Future;
|
|||
import scala.concurrent.Await;
|
||||
import scala.concurrent.Promise;
|
||||
import akka.util.Timeout;
|
||||
|
||||
|
||||
//#imports1
|
||||
|
||||
//#imports2
|
||||
|
|
@ -19,27 +21,39 @@ import akka.japi.Function;
|
|||
import java.util.concurrent.Callable;
|
||||
import static akka.dispatch.Futures.future;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
|
||||
//#imports2
|
||||
|
||||
//#imports3
|
||||
import static akka.dispatch.Futures.sequence;
|
||||
|
||||
|
||||
//#imports3
|
||||
|
||||
//#imports4
|
||||
import static akka.dispatch.Futures.traverse;
|
||||
|
||||
|
||||
//#imports4
|
||||
|
||||
//#imports5
|
||||
import akka.japi.Function2;
|
||||
import static akka.dispatch.Futures.fold;
|
||||
|
||||
|
||||
//#imports5
|
||||
|
||||
//#imports6
|
||||
import static akka.dispatch.Futures.reduce;
|
||||
|
||||
|
||||
//#imports6
|
||||
|
||||
//#imports7
|
||||
import static akka.pattern.Patterns.after;
|
||||
|
||||
|
||||
import java.util.Arrays;
|
||||
//#imports7
|
||||
|
||||
|
|
|
|||
|
|
@ -1,173 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
|
||||
package jdocs.pattern;
|
||||
|
||||
import akka.actor.*;
|
||||
import akka.testkit.*;
|
||||
import akka.testkit.TestEvent.Mute;
|
||||
import akka.testkit.TestEvent.UnMute;
|
||||
import jdocs.AbstractJavaTest;
|
||||
import akka.testkit.javadsl.TestKit;
|
||||
import org.junit.*;
|
||||
import scala.concurrent.duration.Duration;
|
||||
import scala.concurrent.duration.FiniteDuration;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class SchedulerPatternTest extends AbstractJavaTest {
|
||||
|
||||
@ClassRule
|
||||
public static AkkaJUnitActorSystemResource actorSystemResource =
|
||||
new AkkaJUnitActorSystemResource("SchedulerPatternTest", AkkaSpec.testConf());
|
||||
|
||||
private final ActorSystem system = actorSystemResource.getSystem();
|
||||
|
||||
static
|
||||
//#schedule-constructor
|
||||
public class ScheduleInConstructor extends AbstractActor {
|
||||
|
||||
private final Cancellable tick = getContext().getSystem().scheduler().schedule(
|
||||
Duration.create(500, TimeUnit.MILLISECONDS),
|
||||
Duration.create(1, TimeUnit.SECONDS),
|
||||
getSelf(), "tick", getContext().dispatcher(), null);
|
||||
//#schedule-constructor
|
||||
// this variable and constructor is declared here to not show up in the docs
|
||||
final ActorRef target;
|
||||
public ScheduleInConstructor(ActorRef target) {
|
||||
this.target = target;
|
||||
}
|
||||
//#schedule-constructor
|
||||
|
||||
@Override
|
||||
public void postStop() {
|
||||
tick.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return receiveBuilder()
|
||||
.matchEquals("tick", message -> {
|
||||
// do something useful here
|
||||
//#schedule-constructor
|
||||
target.tell(message, getSelf());
|
||||
//#schedule-constructor
|
||||
})
|
||||
.matchEquals("restart", message -> {
|
||||
throw new ArithmeticException();
|
||||
})
|
||||
.build();
|
||||
}
|
||||
}
|
||||
//#schedule-constructor
|
||||
|
||||
static
|
||||
//#schedule-receive
|
||||
public class ScheduleInReceive extends AbstractActor {
|
||||
//#schedule-receive
|
||||
// this variable and constructor is declared here to not show up in the docs
|
||||
final ActorRef target;
|
||||
public ScheduleInReceive(ActorRef target) {
|
||||
this.target = target;
|
||||
}
|
||||
//#schedule-receive
|
||||
|
||||
@Override
|
||||
public void preStart() {
|
||||
getContext().getSystem().scheduler().scheduleOnce(
|
||||
Duration.create(500, TimeUnit.MILLISECONDS),
|
||||
getSelf(), "tick", getContext().dispatcher(), null);
|
||||
}
|
||||
|
||||
// override postRestart so we don't call preStart and schedule a new message
|
||||
@Override
|
||||
public void postRestart(Throwable reason) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return receiveBuilder()
|
||||
.matchEquals("tick", message -> {
|
||||
// send another periodic tick after the specified delay
|
||||
getContext().getSystem().scheduler().scheduleOnce(
|
||||
Duration.create(1, TimeUnit.SECONDS),
|
||||
getSelf(), "tick", getContext().dispatcher(), null);
|
||||
// do something useful here
|
||||
//#schedule-receive
|
||||
target.tell(message, getSelf());
|
||||
//#schedule-receive
|
||||
})
|
||||
.matchEquals("restart", message -> {
|
||||
throw new ArithmeticException();
|
||||
})
|
||||
.build();
|
||||
}
|
||||
}
|
||||
//#schedule-receive
|
||||
|
||||
@Test
|
||||
@Ignore // no way to tag this as timing sensitive
|
||||
public void scheduleInConstructor() {
|
||||
new TestSchedule(system) {{
|
||||
final TestKit probe = new TestKit(system);
|
||||
final Props props = Props.create(ScheduleInConstructor.class, probe.getRef());
|
||||
testSchedule(probe, props, duration("3000 millis"), duration("2000 millis"));
|
||||
}};
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore // no way to tag this as timing sensitive
|
||||
public void scheduleInReceive() {
|
||||
new TestSchedule(system) {{
|
||||
final TestKit probe = new TestKit(system);
|
||||
final Props props = Props.create(ScheduleInReceive.class, probe.getRef());
|
||||
testSchedule(probe, props, duration("3000 millis"), duration("2500 millis"));
|
||||
}};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doNothing() {
|
||||
// actorSystemResource.after is not called when all tests are ignored
|
||||
}
|
||||
|
||||
public static class TestSchedule extends TestKit {
|
||||
private ActorSystem system;
|
||||
|
||||
public TestSchedule(ActorSystem system) {
|
||||
super(system);
|
||||
this.system = system;
|
||||
}
|
||||
|
||||
public void testSchedule(final TestKit probe, Props props,
|
||||
FiniteDuration startDuration,
|
||||
FiniteDuration afterRestartDuration) {
|
||||
Iterable<akka.testkit.EventFilter> filter =
|
||||
Arrays.asList(new akka.testkit.EventFilter[]{
|
||||
(akka.testkit.EventFilter) new ErrorFilter(ArithmeticException.class)});
|
||||
try {
|
||||
system.eventStream().publish(new Mute(filter));
|
||||
|
||||
final ActorRef actor = system.actorOf(props);
|
||||
within(startDuration, () -> {
|
||||
probe.expectMsgEquals("tick");
|
||||
probe.expectMsgEquals("tick");
|
||||
probe.expectMsgEquals("tick");
|
||||
return null;
|
||||
});
|
||||
|
||||
actor.tell("restart", getRef());
|
||||
within(afterRestartDuration, () -> {
|
||||
probe.expectMsgEquals("tick");
|
||||
probe.expectMsgEquals("tick");
|
||||
return null;
|
||||
});
|
||||
|
||||
system.stop(actor);
|
||||
}
|
||||
finally {
|
||||
system.eventStream().publish(new UnMute(filter));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,8 @@ package jdocs.persistence;
|
|||
|
||||
import docs.persistence.ExampleJsonMarshaller;
|
||||
import docs.persistence.proto.FlightAppModels;
|
||||
|
||||
import java.io.NotSerializableException;
|
||||
import java.nio.charset.Charset;
|
||||
import spray.json.JsObject;
|
||||
|
||||
|
|
@ -72,7 +74,7 @@ public class PersistenceSchemaEvolutionDocTest {
|
|||
return o.getClass().getName();
|
||||
}
|
||||
|
||||
@Override public Object fromBinary(byte[] bytes, String manifest) {
|
||||
@Override public Object fromBinary(byte[] bytes, String manifest) throws NotSerializableException{
|
||||
if (seatReservedManifest.equals(manifest)) {
|
||||
// use generated protobuf serializer
|
||||
try {
|
||||
|
|
@ -81,7 +83,7 @@ public class PersistenceSchemaEvolutionDocTest {
|
|||
throw new IllegalArgumentException(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unable to handle manifest: " + manifest);
|
||||
throw new NotSerializableException("Unable to handle manifest: " + manifest);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -208,13 +210,13 @@ public class PersistenceSchemaEvolutionDocTest {
|
|||
}
|
||||
|
||||
// deserialize the object, using the manifest to indicate which logic to apply
|
||||
@Override public Object fromBinary(byte[] bytes, String manifest) {
|
||||
@Override public Object fromBinary(byte[] bytes, String manifest) throws NotSerializableException {
|
||||
if (personManifest.equals(manifest)) {
|
||||
String nameAndSurname = new String(bytes, utf8);
|
||||
String[] parts = nameAndSurname.split("[|]");
|
||||
return new Person(parts[0], parts[1]);
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
throw new NotSerializableException(
|
||||
"Unable to deserialize from bytes, manifest was: " + manifest +
|
||||
"! Bytes length: " + bytes.length);
|
||||
}
|
||||
|
|
@ -412,12 +414,12 @@ public class PersistenceSchemaEvolutionDocTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Override public Object fromBinary(byte[] bytes, String manifest) {
|
||||
@Override public Object fromBinary(byte[] bytes, String manifest) throws NotSerializableException {
|
||||
if (oldPayloadClassName.equals(manifest))
|
||||
return new SamplePayload(new String(bytes, utf8));
|
||||
else if (myPayloadClassName.equals(manifest))
|
||||
return new SamplePayload(new String(bytes, utf8));
|
||||
else throw new IllegalArgumentException("unexpected manifest [" + manifest + "]");
|
||||
else throw new NotSerializableException("unexpected manifest [" + manifest + "]");
|
||||
}
|
||||
}
|
||||
//#string-serializer-handle-rename
|
||||
|
|
|
|||
276
akka-docs/src/test/java/jdocs/sharding/ClusterShardingTest.java
Normal file
276
akka-docs/src/test/java/jdocs/sharding/ClusterShardingTest.java
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
|
||||
package jdocs.sharding;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import java.util.Optional;
|
||||
import scala.concurrent.duration.Duration;
|
||||
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.actor.ActorInitializationException;
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.actor.OneForOneStrategy;
|
||||
import akka.actor.PoisonPill;
|
||||
import akka.actor.Props;
|
||||
import akka.actor.SupervisorStrategy;
|
||||
import akka.actor.Terminated;
|
||||
import akka.actor.ReceiveTimeout;
|
||||
//#counter-extractor
|
||||
import akka.cluster.sharding.ShardRegion;
|
||||
|
||||
//#counter-extractor
|
||||
|
||||
//#counter-start
|
||||
import akka.japi.Option;
|
||||
import akka.cluster.sharding.ClusterSharding;
|
||||
import akka.cluster.sharding.ClusterShardingSettings;
|
||||
|
||||
//#counter-start
|
||||
import akka.persistence.AbstractPersistentActor;
|
||||
import akka.cluster.Cluster;
|
||||
import akka.japi.pf.DeciderBuilder;
|
||||
|
||||
// Doc code, compile only
|
||||
public class ClusterShardingTest {
|
||||
|
||||
ActorSystem system = null;
|
||||
|
||||
ActorRef getSelf() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void demonstrateUsage() {
|
||||
//#counter-extractor
|
||||
ShardRegion.MessageExtractor messageExtractor = new ShardRegion.MessageExtractor() {
|
||||
|
||||
@Override
|
||||
public String entityId(Object message) {
|
||||
if (message instanceof Counter.EntityEnvelope)
|
||||
return String.valueOf(((Counter.EntityEnvelope) message).id);
|
||||
else if (message instanceof Counter.Get)
|
||||
return String.valueOf(((Counter.Get) message).counterId);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object entityMessage(Object message) {
|
||||
if (message instanceof Counter.EntityEnvelope)
|
||||
return ((Counter.EntityEnvelope) message).payload;
|
||||
else
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String shardId(Object message) {
|
||||
int numberOfShards = 100;
|
||||
if (message instanceof Counter.EntityEnvelope) {
|
||||
long id = ((Counter.EntityEnvelope) message).id;
|
||||
return String.valueOf(id % numberOfShards);
|
||||
} else if (message instanceof Counter.Get) {
|
||||
long id = ((Counter.Get) message).counterId;
|
||||
return String.valueOf(id % numberOfShards);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
//#counter-extractor
|
||||
|
||||
//#counter-start
|
||||
Option<String> roleOption = Option.none();
|
||||
ClusterShardingSettings settings = ClusterShardingSettings.create(system);
|
||||
ActorRef startedCounterRegion = ClusterSharding.get(system).start("Counter",
|
||||
Props.create(Counter.class), settings, messageExtractor);
|
||||
//#counter-start
|
||||
|
||||
//#counter-usage
|
||||
ActorRef counterRegion = ClusterSharding.get(system).shardRegion("Counter");
|
||||
counterRegion.tell(new Counter.Get(123), getSelf());
|
||||
|
||||
counterRegion.tell(new Counter.EntityEnvelope(123,
|
||||
Counter.CounterOp.INCREMENT), getSelf());
|
||||
counterRegion.tell(new Counter.Get(123), getSelf());
|
||||
//#counter-usage
|
||||
|
||||
//#counter-supervisor-start
|
||||
ClusterSharding.get(system).start("SupervisedCounter",
|
||||
Props.create(CounterSupervisor.class), settings, messageExtractor);
|
||||
//#counter-supervisor-start
|
||||
|
||||
//#proxy-dc
|
||||
ActorRef counterProxyDcB =
|
||||
ClusterSharding.get(system).startProxy(
|
||||
"Counter",
|
||||
Optional.empty(),
|
||||
Optional.of("B"), // data center name
|
||||
messageExtractor);
|
||||
//#proxy-dc
|
||||
}
|
||||
|
||||
public void demonstrateUsage2() {
|
||||
ShardRegion.MessageExtractor messageExtractor = new ShardRegion.MessageExtractor() {
|
||||
|
||||
@Override
|
||||
public String entityId(Object message) {
|
||||
if (message instanceof Counter.EntityEnvelope)
|
||||
return String.valueOf(((Counter.EntityEnvelope) message).id);
|
||||
else if (message instanceof Counter.Get)
|
||||
return String.valueOf(((Counter.Get) message).counterId);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object entityMessage(Object message) {
|
||||
if (message instanceof Counter.EntityEnvelope)
|
||||
return ((Counter.EntityEnvelope) message).payload;
|
||||
else
|
||||
return message;
|
||||
}
|
||||
|
||||
//#extractShardId-StartEntity
|
||||
@Override
|
||||
public String shardId(Object message) {
|
||||
int numberOfShards = 100;
|
||||
if (message instanceof Counter.EntityEnvelope) {
|
||||
long id = ((Counter.EntityEnvelope) message).id;
|
||||
return String.valueOf(id % numberOfShards);
|
||||
} else if (message instanceof Counter.Get) {
|
||||
long id = ((Counter.Get) message).counterId;
|
||||
return String.valueOf(id % numberOfShards);
|
||||
} else if (message instanceof ShardRegion.StartEntity) {
|
||||
long id = Long.valueOf(((ShardRegion.StartEntity) message).entityId());
|
||||
return String.valueOf(id % numberOfShards);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//#extractShardId-StartEntity
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
static//#counter-actor
|
||||
public class Counter extends AbstractPersistentActor {
|
||||
|
||||
public static enum CounterOp {
|
||||
INCREMENT, DECREMENT
|
||||
}
|
||||
|
||||
public static class Get {
|
||||
final public long counterId;
|
||||
|
||||
public Get(long counterId) {
|
||||
this.counterId = counterId;
|
||||
}
|
||||
}
|
||||
|
||||
public static class EntityEnvelope {
|
||||
final public long id;
|
||||
final public Object payload;
|
||||
|
||||
public EntityEnvelope(long id, Object payload) {
|
||||
this.id = id;
|
||||
this.payload = payload;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CounterChanged {
|
||||
final public int delta;
|
||||
|
||||
public CounterChanged(int delta) {
|
||||
this.delta = delta;
|
||||
}
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
// getSelf().path().name() is the entity identifier (utf-8 URL-encoded)
|
||||
@Override
|
||||
public String persistenceId() {
|
||||
return "Counter-" + getSelf().path().name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preStart() throws Exception {
|
||||
super.preStart();
|
||||
getContext().setReceiveTimeout(Duration.create(120, SECONDS));
|
||||
}
|
||||
|
||||
void updateState(CounterChanged event) {
|
||||
count += event.delta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Receive createReceiveRecover() {
|
||||
return receiveBuilder()
|
||||
.match(CounterChanged.class, this::updateState)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
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().getParent().tell(
|
||||
new ShardRegion.Passivate(PoisonPill.getInstance()), getSelf());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//#counter-actor
|
||||
|
||||
static//#supervisor
|
||||
public class CounterSupervisor extends AbstractActor {
|
||||
|
||||
private final ActorRef counter = getContext().actorOf(
|
||||
Props.create(Counter.class), "theCounter");
|
||||
|
||||
private static final SupervisorStrategy strategy =
|
||||
new OneForOneStrategy(DeciderBuilder.
|
||||
match(IllegalArgumentException.class, e -> SupervisorStrategy.resume()).
|
||||
match(ActorInitializationException.class, e -> SupervisorStrategy.stop()).
|
||||
match(Exception.class, e -> SupervisorStrategy.restart()).
|
||||
matchAny(o -> SupervisorStrategy.escalate()).build());
|
||||
|
||||
@Override
|
||||
public SupervisorStrategy supervisorStrategy() {
|
||||
return strategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return receiveBuilder()
|
||||
.match(Object.class, msg -> counter.forward(msg, getContext()))
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
//#supervisor
|
||||
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ import java.util.concurrent.ExecutionException;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import akka.NotUsed;
|
||||
import akka.japi.pf.PFBuilder;
|
||||
import jdocs.AbstractJavaTest;
|
||||
import akka.testkit.javadsl.TestKit;
|
||||
import org.junit.AfterClass;
|
||||
|
|
@ -138,4 +139,64 @@ public class FlowErrorDocTest extends AbstractJavaTest {
|
|||
result.toCompletableFuture().get(3, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demonstrateRecover() {
|
||||
//#recover
|
||||
final Materializer mat = ActorMaterializer.create(system);
|
||||
Source.from(Arrays.asList(0, 1, 2, 3, 4, 5, 6)).map(n -> {
|
||||
if (n < 5) return n.toString();
|
||||
else throw new RuntimeException("Boom!");
|
||||
}).recover(new PFBuilder()
|
||||
.match(RuntimeException.class, ex -> "stream truncated")
|
||||
.build()
|
||||
).runForeach(System.out::println, mat);
|
||||
//#recover
|
||||
|
||||
/*
|
||||
Output:
|
||||
//#recover-output
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
stream truncated
|
||||
//#recover-output
|
||||
*/
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demonstrateRecoverWithRetries() {
|
||||
//#recoverWithRetries
|
||||
final Materializer mat = ActorMaterializer.create(system);
|
||||
Source<String, NotUsed> planB = Source.from(Arrays.asList("five", "six", "seven", "eight"));
|
||||
|
||||
Source.from(Arrays.asList(0, 1, 2, 3, 4, 5, 6)).map(n -> {
|
||||
if (n < 5) return n.toString();
|
||||
else throw new RuntimeException("Boom!");
|
||||
}).recoverWithRetries(
|
||||
1, // max attempts
|
||||
new PFBuilder()
|
||||
.match(RuntimeException.class, ex -> planB)
|
||||
.build()
|
||||
).runForeach(System.out::println, mat);
|
||||
//#recoverWithRetries
|
||||
|
||||
/*
|
||||
Output:
|
||||
//#recoverWithRetries-output
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
five
|
||||
six
|
||||
seven
|
||||
eight
|
||||
//#recoverWithRetries-output
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,13 @@ import akka.stream.Attributes;
|
|||
import akka.stream.Materializer;
|
||||
import akka.stream.Outlet;
|
||||
import akka.stream.SourceShape;
|
||||
import akka.stream.stage.*;
|
||||
//#stage-with-logging
|
||||
import akka.stream.stage.AbstractOutHandler;
|
||||
import akka.stream.stage.GraphStage;
|
||||
import akka.stream.stage.GraphStageLogic;
|
||||
import akka.stream.stage.GraphStageLogicWithLogging;
|
||||
|
||||
//#stage-with-logging
|
||||
import jdocs.AbstractJavaTest;
|
||||
import org.junit.Test;
|
||||
|
||||
|
|
@ -21,7 +27,7 @@ public class GraphStageLoggingDocTest extends AbstractJavaTest {
|
|||
|
||||
@Test
|
||||
public void compileOnlyTestClass() throws Exception { }
|
||||
|
||||
|
||||
//#stage-with-logging
|
||||
public class RandomLettersSource extends GraphStage<SourceShape<String>> {
|
||||
public final Outlet<String> out = Outlet.create("RandomLettersSource.in");
|
||||
|
|
|
|||
|
|
@ -11,17 +11,25 @@ import akka.japi.Pair;
|
|||
import akka.stream.ActorMaterializer;
|
||||
import akka.stream.KillSwitches;
|
||||
import akka.stream.Materializer;
|
||||
import akka.stream.ThrottleMode;
|
||||
import akka.stream.UniqueKillSwitch;
|
||||
import akka.stream.javadsl.*;
|
||||
import akka.stream.javadsl.PartitionHub.ConsumerInfo;
|
||||
|
||||
import jdocs.AbstractJavaTest;
|
||||
import akka.testkit.javadsl.TestKit;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import scala.concurrent.duration.Duration;
|
||||
import scala.concurrent.duration.FiniteDuration;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.ToLongBiFunction;
|
||||
|
||||
public class HubDocTest extends AbstractJavaTest {
|
||||
|
||||
|
|
@ -137,4 +145,136 @@ public class HubDocTest extends AbstractJavaTest {
|
|||
killSwitch.shutdown();
|
||||
//#pub-sub-4
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dynamicPartition() {
|
||||
// Used to be able to clean up the running stream
|
||||
ActorMaterializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
//#partition-hub
|
||||
// A simple producer that publishes a new "message-n" every second
|
||||
Source<String, Cancellable> producer = Source.tick(
|
||||
FiniteDuration.create(1, TimeUnit.SECONDS),
|
||||
FiniteDuration.create(1, TimeUnit.SECONDS),
|
||||
"message"
|
||||
).zipWith(Source.range(0, 100), (a, b) -> a + "-" + b);
|
||||
|
||||
// Attach a PartitionHub Sink to the producer. This will materialize to a
|
||||
// corresponding Source.
|
||||
// (We need to use toMat and Keep.right since by default the materialized
|
||||
// value to the left is used)
|
||||
RunnableGraph<Source<String, NotUsed>> runnableGraph =
|
||||
producer.toMat(PartitionHub.of(
|
||||
String.class,
|
||||
(size, elem) -> Math.abs(elem.hashCode()) % size,
|
||||
2, 256), Keep.right());
|
||||
|
||||
// By running/materializing the producer, we get back a Source, which
|
||||
// gives us access to the elements published by the producer.
|
||||
Source<String, NotUsed> fromProducer = runnableGraph.run(materializer);
|
||||
|
||||
// Print out messages from the producer in two independent consumers
|
||||
fromProducer.runForeach(msg -> System.out.println("consumer1: " + msg), materializer);
|
||||
fromProducer.runForeach(msg -> System.out.println("consumer2: " + msg), materializer);
|
||||
//#partition-hub
|
||||
|
||||
// Cleanup
|
||||
materializer.shutdown();
|
||||
}
|
||||
|
||||
//#partition-hub-stateful-function
|
||||
// Using a class since variable must otherwise be final.
|
||||
// New instance is created for each materialization of the PartitionHub.
|
||||
static class RoundRobin<T> implements ToLongBiFunction<ConsumerInfo, T> {
|
||||
|
||||
private long i = -1;
|
||||
|
||||
@Override
|
||||
public long applyAsLong(ConsumerInfo info, T elem) {
|
||||
i++;
|
||||
return info.consumerIdByIdx((int) (i % info.size()));
|
||||
}
|
||||
}
|
||||
//#partition-hub-stateful-function
|
||||
|
||||
@Test
|
||||
public void dynamicStatefulPartition() {
|
||||
// Used to be able to clean up the running stream
|
||||
ActorMaterializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
//#partition-hub-stateful
|
||||
// A simple producer that publishes a new "message-n" every second
|
||||
Source<String, Cancellable> producer = Source.tick(
|
||||
FiniteDuration.create(1, TimeUnit.SECONDS),
|
||||
FiniteDuration.create(1, TimeUnit.SECONDS),
|
||||
"message"
|
||||
).zipWith(Source.range(0, 100), (a, b) -> a + "-" + b);
|
||||
|
||||
// Attach a PartitionHub Sink to the producer. This will materialize to a
|
||||
// corresponding Source.
|
||||
// (We need to use toMat and Keep.right since by default the materialized
|
||||
// value to the left is used)
|
||||
RunnableGraph<Source<String, NotUsed>> runnableGraph =
|
||||
producer.toMat(
|
||||
PartitionHub.ofStateful(
|
||||
String.class,
|
||||
() -> new RoundRobin<String>(),
|
||||
2,
|
||||
256),
|
||||
Keep.right());
|
||||
|
||||
// By running/materializing the producer, we get back a Source, which
|
||||
// gives us access to the elements published by the producer.
|
||||
Source<String, NotUsed> fromProducer = runnableGraph.run(materializer);
|
||||
|
||||
// Print out messages from the producer in two independent consumers
|
||||
fromProducer.runForeach(msg -> System.out.println("consumer1: " + msg), materializer);
|
||||
fromProducer.runForeach(msg -> System.out.println("consumer2: " + msg), materializer);
|
||||
//#partition-hub-stateful
|
||||
|
||||
// Cleanup
|
||||
materializer.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dynamicFastestPartition() {
|
||||
// Used to be able to clean up the running stream
|
||||
ActorMaterializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
//#partition-hub-fastest
|
||||
Source<Integer, NotUsed> producer = Source.range(0, 100);
|
||||
|
||||
// ConsumerInfo.queueSize is the approximate number of buffered elements for a consumer.
|
||||
// Note that this is a moving target since the elements are consumed concurrently.
|
||||
RunnableGraph<Source<Integer, NotUsed>> runnableGraph =
|
||||
producer.toMat(
|
||||
PartitionHub.ofStateful(
|
||||
Integer.class,
|
||||
() -> (info, elem) -> {
|
||||
final List<Object> ids = info.getConsumerIds();
|
||||
int minValue = info.queueSize(0);
|
||||
long fastest = info.consumerIdByIdx(0);
|
||||
for (int i = 1; i < ids.size(); i++) {
|
||||
int value = info.queueSize(i);
|
||||
if (value < minValue) {
|
||||
minValue = value;
|
||||
fastest = info.consumerIdByIdx(i);
|
||||
}
|
||||
}
|
||||
return fastest;
|
||||
},
|
||||
2,
|
||||
8),
|
||||
Keep.right());
|
||||
|
||||
Source<Integer, NotUsed> fromProducer = runnableGraph.run(materializer);
|
||||
|
||||
fromProducer.runForeach(msg -> System.out.println("consumer1: " + msg), materializer);
|
||||
fromProducer.throttle(10, Duration.create(100, TimeUnit.MILLISECONDS), 10, ThrottleMode.shaping())
|
||||
.runForeach(msg -> System.out.println("consumer2: " + msg), materializer);
|
||||
//#partition-hub-fastest
|
||||
|
||||
// Cleanup
|
||||
materializer.shutdown();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
86
akka-docs/src/test/java/jdocs/stream/RestartDocTest.java
Normal file
86
akka-docs/src/test/java/jdocs/stream/RestartDocTest.java
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/**
|
||||
* Copyright (C) 2015-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
package jdocs.stream;
|
||||
|
||||
import akka.NotUsed;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.stream.KillSwitch;
|
||||
import akka.stream.KillSwitches;
|
||||
import akka.stream.Materializer;
|
||||
import akka.stream.javadsl.*;
|
||||
import scala.concurrent.duration.Duration;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class RestartDocTest {
|
||||
|
||||
static ActorSystem system;
|
||||
static Materializer materializer;
|
||||
|
||||
// Mocking akka-http
|
||||
public static class Http {
|
||||
public static Http get(ActorSystem system) {
|
||||
return new Http();
|
||||
}
|
||||
public CompletionStage<Http> singleRequest(String uri) {
|
||||
return new CompletableFuture<>();
|
||||
}
|
||||
public NotUsed entity() {
|
||||
return NotUsed.getInstance();
|
||||
}
|
||||
}
|
||||
public static class HttpRequest {
|
||||
public static String create(String uri) {
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
public static class ServerSentEvent {}
|
||||
public static class EventStreamUnmarshalling {
|
||||
public static EventStreamUnmarshalling fromEventStream() {
|
||||
return new EventStreamUnmarshalling();
|
||||
}
|
||||
public CompletionStage<Source<ServerSentEvent, NotUsed>> unmarshall(Http http, Materializer mat) {
|
||||
return new CompletableFuture<>();
|
||||
}
|
||||
}
|
||||
public void doSomethingElse() {
|
||||
|
||||
}
|
||||
|
||||
public void recoverWithBackoffSource() {
|
||||
//#restart-with-backoff-source
|
||||
Source<ServerSentEvent, NotUsed> eventStream = RestartSource.withBackoff(
|
||||
Duration.apply(3, TimeUnit.SECONDS), // min backoff
|
||||
Duration.apply(30, TimeUnit.SECONDS), // max backoff
|
||||
0.2, // adds 20% "noise" to vary the intervals slightly
|
||||
|
||||
() ->
|
||||
// Create a source from a future of a source
|
||||
Source.fromSourceCompletionStage(
|
||||
// Issue a GET request on the event stream
|
||||
Http.get(system).singleRequest(HttpRequest.create("http://example.com/eventstream"))
|
||||
.thenCompose(response ->
|
||||
// Unmarshall it to a stream of ServerSentEvents
|
||||
EventStreamUnmarshalling.fromEventStream()
|
||||
.unmarshall(response, materializer)
|
||||
)
|
||||
)
|
||||
);
|
||||
//#restart-with-backoff-source
|
||||
|
||||
//#with-kill-switch
|
||||
KillSwitch killSwitch = eventStream
|
||||
.viaMat(KillSwitches.single(), Keep.right())
|
||||
.toMat(Sink.foreach(event -> System.out.println("Got event: " + event)), Keep.left())
|
||||
.run(materializer);
|
||||
|
||||
doSomethingElse();
|
||||
|
||||
killSwitch.shutdown();
|
||||
//#with-kill-switch
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -64,7 +64,7 @@ public class StreamTcpDocTest extends AbstractJavaTest {
|
|||
//#echo-server-simple-bind
|
||||
// IncomingConnection and ServerBinding imported from Tcp
|
||||
final Source<IncomingConnection, CompletionStage<ServerBinding>> connections =
|
||||
Tcp.get(system).bind("127.0.0.1", 8889);
|
||||
Tcp.get(system).bind("127.0.0.1", 8888);
|
||||
//#echo-server-simple-bind
|
||||
}
|
||||
{
|
||||
|
|
@ -133,7 +133,7 @@ public class StreamTcpDocTest extends AbstractJavaTest {
|
|||
{
|
||||
//#repl-client
|
||||
final Flow<ByteString, ByteString, CompletionStage<OutgoingConnection>> connection =
|
||||
Tcp.get(system).outgoingConnection("127.0.0.1", 8889);
|
||||
Tcp.get(system).outgoingConnection("127.0.0.1", 8888);
|
||||
//#repl-client
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
package jdocs.tutorial_1;
|
||||
//#print-refs
|
||||
package com.lightbend.akka.sample;
|
||||
|
||||
//#print-refs
|
||||
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.actor.AbstractActor.Receive;
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.actor.Props;
|
||||
import akka.testkit.javadsl.TestKit;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
|
|
@ -12,6 +10,12 @@ import org.junit.Test;
|
|||
import org.scalatest.junit.JUnitSuite;
|
||||
|
||||
//#print-refs
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.actor.AbstractActor.Receive;
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.actor.Props;
|
||||
|
||||
class PrintMyActorRefActor extends AbstractActor {
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
|
|
@ -106,6 +110,26 @@ class SupervisedActor extends AbstractActor {
|
|||
}
|
||||
//#supervise
|
||||
|
||||
//#print-refs
|
||||
public class ActorHierarchyExperiments {
|
||||
public static void main(String[] args) throws java.io.IOException {
|
||||
ActorSystem system = ActorSystem.create("test");
|
||||
|
||||
ActorRef firstRef = system.actorOf(Props.create(PrintMyActorRefActor.class), "first-actor");
|
||||
System.out.println("First: " + firstRef);
|
||||
firstRef.tell("printit", ActorRef.noSender());
|
||||
|
||||
System.out.println(">>> Press ENTER to exit <<<");
|
||||
try {
|
||||
System.in.read();
|
||||
} finally {
|
||||
system.terminate();
|
||||
}
|
||||
}
|
||||
}
|
||||
//#print-refs
|
||||
|
||||
|
||||
class ActorHierarchyExperimentsTest extends JUnitSuite {
|
||||
static ActorSystem system;
|
||||
|
||||
|
|
@ -120,28 +144,19 @@ class ActorHierarchyExperimentsTest extends JUnitSuite {
|
|||
system = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTopAndChildActor() {
|
||||
//#print-refs
|
||||
ActorRef firstRef = system.actorOf(Props.create(PrintMyActorRefActor.class), "first-actor");
|
||||
System.out.println("First : " + firstRef);
|
||||
firstRef.tell("printit", ActorRef.noSender());
|
||||
//#print-refs
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartAndStopActors() {
|
||||
//#start-stop
|
||||
//#start-stop-main
|
||||
ActorRef first = system.actorOf(Props.create(StartStopActor1.class), "first");
|
||||
first.tell("stop", ActorRef.noSender());
|
||||
//#start-stop
|
||||
//#start-stop-main
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuperviseActors() {
|
||||
//#supervise
|
||||
//#supervise-main
|
||||
ActorRef supervisingActor = system.actorOf(Props.create(SupervisingActor.class), "supervising-actor");
|
||||
supervisingActor.tell("failChild", ActorRef.noSender());
|
||||
//#supervise
|
||||
//#supervise-main
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
package jdocs.tutorial_1;
|
||||
|
||||
//#iot-app
|
||||
package com.lightbend.akka.sample;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
package jdocs.tutorial_1;
|
||||
|
||||
//#iot-supervisor
|
||||
package com.lightbend.akka.sample;
|
||||
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.actor.ActorLogging;
|
||||
|
|
@ -3,18 +3,16 @@
|
|||
*/
|
||||
package jdocs.tutorial_3;
|
||||
|
||||
//#device-with-register
|
||||
//#full-device
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.actor.AbstractActor.Receive;
|
||||
import akka.actor.Props;
|
||||
import akka.event.Logging;
|
||||
import akka.event.LoggingAdapter;
|
||||
|
||||
import jdocs.tutorial_3.DeviceManager.DeviceRegistered;
|
||||
import jdocs.tutorial_3.DeviceManager.RequestTrackDevice;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class Device extends AbstractActor {
|
||||
private final LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
|
||||
|
||||
|
|
@ -82,16 +80,6 @@ public class Device extends AbstractActor {
|
|||
@Override
|
||||
public Receive createReceive() {
|
||||
return receiveBuilder()
|
||||
.match(RequestTrackDevice.class, r -> {
|
||||
if (this.groupId.equals(r.groupId) && this.deviceId.equals(r.deviceId)) {
|
||||
getSender().tell(new DeviceRegistered(), getSelf());
|
||||
} else {
|
||||
log.warning(
|
||||
"Ignoring TrackDevice request for {}-{}.This actor is responsible for {}-{}.",
|
||||
r.groupId, r.deviceId, this.groupId, this.deviceId
|
||||
);
|
||||
}
|
||||
})
|
||||
.match(RecordTemperature.class, r -> {
|
||||
log.info("Recorded temperature reading {} with {}", r.value, r.requestId);
|
||||
lastTemperatureReading = Optional.of(r.value);
|
||||
|
|
@ -103,4 +91,4 @@ public class Device extends AbstractActor {
|
|||
.build();
|
||||
}
|
||||
}
|
||||
//#device-with-register
|
||||
//#full-device
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
package jdocs.tutorial_2;
|
||||
package jdocs.tutorial_3;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import jdocs.tutorial_2.Device.ReadTemperature;
|
||||
import jdocs.tutorial_2.Device.RecordTemperature;
|
||||
import jdocs.tutorial_2.Device.RespondTemperature;
|
||||
import jdocs.tutorial_2.Device.TemperatureRecorded;
|
||||
import jdocs.tutorial_3.Device.ReadTemperature;
|
||||
import jdocs.tutorial_3.Device.RecordTemperature;
|
||||
import jdocs.tutorial_3.Device.RespondTemperature;
|
||||
import jdocs.tutorial_3.Device.TemperatureRecorded;
|
||||
|
||||
class DeviceInProgress1 {
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package jdocs.tutorial_2;
|
||||
package jdocs.tutorial_3;
|
||||
|
||||
class DeviceInProgress3 {
|
||||
|
||||
|
|
@ -3,9 +3,7 @@
|
|||
*/
|
||||
package jdocs.tutorial_3;
|
||||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.testkit.javadsl.TestKit;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
|
|
@ -14,7 +12,9 @@ import static org.junit.Assert.assertEquals;
|
|||
|
||||
import org.scalatest.junit.JUnitSuite;
|
||||
|
||||
import java.util.Optional;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.actor.ActorRef;
|
||||
import akka.testkit.javadsl.TestKit;
|
||||
|
||||
public class DeviceTest extends JUnitSuite {
|
||||
|
||||
|
|
@ -31,30 +31,6 @@ public class DeviceTest extends JUnitSuite {
|
|||
system = null;
|
||||
}
|
||||
|
||||
//#device-registration-tests
|
||||
@Test
|
||||
public void testReplyToRegistrationRequests() {
|
||||
TestKit probe = new TestKit(system);
|
||||
ActorRef deviceActor = system.actorOf(Device.props("group", "device"));
|
||||
|
||||
deviceActor.tell(new DeviceManager.RequestTrackDevice("group", "device"), probe.getRef());
|
||||
probe.expectMsgClass(DeviceManager.DeviceRegistered.class);
|
||||
assertEquals(deviceActor, probe.getLastSender());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIgnoreWrongRegistrationRequests() {
|
||||
TestKit probe = new TestKit(system);
|
||||
ActorRef deviceActor = system.actorOf(Device.props("group", "device"));
|
||||
|
||||
deviceActor.tell(new DeviceManager.RequestTrackDevice("wrongGroup", "device"), probe.getRef());
|
||||
probe.expectNoMsg();
|
||||
|
||||
deviceActor.tell(new DeviceManager.RequestTrackDevice("group", "wrongDevice"), probe.getRef());
|
||||
probe.expectNoMsg();
|
||||
}
|
||||
//#device-registration-tests
|
||||
|
||||
//#device-read-test
|
||||
@Test
|
||||
public void testReplyWithEmptyReadingIfNoTemperatureIsKnown() {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package jdocs.tutorial_2.inprogress2;
|
||||
package jdocs.tutorial_3.inprogress2;
|
||||
|
||||
//#device-with-read
|
||||
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
*/
|
||||
package jdocs.tutorial_4;
|
||||
|
||||
//#device-with-register
|
||||
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.actor.Props;
|
||||
import akka.event.Logging;
|
||||
|
|
@ -101,3 +103,4 @@ public class Device extends AbstractActor {
|
|||
.build();
|
||||
}
|
||||
}
|
||||
//#device-with-register
|
||||
|
|
|
|||
|
|
@ -3,20 +3,21 @@
|
|||
*/
|
||||
package jdocs.tutorial_4;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.Props;
|
||||
import akka.actor.Terminated;
|
||||
import akka.event.Logging;
|
||||
import akka.event.LoggingAdapter;
|
||||
import scala.concurrent.duration.FiniteDuration;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import jdocs.tutorial_4.Device;
|
||||
import jdocs.tutorial_4.DeviceManager;
|
||||
|
||||
//#query-added
|
||||
//#device-group-full
|
||||
public class DeviceGroup extends AbstractActor {
|
||||
private final LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
|
||||
|
||||
|
|
@ -26,9 +27,11 @@ public class DeviceGroup extends AbstractActor {
|
|||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
//#device-group-register
|
||||
public static Props props(String groupId) {
|
||||
return Props.create(DeviceGroup.class, groupId);
|
||||
}
|
||||
//#device-group-register
|
||||
|
||||
public static final class RequestDeviceList {
|
||||
final long requestId;
|
||||
|
|
@ -47,51 +50,15 @@ public class DeviceGroup extends AbstractActor {
|
|||
this.ids = ids;
|
||||
}
|
||||
}
|
||||
|
||||
//#query-protocol
|
||||
public static final class RequestAllTemperatures {
|
||||
final long requestId;
|
||||
|
||||
public RequestAllTemperatures(long requestId) {
|
||||
this.requestId = requestId;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class RespondAllTemperatures {
|
||||
final long requestId;
|
||||
final Map<String, TemperatureReading> temperatures;
|
||||
|
||||
public RespondAllTemperatures(long requestId, Map<String, TemperatureReading> temperatures) {
|
||||
this.requestId = requestId;
|
||||
this.temperatures = temperatures;
|
||||
}
|
||||
}
|
||||
|
||||
public static interface TemperatureReading {
|
||||
}
|
||||
|
||||
public static final class Temperature implements TemperatureReading {
|
||||
public final double value;
|
||||
|
||||
public Temperature(double value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class TemperatureNotAvailable implements TemperatureReading {
|
||||
}
|
||||
|
||||
public static final class DeviceNotAvailable implements TemperatureReading {
|
||||
}
|
||||
|
||||
public static final class DeviceTimedOut implements TemperatureReading {
|
||||
}
|
||||
//#query-protocol
|
||||
|
||||
//#device-group-register
|
||||
//#device-group-register
|
||||
//#device-group-register
|
||||
//#device-group-remove
|
||||
|
||||
final Map<String, ActorRef> deviceIdToActor = new HashMap<>();
|
||||
//#device-group-register
|
||||
final Map<ActorRef, String> actorToDeviceId = new HashMap<>();
|
||||
final long nextCollectionId = 0L;
|
||||
//#device-group-register
|
||||
|
||||
@Override
|
||||
public void preStart() {
|
||||
|
|
@ -103,19 +70,20 @@ public class DeviceGroup extends AbstractActor {
|
|||
log.info("DeviceGroup {} stopped", groupId);
|
||||
}
|
||||
|
||||
//#query-added
|
||||
private void onTrackDevice(DeviceManager.RequestTrackDevice trackMsg) {
|
||||
if (this.groupId.equals(trackMsg.groupId)) {
|
||||
ActorRef ref = deviceIdToActor.get(trackMsg.deviceId);
|
||||
if (ref != null) {
|
||||
ref.forward(trackMsg, getContext());
|
||||
ActorRef deviceActor = deviceIdToActor.get(trackMsg.deviceId);
|
||||
if (deviceActor != null) {
|
||||
deviceActor.forward(trackMsg, getContext());
|
||||
} else {
|
||||
log.info("Creating device actor for {}", trackMsg.deviceId);
|
||||
ActorRef deviceActor = getContext().actorOf(Device.props(groupId, trackMsg.deviceId), "device-" + trackMsg.deviceId);
|
||||
deviceActor = getContext().actorOf(Device.props(groupId, trackMsg.deviceId), "device-" + trackMsg.deviceId);
|
||||
//#device-group-register
|
||||
getContext().watch(deviceActor);
|
||||
deviceActor.forward(trackMsg, getContext());
|
||||
actorToDeviceId.put(deviceActor, trackMsg.deviceId);
|
||||
//#device-group-register
|
||||
deviceIdToActor.put(trackMsg.deviceId, deviceActor);
|
||||
deviceActor.forward(trackMsg, getContext());
|
||||
}
|
||||
} else {
|
||||
log.warning(
|
||||
|
|
@ -136,24 +104,16 @@ public class DeviceGroup extends AbstractActor {
|
|||
actorToDeviceId.remove(deviceActor);
|
||||
deviceIdToActor.remove(deviceId);
|
||||
}
|
||||
//#query-added
|
||||
|
||||
private void onAllTemperatures(RequestAllTemperatures r) {
|
||||
getContext().actorOf(DeviceGroupQuery.props(
|
||||
actorToDeviceId, r.requestId, getSender(), new FiniteDuration(3, TimeUnit.SECONDS)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
//#query-added
|
||||
return receiveBuilder()
|
||||
.match(DeviceManager.RequestTrackDevice.class, this::onTrackDevice)
|
||||
.match(RequestDeviceList.class, this::onDeviceList)
|
||||
.match(Terminated.class, this::onTerminated)
|
||||
//#query-added
|
||||
// ... other cases omitted
|
||||
.match(RequestAllTemperatures.class, this::onAllTemperatures)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
//#query-added
|
||||
//#device-group-remove
|
||||
//#device-group-register
|
||||
//#device-group-full
|
||||
|
|
|
|||
|
|
@ -3,10 +3,8 @@
|
|||
*/
|
||||
package jdocs.tutorial_4;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.ActorSystem;
|
||||
|
|
@ -21,8 +19,6 @@ import static org.junit.Assert.assertNotEquals;
|
|||
|
||||
import org.scalatest.junit.JUnitSuite;
|
||||
|
||||
import static jdocs.tutorial_4.DeviceGroupQueryTest.assertEqualTemperatures;
|
||||
|
||||
public class DeviceGroupTest extends JUnitSuite {
|
||||
|
||||
static ActorSystem system;
|
||||
|
|
@ -38,6 +34,7 @@ public class DeviceGroupTest extends JUnitSuite {
|
|||
system = null;
|
||||
}
|
||||
|
||||
//#device-group-test-registration
|
||||
@Test
|
||||
public void testRegisterDeviceActor() {
|
||||
TestKit probe = new TestKit(system);
|
||||
|
|
@ -67,7 +64,9 @@ public class DeviceGroupTest extends JUnitSuite {
|
|||
groupActor.tell(new DeviceManager.RequestTrackDevice("wrongGroup", "device1"), probe.getRef());
|
||||
probe.expectNoMsg();
|
||||
}
|
||||
//#device-group-test-registration
|
||||
|
||||
//#device-group-test3
|
||||
@Test
|
||||
public void testReturnSameActorForSameDeviceId() {
|
||||
TestKit probe = new TestKit(system);
|
||||
|
|
@ -82,7 +81,9 @@ public class DeviceGroupTest extends JUnitSuite {
|
|||
ActorRef deviceActor2 = probe.getLastSender();
|
||||
assertEquals(deviceActor1, deviceActor2);
|
||||
}
|
||||
//#device-group-test3
|
||||
|
||||
//#device-group-list-terminate-test
|
||||
@Test
|
||||
public void testListActiveDevices() {
|
||||
TestKit probe = new TestKit(system);
|
||||
|
|
@ -125,49 +126,12 @@ public class DeviceGroupTest extends JUnitSuite {
|
|||
// to see the Terminated, that order is undefined
|
||||
probe.awaitAssert(() -> {
|
||||
groupActor.tell(new DeviceGroup.RequestDeviceList(1L), probe.getRef());
|
||||
DeviceGroup.ReplyDeviceList r =
|
||||
DeviceGroup.ReplyDeviceList r =
|
||||
probe.expectMsgClass(DeviceGroup.ReplyDeviceList.class);
|
||||
assertEquals(1L, r.requestId);
|
||||
assertEquals(Stream.of("device2").collect(Collectors.toSet()), r.ids);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
//#group-query-integration-test
|
||||
@Test
|
||||
public void testCollectTemperaturesFromAllActiveDevices() {
|
||||
TestKit probe = new TestKit(system);
|
||||
ActorRef groupActor = system.actorOf(DeviceGroup.props("group"));
|
||||
|
||||
groupActor.tell(new DeviceManager.RequestTrackDevice("group", "device1"), probe.getRef());
|
||||
probe.expectMsgClass(DeviceManager.DeviceRegistered.class);
|
||||
ActorRef deviceActor1 = probe.getLastSender();
|
||||
|
||||
groupActor.tell(new DeviceManager.RequestTrackDevice("group", "device2"), probe.getRef());
|
||||
probe.expectMsgClass(DeviceManager.DeviceRegistered.class);
|
||||
ActorRef deviceActor2 = probe.getLastSender();
|
||||
|
||||
groupActor.tell(new DeviceManager.RequestTrackDevice("group", "device3"), probe.getRef());
|
||||
probe.expectMsgClass(DeviceManager.DeviceRegistered.class);
|
||||
ActorRef deviceActor3 = probe.getLastSender();
|
||||
|
||||
// Check that the device actors are working
|
||||
deviceActor1.tell(new Device.RecordTemperature(0L, 1.0), probe.getRef());
|
||||
assertEquals(0L, probe.expectMsgClass(Device.TemperatureRecorded.class).requestId);
|
||||
deviceActor2.tell(new Device.RecordTemperature(1L, 2.0), probe.getRef());
|
||||
assertEquals(1L, probe.expectMsgClass(Device.TemperatureRecorded.class).requestId);
|
||||
// No temperature for device 3
|
||||
|
||||
groupActor.tell(new DeviceGroup.RequestAllTemperatures(0L), probe.getRef());
|
||||
DeviceGroup.RespondAllTemperatures response = probe.expectMsgClass(DeviceGroup.RespondAllTemperatures.class);
|
||||
assertEquals(0L, response.requestId);
|
||||
|
||||
Map<String, DeviceGroup.TemperatureReading> expectedTemperatures = new HashMap<>();
|
||||
expectedTemperatures.put("device1", new DeviceGroup.Temperature(1.0));
|
||||
expectedTemperatures.put("device2", new DeviceGroup.Temperature(2.0));
|
||||
expectedTemperatures.put("device3", new DeviceGroup.TemperatureNotAvailable());
|
||||
|
||||
assertEqualTemperatures(expectedTemperatures, response.temperatures);
|
||||
}
|
||||
//#group-query-integration-test
|
||||
//#device-group-list-terminate-test
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@
|
|||
|
||||
package jdocs.tutorial_4;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.Props;
|
||||
|
|
@ -11,9 +14,7 @@ import akka.actor.Terminated;
|
|||
import akka.event.Logging;
|
||||
import akka.event.LoggingAdapter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
//#device-manager-full
|
||||
public class DeviceManager extends AbstractActor {
|
||||
private final LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
|
||||
|
||||
|
|
@ -21,6 +22,7 @@ public class DeviceManager extends AbstractActor {
|
|||
return Props.create(DeviceManager.class);
|
||||
}
|
||||
|
||||
//#device-manager-msgs
|
||||
public static final class RequestTrackDevice {
|
||||
public final String groupId;
|
||||
public final String deviceId;
|
||||
|
|
@ -34,6 +36,7 @@ public class DeviceManager extends AbstractActor {
|
|||
public static final class DeviceRegistered {
|
||||
}
|
||||
|
||||
//#device-manager-msgs
|
||||
final Map<String, ActorRef> groupIdToActor = new HashMap<>();
|
||||
final Map<ActorRef, String> actorToGroupId = new HashMap<>();
|
||||
|
||||
|
|
@ -78,3 +81,4 @@ public class DeviceManager extends AbstractActor {
|
|||
}
|
||||
|
||||
}
|
||||
//#device-manager-full
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@
|
|||
*/
|
||||
package jdocs.tutorial_4;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.testkit.javadsl.TestKit;
|
||||
|
|
@ -16,6 +14,8 @@ import static org.junit.Assert.assertEquals;
|
|||
|
||||
import org.scalatest.junit.JUnitSuite;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class DeviceTest extends JUnitSuite {
|
||||
|
||||
static ActorSystem system;
|
||||
|
|
@ -31,6 +31,7 @@ public class DeviceTest extends JUnitSuite {
|
|||
system = null;
|
||||
}
|
||||
|
||||
//#device-registration-tests
|
||||
@Test
|
||||
public void testReplyToRegistrationRequests() {
|
||||
TestKit probe = new TestKit(system);
|
||||
|
|
@ -52,7 +53,9 @@ public class DeviceTest extends JUnitSuite {
|
|||
deviceActor.tell(new DeviceManager.RequestTrackDevice("group", "wrongDevice"), probe.getRef());
|
||||
probe.expectNoMsg();
|
||||
}
|
||||
//#device-registration-tests
|
||||
|
||||
//#device-read-test
|
||||
@Test
|
||||
public void testReplyWithEmptyReadingIfNoTemperatureIsKnown() {
|
||||
TestKit probe = new TestKit(system);
|
||||
|
|
@ -62,7 +65,9 @@ public class DeviceTest extends JUnitSuite {
|
|||
assertEquals(42L, response.requestId);
|
||||
assertEquals(Optional.empty(), response.value);
|
||||
}
|
||||
//#device-read-test
|
||||
|
||||
//#device-write-read-test
|
||||
@Test
|
||||
public void testReplyWithLatestTemperatureReading() {
|
||||
TestKit probe = new TestKit(system);
|
||||
|
|
@ -84,5 +89,6 @@ public class DeviceTest extends JUnitSuite {
|
|||
assertEquals(4L, response2.requestId);
|
||||
assertEquals(Optional.of(55.0), response2.value);
|
||||
}
|
||||
//#device-write-read-test
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import akka.actor.AbstractActor;
|
|||
import akka.actor.Props;
|
||||
import akka.event.Logging;
|
||||
import akka.event.LoggingAdapter;
|
||||
|
||||
import jdocs.tutorial_5.DeviceManager.DeviceRegistered;
|
||||
import jdocs.tutorial_5.DeviceManager.RequestTrackDevice;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
//#query-added
|
||||
public class DeviceGroup extends AbstractActor {
|
||||
private final LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
|
||||
|
||||
|
|
@ -47,6 +48,7 @@ public class DeviceGroup extends AbstractActor {
|
|||
}
|
||||
}
|
||||
|
||||
//#query-protocol
|
||||
public static final class RequestAllTemperatures {
|
||||
final long requestId;
|
||||
|
||||
|
|
@ -84,6 +86,8 @@ public class DeviceGroup extends AbstractActor {
|
|||
|
||||
public static final class DeviceTimedOut implements TemperatureReading {
|
||||
}
|
||||
//#query-protocol
|
||||
|
||||
|
||||
final Map<String, ActorRef> deviceIdToActor = new HashMap<>();
|
||||
final Map<ActorRef, String> actorToDeviceId = new HashMap<>();
|
||||
|
|
@ -99,6 +103,7 @@ public class DeviceGroup extends AbstractActor {
|
|||
log.info("DeviceGroup {} stopped", groupId);
|
||||
}
|
||||
|
||||
//#query-added
|
||||
private void onTrackDevice(DeviceManager.RequestTrackDevice trackMsg) {
|
||||
if (this.groupId.equals(trackMsg.groupId)) {
|
||||
ActorRef ref = deviceIdToActor.get(trackMsg.deviceId);
|
||||
|
|
@ -131,6 +136,7 @@ public class DeviceGroup extends AbstractActor {
|
|||
actorToDeviceId.remove(deviceActor);
|
||||
deviceIdToActor.remove(deviceId);
|
||||
}
|
||||
//#query-added
|
||||
|
||||
private void onAllTemperatures(RequestAllTemperatures r) {
|
||||
getContext().actorOf(DeviceGroupQuery.props(
|
||||
|
|
@ -139,11 +145,15 @@ public class DeviceGroup extends AbstractActor {
|
|||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
//#query-added
|
||||
return receiveBuilder()
|
||||
.match(DeviceManager.RequestTrackDevice.class, this::onTrackDevice)
|
||||
.match(RequestDeviceList.class, this::onDeviceList)
|
||||
.match(Terminated.class, this::onTerminated)
|
||||
//#query-added
|
||||
// ... other cases omitted
|
||||
.match(RequestAllTemperatures.class, this::onAllTemperatures)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
//#query-added
|
||||
|
|
|
|||
|
|
@ -3,16 +3,24 @@
|
|||
*/
|
||||
package jdocs.tutorial_5;
|
||||
|
||||
import akka.actor.*;
|
||||
import akka.event.Logging;
|
||||
import akka.event.LoggingAdapter;
|
||||
import scala.concurrent.duration.FiniteDuration;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import scala.concurrent.duration.FiniteDuration;
|
||||
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.Cancellable;
|
||||
import akka.actor.Props;
|
||||
import akka.actor.Terminated;
|
||||
|
||||
import akka.event.Logging;
|
||||
import akka.event.LoggingAdapter;
|
||||
|
||||
//#query-full
|
||||
//#query-outline
|
||||
public class DeviceGroupQuery extends AbstractActor {
|
||||
public static final class CollectionTimeout {
|
||||
}
|
||||
|
|
@ -52,6 +60,8 @@ public class DeviceGroupQuery extends AbstractActor {
|
|||
queryTimeoutTimer.cancel();
|
||||
}
|
||||
|
||||
//#query-outline
|
||||
//#query-state
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return waitingForReplies(new HashMap<>(), actorToDeviceId.keySet());
|
||||
|
|
@ -69,10 +79,7 @@ public class DeviceGroupQuery extends AbstractActor {
|
|||
receivedResponse(deviceActor, reading, stillWaiting, repliesSoFar);
|
||||
})
|
||||
.match(Terminated.class, t -> {
|
||||
if (stillWaiting.contains(t.getActor())) {
|
||||
receivedResponse(t.getActor(), new DeviceGroup.DeviceNotAvailable(), stillWaiting, repliesSoFar);
|
||||
}
|
||||
// else ignore
|
||||
receivedResponse(t.getActor(), new DeviceGroup.DeviceNotAvailable(), stillWaiting, repliesSoFar);
|
||||
})
|
||||
.match(CollectionTimeout.class, t -> {
|
||||
Map<String, DeviceGroup.TemperatureReading> replies = new HashMap<>(repliesSoFar);
|
||||
|
|
@ -85,7 +92,9 @@ public class DeviceGroupQuery extends AbstractActor {
|
|||
})
|
||||
.build();
|
||||
}
|
||||
//#query-state
|
||||
|
||||
//#query-collect-reply
|
||||
public void receivedResponse(ActorRef deviceActor,
|
||||
DeviceGroup.TemperatureReading reading,
|
||||
Set<ActorRef> stillWaiting,
|
||||
|
|
@ -105,4 +114,8 @@ public class DeviceGroupQuery extends AbstractActor {
|
|||
getContext().become(waitingForReplies(newRepliesSoFar, newStillWaiting));
|
||||
}
|
||||
}
|
||||
//#query-collect-reply
|
||||
//#query-outline
|
||||
}
|
||||
//#query-outline
|
||||
//#query-full
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ import akka.testkit.javadsl.TestKit;
|
|||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.scalatest.junit.JUnitSuite;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import org.scalatest.junit.JUnitSuite;
|
||||
import scala.concurrent.duration.FiniteDuration;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
|
@ -37,6 +37,7 @@ public class DeviceGroupQueryTest extends JUnitSuite {
|
|||
system = null;
|
||||
}
|
||||
|
||||
//#query-test-normal
|
||||
@Test
|
||||
public void testReturnTemperatureValueForWorkingDevices() {
|
||||
TestKit requester = new TestKit(system);
|
||||
|
|
@ -69,7 +70,9 @@ public class DeviceGroupQueryTest extends JUnitSuite {
|
|||
|
||||
assertEqualTemperatures(expectedTemperatures, response.temperatures);
|
||||
}
|
||||
//#query-test-normal
|
||||
|
||||
//#query-test-no-reading
|
||||
@Test
|
||||
public void testReturnTemperatureNotAvailableForDevicesWithNoReadings() {
|
||||
TestKit requester = new TestKit(system);
|
||||
|
|
@ -102,7 +105,9 @@ public class DeviceGroupQueryTest extends JUnitSuite {
|
|||
|
||||
assertEqualTemperatures(expectedTemperatures, response.temperatures);
|
||||
}
|
||||
//#query-test-no-reading
|
||||
|
||||
//#query-test-stopped
|
||||
@Test
|
||||
public void testReturnDeviceNotAvailableIfDeviceStopsBeforeAnswering() {
|
||||
TestKit requester = new TestKit(system);
|
||||
|
|
@ -135,7 +140,9 @@ public class DeviceGroupQueryTest extends JUnitSuite {
|
|||
|
||||
assertEqualTemperatures(expectedTemperatures, response.temperatures);
|
||||
}
|
||||
//#query-test-stopped
|
||||
|
||||
//#query-test-stopped-later
|
||||
@Test
|
||||
public void testReturnTemperatureReadingEvenIfDeviceStopsAfterAnswering() {
|
||||
TestKit requester = new TestKit(system);
|
||||
|
|
@ -169,7 +176,9 @@ public class DeviceGroupQueryTest extends JUnitSuite {
|
|||
|
||||
assertEqualTemperatures(expectedTemperatures, response.temperatures);
|
||||
}
|
||||
//#query-test-stopped-later
|
||||
|
||||
//#query-test-timeout
|
||||
@Test
|
||||
public void testReturnDeviceTimedOutIfDeviceDoesNotAnswerInTime() {
|
||||
TestKit requester = new TestKit(system);
|
||||
|
|
@ -203,6 +212,7 @@ public class DeviceGroupQueryTest extends JUnitSuite {
|
|||
|
||||
assertEqualTemperatures(expectedTemperatures, response.temperatures);
|
||||
}
|
||||
//#query-test-timeout
|
||||
|
||||
public static void assertEqualTemperatures(Map<String, DeviceGroup.TemperatureReading> expected, Map<String, DeviceGroup.TemperatureReading> actual) {
|
||||
for (Map.Entry<String, DeviceGroup.TemperatureReading> entry : expected.entrySet()) {
|
||||
|
|
|
|||
|
|
@ -16,10 +16,11 @@ import akka.testkit.javadsl.TestKit;
|
|||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.scalatest.junit.JUnitSuite;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
|
||||
import org.scalatest.junit.JUnitSuite;
|
||||
|
||||
import static jdocs.tutorial_5.DeviceGroupQueryTest.assertEqualTemperatures;
|
||||
|
||||
public class DeviceGroupTest extends JUnitSuite {
|
||||
|
|
@ -132,6 +133,7 @@ public class DeviceGroupTest extends JUnitSuite {
|
|||
});
|
||||
}
|
||||
|
||||
//#group-query-integration-test
|
||||
@Test
|
||||
public void testCollectTemperaturesFromAllActiveDevices() {
|
||||
TestKit probe = new TestKit(system);
|
||||
|
|
@ -167,4 +169,5 @@ public class DeviceGroupTest extends JUnitSuite {
|
|||
|
||||
assertEqualTemperatures(expectedTemperatures, response.temperatures);
|
||||
}
|
||||
//#group-query-integration-test
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import static org.junit.Assert.assertEquals;
|
|||
|
||||
import org.scalatest.junit.JUnitSuite;
|
||||
|
||||
|
||||
public class DeviceTest extends JUnitSuite {
|
||||
|
||||
static ActorSystem system;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
package jdocs.tutorial_2;
|
||||
|
||||
//#full-device
|
||||
|
||||
import java.util.Optional;
|
||||
package jdocs.tutorial_6;
|
||||
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.actor.AbstractActor.Receive;
|
||||
import akka.actor.Props;
|
||||
import akka.event.Logging;
|
||||
import akka.event.LoggingAdapter;
|
||||
import jdocs.tutorial_6.DeviceManager.DeviceRegistered;
|
||||
import jdocs.tutorial_6.DeviceManager.RequestTrackDevice;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class Device extends AbstractActor {
|
||||
private final LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
|
||||
|
|
@ -80,6 +79,16 @@ public class Device extends AbstractActor {
|
|||
@Override
|
||||
public Receive createReceive() {
|
||||
return receiveBuilder()
|
||||
.match(RequestTrackDevice.class, r -> {
|
||||
if (this.groupId.equals(r.groupId) && this.deviceId.equals(r.deviceId)) {
|
||||
getSender().tell(new DeviceRegistered(), getSelf());
|
||||
} else {
|
||||
log.warning(
|
||||
"Ignoring TrackDevice request for {}-{}.This actor is responsible for {}-{}.",
|
||||
r.groupId, r.deviceId, this.groupId, this.deviceId
|
||||
);
|
||||
}
|
||||
})
|
||||
.match(RecordTemperature.class, r -> {
|
||||
log.info("Recorded temperature reading {} with {}", r.value, r.requestId);
|
||||
lastTemperatureReading = Optional.of(r.value);
|
||||
|
|
@ -91,4 +100,3 @@ public class Device extends AbstractActor {
|
|||
.build();
|
||||
}
|
||||
}
|
||||
//#full-device
|
||||
|
|
@ -1,11 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
package jdocs.tutorial_3;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
package jdocs.tutorial_6;
|
||||
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.actor.ActorRef;
|
||||
|
|
@ -13,11 +9,13 @@ import akka.actor.Props;
|
|||
import akka.actor.Terminated;
|
||||
import akka.event.Logging;
|
||||
import akka.event.LoggingAdapter;
|
||||
import scala.concurrent.duration.FiniteDuration;
|
||||
|
||||
import jdocs.tutorial_3.Device;
|
||||
import jdocs.tutorial_3.DeviceManager;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
//#device-group-full
|
||||
public class DeviceGroup extends AbstractActor {
|
||||
private final LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
|
||||
|
||||
|
|
@ -27,11 +25,9 @@ public class DeviceGroup extends AbstractActor {
|
|||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
//#device-group-register
|
||||
public static Props props(String groupId) {
|
||||
return Props.create(DeviceGroup.class, groupId);
|
||||
}
|
||||
//#device-group-register
|
||||
|
||||
public static final class RequestDeviceList {
|
||||
final long requestId;
|
||||
|
|
@ -50,15 +46,48 @@ public class DeviceGroup extends AbstractActor {
|
|||
this.ids = ids;
|
||||
}
|
||||
}
|
||||
//#device-group-register
|
||||
//#device-group-register
|
||||
//#device-group-register
|
||||
//#device-group-remove
|
||||
|
||||
public static final class RequestAllTemperatures {
|
||||
final long requestId;
|
||||
|
||||
public RequestAllTemperatures(long requestId) {
|
||||
this.requestId = requestId;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class RespondAllTemperatures {
|
||||
final long requestId;
|
||||
final Map<String, TemperatureReading> temperatures;
|
||||
|
||||
public RespondAllTemperatures(long requestId, Map<String, TemperatureReading> temperatures) {
|
||||
this.requestId = requestId;
|
||||
this.temperatures = temperatures;
|
||||
}
|
||||
}
|
||||
|
||||
public static interface TemperatureReading {
|
||||
}
|
||||
|
||||
public static final class Temperature implements TemperatureReading {
|
||||
public final double value;
|
||||
|
||||
public Temperature(double value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class TemperatureNotAvailable implements TemperatureReading {
|
||||
}
|
||||
|
||||
public static final class DeviceNotAvailable implements TemperatureReading {
|
||||
}
|
||||
|
||||
public static final class DeviceTimedOut implements TemperatureReading {
|
||||
}
|
||||
|
||||
final Map<String, ActorRef> deviceIdToActor = new HashMap<>();
|
||||
//#device-group-register
|
||||
final Map<ActorRef, String> actorToDeviceId = new HashMap<>();
|
||||
//#device-group-register
|
||||
final long nextCollectionId = 0L;
|
||||
|
||||
@Override
|
||||
public void preStart() {
|
||||
|
|
@ -72,18 +101,16 @@ public class DeviceGroup extends AbstractActor {
|
|||
|
||||
private void onTrackDevice(DeviceManager.RequestTrackDevice trackMsg) {
|
||||
if (this.groupId.equals(trackMsg.groupId)) {
|
||||
ActorRef deviceActor = deviceIdToActor.get(trackMsg.deviceId);
|
||||
if (deviceActor != null) {
|
||||
deviceActor.forward(trackMsg, getContext());
|
||||
ActorRef ref = deviceIdToActor.get(trackMsg.deviceId);
|
||||
if (ref != null) {
|
||||
ref.forward(trackMsg, getContext());
|
||||
} else {
|
||||
log.info("Creating device actor for {}", trackMsg.deviceId);
|
||||
deviceActor = getContext().actorOf(Device.props(groupId, trackMsg.deviceId), "device-" + trackMsg.deviceId);
|
||||
//#device-group-register
|
||||
ActorRef deviceActor = getContext().actorOf(Device.props(groupId, trackMsg.deviceId), "device-" + trackMsg.deviceId);
|
||||
getContext().watch(deviceActor);
|
||||
actorToDeviceId.put(deviceActor, trackMsg.deviceId);
|
||||
//#device-group-register
|
||||
deviceIdToActor.put(trackMsg.deviceId, deviceActor);
|
||||
deviceActor.forward(trackMsg, getContext());
|
||||
actorToDeviceId.put(deviceActor, trackMsg.deviceId);
|
||||
deviceIdToActor.put(trackMsg.deviceId, deviceActor);
|
||||
}
|
||||
} else {
|
||||
log.warning(
|
||||
|
|
@ -105,15 +132,18 @@ public class DeviceGroup extends AbstractActor {
|
|||
deviceIdToActor.remove(deviceId);
|
||||
}
|
||||
|
||||
private void onAllTemperatures(RequestAllTemperatures r) {
|
||||
getContext().actorOf(DeviceGroupQuery.props(
|
||||
actorToDeviceId, r.requestId, getSender(), new FiniteDuration(3, TimeUnit.SECONDS)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return receiveBuilder()
|
||||
.match(DeviceManager.RequestTrackDevice.class, this::onTrackDevice)
|
||||
.match(RequestDeviceList.class, this::onDeviceList)
|
||||
.match(Terminated.class, this::onTerminated)
|
||||
.match(RequestAllTemperatures.class, this::onAllTemperatures)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
//#device-group-remove
|
||||
//#device-group-register
|
||||
//#device-group-full
|
||||
|
|
@ -1,26 +1,18 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
package jdocs.tutorial_4;
|
||||
package jdocs.tutorial_6;
|
||||
|
||||
import akka.actor.*;
|
||||
import akka.event.Logging;
|
||||
import akka.event.LoggingAdapter;
|
||||
import scala.concurrent.duration.FiniteDuration;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import scala.concurrent.duration.FiniteDuration;
|
||||
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.Cancellable;
|
||||
import akka.actor.Props;
|
||||
import akka.actor.Terminated;
|
||||
|
||||
import akka.event.Logging;
|
||||
import akka.event.LoggingAdapter;
|
||||
|
||||
//#query-full
|
||||
//#query-outline
|
||||
public class DeviceGroupQuery extends AbstractActor {
|
||||
public static final class CollectionTimeout {
|
||||
}
|
||||
|
|
@ -60,8 +52,6 @@ public class DeviceGroupQuery extends AbstractActor {
|
|||
queryTimeoutTimer.cancel();
|
||||
}
|
||||
|
||||
//#query-outline
|
||||
//#query-state
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return waitingForReplies(new HashMap<>(), actorToDeviceId.keySet());
|
||||
|
|
@ -79,7 +69,10 @@ public class DeviceGroupQuery extends AbstractActor {
|
|||
receivedResponse(deviceActor, reading, stillWaiting, repliesSoFar);
|
||||
})
|
||||
.match(Terminated.class, t -> {
|
||||
receivedResponse(t.getActor(), new DeviceGroup.DeviceNotAvailable(), stillWaiting, repliesSoFar);
|
||||
if (stillWaiting.contains(t.getActor())) {
|
||||
receivedResponse(t.getActor(), new DeviceGroup.DeviceNotAvailable(), stillWaiting, repliesSoFar);
|
||||
}
|
||||
// else ignore
|
||||
})
|
||||
.match(CollectionTimeout.class, t -> {
|
||||
Map<String, DeviceGroup.TemperatureReading> replies = new HashMap<>(repliesSoFar);
|
||||
|
|
@ -92,9 +85,7 @@ public class DeviceGroupQuery extends AbstractActor {
|
|||
})
|
||||
.build();
|
||||
}
|
||||
//#query-state
|
||||
|
||||
//#query-collect-reply
|
||||
public void receivedResponse(ActorRef deviceActor,
|
||||
DeviceGroup.TemperatureReading reading,
|
||||
Set<ActorRef> stillWaiting,
|
||||
|
|
@ -114,8 +105,4 @@ public class DeviceGroupQuery extends AbstractActor {
|
|||
getContext().become(waitingForReplies(newRepliesSoFar, newStillWaiting));
|
||||
}
|
||||
}
|
||||
//#query-collect-reply
|
||||
//#query-outline
|
||||
}
|
||||
//#query-outline
|
||||
//#query-full
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
package jdocs.tutorial_4;
|
||||
package jdocs.tutorial_6;
|
||||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.ActorSystem;
|
||||
|
|
@ -11,10 +11,10 @@ import akka.testkit.javadsl.TestKit;
|
|||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.scalatest.junit.JUnitSuite;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import org.scalatest.junit.JUnitSuite;
|
||||
import scala.concurrent.duration.FiniteDuration;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
|
@ -37,7 +37,6 @@ public class DeviceGroupQueryTest extends JUnitSuite {
|
|||
system = null;
|
||||
}
|
||||
|
||||
//#query-test-normal
|
||||
@Test
|
||||
public void testReturnTemperatureValueForWorkingDevices() {
|
||||
TestKit requester = new TestKit(system);
|
||||
|
|
@ -70,9 +69,7 @@ public class DeviceGroupQueryTest extends JUnitSuite {
|
|||
|
||||
assertEqualTemperatures(expectedTemperatures, response.temperatures);
|
||||
}
|
||||
//#query-test-normal
|
||||
|
||||
//#query-test-no-reading
|
||||
@Test
|
||||
public void testReturnTemperatureNotAvailableForDevicesWithNoReadings() {
|
||||
TestKit requester = new TestKit(system);
|
||||
|
|
@ -105,9 +102,7 @@ public class DeviceGroupQueryTest extends JUnitSuite {
|
|||
|
||||
assertEqualTemperatures(expectedTemperatures, response.temperatures);
|
||||
}
|
||||
//#query-test-no-reading
|
||||
|
||||
//#query-test-stopped
|
||||
@Test
|
||||
public void testReturnDeviceNotAvailableIfDeviceStopsBeforeAnswering() {
|
||||
TestKit requester = new TestKit(system);
|
||||
|
|
@ -140,9 +135,7 @@ public class DeviceGroupQueryTest extends JUnitSuite {
|
|||
|
||||
assertEqualTemperatures(expectedTemperatures, response.temperatures);
|
||||
}
|
||||
//#query-test-stopped
|
||||
|
||||
//#query-test-stopped-later
|
||||
@Test
|
||||
public void testReturnTemperatureReadingEvenIfDeviceStopsAfterAnswering() {
|
||||
TestKit requester = new TestKit(system);
|
||||
|
|
@ -176,9 +169,7 @@ public class DeviceGroupQueryTest extends JUnitSuite {
|
|||
|
||||
assertEqualTemperatures(expectedTemperatures, response.temperatures);
|
||||
}
|
||||
//#query-test-stopped-later
|
||||
|
||||
//#query-test-timeout
|
||||
@Test
|
||||
public void testReturnDeviceTimedOutIfDeviceDoesNotAnswerInTime() {
|
||||
TestKit requester = new TestKit(system);
|
||||
|
|
@ -212,7 +203,6 @@ public class DeviceGroupQueryTest extends JUnitSuite {
|
|||
|
||||
assertEqualTemperatures(expectedTemperatures, response.temperatures);
|
||||
}
|
||||
//#query-test-timeout
|
||||
|
||||
public static void assertEqualTemperatures(Map<String, DeviceGroup.TemperatureReading> expected, Map<String, DeviceGroup.TemperatureReading> actual) {
|
||||
for (Map.Entry<String, DeviceGroup.TemperatureReading> entry : expected.entrySet()) {
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
package jdocs.tutorial_3;
|
||||
package jdocs.tutorial_6;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.ActorSystem;
|
||||
|
|
@ -14,10 +16,11 @@ import akka.testkit.javadsl.TestKit;
|
|||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.scalatest.junit.JUnitSuite;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
|
||||
import org.scalatest.junit.JUnitSuite;
|
||||
import static jdocs.tutorial_6.DeviceGroupQueryTest.assertEqualTemperatures;
|
||||
|
||||
public class DeviceGroupTest extends JUnitSuite {
|
||||
|
||||
|
|
@ -34,7 +37,6 @@ public class DeviceGroupTest extends JUnitSuite {
|
|||
system = null;
|
||||
}
|
||||
|
||||
//#device-group-test-registration
|
||||
@Test
|
||||
public void testRegisterDeviceActor() {
|
||||
TestKit probe = new TestKit(system);
|
||||
|
|
@ -49,7 +51,7 @@ public class DeviceGroupTest extends JUnitSuite {
|
|||
ActorRef deviceActor2 = probe.getLastSender();
|
||||
assertNotEquals(deviceActor1, deviceActor2);
|
||||
|
||||
// Check that the device actors are workingl
|
||||
// Check that the device actors are working
|
||||
deviceActor1.tell(new Device.RecordTemperature(0L, 1.0), probe.getRef());
|
||||
assertEquals(0L, probe.expectMsgClass(Device.TemperatureRecorded.class).requestId);
|
||||
deviceActor2.tell(new Device.RecordTemperature(1L, 2.0), probe.getRef());
|
||||
|
|
@ -64,9 +66,7 @@ public class DeviceGroupTest extends JUnitSuite {
|
|||
groupActor.tell(new DeviceManager.RequestTrackDevice("wrongGroup", "device1"), probe.getRef());
|
||||
probe.expectNoMsg();
|
||||
}
|
||||
//#device-group-test-registration
|
||||
|
||||
//#device-group-test3
|
||||
@Test
|
||||
public void testReturnSameActorForSameDeviceId() {
|
||||
TestKit probe = new TestKit(system);
|
||||
|
|
@ -81,9 +81,7 @@ public class DeviceGroupTest extends JUnitSuite {
|
|||
ActorRef deviceActor2 = probe.getLastSender();
|
||||
assertEquals(deviceActor1, deviceActor2);
|
||||
}
|
||||
//#device-group-test3
|
||||
|
||||
//#device-group-list-terminate-test
|
||||
@Test
|
||||
public void testListActiveDevices() {
|
||||
TestKit probe = new TestKit(system);
|
||||
|
|
@ -133,5 +131,40 @@ public class DeviceGroupTest extends JUnitSuite {
|
|||
return null;
|
||||
});
|
||||
}
|
||||
//#device-group-list-terminate-test
|
||||
|
||||
@Test
|
||||
public void testCollectTemperaturesFromAllActiveDevices() {
|
||||
TestKit probe = new TestKit(system);
|
||||
ActorRef groupActor = system.actorOf(DeviceGroup.props("group"));
|
||||
|
||||
groupActor.tell(new DeviceManager.RequestTrackDevice("group", "device1"), probe.getRef());
|
||||
probe.expectMsgClass(DeviceManager.DeviceRegistered.class);
|
||||
ActorRef deviceActor1 = probe.getLastSender();
|
||||
|
||||
groupActor.tell(new DeviceManager.RequestTrackDevice("group", "device2"), probe.getRef());
|
||||
probe.expectMsgClass(DeviceManager.DeviceRegistered.class);
|
||||
ActorRef deviceActor2 = probe.getLastSender();
|
||||
|
||||
groupActor.tell(new DeviceManager.RequestTrackDevice("group", "device3"), probe.getRef());
|
||||
probe.expectMsgClass(DeviceManager.DeviceRegistered.class);
|
||||
ActorRef deviceActor3 = probe.getLastSender();
|
||||
|
||||
// Check that the device actors are working
|
||||
deviceActor1.tell(new Device.RecordTemperature(0L, 1.0), probe.getRef());
|
||||
assertEquals(0L, probe.expectMsgClass(Device.TemperatureRecorded.class).requestId);
|
||||
deviceActor2.tell(new Device.RecordTemperature(1L, 2.0), probe.getRef());
|
||||
assertEquals(1L, probe.expectMsgClass(Device.TemperatureRecorded.class).requestId);
|
||||
// No temperature for device 3
|
||||
|
||||
groupActor.tell(new DeviceGroup.RequestAllTemperatures(0L), probe.getRef());
|
||||
DeviceGroup.RespondAllTemperatures response = probe.expectMsgClass(DeviceGroup.RespondAllTemperatures.class);
|
||||
assertEquals(0L, response.requestId);
|
||||
|
||||
Map<String, DeviceGroup.TemperatureReading> expectedTemperatures = new HashMap<>();
|
||||
expectedTemperatures.put("device1", new DeviceGroup.Temperature(1.0));
|
||||
expectedTemperatures.put("device2", new DeviceGroup.Temperature(2.0));
|
||||
expectedTemperatures.put("device3", new DeviceGroup.TemperatureNotAvailable());
|
||||
|
||||
assertEqualTemperatures(expectedTemperatures, response.temperatures);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,10 +2,7 @@
|
|||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
|
||||
package jdocs.tutorial_3;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
package jdocs.tutorial_6;
|
||||
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.actor.ActorRef;
|
||||
|
|
@ -14,7 +11,9 @@ import akka.actor.Terminated;
|
|||
import akka.event.Logging;
|
||||
import akka.event.LoggingAdapter;
|
||||
|
||||
//#device-manager-full
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class DeviceManager extends AbstractActor {
|
||||
private final LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
|
||||
|
||||
|
|
@ -22,7 +21,6 @@ public class DeviceManager extends AbstractActor {
|
|||
return Props.create(DeviceManager.class);
|
||||
}
|
||||
|
||||
//#device-manager-msgs
|
||||
public static final class RequestTrackDevice {
|
||||
public final String groupId;
|
||||
public final String deviceId;
|
||||
|
|
@ -36,7 +34,6 @@ public class DeviceManager extends AbstractActor {
|
|||
public static final class DeviceRegistered {
|
||||
}
|
||||
|
||||
//#device-manager-msgs
|
||||
final Map<String, ActorRef> groupIdToActor = new HashMap<>();
|
||||
final Map<ActorRef, String> actorToGroupId = new HashMap<>();
|
||||
|
||||
|
|
@ -81,4 +78,3 @@ public class DeviceManager extends AbstractActor {
|
|||
}
|
||||
|
||||
}
|
||||
//#device-manager-full
|
||||
|
|
@ -1,10 +1,14 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
package jdocs.tutorial_2;
|
||||
package jdocs.tutorial_6;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.testkit.javadsl.TestKit;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
|
@ -12,9 +16,6 @@ import static org.junit.Assert.assertEquals;
|
|||
|
||||
import org.scalatest.junit.JUnitSuite;
|
||||
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.actor.ActorRef;
|
||||
import akka.testkit.javadsl.TestKit;
|
||||
|
||||
public class DeviceTest extends JUnitSuite {
|
||||
|
||||
|
|
@ -31,7 +32,28 @@ public class DeviceTest extends JUnitSuite {
|
|||
system = null;
|
||||
}
|
||||
|
||||
//#device-read-test
|
||||
@Test
|
||||
public void testReplyToRegistrationRequests() {
|
||||
TestKit probe = new TestKit(system);
|
||||
ActorRef deviceActor = system.actorOf(Device.props("group", "device"));
|
||||
|
||||
deviceActor.tell(new DeviceManager.RequestTrackDevice("group", "device"), probe.getRef());
|
||||
probe.expectMsgClass(DeviceManager.DeviceRegistered.class);
|
||||
assertEquals(deviceActor, probe.getLastSender());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIgnoreWrongRegistrationRequests() {
|
||||
TestKit probe = new TestKit(system);
|
||||
ActorRef deviceActor = system.actorOf(Device.props("group", "device"));
|
||||
|
||||
deviceActor.tell(new DeviceManager.RequestTrackDevice("wrongGroup", "device"), probe.getRef());
|
||||
probe.expectNoMsg();
|
||||
|
||||
deviceActor.tell(new DeviceManager.RequestTrackDevice("group", "wrongDevice"), probe.getRef());
|
||||
probe.expectNoMsg();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplyWithEmptyReadingIfNoTemperatureIsKnown() {
|
||||
TestKit probe = new TestKit(system);
|
||||
|
|
@ -41,9 +63,7 @@ public class DeviceTest extends JUnitSuite {
|
|||
assertEquals(42L, response.requestId);
|
||||
assertEquals(Optional.empty(), response.value);
|
||||
}
|
||||
//#device-read-test
|
||||
|
||||
//#device-write-read-test
|
||||
@Test
|
||||
public void testReplyWithLatestTemperatureReading() {
|
||||
TestKit probe = new TestKit(system);
|
||||
|
|
@ -65,6 +85,5 @@ public class DeviceTest extends JUnitSuite {
|
|||
assertEquals(4L, response2.requestId);
|
||||
assertEquals(Optional.of(55.0), response2.value);
|
||||
}
|
||||
//#device-write-read-test
|
||||
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
package jdocs.tutorial_5;
|
||||
package jdocs.tutorial_6;
|
||||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.ActorSystem;
|
||||
|
|
@ -26,4 +26,4 @@ public class IotMain {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
package jdocs.tutorial_5;
|
||||
package jdocs.tutorial_6;
|
||||
|
||||
//#iot-supervisor
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue