Java Finite State Machine (FSM) Manager
JFSMμ μλ° κΈ°λ°μ κ²½λ μν λ¨Έμ (Finite State Machine) νλ μμν¬μ λλ€. κ°κ²°ν APIλ‘ μν μ²μ΄(transition)λ₯Ό μ μνκ³ , μ½λ°±κ³Ό 쑰건(EventCondition), μ§μ° μ€ν λ° μ¬μλ(Retry) κ°μ μ€λ¬΄ μΉν κΈ°λ₯μ μ 곡ν©λλ€. λ©ν° μ€λ λ κΈ°λ°μ νμ€ν¬ μ€νκΈ°(StateTaskManager)λ₯Ό ν΅ν΄ λΉλκΈ°/μ§μ° μ΄λ²€νΈ μ²λ¦¬λ μ½κ² ꡬμ±ν μ μμ΅λλ€.
- μ΅μ μμ‘΄μ±, μμ μλ°λ‘ λμ
- κ°λ¨ν DSL μ€νμΌμ μν μ²μ΄ λ±λ‘ API
- μ²μ΄ μ±κ³΅/μ€ν¨ μ½λ°±, λ€μ μ΄λ²€νΈ 체μ΄λ(nextEvent)
- μ§μ°(delay) λ° μ¬μλ(nextEventRetryCount) μ§μ
- μ‘°κ±΄λΆ μ€ν(EventCondition) λ° μ€μΌμ€λ§ μ§μ
- λ©ν° μ€λ λ νμ€ν¬ μ²λ¦¬(StateTaskManager)μ μμ ν λμμ± μ μ΄
- μκ°
- λΉ λ₯Έ μμ
- ν΅μ¬ κ°λ
- μ¬μ© λ°©λ²
- κ³ κΈ μ£Όμ : μ§μ°/μ¬μλ, 쑰건, μ€μΌμ€λ§
- μμ : ATM λ°λͺ¨
- ν μ€νΈ μ€ν
- Maven/λΉλ
- FAQ
- λΌμ΄μ μ€
JFSMμ λ€μκ³Ό κ°μ μν©μ μ μ©ν©λλ€.
- μ¬μ©μ/μ 무 νλ¦μ λͺ νν μνλ‘ λͺ¨λΈλ§νκ³ μΆμ κ²½μ°
- μ΄λ²€νΈ κΈ°λ°μΌλ‘ λ¨κ³λ³ μ²λ¦¬μ μλ¬ λ³΅κ΅¬/μ¬μλλ₯Ό νκ³ μΆμ κ²½μ°
- λ©ν° μ€λ λμμ λ€μμ μν κ°μ²΄λ₯Ό μμ νκ² κ΄λ¦¬νκ³ μΆμ κ²½μ°
κ΅¬μ± μμλ λ€μκ³Ό κ°μ΅λλ€.
StateManager: FSM μ 체λ₯Ό κ΄λ¦¬νλ μμ λ§€λμ . μ€λ λ ν ν¬κΈ°, νΈλ€λ¬/μ λ λ±λ‘/μμ λ± μ 곡StateHandler: νΉμ λλ©μΈ(μ ν)μ μ΄λ²€νΈμ μ²μ΄λ₯Ό κ΄λ¦¬StateEventManager: μ²μ΄ ν μ΄λΈκ³Ό μ€ν λ‘μ§μ λ΄λΉStateUnit: μ€μ λ‘ μνλ₯Ό κ°κ³ μ΄λ²€νΈλ₯Ό μ μ©λ°λ κ°μ²΄(μΈμ /μ£Όλ¬Έ/μ₯λΉ λ±)EventCondition: μ²μ΄ μ€ν μ 쑰건 κ²μ¬ μΈν°νμ΄μ€CallBack: μ²μ΄ μ±κ³΅/μ€ν¨ μμ νμ²λ¦¬ μ½λ°± μΈν°νμ΄μ€RetryManager: μ¬μλ μ μ± λ° μν κ΄λ¦¬ μ νΈ
UML κ°μ(μμ):
- FSM:
JFSM/src/main/resources/uml/fsm_uml.puml - ATM λ°λͺ¨:
JFSM/src/test/java/atm/uml/atm_state.puml
νλ μλΉμ€λ μ¬μ©μ νλ¦, μ£Όλ¬ΈΒ·κ²°μ , κΈ°κΈ° μ μ΄(μ: ATM) λ± λ¨κ³μ λ‘μ§μ΄ λ§μ΅λλ€. μ΄ νλ¦μ if-else/switch, νμ΄λ¨Έ, μ€λ λ, ν λ‘μ§μΌλ‘ μ¦ν₯ ꡬννλ©΄ μ½λκ° λΉ λ₯΄κ² 볡μ‘ν΄μ§κ³ κ²°ν©λκ° λμμ§λλ€. FSM(Finite State Machine)μ μμ€ν μ κ°λ₯ν μνμ μ΄λ²€νΈ, μ μ΄λ₯Ό λͺ νν μ μν΄ λ€μκ³Ό κ°μ μ΄μ μ μ 곡ν©λλ€.
- 볡μ‘λ κ΄λ¦¬μ κ°λ
μ± ν₯μ
- μνμ μ μ΄(transition)λ₯Ό νλ‘ κ΄λ¦¬νλ―λ‘ νλ¦μ΄ λͺ νν©λλ€.
- "μ΄λ€ μ΄λ²€νΈκ° μ΄λ€ μνμμ μ ν¨νκ°"λ₯Ό μ½λ λ 벨μμ κ°μ ν μ μμ΅λλ€.
- μ λ’°μ± μλ μ€λ₯ μ²λ¦¬μ ν볡λ ₯
- μ±κ³΅/μ€ν¨ μ½λ°±μΌλ‘ νμ²λ¦¬λ₯Ό μΌκ΄λκ² μνν©λλ€.
nextEvent+delay+nextEventRetryCountλ‘ μ€ν¨ ν μ¬μλΒ·λ°±μ€νΒ·λκΈ° κ°μ ν볡 μ λ΅μ μ μΈμ μΌλ‘ ꡬμ±ν μ μμ΅λλ€.
- λμμ± μ μ΄μ λΉλκΈ° μ²λ¦¬μ λ¨μν
StateTaskManagerκ° μ§μ° μ€νκ³Ό 체μ΄λμ μ€λ λ νμμ μμ νκ² μ²λ¦¬ν©λλ€.- μ΄λ²€νΈ 쑰건(
EventCondition)μΌλ‘ κ²½μ 쑰건μ΄λ μ ν¨μ± μ κ²μ¬λ₯Ό μ€μνν©λλ€.
- ν
μ€νΈ μ©μ΄μ±κ³Ό μΆμ κ°λ₯μ±
- κ° μ μ΄μ μ½λ°±μ΄ λ 립 λͺ¨λμ΄λ―λ‘ λ¨μ ν μ€νΈκ° μ½μ΅λλ€.
- μν μ΄λ ₯(Prev/Cur)μΌλ‘ μΌμ΄μ€ μ¬νκ³Ό μ₯μ λΆμμ΄ μ©μ΄ν©λλ€.
- λλ©μΈ μ ν©μ±κ³Ό νμ₯μ±
- λλ©μΈ μΈμ΄(μ: READY β CARD_READ β PIN_ENTRY)λ₯Ό κ·Έλλ‘ μ½λμ λ°μν΄ μμ¬μν΅μ΄ μ¬μμ§λλ€.
- μλ‘μ΄ μν/μ΄λ²€νΈ μΆκ°κ° κΈ°μ‘΄ μ½λμ μ΅μνμ μν₯μΌλ‘ κ°λ₯ν©λλ€.
JFSMμ΄ μ 곡νλ κΈ°λ₯μ FSM λμ ν¨κ³Όλ₯Ό μ€λ¬΄μ λ§κ² κ°νν©λλ€.
- μ μΈμ API:
StateHandler.addState(...)λ‘ μ μ΄, μ½λ°±, λ€μ μ΄λ²€νΈ, μ§μ°, μ¬μλκΉμ§ ν λ²μ μ μ - μ‘°κ±΄λΆ μ€ν:
EventConditionμΌλ‘ μ¬μ κ²μ¦ λ‘μ§μ νλ¬κ·ΈμΈμ²λΌ μΆκ° - λΉλκΈ°Β·μ€μΌμ€λ§: λ΄λΆ μ€μΌμ€λ¬/μ€λ λν κΈ°λ°μΌλ‘ λ€μ μ΄λ²€νΈ μλ μ²λ¦¬
- μ¬μλ μ λ΅:
RetryManagerλ₯Ό ν΅ν΄ λ¨μ λ°λ³΅λΆν° μ ν νμΒ·μ§μ° κ°κ²©κΉμ§ μ μ°νκ² κ΅¬μ±
μΈμ FSMμ μ°μ§ μμλ λλκ°?
- μ μ΄κ° 1~2κ° μμ€μ μμ£Ό λ¨μν νλ‘μ°
- μν κ°λ μ΄ κ±°μ μκ³ μ μ°¨ν μ§μ λ‘μ§μΌλ‘ μΆ©λΆν κ²½μ° μ΄λ° κ²½μ°μλ μ€νλ € FSMμ΄ κ³Όμ€κ³κ° λ μ μμ΅λλ€. κ·Έλ¬λ μν/μ΄λ²€νΈκ° λμ΄λκ³ λΉλκΈ°Β·μ§μ°Β·μ¬μλκ° μ½νλ©΄ FSMμ΄ μ₯κΈ° μ μ§λ³΄μ κ΄μ μμ μ 리ν©λλ€.
μλλ μ΅μ κ΅¬μ± μμμ λλ€.
import com.fsm.StateManager;
import com.fsm.module.StateHandler;
import com.fsm.event.base.CallBack;
// 1) FSM λ§€λμ μμ± (νμ€ν¬ μ€λ λ μ΅λ κ°μ μ§μ )
StateManager fsm = new StateManager(4);
// 2) νΈλ€λ¬ λ±λ‘ (λλ©μΈ κ·Έλ£Ήλͺ
)
fsm.addStateHandler("ATM");
StateHandler handler = fsm.getStateHandler("ATM");
// 3) μν μ λ λ±λ‘ (μ€μ μνλ₯Ό κ°μ§ κ°μ²΄)
fsm.addStateUnit("acct-001", "ATM", "INIT", new Object());
// 4) μ²μ΄ μ μ (event, from, to, μ±κ³΅μ½λ°±, μ€ν¨μ½λ°±, λ€μμ΄λ²€νΈ, μ§μ°(ms), μ¬μλ)
CallBack ok = (unit, params) -> {
System.out.println("on success: " + unit.getName());
return true;
};
CallBack fail = (unit, params) -> {
System.out.println("on fail: " + unit.getName());
return true;
};
handler.addState(
"INPUT_PIN", // event
"INIT", // from
"PIN_ENTERED", // to
ok, // success callback
fail, // fail callback
"SELECT_MENU", // next event (체μ΄λ)
0, // delay (ms)
0 // retry count
);
// 5) μ΄λ²€νΈ μ€ν
var unit = fsm.getStateUnit("acct-001");
String nextState = handler.fire("INPUT_PIN", unit);
System.out.println("Next State: " + nextState);
// μ’
λ£ μ
fsm.stop();- StateManager
- FSM μ 체 μλͺ κ³Ό 리μμ€λ₯Ό κ΄λ¦¬ν©λλ€.
- νΈλ€λ¬(
StateHandler)μ μ λ(StateUnit)μ λ±λ‘/μμ ν©λλ€. - λ΄λΆμ
StateTaskManagerλ₯Ό 보μ νλ©°, λΉλκΈ° μμ μ μ€νν©λλ€.
- StateHandler
- μ΄λ²€νΈ μ²μ΄ μ μλ₯Ό λ±λ‘νκ³ ,
fire/handleλ‘ μ€νν©λλ€. - λμΌ νΈλ€λ¬ λ΄ μ΄λ²€νΈ κ° μ²΄μ΄λ(nextEvent)κ³Ό μ§μ°μ μ²λ¦¬ν μ μμ΅λλ€.
- μ΄λ²€νΈ μ²μ΄ μ μλ₯Ό λ±λ‘νκ³ ,
- StateEventManager
- μ²μ΄ ν μ΄λΈμ μ μ₯νκ³ μ€ν λ‘μ§μ μνν©λλ€.
- μ±κ³΅/μ€ν¨ μ½λ°±, μ¬μλ, 쑰건 κ²μ¬ λ±μ μ€μΌμ€νΈλ μ΄μ ν©λλ€.
- StateUnit
- μ΄λ¦, νμ¬ μν, λΆκ° λ°μ΄ν°(payload)λ₯Ό κ°λ FSMμ λμ κ°μ²΄μ λλ€.
- EventCondition
- νΉμ μ΄λ²€νΈ μ€ν μ 쑰건μ κ²μ¦ν©λλ€.
- μ€ν¨/μ€λ₯ μν© μ λ€μ μ²μ΄λ μ½λ°± λμμ μν₯μ μ€ μ μμ΅λλ€.
- CallBack
- μ²μ΄ μ±κ³΅/μ€ν¨ μμ μ νμ²λ¦¬λ₯Ό ꡬνν©λλ€.
- Retry(μ¬μλ)
nextEventRetryCountλ‘ λ€μ μ΄λ²€νΈ 체μ΄λ μ μ¬μλ νμλ₯Ό μ§μ ν©λλ€.
StateManager fsm = new StateManager(4); // μ€λ λ μ΅λ 4κ°fsm.addStateHandler("ORDER");
StateHandler handler = fsm.getStateHandler("ORDER");fsm.addStateUnit("order-1001", "ORDER", "NEW", payload);
fsm.getStateUnit("order-1001");
fsm.removeStateUnit("order-1001");μ€λ²λ‘λ λ κ°μ§κ° μ 곡λ©λλ€.
- λ¨μΌ from μνμμ toλ‘ μ²μ΄
- λ€μ€ from μν μ§ν©μμ toλ‘ μ²μ΄
boolean added = handler.addState(
"PAY", // event
"NEW", // fromState
"PAID", // toState
successCb, failCb,
null, // nextEvent μμΌλ©΄ null
0, // delay
0, // retry count
new Object[]{/* next params */}
);HashSet<String> from = new HashSet<>(List.of("NEW", "RETRY"));
boolean added = handler.addState(
"PAY", // event
from, // fromStateSet
"PAID", // toState
successCb, failCb,
"SHIP", // nextEvent
1000, // delay 1s ν nextEvent
3, // μ΅λ 3ν μ¬μλ
"param1", 123
);String nextState = handler.fire("PAY", stateUnit);addStateμnextEvent,delayλ₯Ό μ§μ νλ©΄ μ±κ³΅ μ λ€μ μ΄λ²€νΈλ₯Ό μ§μ° ν μλ μ€νν©λλ€.nextEventRetryCountλ‘ μ€ν¨ μ μ¬μλ νμλ₯Ό μ μ΄ν μ μμ΅λλ€.
EventConditionμ νΈλ€λ¬μ λ±λ‘ν΄ μ΄λ²€νΈ μ€ν μ κ²μ¦μ μνν μ μμ΅λλ€.
handler.addEventCondition((event, unit) -> {
// μ€ν κ°λ₯ μ¬λΆ κ²μ¬ (true=ν΅κ³Ό)
return unit != null && unit.getIsAlive();
}, 0); // delay(ms) β νμ μ 쑰건λ μ§μ° νκ° κ°λ₯StateTaskManagerμ λ΄λΆ μ€μΌμ€λ¬κ° μ§μ°/체μ΄λ μμ μ λ°±κ·ΈλΌμ΄λμμ μ€νν©λλ€.StateManagerμμ±μμ νλΌλ―Έν°λ‘ μ€λ λ μ΅λ κ°μλ₯Ό μ€μ ν©λλ€.
- μ€ν¨ μ½λ°±μμ λ‘κΉ /μλ¦Ό/보μ λ‘μ§μ μνν μ μμ΅λλ€.
nextEventRetryCountμRetryManagerλ₯Ό ν΅ν΄ λ°λ³΅ μ€ν μ λ΅μ μ€κ³νμΈμ.
ν μ€νΈ μ½λμ ATM μν λ¨Έμ μμ κ° ν¬ν¨λμ΄ μμ΅λλ€.
- κ²½λ‘:
JFSM/src/test/java/atm - UML:
JFSM/src/test/java/atm/uml/atm_state.puml
μ€ν ν:
# Maven ν
μ€νΈ μ€ν
mvn -f JFSM/pom.xml test -Dtest=atm.BasicAtmStateTest# μ 체 ν
μ€νΈ
mvn -f JFSM/pom.xml test
# νΉμ ν
μ€νΈ ν΄λμ€
mvn -f JFSM/pom.xml -Dtest=TestMain testνλ‘μ νΈλ Maven κΈ°λ°μ
λλ€. λ‘컬 λͺ¨λλ‘ μ¬μ©ν κ²½μ° StateManager λ± APIλ₯Ό μ§μ import νμ¬ μ¬μ©νμΈμ. (λ³λμ μ€μ μ μ₯μ λ°°ν¬ μ λ³΄κ° μλ€λ©΄ μμ€ μμ‘΄ λλ λ‘컬 μ€μΉλ₯Ό μ΄μ©νμΈμ.)
# λΉλ
mvn -f JFSM/pom.xml clean package
# λ‘컬 μ€μΉ (λ€λ₯Έ νλ‘μ νΈμμ μ¬μ©)
mvn -f JFSM/pom.xml clean installμμ‘΄ μμ(pom.xml) β λ‘컬 μ€μΉ ν μ¬μ© μ:
<dependency>
<groupId>com.fsm</groupId>
<artifactId>JFSM</artifactId>
<version>1.0.0</version>
</dependency>- Q. μν μ²μ΄λ μ΄λμ μ μνλμ?
- A.
StateHandler.addState(...)λ₯Ό ν΅ν΄ μ μν©λλ€.
- A.
- Q. μ¬λ¬ from μνμμ κ°μ μ΄λ²€νΈλ‘ μ²μ΄ κ°λ₯ν κΉμ?
- A. κ°λ₯ν©λλ€.
HashSet<String>μΌλ‘ from μν μ§ν©μ λκΈ°λ©΄ λ©λλ€.
- A. κ°λ₯ν©λλ€.
- Q. λ€μ μ΄λ²€νΈλ₯Ό μλ μ€ννκ³ μΆμ΅λλ€.
- A.
nextEventμdelayλ₯Ό μ§μ νμΈμ. μ€ν¨ μnextEventRetryCountλ‘ μ¬μλ νμλ₯Ό μ€μ ν©λλ€.
- A.
- Q. μ€λ λ κ°μλ μ΄λ»κ² μ‘°μ νλμ?
- A.
new StateManager(threadMaxCount)λ‘ μμ± μ μ€μ ν©λλ€.
- A.
μ΄ νλ‘μ νΈλ LICENSE νμΌμ λ΄μ©μ λ°λ¦
λλ€.
