I am new to Scala Akka actor. Based on the akka tutorial example on their website,i coded a similar example with just printing messages with the time they started. But the code does not get executed in my eclipse IDE.
import akka.actor.Actor
import akka.actor.ActorRef
import scala.collection.mutable.ListBuffer
import akka.actor.Props
import akka.routing.RoundRobinRouter
import akka.actor.ActorSystem
object AkaObj extends App{
process()
sealed trait PiMessage
case class Work(value:String) extends PiMessage
case class printComp(valu:String)extends PiMessage
case object Start extends PiMessage
case class ListObj(cont:Seq[String]) extends PiMessage
class Worker extends Actor{
def receive()={
case Work(value:String)=>
println(value)
sender ! printComp(value)
}
}
class Master(listener: ActorRef) extends Actor{
val nrOfWorkers = 10
var counter = 0
var lis = ListBuffer[String]()
val workerRouter = context.actorOf(
Props[Worker].withRouter(RoundRobinRouter(nrOfWorkers)), name = "workerRouter")
def receive()={
//handle message
case Start=>workerRouter ! Work("Start now " + System.currentTimeMillis())
case printComp(value:String)=> {
counter+=1
lis.append(counter+"-"+value)
if(counter >=10)
listener ! ListObj(lis.toSeq)
// Stops this actor and all its supervised children
context.stop(self)
}
}
}
class Listener extends Actor{
def receive()={
case ListObj(cont:Seq[String])=>
println("Completed " +cont)
context.system.shutdown()
}
}
def process(){
// Create an Akka system
val system = ActorSystem("PiSystem")
// create the result listener, which will print the result and shutdown the system
val listener = system.actorOf(Props[Listener], name = "listener")
// create the master
val master = system.actorOf(Props(new Master(listener)),
name = "master")
// start the calculation
master ! Start
}
}
Issue: The error message i get is: "Error: Could not find or load main class AkaObj"
It does not even compiling i think. When i run the project,that's the message i get.
Actually, I managed to run your code just after dependencies were resolved. So the problem is in your environment configuration. I use Intellij Idea IDE.
Try this Eclipse Error: Could not find or load main class
The actual problem is the use of object AkaObj extends App. When programming scala I have noticed that using scala.App is restricted to simple programs (at least in IDEs). As soon as you start defining objects and classes inside scala.App Eclipse starts complaining about not finding the entry point of the program.
Try to fix this as follows:
import akka.actor.Actor
import akka.actor.ActorRef
import scala.collection.mutable.ListBuffer
import akka.actor.Props
import akka.routing.RoundRobinRouter
import akka.actor.ActorSystem
object AkaObj {
def main(args:Array[String]) {
process()
}
def process() {
// Create an Akka system
val system = ActorSystem("PiSystem")
// create the result listener, which will print the result and shutdown the system
val listener = system.actorOf(Props[Listener], name = "listener")
// create the master
val master = system.actorOf(Props(new Master(listener)),
name = "master")
// start the calculation
master ! Start
}
sealed trait PiMessage
case class Work(value:String) extends PiMessage
case class printComp(valu:String)extends PiMessage
case object Start extends PiMessage
case class ListObj(cont:Seq[String]) extends PiMessage
class Worker extends Actor{
def receive()={
case Work(value:String)=>
println(value)
sender ! printComp(value)
}
}
class Master(listener: ActorRef) extends Actor{
val nrOfWorkers = 10
var counter = 0
var lis = ListBuffer[String]()
val workerRouter = context.actorOf(
Props[Worker].withRouter(RoundRobinRouter(nrOfWorkers)), name = "workerRouter")
def receive()={
//handle message
case Start=>workerRouter ! Work("Start now " + System.currentTimeMillis())
case printComp(value:String)=> {
counter+=1
lis.append(counter+"-"+value)
if(counter >=10)
listener ! ListObj(lis.toSeq)
// Stops this actor and all its supervised children
context.stop(self)
}
}
}
class Listener extends Actor{
def receive()={
case ListObj(cont:Seq[String])=>
println("Completed " +cont)
context.system.shutdown()
}
}
}
Using def main(args:Array[String]) fixes the problem in most cases.
Related
I'm just messing around with some basics to learn akka remoting, and I'm running into an issue where my proxy class sends Identify messages to my backend, but never receives a response back.
I've verified that the backend receives messages sent to the ActorSelection, and I've seen a logging message where the backend actor says it will use xxx for serialization of the ActorIdentity messages. Not sure where I'm getting it wrong.
Here is my proxy class:
package remoting
import akka.actor.{Actor, ActorIdentity, ActorRef, Identify, ReceiveTimeout, Terminated}
import com.typesafe.config.ConfigFactory
import scala.concurrent.duration._
class BackendProxyActor extends Actor {
context.setReceiveTimeout(3.seconds)
val path = createPath
val backendSelection = context.actorSelection(path)
println(f"BackendSelection is $backendSelection")
override def preStart(): Unit = backendSelection ! Identify(1)
override def receive: Receive = {
case ActorIdentity(1, Some(actor)) =>
context.setReceiveTimeout(Duration.Undefined)
context.watch(actor)
context.become(active(actor))
case ActorIdentity(1, None) =>
println("Backend actor not available")
case ReceiveTimeout =>
backendSelection ! Identify(1)
case msg: Any => println(f"Received $msg while identifying backend")
}
def active(backend: ActorRef): Receive = {
case msg: Any => backend ! msg
case Terminated(backend) =>
println("backend terminated, going to identifying state")
context.setReceiveTimeout(3.seconds)
context.become(receive)
}
def createPath: String = {
val config = ConfigFactory.load("frontend").getConfig("backend")
val name = config.getString("name")
val host = config.getString("host")
val port = config.getString("port")
val system = config.getString("system")
val protocol = config.getString("protocol")
f"$protocol://$system#$host:$port/$name"
}
}
Here is my backend class:
package remoting
import akka.actor.{Actor, Identify, PoisonPill}
import com.typesafe.config.ConfigFactory
class BackendActor extends Actor {
val config = ConfigFactory.load("backend")
override def receive: Receive = {
case "stop" => self ! PoisonPill
case msg: Any => println(f"Received $msg")
}
}
My frontend class:
package remoting
import akka.actor.{Actor, Props}
class FrontendActor extends Actor {
val proxy = context.actorOf(Props[BackendProxyActor], "backendProxy")
override def receive: Receive = {
case msg: Any => proxy ! msg
}
}
and finally my App class:
package remoting
import akka.actor.{ActorSystem, Props}
import com.typesafe.config.ConfigFactory
object Main extends App {
val frontendConfig = ConfigFactory.load("frontend")
val frontend = ActorSystem("frontend", frontendConfig)
val frontendActor = frontend.actorOf(Props[FrontendActor], "FrontendActor")
(1 to 20).foreach(i => frontendActor ! f"Msg #$i")
frontendActor ! "stop"
}
My backend is being started in another process, and is run on port 2551, while my frontend is on port 2552.
In "Dependency Injecting Actors" it's shown how to inject a parameter into the constructor of a child actor. The parent actor uses injectedChild to be allowed to pass to the child (at child creation time) only the non-injected parameter and then let Guice inject the rest. To do this, it extends InjectedActorSupport and gets the child's factory injected in the constructor:
class MyParent #Inject() (childFactory: MyChild.Factory,
#Assisted something: Something,
#Assisted somethingElse: SomethingElse) extends Actor with InjectedActorSupport
[..]
val child: ActorRef = injectedChild(childFactory(something, somethingElse), childName)
But what about the class that starts the parent and is not an actor but a custom ApplicationLoader?
How can I start the parent actor from there? No mention of this is in the documentation.
I tried doing the same for the loader as I did for parent:
class MyLoader #Inject() (parentFactory: MyParent.Factory) extends ApplicationLoader with Actor with InjectedActorSupport {
[..]
val parent = injectedChild(parentFactory(something, somethingElse), parentName)
would this be correct? How can I test it?
class MyModule extends AbstractModule with AkkaGuiceSupport {
def configure = {
bindActor[MyParent](parentName)
bindActor[MyLoader](loaderName)
bindActorFactory[MyChild, MyChild.Factory]
bindActorFactory[MyParent, MyParent.Factory]
}
}
So:
How do I start the parent from MyLoader while letting Guice dependency-inject what's required?
How can I test MyLoader?
This has been my test so far but now I need to pass the injected thingy to MyLoader and I don't know how (note the ***???**** in place of the argument which I do not know where to find):
class MyLoaderSpec(_system: ActorSystem, implicit val ec: ExecutionContext) extends TestKit(_system) with WordSpecLike with BeforeAndAfterAll with Matchers {
val loader = new SimstimLoader(???)
override def beforeAll(): Unit = {
loader.load(ApplicationLoader.createContext(new Environment(new File("."), ApplicationLoader.getClass.getClassLoader, Mode.Test)))
}
Thanks a million in advance!
Here is how I solved this issue.
--> How to start a parent actor who needs dependency-injection.
First of all, manually starting such an actor is impossible if you, like me, need to dependency-inject an instance which you do not know how to pass and where from. The solution is to let Guice start the actor automagically. Here is how.
First, create your binder module for Guice:
class MyModule extends AbstractModule with AkkaGuiceSupport{
override def configure(): Unit = {
bindActor[Root](Root.NAME)
bind(classOf[StartupActors]).asEagerSingleton()
}
}
Then, tell Play where your binder module is located by adding the following in your conf/application.conf:
play.modules={
enabled += "my.path.to.MyModule"
}
The StartupActors is simply a class I use to log whenever the automagic start of dependency-injected actors actually takes place. I log the event so that I can be sure of when and whether it occurs:
class StartupActors #Inject() (#Named(Root.NAME) root: ActorRef) {
play.api.Logger.info(s"Initialised $root")
}
The Root actor in my case takes care of parsing a custom configuration. Since the resulting vars from the parsing is required by my parent actor and during the tests I need to mock such resulting vars, I delegate the parsing to an actor other than the parent actor, i.e., the Root actor:
object Root {
final val NAME = "THERoot"
case class ParseConfiguration()
}
class Root #Inject()(configuration: Configuration, projectDAO: ProjectDAO) extends Actor {
val resultingVar: Something = myConfigParsing()
override def preStart(): Unit = {
context.actorOf(Props(new MyParent(resultingVar: Something, somethingElse: SomethingElse, projectDAO: ProjectDAO)))
}
override def receive: Receive = {
case ParseConfiguration => sender ! myConfigParsing()
case _ => logger.error("Root actor received an unsupported message")
}
}
The ParseConfiguration message is used uniquely for testing purposes. Normally the configuration parsing occurs instead because of the initialisation of the resultingVar attribute.
This way, MyParent wont need to get anything injected. Only StartupActors and Root will get injected. MyParent will simply get projectDAO from Root and pass it on to all its children.
class MyParent(something: Something, somethingElse: SomethingElse, projectDAO: ProjectDAO) extends Actor { ... }
Finally, for completion, I'm reporting here how I wrote the tests since I had troubles finding enough information online around this as well.
import akka.actor.{ActorRef, ActorSystem, Props}
import akka.testkit.{TestKit, TestProbe}
import com.typesafe.config.ConfigFactory
import org.mockito.Mockito.mock
import org.scalatest.{BeforeAndAfterAll, WordSpecLike}
import org.specs2.matcher.MustMatchers
import play.api.Configuration
import scala.concurrent.ExecutionContext
class RootSpec(_system: ActorSystem) extends TestKit(_system)
with WordSpecLike with BeforeAndAfterAll with MustMatchers {
implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.global
val conf: com.typesafe.config.Config = ConfigFactory.load()
val configuration: Configuration = Configuration(conf)
val projectDAOMock: ProjectDAO = mock(classOf[ProjectDAO])
private var mainActor: ActorRef = _
private var something: Something = Something.empty
def this() = this(ActorSystem("MySpec"))
override def afterAll: Unit = {
system.shutdown()
}
override def beforeAll(): Unit = {
mainActor = system.actorOf(Props(new Root(configuration, projectDAOMock)), Root.NAME)
}
"RootSpec: Root Actor" should {
val probe = TestProbe()
"successfully parse the configuration file" in {
probe.send(mainActor, ParseConfiguration)
something = probe.expectMsgPF() {
case msg => msg.asInstanceOf[Something]
}
}
}
}
and then I test MyParent by conveniently providing mock objects in place of vars resulting from the configuration parsing:
import akka.actor.{ActorRef, ActorSystem, Props}
import akka.testkit.{TestKit, TestProbe}
import org.mockito.Mockito
import org.mockito.Mockito._
import org.scalatest.{BeforeAndAfterAll, WordSpecLike}
import org.specs2.matcher.MustMatchers
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{ExecutionContext, Future}
case class AnyProjectAPI(val projectAPI: ProjectAPI) extends AnyVal
class MyParentSpec(_system: ActorSystem, implicit val ec: ExecutionContext) extends TestKit(_system)
with WordSpecLike with BeforeAndAfterAll with MustMatchers {
val something = mock(classOf[Something])
val somethingElse = mock(classOf[somethingElse])
val projectDAOMock: ProjectDAO = mock(classOf[ProjectDAO])
val projectTest: ProjectAPI = new ProjectAPI(allMyRandomConstructorArguments),
val projectsList: List[ProjectAPI] = List(projectTest)
val expectedCreationId = 1
private var parent: ActorRef = _
def this() = this(ActorSystem("MySpec"), scala.concurrent.ExecutionContext.global)
override def afterAll: Unit = {
system.shutdown()
}
override def beforeAll(): Unit = {
parent = system.actorOf(Props(new MyParent(something, somethingElse, projectDAOMock)), MyParent.NAME)
}
"MyParentTesting: parent's pull request" should {
when(myProjApi.getAllProjects).thenReturn(Future {projectsList})
val anyProject: AnyProjectAPI = AnyProjectAPI(org.mockito.Matchers.any[ProjectAPI])
Mockito.when(projectDAOMock.create(org.mockito.Matchers.any[ProjectAPI]))
.thenReturn(Future {expectedCreationId}: Future[Int])
val probe = TestProbe()
val probe1 = TestProbe()
"be successfully satisfied by all children when multiple senders are waiting for an answer" in {
probe.send(parent, UpdateProjects)
probe1.send(parent, UpdateProjects)
allChildren.foreach(child =>
probe.expectMsg(expectedCreationId))
allChildren.foreach(child =>
probe1.expectMsg(expectedCreationId))
}
}
}
For router actor with "group mode", we new a series of actors in advance, and then we will use actorOf to associate one router with remote routees.
In fact, it internal will use actorSelection, my question is: how to assure the association already finish?
For actorSelection, we can use resolveOne to make sure the selection is successful, then send message. But how about router actor? See following code, how to assure line2 associate with remote routees before send message to it, just like line line1 does?
package local
import akka.actor._
import akka.routing.RoundRobinGroup
import akka.util.Timeout
import scala.concurrent.duration._
import com.typesafe.config.ConfigFactory
import scala.util.{Failure, Success}
object Local extends App {
val config = ConfigFactory.parseString(s"akka.remote.netty.tcp.port=2251")
val system = ActorSystem("LocalSystem", config.withFallback(ConfigFactory.load()))
system.actorOf(Props[LocalActor], "LocalActor")
val config2 = ConfigFactory.parseString(s"akka.remote.netty.tcp.port=2252")
val system2 = ActorSystem("LocalSystem2", config2.withFallback(ConfigFactory.load()))
system2.actorOf(Props[LocalActor2], "LocalActor2")
}
class LocalActor extends Actor {
def receive = {
case _ => println("hi")
}
}
class LocalActor2 extends Actor {
import scala.concurrent.ExecutionContext.Implicits.global
implicit val timeout = Timeout(5 seconds)
val a = context.actorSelection("akka.tcp://LocalSystem#127.0.0.1:2251/user/LocalActor")
a.resolveOne().onComplete { // line 1
case Success(actor) => println("ready")
a ! 1
case Failure(ex) => println("not ready")
}
val paths = List("akka.tcp://LocalSystem#127.0.0.1:2251/user/LocalActor")
val b = context.actorOf(RoundRobinGroup(paths).props(), "LocalActorRouter") // line 2
b ! 1
def receive = {
case _ =>
}
}
I've followed the example for creating Web Sockets with Scala Play and Akka actors:
https://www.playframework.com/documentation/2.5.x/ScalaWebSockets#Handling-WebSockets-with-Akka-Streams-and-actors
On resume, the controller:
import play.api.mvc._
import play.api.libs.streams._
class Controller1 #Inject() (implicit system: ActorSystem, materializer: Materializer) {
def socket = WebSocket.accept[String, String] { request =>
ActorFlow.actorRef(out => MyWebSocketActor.props(out))
}
And the Actor:
import akka.actor._
object MyWebSocketActor {
def props(out: ActorRef) = Props(new MyWebSocketActor(out))
}
class MyWebSocketActor(out: ActorRef) extends Actor {
def receive = {
case msg: String =>
out ! ("I received your message: " + msg)
}
}
The actors created (one per websocket connection) are child of /user actor. I've created 3 connections and the actor created were:
/user/$b
/user/$c
/user/$d
I want to change the actors' name based in a field of the web socket message. How could i do this?.
You can set the name of the actor as follows:
Create a file BetterActorFlow.scala
package your.package
import akka.actor._
import akka.stream.scaladsl.{Keep, Sink, Source, Flow}
import akka.stream.{Materializer, OverflowStrategy}
object BetterActorFlow {
def actorRef[In, Out](props: ActorRef => Props, bufferSize: Int = 16, overflowStrategy: OverflowStrategy = OverflowStrategy.dropNew, maybeName: Option[String] = None)(implicit factory: ActorRefFactory, mat: Materializer): Flow[In, Out, _] = {
val (outActor, publisher) = Source.actorRef[Out](bufferSize, overflowStrategy)
.toMat(Sink.asPublisher(false))(Keep.both).run()
def flowActorProps: Props = {
Props(new Actor {
val flowActor = context.watch(context.actorOf(props(outActor), "flowActor"))
def receive = {
case Status.Success(_) | Status.Failure(_) => flowActor ! PoisonPill
case Terminated(_) => context.stop(self)
case other => flowActor ! other
}
override def supervisorStrategy = OneForOneStrategy() { case _ => SupervisorStrategy.Stop }
})
}
def actorRefForSink =
maybeName.fold(factory.actorOf(flowActorProps)) { name => factory.actorOf(flowActorProps, name) }
Flow.fromSinkAndSource(Sink.actorRef(actorRefForSink, Status.Success(())), Source.fromPublisher(publisher))
}
}
Use BetterActorFlow instead of ActorFlow:
BetterActorFlow.actorRef(out =>
ChatActor.props(out), 16, OverflowStrategy.dropNew, Some("alicebob"))
This worked for me. The created actor is at user/alicebob (use this with context.system.actorSelection("user/alicebob"))
According to the source code of ActorFlow, it is currently not possible to thange the name of the actual actor spawned for a connection (line 38):
Sink.actorRef(factory.actorOf(Props(new Actor { ... }) /*, name parameter is omitted */)
However, ActorFlow.actorRef accepts an implicit ActorRefFactory, which is implicit system: ActorSystem in all cases in your code. As we know, there are 2 most common ActorRefFactories: ActorSystem and ActorContext. You can modify your code in such a way that each time a connection is accepted another dummy actor would spawn with your preferred name (e.g. myActor1), and pass this new actor's context to ActorFlow.actorRef instead. In return, actors for connections would be named as follows:
/user/myActor1/$a
/user/myActor2/$a
etc
Hey i want to build some small Funsuite test for akka actor application but after combining Testkit with FunSuiteLike i cant call th test anymore.
Somebody an idea why this is happening? is Testkit and funsuite not compatible?
import org.scalatest.{FunSuiteLike, BeforeAndAfterAll}
import akka.testkit.{ImplicitSender, TestKit, TestActorRef}
import akka.actor.{ActorSystem}
class ActorSynchroTest(_system: ActorSystem) extends TestKit(_system) with FunSuiteLike with BeforeAndAfterAll with ImplicitSender{
val actorRef = TestActorRef(new EbTreeDatabase[Int])
val actor = actorRef.underlyingActor
//override def afterAll = TestKit.shutdownActorSystem( system )
test("EbTreeDatabase InsertNewObject is invoked"){
val idList = List(1024L,1025L,1026L,1032L,1033L,1045L,1312L,1800L)
idList.
foreach(x => actorRef ! EbTreeDataObject[Int](x,x,1,None,null))
var cursor:Long = actor.uIdTree.firstKey()
var actorItems:List[Long] = List(cursor)
while(cursor!=actor.uIdTree.lastKey()){
cursor = actor.uIdTree.next(cursor)
cursor :: actorItems
}
assert(idList.diff(actorItems) == List())
}
}
The intelliJ idea test enviroment says:
One or more requested classes are not Suites: model.ActorSynchroTest
class ActorSynchroTest extends TestKit(ActorSystem("ActorSynchroTest",ConfigFactory.parseString(ActorSynchroTest.config)))
with DefaultTimeout with ImplicitSender
with FunSuiteLike with Matchers with BeforeAndAfterAll {
...
}
object ActorSynchroTest {
// Define your test specific configuration here
val config = """
akka {
loglevel = "WARNING"
}
"""
}
Different initialization of the testkit worked in the end before the standard config was used which didn't fit