statemachine

State machine management system, provide easy implementation of complex state flow of a system


Project maintained by Priytam Hosted on GitHub Pages — Theme by mattgraham

State Machine Management System (SMMS)

SMMS provides state management in simple and effective way, has capability of handling events synchronously and asynchronously both.

GitHub license Build Status Maven Central

HitCount Open Source Love svg2 Maintenance

Purpose

To write a state machine system in easy and effective way

If your system requires managing complex states this tool as your life saver

Keep below terms in mind while reading this doc

Example state diagram

Table of Contents

  1. Creating state machine
  2. Posting an event
  3. Initial state
  4. Creating states
  5. Persisting state machine
  6. Example

Creating state machine

There are two types of state machine

  1. SyncStateMachine (Sync processing of an event to it)
  2. StateMachine (Async processing of an event to it)

Creating async state machine

     new StateMachine(new ContextObject(), 10, 3);

arguments are

  1. A context that will be passed on each life cycle of a state
  2. Max number of pending events
  3. Thread pool

Creating sync state machine

     new SyncStateMachine(new ContextObject());

arguments are

  1. A context that will be passed on each life cycle of a state

Back to top

Posting an event

An event can be posted to stateMachine, postEvent on stateMachine calls current state’s onEvent method. On async StateMachine’s postEvent call return immediately and perform state’s onEvent method on a separate thread but on sync StateMachine it returns when state’s onEvent completes, there is also postEventAndWait(event, waitTimeInMillis) method on async stateMachine that waits for the given time and throws exception if time expires.

   stateMachine.postEvent(event);
   stateMachine.postEventAndWait(event, timeInMillis);

an event is a subClass of Object class and will be passed as argument on state’s onEvent method

Back to top

Initial(begin) state

Initial state of a state machine (beginning state)

    stateMachine.initState(new BeginState());

where BeginState is subClass of State class

Back to top

Creating states

To create a state extends State class, it primarily asks to implement onEntry, onEvent and onExit

onEntry

    public void onEntry(Object context, State fromState) throws StateException

onExit

    public void onExit(Object context, State toState) throws StateException

onEvent

    protected State onEvent(Object context, Object theEvent) throws StateException {

other functionalities can be overridden

  1. void doConstantly(Object context)
  2. Object onRequest(Object context, Object theRequest)
  3. State completeHandleEvent(Object context, Object theEvent)

Back to top

Persisting State Machine

*todo: cover detailed explanation**

Back to top

Example

Let’s write a system which manage states as below diagram Example state diagram

Complete implementation can be found here

States

Click to see state complete implementation

In above diagram system has 3 states (Opened, Closed and Locked)

Opened State implementation

if CloseEvent is posted on this state then state will be changed to Closed**

public class OpenedState extends AbstractState {
    private static final Logger log = Logger.getLogger(OpenedState.class);

    public OpenedState(int iType) {
        super(iType);
    }

    @Override
    public void onEntry(Object context, State fromState) throws StateException {
        log.info("entering in Opened state");
    }

    @Override
    protected State onEvent(Object context, Object theEvent) throws StateException {
        if (theEvent instanceof CloseEvent) {
            return StateFactory.getInstance().getState(DoorStates.CLOSED);
        }
        return null;
    }

    @Override
    public void onExit(Object context, State toState) throws StateException {
        log.info("exiting in Opened state");
    }
}

Closed State implementation

if theEvent is of type LockEvent change state to Locked similarly for OpenEvent.

public class ClosedState extends AbstractState {
    private static final Logger log = Logger.getLogger(ClosedState.class);

    public ClosedState(int iType) {
        super(iType);
    }

    @Override
    public void onEntry(Object context, State fromState) throws StateException {
        log.info("entering in Closed state");
    }

    @Override
    protected State onEvent(Object context, Object theEvent) throws StateException {
        if (theEvent instanceof LockEvent) {
            return StateFactory.getInstance().getState(DoorStates.LOCKED);
        }
        if (theEvent instanceof OpenEvent) {
            return StateFactory.getInstance().getState(DoorStates.OPENED);
        }
        return null;
    }

    @Override
    public void onExit(Object context, State toState) throws StateException {
        log.info("exiting in Locked state");
    }
}

Locked State implementation

if theEvent is of type UnlockEvent change state to Closed**

public class LockedState extends AbstractState {
    private static final Logger log = Logger.getLogger(LockedState.class);

    public LockedState(int iType) {
        super(iType);
    }

    @Override
    public void onEntry(Object context, State fromState) throws StateException {
        log.info("entering in Locked state");

    }

    @Override
    protected State onEvent(Object context, Object theEvent) throws StateException {
        if (theEvent instanceof UnlockEvent) {
            return StateFactory.getInstance().getState(DoorStates.CLOSED);
        }
        return null;
    }

    @Override
    public void onExit(Object context, State toState) throws StateException {
        log.info("exiting in Locked state");
    }
}

Events

There are four events can be performed on system (Open, Close, Lock and Unlock)

Click to see event classes

Create an empty class per events for identifying event like below State Events

State Handler Post an event to change the state

 public class StateHandler {
     public static final int MAX_EVENTS = 40;
     public static final int THREAD_POOL_SIZE = 3;
     private StateMachine stateMachine;
 
     public boolean init() {
         stateMachine = new StateMachine(new DummyLocker(), MAX_EVENTS, THREAD_POOL_SIZE);
         stateMachine.initState(StateFactory.getInstance().getState(DoorStates.OPENED));
         return true;
     }
 
     public boolean open() {
         //return stateMachine.postEvent(new OpenEvent());
         try {
             return stateMachine.postEventAndWait(new OpenEvent(), 2000);
         } catch (Throwable e) {
             System.out.println(e.getMessage());
         }
         return false;
     }
 
     public boolean close() {
         //return stateMachine.postEvent(new CloseEvent());
         try {
             return stateMachine.postEventAndWait(new CloseEvent(), 2000);
         } catch (Throwable e) {
             System.out.println(e.getMessage());
         }
         return false;
     }
 
     public boolean lock() {
         //return stateMachine.postEvent(new LockEvent());
         try {
             return stateMachine.postEventAndWait(new LockEvent(), 2000);
         } catch (Throwable e) {
             System.out.println(e.getMessage());
         }
         return false;
     }
 
     public boolean unlock() {
         //return stateMachine.postEvent(new UnlockEvent());
         try {
             return stateMachine.postEventAndWait(new UnlockEvent(), 2000);
         } catch (Throwable e) {
             System.out.println(e.getMessage());
         }
         return false;
     }
 
     public String getState() {
         return DoorStates.getState(stateMachine.getState().getStateType());
     }
 
     public void shutDown() {
         stateMachine.shutDown();
     }
 
     public static class DummyLocker {
 
     }
 }

Example of stateHandler

public class DoorStateTest {

    static {
        BasicConfigurator.configure();
        Logger.getRootLogger().setLevel(Level.INFO);
    }
    public static void main(String[] args) {

        // begin
        StateHandler stateHandler = new StateHandler();
        stateHandler.init();
        System.out.println("Initially locker was  -> " +  stateHandler.getState());

        // try closing locker
        stateHandler.close();
        System.out.println("Locker is  -> " +  stateHandler.getState());


        // try opening again
        stateHandler.open();
        System.out.println("Locker is  -> " +  stateHandler.getState());

        // try locking on open nothing will happen
        stateHandler.lock();
        System.out.println("Locker is  -> " +  stateHandler.getState());


        // try close and lock
        stateHandler.close();
        stateHandler.lock();
        System.out.println("Locker is  -> " +  stateHandler.getState());

        stateHandler.unlock();
        System.out.println("Locker is  -> " +  stateHandler.getState());
        stateHandler.shutDown();
    }
}

Output is

State Events