I think we can do a little better than copypasta from FSM docs ( http://doc.akka.io/docs/akka/snapshot/java/lambda-fsm.html ). First, study your use case a little.
You have two triggers (or events or signals) - powerOn and powerOff. You would like to send these signals to the Actor and change his state, of which two significant states are turned on and off.
Now, strictly speaking, FSM needs another component: the action you want to take to transition.
FSM: State (S) x Event (E) -> Action (A), State (S') Read: "When in state S, if signal E is received, produce action A and advance to state S'"
You DO NOT NEED any action, but the actor cannot be directly examined or changed. All mutation and confirmation occur through asynchronous messaging.
In your example, which does not give any action to go to the transition, you basically have a state machine that does not work. Actions occur, state transitions without a side effect, and this state is invisible, so the working machine is identical to a broken one. And since all this happens asynchronously, you don’t even know when the broken thing ended.
So let me expand your contract a bit and include the following definitions in the FSM definitions:
When in Off, if powerOn is received, advance state to On and respond to the caller with the new state When in On, if powerOff is received, advance state to Off and respond to the caller with the new state
Now we could create an FSM that can actually be verified.
Define a couple of classes for your two signals. (AbstractFSM DSL expects a class match):
public static class PowerOn {} public static class PowerOff {}
Define a pair of enumerations for your two states:
enum LightswitchState { on, off }
Define the AbstractFSM actor ( http://doc.akka.io/japi/akka/2.3.8/akka/actor/AbstractFSM.html ). The AbstractFSM extension allows us to define an actor using a chain of FSM definitions similar to the above, rather than defining the behavior of the message directly in the onReceive () method. It provides a nice little DSL for these definitions and (somewhat bizarrely) expects the definitions to be installed in a static initializer.
A quick detour: AbstractFSM has two generic files that are used to provide compile-time type checking.
S is the base of the state types we want to use, and D is the database of data types. If you are creating an FSM that will hold and modify data (maybe a power meter for your lighting switch?), You should create a separate class to store this data, and not try to add new members to your AbstractFSM subclass. Since we have no data, let's define a dummy class so you can see how it goes:
public static class NoDataItsJustALightswitch {}
So, from this point of view, we can build a class of our actor.
public class Lightswitch extends AbstractFSM<LightswitchState, NoDataItsJustALightswitch> { {
I'm sure you are wondering: how can I use this ?! So give you a test harness using the direct JUnit and Akka Testkit for java:
public class LightswitchTest { @Test public void testLightswitch() { ActorSystem system = ActorSystem.create("lightswitchtest");
And here you are: the FSM indicator light. Honestly, the example, this trivial one, does not really show the power of FSM, since the dataless example can be executed as a set of “become / unsafe” actions in half of the LoC without generics or lambda. Much more readable IMO.
PS consider learning Scala, if only you can read the code of other nations! The first half of Atomic Scala is available free online.
PPS, if all you really need is a compiled state machine, I support Pulleys , a state machine mechanism based on pure Java statecharts. This has been going on for many years (a lot of XML and old templates, without DI integration), but if you really want to separate the state machine implementation from the inputs and outputs, there might be some inspiration.