Skip to content

Commit

Permalink
✨ feat(statemachine): add current state fetcher for event verificatio…
Browse files Browse the repository at this point in the history
…n and firing in StateMachine implementation

Change-Id: Ifcfdaf68eae57a902ee7fff0d5e341085c45bf8b
  • Loading branch information
Ivan97 committed Dec 9, 2024
1 parent 09ce216 commit 987c49a
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.alibaba.cola.statemachine;

/**
* CurrentStateFetcher is used to fetch the current state from the context
*
* @author Yanchi Zhang
* @date 2024-12-09 11:21 AM
*/
public interface CurrentStateFetcher<S, C> {

/**
* Fetch the current state from the context
*
* @param context the context
* @return the current state
*/
S currentState(C context);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,21 @@ public interface StateMachine<S, E, C> extends Visitable{

/**
* Verify if an event {@code E} can be fired from current state {@code S}
*
* @param sourceStateId
* @param event
* @return
*/
boolean verify(S sourceStateId,E event);
boolean verify(S sourceStateId, E event);

/**
* Verify if an event {@code E} can be fired from current state {@code S}
* The source state will be fetched by {@link CurrentStateFetcher}
*
* @param event
* @return
*/
boolean verify(E event);

/**
* Send an event {@code E} to the state machine.
Expand All @@ -32,8 +42,20 @@ public interface StateMachine<S, E, C> extends Visitable{
*/
S fireEvent(S sourceState, E event, C ctx);

/**
* Send an event {@code E} to the state machine.
* The source state will be fetched by {@link CurrentStateFetcher}
*
* @param event the event to send
* @param ctx the user defined context
* @return the target state
*/
S fireEvent(E event, C ctx);

List<S> fireParallelEvent(S sourceState, E event, C ctx);

List<S> fireParallelEvent(E event, C ctx);

/**
* MachineId is the identifier for a State Machine
* @return
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.alibaba.cola.statemachine.builder;

import com.alibaba.cola.statemachine.CurrentStateFetcher;
import com.alibaba.cola.statemachine.StateMachine;

/**
Expand Down Expand Up @@ -43,6 +44,13 @@ public interface StateMachineBuilder<S, E, C> {
*/
void setFailCallback(FailCallback<S, E, C> callback);

/**
* Set up current state fetcher
*
* @param fetcher the current state fetcher
*/
void setCurrentStateFetcher(CurrentStateFetcher<S, C> fetcher);

StateMachine<S, E, C> build(String machineId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.alibaba.cola.statemachine.CurrentStateFetcher;
import com.alibaba.cola.statemachine.State;
import com.alibaba.cola.statemachine.StateMachine;
import com.alibaba.cola.statemachine.StateMachineFactory;
Expand All @@ -23,6 +24,7 @@ public class StateMachineBuilderImpl<S, E, C> implements StateMachineBuilder<S,
private final Map<S, State<S, E, C>> stateMap = new ConcurrentHashMap<>();
private final StateMachineImpl<S, E, C> stateMachine = new StateMachineImpl<>(stateMap);
private FailCallback<S, E, C> failCallback = new NumbFailCallback<>();
private CurrentStateFetcher<S, C> currentStateFetcher;

@Override
public ExternalTransitionBuilder<S, E, C> externalTransition() {
Expand All @@ -49,11 +51,17 @@ public void setFailCallback(FailCallback<S, E, C> callback) {
this.failCallback = callback;
}

@Override
public void setCurrentStateFetcher(CurrentStateFetcher<S, C> fetcher) {
this.currentStateFetcher = fetcher;
}

@Override
public StateMachine<S, E, C> build(String machineId) {
stateMachine.setMachineId(machineId);
stateMachine.setReady(true);
stateMachine.setFailCallback(failCallback);
stateMachine.setCurrentStateFetcher(currentStateFetcher);
StateMachineFactory.register(stateMachine);
return stateMachine;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@
import java.util.List;
import java.util.Map;

import com.alibaba.cola.statemachine.State;
import com.alibaba.cola.statemachine.StateMachine;
import com.alibaba.cola.statemachine.Transition;
import com.alibaba.cola.statemachine.Visitor;
import com.alibaba.cola.statemachine.*;
import com.alibaba.cola.statemachine.builder.FailCallback;
import com.alibaba.cola.statemachine.exception.TransitionFailException;

/**
* For performance consideration,
Expand All @@ -30,6 +28,8 @@ public class StateMachineImpl<S, E, C> implements StateMachine<S, E, C> {

private FailCallback<S, E, C> failCallback;

private CurrentStateFetcher<S,C> currentStateFetcher;

public StateMachineImpl(Map<S, State<S, E, C>> stateMap) {
this.stateMap = stateMap;
}
Expand All @@ -45,6 +45,17 @@ public boolean verify(S sourceStateId, E event) {
return transitions != null && transitions.size() != 0;
}

@Override
public boolean verify(E event) {
isReady();
if (this.currentStateFetcher != null) {
S currentState = this.currentStateFetcher.currentState(null);
return verify(currentState, event);
} else {
throw new TransitionFailException("currentStateFetcher not set");
}
}

@Override
public S fireEvent(S sourceStateId, E event, C ctx) {
isReady();
Expand All @@ -58,6 +69,18 @@ public S fireEvent(S sourceStateId, E event, C ctx) {

return transition.transit(ctx, false).getId();
}

@Override
public S fireEvent(E event, C ctx) {
isReady();
if (this.currentStateFetcher != null) {
S currentState = this.currentStateFetcher.currentState(ctx);
return fireEvent(currentState, event, ctx);
} else {
throw new TransitionFailException("currentStateFetcher not set");
}
}

@Override
public List<S> fireParallelEvent(S sourceState, E event, C context) {
isReady();
Expand All @@ -76,6 +99,17 @@ public List<S> fireParallelEvent(S sourceState, E event, C context) {
return result;
}

@Override
public List<S> fireParallelEvent(E event, C ctx) {
isReady();
if (this.currentStateFetcher != null) {
S currentState = this.currentStateFetcher.currentState(ctx);
return fireParallelEvent(currentState, event, ctx);
} else {
throw new TransitionFailException("currentStateFetcher not set");
}
}

private Transition<S, E, C> routeTransition(S sourceStateId, E event, C ctx) {
State sourceState = getState(sourceStateId);

Expand Down Expand Up @@ -171,4 +205,8 @@ public void setReady(boolean ready) {
public void setFailCallback(FailCallback<S, E, C> failCallback) {
this.failCallback = failCallback;
}

public void setCurrentStateFetcher(CurrentStateFetcher<S,C> currentStateFetcher) {
this.currentStateFetcher = currentStateFetcher;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,21 @@ public void testVerify() {
Assertions.assertFalse(stateMachine.verify(States.STATE1, Events.EVENT2));
}

@Test
public void testCurrentStatusFetcher() {
StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create();
builder.externalTransition()
.from(States.STATE1)
.to(States.STATE2)
.on(Events.EVENT1)
.when(checkCondition())
.perform(doAction());
builder.setCurrentStateFetcher((ctx) -> States.STATE1);
StateMachine<States, Events, Context> stateMachine = builder.build(MACHINE_ID + "-testCurrentStatusFetcher");
Assertions.assertTrue(stateMachine.verify(Events.EVENT1));
Assertions.assertFalse(stateMachine.verify(Events.EVENT2));
}

@Test
public void testExternalTransitionsNormal() {
StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create();
Expand Down

0 comments on commit 987c49a

Please sign in to comment.