Tuesday, September 11, 2012

Java University 2012


Now that I am a speaker at JavaOne 2012, I would like to take the opportunity to promote Java University 2012 (where I present the Developing Secure Java Web Services track).


Java University 2012 (see http://www.oracle.com/javaone/program/schedule/university/index.html) which will be held on Sunday September 30, 2012 8:00 a.m.– 3:30 p.m, will be available to JavaOne with Java University Pass and Java University Pass holders. Learn form the experts the latest state of the art technologies like: Java SE 7 New Features, Java EE 6, JavaFX, SOA, Cloud Computing, Secure Web Services, Web 2.0, JSF 2.2 and much more.


Architect and Design Robust Enterprise Java Applications for the Cloud and Beyond
In other words, how to structure applications to head off the rogue side effects that impact quality of service, such as performance, reliability, availability, and security. The latest java EE 6 practices and patterns addressed in this course give you the answer. Design Robust Enterprise Java Applications for the Cloud. Adopt SOA capabilities to efficiently respond to changing market conditions in a cost-effective manner. Learn about the Enterprise Service Bus (ESB) (based on Java Business Integration (JBI) specification) which helps automate, manage, and optimize business processes and workflows across systems, people, and partners.

Developing Secure Java Web Services
The focus here is on B2B applications: Secure Web Services that communicate with each other through XML documents. The technology in question is the new robust Java API for XML-based Web services (JAX-WS) and Restful Web services (JAX-RS). The result: a seamless, transparent process, independent of operating systems and other platforms.

Building Dynamic Rich Internet Applications (RIAs) using JavaScript, Ajax, Comet, and Dojo Toolkit
Learn how to build rich interactive web applications using Ajax, Asynchronous JavaScript and XML technologies. We'll explore Comet and the Dojo toolkit which has Ajax components to simplify the creation of rich web applications. Dojo provides a rich set of APIs to interact with the DOM, a client side event model, as well as rich set of client side UI components.

Developing Portable Java EE Applications with the Enterprise JavaBeans 3.1 API and Java Persistence API 2.0
Learn how to build business logic that can be invoked synchronously or asynchronously with an application server that complies with the Java EE platform. The technology in question is the efficient and robust EJB EJB3.1 framework. Learn the new robust Java Persistence API 2.0 needed to develop and deploy data-driven applications with the Java EE and Java SE platforms. Learn how the Java Persistence API allows Java SE and Java EE technology developers model database entities as POJOs (Plain Old Java Objects) and how it integrates with EJB 3.1 component services to facilitate the development of enterprise applications.

Developing Enterprise Applications with the Spring Framework
Gain a clear understanding of the Spring framework. Learn the Spring’s extensive support for middle-tier functionality including persistence, remoting, management, messaging, and control flow. And of course get the new features of Spring.

Developing Rich Client Applications with Java SE 7
The demand continues to grow for secure, interactive content, applications, and services that run on a variety of clients. To simplify and speed the creation and deployment of high-impact content for a wide range of devices, learn JavaFX for GUI, a new family of products based on Java technology designed to enable consistent user experiences.

Extreme Performance: Tuning Java SE for Throughput and Latency
A large family of software applications has very stringent response time or service level agreement goals. The requirements of this family of applications have traditionally been challenging for Java SE applications to meet due to garbage collection pauses. However, with advancements made to Java Virtual Machines and the introduction of Java Real-Time System (Java RTS), these stringent response time requirements can be met. This seminar will give you the knowledge and skills required to tune both Java SE and Java RTS applications.

More Than Skin Deep: JSF 2.2 Foundation and Practice
An in-depth survey of JavaServer Faces (JSF) 2.2, the standard Web application framework for Java EE. The Seminar assumes no familiarity with JSF, but the material covering the new features of JSF 2.2 is used for the second half of the course. Typical JSF gotchas will also be covered in context.

Java SE 7: New Features
An in-depth overview of the major updates available with the Java SE 7 release for Java professionals who are already proficient with developing Java programs by using Java SE 6 or earlier Java SE platforms.

Friday, April 13, 2012

Pure Functional Futures






Expect frequent changes.





Pure Functional Futures


The idea behind this post is (as its title suggests) to implement futures in a pure functional way.

More precisely, the identity monad is transformed to add state and control to it.
The implementation does not make use of var's nor does it make use of the delimited continuations plugin.




Note: this post is not self contained.






Computational Features


Here are the main computational features that the implementation makes use of




// computations yield a result
def result[Z]: Z => M[Z]

// functor binding (and sequencing)
def bindF[B](a_f_b: A => B): M[B]

def seqF[B](b: => B) =
m_a bindF { _ =>
b
}

// idiom binding (and sequencing)
def bindI[B](m__a_f_b: M[A => B]): M[B]

def seqI[B](b: => B) =
m_a bindI {
result { (_: A) =>
b
}
}

// monad binding (and sequencing)
def bindM[B](a__f__m_b: A => M[B]): M[B]

def seqM[B](m_b: => M[B]) =
m_a bindM { _ =>
m_b
}

// state (I made setS private[imaginej])
def getS: Unit => M[State]

private[imaginej] def setS: State => M[Unit]

def doS: (State => State) => M[Unit] =
s_f_s =>
getS(()) bindM { s =>
setS(s_f_s(s))
}

// control
def reset(m_b: M[B]): M[C]

def shift(a__f__m_b__f___m_c: (A => M[B]) => M[C]): M[A]






Pure Functional Futures


As stated above, all we need is state and control, so we are ready for our implementation.

Cells and futures (making use of cells) are implemented in a way that closely follows the implementation of
Node.scala - Implementing Scalable Async IO using Delimited Continuations as presented at the Scala Days 2012.




private[imaginej] trait FutureIdentityMonadWithOptionStateWithControlModule
extends IdentityMonadWithStateWithControlModule {

import java.util.concurrent.{ Future => FutureJ }
import java.util.concurrent.Callable
import java.util.concurrent.ExecutorService
import java.util.concurrent.ScheduledThreadPoolExecutor
import java.util.logging.Logger

import instances.identity.monad.state.IdentityMonadWithStateModule

import scala.math._

val logger = Logger.getLogger("")

def sleep(time: Int) {
Thread.sleep(round(time * random))
}

type B = A
type C = FutureJ[A]

object IdentityMonadWithOptionState
extends IdentityMonadWithStateModule {
type State = Option[A]
}

private[imaginej] val fromMonad = IdentityMonadWithOptionState
private[imaginej]type MonadModuleType = IdentityMonadWithOptionState.type

class Cell(executor: ExecutorService) {
def get(): M[FutureJ[A]] =
reset {
shift { c =>
getS(()) bindM { o_a =>
o_a match {
case Some(a) =>
val callable = new Callable[A] {
def call(): A = {
logger.info("submit c(" + a + ") ...")
(c(a) run ())(o_a)._1
}
}
result {
executor.submit(callable)
}
case None =>
sys.error("state not set")
}
}
} bindF { a =>
sleep(10000);
logger.info("result(" + a + ")")
a
}
}

def set(a: A): M[Cell] =
getS(()) bindM { o_a =>
o_a match {
case Some(_) =>
sys.error("state already set")
case None =>
logger.info("set " + a)
setS(Some(a)) seqF {
this
}
}
}
}

def m_cell(implicit executor: ExecutorService): A => M[Cell] =
new Cell(executor) set (_)

type Future[A] = M[FutureJ[A]]

def future(implicit executor: ExecutorService): A => Future[A] =
a => {
val m__fj_a = m_cell(executor)(a) bindM { cell =>
cell get ()
}
result {
((m__fj_a run ())(None))._1
}
}

}





Example


Here is an example




object PureFutureExample {
val logger = Logger.getLogger("")
def sleep(time: Int) {
Thread.sleep(round(time * random))
}
object Implicits {
implicit val executor = new ScheduledThreadPoolExecutor(10)
}
def parFuturesTest() {
import Implicits.executor
val monad_l = new IntFutureIdentityMonadWithOptionStateWithControl
val monad_r = new IntFutureIdentityMonadWithOptionStateWithControl
val future_l = monad_l.future
val future_r = monad_r.future
logger.info("set futures")
val (m__fj_l, m__fj_r) = (future_l apply (1), future_r apply (2))
logger.info("sleep before get futures ...")
sleep(12000)
logger.info("get futures")
val m_l =
m__fj_l bindF { fj_l =>
fj_l.get()
}
val m_r =
m__fj_r bindF { fj_r =>
fj_r.get()
}
logger.info("(" + ((m_l run ())(None))._1 + ", " + ((m_r run ())(None))._1 + ")")
executor.shutdown()
}

def main(args: Array[String]) {
parFuturesTest()
}
}





Running the Example


Running the example a few times shows parallelism.




[info] Running examples.future.PureFutureExample
Apr 13, 2012 2:35:22 PM java.util.logging.LogManager$RootLogger log
INFO: set futures
Apr 13, 2012 2:35:22 PM java.util.logging.LogManager$RootLogger log
INFO: set 1
Apr 13, 2012 2:35:22 PM java.util.logging.LogManager$RootLogger log
INFO: set 2
Apr 13, 2012 2:35:22 PM java.util.logging.LogManager$RootLogger log
INFO: submit c(1) ...
Apr 13, 2012 2:35:22 PM java.util.logging.LogManager$RootLogger log
INFO: sleep before get futures ...
Apr 13, 2012 2:35:22 PM java.util.logging.LogManager$RootLogger log
INFO: submit c(2) ...
Apr 13, 2012 2:35:27 PM java.util.logging.LogManager$RootLogger log
INFO: result(2)
Apr 13, 2012 2:35:27 PM java.util.logging.LogManager$RootLogger log
INFO: get futures
Apr 13, 2012 2:35:28 PM java.util.logging.LogManager$RootLogger log
INFO: result(1)
Apr 13, 2012 2:35:28 PM java.util.logging.LogManager$RootLogger log
INFO: (1, 2)

[info] Running examples.future.PureFutureExample
Apr 13, 2012 2:35:36 PM java.util.logging.LogManager$RootLogger log
INFO: set futures
Apr 13, 2012 2:35:36 PM java.util.logging.LogManager$RootLogger log
INFO: set 1
Apr 13, 2012 2:35:36 PM java.util.logging.LogManager$RootLogger log
INFO: submit c(1) ...
Apr 13, 2012 2:35:36 PM java.util.logging.LogManager$RootLogger log
INFO: set 2
Apr 13, 2012 2:35:36 PM java.util.logging.LogManager$RootLogger log
INFO: submit c(2) ...
Apr 13, 2012 2:35:36 PM java.util.logging.LogManager$RootLogger log
INFO: sleep before get futures ...
Apr 13, 2012 2:35:36 PM java.util.logging.LogManager$RootLogger log
INFO: result(2)
Apr 13, 2012 2:35:40 PM java.util.logging.LogManager$RootLogger log
INFO: result(1)
Apr 13, 2012 2:35:47 PM java.util.logging.LogManager$RootLogger log
INFO: get futures
Apr 13, 2012 2:35:47 PM java.util.logging.LogManager$RootLogger log
INFO: (1, 2)

[info] Running examples.future.PureFutureExample
Apr 13, 2012 2:35:54 PM java.util.logging.LogManager$RootLogger log
INFO: set futures
Apr 13, 2012 2:35:54 PM java.util.logging.LogManager$RootLogger log
INFO: set 1
Apr 13, 2012 2:35:54 PM java.util.logging.LogManager$RootLogger log
INFO: submit c(1) ...
Apr 13, 2012 2:35:54 PM java.util.logging.LogManager$RootLogger log
INFO: set 2
Apr 13, 2012 2:35:54 PM java.util.logging.LogManager$RootLogger log
INFO: submit c(2) ...
Apr 13, 2012 2:35:54 PM java.util.logging.LogManager$RootLogger log
INFO: sleep before get futures ...
Apr 13, 2012 2:35:59 PM java.util.logging.LogManager$RootLogger log
INFO: result(1)
Apr 13, 2012 2:35:59 PM java.util.logging.LogManager$RootLogger log
INFO: result(2)
Apr 13, 2012 2:36:01 PM java.util.logging.LogManager$RootLogger log
INFO: get futures
Apr 13, 2012 2:36:01 PM java.util.logging.LogManager$RootLogger log
INFO: (1, 2)

[info] Running examples.future.PureFutureExample
Apr 13, 2012 2:39:45 PM java.util.logging.LogManager$RootLogger log
INFO: set futures
Apr 13, 2012 2:39:45 PM java.util.logging.LogManager$RootLogger log
INFO: set 1
Apr 13, 2012 2:39:45 PM java.util.logging.LogManager$RootLogger log
INFO: submit c(1) ...
Apr 13, 2012 2:39:45 PM java.util.logging.LogManager$RootLogger log
INFO: set 2
Apr 13, 2012 2:39:45 PM java.util.logging.LogManager$RootLogger log
INFO: submit c(2) ...
Apr 13, 2012 2:39:45 PM java.util.logging.LogManager$RootLogger log
INFO: sleep before get futures ...
Apr 13, 2012 2:39:52 PM java.util.logging.LogManager$RootLogger log
INFO: get futures
Apr 13, 2012 2:39:53 PM java.util.logging.LogManager$RootLogger log
INFO: result(2)
Apr 13, 2012 2:39:54 PM java.util.logging.LogManager$RootLogger log
INFO: result(1)
Apr 13, 2012 2:39:54 PM java.util.logging.LogManager$RootLogger log
INFO: (1, 2)




Saturday, March 3, 2012

Akka Spaces Revisited






Expect frequent changes.





AkkaSpace


The idea behind this Akka Space library is, again, (as its name suggests) to implement Local Akka Spaces and Remote Akka Spaces (in another way than in the previous posts).

Note: this post is, again, self contained, so, some code similar to code of previous posts has been repeated.

I have (temporarily) given up the idea to make use of partial functions for pattern matching. On the one hand this is a pity (the { case pattern => code } idiom looks like a very appealing one to me). On the other hand, remotely the idiom comes with issues (locally it works as a charm).

In this post I make use of (less powerful) equality insead of pattern matching. Luckily, both the pingpong table and the stick table examples can elegantly be implemented using equality (the full power of pattern matching is not needed).

Note: in the previous Remote Akka Spaces post I forgot to clean up the space when space actors have finished using the space (for example, when pingers and pongers have finished using the pingpong table).






Messages


case class Put[A](a: A)

case class Reg[A](b: A)

case class PutCleanup[A](as: Vector[A])

case class RegCleanup[A](bs: Vector[A])

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 classes PutCleanup[A] and RegCleanup[A] model messages that a space actor can send to a space (typically when it has finished using the space).


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






Space


class Space[A](spaceSystem: ActorSystem) extends Actor {
val log = Logging(context.system, this)

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

implicit val timeout = new Timeout(1000)

override def toString =
"\n\n Put: " + pva.await +
"\n Reg: " + (rva.await map { case (a, _) => a }) +
"\n Reg (actor refs): " + (rva.await map { case (_, ar) => ar }) +
"\n"

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

private def reg(b: A, ar: ActorRef) =
atomic {
_ =>
pva.await find { case a => b == a } match {
case None =>
val r = (b, ar)
rva send (_ :+ r)
log.info("" + this)
case Some(a) =>
pva send (_ diff Vector(a))
log.info("" + this)
ar ! Get(a)
}
}

private def putCleanup(as: Vector[A]) =
atomic {
_ =>
val pv = pva.await
val fpv = for {
a <- pv; if (as contains a)
} yield a
pva send (_ diff fpv)
log.info("" + this)
}

private def regCleanup(bs: Vector[A]) =
atomic {
_ =>
val rv = rva.await
val frv = for {
r @ (b, _) <- rv; if (bs contains b)
} yield r
rva send (_ diff frv)
log.info("" + this)
}

def receive = {
case Put(a: A) => put(a)
case Reg(b: A) => reg(b, sender)
case PutCleanup(as: Vector[A]) => putCleanup(as)
case RegCleanup(bs: Vector[A]) => regCleanup(bs)
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) puts to be matched to regs
  • rva: a vector of (agents of) regs to be matched to puts

When receiving a put

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

When receiving a reg

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

When receiving a put cleanup

  • remaining matching puts are cleaned up

When receiving a reg cleanup

  • remaining matching regs are cleaned up






SpaceActor


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.






PingPongTableApp


We are ready for a first application PingPongTableApp.






PingPong


sealed abstract class PingPong

case class Ping() extends PingPong

case class Pong() extends PingPong

case class KoPing() extends PingPong

case class KoPong() extends PingPong

case class OkPing() extends PingPong

case class OkPong() extends PingPong



The sealed abstract class PingPong and the case classes 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


case class Pingers(pingPongTableRef: ActorRef, numberOfPingers: Int)
extends SpaceActor[PingPong] {
val log = Logging(context.system, this)

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

def playingState() {
pf = {
case KoPing() =>
log.info("ko")
pingPongTableRef ! Put(OkPong())
log.info("pingers shutdown")
pingPongTableRef ! RegCleanup(Vector(OkPing(), Pong()))
pingersSystem.shutdown()
case OkPing() =>
log.info("ok")
log.info("pingers shutdown")
pingPongTableRef ! RegCleanup(Vector(KoPing(), Pong()))
pingersSystem.shutdown()
}

pingPongTableRef ! Reg(KoPing())
pingPongTableRef ! Reg(OkPing())
}

def initialState() {
playingState()
}

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

case class Pongers(pingPongTableRef: ActorRef, numberOfPongers: Int)
extends SpaceActor[PingPong] {
val log = Logging(context.system, this)

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

def playingState() {
pf = {
case KoPong() =>
log.info("ko")
pingPongTableRef ! Put(OkPing())
log.info("pongers shutdown")
pingPongTableRef ! RegCleanup(Vector(OkPong(), Ping()))
pongersSystem.shutdown()
case OkPong() =>
log.info("ok")
log.info("pongers shutdown")
pingPongTableRef ! RegCleanup(Vector(KoPong(), Ping()))
pongersSystem.shutdown()
}

pingPongTableRef ! Reg(KoPong())
pingPongTableRef ! Reg(OkPong())
}

def initialState() {
playingState()
}
}

case class Pinger(pingPongTableRef: ActorRef)
extends SpaceActor[PingPong] {
def pingState() {
val log = Logging(context.system, this)

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

pingPongTableRef ! Reg(Pong())
}

def initialState() {
pingState()
}
}

case class Ponger(pingPongTableRef: ActorRef)
extends SpaceActor[PingPong] {
def pongState() {
val log = Logging(context.system, this)

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

pingPongTableRef ! Reg(Ping())
}

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 a Ping on the pingpong table and stays in its ping state, or fail, in which case he puts a KoPing 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 a Pong on the pingpong table and stays in its pong state, or fail, in which case he puts a KoPong on the pingpong table.


The pingers supervisor creates pingers and starts in its playing state receiving a KoPing from a pinger, in which case he puts an OkPong on the pingpong table and cleans up, or an OkPing from a pongers supervisor, in which case he cleans up.


The pongers supervisor creates pongers and starts in its playing state receiving a KoPong from a ponger, in which case he puts an OkPing on the pingpong table and cleans up, or an OkPong from a pingers supervisor, in which case he cleans up.


Note: in the preStart of the pingers supervisor, a ping is put on the pingpong table.






PingPongTableApp


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

class PingPongTableApplication extends Bootable {
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 pingpong table app simply installs a pingpong table.






PingersApp and PongersApp


object PingersGlobal {
val pingersSystem = ActorSystem(
"Pingers",
ConfigFactory.load.getConfig("pingers"))
}

class PingersApplication extends Bootable {
val pingPongTableRef = pingersSystem.actorFor(
"akka://PingPongTable@127.0.0.1:2552/user/pingPongTable")

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
}
}

object PongersGlobal {
val pongersSystem = ActorSystem(
"Pongers",
ConfigFactory.load.getConfig("pongers"))
}

class PongersApplication extends Bootable {
val pingPongTableRef = pongersSystem.actorFor(
"akka://PingPongTable@127.0.0.1:2552/user/pingPongTable")

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
}
}



The pingers app simply looks up a pingpong table and installs a pingers supervisor.

The pongers app simply looks up a pingpong table and installs a pongers supervisor.






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 PingPongTableApp, PongersApp and PingersApp


Running the apps produces something like below.




[info] Running akka.space.PingPongTableApp
[INFO] [03/03/2012 22:28:31.327] [run-main] [ActorSystem(PingPongTable)]
REMOTE: RemoteServerStarted@akka://PingPongTable@127.0.0.1:2552
[INFO] [03/03/2012 22:28:39.229] [PingPongTable-6] [ActorSystem(PingPongTable)]
REMOTE: RemoteClientStarted@akka://Pongers@127.0.0.1:2554
[INFO] [03/03/2012 22:28:39.320] ... [...pingPongTable]

Put: Vector()
Reg: Vector()

[INFO] [03/03/2012 22:28:39.339] ... [...pingPongTable]

Put: Vector()
Reg: Vector(KoPong())

[INFO] [03/03/2012 22:28:39.341] ... [...pingPongTable]

Put: Vector()
Reg: Vector(KoPong(), OkPong())

[INFO] [03/03/2012 22:28:39.342] ... [...pingPongTable]

Put: Vector()
Reg: Vector(KoPong(), OkPong(), Ping())

[INFO] [03/03/2012 22:28:50.809] [PingPongTable-7] [ActorSystem(PingPongTable)]
REMOTE: RemoteClientStarted@akka://Pingers@127.0.0.1:2553
[INFO] [03/03/2012 22:28:50.821] ... [...pingPongTable]

Put: Vector()
Reg: Vector(KoPong(), OkPong(), Ping(), Ping())

[INFO] [03/03/2012 22:28:50.826] ... [...pingPongTable]

Put: Vector()
Reg: Vector(KoPong(), OkPong(), Ping(), Ping(), KoPing())

[INFO] [03/03/2012 22:28:50.834] ... [...pingPongTable]

Put: Vector()
Reg: Vector(KoPong(), OkPong(), Ping(), Ping(), KoPing(), OkPing())

[INFO] [03/03/2012 22:28:50.835] ... [...pingPongTable]

Put: Vector()
Reg: Vector(KoPong(), OkPong(), Ping(), Ping(), KoPing(), OkPing(), Pong())

[INFO] [03/03/2012 22:28:50.841] ... [...pingPongTable]

Put: Vector()
Reg: Vector(KoPong(), OkPong(), Ping(), Ping(), KoPing(), OkPing(), Pong(), Pong())

[INFO] [03/03/2012 22:28:51.621] ... [...pingPongTable]

Put: Vector()
Reg: Vector(KoPong(), OkPong(), Ping(), KoPing(), OkPing(), Pong(), Pong())

[INFO] [03/03/2012 22:28:51.625] ... [...pingPongTable]

Put: Vector()
Reg: Vector(KoPong(), OkPong(), Ping(), KoPing(), OkPing(), Pong())

[INFO] [03/03/2012 22:28:51.706] ... [...pingPongTable]

Put: Vector()
Reg: Vector(KoPong(), OkPong(), Ping(), KoPing(), OkPing(), Pong(), Ping())

[INFO] [03/03/2012 22:28:51.715] ... [...pingPongTable]

Put: Vector()
Reg: Vector(KoPong(), OkPong(), KoPing(), OkPing(), Pong(), Ping())

[INFO] [03/03/2012 22:28:52.68] ... [...pingPongTable]

Put: Vector()
Reg: Vector(KoPong(), OkPong(), KoPing(), OkPing(), Pong(), Ping(), Pong())

[INFO] [03/03/2012 22:28:52.86] ... [...pingPongTable]

Put: Vector()
Reg: Vector(OkPong(), KoPing(), OkPing(), Pong(), Ping(), Pong())

[INFO] [03/03/2012 22:28:52.142] ... [...pingPongTable]

Put: Vector()
Reg: Vector(OkPong(), KoPing(), Pong(), Ping(), Pong())

[INFO] [03/03/2012 22:28:52.161] ... [...pingPongTable]

Put: Vector()
Reg: Vector(KoPing(), Pong(), Pong())



[info] Running akka.space.PongersApp
[INFO] [03/03/2012 22:28:39.19] [run-main] [ActorSystem(Pongers)]
REMOTE: RemoteServerStarted@akka://Pongers@127.0.0.1:2554
[INFO] [03/03/2012 22:28:39.184] ... [ActorSystem(Pongers)]
REMOTE: RemoteClientStarted@akka://PingPongTable@127.0.0.1:2552
[INFO] [03/03/2012 22:28:51.606] ... [...ponger_1] pong
[INFO] [03/03/2012 22:28:52.62] ... [...ponger_2] no pong
[INFO] [03/03/2012 22:28:52.72] ... [...pongers] ko
[INFO] [03/03/2012 22:28:52.80] ... [...pongers] pongers shutdown
[INFO] [03/03/2012 22:28:52.147] ... [ActorSystem(Pongers)]
REMOTE: RemoteClientShutdown@akka://PingPongTable@127.0.0.1:2552
[INFO] [03/03/2012 22:28:52.163] ... [ActorSystem(Pongers)]
REMOTE: RemoteServerShutdown@akka://Pongers@127.0.0.1:2554
[success] Total time: 17 s, completed Mar 3, 2012 10:28:52 PM
>



[info] Running akka.space.PingersApp
[INFO] [03/03/2012 22:28:50.689] [run-main] [ActorSystem(Pingers)]
REMOTE: RemoteServerStarted@akka://Pingers@127.0.0.1:2553
[INFO] [03/03/2012 22:28:50.807] ... [ActorSystem(Pingers)]
REMOTE: RemoteClientStarted@akka://PingPongTable@127.0.0.1:2552
[INFO] [03/03/2012 22:28:51.699] ... [...pinger_2] ping
[INFO] [03/03/2012 22:28:52.90] ... [.../pingers] ok
[INFO] [03/03/2012 22:28:52.90] ... [.../pingers] pingers shutdown
[INFO] [03/03/2012 22:28:52.146] ... [ActorSystem(Pingers)]
REMOTE: RemoteClientShutdown@akka://PingPongTable@127.0.0.1:2552
[INFO] [03/03/2012 22:28:52.167] ... [ActorSystem(Pingers)]
REMOTE: RemoteServerShutdown@akka://Pingers@127.0.0.1:2553
[success] Total time: 8 s, completed Mar 3, 2012 10:28:52 PM
>



Note: the actor ref space output is not shown.

Note: space output is one step behind reality (just run the pongers and the pingers again to realize that the pingpong table has been cleaned up).

Here is the space output after starting the pongers.




[INFO] [03/03/2012 22:32:16.137] [PingPongTable-13] [ActorSystem(PingPongTable)]
REMOTE: RemoteClientStarted@akka://Pongers@127.0.0.1:2554
[INFO] [03/03/2012 22:32:16.140] [PingPongTable-13] [ActorSystem(PingPongTable)]
REMOTE: RemoteClientShutdown@akka://Pongers@127.0.0.1:2554
[INFO] [03/03/2012 22:32:16.145] ... [...pingPongTable]

Put: Vector()
Reg: Vector()

[INFO] [03/03/2012 22:32:16.148] ... [...pingPongTable]

Put: Vector()
Reg: Vector(KoPong())

[INFO] [03/03/2012 22:32:16.153] ... [...pingPongTable]

Put: Vector()
Reg: Vector(KoPong(), OkPong())

[INFO] [03/03/2012 22:32:16.154] ... [...pingPongTable]

Put: Vector()
Reg: Vector(KoPong(), OkPong(), Ping())





StickTableApp


We are ready for a second application StickTableApp.






Stick


case class Stick(i: Int)



The case class Stick models the data that drive the application.






PingPongTable


case class StickTable(spaceSystem: ActorSystem, numberOfSticks: Int)
extends Space[Stick](spaceSystem) {
override def preStart() {
(1 to numberOfSticks) foreach { i =>
self ! Put(Stick(i))
}
}
}



The case class PingPongTable models the stick table.

Note: in the preStart of the stick table, the sticks are put on the stick table.






Hackers


case class Hackers(stickTableRef: ActorRef, numberOfSticks: Int)
extends Actor {
(1 to numberOfSticks) foreach { i =>
context.actorOf(
Props(Hakker(i,
numberOfSticks, stickTableRef,
4000, 4000, 5000, 8000)),
name = "hacker_" + i)
}

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

case class Hakker(
n: Int,
numberOfSticks: Int, stickTableRef: ActorRef,
startTime: Long, thinkTime: Long, eatTime: Long, delayTime: Long)
extends SpaceActor[Stick] {
val log = Logging(context.system, this)

private def next(n: Int) =
if (n == numberOfSticks) { 1 } else { n + 1 }

def takeLeftState() {
val left = n

pf = {
case l @ Stick(`left`) => {
log.info("has taken " + l)
takeRightState()
}
}

stickTableRef ! Reg(Stick(n))
}

def takeRightState() {
val right = next(n)

pf = {
case r @ Stick(`right`) => {
log.info("has taken " + r)
hack()
takeLeftState()
}
}

Thread.sleep(round(delayTime * random))

stickTableRef ! Reg(Stick(next(n)))
}

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

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

private def putBackSticks() {
val l = Stick(n)
log.info("puts back " + l)
stickTableRef ! Put(l)
val r = Stick(if (n == numberOfSticks) { 1 } else { n + 1 })
log.info("puts back " + r)
stickTableRef ! Put(r)
}

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

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




A hackers supervisor creates hackers.

A hacker starts in its take left state taking the Stick left to and 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.






StickTableApp


class StickTableApplication extends Bootable {
val stickTableSystem = ActorSystem(
"StickTable",
ConfigFactory.load.getConfig("stickTable"))

val numberOfSticks = 5

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

def startup() {
}

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

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



The stick table app simply installs a stick table.






PingersApp and PongersApp


class HackersApplication extends Bootable {
val hackersSystem = ActorSystem(
"Hackers",
ConfigFactory.load.getConfig("hackers"))

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

val numberOfSticks = 5

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

def startup() {
}

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

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



The hackers app simply looks up a stick table and installs a hackers supervisor.






Configuration


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

akka {
remote.netty.port = 2555
}
}

hackers {
include "common"

akka {
remote.netty.port = 2556
}
}





Running the HackersApp and StickTableApp


Running the apps produces something like below (in this case an immediate deadlock).




[info] Running akka.space.StickTableApp
[INFO] [03/03/2012 23:03:55.699] [run-main] [ActorSystem(StickTable)]
REMOTE: RemoteServerStarted@akka://StickTable@127.0.0.1:2555
[INFO] [03/03/2012 23:03:55.810] ... [...stickTable]

Put: Vector()
Reg: Vector()
Reg (actor refs): Vector()

[INFO] [03/03/2012 23:03:55.835] ... [...stickTable]

Put: Vector(Stick(1))
Reg: Vector()
Reg (actor refs): Vector()

[INFO] [03/03/2012 23:03:55.836] ... [...stickTable]

Put: Vector(Stick(1), Stick(2))
Reg: Vector()
Reg (actor refs): Vector()

[INFO] [03/03/2012 23:03:55.837] ... [...stickTable]

Put: Vector(Stick(1), Stick(2), Stick(3))
Reg: Vector()
Reg (actor refs): Vector()

[INFO] [03/03/2012 23:03:55.842] ... [...stickTable]

Put: Vector(Stick(1), Stick(2), Stick(3), Stick(4))
Reg: Vector()
Reg (actor refs): Vector()

[INFO] [03/03/2012 23:04:14.951] [StickTable-6] [ActorSystem(StickTable)]
REMOTE: RemoteClientStarted@akka://Hackers@127.0.0.1:2556
[INFO] [03/03/2012 23:04:14.966] ... [...stickTable]

Put: Vector(Stick(1), Stick(2), Stick(3), Stick(4), Stick(5))
Reg: Vector()
Reg (actor refs): Vector()

[INFO] [03/03/2012 23:04:15.415] ... [...stickTable]

Put: Vector(Stick(1), Stick(3), Stick(4), Stick(5))
Reg: Vector()
Reg (actor refs): Vector()

[INFO] [03/03/2012 23:04:16.224] ... [...stickTable]

Put: Vector(Stick(1), Stick(3), Stick(4))
Reg: Vector()
Reg (actor refs): Vector()

[INFO] [03/03/2012 23:04:16.807] ... [...stickTable]

Put: Vector(Stick(1), Stick(4))
Reg: Vector()
Reg (actor refs): Vector()

[INFO] [03/03/2012 23:04:16.849] ... [...stickTable]

Put: Vector(Stick(4))
Reg: Vector()
Reg (actor refs): Vector()

[INFO] [03/03/2012 23:04:17.821] ... [...stickTable]

Put: Vector()
Reg: Vector()
Reg (actor refs): Vector()

[INFO] [03/03/2012 23:04:19.748] ... [...stickTable]

Put: Vector()
Reg: Vector(Stick(4))
Reg (actor refs): Vector(...hacker_3)

[INFO] [03/03/2012 23:04:20.360] ... [...stickTable]

Put: Vector()
Reg: Vector(Stick(4), Stick(2))
Reg (actor refs): Vector(...hacker_3, ...hacker_1)

[INFO] [03/03/2012 23:04:21.884] ... [...stickTable]

Put: Vector()
Reg: Vector(Stick(4), Stick(2), Stick(3))
Reg (actor refs): Vector(...hacker_3, ...hacker_1, ...hacker_2)

[INFO] [03/03/2012 23:04:24.842] ... [...stickTable]

Put: Vector()
Reg: Vector(Stick(4), Stick(2), Stick(3), Stick(1))
Reg (actor refs): Vector(...hacker_3, ...hacker_1, ...hacker_2, ...hacker_5)


[info] Running akka.space.HackersApp
[INFO] [03/03/2012 23:04:14.263] [run-main] [ActorSystem(Hackers)]
REMOTE: RemoteServerStarted@akka://Hackers@127.0.0.1:2556
[INFO] [03/03/2012 23:04:14.912] ... [ActorSystem(Hackers)]
REMOTE: RemoteClientStarted@akka://StickTable@127.0.0.1:2555
[INFO] [03/03/2012 23:04:14.992] ... [...hacker_2] has taken Stick(2)
[INFO] [03/03/2012 23:04:15.418] ... [...hacker_5] has taken Stick(5)
[INFO] [03/03/2012 23:04:16.226] ... [...hacker_3] has taken Stick(3)
[INFO] [03/03/2012 23:04:16.809] ... [...hacker_1] has taken Stick(1)
[INFO] [03/03/2012 23:04:16.853] ... [...hacker_4] has taken Stick(4)






Running the apps may also produce something like below (in this case no deadlock (yet)).




[info] Running akka.space.StickTableApp
[INFO] [03/04/2012 17:14:28.520] [run-main] [ActorSystem(StickTable)]
REMOTE: RemoteServerStarted@akka://StickTable@127.0.0.1:2555
[INFO] [03/04/2012 17:14:28.633] ... [...stickTable]

Put: Vector()
Reg: Vector()
Reg (actor refs): Vector()

[INFO] [03/04/2012 17:14:28.650] ... [...stickTable]

Put: Vector(Stick(1))
Reg: Vector()
Reg (actor refs): Vector()

[INFO] [03/04/2012 17:14:28.651] ... [...stickTable]

Put: Vector(Stick(1), Stick(2))
Reg: Vector()
Reg (actor refs): Vector()

[INFO] [03/04/2012 17:14:28.653] ... [...stickTable]

Put: Vector(Stick(1), Stick(2), Stick(3))
Reg: Vector()
Reg (actor refs): Vector()

[INFO] [03/04/2012 17:14:28.655] ... [...stickTable]

Put: Vector(Stick(1), Stick(2), Stick(3), Stick(4))
Reg: Vector()
Reg (actor refs): Vector()

[INFO] [03/04/2012 17:14:39.585] [StickTable-7] [ActorSystem(StickTable)]
REMOTE: RemoteClientStarted@akka://Hackers@127.0.0.1:2556
[INFO] [03/04/2012 17:14:39.596] ... [...stickTable]

Put: Vector(Stick(1), Stick(2), Stick(3), Stick(4), Stick(5))
Reg: Vector()
Reg (actor refs): Vector()

[INFO] [03/04/2012 17:14:39.690] ... [...stickTable]

Put: Vector(Stick(1), Stick(2), Stick(3), Stick(4))
Reg: Vector()
Reg (actor refs): Vector()

[INFO] [03/04/2012 17:14:40.145] ... [...stickTable]

Put: Vector(Stick(2), Stick(3), Stick(4))
Reg: Vector()
Reg (actor refs): Vector()

[INFO] [03/04/2012 17:14:40.803] ... [...stickTable]

Put: Vector(Stick(2), Stick(3), Stick(4))
Reg: Vector(Stick(1))
Reg (actor refs): Vector(...hacker_1)

[INFO] [03/04/2012 17:14:40.830] ... [...stickTable]

Put: Vector(Stick(3), Stick(4))
Reg: Vector(Stick(1))
Reg (actor refs): Vector(...hacker_1)

[INFO] [03/04/2012 17:14:41.957] ... [...stickTable]

Put: Vector(Stick(4))
Reg: Vector(Stick(1))
Reg (actor refs): Vector(...hacker_1)

[INFO] [03/04/2012 17:14:42.94] ... [...stickTable]

Put: Vector()
Reg: Vector(Stick(1))
Reg (actor refs): Vector(...hacker_1)

[INFO] [03/04/2012 17:14:42.114] ... [...stickTable]

Put: Vector()
Reg: Vector(Stick(1), Stick(3))
Reg (actor refs): Vector(...hacker_1, ...hacker_3)

[INFO] [03/04/2012 17:14:43.466] ... [...stickTable]

Put: Vector()
Reg: Vector(Stick(1), Stick(3), Stick(5))
Reg (actor refs): Vector(...hacker_1, ...hacker_3, ...hacker_4)

[INFO] [03/04/2012 17:14:43.470] ... [...stickTable]

Put: Vector(Stick(2))
Reg: Vector(Stick(1), Stick(3), Stick(5))
Reg (actor refs): Vector(...hacker_1, ...hacker_3, ...hacker_4)

[INFO] [03/04/2012 17:14:43.710] ... [...stickTable]

Put: Vector(Stick(2))
Reg: Vector(Stick(1), Stick(5))
Reg (actor refs): Vector(...hacker_1, ...hacker_4)

[INFO] [03/04/2012 17:14:44.443] ... [...stickTable]

Put: Vector(Stick(2))
Reg: Vector(Stick(1), Stick(5), Stick(4))
Reg (actor refs): Vector(...hacker_1, ...hacker_4, ...hacker_3)

[INFO] [03/04/2012 17:14:44.447] ... [...stickTable]

Put: Vector(Stick(2))
Reg: Vector(Stick(1), Stick(4))
Reg (actor refs): Vector(...hacker_1, ...hacker_3)

[INFO] [03/04/2012 17:14:44.458] ... [...stickTable]

Put: Vector(Stick(2))
Reg: Vector(Stick(4))
Reg (actor refs): Vector(...hacker_3)

[INFO] [03/04/2012 17:14:44.574] ... [...stickTable]

Put: Vector()
Reg: Vector(Stick(4))
Reg (actor refs): Vector(...hacker_3)

[INFO] [03/04/2012 17:14:45.82] ... [...stickTable]

Put: Vector()
Reg: Vector(Stick(4), Stick(3))
Reg (actor refs): Vector(...hacker_3, ...hacker_2)

[INFO] [03/04/2012 17:14:45.423] ... [...stickTable]

Put: Vector()
Reg: Vector(Stick(4), Stick(3), Stick(2))
Reg (actor refs): Vector(...hacker_3, ...hacker_2, ...hacker_1)

[INFO] [03/04/2012 17:14:45.426] ... [...stickTable]

Put: Vector()
Reg: Vector(Stick(3), Stick(2))
Reg (actor refs): Vector(...hacker_2, ...hacker_1)

[INFO] [03/04/2012 17:14:46.805] ... [...stickTable]

Put: Vector(Stick(5))
Reg: Vector(Stick(3), Stick(2))
Reg (actor refs): Vector(...hacker_2, ...hacker_1)

[INFO] [03/04/2012 17:14:47.750] ... [...stickTable]

Put: Vector()
Reg: Vector(Stick(3), Stick(2))
Reg (actor refs): Vector(...hacker_2, ...hacker_1)

[INFO] [03/04/2012 17:14:47.908] ... [...stickTable]

Put: Vector()
Reg: Vector(Stick(3), Stick(2), Stick(1))
Reg (actor refs): Vector(...hacker_2, ...hacker_1, ...hacker_5)

[INFO] [03/04/2012 17:14:50.216] ... [...stickTable]

Put: Vector()
Reg: Vector(Stick(3), Stick(2), Stick(1), Stick(4))
Reg (actor refs): Vector(...hacker_2, ...hacker_1, ...hacker_5, ...hacker_4)

[INFO] [03/04/2012 17:14:50.220] ... [...stickTable]

Put: Vector()
Reg: Vector(Stick(2), Stick(1), Stick(4))
Reg (actor refs): Vector(...hacker_1, ...hacker_5, ...hacker_4)



[info] Running akka.space.HackersApp
[INFO] [03/04/2012 17:14:38.303] [run-main] [ActorSystem(Hackers)]
REMOTE: RemoteServerStarted@akka://Hackers@127.0.0.1:2556
[INFO] [03/04/2012 17:14:39.528] ... [ActorSystem(Hackers)]
REMOTE: RemoteClientStarted@akka://StickTable@127.0.0.1:2555
[INFO] [03/04/2012 17:14:39.631] ... [...hacker_5] has taken Stick(5)
[INFO] [03/04/2012 17:14:39.694] ... [...hacker_5] has taken Stick(1)
[INFO] [03/04/2012 17:14:39.694] ... [...hacker_5] starts eating
[INFO] [03/04/2012 17:14:40.807] ... [...hacker_2] has taken Stick(2)
[INFO] [03/04/2012 17:14:40.831] ... [...hacker_2] has taken Stick(3)
[INFO] [03/04/2012 17:14:40.832] ... [...hacker_2] starts eating
[INFO] [03/04/2012 17:14:41.960] ... [...hacker_4] has taken Stick(4)
[INFO] [03/04/2012 17:14:43.453] ... [...hacker_2] stops eating
[INFO] [03/04/2012 17:14:43.453] ... [...hacker_2] puts back Stick(2)
[INFO] [03/04/2012 17:14:43.458] ... [...hacker_2] puts back Stick(3)
[INFO] [03/04/2012 17:14:43.462] ... [...hacker_2] starts thinking
[INFO] [03/04/2012 17:14:43.473] ... [...hacker_3] has taken Stick(3)
[INFO] [03/04/2012 17:14:44.436] ... [...hacker_5] stops eating
[INFO] [03/04/2012 17:14:44.437] ... [...hacker_5] puts back Stick(5)
[INFO] [03/04/2012 17:14:44.439] ... [...hacker_5] puts back Stick(1)
[INFO] [03/04/2012 17:14:44.440] ... [...hacker_5] starts thinking
[INFO] [03/04/2012 17:14:44.446] ... [...hacker_4] has taken Stick(5)
[INFO] [03/04/2012 17:14:44.446] ... [...hacker_4] starts eating
[INFO] [03/04/2012 17:14:44.448] ... [...hacker_2] stops thinking
[INFO] [03/04/2012 17:14:44.456] ... [...hacker_1] has taken Stick(1)
[INFO] [03/04/2012 17:14:44.459] ... [...hacker_2] has taken Stick(2)
[INFO] [03/04/2012 17:14:45.417] ... [...hacker_4] stops eating
[INFO] [03/04/2012 17:14:45.418] ... [...hacker_4] puts back Stick(4)
[INFO] [03/04/2012 17:14:45.419] ... [...hacker_4] puts back Stick(5)
[INFO] [03/04/2012 17:14:45.420] ... [...hacker_4] starts thinking
[INFO] [03/04/2012 17:14:45.426] ... [...hacker_3] has taken Stick(4)
[INFO] [03/04/2012 17:14:45.426] ... [...hacker_3] starts eating
[INFO] [03/04/2012 17:14:46.800] ... [...hacker_5] stops thinking
[INFO] [03/04/2012 17:14:46.808] ... [...hacker_5] has taken Stick(5)
[INFO] [03/04/2012 17:14:47.903] ... [...hacker_4] stops thinking
[INFO] [03/04/2012 17:14:50.210] ... [...hacker_3] stops eating
[INFO] [03/04/2012 17:14:50.211] ... [...hacker_3] puts back Stick(3)
[INFO] [03/04/2012 17:14:50.214] ... [...hacker_3] puts back Stick(4)
[INFO] [03/04/2012 17:14:50.215] ... [...hacker_3] starts thinking
[INFO] [03/04/2012 17:14:50.219] ... [...hacker_2] has taken Stick(3)
[INFO] [03/04/2012 17:14:50.220] ... [...hacker_2] starts eating
[INFO] [03/04/2012 17:14:50.223] ... [...hacker_4] has taken Stick(4)



Note: at [03/04/2012 17:14:40.832] both hacker_5 and hacker_2
are eating.

Note: the situation of the table around that time is


[INFO] [03/04/2012 17:14:41.957] ... [...stickTable]

Put: Vector(Stick(4))
Reg: Vector(Stick(1))
Reg (actor refs): Vector(...hacker_1)

Stick(4) is on the stick table and and hacker_1 is waiting for Stick(1).

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