I'm tring to set-up Akka actors to deal with web-socket in play.
I've defined a simple actor to send out messages trough web socket:
package actors;
import akka.actor.*;
public class MyWebSocketActor extends UntypedActor {
public static Props props(ActorRef out) {
return Props.create(MyWebSocketActor.class, out);
}
private final ActorRef out;
public MyWebSocketActor(ActorRef out ) {
this.out = out;
}
public void onReceive(Object message) throws Exception {
if (message instanceof String) {
out.tell(message, self());
}
}
}
In the Application controller I've got the web-socket:
package controllers;
import play.*;
import play.mvc.*;
import akka.util.*;
import views.html.*;
import actors.MyWebSocketActor;
import play.libs.F.*;
public class Application extends Controller {
public static Result index() {
return ok(index.render("Your new application is ready."));
}
public static WebSocket<String> socket() {
return WebSocket.withActor(MyWebSocketActor::props);
}
}
Until now everything works as I expected. Now, in a controller I'm trying to pass a message to that actor. I've try with:
public class Messages extends Controller {
private static final Form<Message> messageForm = Form.form(Message.class);
@BodyParser.Of(BodyParser.Json.class)
public static Result list(Integer page) {
// --- Render
ActorRef myActor = Akka.system().actorOf(Props.create(MyWebSocketActor.class));
String message = "test";
myActor.tell(message, ActorRef.noSender());
//
}
But Here comes the error: "Cannot invoke the action, eventually got an error: java.lang.IllegalArgumentException: no matching constructor found on class actors.MyWebSocketActor for arguments[].
2014-08-05 05:50:23,503 - [INFO] - from play in pool-4-thread-4
Listening for HTTP on /0:0:0:0:0:0:0:0:9000
2014-08-05 05:51:02,887 - [ERROR] - from application in New I/O worker #1
! @6j5b25956 - Internal server error, for (GET) [/] ->
play.PlayExceptions$CompilationException: Compilation error[error: method tell in class ActorRef cannot be applied to given types;]
at play.PlayExceptions$CompilationException$.apply(PlayExceptions.scala:27) ~[na:na]
at play.PlayExceptions$CompilationException$.apply(PlayExceptions.scala:27) ~[na:na]
at scala.Option.map(Option.scala:145) ~[scala-library-2.11.1.jar:na]
at play.PlayReloader$$anon$1$$anonfun$play$PlayReloader$$anon$$taskFailureHandler$1.apply(PlayReloader.scala:297) ~[na:na]
at play.PlayReloader$$anon$1$$anonfun$play$PlayReloader$$anon$$taskFailureHandler$1.apply(PlayReloader.scala:292) ~[na:na]
at scala.Option.map(Option.scala:145) ~[scala-library-2.11.1.jar:na]
at play.PlayReloader$$anon$1.play$PlayReloader$$anon$$taskFailureHandler(PlayReloader.scala:292) ~[na:na]
at play.PlayReloader$$anon$1$$anonfun$reload$2.apply(PlayReloader.scala:325) ~[na:na]
at play.PlayReloader$$anon$1$$anonfun$reload$2.apply(PlayReloader.scala:325) ~[na:na]
at scala.util.Either$LeftProjection.map(Either.scala:377) ~[scala-library-2.11.1.jar:na]
at play.PlayReloader$$anon$1.reload(PlayReloader.scala:325) ~[na:na]
at play.core.ReloadableApplication$$anonfun$get$1.apply(ApplicationProvider.scala:107) ~[play_2.11-2.3.1.jar:2.3.1]
at play.core.ReloadableApplication$$anonfun$get$1.apply(ApplicationProvider.scala:105) ~[play_2.11-2.3.1.jar:2.3.1]
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24) ~[scala-library-2.11.1.jar:na]
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24) ~[scala-library-2.11.1.jar:na]
at scala.concurrent.forkjoin.ForkJoinTask$AdaptedRunnableAction.exec(ForkJoinTask.java:1361) ~[scala-library-2.11.1.jar:na]
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260) ~[scala-library-2.11.1.jar:na]
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339) ~[scala-library-2.11.1.jar:na]
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979) ~[scala-library-2.11.1.jar:na]
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107) ~[scala-library-2.11.1.jar:na]
2014-08-05 05:51:03,084 - [WARN] - from play in New I/O worker #1
No application found at invoker init
2014-08-05 06:08:03,586 - [ERROR] - from application in New I/O worker #2
A problem I can see is that in your controller you are trying to instantiate a new instance of the MyWebSocketActor
but you are not giving it the correct constructor info to allow that new instance to be created. The issue is this line here:
ActorRef myActor = Akka.system().actorOf(Props.create(MyWebSocketActor.class));
In your MyWebSocketActor, you do not have a no-args constructor. You have a single constructor defined as:
public MyWebSocketActor(ActorRef out ) {
this.out = out;
}
Now, you are doing this correctly in your static props
method on MyWebSocketActor. If you truly want to be able to instantiate a new instance of this actor in your controller (as opposed to looking up an existing one), you will need to have an ActorRef
(called out
in your constructor) to pass in. If you have that, then you can modify your code as follows:
ActorRef myActor = Akka.system().actorOf(MyWebSocketActor.props(outRef));
Edit
Now if you wanted to lookup an existing actor as opposed to creating a per-request actor, you would first need to make sure that the actor instance you wanted to lookup was already created and named in such a way that you could look it up. So something like this:
Akka.system().actorOf(MyWebSocketActor.props(outRef), "myactor");
Then, in your controller, you could use an ActorSelection
to find that pre-existing actor like so:
ActorSelection myActorSel = Akka.system().actorSelection("/user/myactor");
An ActorSelection
is not an ActorRef
, nor does it share some base interface/abstract class. It does however support the tell
operation in the same way an ActorRef
does, so you can call tell
on it after you look it up. If you actor exists with that name, then things should work out just fine.
You can read more about ActorSelection
s here under the section "Identifying Actors via ActorSelection".