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...