Akka static usage - akka

I have a simple actor system with default configuration.
I have a class extended Actor
class Test extend Actor {
def receive: Receive = {
case Foo(collection) => sender ! extract(collection)
}
private def extract(c: List[FooItem]): List[BarItem] = ???
}
This actor have a companion object
object Test {
def props: Props = ???
}
Is there are safe to do function extract like this:
object Test {
def props: Props = ???
def extract(c: List[FooItem]): List[BarItem] = ???
}
and use from another Actor ?

Yes, it's ok to define a method on a companion and then import and use that method in an actor class. Something like this would work just fine:
object Test {
def props: Props = Props[Test]
def extract(c: List[FooItem]): List[BarItem] = {
. . .
}
}
class Test extend Actor {
import Test._
def receive: Receive = {
case Foo(collection) => sender ! extract(collection)
}
}

Related

Why the actorSystem is not creating the actor to run with custom dispatcher

Hi have below typesafe config in file application-typed.conf.
akka {
loggers = ["akka.event.slf4j.Slf4jLogger"]
loglevel = "DEBUG"
logging-filter = "akka.event.slf4j.Slf4jLoggingFilter"
actor {
provider = "local"
}
}
custom-thread-pool {
type = Dispatcher
executor = "thread-pool-executor"
thread-pool-executor {
fixed-pool-size = 40
}
throughput = 2
}
Below is the akka-typed actor code.
import akka.actor.typed.{ActorSystem, Behavior, DispatcherSelector, PostStop, Signal}
import akka.actor.typed.scaladsl.AbstractBehavior
import akka.actor.typed.scaladsl.ActorContext
import akka.actor.typed.scaladsl.Behaviors
import com.typesafe.config.ConfigFactory
import scala.concurrent.ExecutionContext
trait PrintMessage
case class PrintMessageAny(x: Any) extends PrintMessage
object PrintMeActor {
def apply(): Behavior[PrintMessage] =
Behaviors.setup[PrintMessage](context => new PrintMeActor(context))
}
class PrintMeActor(context: ActorContext[PrintMessage]) extends AbstractBehavior[PrintMessage](context) {
val dispatcherSelector: DispatcherSelector = DispatcherSelector.fromConfig("custom-thread-pool")
implicit val executionContext: ExecutionContext = context.system.dispatchers.lookup(dispatcherSelector)
println(s"PrintMeActor Application started in Thread ${Thread.currentThread().getName}")
override def onMessage(msg: PrintMessage): Behavior[PrintMessage] = {
// No need to handle any messages
println(s"Got $msg in Thread ${Thread.currentThread().getName}")
Behaviors.same
}
override def onSignal: PartialFunction[Signal, Behavior[PrintMessage]] = {
case PostStop =>
context.log.info("PrintMeActor Application stopped")
this
}
}
object TestTypedActorApp extends App {
val config = ConfigFactory.load("application-typed.conf")
val as: ActorSystem[PrintMessage] = ActorSystem(PrintMeActor(), "PrintAnyTypeMessage", config)
as.tell(PrintMessageAny("test"))
Thread.sleep(2000)
}
When I run the code, I get the below output.
PrintMeActor Application started in Thread PrintAnyTypeMessage-akka.actor.default-dispatcher-6
Got PrintMessageAny(test) in Thread PrintAnyTypeMessage-akka.actor.default-dispatcher-6
I want this actor to run on the custom-thread-pool but it is not happening. How can I achieve the same?
You associate the dispatcher with the actor when you spawn it, by passing an akka.actor.typed.DispatcherSelector (which extends akka.actor.typed.Props) corresponding to the desired dispatcher.
When spawning the ActorSystem on a custom dispatcher, one can only pass Props through the overloads that take either a Config or an ActorSystemSetup.
If wanting to override the actor for the user guardian actor (the actor with the behavior you passed into the ActorSystem), it may make more sense to make that dispatcher the default dispatcher:
akka.actor.default-dispatcher {
executor = "thread-pool-executor"
thread-pool-executor {
fixed-pool-size = 40
}
throughput = 2
}

Marshall or UnMarshall case object with SprayJsonSupport

I have a case object in an actor like this:
object UserActor{
case object getResult
def props = Props(new UserActor)
}
class UserActor extends Actor {
def receive = {
case getResult =>
val reply = sender
reply ! "Hello world"
}
}
which I want to use in a route as this:
val route: Route =
path("users") { id =>
get {
complete {
userActor ? getResult
}
}
}
However, I got the error, expected ToResponseMarshallable. So question, how do I marshall case object using SprayJsonSupport?

Sending similar jobs from different actors to a single router in Akka

I want to make an application using Akka where different actors will be using the same router to submit their jobs.
My question is, is it possible to send similar jobs from different actors to the same router in Akka ?
As long as your actors are in the same ActorSystem, then you should have no problems.
In this case just the actor which receives the messages will have a different sender.
Note that this sounds like a bad design, though. For the sake of the answer though, here is an example:
import akka.actor.{ActorSystem, ActorRef, Props, Actor}
import akka.routing.RoundRobinPool
class Worker extends Actor {
override def receive: Receive = {
case PowerMessage(value) =>
sender ! ResultMessage(value * value)
}
}
class Sender(router: ActorRef) extends Actor {
val actorName = self.path.name
override def receive: Actor.Receive = {
case m # PowerMessage(value) =>
router ! m
case ResultMessage(value) =>
System.out.println(s"Sender ($actorName). Result: $value.")
}
}
class Master(workers: Int) extends Actor {
val router: ActorRef = context.actorOf(
Props[Worker].withRouter(RoundRobinPool(workers)),
name = "workerRouter"
)
val sender1 = context.actorOf(
Props(new Sender(router)),
name = "sender1"
)
val sender2 = context.actorOf(
Props(new Sender(router)),
name = "sender2"
)
override def receive: Actor.Receive = {
case StartCalc =>
(1 to workers * 2).foreach {
case index =>
if (index % 2 == 1) {
sender1 ! PowerMessage(index)
} else {
sender2 ! PowerMessage(index)
}
}
}
}
object Senders {
def main(args: Array[String]): Unit = {
val system = ActorSystem("TestSystem")
val master = system.actorOf(
Props(new Master(10)),
name = "master"
)
master ! StartCalc
}
}
sealed trait MainMessage
case class PowerMessage(value: Long) extends MainMessage
case class ResultMessage(value: Long) extends MainMessage
case object StartCalc extends MainMessage

Grails / Spock: How to mock single method within class where method is called from within the class itself?

Given the following, how do I mock processMessage() using Spock, so that I can check that processBulkMessage() calls processMessage() n times, where n is the number of messages within a BulkMessage?
class BulkMessage {
List messages
}
class MyService {
def processBulkMessage(BulkMessage msg) {
msg.messages.each {subMsg->
processMessage(subMsg)
}
}
def processMessage(Message message) {
}
}
You can use spies and partial mocks (requires Spock 0.7 or newer).
After creating a spy, you can listen in on the conversation between the caller and the real object underlying the spy:
def subscriber = Spy(SubscriberImpl, constructorArgs: ["Fred"])
subscriber.receive(_) >> "ok"
Sometimes, it is desirable to both execute some code and delegate to the real method:
subscriber.receive(_) >> { String message -> callRealMethod(); message.size() > 3 ? "ok" : "fail" }
In my opinion this is not a well designed solution. Tests and design walk hand in hand - I recommend this talk to investigate it better. If there's a need to check if other method was invoked on an object being under test it seems it should be moved to other object with different responsibility.
Here's how I would do it. I know how visibility works in groovy so mind the comments.
#Grab('org.spockframework:spock-core:0.7-groovy-2.0')
#Grab('cglib:cglib-nodep:3.1')
import spock.lang.*
class MessageServiceSpec extends Specification {
def 'test'() {
given:
def service = new MessageService()
def sender = GroovyMock(MessageSender)
and:
service.sender = sender
when:
service.sendMessages(['1','2','3'])
then:
3 * sender.sendMessage(_)
}
}
class MessageSender { //package access - low level
def sendMessage(String message) {
//whatever
}
}
class MessageService {
MessageSender sender //package access - low level
def sendMessages(Iterable<String> messages) {
messages.each { m -> sender.sendMessage(m) }
}
}
It does not use Spock built-in Mocking API (not sure how to partially mock an object), but this should do the trick:
class FooSpec extends Specification {
void "Test message processing"() {
given: "A Bulk Message"
BulkMessage bulk = new BulkMessage(messages: ['a', 'b', 'c'])
when: "Service is called"
def processMessageCount = 0
MyService.metaClass.processMessage { message -> processMessageCount++ }
def service = new MyService()
service.processBulkMessage(bulk)
then: "Each message is processed separately"
processMessageCount == bulk.messages.size()
}
}
For Java Spring folks testing in Spock:
constructorArgs is the way to go, but use constructor injection. Spy() will not let you set autowired fields directly.
// **Java Spring**
class A {
private ARepository aRepository;
#Autowire
public A(aRepository aRepository){
this.aRepository = aRepository;
}
public String getOne(String id) {
tryStubMe(id) // STUBBED. WILL RETURN "XXX"
...
}
public String tryStubMe(String id) {
return aRepository.findOne(id)
}
public void tryStubVoid(String id) {
aRepository.findOne(id)
}
}
// **Groovy Spock**
class ATest extends Specification {
def 'lets stub that sucker' {
setup:
ARepository aRepository = Mock()
A a = Spy(A, constructorArgs: [aRepository])
when:
a.getOne()
then:
// Stub tryStubMe() on a spy
// Make it return "XXX"
// Verify it was called once
1 * a.tryStubMe("1") >> "XXX"
}
}
Spock - stubbing void method on Spy object
// **Groovy Spock**
class ATest extends Specification {
def 'lets stub that sucker' {
setup:
ARepository aRepository = Mock()
A a = Spy(A, constructorArgs: [aRepository]) {
1 * tryStubVoid(_) >> {}
}
when:
...
then:
...
}
}

Time for create TestProbe in Akka TestKit

I have trait for override actorOf in tests:
trait ActorRefFactory {
this: Actor =>
def actorOf(props: Props) = context.actorOf(props)
}
And I have worker actor, which stop self when receive any message:
class WorkerActor extends Actor {
override def receive: Actor.Receive = {
case _ => { context.stop(self) }
}
}
Also I have master actor, who creates actors and hold them in queue:
class MasterActor extends Actor with ActorRefFactory {
var workers = Set.empty[ActorRef]
override val supervisorStrategy = SupervisorStrategy.stoppingStrategy
def createWorker() = {
val worker = context watch actorOf(Props(classOf[WorkerActor]))
workers += worker
worker
}
override def receive: Receive = {
case m: String =>
createWorker()
case Terminated(ref) =>
workers -= ref
createWorker()
}
}
And this test, which is failed:
class ActorTest(val _system: ActorSystem) extends akka.testkit.TestKit(_system)
with ImplicitSender
with Matchers
with FlatSpecLike {
def this() = this(ActorSystem("test"))
def fixture = new {
val master = TestActorRef(new MasterActor() {
override def actorOf(props: Props) = TestProbe().ref
})
}
it should "NOT FAILED" in {
val f = fixture
f.master ! "create"
f.master ! "create"
f.master.underlyingActor.workers.size shouldBe 2
val worker = f.master.underlyingActor.workers.head
system.stop(worker)
Thread.sleep(100)
f.master.underlyingActor.workers.size shouldBe 2
}
}
After Thread.sleep in the test, I give error by "1 was not equal to 2". I have not idea what happening. But, if guess I can assume that TestProbe() can't create in the time. What can I do?
This basically boils down to an asynchronicity issue that you want to try and avoid in unit tests for Akka. You are correctly using a TestActorRef to get hooked into the CallingThreadDispatcher for the master actor. But when you call system.stop(worker), the system still us using the default async dispatcher which will introduce this race condition on the stopping and then re-creating of a worker. The simplest way I can see to fix this issue consistently is to stop the worker like so:
master.underlyingActor.context.stop(worker)
Because you are using the context of master and that actor is using the CallingThreadDispatcher I believe this will remove the asnyc issue that you are seeing. It worked for me when I tried it.