Quantcast

Passing functions to remote actor

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Passing functions to remote actor

Jiansen He
Hello Everyone,

I'm wondering whether functions could be passed between remote actors.  I post my testing code as follows.

Client.scala
============
import scala.actors.Actor
import scala.actors.Actor._
import scala.actors.Exit
import scala.actors.remote.RemoteActor
import scala.actors.remote.RemoteActor._
import scala.actors.remote.Node

object Client{
  def main(args: Array[String]) : Unit = {
    val port = 9001
    val peer = Node("dhcp-90-095.inf.ed.ac.uk", 9000)
    val client = new Client(port, peer, 4)

    client.start()
  }
}

class Client(port: Int, peer: Node, n: Int) extends Actor {
  trapExit = true
/*
  @serializable
  private object Fact extends Prog{
    override def eval (i:Int):Int = {
    println("evaluating...")
    if (i == 0)  1 else i * eval(i-1)
    }
  }
*/
  @serializable
  object Fact extends Function1[Int, Int] {
     RemoteActor.classLoader = getClass().getClassLoader()
RemoteActor.classLoader = classOf[Function1[Int, Int]].getClassLoader()
     def apply(i: Int): Int = {
       println("evaluating...")
       if (i == 0)  1 else i * apply(i-1)
     }
  }

  RemoteActor.classLoader = getClass().getClassLoader()
RemoteActor.classLoader = classOf[Function1[Int, Int]].getClassLoader()
  def act(){
   
    alive(port)
    register('Client, self)
    val server = select(peer, 'Server)
    link(server)
self ! new Request(Fact, n)
    server ! new Request(Fact, n)
println("OK")
    react{
      case Request(pro, i) =>
    sender ! println(pro(i))
      case Response(msg) =>
    println("received message: "+msg.toString)
      case msg =>
        println("unexpected message: "+msg.toString)
      }
    }
}


Server.scala
=================
import scala.actors.Actor
import scala.actors.Actor._
import scala.actors.remote.RemoteActor
import scala.actors.remote.RemoteActor._

abstract class Message
@serializable case class Request(prog: Function1[Int, Int], i:Int) extends Message
@serializable case class Response(msg: String) extends Message

/*
@serializable
trait Prog{eval: Int => Int}
*/

object Server {
    def main(args: Array[String]) : Unit = {
        val port = 9000
        val server = new Server(port)
        server.start()
    }
}

class Serve(port:Int) extends Actor {
    def apply(f: Int => Int, v: Int) = f(v)


    def act(){
      RemoteActor.classLoader = getClass().getClassLoader()
      RemoteActor.classLoader = classOf[Function1[Int, Int]].getClassLoader()

      alive(port)
      register('Server, self)
      println("Server: Started")
      react{
        case Request_7(pro, i) =>
        println("request received")
        sender ! pro(i)
        println("result sent")
      }
    }
}


running result are:

$ scala Server7
Server: Started

$ scala Client7
OK
evaluating...
evaluating...
evaluating...
evaluating...
evaluating...
24
scala.actors.remote.DelegateActor@2880cac9: caught java.io.NotSerializableException: scala.actors.MQueue
java.io.NotSerializableException: scala.actors.MQueue
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1173)
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1526)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1491)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1409)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1167)
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1526)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1491)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1409)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1167)
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1526)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1491)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1409)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1167)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:343)
    at scala.actors.remote.JavaSerializer.serialize(JavaSerializer.scala:46)
    at scala.actors.remote.NetKernel.namedSend(NetKernel.scala:38)
    at scala.actors.remote.NetKernel.forward(NetKernel.scala:71)
    at scala.actors.remote.DelegateActor$$anonfun$act$1$$anonfun$apply$1.apply(Proxy.scala:182)
    at scala.actors.remote.DelegateActor$$anonfun$act$1$$anonfun$apply$1.apply(Proxy.scala:123)
    at scala.actors.ReactorTask.run(ReactorTask.scala:34)
    at scala.actors.ReactorTask.compute(ReactorTask.scala:66)
    at scala.concurrent.forkjoin.RecursiveAction.exec(RecursiveAction.java:147)
    at scala.concurrent.forkjoin.ForkJoinTask.quietlyExec(ForkJoinTask.java:422)
    at scala.concurrent.forkjoin.ForkJoinWorkerThread.mainLoop(ForkJoinWorkerThread.java:340)
    at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:325)


I think this suggests that functions are not serializable.  Any suggestions?

p.s. Isn't there a eval(String):Any function in Scala so that I could passing a script to remote node?

Jiansen He
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Passing functions to remote actor

Stephen Tu

On Mon, Oct 11, 2010 at 10:34 PM, Jiansen He <[hidden email]> wrote:

I think this suggests that functions are not serializable.  Any suggestions?



jiansen,

actually no, functions are serializable (in fact thats how remote link is implemented). the *real* problem is that scala actors are not serializable. there's an open ticket on this somewhere, i'll try to find it for you.

for now, try moving your fact function outside of the client class, and making it a top level class. my guess is that in trying to serialize fact, java serialization tries to also serialize the reference to the outer client actor instance, and thats where your exception is thrown.

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Passing functions to remote actor

Stephen Tu


On Mon, Oct 11, 2010 at 10:45 PM, Stephen Tu <[hidden email]> wrote:

On Mon, Oct 11, 2010 at 10:34 PM, Jiansen He <[hidden email]> wrote:

I think this suggests that functions are not serializable.  Any suggestions?



jiansen,

actually no, functions are serializable (in fact thats how remote link is implemented). the *real* problem is that scala actors are not serializable. there's an open ticket on this somewhere, i'll try to find it for you.


http://lampsvn.epfl.ch/trac/scala/ticket/843

its an old bug which has been postponed. if i have some free time i might try to take a stab at this actually, but probably not for a while
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Passing functions to remote actor

√iktor Klang
Hi Stephen,

if you can't get that to work with Scala Actors, you are more than welcome to join the Akka Community.

Best wishes,

On Tue, Oct 12, 2010 at 7:48 AM, Stephen Tu <[hidden email]> wrote:


On Mon, Oct 11, 2010 at 10:45 PM, Stephen Tu <[hidden email]> wrote:

On Mon, Oct 11, 2010 at 10:34 PM, Jiansen He <[hidden email]> wrote:

I think this suggests that functions are not serializable.  Any suggestions?



jiansen,

actually no, functions are serializable (in fact thats how remote link is implemented). the *real* problem is that scala actors are not serializable. there's an open ticket on this somewhere, i'll try to find it for you.


http://lampsvn.epfl.ch/trac/scala/ticket/843

its an old bug which has been postponed. if i have some free time i might try to take a stab at this actually, but probably not for a while



--
Viktor Klang,
Code Connoisseur
Work:   www.akkasource.com
Code:   github.com/viktorklang
Follow: twitter.com/viktorklang
Read:   klangism.tumblr.com

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Passing functions to remote actor

Jiansen He
Thank you Stephen.  Your trick solved my problem.
And i will be aware of this bug.

Thanks again

Jiansen

On Tue, Oct 12, 2010 at 8:51 AM, √iktor Klang <[hidden email]> wrote:
Hi Stephen,

if you can't get that to work with Scala Actors, you are more than welcome to join the Akka Community.

Best wishes,


On Tue, Oct 12, 2010 at 7:48 AM, Stephen Tu <[hidden email]> wrote:


On Mon, Oct 11, 2010 at 10:45 PM, Stephen Tu <[hidden email]> wrote:

On Mon, Oct 11, 2010 at 10:34 PM, Jiansen He <[hidden email]> wrote:

I think this suggests that functions are not serializable.  Any suggestions?



jiansen,

actually no, functions are serializable (in fact thats how remote link is implemented). the *real* problem is that scala actors are not serializable. there's an open ticket on this somewhere, i'll try to find it for you.


http://lampsvn.epfl.ch/trac/scala/ticket/843

its an old bug which has been postponed. if i have some free time i might try to take a stab at this actually, but probably not for a while



--
Viktor Klang,
Code Connoisseur
Work:   www.akkasource.com
Code:   github.com/viktorklang
Follow: twitter.com/viktorklang
Read:   klangism.tumblr.com


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Passing functions to remote actor

Jiansen He

I'm too careless in testing.  If I separate server program and client program into 2 folders,  server side will raise an Error: scala.actors.remote.NetKernel@f06fc94: caught java.lang.ClassNotFoundException: Fact.

Following are my program

server.scala
========
import scala.actors.Actor
import scala.actors.Actor._
import scala.actors.remote.RemoteActor
import scala.actors.remote.RemoteActor._

abstract class Message
@serializable case class Request(prog: Function1[Int, Any], i:Int) extends Message
@serializable case class Response(msg: String) extends Message

object Server {
    def main(args: Array[String]) : Unit = {
        val port = 9000
        val server = new Server(port)
        server.start()
    }
}

class Server(port:Int) extends Actor {

    def act(){
      RemoteActor.classLoader = getClass().getClassLoader()
      alive(port)
      register('Server, self)

      println("Server: Started")
      loop {
        react{
        case Request(pro, i) =>
        println("request received")
        sender ! Response(pro.apply(i).toString)
        println("result sent")
        }
      }
    }
}


client.scala
========
import scala.actors.Actor
import scala.actors.Actor._
import scala.actors.Exit
import scala.actors.remote.RemoteActor
import scala.actors.remote.RemoteActor._
import scala.actors.remote.Node

object Client {
  def main(args: Array[String]) : Unit = {
    val port = 9001
    val peer = Node("localhost", 9000)
    val client = new Client7(port, peer, 4)

    client.start()
  }
}

@serializable
class Fact extends Function1[Int, Int] {
     def apply(i: Int): Int = {
       println("evaluating...")
       if (i == 0)  1 else i * apply(i-1)
     }
}


@serializable
class IsOdd extends Function1[Int, Boolean] {
     def apply(i: Int): Boolean = {
       println("isOdd: evaluating...")
       if (i == 0)  false else (new IsEven)(i-1)
     }
}

@serializable
class IsEven extends Function1[Int, Boolean] {
     def apply(i: Int): Boolean = {
       println("isEven: evaluating...")
       if (i == 0)  false else (new IsOdd)(i-1)
     }
}

class Client(port: Int, peer: Node, n: Int) extends Actor {
  trapExit = true

  def act(){
    RemoteActor.classLoader = getClass().getClassLoader()
    alive(port)
    register('Client, self)
    val server = select(peer, 'Server)
    link(server)
    val fact = new Fact
    server ! new Request(fact, n)
//    server ! new Request(new IsOdd, n)

    react{
      case Response(msg) =>
    println("received message: "+msg.toString)
      case msg =>
        println("unexpected message: "+msg.toString)
      }
    }
}

==server
$ scalac Server.scala

$ scala Server
Server: Started
Error: scala.actors.remote.NetKernel@f06fc94: caught java.lang.ClassNotFoundException: Fact

==client
$ scalac server.scala
$ scalac client.scala
$ scala Client


I run scalac server.scala at client side to get Message.class

Why should send the server Fact.class?  Doesn't the apply function contained in object fact? 

If I want the server evaluate isOdd(4) for me, shall I know it will require IsEven?


Jiansen


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Passing functions to remote actor

Stephen Tu


On Thu, Oct 21, 2010 at 12:24 PM, Jiansen He <[hidden email]> wrote:

I'm too careless in testing.  If I separate server program and client program into 2 folders,  server side will raise an Error: scala.actors.remote.NetKernel@f06fc94: caught java.lang.ClassNotFoundException: Fact.

Following are my program



Jiansen,

Looks like you are looking for something beyond java serialization, which simply serializes the *state* of the object (which in this case has no state). Your original solution assumes that the Fact class is a valid class defined in both the server and client JVMs.

If you want the server to be able to run any arbitrary code that it is not aware of (ie not on its classpath), you will need a way to pass the bytecode of a class to the server, have it load the class into the JVM, and then create a new instance of that class. while i don't have any sample code to do that immediately, i'm sure you can find some examples on google

hope that helps


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Passing functions to remote actor

Brian Maso

On Thu, Oct 21, 2010 at 1:40 PM, Stephen Tu <[hidden email]> wrote:


On Thu, Oct 21, 2010 at 12:24 PM, Jiansen He <[hidden email]> wrote:

I'm too careless in testing.  If I separate server program and client program into 2 folders,  server side will raise an Error: scala.actors.remote.NetKernel@f06fc94: caught java.lang.ClassNotFoundException: Fact.

Following are my program



Jiansen,

Looks like you are looking for something beyond java serialization, which simply serializes the *state* of the object (which in this case has no state). Your original solution assumes that the Fact class is a valid class defined in both the server and client JVMs.

If you want the server to be able to run any arbitrary code that it is not aware of (ie not on its classpath), you will need a way to pass the bytecode of a class to the server, have it load the class into the JVM, and then create a new instance of that class. while i don't have any sample code to do that immediately, i'm sure you can find some examples on google

hope that helps



Actually, there is an obscure class that supports pushing objects along with a URL where to download the class' bytecode from -- its part of the venerable Java RMI (JRMP protocol) API. Let me see if I can find it...

Ah yes: the java.rmi.MarshalledObject class. Supports dynamically injecting bytecode in to the receiving VM. It works by automatically copying the URL location of an originating object's .class file along with serialized copies of the object. When deserializing the object, can download and inject .class file in to receiving classloader context. Pretty cool stuff. IIRC, this was invented to support the Jini Lookup services, and can trace its origination way way back to an early feature of JBoss allowing RMI proxy objects to travel around with their own bytecode (anyone on this list remember the name "Rickard Oberg"? -- he's the clever guy who originally invented that IIRC).

But you're going to have to get pretty familiar with the Java 2 stack-based Security Policy stuff to get this working if you have a Java SecurityManager installed, and no matter what you're going to have to learn a fair amount about Java classloading.

--
Best regards,
Brian Maso
(949) 395-8551
[hidden email]

Loading...