diff --git a/akka-actor/src/main/scala/akka/actor/FSM.scala b/akka-actor/src/main/scala/akka/actor/FSM.scala
index 5b9c472c90..f0e22909d5 100644
--- a/akka-actor/src/main/scala/akka/actor/FSM.scala
+++ b/akka-actor/src/main/scala/akka/actor/FSM.scala
@@ -98,6 +98,23 @@ object FSM {
* Each of the above also supports the method replying(AnyRef) for
* sending a reply before changing state.
*
+ * While changing state, custom handlers may be invoked which are registered
+ * using onTransition. This is meant to enable concentrating
+ * different concerns in different places; you may choose to use
+ * when for describing the properties of a state, including of
+ * course initiating transitions, but you can describe the transitions using
+ * onTransision to avoid having to duplicate that code among
+ * multiple paths which lead to a transition:
+ *
+ *
+ * onTransition {
+ * case Active -> _ => cancelTimer("activeTimer")
+ * }
+ *
+ *
+ * Multiple such blocks are supported and all of them will be called, not only
+ * the first matching one.
+ *
* Another feature is that other actors may subscribe for transition events by
* sending a SubscribeTransitionCallback message to this actor;
* use UnsubscribeTransitionCallback before stopping the other
@@ -247,12 +264,43 @@ trait FSM[S, D] {
/**
* Set handler which is called upon each state transition, i.e. not when
- * staying in the same state.
+ * staying in the same state. This may use the pair extractor defined in the
+ * FSM companion object like so:
+ *
+ *
+ * onTransition {
+ * case Old -> New => doSomething
+ * }
+ *
+ *
+ * It is also possible to supply a 2-ary function object:
+ *
+ *
+ * onTransition(handler _)
+ *
+ * private def handler(from: S, to: S) { ... }
+ *
+ *
+ * The underscore is unfortunately necessary to enable the nicer syntax shown
+ * above (it uses the implicit conversion total2pf under the hood).
+ *
+ * Multiple handlers may be installed, and every one of them will be
+ * called, not only the first one matching.
*/
- protected final def onTransition(transitionHandler: TransitionHandler) = {
+ protected final def onTransition(transitionHandler: TransitionHandler) {
transitionEvent :+= transitionHandler
}
+ /**
+ * Convenience wrapper for using a total function instead of a partial
+ * function literal. To be used with onTransition.
+ */
+ implicit protected final def total2pf(transitionHandler: (S, S) => Unit) =
+ new PartialFunction[(S, S), Unit] {
+ def isDefinedAt(in : (S, S)) = true
+ def apply(in : (S, S)) { transitionHandler(in._1, in._2) }
+ }
+
/**
* Set handler which is called upon termination of this FSM actor.
*/
diff --git a/akka-actor/src/test/scala/akka/actor/actor/FSMActorSpec.scala b/akka-actor/src/test/scala/akka/actor/actor/FSMActorSpec.scala
index 42510aaad4..31d09c8ebf 100644
--- a/akka-actor/src/test/scala/akka/actor/actor/FSMActorSpec.scala
+++ b/akka-actor/src/test/scala/akka/actor/actor/FSMActorSpec.scala
@@ -68,6 +68,13 @@ object FSMActorSpec {
case Locked -> Open => transitionLatch.open
}
+ // verify that old-style does still compile
+ onTransition (transitionHandler _)
+
+ def transitionHandler(from: LockState, to: LockState) = {
+ // dummy
+ }
+
onTermination {
case StopEvent(Shutdown, Locked, _) =>
// stop is called from lockstate with shutdown as reason...