From e6f679fe8b7ba451e31c47a61175cfd07edbd445 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Mon, 10 Feb 2014 17:23:19 +0100 Subject: [PATCH] +sam #3689 Make activator template of the fsm sample --- akka-docs/rst/scala/actors.rst | 2 +- akka-docs/rst/scala/fsm.rst | 7 +- akka-samples/akka-sample-fsm-scala/LICENSE | 13 +++ .../activator.properties | 7 ++ akka-samples/akka-sample-fsm-scala/build.sbt | 10 ++ .../project/build.properties | 1 + .../become}/DiningHakkersOnBecome.scala | 34 +++--- .../sample/fsm}/DiningHakkersOnFsm.scala | 18 +-- .../akka-sample-fsm-scala/tutorial/index.html | 73 ++++++++++++ akka-samples/akka-sample-fsm/README.md | 15 --- .../src/main/scala/Buncher.scala | 107 ------------------ project/AkkaBuild.scala | 8 +- 12 files changed, 138 insertions(+), 157 deletions(-) create mode 100644 akka-samples/akka-sample-fsm-scala/LICENSE create mode 100644 akka-samples/akka-sample-fsm-scala/activator.properties create mode 100644 akka-samples/akka-sample-fsm-scala/build.sbt create mode 100644 akka-samples/akka-sample-fsm-scala/project/build.properties rename akka-samples/{akka-sample-fsm/src/main/scala => akka-sample-fsm-scala/src/main/scala/sample/become}/DiningHakkersOnBecome.scala (88%) rename akka-samples/{akka-sample-fsm/src/main/scala => akka-sample-fsm-scala/src/main/scala/sample/fsm}/DiningHakkersOnFsm.scala (94%) create mode 100644 akka-samples/akka-sample-fsm-scala/tutorial/index.html delete mode 100644 akka-samples/akka-sample-fsm/README.md delete mode 100644 akka-samples/akka-sample-fsm/src/main/scala/Buncher.scala diff --git a/akka-docs/rst/scala/actors.rst b/akka-docs/rst/scala/actors.rst index 407bc1927f..7d70b4cfb0 100644 --- a/akka-docs/rst/scala/actors.rst +++ b/akka-docs/rst/scala/actors.rst @@ -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 diff --git a/akka-docs/rst/scala/fsm.rst b/akka-docs/rst/scala/fsm.rst index 990528d791..c6c891e50c 100644 --- a/akka-docs/rst/scala/fsm.rst +++ b/akka-docs/rst/scala/fsm.rst @@ -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 `_ template named +`Akka FSM in Scala `_ diff --git a/akka-samples/akka-sample-fsm-scala/LICENSE b/akka-samples/akka-sample-fsm-scala/LICENSE new file mode 100644 index 0000000000..970cb31478 --- /dev/null +++ b/akka-samples/akka-sample-fsm-scala/LICENSE @@ -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. diff --git a/akka-samples/akka-sample-fsm-scala/activator.properties b/akka-samples/akka-sample-fsm-scala/activator.properties new file mode 100644 index 0000000000..dd3e4429a8 --- /dev/null +++ b/akka-samples/akka-sample-fsm-scala/activator.properties @@ -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 diff --git a/akka-samples/akka-sample-fsm-scala/build.sbt b/akka-samples/akka-sample-fsm-scala/build.sbt new file mode 100644 index 0000000000..410850f86b --- /dev/null +++ b/akka-samples/akka-sample-fsm-scala/build.sbt @@ -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" +) + diff --git a/akka-samples/akka-sample-fsm-scala/project/build.properties b/akka-samples/akka-sample-fsm-scala/project/build.properties new file mode 100644 index 0000000000..37b489cb6e --- /dev/null +++ b/akka-samples/akka-sample-fsm-scala/project/build.properties @@ -0,0 +1 @@ +sbt.version=0.13.1 diff --git a/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnBecome.scala b/akka-samples/akka-sample-fsm-scala/src/main/scala/sample/become/DiningHakkersOnBecome.scala similarity index 88% rename from akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnBecome.scala rename to akka-samples/akka-sample-fsm-scala/src/main/scala/sample/become/DiningHakkersOnBecome.scala index 7374c676b2..5340faf0a3 100644 --- a/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnBecome.scala +++ b/akka-samples/akka-sample-fsm-scala/src/main/scala/sample/become/DiningHakkersOnBecome.scala @@ -1,16 +1,14 @@ /** * Copyright (C) 2009-2014 Typesafe Inc. . */ -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)) - become(thinking) - system.scheduler.scheduleOnce(5 seconds, self, Eat) + startThinking(5.seconds) + } + + private def startThinking(duration: FiniteDuration): Unit = { + become(thinking) + 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() diff --git a/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnFsm.scala b/akka-samples/akka-sample-fsm-scala/src/main/scala/sample/fsm/DiningHakkersOnFsm.scala similarity index 94% rename from akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnFsm.scala rename to akka-samples/akka-sample-fsm-scala/src/main/scala/sample/fsm/DiningHakkersOnFsm.scala index f36a1ab26c..b7be76c6da 100644 --- a/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnFsm.scala +++ b/akka-samples/akka-sample-fsm-scala/src/main/scala/sample/fsm/DiningHakkersOnFsm.scala @@ -1,13 +1,15 @@ /** * Copyright (C) 2009-2014 Typesafe Inc. . */ -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 diff --git a/akka-samples/akka-sample-fsm-scala/tutorial/index.html b/akka-samples/akka-sample-fsm-scala/tutorial/index.html new file mode 100644 index 0000000000..48641d39cf --- /dev/null +++ b/akka-samples/akka-sample-fsm-scala/tutorial/index.html @@ -0,0 +1,73 @@ + + + Akka FSM in Scala + + + + +
+

Finite State Machine in Actors

+ +

+This sample is an adaptation of +Dining Hakkers. +It illustrates how state and behavior can be managed within +an Actor with two different approaches; using become and using +the FSM trait. +

+ + +
+
+ +

Dining Hakkers with Become

+ +

+Open DiningHakkersOnBecome.scala. +

+ +

+It illustrates how current behavior can be replaced with context.become. +Note that no var members are used, instead the state is encoded in the current +behavior and its parameters. +

+ +

+Go to the Run tab, and start the application main class +sample.become.DiningHakkersOnBecome. +In the log output you can see the actions of the Hakker actors. +

+ +

+Read more about become in +the documentation. +

+ +
+
+ +

Dining Hakkers with FSM

+ +

+Open DiningHakkersOnFsm.scala. +

+ +

+It illustrates how the states and transitions can be defined with the akka.actor.FSM trait. +

+ +

+Go to the Run tab, and start the application main class +sample.fsm.DiningHakkersOnFsm. +In the log output you can see the actions of the Hakker actors. +

+ +

+Read more about akka.actor.FSM in +the documentation. +

+ +
+ + + diff --git a/akka-samples/akka-sample-fsm/README.md b/akka-samples/akka-sample-fsm/README.md deleted file mode 100644 index 223782c0f1..0000000000 --- a/akka-samples/akka-sample-fsm/README.md +++ /dev/null @@ -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). diff --git a/akka-samples/akka-sample-fsm/src/main/scala/Buncher.scala b/akka-samples/akka-sample-fsm/src/main/scala/Buncher.scala deleted file mode 100644 index 3a223da77b..0000000000 --- a/akka-samples/akka-sample-fsm/src/main/scala/Buncher.scala +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright (C) 2009-2014 Typesafe Inc. . - */ -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 - } - -} diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index 7eae0cbca8..727ff9a305 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -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 )