Merge branch 'master' of github.com:akka/akka
This commit is contained in:
commit
4b5d107e66
30 changed files with 462 additions and 1312 deletions
|
|
@ -385,6 +385,12 @@ prints the result and shuts down the ``ActorSystem``.
|
|||
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/java/akka/tutorial/first/java/Pi.java#result-listener
|
||||
|
||||
Please note that shutting down the actor system should be done by that part of
|
||||
the application which can safely determine that everything has been said and
|
||||
done. In this case, it is the Listener actor, but in other scenarios it might
|
||||
be the main thread or some other external service. It is by no means required
|
||||
to call ``system.shutdown()`` from within that system.
|
||||
|
||||
Bootstrap the calculation
|
||||
-------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -417,6 +417,12 @@ prints the result and shuts down the ``ActorSystem``.
|
|||
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala#result-listener
|
||||
|
||||
Please note that shutting down the actor system should be done by that part of
|
||||
the application which can safely determine that everything has been said and
|
||||
done. In this case, it is the Listener actor, but in other scenarios it might
|
||||
be the main thread or some other external service. It is by no means required
|
||||
to call ``system.shutdown()`` from within that system.
|
||||
|
||||
Bootstrap the calculation
|
||||
=========================
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ package akka.docs.actor;
|
|||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.Props;
|
||||
import static akka.actor.Actors.*;
|
||||
import akka.actor.PoisonPill;
|
||||
import akka.actor.UntypedActor;
|
||||
|
||||
//#context-actorOf
|
||||
|
|
@ -16,6 +16,6 @@ public class FirstUntypedActor extends UntypedActor {
|
|||
|
||||
public void onReceive(Object message) {
|
||||
myActor.forward(message, getContext());
|
||||
myActor.tell(poisonPill());
|
||||
myActor.tell(PoisonPill.getInstance());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
package akka.docs.actor;
|
||||
|
||||
//#receive-timeout
|
||||
import akka.actor.Actors;
|
||||
import akka.actor.ReceiveTimeout;
|
||||
import akka.actor.UntypedActor;
|
||||
import akka.util.Duration;
|
||||
|
|
@ -18,7 +17,7 @@ public class MyReceivedTimeoutUntypedActor extends UntypedActor {
|
|||
public void onReceive(Object message) {
|
||||
if (message.equals("Hello")) {
|
||||
getSender().tell("Hello world");
|
||||
} else if (message == Actors.receiveTimeout()) {
|
||||
} else if (message == ReceiveTimeout.getInstance()) {
|
||||
throw new RuntimeException("received timeout");
|
||||
} else {
|
||||
unhandled(message);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ import akka.util.Timeout;
|
|||
//#import-future
|
||||
|
||||
//#import-actors
|
||||
import static akka.actor.Actors.*;
|
||||
import akka.actor.PoisonPill;
|
||||
import akka.actor.Kill;
|
||||
//#import-actors
|
||||
|
||||
//#import-procedure
|
||||
|
|
@ -158,7 +159,7 @@ public class UntypedActorDocTestBase {
|
|||
ActorSystem system = ActorSystem.create("MySystem");
|
||||
ActorRef myActor = system.actorOf(new Props(MyUntypedActor.class));
|
||||
//#poison-pill
|
||||
myActor.tell(poisonPill());
|
||||
myActor.tell(PoisonPill.getInstance());
|
||||
//#poison-pill
|
||||
system.shutdown();
|
||||
}
|
||||
|
|
@ -168,7 +169,7 @@ public class UntypedActorDocTestBase {
|
|||
ActorSystem system = ActorSystem.create("MySystem");
|
||||
ActorRef victim = system.actorOf(new Props(MyUntypedActor.class));
|
||||
//#kill
|
||||
victim.tell(kill());
|
||||
victim.tell(Kill.getInstance());
|
||||
//#kill
|
||||
system.shutdown();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ public class FaultHandlingDocSample {
|
|||
log.info("That's all, shutting down");
|
||||
getContext().system().shutdown();
|
||||
}
|
||||
} else if (msg == Actors.receiveTimeout()) {
|
||||
} else if (msg == ReceiveTimeout.getInstance()) {
|
||||
// No progress within 15 seconds, ServiceUnavailable
|
||||
log.error("Shutting down due to unavailable service");
|
||||
getContext().system().shutdown();
|
||||
|
|
|
|||
|
|
@ -4,27 +4,22 @@
|
|||
package akka.docs.dispatcher;
|
||||
|
||||
//#imports
|
||||
import akka.actor.*;
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.Props;
|
||||
import akka.dispatch.MessageDispatcher;
|
||||
|
||||
import akka.actor.UntypedActor;
|
||||
import akka.actor.UntypedActorFactory;
|
||||
//#imports
|
||||
|
||||
//#imports-prio
|
||||
import akka.actor.UntypedActor;
|
||||
import akka.actor.UntypedActorFactory;
|
||||
import akka.actor.Actors;
|
||||
import akka.event.Logging;
|
||||
import akka.event.LoggingAdapter;
|
||||
|
||||
//#imports-prio
|
||||
|
||||
//#imports-prio-mailbox
|
||||
import akka.actor.ActorContext;
|
||||
import akka.dispatch.PriorityGenerator;
|
||||
import akka.dispatch.UnboundedPriorityMailbox;
|
||||
import akka.dispatch.MailboxType;
|
||||
import akka.dispatch.MessageQueue;
|
||||
import com.typesafe.config.Config;
|
||||
|
||||
//#imports-prio-mailbox
|
||||
|
|
@ -37,7 +32,6 @@ import static org.junit.Assert.*;
|
|||
|
||||
import com.typesafe.config.ConfigFactory;
|
||||
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.docs.actor.MyUntypedActor;
|
||||
import akka.docs.actor.UntypedActorDocTestBase.MyActor;
|
||||
import akka.testkit.AkkaSpec;
|
||||
|
|
@ -93,7 +87,7 @@ public class DispatcherDocTestBase {
|
|||
getSelf().tell("pigdog2");
|
||||
getSelf().tell("pigdog3");
|
||||
getSelf().tell("highpriority");
|
||||
getSelf().tell(Actors.poisonPill());
|
||||
getSelf().tell(PoisonPill.getInstance());
|
||||
}
|
||||
|
||||
public void onReceive(Object message) {
|
||||
|
|
@ -133,7 +127,7 @@ public class DispatcherDocTestBase {
|
|||
return 0; // 'highpriority messages should be treated first if possible
|
||||
else if (message.equals("lowpriority"))
|
||||
return 2; // 'lowpriority messages should be treated last if possible
|
||||
else if (message.equals(Actors.poisonPill()))
|
||||
else if (message.equals(PoisonPill.getInstance()))
|
||||
return 3; // PoisonPill when no other left
|
||||
else
|
||||
return 1; // By default they go between high and low prio
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
Camel
|
||||
#######
|
||||
|
||||
Additional Resources
|
||||
====================
|
||||
For an introduction to akka-camel 2, see also the Peter Gabryanczyk's talk `Migrating akka-camel module to Akka 2.x`_.
|
||||
|
||||
For an introduction to akka-camel 1, see also the `Appendix E - Akka and Camel`_
|
||||
|
|
@ -32,7 +34,9 @@ actor API, actors can now exchange messages with other systems over large number
|
|||
of protocols and APIs such as HTTP, SOAP, TCP, FTP, SMTP or JMS, to mention a
|
||||
few. At the moment, approximately 80 protocols and APIs are supported.
|
||||
|
||||
The akka-camel module is based on `Apache Camel`_, a powerful and leight-weight
|
||||
Apache Camel
|
||||
------------
|
||||
The akka-camel module is based on `Apache Camel`_, a powerful and light-weight
|
||||
integration framework for the JVM. For an introduction to Apache Camel you may
|
||||
want to read this `Apache Camel article`_. Camel comes with a
|
||||
large number of `components`_ that provide bindings to different protocols and
|
||||
|
|
@ -43,6 +47,8 @@ APIs. The `camel-extra`_ project provides further components.
|
|||
.. _components: http://camel.apache.org/components.html
|
||||
.. _camel-extra: http://code.google.com/p/camel-extra/
|
||||
|
||||
Consumer
|
||||
--------
|
||||
Usage of Camel's integration components in Akka is essentially a
|
||||
one-liner. Here's an example.
|
||||
|
||||
|
|
@ -60,16 +66,20 @@ component`_), only the actor's endpointUri method must be changed.
|
|||
|
||||
.. includecode:: code/akka/docs/camel/Introduction.scala#Consumer
|
||||
|
||||
Producer
|
||||
--------
|
||||
Actors can also trigger message exchanges with external systems i.e. produce to
|
||||
Camel endpoints.
|
||||
|
||||
.. includecode:: code/akka/docs/camel/Introduction.scala
|
||||
:include: imports,Producer
|
||||
|
||||
In the above example, any message sent to this actor will be added (produced) to
|
||||
the example JMS queue. Producer actors may choose from the same set of Camel
|
||||
In the above example, any message sent to this actor will be sent to
|
||||
the JMS queue ``orders``. Producer actors may choose from the same set of Camel
|
||||
components as Consumer actors do.
|
||||
|
||||
CamelMessage
|
||||
------------
|
||||
The number of Camel components is constantly increasing. The akka-camel module
|
||||
can support these in a plug-and-play manner. Just add them to your application's
|
||||
classpath, define a component-specific endpoint URI and use it to exchange
|
||||
|
|
@ -83,3 +93,66 @@ representations which are used by Consumer and Producer actors for pattern
|
|||
matching, transformation, serialization or storage.
|
||||
|
||||
__ https://svn.apache.org/repos/asf/camel/trunk/camel-core/src/main/java/org/apache/camel/Message.java
|
||||
|
||||
|
||||
Dependencies
|
||||
============
|
||||
|
||||
SBT
|
||||
---
|
||||
.. code-block:: scala
|
||||
|
||||
"com.typesafe.akka" % "akka-camel" % "2.1-SNAPSHOT"
|
||||
|
||||
Maven
|
||||
-----
|
||||
.. code-block:: xml
|
||||
|
||||
<dependency>
|
||||
<groupId>com.typesafe.akka</groupId>
|
||||
<artifactId>akka-camel</artifactId>
|
||||
<version>2.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
.. _camel-consumer-actors:
|
||||
|
||||
|
||||
Consumer Actors
|
||||
================
|
||||
|
||||
For objects to receive messages, they must mixin the `Consumer`_
|
||||
trait. For example, the following actor class (Consumer1) implements the
|
||||
endpointUri method, which is declared in the Consumer trait, in order to receive
|
||||
messages from the ``file:data/input/actor`` Camel endpoint.
|
||||
|
||||
.. _Consumer: http://github.com/akka/akka/blob/master/akka-camel/src/main/scala/akka/camel/Consumer.scala
|
||||
|
||||
.. includecode:: code/akka/docs/camel/Consumers.scala#Consumer1
|
||||
|
||||
Whenever a file is put into the data/input/actor directory, its content is
|
||||
picked up by the Camel `file component`_ and sent as message to the
|
||||
actor. Messages consumed by actors from Camel endpoints are of type
|
||||
`CamelMessage`_. These are immutable representations of Camel messages.
|
||||
|
||||
.. _file component: http://camel.apache.org/file2.html
|
||||
.. _Message: http://github.com/akka/akka/blob/master/akka-camel/src/main/scala/akka/camel/CamelMessage.scala
|
||||
|
||||
|
||||
Here's another example that sets the endpointUri to
|
||||
``jetty:http://localhost:8877/camel/default``. It causes Camel's `Jetty
|
||||
component`_ to start an embedded `Jetty`_ server, accepting HTTP connections
|
||||
from localhost on port 8877.
|
||||
|
||||
.. _Jetty component: http://camel.apache.org/jetty.html
|
||||
.. _Jetty: http://www.eclipse.org/jetty/
|
||||
|
||||
.. includecode:: code/akka/docs/camel/Consumers.scala#Consumer2
|
||||
|
||||
After starting the actor, clients can send messages to that actor by POSTing to
|
||||
``http://localhost:8877/camel/default``. The actor sends a response by using the
|
||||
self.reply method (Scala). For returning a message body and headers to the HTTP
|
||||
client the response type should be `Message`_. For any other response type, a
|
||||
new Message object is created by akka-camel with the actor response as message
|
||||
body.
|
||||
|
||||
.. _Message: http://github.com/akka/akka/blob/master/akka-camel/src/main/scala/akka/camel/CamelMessage.scala
|
||||
|
|
|
|||
|
|
@ -37,11 +37,15 @@ class FSMDocSpec extends AkkaSpec {
|
|||
//#simple-fsm
|
||||
class Buncher extends Actor with FSM[State, Data] {
|
||||
|
||||
//#fsm-body
|
||||
startWith(Idle, Uninitialized)
|
||||
|
||||
//#when-syntax
|
||||
when(Idle) {
|
||||
case Event(SetTarget(ref), Uninitialized) ⇒ stay using Todo(ref, Vector.empty)
|
||||
case Event(SetTarget(ref), Uninitialized) ⇒
|
||||
stay using Todo(ref, Vector.empty)
|
||||
}
|
||||
//#when-syntax
|
||||
|
||||
//#transition-elided
|
||||
onTransition {
|
||||
|
|
@ -51,10 +55,13 @@ class FSMDocSpec extends AkkaSpec {
|
|||
}
|
||||
}
|
||||
//#transition-elided
|
||||
//#when-syntax
|
||||
|
||||
when(Active, stateTimeout = 1 second) {
|
||||
case Event(Flush | FSM.StateTimeout, t: Todo) ⇒ goto(Idle) using t.copy(queue = Vector.empty)
|
||||
case Event(Flush | StateTimeout, t: Todo) ⇒
|
||||
goto(Idle) using t.copy(queue = Vector.empty)
|
||||
}
|
||||
//#when-syntax
|
||||
|
||||
//#unhandled-elided
|
||||
whenUnhandled {
|
||||
|
|
@ -67,10 +74,116 @@ class FSMDocSpec extends AkkaSpec {
|
|||
stay
|
||||
}
|
||||
//#unhandled-elided
|
||||
//#fsm-body
|
||||
|
||||
initialize
|
||||
}
|
||||
//#simple-fsm
|
||||
object DemoCode {
|
||||
trait StateType
|
||||
case object SomeState extends StateType
|
||||
case object Processing extends StateType
|
||||
case object Error extends StateType
|
||||
case object Idle extends StateType
|
||||
case object Active extends StateType
|
||||
|
||||
class Dummy extends Actor with FSM[StateType, Int] {
|
||||
class X
|
||||
val newData = 42
|
||||
object WillDo
|
||||
object Tick
|
||||
|
||||
//#modifier-syntax
|
||||
when(SomeState) {
|
||||
case Event(msg, _) ⇒
|
||||
goto(Processing) using (newData) forMax (5 seconds) replying (WillDo)
|
||||
}
|
||||
//#modifier-syntax
|
||||
|
||||
//#transition-syntax
|
||||
onTransition {
|
||||
case Idle -> Active ⇒ setTimer("timeout", Tick, 1 second, true)
|
||||
case Active -> _ ⇒ cancelTimer("timeout")
|
||||
case x -> Idle ⇒ log.info("entering Idle from " + x)
|
||||
}
|
||||
//#transition-syntax
|
||||
|
||||
//#alt-transition-syntax
|
||||
onTransition(handler _)
|
||||
|
||||
def handler(from: StateType, to: StateType) {
|
||||
// handle it here ...
|
||||
}
|
||||
//#alt-transition-syntax
|
||||
|
||||
//#stop-syntax
|
||||
when(Error) {
|
||||
case Event("stop", _) ⇒
|
||||
// do cleanup ...
|
||||
stop()
|
||||
}
|
||||
//#stop-syntax
|
||||
|
||||
//#transform-syntax
|
||||
when(SomeState)(transform {
|
||||
case Event(bytes: Array[Byte], read) ⇒ stay using (read + bytes.length)
|
||||
case Event(bytes: List[Byte], read) ⇒ stay using (read + bytes.size)
|
||||
} using {
|
||||
case s @ FSM.State(state, read, timeout, stopReason, replies) if read > 1000 ⇒
|
||||
goto(Processing)
|
||||
})
|
||||
//#transform-syntax
|
||||
|
||||
//#alt-transform-syntax
|
||||
val processingTrigger: PartialFunction[State, State] = {
|
||||
case s @ FSM.State(state, read, timeout, stopReason, replies) if read > 1000 ⇒
|
||||
goto(Processing)
|
||||
}
|
||||
|
||||
when(SomeState)(transform {
|
||||
case Event(bytes: Array[Byte], read) ⇒ stay using (read + bytes.length)
|
||||
case Event(bytes: List[Byte], read) ⇒ stay using (read + bytes.size)
|
||||
} using processingTrigger)
|
||||
//#alt-transform-syntax
|
||||
|
||||
//#termination-syntax
|
||||
onTermination {
|
||||
case StopEvent(FSM.Normal, state, data) ⇒ // ...
|
||||
case StopEvent(FSM.Shutdown, state, data) ⇒ // ...
|
||||
case StopEvent(FSM.Failure(cause), state, data) ⇒ // ...
|
||||
}
|
||||
//#termination-syntax
|
||||
|
||||
//#unhandled-syntax
|
||||
whenUnhandled {
|
||||
case Event(x: X, data) ⇒
|
||||
log.info("Received unhandled event: " + x)
|
||||
stay
|
||||
case Event(msg, _) ⇒
|
||||
log.warning("Received unknown event: " + msg)
|
||||
goto(Error)
|
||||
}
|
||||
//#unhandled-syntax
|
||||
|
||||
}
|
||||
|
||||
//#logging-fsm
|
||||
import akka.actor.LoggingFSM
|
||||
class MyFSM extends Actor with LoggingFSM[StateType, Data] {
|
||||
//#body-elided
|
||||
override def logDepth = 12
|
||||
onTermination {
|
||||
case StopEvent(FSM.Failure(_), state, data) ⇒
|
||||
val lastEvents = getLog.mkString("\n\t")
|
||||
log.warning("Failure in state " + state + " with data " + data + "\n" +
|
||||
"Events leading up to this point:\n\t" + lastEvents)
|
||||
}
|
||||
// ...
|
||||
//#body-elided
|
||||
}
|
||||
//#logging-fsm
|
||||
|
||||
}
|
||||
//#fsm-code-elided
|
||||
|
||||
"batch correctly" in {
|
||||
|
|
|
|||
30
akka-docs/scala/code/akka/docs/camel/Consumers.scala
Normal file
30
akka-docs/scala/code/akka/docs/camel/Consumers.scala
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
package akka.docs.camel
|
||||
|
||||
object Consumers {
|
||||
{
|
||||
//#Consumer1
|
||||
import akka.camel.{ CamelMessage, Consumer }
|
||||
|
||||
class Consumer1 extends Consumer {
|
||||
def endpointUri = "file:data/input/actor"
|
||||
|
||||
def receive = {
|
||||
case msg: CamelMessage ⇒ println("received %s" format msg.bodyAs[String])
|
||||
}
|
||||
}
|
||||
//#Consumer1
|
||||
}
|
||||
{
|
||||
//#Consumer2
|
||||
import akka.camel.{ CamelMessage, Consumer }
|
||||
|
||||
class Consumer2 extends Consumer {
|
||||
def endpointUri = "jetty:http://localhost:8877/camel/default"
|
||||
|
||||
def receive = {
|
||||
case msg: CamelMessage ⇒ sender ! ("Hello %s" format msg.bodyAs[String])
|
||||
}
|
||||
}
|
||||
//#Consumer2
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
package akka.docs.camel
|
||||
|
||||
object wrapper {
|
||||
object Introduction {
|
||||
{
|
||||
//#Consumer-mina
|
||||
import akka.camel.{ CamelMessage, Consumer }
|
||||
|
||||
class MyActor extends Consumer {
|
||||
class MinaClient extends Consumer {
|
||||
def endpointUri = "mina:tcp://localhost:6200?textline=true"
|
||||
|
||||
def receive = {
|
||||
|
|
@ -18,14 +18,14 @@ object wrapper {
|
|||
import akka.actor.{ ActorSystem, Props }
|
||||
|
||||
val sys = ActorSystem("camel")
|
||||
val myActor = sys.actorOf(Props[MyActor])
|
||||
val mina = sys.actorOf(Props[MinaClient])
|
||||
//#Consumer-mina
|
||||
}
|
||||
{
|
||||
//#Consumer
|
||||
import akka.camel.{ CamelMessage, Consumer }
|
||||
|
||||
class MyActor extends Consumer {
|
||||
class JettyAdapter extends Consumer {
|
||||
def endpointUri = "jetty:http://localhost:8877/example"
|
||||
|
||||
def receive = {
|
||||
|
|
@ -39,10 +39,16 @@ object wrapper {
|
|||
//#Producer
|
||||
import akka.actor.Actor
|
||||
import akka.camel.{ Producer, Oneway }
|
||||
import akka.actor.{ ActorSystem, Props }
|
||||
|
||||
class MyActor extends Actor with Producer with Oneway {
|
||||
def endpointUri = "jms:queue:example"
|
||||
class Orders extends Actor with Producer with Oneway {
|
||||
def endpointUri = "jms:queue:Orders"
|
||||
}
|
||||
|
||||
val sys = ActorSystem("camel")
|
||||
val orders = sys.actorOf(Props[Orders])
|
||||
|
||||
orders ! <order amount="100" currency="PLN" itemId="12345"/>
|
||||
//#Producer
|
||||
}
|
||||
}
|
||||
|
|
@ -118,19 +118,11 @@ The FSM Trait and Object
|
|||
|
||||
The :class:`FSM` trait may only be mixed into an :class:`Actor`. Instead of
|
||||
extending :class:`Actor`, the self type approach was chosen in order to make it
|
||||
obvious that an actor is actually created. Importing all members of the
|
||||
:obj:`FSM` object is recommended if you want to directly access the symbols
|
||||
like :obj:`StateTimeout`. This import is usually placed inside the state
|
||||
machine definition:
|
||||
obvious that an actor is actually created:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
class MyFSM extends Actor with FSM[State, Data] {
|
||||
import FSM._
|
||||
|
||||
...
|
||||
|
||||
}
|
||||
.. includecode:: code/akka/docs/actor/FSMDocSpec.scala
|
||||
:include: simple-fsm
|
||||
:exclude: fsm-body
|
||||
|
||||
The :class:`FSM` trait takes two type parameters:
|
||||
|
||||
|
|
@ -153,7 +145,7 @@ Defining States
|
|||
|
||||
A state is defined by one or more invocations of the method
|
||||
|
||||
:func:`when(<name>[, stateTimeout = <timeout>])(stateFunction)`.
|
||||
:func:`when(<name>[, stateTimeout = <timeout>])(stateFunction)`.
|
||||
|
||||
The given name must be an object which is type-compatible with the first type
|
||||
parameter given to the :class:`FSM` trait. This object is used as a hash key,
|
||||
|
|
@ -165,27 +157,18 @@ If the :meth:`stateTimeout` parameter is given, then all transitions into this
|
|||
state, including staying, receive this timeout by default. Initiating the
|
||||
transition with an explicit timeout may be used to override this default, see
|
||||
`Initiating Transitions`_ for more information. The state timeout of any state
|
||||
may be changed during action processing with :func:`setStateTimeout(state,
|
||||
duration)`. This enables runtime configuration e.g. via external message.
|
||||
may be changed during action processing with
|
||||
:func:`setStateTimeout(state, duration)`. This enables runtime configuration
|
||||
e.g. via external message.
|
||||
|
||||
The :meth:`stateFunction` argument is a :class:`PartialFunction[Event, State]`,
|
||||
The :meth:`stateFunction` argument is a :class:`PartialFunction[Event, State]`,
|
||||
which is conveniently given using the partial function literal syntax as
|
||||
demonstrated below:
|
||||
|
||||
.. code-block:: scala
|
||||
.. includecode:: code/akka/docs/actor/FSMDocSpec.scala
|
||||
:include: when-syntax
|
||||
|
||||
when(Idle) {
|
||||
case Event(Start(msg), _) =>
|
||||
goto(Timer) using (msg, sender)
|
||||
}
|
||||
|
||||
when(Timer, stateTimeout = 12 seconds) {
|
||||
case Event(StateTimeout, (msg, sender)) =>
|
||||
sender ! msg
|
||||
goto(Idle)
|
||||
}
|
||||
|
||||
The :class:`Event(msg: Any, data: D)` case class is parameterized with the data
|
||||
The :class:`Event(msg: Any, data: D)` case class is parameterized with the data
|
||||
type held by the FSM for convenient pattern matching.
|
||||
|
||||
Defining the Initial State
|
||||
|
|
@ -193,7 +176,7 @@ Defining the Initial State
|
|||
|
||||
Each FSM needs a starting point, which is declared using
|
||||
|
||||
:func:`startWith(state, data[, timeout])`
|
||||
:func:`startWith(state, data[, timeout])`
|
||||
|
||||
The optionally given timeout argument overrides any specification given for the
|
||||
desired initial state. If you want to cancel a default timeout, use
|
||||
|
|
@ -206,16 +189,8 @@ If a state doesn't handle a received event a warning is logged. If you want to
|
|||
do something else in this case you can specify that with
|
||||
:func:`whenUnhandled(stateFunction)`:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
whenUnhandled {
|
||||
case Event(x : X, data) =>
|
||||
log.info(this, "Received unhandled event: " + x)
|
||||
stay
|
||||
case Event(msg, _) =>
|
||||
log.warn(this, "Received unknown event: " + x)
|
||||
goto(Error)
|
||||
}
|
||||
.. includecode:: code/akka/docs/actor/FSMDocSpec.scala
|
||||
:include: unhandled-syntax
|
||||
|
||||
**IMPORTANT**: This handler is not stacked, meaning that each invocation of
|
||||
:func:`whenUnhandled` replaces the previously installed handler.
|
||||
|
|
@ -230,7 +205,8 @@ The state definition can either be the current state, as described by the
|
|||
:func:`goto(state)`. The resulting object allows further qualification by way
|
||||
of the modifiers described in the following:
|
||||
|
||||
:meth:`forMax(duration)`
|
||||
* :meth:`forMax(duration)`
|
||||
|
||||
This modifier sets a state timeout on the next state. This means that a timer
|
||||
is started which upon expiry sends a :obj:`StateTimeout` message to the FSM.
|
||||
This timer is canceled upon reception of any other message in the meantime;
|
||||
|
|
@ -241,23 +217,21 @@ of the modifiers described in the following:
|
|||
specified for the target state. If you want to cancel the default timeout,
|
||||
use :obj:`Duration.Inf`.
|
||||
|
||||
:meth:`using(data)`
|
||||
* :meth:`using(data)`
|
||||
|
||||
This modifier replaces the old state data with the new data given. If you
|
||||
follow the advice :ref:`above <fsm-philosophy>`, this is the only place where
|
||||
internal state data are ever modified.
|
||||
|
||||
:meth:`replying(msg)`
|
||||
* :meth:`replying(msg)`
|
||||
|
||||
This modifier sends a reply to the currently processed message and otherwise
|
||||
does not modify the state transition.
|
||||
|
||||
All modifier can be chained to achieve a nice and concise description:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
when(State) {
|
||||
case Event(msg, _) =>
|
||||
goto(Processing) using (msg) forMax (5 seconds) replying (WillDo)
|
||||
}
|
||||
.. includecode:: code/akka/docs/actor/FSMDocSpec.scala
|
||||
:include: modifier-syntax
|
||||
|
||||
The parentheses are not actually needed in all cases, but they visually
|
||||
distinguish between modifiers and their arguments and therefore make the code
|
||||
|
|
@ -267,7 +241,7 @@ even more pleasant to read for foreigners.
|
|||
|
||||
Please note that the ``return`` statement may not be used in :meth:`when`
|
||||
blocks or similar; this is a Scala restriction. Either refactor your code
|
||||
using ``if () ... else ...`` or move it into a method definition.
|
||||
using ``if () ... else ...`` or move it into a method definition.
|
||||
|
||||
Monitoring Transitions
|
||||
----------------------
|
||||
|
|
@ -293,13 +267,8 @@ The handler is a partial function which takes a pair of states as input; no
|
|||
resulting state is needed as it is not possible to modify the transition in
|
||||
progress.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
onTransition {
|
||||
case Idle -> Active => setTimer("timeout")
|
||||
case Active -> _ => cancelTimer("timeout")
|
||||
case x -> Idle => log.info("entering Idle from "+x)
|
||||
}
|
||||
.. includecode:: code/akka/docs/actor/FSMDocSpec.scala
|
||||
:include: transition-syntax
|
||||
|
||||
The convenience extractor :obj:`->` enables decomposition of the pair of states
|
||||
with a clear visual reminder of the transition's direction. As usual in pattern
|
||||
|
|
@ -311,13 +280,8 @@ It is also possible to pass a function object accepting two states to
|
|||
:func:`onTransition`, in case your transition handling logic is implemented as
|
||||
a method:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
onTransition(handler _)
|
||||
|
||||
private def handler(from: State, to: State) {
|
||||
...
|
||||
}
|
||||
.. includecode:: code/akka/docs/actor/FSMDocSpec.scala
|
||||
:include: alt-transition-syntax
|
||||
|
||||
The handlers registered with this method are stacked, so you can intersperse
|
||||
:func:`onTransition` blocks with :func:`when` blocks as suits your design. It
|
||||
|
|
@ -338,8 +302,8 @@ External Monitoring
|
|||
|
||||
External actors may be registered to be notified of state transitions by
|
||||
sending a message :class:`SubscribeTransitionCallBack(actorRef)`. The named
|
||||
actor will be sent a :class:`CurrentState(self, stateName)` message immediately
|
||||
and will receive :class:`Transition(actorRef, oldState, newState)` messages
|
||||
actor will be sent a :class:`CurrentState(self, stateName)` message immediately
|
||||
and will receive :class:`Transition(actorRef, oldState, newState)` messages
|
||||
whenever a new state is reached. External monitors may be unregistered by
|
||||
sending :class:`UnsubscribeTransitionCallBack(actorRef)` to the FSM actor.
|
||||
|
||||
|
|
@ -347,13 +311,31 @@ Registering a not-running listener generates a warning and fails gracefully.
|
|||
Stopping a listener without unregistering will remove the listener from the
|
||||
subscription list upon the next transition.
|
||||
|
||||
Transforming State
|
||||
------------------
|
||||
|
||||
The partial functions supplied as argument to the ``when()`` blocks can be
|
||||
transformed using Scala’s full supplement of functional programming tools. In
|
||||
order to retain type inference, there is a helper function which may be used in
|
||||
case some common handling logic shall be applied to different clauses:
|
||||
|
||||
.. includecode:: code/akka/docs/actor/FSMDocSpec.scala
|
||||
:include: transform-syntax
|
||||
|
||||
It goes without saying that the arguments to this method may also be stored, to
|
||||
be used several times, e.g. when applying the same transformation to several
|
||||
``when()`` blocks:
|
||||
|
||||
.. includecode:: code/akka/docs/actor/FSMDocSpec.scala
|
||||
:include: alt-transform-syntax
|
||||
|
||||
Timers
|
||||
------
|
||||
|
||||
Besides state timeouts, FSM manages timers identified by :class:`String` names.
|
||||
You may set a timer using
|
||||
|
||||
:func:`setTimer(name, msg, interval, repeat)`
|
||||
:func:`setTimer(name, msg, interval, repeat)`
|
||||
|
||||
where :obj:`msg` is the message object which will be sent after the duration
|
||||
:obj:`interval` has elapsed. If :obj:`repeat` is :obj:`true`, then the timer is
|
||||
|
|
@ -376,7 +358,7 @@ Termination from Inside
|
|||
|
||||
The FSM is stopped by specifying the result state as
|
||||
|
||||
:func:`stop([reason[, data]])`
|
||||
:func:`stop([reason[, data]])`
|
||||
|
||||
The reason must be one of :obj:`Normal` (which is the default), :obj:`Shutdown`
|
||||
or :obj:`Failure(reason)`, and the second argument may be given to change the
|
||||
|
|
@ -389,25 +371,15 @@ state data which is available during termination handling.
|
|||
the same way as a state transition (but note that the ``return`` statement
|
||||
may not be used within a :meth:`when` block).
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
when(A) {
|
||||
case Event(Stop, _) =>
|
||||
doCleanup()
|
||||
stop()
|
||||
}
|
||||
.. includecode:: code/akka/docs/actor/FSMDocSpec.scala
|
||||
:include: stop-syntax
|
||||
|
||||
You can use :func:`onTermination(handler)` to specify custom code that is
|
||||
executed when the FSM is stopped. The handler is a partial function which takes
|
||||
a :class:`StopEvent(reason, stateName, stateData)` as argument:
|
||||
a :class:`StopEvent(reason, stateName, stateData)` as argument:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
onTermination {
|
||||
case StopEvent(Normal, s, d) => ...
|
||||
case StopEvent(Shutdown, _, _) => ...
|
||||
case StopEvent(Failure(cause), s, d) => ...
|
||||
}
|
||||
.. includecode:: code/akka/docs/actor/FSMDocSpec.scala
|
||||
:include: termination-syntax
|
||||
|
||||
As for the :func:`whenUnhandled` case, this handler is not stacked, so each
|
||||
invocation of :func:`onTermination` replaces the previously installed handler.
|
||||
|
|
@ -419,7 +391,7 @@ When an :class:`ActorRef` associated to a FSM is stopped using the
|
|||
:meth:`stop()` method, its :meth:`postStop` hook will be executed. The default
|
||||
implementation by the :class:`FSM` trait is to execute the
|
||||
:meth:`onTermination` handler if that is prepared to handle a
|
||||
:obj:`StopEvent(Shutdown, ...)`.
|
||||
:obj:`StopEvent(Shutdown, ...)`.
|
||||
|
||||
.. warning::
|
||||
|
||||
|
|
@ -438,11 +410,11 @@ Event Tracing
|
|||
-------------
|
||||
|
||||
The setting ``akka.actor.debug.fsm`` in :ref:`configuration` enables logging of an
|
||||
event trace by :class:`LoggingFSM` instances::
|
||||
event trace by :class:`LoggingFSM` instances:
|
||||
|
||||
class MyFSM extends Actor with LoggingFSM[X, Z] {
|
||||
...
|
||||
}
|
||||
.. includecode:: code/akka/docs/actor/FSMDocSpec.scala
|
||||
:include: logging-fsm
|
||||
:exclude: body-elided
|
||||
|
||||
This FSM will log at DEBUG level:
|
||||
|
||||
|
|
@ -459,17 +431,10 @@ Rolling Event Log
|
|||
|
||||
The :class:`LoggingFSM` trait adds one more feature to the FSM: a rolling event
|
||||
log which may be used during debugging (for tracing how the FSM entered a
|
||||
certain failure state) or for other creative uses::
|
||||
certain failure state) or for other creative uses:
|
||||
|
||||
class MyFSM extends Actor with LoggingFSM[X, Z] {
|
||||
override def logDepth = 12
|
||||
onTermination {
|
||||
case StopEvent(Failure(_), state, data) =>
|
||||
log.warning(this, "Failure in state "+state+" with data "+data+"\n"+
|
||||
"Events leading up to this point:\n\t"+getLog.mkString("\n\t"))
|
||||
}
|
||||
...
|
||||
}
|
||||
.. includecode:: code/akka/docs/actor/FSMDocSpec.scala
|
||||
:include: logging-fsm
|
||||
|
||||
The :meth:`logDepth` defaults to zero, which turns off the event log.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue