Thursday, March 1, 2012

Remote Akka Spaces






Expect frequent changes.





AkkaSpace


The idea behind the Remote Akka Space library is (as its name suggests) to implement remote Akka Spaces.

Note: this post is self contained, so, some code similar to code of Akka Spaces has been repeated.
The most important issue to deal with is to avoid a java.io.InvalidClassException because (partial) functions capturing variables of an outer scope have to be serialized.

This has lead to a design where (partial) functions to be applied to arguments in the space actor JVM are separated from matchers that are stored in the space JVM.

For the PingPongApp this results in a perfect solution. For the StickApp there is still an issue because the matchers themselves are (partial) functions capturing variables of an outer scope. So, the distributed version of the StickApp has been implemented in a way that cannot be parameterized over the number of sticks.





I may be missing something here. All comments are welcome.





Messages


case class Put[A](a: A)

case class Reg[A](m: PartialFunction[A, Unit])

case class Get[A](a: A)



The case classes Put[A] and Reg[A] model messages that a space actor can send to a space.


The case class Get[A] models messages that a space can send back to a space actor.






Space


import akka.actor.Actor
import akka.actor.ActorSystem

class Space[A](spaceSystem: ActorSystem) extends Actor {
import collection.immutable.Vector
import akka.actor.ActorRef
import akka.agent.Agent

private val pva =
Agent(Vector[A]())(spaceSystem)
private val rva =
Agent(Vector[(PartialFunction[A, Unit], ActorRef)]())(spaceSystem)

import akka.util.Timeout

implicit val timeout = new Timeout(1000)

override def toString = {
val av = pva.await
val arv = for { (_, ar) <- rva.await } yield ar
"\n" + av + "\n" + arv
}

import concurrent.stm.atomic
import concurrent.stm.InTxn

private def put(a: A) =
atomic {
_ =>
import akka.event.Logging
val log = Logging(context.system, this)
rva.await find {
case (m, _) => m.isDefinedAt(a)
} match {
case None =>
pva send (_ :+ a)
log.info("" + this)
case Some(r) =>
val (m, ar) = r
rva send (_ diff Vector(r))
log.info("" + this)
ar ! Get(a)
}
}

private def reg(m: PartialFunction[A, Unit], ar: ActorRef) =
atomic {
_ =>
import akka.event.Logging
val log = Logging(context.system, this)
pva.await find {
case a => m.isDefinedAt(a)
} match {
case None =>
val r = (m, ar)
rva send (_ :+ r)
log.info("" + this)
case Some(a) =>
pva send (_ diff Vector(a))
log.info("" + this)
ar ! Get(a)
}
}

def receive = {
case Put(a: A) => put(a)
case Reg(m) => reg(m, sender)
case other => sys.error("space: this should never happen")
}
}



A space processes Put[A] and Reg[A] messages.
The space has two vectors of agents


  • pva: a vector of (agents of) argument puts to be matched to matchers
  • rva: a queue of (agents of) matcher regs to be matched to arguments

When receiving an argument put

  • when there is no matching matcher reg, the argument put is added to the vector of arguments puts
  • when there is a matching matcher reg, the argument is sent back to its sender

When receiving a matcher reg

  • when there is no matching argument put, the matcher put is added to the vector of matcher regs
  • when there is a matching argument put, the argument is sent back to its sender






SpaceActor


import akka.actor.Actor

abstract class SpaceActor[A] extends Actor {
initialState()

var pf: PartialFunction[A, Unit] = _

protected def initialState(): Unit

private def get(a: A) {
pf apply a
}

def receive = {
case Get(a: A) => get(a)
case other => sys.error("space actor: this should never happen")
}
}



A space actor processes Get[A] messages.
The space actor has a partial function.
The space actor processes a message by applying its partial function to the message argument.


A space actor starts in its (to be defined) initial state.






Local PingPongTableApp


We are ready for a first local application PingPongTableApp.






PingPong


sealed abstract class PingPong

case class Ping() extends PingPong

case class Pong() extends PingPong

case class FailPing() extends PingPong

case class FailPong() extends PingPong



The sealed abstract class PingPong and the case objects implementing it model the data that drive the application.






PingPongTable


import akka.actor.ActorSystem

case class PingPongTable(spaceSystem: ActorSystem)
extends Space[PingPong](spaceSystem) {
}



The case class PingPongTable models the pingpong table.






Pingers and Pongers


object PingPongGlobal {
import akka.actor.ActorSystem
val pingPongTableSystem = ActorSystem("pingPongTable")
}

import akka.actor.ActorRef

case class Pingers(pingPongTableRef: ActorRef, numberOfPingers: Int)
extends SpaceActor[PingPong] {
import akka.actor.Props

val pingers = for { i <= 1 to numberOfPingers } yield {
context.actorOf(
Props(Pinger(pingPongTableRef)),
name = "pinger_" + i)
}

def shutdownState() {
import akka.event.Logging

val log = Logging(context.system, this)

pf = {
case FailPing() =>
log.info("shutdown")
import PingPongGlobal.pingPongTableSystem
pingPongTableSystem.shutdown()
}

val shutdownMatcher: PartialFunction[PingPong, Unit] = { case FailPing() => }

pingPongTableRef ! Reg(shutdownMatcher)
}

def initialState() {
shutdownState()
}

override def preStart() {
pingPongTableRef.!(Put(Ping()))(pingers.head)
}
}

case class Pongers(pingPongTableRef: ActorRef, numberOfPongers: Int)
extends SpaceActor[PingPong] {
import akka.actor.Props

val pongers = for { i <= 1 to numberOfPongers } yield {
context.actorOf(
Props(Ponger(pingPongTableRef)),
name = "ponger_" + i)
}

def shutdownState() {
import akka.event.Logging

val log = Logging(context.system, this)

pf = {
case FailPong() =>
log.info("shutdown")
import PingPongGlobal.pingPongTableSystem
pingPongTableSystem.shutdown()
}

val shutdownMatcher: PartialFunction[PingPong, Unit] = { case FailPong() => }

pingPongTableRef ! Reg(shutdownMatcher)
}

def initialState() {
shutdownState()
}
}

case class Pinger(pingPongTableRef: ActorRef)
extends SpaceActor[PingPong] {
def pingState() {
import akka.event.Logging

val log = Logging(context.system, this)

pf = {
case Pong() =>
import scala.math.random
import scala.math.round
Thread.sleep(round(1000 * random))
if (random < 0.95) {
log.info("ping")
pingPongTableRef ! Put(Ping())
} else {
log.info("fail ping")
pingPongTableRef ! Put(FailPing())
}
pingState()
}

val pingMatcher: PartialFunction[PingPong, Unit] = { case Pong() => }

pingPongTableRef ! Reg(pingMatcher)
}

def initialState() {
pingState()
}
}

case class Ponger(pingPongTableRef: ActorRef)
extends SpaceActor[PingPong] {
def pongState() {
import akka.event.Logging

val log = Logging(context.system, this)

pf = {
case Ping() =>
import scala.math.random
import scala.math.round
Thread.sleep(round(1000 * random))
if (random < 0.95) {
log.info("pong")
pingPongTableRef ! Put(Pong())
} else {
log.info("fail pong")
pingPongTableRef ! Put(FailPong())
}
pongState()
}

val pongMatcher: PartialFunction[PingPong, Unit] = { case Ping() => }

pingPongTableRef ! Reg(pongMatcher)
}

def initialState() {
pongState()
}
}




A pinger starts in its ping state receiving a Pong from a ponger. This can either succeed, in which case he puts Ping on the pingpong table and stays in its ping state, or fail, in which case he puts a FailPing and a FailPong on the pingpong table.


A ponger starts in its pong state receiving a Ping from a pinger. This can either succeed, in which case he puts Pong on the pingpong table and stays in its pong state, or fail, in which case he puts a FailPong and a FailPing on the pingpong table.


The pingers supervisor creates pingers and starts in its shutdown state receiving a FailPing from a pinger, in which case he cleans up.


The pongers supervisor creates pongers and starts in its shutdown state receiving a FailPong from a ponger, in which case he cleans up.






PingPongTableApp


object PingPongTableApp {
def main(args: Array[String]) = {
import PingPongGlobal.pingPongTableSystem
import akka.actor.Props

val pingPongTableRef = pingPongTableSystem.actorOf(
Props(PingPongTable(pingPongTableSystem)),
name = "pingPongTable")

val numberOfPongers = 2

val pongersRef = pingPongTableSystem.actorOf(
Props(Pongers(pingPongTableRef, numberOfPongers)),
name = "pongers")

val numberOfPingers = 2

val pingersRef = pingPongTableSystem.actorOf(
Props(Pingers(pingPongTableRef, numberOfPingers)),
name = "pingers")

def startup() {
}

def shutdown() {
pingPongTableSystem.shutdown()
}
}
}



The pingpong table app simply installs a pingpong table, its pingers and its pongers.






Running the PingPongTableApp


Running the pingpong table app produces something like below.




[info] Running akka.space.PingPongTableApp
[INFO] [03/02/2012 11:55:36.209] ... [akka://.../pingPongTable]
Vector()
Vector()
[INFO] [03/02/2012 11:55:36.227] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers])
[INFO] [03/02/2012 11:55:36.228] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers])
[INFO] [03/02/2012 11:55:36.233] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../pinger_2])
[INFO] [03/02/2012 11:55:36.235] ... [akka://.../pingPongTable]
Vector(Ping())
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../pinger_2])
[INFO] [03/02/2012 11:55:36.247] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../pinger_2])
[INFO] [03/02/2012 11:55:36.248] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../pinger_2], Actor[.../pinger_1])
[INFO] [03/02/2012 11:55:37.109] ... [.../ponger_1] pong
[INFO] [03/02/2012 11:55:37.113] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../pinger_2], Actor[.../pinger_1], Actor[.../ponger_2])
[INFO] [03/02/2012 11:55:37.115] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../pinger_1], Actor[.../ponger_2])
[INFO] [03/02/2012 11:55:37.497] ... [.../pinger_2] ping
[INFO] [03/02/2012 11:55:37.500] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../pinger_1], Actor[.../ponger_2], Actor[.../ponger_1])
[INFO] [03/02/2012 11:55:37.501] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../pinger_1], Actor[.../ponger_1])
[INFO] [03/02/2012 11:55:37.529] ... [.../ponger_2] fail pong
[INFO] [03/02/2012 11:55:37.534] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../pinger_1], Actor[.../ponger_1], Actor[.../pinger_2])
[INFO] [03/02/2012 11:55:37.536] ... [akka://.../pongers] shutdown
[success] Total time: 5 s, completed Mar 2, 2012 11:55:37 AM





Remote PingPongTableApp


We are ready for a first remote application PingPongTableApp.






Configuration


//# common.conf
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
netty {
hostname = "127.0.0.1"
}
}
}

//# application.conf
pingPongTable {
include "common"

akka {
remote.netty.port = 2552
}
}

pingers {
include "common"

akka {
remote.netty.port = 2553
}
}

pongers {
include "common"

akka {
remote.netty.port = 2554
}
}



Running the pingpong table app remotely is merely a question of configuration.






Pingers and Pongers


// Pingers
pf = {
case FailPing() =>
log.info("shutdown")
pingPongTableRef ! Put(FailPong())
import PingersGlobal.pingersSystem
pingersSystem.shutdown()
}
// Pongers
pf = {
case FailPong() =>
log.info("shutdown")
pingPongTableRef ! Put(FailPing())
import PongersGlobal.pongersSystem
pongersSystem.shutdown()
}



The only programming difference is the definition of the partial function pf of the shutdownState of the Pingers and Pongers.






PingersApp, PongersApp and PingPongTableApp


package akka.space

object PingersGlobal {
import akka.actor.ActorSystem
import com.typesafe.config.ConfigFactory
val pingersSystem = ActorSystem(
"Pingers",
ConfigFactory.load.getConfig("pingers"))
}

import akka.kernel.Bootable

class PingersApplication extends Bootable {
import PingersGlobal.pingersSystem

val pingPongTableRef = pingersSystem.actorFor(
"akka://PingPongTable@127.0.0.1:2552/user/pingPongTable")

import akka.actor.Props

val numberOfPingers = 2

val pingersRef = pingersSystem.actorOf(
Props(Pingers(pingPongTableRef, numberOfPingers)),
name = "pingers")

def startup() {
}

def shutdown() {
pingersSystem.shutdown()
}
}

object PingersApp {
def main(args: Array[String]) = {
val application = new PingersApplication
}
}

package akka.space

object PongersGlobal {
import akka.actor.ActorSystem
import com.typesafe.config.ConfigFactory
val pongersSystem = ActorSystem(
"Pongers",
ConfigFactory.load.getConfig("pongers"))
}

import akka.kernel.Bootable

class PongersApplication extends Bootable {
import PongersGlobal.pongersSystem

val pingPongTableRef = pongersSystem.actorFor(
"akka://PingPongTable@127.0.0.1:2552/user/pingPongTable")

import akka.actor.Props

val numberOfPongers = 2

val pongersRef = pongersSystem.actorOf(
Props(Pongers(pingPongTableRef, numberOfPongers)),
name = "pongers")

def startup() {
}

def shutdown() {
pongersSystem.shutdown()
}
}

object PongersApp {
def main(args: Array[String]) = {
val application = new PongersApplication
}
}

package akka.space

object PingPongTableGlobal {
import akka.actor.ActorSystem
import com.typesafe.config.ConfigFactory

val pingPongTableSystem = ActorSystem(
"PingPongTable",
ConfigFactory.load.getConfig("pingPongTable"))
}

import akka.kernel.Bootable

class PingPongTableApplication extends Bootable {
import PingPongTableGlobal.pingPongTableSystem
import akka.actor.Props

val pingPongTableRef = pingPongTableSystem.actorOf(
Props(PingPongTable(pingPongTableSystem)),
name = "pingPongTable")

def startup() {
}

def shutdown() {
pingPongTableSystem.shutdown()
}
}

object PingPongTableApp {
def main(args: Array[String]) = {
val application = new PingPongTableApplication
}
}



The three applications are similar to the (only) local one.






Running the PingersApp, PongersApp and PingPongTableApp


Running the apps produces something like below.




[info] Running akka.space.PingPongTableApp
[INFO] [03/02/2012 12:16:22.586] [run-main] [ActorSystem(PingPongTable)] REMOTE: RemoteServerStarted@akka://PingPongTable@127.0.0.1:2552
[INFO] [03/02/2012 12:16:27.326] [PingPongTable-7] [ActorSystem(PingPongTable)] REMOTE: RemoteClientStarted@akka://Pongers@127.0.0.1:2554
[INFO] [03/02/2012 12:16:27.439] ... [akka://.../pingPongTable]
Vector()
Vector()
[INFO] [03/02/2012 12:16:27.477] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers])
[INFO] [03/02/2012 12:16:27.489] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[.../ponger_1])
[INFO] [03/02/2012 12:16:31.389] [PingPongTable-7] [ActorSystem(PingPongTable)] REMOTE: RemoteClientStarted@akka://Pingers@127.0.0.1:2553
[INFO] [03/02/2012 12:16:31.408] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[.../ponger_1], Actor[.../ponger_2])
[INFO] [03/02/2012 12:16:31.425] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[.../ponger_1], Actor[.../ponger_2], Actor[akka://.../pingers])
[INFO] [03/02/2012 12:16:31.437] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[.../ponger_1], Actor[.../ponger_2], Actor[akka://.../pingers], Actor[.../pinger_1])
[INFO] [03/02/2012 12:16:31.478] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[.../ponger_2], Actor[akka://.../pingers], Actor[.../pinger_1])
[INFO] [03/02/2012 12:16:32.16] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[.../ponger_2], Actor[akka://.../pingers], Actor[.../pinger_1], Actor[.../pinger_2])
[INFO] [03/02/2012 12:16:32.18] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[.../ponger_2], Actor[akka://.../pingers], Actor[.../pinger_2])
[INFO] [03/02/2012 12:16:32.702] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[.../ponger_2], Actor[akka://.../pingers], Actor[.../pinger_2], Actor[.../ponger_1])
[INFO] [03/02/2012 12:16:32.707] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../pinger_2], Actor[.../ponger_1])
[INFO] [03/02/2012 12:16:33.546] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../pinger_2], Actor[.../ponger_1], Actor[.../pinger_1])
[INFO] [03/02/2012 12:16:33.552] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../ponger_1], Actor[.../pinger_1])
[INFO] [03/02/2012 12:16:34.82] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../ponger_1], Actor[.../pinger_1], Actor[.../ponger_2])
[INFO] [03/02/2012 12:16:34.97] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../pinger_1], Actor[.../ponger_2])
[INFO] [03/02/2012 12:16:34.293] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../pinger_1], Actor[.../ponger_2], Actor[.../pinger_2])
[INFO] [03/02/2012 12:16:34.298] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../ponger_2], Actor[.../pinger_2])
[INFO] [03/02/2012 12:16:34.735] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../ponger_2], Actor[.../pinger_2], Actor[.../ponger_1])
[INFO] [03/02/2012 12:16:34.739] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../pinger_2], Actor[.../ponger_1])
[INFO] [03/02/2012 12:16:35.332] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../pinger_2], Actor[.../ponger_1], Actor[.../pinger_1])
[INFO] [03/02/2012 12:16:35.337] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../ponger_1], Actor[.../pinger_1])
[INFO] [03/02/2012 12:16:35.850] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../ponger_1], Actor[.../pinger_1], Actor[.../ponger_2])
[INFO] [03/02/2012 12:16:35.856] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../pinger_1], Actor[.../ponger_2])
[INFO] [03/02/2012 12:16:36.774] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../pinger_1], Actor[.../ponger_2], Actor[.../pinger_2])
[INFO] [03/02/2012 12:16:36.779] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../ponger_2], Actor[.../pinger_2])
[INFO] [03/02/2012 12:16:36.934] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../ponger_2], Actor[.../pinger_2], Actor[.../ponger_1])
[INFO] [03/02/2012 12:16:36.937] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../pinger_2], Actor[.../ponger_1])
[INFO] [03/02/2012 12:16:37.176] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pongers], Actor[akka://.../pingers], Actor[.../pinger_2], Actor[.../ponger_1], Actor[.../pinger_1])
[INFO] [03/02/2012 12:16:37.200] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[akka://.../pingers], Actor[.../pinger_2], Actor[.../ponger_1], Actor[.../pinger_1])
[INFO] [03/02/2012 12:16:37.212] ... [akka://.../pingPongTable]
Vector()
Vector(Actor[.../pinger_2], Actor[.../ponger_1], Actor[.../pinger_1])


[info] Running akka.space.PongersApp
[INFO] [03/02/2012 12:16:27.44] [run-main] [ActorSystem(Pongers)] REMOTE: RemoteServerStarted@akka://Pongers@127.0.0.1:2554
[INFO] [03/02/2012 12:16:27.262] ... [ActorSystem(Pongers)] REMOTE: RemoteClientStarted@akka://PingPongTable@127.0.0.1:2552
[INFO] [03/02/2012 12:16:31.993] ... [akka://Pongers/user/pongers/ponger_1] pong
[INFO] [03/02/2012 12:16:33.541] ... [akka://Pongers/user/pongers/ponger_2] pong
[INFO] [03/02/2012 12:16:34.288] ... [akka://Pongers/user/pongers/ponger_1] pong
[INFO] [03/02/2012 12:16:35.327] ... [akka://Pongers/user/pongers/ponger_2] pong
[INFO] [03/02/2012 12:16:36.769] ... [akka://Pongers/user/pongers/ponger_1] pong
[INFO] [03/02/2012 12:16:37.166] ... [akka://Pongers/user/pongers/ponger_2] fail pong
[INFO] [03/02/2012 12:16:37.182] ... [akka://Pongers/user/pongers] shutdown
[INFO] [03/02/2012 12:16:37.223] ... [ActorSystem(Pongers)] REMOTE: RemoteClientShutdown@akka://PingPongTable@127.0.0.1:2552
[INFO] [03/02/2012 12:16:37.248] ... [ActorSystem(Pongers)] REMOTE: RemoteServerShutdown@akka://Pongers@127.0.0.1:2554
[success] Total time: 24 s, completed Mar 2, 2012 12:16:37 PM



[info] Running akka.space.PingersApp
[INFO] [03/02/2012 12:16:31.215] [run-main] [ActorSystem(Pingers)] REMOTE: RemoteServerStarted@akka://Pingers@127.0.0.1:2553
[INFO] [03/02/2012 12:16:31.387] ... [ActorSystem(Pingers)] REMOTE: RemoteClientStarted@akka://PingPongTable@127.0.0.1:2552
[INFO] [03/02/2012 12:16:32.696] ... [akka://Pingers/user/pingers/pinger_1] ping
[INFO] [03/02/2012 12:16:34.76] ... [akka://Pingers/user/pingers/pinger_2] ping
[INFO] [03/02/2012 12:16:34.729] ... [akka://Pingers/user/pingers/pinger_1] ping
[INFO] [03/02/2012 12:16:35.846] ... [akka://Pingers/user/pingers/pinger_2] ping
[INFO] [03/02/2012 12:16:36.926] ... [akka://Pingers/user/pingers/pinger_1] ping
[INFO] [03/02/2012 12:16:37.204] ... [akka://Pingers/user/pingers] shutdown
[INFO] [03/02/2012 12:16:37.250] ... [ActorSystem(Pingers)] REMOTE: RemoteClientShutdown@akka://PingPongTable@127.0.0.1:2552
[INFO] [03/02/2012 12:16:37.278] ... [ActorSystem(Pingers)] REMOTE: RemoteServerShutdown@akka://Pingers@127.0.0.1:2553
[success] Total time: 20 s, completed Mar 2, 2012 12:16:37 PM





Remote StickTableApp


So what about a second remote application StickTableApp?

Changing the stick table app from local to remote is not as simple as changing the pingpong table app from local to remote because the matchers are more complex.

A hacker registers a matcher for a stick on his left or on his right, so the matcher refers to an instance variable (left or right) of the hacker.

Therefore the remote application deals with a fixed number (say, 5) of sticks.






Configuration


//# application.conf
stickTable {
include "common"

akka {
remote.netty.port = 2555
}
}

hackers {
include "common"

akka {
remote.netty.port = 2556
}
}



Running the stick table app remotely is, again, merely a question of configuration.






Stick


sealed abstract class Stick

case class Stick1() extends Stick

case class Stick2() extends Stick

case class Stick3() extends Stick

case class Stick4() extends Stick

case class Stick5() extends Stick



The sealed abstract class Stick and the case objects implementing it model the data that drive the application.






StickTable


import akka.actor.ActorSystem

case class StickTable(spaceSystem: ActorSystem)
extends Space[Stick](spaceSystem) {
override def preStart() {
self ! Put(Stick1())
self ! Put(Stick2())
self ! Put(Stick3())
self ! Put(Stick4())
self ! Put(Stick5())
}
}



The case class StickTable models the stick table.

Note that the 5 sticks are put on it in its preStart method.






Hackers


import akka.actor.Actor
import akka.actor.ActorRef

case class Hackers(stickTableRef: ActorRef)
extends Actor {
import akka.actor.Props

context.actorOf(
Props(Hakker1(stickTableRef,
4000, 4000, 5000, 10000)),
name = "hacker_" + 1)

context.actorOf(
Props(Hakker2(stickTableRef,
4000, 4000, 5000, 10000)),
name = "hacker_" + 2)

context.actorOf(
Props(Hakker3(stickTableRef,
4000, 4000, 5000, 10000)),
name = "hacker_" + 3)

context.actorOf(
Props(Hakker4(stickTableRef,
4000, 4000, 5000, 10000)),
name = "hacker_" + 4)

context.actorOf(
Props(Hakker5(stickTableRef,
5000, 4000, 5000, 10000)),
name = "hacker_" + 5)

def receive: Receive = {
case _ =>
}
}

abstract class Hacker(
stickTableRef: ActorRef,
startTime: Long, thinkTime: Long, eatTime: Long, delayTime: Long)
extends SpaceActor[Stick] {
import scala.math.random
import scala.math.round

protected def takeLeftState()

protected def takeRightState()

protected def putBackSticks()

private def eat() {
import akka.event.Logging

val log = Logging(context.system, this)

log.info("starts eating")
Thread.sleep(round(eatTime * random))
log.info("stops eating")
}

private def think() {
import akka.event.Logging

val log = Logging(context.system, this)

log.info("starts thinking")
Thread.sleep(round(thinkTime * random))
log.info("stops thinking")
}

protected def hack() {
eat()
putBackSticks()
think()
}

def initialState() {
Thread.sleep(round(startTime * random))
takeLeftState()
}
}

case class Hakker1(
stickTableRef: ActorRef,
startTime: Long, thinkTime: Long, eatTime: Long, delayTime: Long)
extends Hacker(stickTableRef,
startTime, thinkTime, eatTime, delayTime) {

import scala.math.random
import scala.math.round

def takeLeftState() {
import akka.event.Logging

val log = Logging(context.system, this)
pf = {
case l @ Stick1() => {
log.info("has taken " + l)
takeRightState()
}
}

val takeLeftMatcher: PartialFunction[Stick, Unit] = { case Stick1() => }

stickTableRef ! Reg(takeLeftMatcher)
}

def takeRightState() {
import akka.event.Logging

val log = Logging(context.system, this)
pf = {
case r @ Stick2() => {
log.info("has taken " + r)
hack()
takeLeftState()
}
}

Thread.sleep(round(delayTime * random))

val takeRightMatcher: PartialFunction[Stick, Unit] = { case Stick2() => }

stickTableRef ! Reg(takeRightMatcher)
}

def putBackSticks() {
import akka.event.Logging

val log = Logging(context.system, this)

val l = Stick1()
log.info("puts back " + l)
stickTableRef ! Put(l)
val r = Stick2()
log.info("puts back " + r)
stickTableRef ! Put(r)
}

}

case class Hakker2(
stickTableRef: ActorRef,
startTime: Long, thinkTime: Long, eatTime: Long, delayTime: Long)
extends Hacker(stickTableRef,
startTime, thinkTime, eatTime, delayTime) {

import scala.math.random
import scala.math.round

def takeLeftState() {
import akka.event.Logging

val log = Logging(context.system, this)
pf = {
case l @ Stick2() => {
log.info("has taken " + l)
takeRightState()
}
}

val takeLeftMatcher: PartialFunction[Stick, Unit] = { case Stick2() => }

stickTableRef ! Reg(takeLeftMatcher)
}

def takeRightState() {
import akka.event.Logging

val log = Logging(context.system, this)
pf = {
case r @ Stick3() => {
log.info("has taken " + r)
hack()
takeLeftState()
}
}

Thread.sleep(round(delayTime * random))

val takeRightMatcher: PartialFunction[Stick, Unit] = { case Stick3() => }

stickTableRef ! Reg(takeRightMatcher)
}

def putBackSticks() {
import akka.event.Logging

val log = Logging(context.system, this)

val l = Stick2()
log.info("puts back " + l)
stickTableRef ! Put(l)
val r = Stick3()
log.info("puts back " + r)
stickTableRef ! Put(r)
}
}

case class Hakker3(
stickTableRef: ActorRef,
startTime: Long, thinkTime: Long, eatTime: Long, delayTime: Long)
extends Hacker(stickTableRef,
startTime, thinkTime, eatTime, delayTime) {

import scala.math.random
import scala.math.round

def takeLeftState() {
import akka.event.Logging

val log = Logging(context.system, this)
pf = {
case l @ Stick3() => {
log.info("has taken " + l)
takeRightState()
}
}

val takeLeftMatcher: PartialFunction[Stick, Unit] = { case Stick3() => }

stickTableRef ! Reg(takeLeftMatcher)
}

def takeRightState() {
import akka.event.Logging

val log = Logging(context.system, this)
pf = {
case r @ Stick4() => {
log.info("has taken " + r)
hack()
takeLeftState()
}
}

Thread.sleep(round(delayTime * random))

val takeRightMatcher: PartialFunction[Stick, Unit] = { case Stick4() => }

stickTableRef ! Reg(takeRightMatcher)
}

def putBackSticks() {
import akka.event.Logging

val log = Logging(context.system, this)

val l = Stick3()
log.info("puts back " + l)
stickTableRef ! Put(l)
val r = Stick4()
log.info("puts back " + r)
stickTableRef ! Put(r)
}
}

case class Hakker4(
stickTableRef: ActorRef,
startTime: Long, thinkTime: Long, eatTime: Long, delayTime: Long)
extends Hacker(stickTableRef,
startTime, thinkTime, eatTime, delayTime) {

import scala.math.random
import scala.math.round

def takeLeftState() {
import akka.event.Logging

val log = Logging(context.system, this)
pf = {
case l @ Stick4() => {
log.info("has taken " + l)
takeRightState()
}
}

val takeLeftMatcher: PartialFunction[Stick, Unit] = { case Stick4() => }

stickTableRef ! Reg(takeLeftMatcher)
}

def takeRightState() {
import akka.event.Logging

val log = Logging(context.system, this)
pf = {
case r @ Stick5() => {
log.info("has taken " + r)
hack()
takeLeftState()
}
}

Thread.sleep(round(delayTime * random))

val takeRightMatcher: PartialFunction[Stick, Unit] = { case Stick5() => }

stickTableRef ! Reg(takeRightMatcher)
}

def putBackSticks() {
import akka.event.Logging

val log = Logging(context.system, this)

val l = Stick4()
log.info("puts back " + l)
stickTableRef ! Put(l)
val r = Stick5()
log.info("puts back " + r)
stickTableRef ! Put(r)
}
}

case class Hakker5(
stickTableRef: ActorRef,
startTime: Long, thinkTime: Long, eatTime: Long, delayTime: Long)
extends Hacker(stickTableRef,
startTime, thinkTime, eatTime, delayTime) {

import scala.math.random
import scala.math.round

def takeLeftState() {
import akka.event.Logging

val log = Logging(context.system, this)
pf = {
case l @ Stick5() => {
log.info("has taken " + l)
takeRightState()
}
}

val takeLeftMatcher: PartialFunction[Stick, Unit] = { case Stick5() => }

stickTableRef ! Reg(takeLeftMatcher)
}

def takeRightState() {
import akka.event.Logging

val log = Logging(context.system, this)
pf = {
case r @ Stick1() => {
log.info("has taken " + r)
hack()
takeLeftState()
}
}

Thread.sleep(round(delayTime * random))

val takeRightMatcher: PartialFunction[Stick, Unit] = { case Stick1() => }

stickTableRef ! Reg(takeRightMatcher)
}

def putBackSticks() {
import akka.event.Logging

val log = Logging(context.system, this)

val l = Stick5()
log.info("puts back " + l)
stickTableRef ! Put(l)
val r = Stick1()
log.info("puts back " + r)
stickTableRef ! Put(r)
}
}



A hacker starts in its take left state taking the Stick left to it, transitioning to its take right state taking the Stick right to it.


When he has taken both sticks, he starts eating, puts back the sticks, thinks for a moment, and transitions back to its take left state.






HackersApp and StickTableApp


package akka.space

import akka.kernel.Bootable

class HackersApplication extends Bootable {
import akka.actor.ActorSystem
import com.typesafe.config.ConfigFactory

val hackersSystem = ActorSystem(
"Hackers",
ConfigFactory.load.getConfig("hackers"))

val stickTableRef = hackersSystem.actorFor(
"akka://StickTable@127.0.0.1:2555/user/stickTable")

import akka.actor.Props

val hackersRef = hackersSystem.actorOf(
Props(Hackers(stickTableRef)),
name = "hackers")

def startup() {
}

def shutdown() {
hackersSystem.shutdown()
}
}

object HackersApp {
def main(args: Array[String]) = {
val application = new HackersApplication
}
}

package akka.space

import akka.kernel.Bootable

class StickTableApplication extends Bootable {
import akka.actor.ActorSystem
import com.typesafe.config.ConfigFactory

val stickTableSystem = ActorSystem(
"StickTable",
ConfigFactory.load.getConfig("stickTable"))

import akka.actor.Props

val stickTableRef = stickTableSystem.actorOf(
Props(StickTable(stickTableSystem)),
name = "stickTable")

import akka.event.Logging

val log = Logging(stickTableSystem, stickTableRef)

log.info("stickTableRef: " + stickTableRef)

def startup() {
}

def shutdown() {
stickTableSystem.shutdown()
}
}

object StickTableApp {
def main(args: Array[String]) = {
val application = new StickTableApplication
}
}



The two applications are similar to the (only) local one.






Running the HackersApp and StickTableApp


Running the apps produces something like below (in this case an immediate deadlock because of a long delay time).




[info] Running akka.space.StickTableApp
[INFO] [03/02/2012 13:45:43.952] [run-main] [ActorSystem(StickTable)] REMOTE: RemoteServerStarted@akka://StickTable@127.0.0.1:2555
[INFO] [03/02/2012 13:45:43.977] [run-main] [akka://StickTable/user/stickTable] stickTableRef: Actor[akka://StickTable/user/stickTable]
[INFO] [03/02/2012 13:45:44.113] ... [akka://StickTable/user/stickTable]
Vector()
Vector()
[INFO] [03/02/2012 13:45:44.134] ... [akka://StickTable/user/stickTable]
Vector(Stick1())
Vector()
[INFO] [03/02/2012 13:45:44.135] ... [akka://StickTable/user/stickTable]
Vector(Stick1(), Stick2())
Vector()
[INFO] [03/02/2012 13:45:44.138] ... [akka://StickTable/user/stickTable]
Vector(Stick1(), Stick2(), Stick3())
Vector()
[INFO] [03/02/2012 13:45:44.138] ... [akka://StickTable/user/stickTable]
Vector(Stick1(), Stick2(), Stick3(), Stick4())
Vector()
[INFO] [03/02/2012 13:45:54.714] [StickTable-7] [ActorSystem(StickTable)] REMOTE: RemoteClientStarted@akka://Hackers@127.0.0.1:2556
[INFO] [03/02/2012 13:45:54.743] ... [akka://StickTable/user/stickTable]
Vector(Stick1(), Stick2(), Stick3(), Stick4(), Stick5())
Vector()
[INFO] [03/02/2012 13:45:54.762] ... [akka://StickTable/user/stickTable]
Vector(Stick1(), Stick3(), Stick4(), Stick5())
Vector()
[INFO] [03/02/2012 13:45:54.879] ... [akka://StickTable/user/stickTable]
Vector(Stick1(), Stick4(), Stick5())
Vector()
[INFO] [03/02/2012 13:45:56.962] ... [akka://StickTable/user/stickTable]
Vector(Stick4(), Stick5())
Vector()
[INFO] [03/02/2012 13:45:57.198] ... [akka://StickTable/user/stickTable]
Vector(Stick4(), Stick5())
Vector(Actor[akka://.../hacker_2])
[INFO] [03/02/2012 13:45:58.296] ... [akka://StickTable/user/stickTable]
Vector(Stick5())
Vector(Actor[akka://.../hacker_2])
[INFO] [03/02/2012 13:46:01.347] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka://.../hacker_2])
[INFO] [03/02/2012 13:46:02.186] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka://.../hacker_2], Actor[akka://.../hacker_5])
[INFO] [03/02/2012 13:46:04.725] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka://.../hacker_2], Actor[akka://.../hacker_5], Actor[akka://.../hacker_1])
[INFO] [03/02/2012 13:46:06.628] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka://.../hacker_2], Actor[akka://.../hacker_5], Actor[akka://.../hacker_1], Actor[akka://.../hacker_3])


[info] Running akka.space.HackersApp
[INFO] [03/02/2012 13:45:53.239] [run-main] [ActorSystem(Hackers)] REMOTE: RemoteServerStarted@akka://Hackers@127.0.0.1:2556
[INFO] [03/02/2012 13:45:54.650] ... [ActorSystem(Hackers)] REMOTE: RemoteClientStarted@akka://StickTable@127.0.0.1:2555
[INFO] [03/02/2012 13:45:54.779] ... [akka://Hackers/.../hacker_2] has taken Stick2()
[INFO] [03/02/2012 13:45:54.805] ... [akka://Hackers/.../hacker_3] has taken Stick3()
[INFO] [03/02/2012 13:45:54.885] ... [akka://Hackers/.../hacker_1] has taken Stick1()
[INFO] [03/02/2012 13:45:57.205] ... [akka://Hackers/.../hacker_4] has taken Stick4()
[INFO] [03/02/2012 13:45:58.301] ... [akka://Hackers/.../hacker_5] has taken Stick5()








Running the apps may also produce something like below (in this case no deadlock (yet) because of a short delay time).





[info] Running akka.space.StickTableApp
[INFO] [03/02/2012 14:04:25.97] [run-main] [ActorSystem(StickTable)] REMOTE: RemoteServerStarted@akka://StickTable@127.0.0.1:2555
[INFO] [03/02/2012 14:04:25.118] [run-main] [akka://StickTable/user/stickTable] stickTableRef: Actor[akka://StickTable/user/stickTable]
[INFO] [03/02/2012 14:04:25.254] ... [akka://StickTable/user/stickTable]
Vector()
Vector()
[INFO] [03/02/2012 14:04:25.271] ... [akka://StickTable/user/stickTable]
Vector(Stick1())
Vector()
[INFO] [03/02/2012 14:04:25.273] ... [akka://StickTable/user/stickTable]
Vector(Stick1(), Stick2())
Vector()
[INFO] [03/02/2012 14:04:25.274] ... [akka://StickTable/user/stickTable]
Vector(Stick1(), Stick2(), Stick3())
Vector()
[INFO] [03/02/2012 14:04:25.281] ... [akka://StickTable/user/stickTable]
Vector(Stick1(), Stick2(), Stick3(), Stick4())
Vector()
[INFO] [03/02/2012 14:04:34.5] [StickTable-7] [ActorSystem(StickTable)] REMOTE: RemoteClientStarted@akka://Hackers@127.0.0.1:2556
[INFO] [03/02/2012 14:04:34.23] ... [akka://StickTable/user/stickTable]
Vector(Stick1(), Stick2(), Stick3(), Stick4(), Stick5())
Vector()
[INFO] [03/02/2012 14:04:34.492] ... [akka://StickTable/user/stickTable]
Vector(Stick2(), Stick3(), Stick4(), Stick5())
Vector()
[INFO] [03/02/2012 14:04:34.798] ... [akka://StickTable/user/stickTable]
Vector(Stick2(), Stick3(), Stick4())
Vector()
[INFO] [03/02/2012 14:04:34.966] ... [akka://StickTable/user/stickTable]
Vector(Stick2(), Stick3(), Stick4())
Vector(Actor[akka:....../hacker_5])
[INFO] [03/02/2012 14:04:35.657] ... [akka://StickTable/user/stickTable]
Vector(Stick3(), Stick4())
Vector(Actor[akka:....../hacker_5])
[INFO] [03/02/2012 14:04:36.217] ... [akka://StickTable/user/stickTable]
Vector(Stick4())
Vector(Actor[akka:....../hacker_5])
[INFO] [03/02/2012 14:04:36.305] ... [akka://StickTable/user/stickTable]
Vector(Stick4())
Vector(Actor[akka:....../hacker_5], Actor[akka:....../hacker_2])
[INFO] [03/02/2012 14:04:36.462] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_5], Actor[akka:....../hacker_2])
[INFO] [03/02/2012 14:04:36.886] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_5], Actor[akka:....../hacker_2], Actor[akka:....../hacker_3])
[INFO] [03/02/2012 14:04:38.860] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_5], Actor[akka:....../hacker_2], Actor[akka:....../hacker_3], Actor[akka:....../hacker_4])
[INFO] [03/02/2012 14:04:38.863] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_2], Actor[akka:....../hacker_3], Actor[akka:....../hacker_4])
[INFO] [03/02/2012 14:04:39.650] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_3], Actor[akka:....../hacker_4])
[INFO] [03/02/2012 14:04:41.97] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_3], Actor[akka:....../hacker_4], Actor[akka:....../hacker_2])
[INFO] [03/02/2012 14:04:41.102] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_3], Actor[akka:....../hacker_2])
[INFO] [03/02/2012 14:04:41.498] ... [akka://StickTable/user/stickTable]
Vector(Stick1())
Vector(Actor[akka:....../hacker_3], Actor[akka:....../hacker_2])
[INFO] [03/02/2012 14:04:42.290] ... [akka://StickTable/user/stickTable]
Vector(Stick1())
Vector(Actor[akka:....../hacker_3], Actor[akka:....../hacker_2], Actor[akka:....../hacker_5])
[INFO] [03/02/2012 14:04:42.793] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_3], Actor[akka:....../hacker_2], Actor[akka:....../hacker_5])
[INFO] [03/02/2012 14:04:42.795] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_2], Actor[akka:....../hacker_5])
[INFO] [03/02/2012 14:04:42.939] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_2])
[INFO] [03/02/2012 14:04:42.995] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_2], Actor[akka:....../hacker_5])
[INFO] [03/02/2012 14:04:45.798] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_2], Actor[akka:....../hacker_5], Actor[akka:....../hacker_1])
[INFO] [03/02/2012 14:04:45.802] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_5], Actor[akka:....../hacker_1])
[INFO] [03/02/2012 14:04:45.887] ... [akka://StickTable/user/stickTable]
Vector(Stick4())
Vector(Actor[akka:....../hacker_5], Actor[akka:....../hacker_1])
[INFO] [03/02/2012 14:04:45.897] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_5], Actor[akka:....../hacker_1])
[INFO] [03/02/2012 14:04:45.903] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_5])
[INFO] [03/02/2012 14:04:46.380] ... [akka://StickTable/user/stickTable]
Vector(Stick3())
Vector(Actor[akka:....../hacker_5])
[INFO] [03/02/2012 14:04:46.942] ... [akka://StickTable/user/stickTable]
Vector(Stick3())
Vector(Actor[akka:....../hacker_5], Actor[akka:....../hacker_4])
[INFO] [03/02/2012 14:04:47.364] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_5], Actor[akka:....../hacker_4])
[INFO] [03/02/2012 14:04:49.100] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_5], Actor[akka:....../hacker_4], Actor[akka:....../hacker_3])
[INFO] [03/02/2012 14:04:49.104] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_4], Actor[akka:....../hacker_3])
[INFO] [03/02/2012 14:04:49.134] ... [akka://StickTable/user/stickTable]
Vector(Stick2())
Vector(Actor[akka:....../hacker_4], Actor[akka:....../hacker_3])
[INFO] [03/02/2012 14:04:49.841] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_4], Actor[akka:....../hacker_3])
[INFO] [03/02/2012 14:04:51.560] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_4], Actor[akka:....../hacker_3], Actor[akka:....../hacker_2])
[INFO] [03/02/2012 14:04:52.773] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_4], Actor[akka:....../hacker_3], Actor[akka:....../hacker_2], Actor[akka:....../hacker_1])
[INFO] [03/02/2012 14:04:52.775] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_3], Actor[akka:....../hacker_2], Actor[akka:....../hacker_1])
[INFO] [03/02/2012 14:04:53.468] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_3], Actor[akka:....../hacker_2])
[INFO] [03/02/2012 14:04:53.910] ... [akka://StickTable/user/stickTable]
Vector()
Vector(Actor[akka:....../hacker_3], Actor[akka:....../hacker_2], Actor[akka:....../hacker_1])


[info] Running akka.space.HackersApp
[INFO] [03/02/2012 14:04:33.693] [run-main] [ActorSystem(Hackers)] REMOTE: RemoteServerStarted@akka://Hackers@127.0.0.1:2556
[INFO] [03/02/2012 14:04:33.930] ... [ActorSystem(Hackers)] REMOTE: RemoteClientStarted@akka://StickTable@127.0.0.1:2555
[INFO] [03/02/2012 14:04:34.64] ... [akka://Hackers/.../hacker_1] has taken Stick1()
[INFO] [03/02/2012 14:04:34.508] ... [akka://Hackers/.../hacker_5] has taken Stick5()
[INFO] [03/02/2012 14:04:34.974] ... [akka://Hackers/.../hacker_1] has taken Stick2()
[INFO] [03/02/2012 14:04:34.975] ... [akka://Hackers/.../hacker_1] starts eating
[INFO] [03/02/2012 14:04:35.661] ... [akka://Hackers/.../hacker_3] has taken Stick3()
[INFO] [03/02/2012 14:04:36.310] ... [akka://Hackers/.../hacker_4] has taken Stick4()
[INFO] [03/02/2012 14:04:38.843] ... [akka://Hackers/.../hacker_1] stops eating
[INFO] [03/02/2012 14:04:38.843] ... [akka://Hackers/.../hacker_1] puts back Stick1()
[INFO] [03/02/2012 14:04:38.858] ... [akka://Hackers/.../hacker_1] puts back Stick2()
[INFO] [03/02/2012 14:04:38.859] ... [akka://Hackers/.../hacker_1] starts thinking
[INFO] [03/02/2012 14:04:38.862] ... [akka://Hackers/.../hacker_5] has taken Stick1()
[INFO] [03/02/2012 14:04:38.862] ... [akka://Hackers/.../hacker_5] starts eating
[INFO] [03/02/2012 14:04:38.864] ... [akka://Hackers/.../hacker_2] has taken Stick2()
[INFO] [03/02/2012 14:04:41.92] ... [akka://Hackers/.../hacker_5] stops eating
[INFO] [03/02/2012 14:04:41.92] ... [akka://Hackers/.../hacker_5] puts back Stick5()
[INFO] [03/02/2012 14:04:41.100] ... [akka://Hackers/.../hacker_5] puts back Stick1()
[INFO] [03/02/2012 14:04:41.101] ... [akka://Hackers/.../hacker_5] starts thinking
[INFO] [03/02/2012 14:04:41.101] ... [akka://Hackers/.../hacker_4] has taken Stick5()
[INFO] [03/02/2012 14:04:41.101] ... [akka://Hackers/.../hacker_4] starts eating
[INFO] [03/02/2012 14:04:41.495] ... [akka://Hackers/.../hacker_5] stops thinking
[INFO] [03/02/2012 14:04:42.284] ... [akka://Hackers/.../hacker_1] stops thinking
[INFO] [03/02/2012 14:04:42.293] ... [akka://Hackers/.../hacker_1] has taken Stick1()
[INFO] [03/02/2012 14:04:42.788] ... [akka://Hackers/.../hacker_4] stops eating
[INFO] [03/02/2012 14:04:42.790] ... [akka://Hackers/.../hacker_4] puts back Stick4()
[INFO] [03/02/2012 14:04:42.790] ... [akka://Hackers/.../hacker_4] puts back Stick5()
[INFO] [03/02/2012 14:04:42.791] ... [akka://Hackers/.../hacker_4] starts thinking
[INFO] [03/02/2012 14:04:42.794] ... [akka://Hackers/.../hacker_3] has taken Stick4()
[INFO] [03/02/2012 14:04:42.794] ... [akka://Hackers/.../hacker_3] starts eating
[INFO] [03/02/2012 14:04:42.796] ... [akka://Hackers/.../hacker_5] has taken Stick5()
[INFO] [03/02/2012 14:04:45.793] ... [akka://Hackers/.../hacker_3] stops eating
[INFO] [03/02/2012 14:04:45.794] ... [akka://Hackers/.../hacker_3] puts back Stick3()
[INFO] [03/02/2012 14:04:45.794] ... [akka://Hackers/.../hacker_3] puts back Stick4()
[INFO] [03/02/2012 14:04:45.795] ... [akka://Hackers/.../hacker_3] starts thinking
[INFO] [03/02/2012 14:04:45.801] ... [akka://Hackers/.../hacker_2] has taken Stick3()
[INFO] [03/02/2012 14:04:45.802] ... [akka://Hackers/.../hacker_2] starts eating
[INFO] [03/02/2012 14:04:45.883] ... [akka://Hackers/.../hacker_4] stops thinking
[INFO] [03/02/2012 14:04:45.889] ... [akka://Hackers/.../hacker_4] has taken Stick4()
[INFO] [03/02/2012 14:04:45.891] ... [akka://Hackers/.../hacker_2] stops eating
[INFO] [03/02/2012 14:04:45.892] ... [akka://Hackers/.../hacker_2] puts back Stick2()
[INFO] [03/02/2012 14:04:45.892] ... [akka://Hackers/.../hacker_2] puts back Stick3()
[INFO] [03/02/2012 14:04:45.902] ... [akka://Hackers/.../hacker_2] starts thinking
[INFO] [03/02/2012 14:04:45.903] ... [akka://Hackers/.../hacker_1] has taken Stick2()
[INFO] [03/02/2012 14:04:45.903] ... [akka://Hackers/.../hacker_1] starts eating
[INFO] [03/02/2012 14:04:46.938] ... [akka://Hackers/.../hacker_3] stops thinking
[INFO] [03/02/2012 14:04:46.944] ... [akka://Hackers/.../hacker_3] has taken Stick3()
[INFO] [03/02/2012 14:04:49.95] ... [akka://Hackers/.../hacker_1] stops eating
[INFO] [03/02/2012 14:04:49.95] ... [akka://Hackers/.../hacker_1] puts back Stick1()
[INFO] [03/02/2012 14:04:49.95] ... [akka://Hackers/.../hacker_1] puts back Stick2()
[INFO] [03/02/2012 14:04:49.96] ... [akka://Hackers/.../hacker_1] starts thinking
[INFO] [03/02/2012 14:04:49.103] ... [akka://Hackers/.../hacker_5] has taken Stick1()
[INFO] [03/02/2012 14:04:49.103] ... [akka://Hackers/.../hacker_5] starts eating
[INFO] [03/02/2012 14:04:49.126] ... [akka://Hackers/.../hacker_2] stops thinking
[INFO] [03/02/2012 14:04:49.133] ... [akka://Hackers/.../hacker_2] has taken Stick2()
[INFO] [03/02/2012 14:04:51.556] ... [akka://Hackers/.../hacker_1] stops thinking
[INFO] [03/02/2012 14:04:52.768] ... [akka://Hackers/.../hacker_5] stops eating
[INFO] [03/02/2012 14:04:52.768] ... [akka://Hackers/.../hacker_5] puts back Stick5()
[INFO] [03/02/2012 14:04:52.769] ... [akka://Hackers/.../hacker_5] puts back Stick1()
[INFO] [03/02/2012 14:04:52.769] ... [akka://Hackers/.../hacker_5] starts thinking
[INFO] [03/02/2012 14:04:52.774] ... [akka://Hackers/.../hacker_4] has taken Stick5()
[INFO] [03/02/2012 14:04:52.774] ... [akka://Hackers/.../hacker_4] starts eating
[INFO] [03/02/2012 14:04:52.777] ... [akka://Hackers/.../hacker_1] has taken Stick1()
[INFO] [03/02/2012 14:04:53.906] ... [akka://Hackers/.../hacker_5] stops thinking
^C



No comments:

Post a Comment