+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
|
stack), which means that you do not use :meth:`unbecome`, instead always the
|
||||||
next behavior is explicitly installed.
|
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 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
|
the behavior stack. In this case care must be taken to ensure that the number
|
||||||
|
|
|
||||||
|
|
@ -479,7 +479,6 @@ zero.
|
||||||
Examples
|
Examples
|
||||||
========
|
========
|
||||||
|
|
||||||
A bigger FSM example contrasted with Actor's :meth:`become`/:meth:`unbecome` can be found in the sources:
|
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
|
||||||
* `Dining Hakkers using FSM <@github@/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnFsm.scala>`_
|
`Akka FSM in Scala <http://typesafe.com/activator/template/akka-sample-fsm-scala>`_
|
||||||
* `Dining Hakkers using become <@github@/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnBecome.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>.
|
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>.
|
||||||
*/
|
*/
|
||||||
package sample.fsm.dining.become
|
package sample.become
|
||||||
|
|
||||||
import language.postfixOps
|
|
||||||
|
|
||||||
//Akka adaptation of
|
|
||||||
//http://www.dalnefre.com/wp/2010/08/dining-philosophers-in-humus/
|
|
||||||
|
|
||||||
import akka.actor._
|
import akka.actor._
|
||||||
import scala.concurrent.duration._
|
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
|
* 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`) =>
|
case Taken(`chopstickToWaitFor`) =>
|
||||||
println("%s has picked up %s and %s and starts to eat".format(name, left.path.name, right.path.name))
|
println("%s has picked up %s and %s and starts to eat".format(name, left.path.name, right.path.name))
|
||||||
become(eating)
|
become(eating)
|
||||||
system.scheduler.scheduleOnce(5 seconds, self, Think)
|
system.scheduler.scheduleOnce(5.seconds, self, Think)
|
||||||
|
|
||||||
case Busy(chopstick) =>
|
case Busy(chopstick) =>
|
||||||
become(thinking)
|
|
||||||
otherChopstick ! Put(self)
|
otherChopstick ! Put(self)
|
||||||
self ! Eat
|
startThinking(10.milliseconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
//When the results of the other grab comes back,
|
//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
|
//Then go back and think and try to grab the chopsticks again
|
||||||
def denied_a_chopstick: Receive = {
|
def denied_a_chopstick: Receive = {
|
||||||
case Taken(chopstick) =>
|
case Taken(chopstick) =>
|
||||||
become(thinking)
|
|
||||||
chopstick ! Put(self)
|
chopstick ! Put(self)
|
||||||
self ! Eat
|
startThinking(10.milliseconds)
|
||||||
case Busy(chopstick) =>
|
case Busy(chopstick) =>
|
||||||
become(thinking)
|
startThinking(10.milliseconds)
|
||||||
self ! Eat
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//When a hakker is eating, he can decide to start to think,
|
//When a hakker is eating, he can decide to start to think,
|
||||||
//then he puts down his chopsticks and starts to think
|
//then he puts down his chopsticks and starts to think
|
||||||
def eating: Receive = {
|
def eating: Receive = {
|
||||||
case Think =>
|
case Think =>
|
||||||
become(thinking)
|
|
||||||
left ! Put(self)
|
left ! Put(self)
|
||||||
right ! Put(self)
|
right ! Put(self)
|
||||||
println("%s puts down his chopsticks and starts to think".format(name))
|
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
|
//All hakkers start in a non-eating state
|
||||||
def receive = {
|
def receive = {
|
||||||
case Think =>
|
case Think =>
|
||||||
println("%s starts to think".format(name))
|
println("%s starts to think".format(name))
|
||||||
become(thinking)
|
startThinking(5.seconds)
|
||||||
system.scheduler.scheduleOnce(5 seconds, self, Eat)
|
}
|
||||||
|
|
||||||
|
private def startThinking(duration: FiniteDuration): Unit = {
|
||||||
|
become(thinking)
|
||||||
|
system.scheduler.scheduleOnce(duration, self, Eat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Alright, here's our test-harness
|
* Alright, here's our test-harness
|
||||||
*/
|
*/
|
||||||
object DiningHakkers {
|
object DiningHakkersOnBecome {
|
||||||
val system = ActorSystem()
|
val system = ActorSystem()
|
||||||
|
|
||||||
def main(args: Array[String]): Unit = run()
|
def main(args: Array[String]): Unit = run()
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
/**
|
/**
|
||||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>.
|
* 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._
|
||||||
import akka.actor.FSM._
|
import akka.actor.FSM._
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
|
// Akka adaptation of
|
||||||
|
// http://www.dalnefre.com/wp/2010/08/dining-philosophers-in-humus/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Some messages for the chopstick
|
* Some messages for the chopstick
|
||||||
*/
|
*/
|
||||||
|
|
@ -91,7 +93,7 @@ class FSMHakker(name: String, left: ActorRef, right: ActorRef) extends Actor wit
|
||||||
when(Waiting) {
|
when(Waiting) {
|
||||||
case Event(Think, _) =>
|
case Event(Think, _) =>
|
||||||
println("%s starts to think".format(name))
|
println("%s starts to think".format(name))
|
||||||
startThinking(5 seconds)
|
startThinking(5.seconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
//When a hakker is thinking it can become hungry
|
//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)) =>
|
case Event(Busy(chopstick), TakenChopsticks(leftOption, rightOption)) =>
|
||||||
leftOption.foreach(_ ! Put)
|
leftOption.foreach(_ ! Put)
|
||||||
rightOption.foreach(_ ! Put)
|
rightOption.foreach(_ ! Put)
|
||||||
startThinking(10 milliseconds)
|
startThinking(10.milliseconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def startEating(left: ActorRef, right: ActorRef): State = {
|
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))
|
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,
|
// 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) {
|
when(FirstChopstickDenied) {
|
||||||
case Event(Taken(secondChopstick), _) =>
|
case Event(Taken(secondChopstick), _) =>
|
||||||
secondChopstick ! Put
|
secondChopstick ! Put
|
||||||
startThinking(10 milliseconds)
|
startThinking(10.milliseconds)
|
||||||
case Event(Busy(chopstick), _) =>
|
case Event(Busy(chopstick), _) =>
|
||||||
startThinking(10 milliseconds)
|
startThinking(10.milliseconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
// When a hakker is eating, he can decide to start to think,
|
// 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))
|
println("%s puts down his chopsticks and starts to think".format(name))
|
||||||
left ! Put
|
left ! Put
|
||||||
right ! Put
|
right ! Put
|
||||||
startThinking(5 seconds)
|
startThinking(5.seconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the hakker
|
// 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,
|
settings = parentSettings ++ ActivatorDist.settings,
|
||||||
aggregate = Seq(camelSampleJava, camelSampleScala, mainSampleJava, mainSampleScala,
|
aggregate = Seq(camelSampleJava, camelSampleScala, mainSampleJava, mainSampleScala,
|
||||||
remoteSampleJava, remoteSampleScala, clusterSampleJava, clusterSampleScala,
|
remoteSampleJava, remoteSampleScala, clusterSampleJava, clusterSampleScala,
|
||||||
fsmSample, persistenceSample,
|
fsmSampleScala, persistenceSample,
|
||||||
multiNodeSample, helloKernelSample, osgiDiningHakkersSample)
|
multiNodeSample, helloKernelSample, osgiDiningHakkersSample)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -387,9 +387,9 @@ object AkkaBuild extends Build {
|
||||||
settings = sampleSettings ++ Seq(libraryDependencies ++= Dependencies.camelSample)
|
settings = sampleSettings ++ Seq(libraryDependencies ++= Dependencies.camelSample)
|
||||||
)
|
)
|
||||||
|
|
||||||
lazy val fsmSample = Project(
|
lazy val fsmSampleScala = Project(
|
||||||
id = "akka-sample-fsm",
|
id = "akka-sample-fsm-scala",
|
||||||
base = file("akka-samples/akka-sample-fsm"),
|
base = file("akka-samples/akka-sample-fsm-scala"),
|
||||||
dependencies = Seq(actor),
|
dependencies = Seq(actor),
|
||||||
settings = sampleSettings
|
settings = sampleSettings
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue