Reputation: 170158
I have an Akka system with a ParentOrderActor
that receives Create
and Read
messages to create- and read child-OrderActor
respectively.
All goes well if I create all Actors in the main "/user/..."
path:
// Inside the ParentOrderActor ("/user/orders")
// Create in "/user"
getContext().system().actorOf(new Props(OrderActor.class), "ABC");
// Read via an ActorSelection
ActorSelection actorSelection = getContext().system().actorSelection("/user/ABC");
The logs show this when all goes well:
ParentOrderActor -> created: Actor[akka://system/user/ABC#1132819541]
ParentOrderActor -> received: Message[name=Read, id=ABC]
ParentOrderActor -> actorSelection: ActorSelection[Actor[akka://system/]/user/ABC]
ParentOrderActor -> for id=Message[name=Read, id=ABC], we retrieved ActorRef: Actor[akka://system/user/ABC#1132819541]
But when trying to create- and read the child actors inside the ParentOrderActor:
// Inside the ParentOrderActor ("/user/orders")
// Create in "/user/orders"
getContext().actorOf(new Props(OrderActor.class), "ABC");
// Read via an ActorSelection
ActorSelection actorSelection = getContext().actorSelection("ABC");
this is being printed:
ParentOrderActor -> created: Actor[akka://system/user/orders/$a/ABC#-436492577]
ParentOrderActor -> received: Message[name=Read, id=ABC]
ParentOrderActor -> actorSelection: ActorSelection[Actor[akka://system/user/orders/$a#478613574]/ABC]
Oops: java.util.concurrent.TimeoutException: Futures timed out after [2 seconds]
ParentOrderActor -> for id=Message[name=Read, id=ABC], we retrieved ActorRef: null
I've tried all kind of paths in the actorSelection(...)
, but the ActorRef
is always null
.
For completeness sake, here is a short SSCCE that demonstrates the above:
public class Main {
private static boolean createChildrenInUser = false;
private static LoggingAdapter log;
static abstract class Message {
final String id;
Message(String id) {
this.id = id;
}
@Override
public String toString() {
return "Message[name=" + getClass().getSimpleName() + ", id=" + id + "]";
}
}
static class Create extends Message {
Create(String id) {
super(id);
}
}
static class Read extends Message {
Read(String id) {
super(id);
}
}
static class ParentOrderActor extends UntypedActor {
@Override
public void preStart() {
log.debug("ParentOrderActor -> starting...");
}
@Override
public void onReceive(Object message) throws Exception {
log.debug("ParentOrderActor -> received: {}", message);
if(message instanceof Create) {
if(createChildrenInUser) {
// Create an Actor in the root context ("/user/id").
ActorRef created = getContext().system().actorOf(new Props(OrderActor.class), ((Create)message).id);
log.debug("ParentOrderActor -> created: {}", created);
}
else {
// Create an Actor in this ParentOrderActor's context ("/user/orders/id")
ActorRef created = getContext().actorOf(new Props(OrderActor.class), ((Create)message).id);
log.debug("ParentOrderActor -> created: {}", created);
}
}
else if(message instanceof Read) {
ActorRef ref = select((Message)message);
log.debug("ParentOrderActor -> for id={}, we retrieved ActorRef: {}", message, ref);
getContext().system().shutdown();
}
else {
unhandled(message);
}
}
private ActorRef select(Message message) {
try {
ActorSelection actorSelection;
if(createChildrenInUser) {
// Select the Actor with a certain id in the root context ("/user/id").
actorSelection = getContext().system().actorSelection("/user/" + message.id);
}
else {
// Create an Actor in this ParentOrderActor's context ("/user/orders/id")
actorSelection = getContext().actorSelection(message.id);
}
log.debug("ParentOrderActor -> actorSelection: {}", actorSelection);
Timeout timeout = new Timeout(2, TimeUnit.SECONDS);
AskableActorSelection askableActorSelection = new AskableActorSelection(actorSelection);
Future<Object> future = askableActorSelection.ask(new Identify(1), timeout);
ActorIdentity actorIdentity = (ActorIdentity) Await.result(future, timeout.duration());
return actorIdentity.getRef();
}
catch(Exception e) {
log.debug("Oops: {}", e);
return null;
}
}
}
static class OrderActor extends UntypedActor {
@Override
public void preStart() {
log.debug("OrderActor -> starting...");
}
@Override
public void onReceive(Object message) throws Exception {
log.debug("OrderActor -> received: {}", message);
unhandled(message);
}
}
public static void main(String[] args) {
ActorSystem system = ActorSystem.create("system");
log = Logging.getLogger(system, Main.class);
ActorRef orders = system.actorOf(new Props(ParentOrderActor.class).withRouter(new RoundRobinRouter(1)), "orders");
orders.tell(new Create("ABC"), orders);
orders.tell(new Read("ABC"), orders);
}
}
Running the code above will cause the Read
to return an ActorRef
which is null
. Changing the boolean flag createChildrenInUser
to true
will create the child actors inside "/user"
and all goes well.
The question: how do I create an ActorSelection
and get an ActorRef
of an actor that is created inside /user/orders
(ParentOrderActor
)?
Upvotes: 2
Views: 4526
Reputation: 35443
If I understand your problem correctly and you want the best way to load a child actor from within the parent actor, then you can use:
getContext().child("ABC")
That will return a scala.Option<ActorRef>
though and not an ActorSelection
, so if you wanted to lookup multiple children with wildcarding and send them all a message, this approach will not work.
Upvotes: 5