Referencing external val in actor class - scala

Is it prohibited by design or am I doing something wrong?
val ext = 1
class Test extends Actor {
def receive = { case _ => println(ext) }
}
try {
val sys = ActorSystem("lol")
sys.actorOf(Props[Test], "test") ! true
} catch {
case e: Throwable => println(e)
}
once I send a message to Test I get an exception java.lang.IllegalArgumentException: no matching constructor found on class HelloAkkaScala$Test$1 for arguments [].
I don't have this exception if I don't reference ext inside of Test class.
I'm using Akka 2.3.4

There is nothing wrong in accessing val (not var or any other mutable state) defined outside of the actor.
Just tried to run your example code in Akka 2.3.5 and it works fine. You probably have a typo somwhere in your original code.
UPDATE
Looking closer at the error you are getting it seems like you've defined Test class inside some other class.
In this case inner class (Test) will receive reference to outer class behind the scenes in order to be able to close on it's members (ext).
This also means that constructor of the inner class will take reference to the outer class (syntactic sugar hides this).
This also explains why you are getting the error (you pass 0 args to constructor but there actually some hidden ones that you are not supplying).
Here is the example that reproduces this:
class Boot {
val ext = 1
class Test extends Actor {
def receive = { case _ => println(ext) }
}
try {
val sys = ActorSystem("lol")
sys.actorOf(Props[Test], "test") ! true
} catch {
case e: Throwable => println(e)
}
}
object Boot extends App {
new Boot
}
... and here is a quick workaround (in this case I make Test and ext "static" by moving them to companion object, but you can achieve similar results by referencing ext as a member of some other instance, passed to constructor):
class Boot {
import Boot.Test
try {
val sys = ActorSystem("lol")
sys.actorOf(Props[Test], "test") ! true
} catch {
case e: Throwable => println(e)
}
}
object Boot extends App {
val ext = 1
class Test extends Actor {
def receive = { case _ => println(ext) }
}
new Boot
}

Related

Why is data always empty in this akka FSM actor?

Here is the code:
package com.packt.akka
import akka.actor.{ActorSystem, FSM, Props, Stash}
object UserStorageFSM {
// FSM State
sealed trait State
case object Connected extends State
case object Disconnected extends State
// FSM Data
sealed trait Data {
def data: List[User]
}
case object EmptyData extends Data {
val data = List.empty
}
trait DBOperation
object DBOperation {
case object Create extends DBOperation
case object Update extends DBOperation
case object Read extends DBOperation
case object Delete extends DBOperation
}
case object Connect
case object Disconnect
case class Operation(op: DBOperation, user: User)
case class User(username: String, email: String)
}
class UserStorageFSM extends FSM[UserStorageFSM.State, UserStorageFSM.Data] with Stash {
import UserStorageFSM._
// 1. define start with
startWith(Disconnected, EmptyData)
// 2. define states
when(Disconnected) {
case Event(Connect, _) =>
println("UserStorage Connected to DB")
unstashAll()
goto(Connected) using (EmptyData)
case Event(_, _) =>
stash()
stay using (EmptyData)
}
when(Connected) {
case Event(Disconnect, _) =>
println("UserStorage disconnected from DB")
goto(Disconnected) using EmptyData
case Event(Operation(op, user), oldData) =>
op match {
case DBOperation.Create =>
stay using new Data {
override def data = user :: oldData.data
}
case DBOperation.Delete => stay using new Data {
override def data = oldData.data.filter(_ != user)
}
case DBOperation.Read => {
println(oldData.data)
stay using oldData
}
case DBOperation.Update => {
stay using new Data {
override def data: List[User] = user :: oldData.data.filter(_.username != user.username)
}
}
}
stay using EmptyData
}
// 3. initialize
initialize()
}
object FiniteStateMachine extends App {
import UserStorageFSM._
val system = ActorSystem("Hotswap-FSM")
val userStorage = system.actorOf(Props[UserStorageFSM], "userStorage-fsm")
userStorage ! Connect
userStorage ! Operation(DBOperation.Create, User("Admin", "admin#packt.com"))
userStorage ! Operation(DBOperation.Create, User("Admin1", "admin#packt.com"))
userStorage ! Operation(DBOperation.Read, User("Admin", "admin#packt.com"))
userStorage ! Disconnect
Thread.sleep(1000)
system.terminate()
}
It simulates a storage system that allows CRUD operations. The problem here is that the system seems to always contain empty data. What went wrong here?
In your handler for Operation you call stay in the pattern match on op, but then at the bottom you call stay using EmptyData and that is the one that gets used. Remove stay using EmptyData from the bottom of case Event(Operation(op, user), oldData) => and you should start seeing the updated Data.

Case class serialazation in flink

I am trying to build a dataset using a case class for Scala (I would like to use case classes over tuples because I want to join fields by name).
Here is one iteration of a join I am working on:
case class TestTarget(tacticId: String, partnerId:Long)
campaignPartners.join(partnerInput).where(1).equalTo("id") {
(target, partnerInfo, out: Collector[TestTarget]) => {
partnerInfo.partner_pricing match {
case Some(pricing) =>
out.collect(TestTarget(target._1, partnerInfo.partner_id))
case None => ()
}
}
}
Obviously this throws the error:
org.apache.flink.api.common.InvalidProgramException: Task not
serializable at
org.apache.flink.api.scala.ClosureCleaner$.ensureSerializable(ClosureCleaner.scala:179)
at
org.apache.flink.api.scala.ClosureCleaner$.clean(ClosureCleaner.scala:171)
at org.apache.flink.api.scala.DataSet.clean(DataSet.scala:121) at
org.apache.flink.api.scala.JoinDataSet$$anon$2.(joinDataSet.scala:108)
at
org.apache.flink.api.scala.JoinDataSet.apply(joinDataSet.scala:107)
at
com.adfin.dataimport.vendors.dbm.Job.calculateVendorFees(Job.scala:84)
I have seen the docs here that state that I need to implement serializable for the class. As far as I can tell in new versions of Scala there is no good way to automatically serialize case classes. (I looked into manual serialization but I think I would need to do some extra work with link for this to work).
Edit:
As per Till Rohrmann's suggestion I tried to reproduce this error using a small case. This is what I used to try and reproduce the error. This example worked and I failed to reproduce the error. I also tried putting Option cases everywhere but that cause the job to fail either.
val text = env.fromElements("To be, or not to be,--that is the question:--")
val words = text.flatMap { _.toLowerCase.split("\\W+") }.map(x => (1,x))
val nums = env.fromElements(List(1,2,3,4,5)).flatMap(x => x).map(x => First(1,x))
val counts = words.join(nums).where(0).equalTo("a") {
(a, b, out: Collector[TestTarget]) => {
b.b match {
case 2 => ()
case _ => out.collect(TestTarget(a._2, b.b))
}
}
}
The definition of my program used a class
class Job(conf: AdfinConfig)(implicit env: ExecutionEnvironment)
extends DspJob(conf){
...
case class TestTarget(tacticId: String, partnerId:Long)
campaignPartners.join(partnerInput).where(1).equalTo("id") {
...
}
Since it was an inner class it wasn't being serialized automatically
If you switch the class to not be an inner class then everything works out
case class TestTarget(tacticId: String, partnerId:Long)
class Job(conf: AdfinConfig)(implicit env: ExecutionEnvironment)
extends DspJob(conf){
...
words.join( ....)
...
}

Loss of type info in servlet code

I have a simple flash implementation for use with Jersey that looks like this:
#PostConstruct def before { flash.rotateIn }
#PreDestroy def after { flash.rotateOut }
object flash {
val KeyNow = "local.flash.now"
val KeyNext = "local.flash.next"
// case class Wrapper(wrapped: Map[String, Seq[String]])
case class Wrapper(wrapped: String)
def rotateIn {
for {
session <- Option(request.getSession(false))
obj <- Option(session.getAttribute(KeyNext))
} {
request.setAttribute(KeyNow, obj)
session.removeAttribute(KeyNext)
}
}
def rotateOut {
for (obj <- Option(request.getAttribute(KeyNext))) {
request.getSession.setAttribute(KeyNext, obj)
}
}
def now = Option(request.getAttribute(KeyNow)) match {
case Some(x: Wrapper) => x.wrapped
case Some(x) if x.isInstanceOf[Wrapper] => "WHAT"
case _ => "NOPE"
}
def next(value: String) {
request.setAttribute(KeyNext, Wrapper(value))
}
}
I have simplified it here somewhat, but it lets me set a value for flash with flash.next and read the current flash value with flash.now.
The weird thing is that my now value is always "WHAT". If I do something similar in my REPL, I don't have the same issues:
val req = new org.springframework.mock.web.MockHttpServletRequest
val res = req.getSession
res.setAttribute("foo", Wrapper("foo"))
req.setAttribute("foo", res.getAttribute("foo"))
// Is not None
Option(req.getAttribute("foo")).collect { case x: Wrapper => x }
Am I missing something obvious?
EDIT
I've added a minimal example webapp replicating this issue at https://github.com/kardeiz/sc-issue-20160229.
I tried your example. Check my answer for your other question for details how pattern matching works in this case.
In short, as you Wrapper is an inner class, patter matching also checks the "outer class" reference. It seems that depending on the application server implementation Router.flash can be different instance for each request, so pattern matching fails.
Simple fix for that is to make Wrapper top-level class, so it doesn't have reference to any other class.

how to correctly setup Akka.Actors compared to Scala.Actors

How do I run 2 Akka actors with the caller sending the consumer a message every n seconds?
As there are no loop-react methods as in the Scala.Actors library I am stuck
somehow, the following will not compile and produces:
overriding method receive in trait Actor of type =>
Caller.this.Receive; method receive has incompatible type
object Foo {
def init() {
actorOf[Caller].start()
actorOf[Consumer].start()
}
}
class Caller extends Actor {
def receive {
while (true) {
self ! "msg"
Thread.sleep(1000)
}
}
}
class Consumer extends Actor {
def receive = {
case msg:String => doStuff()
case e => _
}
}
You're missing the equals sign after receive in Caller. Without it, the method is defined as returning Unit (i.e. no useful value), and akka needs you to return a PartialFunction[Any,Unit] from receive.
Now, to actually implement your logic in the idiomatic way, you probably want to use a ReceiveTimeout, like so:
class Caller extends Actor {
self.receiveTimeout = Some(1000)
def receive = {
case ReceiveTimeout =>
self ! "msg"
}
}

How do I get exception details from an Akka Supervisor?

I'm testing how a new Actor I'm working on handles unexpected messages. I'd like to assert that it throws a GibberishException in these cases. Here's the test and the implementation so far:
Test:
"""throw a GibberishException for unrecognized messages""" in {
//define a service that creates gibberish-speaking repositories
val stubs = new svcStub(
actorOf(new Actor{
def receive = { case _ => {
self.channel ! "you're savage with the cabbage"
}
}
})
)
val model = actorOf(new HomeModel(stubs.svc,stubs.store))
val supervisor = Supervisor(
SupervisorConfig(
OneForOneStrategy(List(classOf[Exception]), 3, 1000),
Supervise(model,Permanent) :: Nil
)
)
try{
intercept[GibberishException] {
supervisor.start
model !! "plan"
}
} finally {
supervisor.shutdown
}
stubs.store.plan should equal (null)
stubs.svcIsOpen should be (false)
}
Implementation:
class HomeModel(service: PlanService, store: HomeStore)
extends Actor {
private val loaderRepo = service.getRepo()
private var view: Channel[Any] = null
override def postStop() = {
service.close()
}
def receive = {
case "plan" => {
view=self.channel
loaderRepo ! LoadRequest()
}
case p: Plan => {
store.plan=p
view ! store.plan
}
case _ => throw new GibberishException(_)
}
}
However, when I run the test, the exception details get to the Supervisor I established, but I don't know how to do anything with them (like log them or test their type). I'd like to be able to get the exception details here from the supervisor so i can rethrow and intercept them in my test. Outside of a test method, I could imagine this being useful if you wanted to report the nature of an exception in the UI of a running app. Is there a way to get this from the Supervisor when it happens?
Change the OneForOneStrategy to only handle GibberishException, should solve it.