I am trying to send a message using akka remote actors, where the case class is a subclass of a superclass taking argument in its constructor.
Here is a minimum example to reproduce the problem:
package com.tuvistavie.testremote
import akka.actor.{ Actor, ActorSystem, Props, ActorLogging }
import com.typesafe.config.ConfigFactory
abstract class Foo(val a: Int)
case class MessageFoo(override val a: Int) extends Foo(a)
object Sender {
def main(args: Array[String]) {
val system = ActorSystem("Sender", ConfigFactory.load.getConfig("sender"))
val actor = system.actorFor("akka://Receiver@127.0.0.1:2552/user/receiver")
actor ! MessageFoo(1)
}
}
object Receiver {
class ReceiverActor extends Actor with ActorLogging {
def receive = {
case m: MessageFoo => log.debug(m.toString)
}
}
def main(args: Array[String]) {
val system = ActorSystem("Receiver", ConfigFactory.load.getConfig("receiver"))
val actor = system.actorOf(Props[ReceiverActor], "receiver")
}
}
When running this code, I get the following error:
[ERROR] [06/26/2013 02:53:16.132] [Receiver-9]
[NettyRemoteTransport(akka://Receiver@127.0.0.1:2552)]
RemoteServerError@akka://Receiver@127.0.0.1:2552] Error[java.io.InvalidClassException: com.tuvistavie.testremote.MessageFoo; no valid constructor]
I think it is because the message cannot be deserialized (using akka.serialization.JavaSerializer
), because of the parents' constructor.
If it were only one or two messages I know I could write my own serializer, but I have plenty of case classes like this in my application.
Would there be any easy way to pass this kind of object using remote actors?
Like cmbaxter's answer points out, this pattern, where the superclass of the case class does not have a no-arg constructor, leads to
InvalidClassException
on deserialization. Per cmbaxter's answer, avoiding this pattern is one solution.But what's wrong in this pattern? The reason is documented in the API docs for Serializable:
So the problem is that
class A
does not have a no-arg constructor, plus it is notSerializable
. So a simple solution is to make itSerializable
!Things will work if you restructure like so:
I generally try and stay away from class inheritance with case classes. If I need to be able to refer to a set of case classes as an abstract type, I use traits instead.