I'm implementing some abstract classes that extend Actor and provide some extra functionality [1]. However, pattern matching doesn't seem to be working within the receive statements. If I send a case object Connect
from a client to the server, and have a pattern match in the server of the form:
println("Starting to receive, e.g. " + Connect.getClass.toString)
receive {
case Connect => println("Got a connected message")
case m => println("Got something weird: " + m + " of type " + m.getClass.toString)
}
Then the output is
Starting to receive, e.g. class ConnectionTest$Connect$
Got something weird: Connect of type class ConnectionTest$Connect$
...
The incoming message is not recognized as a Connect
object in the pattern match even though getClass says it is. Further weirdness: m
has the same hashCode as Connect
, and serializes to the exact same ByteArray
using ObjectOutputStream
and writeObject
, but is not equal to it (using ==
). My best guess is that the classLoader is somehow behaving incorrectly, but I'm at a loss.
Here is a more complete example of what I'm trying to write:
import scala.actors.{Actor, OutputChannel}
import scala.actors.Actor._
import scala.actors.remote.RemoteActor
import scala.actors.remote.RemoteActor._
import scala.actors.remote.Node
abstract class ConnectionTest(masterNode: Node, port: Int) {
trait Message
case object Connect extends Message
abstract class Master extends Actor {
def act {
RemoteActor.classLoader = getClass.getClassLoader
alive(port)
register('MasterProcess, self)
while (true) {
println("Starting to receive, e.g. " + Connect.getClass.toString)
receive {
case Connect => println("Got a connect message")
case m => println("Got something weird: " + m + " of type " + m.getClass.toString)
}
}
}
}
abstract class Worker extends Actor {
def act {
RemoteActor.classLoader = getClass.getClassLoader
val master = select(masterNode, 'MasterProcess)
link(master)
master ! Connect
}
}
}
Here's an example use:
object MyConnectionApp extends optional.Application {
case class MyConTest(hostname: String, port: Int) extends ConnectionTest(Node(hostname, port), port) {
case object MyMaster extends Master
case object MyWorker extends Worker
}
def main(master: Boolean) = {
if (master)
MyConTest("localhost", 2552).MyMaster start
else
MyConTest("localhost", 2552).MyWorker start
}
}
When I run this program, the output is as above. The Connect
message received remotely from MyWorker
was not recognized by the pattern match in the act method of MyMaster
. Even though getClass.toString
evaluates to the same thing on them, they are somehow not the same. How can I fix this?
[1] More detail: I'm implementing a framework for a certain kind of parallel computation across a large number of nodes. In a more complicated case, I'd actually like to replace ConnectionTest
with ParallelComputation[Data, Result]
where Data
and Result
are type parameters. Message will also include classes that depend on these parameters, like
case object Computed(x: Data, y: Result) extends Message
Ideally I'd like a solution that plays well with this design pattern.
I have not tested, but I think you should not put your Message
trait and implementations (including object Connect
) in class ConnectionTest
. You can put them in a companion object. If you put them in a class, there is a distinct object Connect
for each instance of containing class ConnectionTest
(and worse in the context of serialization, it has a reference to that instance).
objects Connect
belonging to different instances of ConnectionTest are different and do not match each other.