The FsmFactory is inspired by the NModel project from Microsoft Research, https://nmodel.codeplex.com/, which later developed into the SpecExplorer. FsmFactory does only one thing: It creates a finite state chart from a class using the @Transition annotation.
The concept is:
- a class implements
Serializable. - Annotates methods that represents transitions using the
@Transition. A transition is when some state changes. Some properties are changing values. - In a stateful system, transitions are only allowed under certain circumstances. This is done by accompanying guard methods to the transition method. They share the same method name, except that the guard has a the word
Onprefixed to the name.
Consider following code: (See also book Model-based Software Testing and Analysis with C#, the example with Client and Server. http://www.cambridge.org/se/academic/subjects/computer-science/software-engineering-and-development/model-based-software-testing-and-analysis-c?format=PB)
FsmFactory factory = new FsmFactory(); Model model = factory.create(new ClientServer()); System.out.println(DotFileFactory.createDot(model));
and where the ClientServer class looks like:
public class ClientServer implements Serializable { public enum Socket { None, Created, Bound, Listening, Connecting, Connected, Disconnected, Closed } public enum Phase { Send, ServerReceive, ClientReceive } // Control state public Socket serverSocket = Socket.None; public Socket clientSocket = Socket.None; public Phase phase = Phase.Send; // Server enabling conditions and actions public boolean OnServerSocket() { return (serverSocket == Socket.None); } @Transition public void ServerSocket() { serverSocket = Socket.Created; } public boolean OnServerBind() { return (serverSocket == Socket.Created); } @Transition public void ServerBind() { serverSocket = Socket.Bound; } public boolean OnServerListen() { return (serverSocket == Socket.Bound); } @Transition public void ServerListen() { serverSocket = Socket.Listening; } public boolean OnServerAccept() { return (serverSocket == Socket.Listening && clientSocket == Socket.Connecting); } @Transition public void ServerAccept() { serverSocket = Socket.Connected; clientSocket = Socket.Connected; } public boolean OnServerReceive() { return (serverSocket == Socket.Connected && phase == Phase.ServerReceive); } // No parameter needed here, client always sends same thing @Transition public void ServerReceive() { phase = Phase.Send; } public boolean OnServerSend() { return (serverSocket == Socket.Connected && phase == Phase.Send && clientSocket == Socket.Connected); } // Parameter here, server can send different temperatures @Transition public void ServerSend() { phase = Phase.ClientReceive; } public boolean OnServerCloseConnection() { return (serverSocket == Socket.Connected); } @Transition public void ServerCloseConnection() { serverSocket = Socket.Disconnected; } // Prevent Client crashing - does sending to closed partner crash? public boolean OnServerClose() { return (serverSocket != Socket.None // && serverSocket != Socket.Listening && serverSocket != Socket.Connected && serverSocket != Socket.Closed); } @Transition public void ServerClose() { serverSocket = Socket.Closed; } // Client enabling conditions and actions public boolean OnClientSocket() { return (clientSocket == Socket.None); } @Transition public void ClientSocket() { clientSocket = Socket.Created; } public boolean OnClientConnect() { return (clientSocket == Socket.Created && serverSocket == Socket.Listening); } @Transition public void ClientConnect() { clientSocket = Socket.Connecting; } public boolean OnClientSend() { return (clientSocket == Socket.Connected && phase == Phase.Send); } // No parameter needed here, client always sends the same thing @Transition public void ClientSend() { phase = Phase.ServerReceive; } public boolean OnClientReceive() { return (clientSocket == Socket.Connected && phase == Phase.ClientReceive); } // Return value needed here, server sends different values @Transition public void ClientReceive() { phase = Phase.Send; } public boolean OnClientClose() { return (clientSocket == Socket.Connected && phase == Phase.Send); } @Transition public void ClientClose() { clientSocket = Socket.Closed; } }
The FsmFactory will create a state chart, when written and visialized by graphviz and dot, will look like:
(using dot -Tsvg clientserver.dot > login.svg)
