+sam #3689 Make activator template of the fsm sample
This commit is contained in:
parent
23b9fad153
commit
e6f679fe8b
12 changed files with 138 additions and 157 deletions
|
|
@ -754,7 +754,7 @@ Hakkers`_). It will replace the current behavior (i.e. the top of the behavior
|
|||
stack), which means that you do not use :meth:`unbecome`, instead always the
|
||||
next behavior is explicitly installed.
|
||||
|
||||
.. _Dining Hakkers: @github@/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnBecome.scala
|
||||
.. _Dining Hakkers: http://typesafe.com/activator/template/akka-sample-fsm-scala
|
||||
|
||||
The other way of using :meth:`become` does not replace but add to the top of
|
||||
the behavior stack. In this case care must be taken to ensure that the number
|
||||
|
|
|
|||
|
|
@ -479,7 +479,6 @@ zero.
|
|||
Examples
|
||||
========
|
||||
|
||||
A bigger FSM example contrasted with Actor's :meth:`become`/:meth:`unbecome` can be found in the sources:
|
||||
|
||||
* `Dining Hakkers using FSM <@github@/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnFsm.scala>`_
|
||||
* `Dining Hakkers using become <@github@/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnBecome.scala>`_
|
||||
A bigger FSM example contrasted with Actor's :meth:`become`/:meth:`unbecome` can be found in
|
||||
the `Typesafe Activator <http://typesafe.com/platform/getstarted>`_ template named
|
||||
`Akka FSM in Scala <http://typesafe.com/activator/template/akka-sample-fsm-scala>`_
|
||||
|
|
|
|||
13
akka-samples/akka-sample-fsm-scala/LICENSE
Normal file
13
akka-samples/akka-sample-fsm-scala/LICENSE
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
Copyright 2013-2014 Typesafe, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
7
akka-samples/akka-sample-fsm-scala/activator.properties
Normal file
7
akka-samples/akka-sample-fsm-scala/activator.properties
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
name=akka-sample-fsm-scala
|
||||
title=Akka FSM in Scala
|
||||
description=Illustrating how to implement a finite state machine in actors.
|
||||
tags=akka,scala,sample
|
||||
authorName=Akka Team
|
||||
authorLink=http://akka.io/
|
||||
sourceLink=https://github.com/akka/akka
|
||||
10
akka-samples/akka-sample-fsm-scala/build.sbt
Normal file
10
akka-samples/akka-sample-fsm-scala/build.sbt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
name := "akka-sample-main-scala"
|
||||
|
||||
version := "1.0"
|
||||
|
||||
scalaVersion := "2.10.3"
|
||||
|
||||
libraryDependencies ++= Seq(
|
||||
"com.typesafe.akka" %% "akka-actor" % "2.3-SNAPSHOT"
|
||||
)
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
sbt.version=0.13.1
|
||||
|
|
@ -1,16 +1,14 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>.
|
||||
*/
|
||||
package sample.fsm.dining.become
|
||||
|
||||
import language.postfixOps
|
||||
|
||||
//Akka adaptation of
|
||||
//http://www.dalnefre.com/wp/2010/08/dining-philosophers-in-humus/
|
||||
package sample.become
|
||||
|
||||
import akka.actor._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
// Akka adaptation of
|
||||
// http://www.dalnefre.com/wp/2010/08/dining-philosophers-in-humus/
|
||||
|
||||
/*
|
||||
* First we define our messages, they basically speak for themselves
|
||||
*/
|
||||
|
|
@ -86,12 +84,11 @@ class Hakker(name: String, left: ActorRef, right: ActorRef) extends Actor {
|
|||
case Taken(`chopstickToWaitFor`) =>
|
||||
println("%s has picked up %s and %s and starts to eat".format(name, left.path.name, right.path.name))
|
||||
become(eating)
|
||||
system.scheduler.scheduleOnce(5 seconds, self, Think)
|
||||
system.scheduler.scheduleOnce(5.seconds, self, Think)
|
||||
|
||||
case Busy(chopstick) =>
|
||||
become(thinking)
|
||||
otherChopstick ! Put(self)
|
||||
self ! Eat
|
||||
startThinking(10.milliseconds)
|
||||
}
|
||||
|
||||
//When the results of the other grab comes back,
|
||||
|
|
@ -99,38 +96,39 @@ class Hakker(name: String, left: ActorRef, right: ActorRef) extends Actor {
|
|||
//Then go back and think and try to grab the chopsticks again
|
||||
def denied_a_chopstick: Receive = {
|
||||
case Taken(chopstick) =>
|
||||
become(thinking)
|
||||
chopstick ! Put(self)
|
||||
self ! Eat
|
||||
startThinking(10.milliseconds)
|
||||
case Busy(chopstick) =>
|
||||
become(thinking)
|
||||
self ! Eat
|
||||
startThinking(10.milliseconds)
|
||||
}
|
||||
|
||||
//When a hakker is eating, he can decide to start to think,
|
||||
//then he puts down his chopsticks and starts to think
|
||||
def eating: Receive = {
|
||||
case Think =>
|
||||
become(thinking)
|
||||
left ! Put(self)
|
||||
right ! Put(self)
|
||||
println("%s puts down his chopsticks and starts to think".format(name))
|
||||
system.scheduler.scheduleOnce(5 seconds, self, Eat)
|
||||
startThinking(5.seconds)
|
||||
}
|
||||
|
||||
//All hakkers start in a non-eating state
|
||||
def receive = {
|
||||
case Think =>
|
||||
println("%s starts to think".format(name))
|
||||
startThinking(5.seconds)
|
||||
}
|
||||
|
||||
private def startThinking(duration: FiniteDuration): Unit = {
|
||||
become(thinking)
|
||||
system.scheduler.scheduleOnce(5 seconds, self, Eat)
|
||||
system.scheduler.scheduleOnce(duration, self, Eat)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Alright, here's our test-harness
|
||||
*/
|
||||
object DiningHakkers {
|
||||
object DiningHakkersOnBecome {
|
||||
val system = ActorSystem()
|
||||
|
||||
def main(args: Array[String]): Unit = run()
|
||||
|
|
@ -1,13 +1,15 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>.
|
||||
*/
|
||||
package sample.fsm.dining.fsm
|
||||
package sample.fsm
|
||||
|
||||
import language.postfixOps
|
||||
import akka.actor._
|
||||
import akka.actor.FSM._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
// Akka adaptation of
|
||||
// http://www.dalnefre.com/wp/2010/08/dining-philosophers-in-humus/
|
||||
|
||||
/*
|
||||
* Some messages for the chopstick
|
||||
*/
|
||||
|
|
@ -91,7 +93,7 @@ class FSMHakker(name: String, left: ActorRef, right: ActorRef) extends Actor wit
|
|||
when(Waiting) {
|
||||
case Event(Think, _) =>
|
||||
println("%s starts to think".format(name))
|
||||
startThinking(5 seconds)
|
||||
startThinking(5.seconds)
|
||||
}
|
||||
|
||||
//When a hakker is thinking it can become hungry
|
||||
|
|
@ -125,12 +127,12 @@ class FSMHakker(name: String, left: ActorRef, right: ActorRef) extends Actor wit
|
|||
case Event(Busy(chopstick), TakenChopsticks(leftOption, rightOption)) =>
|
||||
leftOption.foreach(_ ! Put)
|
||||
rightOption.foreach(_ ! Put)
|
||||
startThinking(10 milliseconds)
|
||||
startThinking(10.milliseconds)
|
||||
}
|
||||
|
||||
private def startEating(left: ActorRef, right: ActorRef): State = {
|
||||
println("%s has picked up %s and %s and starts to eat".format(name, left.path.name, right.path.name))
|
||||
goto(Eating) using TakenChopsticks(Some(left), Some(right)) forMax (5 seconds)
|
||||
goto(Eating) using TakenChopsticks(Some(left), Some(right)) forMax (5.seconds)
|
||||
}
|
||||
|
||||
// When the results of the other grab comes back,
|
||||
|
|
@ -139,9 +141,9 @@ class FSMHakker(name: String, left: ActorRef, right: ActorRef) extends Actor wit
|
|||
when(FirstChopstickDenied) {
|
||||
case Event(Taken(secondChopstick), _) =>
|
||||
secondChopstick ! Put
|
||||
startThinking(10 milliseconds)
|
||||
startThinking(10.milliseconds)
|
||||
case Event(Busy(chopstick), _) =>
|
||||
startThinking(10 milliseconds)
|
||||
startThinking(10.milliseconds)
|
||||
}
|
||||
|
||||
// When a hakker is eating, he can decide to start to think,
|
||||
|
|
@ -151,7 +153,7 @@ class FSMHakker(name: String, left: ActorRef, right: ActorRef) extends Actor wit
|
|||
println("%s puts down his chopsticks and starts to think".format(name))
|
||||
left ! Put
|
||||
right ! Put
|
||||
startThinking(5 seconds)
|
||||
startThinking(5.seconds)
|
||||
}
|
||||
|
||||
// Initialize the hakker
|
||||
73
akka-samples/akka-sample-fsm-scala/tutorial/index.html
Normal file
73
akka-samples/akka-sample-fsm-scala/tutorial/index.html
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<!-- <html> -->
|
||||
<head>
|
||||
<title>Akka FSM in Scala</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div>
|
||||
<h2>Finite State Machine in Actors</h2>
|
||||
|
||||
<p>
|
||||
This sample is an adaptation of
|
||||
<a href="http://www.dalnefre.com/wp/2010/08/dining-philosophers-in-humus/" target="_blank">Dining Hakkers</a>.
|
||||
It illustrates how state and behavior can be managed within
|
||||
an Actor with two different approaches; using <code>become</code> and using
|
||||
the <code>FSM</code> trait.
|
||||
</p>
|
||||
|
||||
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<h2>Dining Hakkers with Become</h2>
|
||||
|
||||
<p>
|
||||
Open <a href="#code/src/main/scala/sample/become/DiningHakkersOnBecome.scala" class="shortcut">DiningHakkersOnBecome.scala</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
It illustrates how current behavior can be replaced with <code>context.become</code>.
|
||||
Note that no <code>var</code> members are used, instead the state is encoded in the current
|
||||
behavior and its parameters.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Go to the <a href="#run" class="shortcut">Run</a> tab, and start the application main class
|
||||
<code>sample.become.DiningHakkersOnBecome</code>.
|
||||
In the log output you can see the actions of the <code>Hakker</code> actors.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Read more about <code>become</code> in
|
||||
<a href="http://doc.akka.io/docs/akka/2.3-SNAPSHOT/scala/actors.html#Become_Unbecome" target="_blank">the documentation</a>.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<h2>Dining Hakkers with FSM</h2>
|
||||
|
||||
<p>
|
||||
Open <a href="#code/src/main/scala/sample/fsm/DiningHakkersOnFsm.scala" class="shortcut">DiningHakkersOnFsm.scala</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
It illustrates how the states and transitions can be defined with the <code>akka.actor.FSM</code> trait.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Go to the <a href="#run" class="shortcut">Run</a> tab, and start the application main class
|
||||
<code>sample.fsm.DiningHakkersOnFsm</code>.
|
||||
In the log output you can see the actions of the <code>Hakker</code> actors.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Read more about <code>akka.actor.FSM</code> in
|
||||
<a href="http://doc.akka.io/docs/akka/2.3-SNAPSHOT/scala/fsm.html" target="_blank">the documentation</a>.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
FSM Sample
|
||||
==========
|
||||
|
||||
This sample is meant to be used by studying the code; it does not perform any
|
||||
astounding functions when running it. If you want to run it, check out the akka
|
||||
sources on your local hard drive, follow the [instructions for setting up Akka
|
||||
with SBT](http://doc.akka.io/docs/akka/current/intro/getting-started.html).
|
||||
When you start SBT within the checked-out akka source directory, you can run
|
||||
this sample by typing
|
||||
|
||||
akka-sample-fsm/run
|
||||
|
||||
and then choose one of the two ways (one using `context.become` and one using FSM).
|
||||
|
||||
You can read more in the [Akka docs](http://akka.io/docs).
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>.
|
||||
*/
|
||||
package sample.fsm.buncher
|
||||
|
||||
import akka.actor.ActorRefFactory
|
||||
import scala.reflect.ClassTag
|
||||
import scala.concurrent.duration.Duration
|
||||
import akka.actor.{ FSM, Actor, ActorRef }
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
/*
|
||||
* generic typed object buncher.
|
||||
*
|
||||
* To instantiate it, use the factory method like so:
|
||||
* Buncher(100, 500)(x : List[AnyRef] => x foreach println)
|
||||
* which will yield a fully functional ActorRef.
|
||||
* The type of messages allowed is strongly typed to match the
|
||||
* supplied processing method; other messages are discarded (and
|
||||
* possibly logged).
|
||||
*/
|
||||
object GenericBuncher {
|
||||
trait State
|
||||
case object Idle extends State
|
||||
case object Active extends State
|
||||
|
||||
case object Flush // send out current queue immediately
|
||||
case object Stop // poison pill
|
||||
|
||||
class MsgExtractor[A: ClassTag] {
|
||||
def unapply(m: AnyRef): Option[A] =
|
||||
if (implicitly[ClassTag[A]].runtimeClass isAssignableFrom m.getClass)
|
||||
Some(m.asInstanceOf[A])
|
||||
else
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
abstract class GenericBuncher[A: ClassTag, B](val singleTimeout: FiniteDuration, val multiTimeout: FiniteDuration)
|
||||
extends Actor with FSM[GenericBuncher.State, B] {
|
||||
import GenericBuncher._
|
||||
import FSM._
|
||||
|
||||
protected def empty: B
|
||||
protected def merge(acc: B, elem: A): B
|
||||
protected def send(acc: B): Unit
|
||||
|
||||
protected def flush(acc: B) = {
|
||||
send(acc)
|
||||
cancelTimer("multi")
|
||||
goto(Idle) using empty
|
||||
}
|
||||
|
||||
val Msg = new MsgExtractor[A]
|
||||
|
||||
startWith(Idle, empty)
|
||||
|
||||
when(Idle) {
|
||||
case Event(Msg(m), acc) =>
|
||||
setTimer("multi", StateTimeout, multiTimeout, false)
|
||||
goto(Active) using merge(acc, m)
|
||||
case Event(Flush, _) => stay
|
||||
case Event(Stop, _) => stop
|
||||
}
|
||||
|
||||
when(Active, stateTimeout = singleTimeout) {
|
||||
case Event(Msg(m), acc) =>
|
||||
stay using merge(acc, m)
|
||||
case Event(StateTimeout, acc) =>
|
||||
flush(acc)
|
||||
case Event(Flush, acc) =>
|
||||
flush(acc)
|
||||
case Event(Stop, acc) =>
|
||||
send(acc)
|
||||
cancelTimer("multi")
|
||||
stop
|
||||
}
|
||||
|
||||
initialize()
|
||||
}
|
||||
|
||||
object Buncher {
|
||||
case class Target(target: ActorRef) // for setting the target for default send action
|
||||
|
||||
val Stop = GenericBuncher.Stop // make special message objects visible for Buncher clients
|
||||
val Flush = GenericBuncher.Flush
|
||||
}
|
||||
|
||||
class Buncher[A: ClassTag](singleTimeout: FiniteDuration, multiTimeout: FiniteDuration)
|
||||
extends GenericBuncher[A, List[A]](singleTimeout, multiTimeout) {
|
||||
|
||||
import Buncher._
|
||||
|
||||
private var target: Option[ActorRef] = None
|
||||
protected def send(acc: List[A]): Unit = if (target.isDefined) target.get ! acc.reverse
|
||||
|
||||
protected def empty: List[A] = Nil
|
||||
|
||||
protected def merge(l: List[A], elem: A) = elem :: l
|
||||
|
||||
whenUnhandled {
|
||||
case Event(Target(t), _) =>
|
||||
target = Some(t)
|
||||
stay
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -369,7 +369,7 @@ object AkkaBuild extends Build {
|
|||
settings = parentSettings ++ ActivatorDist.settings,
|
||||
aggregate = Seq(camelSampleJava, camelSampleScala, mainSampleJava, mainSampleScala,
|
||||
remoteSampleJava, remoteSampleScala, clusterSampleJava, clusterSampleScala,
|
||||
fsmSample, persistenceSample,
|
||||
fsmSampleScala, persistenceSample,
|
||||
multiNodeSample, helloKernelSample, osgiDiningHakkersSample)
|
||||
)
|
||||
|
||||
|
|
@ -387,9 +387,9 @@ object AkkaBuild extends Build {
|
|||
settings = sampleSettings ++ Seq(libraryDependencies ++= Dependencies.camelSample)
|
||||
)
|
||||
|
||||
lazy val fsmSample = Project(
|
||||
id = "akka-sample-fsm",
|
||||
base = file("akka-samples/akka-sample-fsm"),
|
||||
lazy val fsmSampleScala = Project(
|
||||
id = "akka-sample-fsm-scala",
|
||||
base = file("akka-samples/akka-sample-fsm-scala"),
|
||||
dependencies = Seq(actor),
|
||||
settings = sampleSettings
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue