statemachine

Statemachine library with sync and async way of handling state events, Also have feature of expirying an state. ------------------------------------------ Project version: 1.0.0 Gradle version: 4.4 ------------------------------------------

License

License

GroupId

GroupId

com.github.priytam
ArtifactId

ArtifactId

statemachine
Last Version

Last Version

1.0.0
Release Date

Release Date

Type

Type

jar
Description

Description

statemachine
Statemachine library with sync and async way of handling state events, Also have feature of expirying an state. ------------------------------------------ Project version: 1.0.0 Gradle version: 4.4 ------------------------------------------
Project URL

Project URL

https://github.com/Priytam/statemachine
Source Code Management

Source Code Management

https://github.com/Priytam/statemachine.git

Download statemachine

How to add to project

<!-- https://jarcasting.com/artifacts/com.github.priytam/statemachine/ -->
<dependency>
    <groupId>com.github.priytam</groupId>
    <artifactId>statemachine</artifactId>
    <version>1.0.0</version>
</dependency>
// https://jarcasting.com/artifacts/com.github.priytam/statemachine/
implementation 'com.github.priytam:statemachine:1.0.0'
// https://jarcasting.com/artifacts/com.github.priytam/statemachine/
implementation ("com.github.priytam:statemachine:1.0.0")
'com.github.priytam:statemachine:jar:1.0.0'
<dependency org="com.github.priytam" name="statemachine" rev="1.0.0">
  <artifact name="statemachine" type="jar" />
</dependency>
@Grapes(
@Grab(group='com.github.priytam', module='statemachine', version='1.0.0')
)
libraryDependencies += "com.github.priytam" % "statemachine" % "1.0.0"
[com.github.priytam/statemachine "1.0.0"]

Dependencies

compile (4)

Group / Artifact Type Version
com.github.priytam : stopwatch jar 1.0.0
concurrent : concurrent jar 1.3.4
com.google.guava : guava jar 28.1-jre
log4j : log4j jar 1.2.16

test (1)

Group / Artifact Type Version
junit : junit jar 4.12

Project Modules

There are no modules declared in this project.

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

  • StateMachine
  • State(s) in a StateMachine (for example Opened, Closed and Locked in below diagram)
  • Events on StateMachine/State (for example open, close, lock and unlock in below diagram)
  • Transition of one state to another (for example close event on Opened state will transit system to Closed State)

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
  • gets called at entry of any state
  • context is the object passed while creating stateMachine
  • fromState the state from which system is transiting and will be null if it is beginning state

onExit

    public void onExit(Object context, State toState) throws StateException
  • gets called at exist of any state
  • context is the object passed while creating stateMachine
  • toState is the state to which system is transiting and will be null if it is beginning state

onEvent

    protected State onEvent(Object context, Object theEvent) throws StateException {
  • gets called every time postEvent is called on stateMachine
  • context is the object passed while creating stateMachine
  • theEvent is the event which is posted on the state machine
  • if a new State is returned from this method and system will transit to that state
  • if null or same State is returned system will stay in same state

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*

  • save state's number to db _ restore from momento

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

Versions

Version
1.0.0