I am new to Akka and I am trying to start an Actor and send messages to that actor from various other actors. The receiver is called Hero and the senders are Messengers
Here is my Hero class
import akka.actor.UntypedActor;
public class Hero extends UntypedActor {
#Override
public void onReceive(Object arg0) throws Exception {
System.out.println("Received = " + arg0);
}
}
I start the Hero using the below code
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
public class TestHero {
public static void main(String[] args) {
ActorSystem system = ActorSystem.create("testHero");
ActorRef master = system.actorOf(Props.create(Hero.class), "master");
master.tell("I am here", ActorRef.noSender());
}
}
When I run the above code I get the message "Received = I am Here".
Now I have my Messenger class's constructor as follows
private static ActorRef hero;
public Messenger() {
ActorSelection master = context().actorSelection("akka://localhost/user/serviceA/master");
hero = master.anchor();
}
When I print the hero object it is always null. What am I missing here? Is this the right way to search for an Actor. The point is these 2 actors will be running in 2 different JVMs.
I enabled remoting and modified the Messenger class as below.
import akka.actor.ActorRef;
import akka.actor.ActorSelection;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.actor.UntypedActor;
public class Messenger extends UntypedActor {
private static ActorRef hero;
public Messenger() {
ActorSelection master = context().actorSelection("akka.tcp://testHero#127.0.0.1:2552/user/master");
System.out.println(master);
hero = master.anchor();
}
#Override
public void onReceive(Object arg0) throws Exception {
System.out.println("msg = " + arg0);
}
public static void main(String[] args) {
ActorSystem system = ActorSystem.create("test");
ActorRef actor = system.actorOf(Props.create(Messenger.class), "msgnr");
System.out.println(actor.getClass() + " >> " + actor);
System.out.println(hero);
actor.tell("Hi", hero);
}
}
The output is given below
class akka.actor.RepointableActorRef >> Actor[akka://test/user/msgnr#-975452280]
null
ActorSelection[Anchor(akka://test/deadLetters), Path(/user/master)]
msg = Hi
How to wait till the Messenger is created so that hero actor ref is instantiated?
I solved the issue by adding an application.conf file to the Messenger also.
Related
I'm attempting to follow best practices for sending messages between Akka actors and reading the following :
https://github.com/akka/akka/blob/v2.6.15/akka-docs/src/test/java/jdocs/actor/ActorDocTest.java#L101-L129
https://doc.akka.io/docs/akka/current/actors.html
It seems best practice is to define a static class that contains the message to be sent.
From reading the docs I've defined the following :
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.persistence.AbstractPersistentActor;
class RecoveryActor extends AbstractPersistentActor {
public static class Msg1 {}
public static class Msg2 {}
private final LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
/**
* Handler that will be called on recovery.
* #return
*/
#Override
public Receive createReceiveRecover() {
return receiveBuilder()
.match(Msg1.class, this::receiveMsg1)
.match(Msg2.class, this::receiveMsg2)
.build();
}
private void receiveMsg1(Msg1 msg) {
log.info("in receive message 1");
}
private void receiveMsg2(Msg2 msg) {
log.info("in receive message 2");
}
/**
* The normal receive method.
* #return
*/
#Override
public Receive createReceive() {
return receiveBuilder()
.match(String.class, s -> s.equals("cmd"), s -> persist("evt", this::handleEvent))
.build();
}
private void handleEvent(String event) {
System.out.println("in handle event");
}
#Override
public String persistenceId() {
return "simple-accountant"; //best practice, the id should be unique.
}
}
But I'm unsure how to send messages using the Msg1 and Msg2
I've defined a new class to send a message :
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
public class ActorMessaging {
public static void main(String args[]){
final ActorSystem system = ActorSystem.create("example");
final ActorRef actor = system.actorOf(Props.create(RecoveryActor.class));
actor.tell(RecoveryActor.Msg1);
}
}
But it fails with compiler error :
java: cannot find symbol
symbol: variable Msg1
location: class recoverydemo.RecoveryActor
How can I send messages to the Actor of type Msg1?
Is this best practice for sending messages between actors, wrapping the messages in a custom class ?
actor.tell is accepting object. RecoveryActor.Msg1 is not that, it's not even class reference. How about trying to send instance of Msg1 like this:
actor.tell(new RecoveryActor.Msg1(), actorRef)
In order to "talk" to remote actor, you first need to obtain reference to it. That's usually done by ActorSystem
Silly mistake on my part. I overrode the incorrect method. I should have overridden createReceive instead :
public Receive createReceive() {
return receiveBuilder()
.match(RecoveryActor.Msg1.class, this::receiveMsg1)
.match(Msg2.class, this::receiveMsg2)
.build();
// return receiveBuilder()
// .match(String.class, s -> s.equals("cmd"), s -> persist("evt", this::handleEvent))
// .build();
}
I'm starting to code my own minecraft 1.14.4 mod. I want to create a very simple mod which will just display a clok showing the in-game time (I couldn't find a similar updated mod, since all other mods show the real-time time, not the in-game time). I took the example mod which comes with Forge, and added some custom code.
When I load the mod, the player turns invisible and immobile. He is there, I can look around and dig and so on, but I can not see him nor move.
If I suppress my custom code, it works... very weird to me.
this is my code so far (it does not show anything since I am stuck in this problem).
package com.alef.simpleclock;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.InterModComms;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent;
import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent;
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.stream.Collectors;
// The value here should match an entry in the META-INF/mods.toml file
#Mod("simpleclock")
public class SimpleClock
{
// Directly reference a log4j logger.
private static final Logger LOGGER = LogManager.getLogger();
public static boolean startTimer;
public static int tick;
public SimpleClock() {
// Register the setup method for modloading
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup);
// Register the enqueueIMC method for modloading
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::enqueueIMC);
// Register the processIMC method for modloading
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::processIMC);
// Register the doClientStuff method for modloading
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::doClientStuff);
// Register ourselves for server and other game events we are interested in
MinecraftForge.EVENT_BUS.register(this);
}
private void setup(final FMLCommonSetupEvent event)
{
// some preinit code
LOGGER.info("HELLO FROM PREINIT");
LOGGER.info("DIRT BLOCK >> {}", Blocks.DIRT.getRegistryName());
}
private void doClientStuff(final FMLClientSetupEvent event) {
// do something that can only be done on the client
LOGGER.info("Got game settings {}", event.getMinecraftSupplier().get().gameSettings);
}
private void enqueueIMC(final InterModEnqueueEvent event)
{
// some example code to dispatch IMC to another mod
InterModComms.sendTo("examplemod", "helloworld", () -> { LOGGER.info("Hello world from the MDK"); return "Hello world";});
}
private void processIMC(final InterModProcessEvent event)
{
// some example code to receive and process InterModComms from other mods
LOGGER.info("Got IMC {}", event.getIMCStream().
map(m->m.getMessageSupplier().get()).
collect(Collectors.toList()));
}
// You can use SubscribeEvent and let the Event Bus discover methods to call
#SubscribeEvent
public void onServerStarting(FMLServerStartingEvent event) {
// do something when the server starts
LOGGER.info("HELLO from server starting");
}
// You can use EventBusSubscriber to automatically subscribe events on the contained class (this is subscribing to the MOD
// Event bus for receiving Registry Events)
#Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD)
public static class RegistryEvents {
#SubscribeEvent
public static void onBlocksRegistry(final RegistryEvent.Register<Block> blockRegistryEvent) {
// register a new block here
LOGGER.info("HELLO from Register Block");
}
}
#EventBusSubscriber
public static class showClock
{
#SubscribeEvent
public static void onJoin(final EntityJoinWorldEvent event)
{
if (event.getEntity() != null && event.getEntity() instanceof ClientPlayerEntity)
{
LOGGER.info("WELCOME " + event.getEntity().getName() + "!!!");
event.setCanceled(true);
if (!SimpleClock.startTimer)
{
SimpleClock.startTimer = true;
}
}
}
#SubscribeEvent
public static void timer(final TickEvent.WorldTickEvent event)
{
if (SimpleClock.startTimer)
{
if (SimpleClock.tick >= 1000)
{
SimpleClock.tick = 0;
drawClock(event.world);
}
else
{
SimpleClock.tick++;
}
}
}
private static void drawClock(World world)
{
int time = (int) world.getWorldInfo().getDayTime() % 1000;
LOGGER.info(time);
}
}
}
Any help is welcome!!!
I have the following piece of code taken from the official Play 2.6 docs that define a client class,
import javax.inject.Inject;
import play.libs.ws.*;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.CompletionStage;
public class MyClient implements WSBodyReadables, WSBodyWritables {
private WSClient ws;
#Inject
public MyClient(WSClient ws) {
this.ws = ws;
sendRequest();
}
public void sendRequest() {
WSRequest request = ws.url("http://example.com");
WSRequest complexRequest = request.addHeader("headerKey", "headerValue")
.setRequestTimeout(Duration.of(1000, ChronoUnit.MILLIS))
.addQueryParameter("paramKey", "paramValue");
CompletionStage<? extends WSResponse> responsePromise = complexRequest.get();
}
}
Now I have a socket actor that handles incoming socket messages. I want to fire an HTTP request every time a new message comes through the socket.
My problem is I don't know how to initialize MyClient class to use its sendRequest method.
public void onReceive(Object message) throws Exception {
if (message instanceof String) {
out.tell("I received your message: " + message, self());
MyClient a = new MyClient(); //problem here
}
}
You must inject it, the same way you did in MyClass:
public class MyActor extends UntypedAbstractActor {
private MyClient client;
#Inject
public MyActor(MyClient client) {
this.client = client;
}
#Override
public void onReceive(Object message) throws Exception {
client.sendRequest();
}
}
If there is some problem that dont let you use this (ex you dont control how the actor is created), add more info to your question.
I have an actor class EmployeeActor, inside that actor, some other actor is fired using payrollRunActor.tell(). I need to write a JUnit test for EmployeeActor.java, but I don't want to fire payrollRunActor.tell(), means I want to mock it.
Is there a way to do it? I tried a lot, but real payrollRunActor is getting fired.
Here is the actual code of my EmployeeActor class.
package com.test.periodic.actors;
import java.util.List;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.test.avs.domain.boundedcontext.Employee;
import com.test.avs.domain.boundedcontext.PayrollRun;
import com.test.entity.BusinessDTO;
import com.test.periodic.actors.aggregrators.EmployeeAggregator;
import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.routing.RoundRobinPool;
public class EmployeeActor extends AbstractActor {
private static final Logger logger = LoggerFactory.getLogger(EmployeeActor.class);
private boolean rollup;
public static Props props() {
return Props.create(EmployeeActorTest.class);
}
private List<PayrollRun> payrollRuns;
private String instanceId;
private String employeeAggregatorId;
private Employee employee;
private ActorRef organizationAggregatorActor;
private List<BusinessDTO> businessDTOs;
final ActorSystem payrollRunSystem = ActorSystem.create("payrollRun");
ActorRef employeeAggregator;
public EmployeeActor(ActorRef organizationAggregatorActor, List<PayrollRun> payrollRuns,
Employee employee, List<BusinessDTO> businessDTOs, boolean rollup) {
this.payrollRuns = payrollRuns;
this.employee = employee;
this.organizationAggregatorActor = organizationAggregatorActor;
this.businessDTOs = businessDTOs;
this.rollup = rollup;
}
#Override
public void preStart() throws Exception {
instanceId = RandomStringUtils.randomAlphanumeric(6);
employeeAggregatorId = "employeeAggregator-" + instanceId;
employeeAggregator = getContext().system().actorOf(
Props.create(EmployeeAggregator.class, organizationAggregatorActor, employee),
employeeAggregatorId);
super.preStart();
}
#Override
public Receive createReceive() {
return receiveBuilder().match(Employee.class, employee -> {
if (rollup) {
logger.info("Rollingup business entities.");
employeeAggregator.tell(employee, getSelf());
} else {
ActorRef payrollRunActor = payrollRunSystem.actorOf(new RoundRobinPool(payrollRuns.size())
.props(Props.create(PayrollRunActor.class, employeeAggregator, employee, businessDTOs)));
for (PayrollRun payrollRun : payrollRuns) {
**payrollRunActor.tell(payrollRun, getSelf());**
}
}
}).match(PayrollRun.class, maxPaydatePayrollRun -> {
ActorRef payrollRunActor = payrollRunSystem
.actorOf(Props.create(PayrollRunActor.class, employeeAggregator, employee, businessDTOs));
**payrollRunActor.tell(maxPaydatePayrollRun, getSelf());**
}).build();
}
}
First of all you would have to mock the static method call which is invoked during the creation of class under test. Then make it return a spied object and mock the method you want to avoid calling:
#RunWith(PowerMockRunner.class)
#PrepareForTest(ActorSystem.class)
public void TestClass{
#Test
public void test(){
// Arrange
PowerMockito.mockStatic(ActorSystem.class);
ActorSystem actorSystemMock = Mockito.mock(ActorSystem.class);
Actor actorSpy = Mockito.spy(new Actor());
Mockito.when(ActorSystem.create("payrollRun")).thenReturn(actorSystemSpy);
Mockito.when(actorSystemMock.actorOf(any(RoundRobinPool.class)))
.thenReturn(actorSpy);
Mockito.doNothing().when(actorSpy)
.tell(Mockito.any(PayrollRun.class), Mockito.any(Self.class));
EmployeeActor employeeActor = new EmployeeActor();
// Act and assert...
employeeActor.createReceive();
}
}
Remember that all other methods of actorSystemSpy will be called will real implementation. If you want to mock all of them then use Mockito.mock instead of spy.
Akka 2.4.1 Java API here. I don't have the bandwidth right now to learn Scala, so I would ask that code examples here also use the Java API.
I have an existing ActorSystem that is chock full of asynchronous actors and which works beautifully. I now have a need to reuse this actor system in a synchronous context like so:
// Groovy pseudo-code; NOT inside the actor system here!
ComputationRequest request = new ComputationRequest(1, 7, true)
MasterActor master = actorSystem.get(...)
ComputationResult result = actorSystem.tell(master, request)
Nowhere in Akka's documentation do I see a clear example of sending equests into an actor system (from the outside) and then retrieving the results. Could I use Futures here perhaps? What's the standard way of handling this pattern in Akka (code samples)?
There is the ask pattern does what you want. It expects the target actor to "return" a response by telling it to getSender(). You'll get a Future for the response and can work with that (blocking, if you must).
import akka.dispatch.*;
import scala.concurrent.ExecutionContext;
import scala.concurrent.Future;
import scala.concurrent.Await;
import scala.concurrent.Promise;
import akka.util.Timeout;
ComputationRequest request = new ComputationRequest(1, 7, true);
MasterActor master = actorSystem.get(...);
Timeout timeout = new Timeout(Duration.create(5, "seconds"));
Future<Object> future = Patterns.ask(master, request, timeout);
ComputationResult result = (ComputationResult) Await.result(future, timeout.duration());
You can inject a callback, wrapped into an actor, into akka system and use it as a "sender" with tell mechanics. Full example:
import akka.actor.*;
import akka.dispatch.Await;
import akka.dispatch.Future;
import akka.pattern.Patterns;
import akka.util.Timeout;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
public class Main {
public static void main(String[] args) throws Exception {
// init your akka system
final ActorSystem system = ActorSystem.create("Cambria");
final ActorRef worker = system.actorOf(new Props(Worker.class), "worker");
worker.tell(new Work(10));
// make callback
final Consumer<Object> callback = a -> System.out.println("from the other side: " + a);
// wrap call back into sub-actor
class TheSpy extends UntypedActor {
#Override
public void onReceive(final Object message) throws Exception {
callback.accept(message);
}
}
// inject callback into the system
final ActorRef theSpy = system.actorOf(new Props(TheSpy::new), "spy");
// find actor you want to hack
final ActorSelection found = system.actorSelection("/user/worker");
// send it a message and observe using callback)
found.tell(new Work(20), theSpy);
final Timeout timeout = new Timeout(5, TimeUnit.SECONDS);
final Future<Object> future = Patterns.ask(worker, new Work(30), timeout);
final Work result = (Work) Await.result(future, timeout.duration());
System.out.println(result);
system.shutdown();
system.awaitTermination();
}
}
public class Worker extends UntypedActor {
public void onReceive(Object message) {
if (message instanceof Work) {
Work work = (Work) message;
System.out.println("work = " + work);
getSender().tell(new Work(work.getStart() + 1));
} else {
unhandled(message);
}
}
}
public class Work {
private final int start;
public Work(int start) {
this.start = start;
}
public int getStart() {
return start;
}
#Override
public String toString() {
return "Work{" +
"start=" + start +
'}';
}
}