I am new to Akka and am not familiar with Scala at all. I am trying to use the Akka+Java for running a project on multiple machines. I was able to successfully run Akka Java examples locally but when I tried to run any of them on two machine, the code stops working. I've looked at the Akka-Sample-Remote source code as well. It also works on a single machine but breaks when used on more than one machine. I think the problem is with the way I set the configurations. I have summarized the problem into a simple HelloWorld problem as below.
There code is divided into two projects with two separate configuration files: a Hello Actor project and a World Actor project. World Actor waits for receiving the Hello Message from the Hello Actor and then prints out the "Hello World". Here below you can see the code and the configuration for these two projects. As you can see, the World Actor is started on port 1719 and the Hello Actor starts on port 1720 and tries to connect to the World Actor using "akka.tcp://WorldApplication#192.27.336.187:1719/user/WorldActor" . Any idea on what is wrong with he code/configuration?
JWorld.java:
public class JWorld {
public static void main(String[] args) {
JWorldApplication app = new JWorldApplication();
System.out.println("Started World Application - waiting for Hello message");
}
}
JWorldApplication.java:
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import com.typesafe.config.ConfigFactory;
public class JWorldApplication {
private ActorSystem system;
public JWorldApplication() {
system = ActorSystem.create("WorldApplication", ConfigFactory.load()
.getConfig("WorldConfig"));
ActorRef actor = system.actorOf(new Props(JWorldActor.class),
"WorldActor");
}
}
JWolrdActor.java:
import akka.actor.UntypedActor;
public class JWorldActor extends UntypedActor {
#Override
public void onReceive(Object message) {
if (message instanceof HelloMessage) {
HelloMessage recMsg = (HelloMessage) message;
System.out.println("Received Message: " + recMsg.getText());
System.out.println("***** Hello World! ******" );
} else {
System.out.println("UnHandled Message Received" );
unhandled(message);
}
}
}
HelloMessage.java:
import akka.actor.ActorRef;
public class HelloMessage{
private ActorRef receiver;
private String text;
HelloMessage() {}
HelloMessage(ActorRef receiver){ this.receiver = receiver;}
public ActorRef getReceiver(){ return receiver;}
public void setText(String text) { this.text = text;}
public String getText() {return text;}
}
Application.conf:
WorldConfig {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
netty.tcp {
hostname="192.27.336.187"
port=1719
}
}
}
JHello.java:
public class JHello {
public static void main(String[] args) {
JHelloApplication testApp = new JHelloApplication();
System.out.println("Started Hello Application - Sending Hello Message");
testApp.sayHello();
}
}
JHelloApplication.java:
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import com.typesafe.config.ConfigFactory;
public class JHelloApplication {
private ActorSystem system;
private ActorRef remoteActor, myActor;
public JHelloApplication() {
system = ActorSystem.create("HelloApplication", ConfigFactory.load()
.getConfig("HelloConfig"));
myActor = system.actorOf(new Props(JHelloActor.class),"HelloActor");
remoteActor = system
.actorFor("akka.tcp://WorldApplication#192.27.336.187:1719/user/WorldActor");
}
public void sayHello() {
myActor.tell(new HelloMessage(remoteActor));
}
}
JHelloActor.java:
import akka.actor.ActorRef;
import akka.actor.UntypedActor;
public class JHelloActor extends UntypedActor {
#Override
public void onReceive(Object message) {
if (message instanceof HelloMessage) {
HelloMessage msg = (HelloMessage) message;
if (msg.getReceiver() !=null){
msg.setText("Hello");
msg.getReceiver().tell(msg, getSelf());
}
} else {
System.out.println("UnHandled Message Received" );
unhandled(message);
}
}
}
application.conf:
HelloConfig {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
netty.tcp {
hostname="192.27.336.187"
port=1720
}
}
}
As mentioned by Patrcik the question is finally answered by Patrik on Akka groups. The problem was that the Akka{} tag is missing in both configuration files. Adding this tag solves the problem.
Related
I am using IntelliJ as my IDE and the code below runs alright if they are in the same src folder. However, what I want is to call the sayHello() method in another project. Is that possible? I thought this is possible since this is what RMI enables, but am I wrong?
I tried to create another project that contains a Main java class and has the same code as the Client Test Drive below, hoping to call the sayHello() method by utilizing Naming.lookup() but it doesn't work! If I try to run it, I was given a java.rmi.UnmarshalException: error unmarshalling return; exception. What am I missing?
How can I call the sayHello() method "remotely"?
Remote Interface:
package Remote;
import java.rmi.*;
public interface HelloRemote extends Remote {
String sayHello() throws RemoteException;
}
Remote Implementation
package Remote;
import java.rmi.*;
import java.rmi.registry.*;
import java.rmi.server.*;
public class HelloRemoteImpl extends UnicastRemoteObject implements HelloRemote{
public HelloRemoteImpl() throws RemoteException {};
#Override
public String sayHello() throws RemoteException {
return "Server says, \"Hello!\"";
}
public static void main(String[] args) {
try {
// 2.3 register the service
HelloRemote service = new HelloRemoteImpl();
final int PORT = 1888;
Registry registry = LocateRegistry.createRegistry(PORT);
registry.rebind("hello", service);
System.out.println("Service running on PORT: " + PORT);
} catch(Exception e) {
e.printStackTrace();
}
}
}
Client Test Drive
package Remote;
import java.rmi.Naming;
public class SayHelloTest {
public static void main(String[] args) {
try {
HelloRemote service = (HelloRemote) Naming.lookup("rmi://127.0.0.1:1888/hello");
String helloStr = service.sayHello();
System.out.println(helloStr);
} catch (Exception e) {
e.printStackTrace();
}
}
}
So I'm having a problem testing my networking, there's a lot of code below but this is as small as I could make an example. The problem I'm having is that it seems the listener I'm registering with the server and client never gets called when when I send a message.
lock.await(5000, TimeUnit.MILLISECONDS) should wait until the message is received or timeout at 5 seconds. However the listener should tell the lock to continue lock.countDown() after it sets the response variable that prevents the test from failing.
As you can guess, this doesn't happen, the message gets sent, the lock continues due to timeout and the test fails because response is null.
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryonet.Client;
import com.esotericsoftware.kryonet.Connection;
import com.esotericsoftware.kryonet.Listener;
import com.esotericsoftware.kryonet.Server;
import com.esotericsoftware.minlog.Log;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static junit.framework.TestCase.fail;
public class TestMessage {
private Server server;
private Client client;
private String response;
private CountDownLatch lock = new CountDownLatch(1);
#Before
public void setUp() {
Log.set(Log.LEVEL_DEBUG);
server = new Server();
try {
Log.debug("Binding to port: " + 30454 + "... ");
server.bind(30454);
Log.debug("Bound to port" + 30454);
} catch (IOException e) {
Log.debug("failed");
Log.error("Unable to bind to port: " + e.getMessage());
server.stop();
fail("Unable to start server");
}
Kryo kryo = server.getKryo();
kryo.register(Message.class);
Log.debug("Adding server listener");
server.addListener(new TestListener());
Log.debug("Starting server... ");
server.start();
Log.debug("Server started successfully");
}
#Test
public void testPacketSending() throws IOException, InterruptedException {
client = new Client();
client.addListener(new TestListener());
Kryo kryo = client.getKryo();
kryo.register(Message.class);
client.start();
client.connect(5000, "127.0.0.1", 30454);
client.sendTCP(new Message("RECEIVED"));
lock.await(5000, TimeUnit.MILLISECONDS);
Assert.assertNotNull(response);
}
private class TestListener extends Listener {
#Override
public void received(Connection connection, Object o) {
Message m = (Message) o;
response = m.message;
Log.debug(m.message);
lock.countDown();
}
}
private class Message {
String message;
Message(String message) {
this.message = message;
}
}
}
The issue here is this:
00:00 ERROR: [kryonet] Error reading TCP from connection: Connection 1
com.esotericsoftware.kryonet.KryoNetException: Error during deserialization.
at com.esotericsoftware.kryonet.TcpConnection.readObject(TcpConnection.java:141)
at com.esotericsoftware.kryonet.Server.update(Server.java:205)
at com.esotericsoftware.kryonet.Server.run(Server.java:372)
at java.lang.Thread.run(Thread.java:745)
Caused by: com.esotericsoftware.kryo.KryoException: Class cannot be created (non-static member class): org.glytching.sandbox.kryo.TestMessage$MessageA
at com.esotericsoftware.kryo.Kryo$DefaultInstantiatorStrategy.newInstantiatorOf(Kryo.java:1308)
at com.esotericsoftware.kryo.Kryo.newInstantiator(Kryo.java:1127)
at com.esotericsoftware.kryo.Kryo.newInstance(Kryo.java:1136)
at com.esotericsoftware.kryo.serializers.FieldSerializer.create(FieldSerializer.java:562)
at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:538)
at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:816)
at com.esotericsoftware.kryonet.KryoSerialization.read(KryoSerialization.java:55)
at com.esotericsoftware.kryonet.TcpConnection.readObject(TcpConnection.java:139)
... 3 more
Kryonet cannot deserialize into Message because (a) it is a non static inner class and (b) it has no public zero-arg constructor. FWIW, there is some background here on why Kryonet does not support serialisation of non static inner classes.
If you (a) refactor Message into its own class or make it static and (b) give it a zero-arg constructor then your test will pass.
The smallest change necessary to make your test pass is to replace this ...
private class Message {
String message;
Message(String message) {
this.message = message;
}
}
... with this:
private static class Message {
String message;
Message() {
}
Message(String message) {
this.message = message;
}
}
Re this:
there's a lot of code below but this is as small as I could make an example
The reproduction case in your question was more than adequate, thanks.
I´m very new to Akka. I have created a simple Hello World App using it.
The App is very simple, it sends a message to my simple Actor. What I want is to send a message back to the first sender of the message. I can´t get the return message. How can someone do that? Do the client have to implement a onReceive method? I have tried to comment in the code.
import akka.actor.UntypedActor;
public class HelloActor extends UntypedActor {
#Override
public void onReceive(Object o) throws Exception {
if(o instanceof String){
String message = (String)o;
System.out.println(message);
// how to get this response ?
getSender().tell("World",getSelf());
}
}
}
The Client
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
public class Client{
public static void main(String[] args){
ActorSystem actorSystem = ActorSystem.create("HelloWorldSystem");
ActorRef listener = actorSystem.actorOf(new Props(HelloActor.class), "listener");
// sending is OK but how to get the response?
listener.tell("Hello");
}
}
The correct answer is to use a Future:
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.dispatch.*;
import akka.dispatch.Future;
import akka.pattern.Patterns;
import akka.util.Duration;
import akka.util.Timeout;
public class Client{
public static void main(String[] args){
ActorSystem actorSystem = ActorSystem.create("HelloWorldSystem");
ActorRef listener = actorSystem.actorOf(new Props(HelloActor.class), "listener");
Timeout timeout = new Timeout(Duration.create(5, "seconds"));
Future<Object> future = Patterns.ask(listener, "Hello", timeout);
try{
String result = (String) Await.result(future, timeout.duration());
System.out.println(result);
}catch (Exception e){
e.printStackTrace();
}
}
}
I have a play framework that uses the play actor integration to reference and communicate with a remote akka system. Looks like I am not getting the remote referencing right. First the remote akka implements the bootable interface and It has a master node that creates a child actor system.
the play actor system then references the remote system. the code snippets are presented below.
this is the Play framework node that creates the local actor system
public void connectMaster (final String classname)
{
localActor.tell(classname);
}
public void connectMaster ()
{
//localActor.tell(getByte(new Coordinates()));
}
public void connectMaster (final WebSocket.In<JsonNode> in, final WebSocket.Out<JsonNode> out )
{
in.onMessage(new Callback<JsonNode>() {
public void invoke(JsonNode event) throws JsonParseException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
#SuppressWarnings("unchecked")
Map<String,ArrayList<Object>> jsonMap = mapper.readValue(event, Map.class);
GesturePoints gp = new GesturePoints();
gp.setPoints(jsonMap);
localActor.tell(gp);
}
}); }
this is the local Actor system in play framework
package controllers;
import Com.RubineEngine.GesturePoints.*;
import akka.actor.ActorRef;
import akka.actor.UntypedActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
public class LocalActor extends UntypedActor {
/**
*
*/
ActorRef masterActor; // = getContext().actorFor("akka://MasterNode#127.0.0.1:2552/user/masterActor");
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
#Override
public void onReceive(Object arg) throws Exception {
System.out.println(" Local Actor 1");
if(arg instanceof GesturePoints)
{ System.out.println(" local Actor 2");
masterActor.tell(arg , getSelf());
System.out.println(" Local Actor 3");}
else
{unhandled(arg);}
}
public void preStart()
{
masterActor = getContext().actorFor("akka://MasterNode#127.0.0.1:2552/user/masterActor");
}
}
this is the remote akka system master Node that creates the master actor
package Rubine_Cluster;
import java.util.Arrays;
import com.typesafe.config.ConfigFactory;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.kernel.Bootable;
/**
* Hello world!
*
*/
public class MasterNode implements Bootable
{
final ActorSystem system;
ActorRef masterActor;
public MasterNode() {
//Create a child actor of this actor upon initialization
system = ActorSystem.create("MasterNode", ConfigFactory.load()
.getConfig("masterNode"));
masterActor = system.actorOf(new Props(MasterActor.class),"masterActor");
}
public void startup() {
}
public void shutdown() {
system.shutdown();
}
}
this is the remote actor system created by the MasterNode
public class MasterActor extends UntypedActor {
/**
*
*/
ActorSystem system = ActorSystem.create("container");
ActorRef worker1;
//public MasterActor(){}
#Override
public void onReceive(Object message) throws Exception {
System.out.println(" Master Actor 5");
if(message instanceof GesturePoints)
{ //GesturePoints gp = (GesturePoints) message;
System.out.println(" Master Actor 1");
try { worker1.tell(message, getSelf());
System.out.println(" Master Actor 2");
} catch (Exception e) {
getSender().tell(new akka.actor.Status.Failure(e), getSelf());
throw e;
}
}
else{ unhandled(message);}
}
public void preStart()
{
worker1 = getContext().actorFor("akka://WorkerNode#127.0.0.1:2553/user/workerActor");
}
}
I think I got the referencing wrong or probably were to send the message any suggestion is welcome
I am trying to use Akka future with play framework to connect to a remote akka system
. After running the system the akka future gives me a warning that one argument is left.
the code are below :
this is the [lay controller code:
p
ackage controllers;
import com.typesafe.config.ConfigFactory;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import play.*;
import play.mvc.*;
import views.html.*;
public class Application extends Controller {
public static Result index() throws InterruptedException {
System.out.println(" Local Node Called0");
ActorSystem csystem = ActorSystem.create("Application", ConfigFactory.load().getConfig("LocalNode"));
ActorRef localNode = csystem.actorOf(new Props(LocalNode.class));
System.out.println(" Local Node Called1");
localNode.tell("Hello");
System.out.println(" Local Node Called2");
Thread.sleep(5000);
csystem.shutdown();
return ok(index.render("I am OK"));
}
}
this is the play framework local actor node
package controllers;
import akka.actor.;
import akka.dispatch.Await;
import akka.dispatch.Future;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.util.Duration;
import akka.util.Timeout;
import akka.pattern.;
public class LocalNode extends UntypedActor {
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
Timeout timeout = new Timeout(Duration.parse("20 seconds"));
ActorRef masterActor;
public void preStart()
{
/* Get reference to Master Node*/
masterActor =
getContext().actorFor("akka://MasterNode#127.0.0.1:2552/user/masterActor");
}
#Override
public void onReceive(Object message) throws Exception {
System.out.println(" Future called ");
Future<Object> future = Patterns.ask(masterActor , message.toString(), timeout);
String result = (String) Await.result(future, timeout.duration());
log.info("Messagefrom Server", result.toString());
}
}
this is the remote akka system master nide
package Rubine_Cluster;
import com.typesafe.config.ConfigFactory;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.kernel.Bootable;
/**
* Hello world!
*
*/
public class MasterNode implements Bootable
{
final ActorSystem system;
public MasterNode() {
system = ActorSystem.create("MasterNode", ConfigFactory.load()
.getConfig("masterNode"));
ActorRef masterActor = system.actorOf(new Props(MasterActor.class),"masterActor");
System.out.println(" Master Node is called ");
}
public void startup() {
}
public void shutdown() {
system.shutdown();
}
}
this is the akka remote system MasterActor
package Rubine_Cluster;
import akka.actor.*;
public class MasterActor extends UntypedActor {
#Override
public void onReceive(Object message) throws Exception {
if (message instanceof String) {
// Get reference to the message sender and reply back
getSender().tell(message + " got something");
}
}
}
this is the message from the play framewrok
[INFO] [09/30/2012 16:47:25.669] [Application-akka.actor.default-dispatcher-1] [
akka://Application/user/$a] Messagefrom Server WARNING arguments left: 1
help is needed cos I am suppose to meet the assignment deadline
thanks to all
The "arguments left: 1" warning is due to the following line:
log.info("Messagefrom Server", result.toString())
You're passing unused params to the logger. It should be this instead:
log.info("Messagefrom Server {}", result.toString())
Try to give a name to your actor in Play:
ActorRef localNode = csystem.actorOf(new Props(LocalNode.class), "localNode");
And also note that if you need to access the Akka system from Play, it is better to use:
ActorSystem csystem = Akka.system();
instead of:
ActorSystem csystem = ActorSystem.create("Application", ConfigFactory.load().getConfig("LocalNode"));