state machine test sandbox

This commit is contained in:
ialbert 2025-07-28 12:52:37 +03:00
parent 57bb118262
commit 4ed2aabf11
8 changed files with 580 additions and 141 deletions

View file

@ -1,120 +1,105 @@
package ru.spcex.clearing.session.teststate;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.StateMachineFactory;
import ru.spcex.clearing.session.stage.TaskType;
import ru.spcex.clearing.session.state.SsnEvent;
import ru.spcex.clearing.session.teststate.config.TestStateMachineConfig;
import ru.spcex.clearing.session.teststate.config.SmpState;
import ru.spcex.clearing.session.teststate.config.TestCurrStateMachineConfig;
import ru.spcex.clearing.session.teststate.config.TestSimpleStateMachineConfig;
import ru.spcex.clearing.session.teststate.config.TestSimpleStateMachineInterceptorConfig;
import ru.spcex.clearing.session.teststate.config.TestSimpleStateMachineRestoreConfig;
import ru.spcex.clearing.session.teststate.config.TestStateMachineExecutorsConfig;
@EnabledIfSystemProperty(named = "ru.spcex.run.manual.tests", matches = "true")
@SpringBootTest(classes = {
TestStateMachineConfig.class,
TestCurrStateMachineConfig.class,
TestSimpleStateMachineConfig.class,
TestSimpleStateMachineInterceptorConfig.class,
TestSimpleStateMachineRestoreConfig.class,
TestStateMachineExecutorsConfig.class
})
class StateMachineTest {
private final Logger log = LoggerFactory.getLogger(getClass());
@Autowired
private StateMachineFactory<TaskType, SsnEvent> stateMachineFactory;
@Qualifier("currTestMachine")
private StateMachineFactory<TaskType, SsnEvent> currStateMachineFactory;
private StateMachine<TaskType, SsnEvent> stateMachine;
@Autowired
@Qualifier("simpleTestMachineGuard")
private StateMachineFactory<SmpState, SsnEvent> smpStateMachineGuardFactory;
@Autowired
@Qualifier("simpleTestMachineInterceptor")
private StateMachineFactory<SmpState, SsnEvent> smpStateMachineInterceptorFactory;
@BeforeEach
public void setup() {
// Create a new state machine for each test
stateMachine = stateMachineFactory.getStateMachine();
@Autowired
@Qualifier("testRestoredStateMachine")
private StateMachine<SmpState, SsnEvent> restoredStateMachine;
@Test
void testCurrency() {
StateMachine<TaskType, SsnEvent> stateMachine = currStateMachineFactory.getStateMachine();
addInterceptor(stateMachine);
stateMachine.start();
log.debug("test started");
stateMachine.sendEvent(SsnEvent.SDF_57);
log.info("Middle SM in state: {}", stateMachine.getState().getId());;
stateMachine.sendEvent(SsnEvent.SDF_57);
stateMachine.sendEvent(SsnEvent.SDF_04);
sleepForNSec(5);
log.info("End SM in state: {}", stateMachine.getState().getId());;
}
@Test
void test() {
log.debug("test started");
// stateMachine
// .getStateMachineAccessor()
// .doWithRegion(function -> function.addStateMachineInterceptor(
// new StateMachineInterceptorAdapter<>() {
// @Override
// public Message<Event> preEvent(Message<Event> message, StateMachine<State, Event> stateMachine) {
// Event payload = message.getPayload();
// log.info("INTERCEPTOR preEvent catched event {}", payload);
// return super.preEvent(message, stateMachine);
// }
//
// @Override
// public void preStateChange(org.springframework.statemachine.state.State<State, Event> state, Message<Event> message, Transition<State, Event> transition, StateMachine<State, Event> stateMachine, StateMachine<State, Event> rootStateMachine) {
// log.info("INTERCEPTOR preStateChange catched");
// super.preStateChange(state, message, transition, stateMachine, rootStateMachine);
// }
//
// @Override
// public void postStateChange(org.springframework.statemachine.state.State<State, Event> state, Message<Event> message, Transition<State, Event> transition, StateMachine<State, Event> stateMachine, StateMachine<State, Event> rootStateMachine) {
// log.info("INTERCEPTOR postStateChange catched");
// super.postStateChange(state, message, transition, stateMachine, rootStateMachine);
// }
//
// @Override
// public StateContext<State, Event> preTransition(StateContext<State, Event> stateContext) {
// log.info("INTERCEPTOR preTransition catched");
// org.springframework.statemachine.state.State<State, Event> target = stateContext.getTarget();
// //if (target.getId().equals(State.continueRevise)) {
// // IllegalStateException ex = new IllegalStateException("pre transition interceptor exception!!!");
// // stateContext.getStateMachine().setStateMachineError(ex);
// // throw ex;
// //}
// return super.preTransition(stateContext);
// }
//
// @Override
// public StateContext<State, Event> postTransition(StateContext<State, Event> stateContext) {
// log.info("INTERCEPTOR postTransition catched");
// return super.postTransition(stateContext);
// }
//
// @Override
// public Exception stateMachineError(StateMachine<State, Event> stateMachine, Exception exception) {
// log.info("INTERCEPTOR error caught!!!");
// return exception;
// }
// }));
void testSmpGuard() {
StateMachine<SmpState, SsnEvent> stateMachine = smpStateMachineGuardFactory.getStateMachine();
addInterceptor(stateMachine);
stateMachine.start();
// stateMachine.sendEvent(SsnEvent.);
// try {
// Thread.sleep(5000);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// stateMachine.sendEvent(Event.SDF_01);
// stateMachine.sendEvent(Event.SDF_04);
// stateMachine.sendEvent(Event.SDF_57);
// try {
// Thread.sleep(10000);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// stateMachine.sendEvent(Event.continueRevise);
// log.info("'ve send a continueRevise");
// try {
// Thread.sleep(100010);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// log.info("FIRST STOP");
// stateMachine.stop();
// log.info("SECOND STOP");
// stateMachine.stop();
sleepForNSec(5);
log.debug("test started");
stateMachine.sendEvent(SsnEvent.SDF_57);
log.info("Middle SM in state: {}", stateMachine.getState().getId());;
stateMachine.sendEvent(SsnEvent.SDF_04);
sleepForNSec(5);
stateMachine.sendEvent(SsnEvent.CONTINUE);
sleepForNSec(5);
log.info("So. We end with SM in state: {}", stateMachine.getState().getId());;
log.info("End SM in state: {}", stateMachine.getState().getId());;
}
@Test
void testSmpInterceptor() {
StateMachine<SmpState, SsnEvent> stateMachine = smpStateMachineInterceptorFactory.getStateMachine();
addInterceptor(stateMachine);
stateMachine.start();
log.debug("test started");
sleepForNSec(10);
log.info("End SM in state: {}", stateMachine.getState().getId());;
}
@Test
void testRestoredSmp() {
addInterceptor(restoredStateMachine);
restoredStateMachine.start();
log.debug("test started");
sleepForNSec(10);
log.info("End SM in state: {}", restoredStateMachine.getState().getId());;
}
private static <S, E> void addInterceptor(StateMachine<S, E> sm) {
sm.getStateMachineAccessor()
.doWithAllRegions(
acs -> acs.addStateMachineInterceptor(new TestStateMachineInterceptorAdapter2<>())
);
}
public static void sleepForNSec(int i) {

View file

@ -0,0 +1,57 @@
package ru.spcex.clearing.session.teststate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.Message;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.state.PseudoStateKind;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.support.StateMachineInterceptorAdapter;
import org.springframework.statemachine.transition.Transition;
class TestStateMachineInterceptorAdapter2<S, E> extends StateMachineInterceptorAdapter<S, E> {
private static final Logger log = LoggerFactory.getLogger(TestStateMachineInterceptorAdapter2.class);
private static <S, E> String target(Transition<S, E> t) {
if (t == null) return null;
String source = t.getSource() != null ? String.valueOf(t.getSource().getId()) : "[unknown source]";
String target = t.getTarget() != null ? String.valueOf(t.getTarget().getId()) : "[unknown target]";
return "source %s, target %s".formatted(source, target);
}
@Override
public void postStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition, StateMachine<S, E> stateMachine, StateMachine<S, E> rootStateMachine) {
log.info("INTERCEPTOR postStateChange " + target(transition));
if (state != null
&& state.getId() != null
&& state.getPseudoState() != null
&& state.getPseudoState().getKind() != null
) {
PseudoStateKind kind = state.getPseudoState().getKind();
if (kind.equals(PseudoStateKind.END)) {
log.info("INTERCEPTOR postStateChange: {} is END state", state.getId());
}
}
}
@Override
public StateContext<S, E> preTransition(StateContext<S, E> stateContext) {
log.info("INTERCEPTOR preTransition " + target(stateContext.getTransition()));
return stateContext;
}
@Override
public void preStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition, StateMachine<S, E> stateMachine, StateMachine<S, E> rootStateMachine) {
log.info("INTERCEPTOR preStateChange " + target(transition));
}
@Override
public StateContext<S, E> postTransition(StateContext<S, E> stateContext) {
log.info("INTERCEPTOR postTransition " + target(stateContext.getTransition()));
return stateContext;
}
}

View file

@ -0,0 +1,6 @@
package ru.spcex.clearing.session.teststate.config;
public enum SmpEvent {
SDF_57,
SDF_04
}

View file

@ -0,0 +1,7 @@
package ru.spcex.clearing.session.teststate.config;
public enum SmpState {
Begin,
WaitForSdf,
Finish
}

View file

@ -20,16 +20,15 @@ import org.springframework.statemachine.listener.StateMachineListenerAdapter;
import org.springframework.statemachine.transition.Transition;
import ru.spcex.clearing.session.stage.TaskType;
import ru.spcex.clearing.session.state.SsnEvent;
import ru.spcex.clearing.session.state.action.SdfReceivedAction;
import ru.spcex.clearing.session.state.guard.PaymentsWereCreatedGuard;
import ru.spcex.clearing.session.state.guard.SdfGuard;
import ru.spcex.clearing.session.state.guard.SdfGuardExtractorAfterAssets;
import ru.spcex.clearing.session.state.action.SdfReceivedActionV2;
import ru.spcex.clearing.session.state.guard.SdfGuardUtil;
import static ru.spcex.clearing.util.StateMachineUtil.chain;
import ru.spcex.platform.enumeration.Section;
import ru.spcex.platform.utils.log.ExceptionUtils;
@Configuration
@EnableStateMachineFactory
public class TestStateMachineConfig extends EnumStateMachineConfigurerAdapter<TaskType, SsnEvent> {
@EnableStateMachineFactory(name = "currTestMachine")
public class TestCurrStateMachineConfig extends EnumStateMachineConfigurerAdapter<TaskType, SsnEvent> {
private final Logger log = LoggerFactory.getLogger(getClass());
@Autowired
@ -44,6 +43,7 @@ public class TestStateMachineConfig extends EnumStateMachineConfigurerAdapter<Ta
.initial(TaskType.StartRevise, chain(
context -> log.info("create session action"),
context -> log.info("send sdf56 action")))
// .state(TaskType.PSEUDO_waitSdfAfterPaymentInstructions, SsnEvent.ALL_SDF_RECEIVED)
.states(new HashSet<>(Arrays.asList(
TaskType.StartRevise,
TaskType.StartRevisePart1,
@ -52,7 +52,6 @@ public class TestStateMachineConfig extends EnumStateMachineConfigurerAdapter<Ta
TaskType.ObligationsAdmission,
TaskType.InclusionToPool,
TaskType.InspectionObligations,
TaskType.FormingRegistersOnOS,
TaskType.FormingPaymentInstruction,
TaskType.FinishingSession,
TaskType.EndStageNotification,
@ -60,7 +59,6 @@ public class TestStateMachineConfig extends EnumStateMachineConfigurerAdapter<Ta
TaskType.PSEUDO_waitAfterObligationAdmissionError
)))
.end(TaskType.EndStageNotification);
}
@Override
@ -69,80 +67,134 @@ public class TestStateMachineConfig extends EnumStateMachineConfigurerAdapter<Ta
.withExternal()
.event(SsnEvent.SDF_57)
.source(TaskType.StartRevise).target(TaskType.StartRevisePart1)
.action(context -> log.info("reviseStage1Action"))
.action(act("reviseStage1Action"))
.and()
.withExternal()
.source(TaskType.StartRevisePart1).target(TaskType.DealsPrepare)
.action(context -> log.info("dealsPrepareAction"))
.action(act("dealsPrepareAction"))
.and()
.withExternal()
.source(TaskType.DealsPrepare).target(TaskType.RequirementsAndObligationsCreate)
.action(context -> log.info("reqAndOblAction"))
.action(act("reqAndOblAction"))
.and()
.withExternal()
.source(TaskType.RequirementsAndObligationsCreate).target(TaskType.ObligationsAdmission)
.action(context -> log.info("obligationAdmissionAction"))
.and()
.withExternal()
.source(TaskType.ObligationsAdmission)
.target(TaskType.PSEUDO_waitAfterObligationAdmissionError)
.guard(context -> true)
.and()
.withExternal()
.source(TaskType.PSEUDO_waitAfterObligationAdmissionError).target(TaskType.InclusionToPool)
.action(chain(ctx -> log.info("obligationAdmissionContinueAction"), ctx -> log.info("inclusionToPoolAction")))
.event(SsnEvent.CONTINUE)
.and()
.withExternal()
.source(TaskType.PSEUDO_waitAfterObligationAdmissionError).target(TaskType.ObligationsAdmission)
.action(chain(act("discardOblAdmStash"), act("obligationAdmissionAction")))
.event(SsnEvent.REPEAT)
.and()
.withExternal()
.source(TaskType.ObligationsAdmission).target(TaskType.InclusionToPool)
.guard(context -> false) //invert(oblAdmGuard)
.action(chain(act("obligationAdmissionContinueAction"), act("inclusionToPoolAction")))
.action(act("obligationAdmissionAction"))
.and()
.withExternal()
.source(TaskType.InclusionToPool).target(TaskType.InspectionObligations)
.action(act("inspOblDepositReturnAction"))
.action(act("inspectionObligationsV2Action"))
.and()
.withExternal()
.source(TaskType.InspectionObligations).target(TaskType.FormingPaymentInstruction)
.action(act("formingPaymentInstructionReturnMkrAction"))
.action(act("formingPaymentInstructionAssetsAction"))
.and()
//если не создалось paymentInstruction'ов
.withExternal()
.source(TaskType.FormingPaymentInstruction)
.target(TaskType.FinishingSession)
.guard(ctx -> true) //PaymentsWereNotCreatedGuard.instance
.action(act("finishingSessionAction"))
.and()
//если создались paymentInstruction, переходим в режим ожидания
.withExternal()
.source(TaskType.FormingPaymentInstruction)
.target(TaskType.PSEUDO_waitSdfAfterPaymentInstructions)
.guard(PaymentsWereCreatedGuard.instance)
.and()
.withExternal()
.source(TaskType.PSEUDO_waitSdfAfterPaymentInstructions)
.target(TaskType.FinishingSession)
.guard(new SdfGuard(SdfGuardExtractorAfterAssets.instance))
.action(act("finishingSessionAction"))
.source(TaskType.PSEUDO_waitSdfAfterAgainRevise)
.target(TaskType.AgainRevise)
.guard(ctx -> true)
.action(act("againRevise"))
.and()
.withExternal()
.source(TaskType.FinishingSession)
.target(TaskType.EndStageNotification)
.action(act("endStageNotificationAction"));
for (var event: EnumSet.of(SsnEvent.SDF_01, SsnEvent.SDF_57, SsnEvent.SDF_04)) {
sourceObligationAdmission(transitions);
sourcePseudoAfterOAError(transitions);
sourceFormingPaymentInstruction(transitions);
sourcePseudoWaitSdfAfterPaymentInstructions(transitions);
sourceAgainRevise(transitions);
}
private void sourceObligationAdmission(StateMachineTransitionConfigurer<TaskType, SsnEvent> transitions) throws Exception {
transitions
.withExternal()
.source(TaskType.ObligationsAdmission)
.target(TaskType.PSEUDO_waitAfterObligationAdmissionError)
.guard(ctx -> false)
.and()
.withExternal()
.source(TaskType.ObligationsAdmission).target(TaskType.InclusionToPool)
.guard(ctx -> true)
.action(chain(act("obligationAdmissionContinueAction"), act("inclusionToPoolAction")));
}
private void sourcePseudoAfterOAError(StateMachineTransitionConfigurer<TaskType, SsnEvent> transitions) throws Exception {
transitions
.withExternal()
.source(TaskType.PSEUDO_waitAfterObligationAdmissionError).target(TaskType.InclusionToPool)
.action(chain(act("obligationAdmissionContinueAction"), act("inclusionToPoolAction")))
.event(SsnEvent.CONTINUE)
.and()
.withExternal()
.source(TaskType.PSEUDO_waitAfterObligationAdmissionError).target(TaskType.ObligationsAdmission)
.action(chain(act("discardOblAdmStash"), act("obligationAdmissionAction")))
.event(SsnEvent.REPEAT);
}
private void sourceFormingPaymentInstruction(StateMachineTransitionConfigurer<TaskType, SsnEvent> transitions) throws Exception {
transitions
//если не создалось paymentInstruction'ов
.withExternal()
.source(TaskType.FormingPaymentInstruction)
.target(TaskType.AgainRevise)
.guard(ctx -> false)
.action(act("againRevise"))
.and()
//если создались paymentInstruction, переходим в режим ожидания
.withExternal()
.source(TaskType.FormingPaymentInstruction)
.target(TaskType.PSEUDO_waitSdfAfterPaymentInstructions)
.guard(ctx -> true);
}
private void sourcePseudoWaitSdfAfterPaymentInstructions(StateMachineTransitionConfigurer<TaskType, SsnEvent> transitions) throws Exception {
// EnumSet<SsnEvent> sdfs = SdfGuardUtil.bySectionAfter7Step(Section.MKR);
// transitions
// .withExternal()
// .source(TaskType.PSEUDO_waitSdfAfterPaymentInstructions)
// .target(TaskType.AgainRevise)
// .guard(new SdfSetGuard(sdfs))
// .action(act("againRevise"));
// for (var event : sdfs) {
// transitions
// .withInternal()
// .source(TaskType.PSEUDO_waitSdfAfterPaymentInstructions)
// .event(event)
// .action(SdfReceivedAction.instance);
// }
EnumSet<SsnEvent> sdfs = SdfGuardUtil.bySectionAfter7Step(Section.MKR);
SdfReceivedActionV2<TaskType> sdfReceivedAction = new SdfReceivedActionV2<>(sdfs);
for (var event : sdfs) {
transitions
.withInternal()
.source(TaskType.PSEUDO_waitSdfAfterPaymentInstructions)
.event(event)
.action(SdfReceivedAction.instance);
.action(sdfReceivedAction);
}
transitions.withExternal()
.source(TaskType.PSEUDO_waitSdfAfterPaymentInstructions)
.target(TaskType.EndStageNotification)
.event(SsnEvent.ALL_SDF_RECEIVED)
.action(context -> {
System.out.println("***** ALL SDF RECEIVED");
log.info("***** ALL SDF RECEIVED");
});
}
private void sourceAgainRevise(StateMachineTransitionConfigurer<TaskType, SsnEvent> transitions) throws Exception {
transitions
.withExternal()
.source(TaskType.AgainRevise)
.target(TaskType.FinishingSession)
.action(act("finishingSessionAction"))
.guard(ctx -> true)
.and()
.withExternal()
.source(TaskType.AgainRevise)
.target(TaskType.PSEUDO_waitSdfAfterAgainRevise)
.guard(ctx -> false);
}
private Action<TaskType, SsnEvent> act(String actionName) {
@ -209,7 +261,7 @@ public class TestStateMachineConfig extends EnumStateMachineConfigurerAdapter<Ta
};
config
.withConfiguration()
.machineId("test-machine")
.machineId("curr-test-machine")
.listener(loggingChangeStateListener)
// .taskExecutor(taskExecutor)
;

View file

@ -0,0 +1,148 @@
package ru.spcex.clearing.session.teststate.config;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.EnableStateMachineFactory;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
import org.springframework.statemachine.transition.Transition;
import ru.spcex.clearing.session.state.SsnEvent;
import ru.spcex.clearing.session.state.action.SdfReceivedActionV2;
import ru.spcex.clearing.session.state.guard.SdfGuardUtil;
import ru.spcex.platform.enumeration.Section;
import ru.spcex.platform.utils.log.ExceptionUtils;
@Configuration
@EnableStateMachineFactory(name = "simpleTestMachineGuard")
public class TestSimpleStateMachineConfig extends EnumStateMachineConfigurerAdapter<SmpState, SsnEvent> {
private final Logger log = LoggerFactory.getLogger(getClass());
@Autowired
@Qualifier("myStateMachineTaskExecutor")
private TaskExecutor taskExecutor;
@Override
public void configure(StateMachineStateConfigurer<SmpState, SsnEvent> states) throws Exception {
states
.withStates()
.initial(SmpState.Begin, context -> log.info("Begin entry"))
.states(new HashSet<>(Arrays.asList(
SmpState.Begin,
SmpState.WaitForSdf
)))
.end(SmpState.Finish);
}
@Override
public void configure(StateMachineTransitionConfigurer<SmpState, SsnEvent> transitions) throws Exception {
transitions
.withExternal()
.source(SmpState.Begin)
.target(SmpState.WaitForSdf)
.action(act("action Begin -> Wait for sdf"))
;
EnumSet<SsnEvent> sdfs = SdfGuardUtil.bySectionAfter7Step(Section.MKR);
SdfReceivedActionV2<SmpState> sdfReceivedAction = new SdfReceivedActionV2<>(sdfs);
for (var event : sdfs) {
transitions
.withInternal()
.source(SmpState.WaitForSdf)
.event(event)
.action(sdfReceivedAction);
}
transitions.withExternal()
.source(SmpState.WaitForSdf)
.target(SmpState.Finish)
.event(SsnEvent.ALL_SDF_RECEIVED)
.action(context -> {
System.out.println("***** ALL SDF RECEIVED");
log.info("***** ALL SDF RECEIVED");
});
}
private Action<SmpState, SsnEvent> act(String actionName) {
return ctx -> log.info(actionName);
}
@Override
public void configure(StateMachineConfigurationConfigurer<SmpState, SsnEvent> config) throws Exception {
StateMachineListenerAdapter<SmpState, SsnEvent> loggingChangeStateListener = new StateMachineListenerAdapter<>() {
// @Override
// public void stateEntered(org.springframework.statemachine.state.State<SmpState, SsnEvent> state) {
// SmpState enteredState = state != null ? state.getId() : null;
// log.info(String.format("LISTENER stateEntered: %s", enteredState));
// }
// @Override
// public void eventNotAccepted(Message<SsnEvent> event) {
// SsnEvent payload = event != null ? event.getPayload() : null;
// log.info(String.format("LISTENER eventNotAccepted: %s", payload));
// }
@Override
public void transition(Transition<SmpState, SsnEvent> transition) {
SmpState source = transition.getSource() != null ? transition.getSource().getId() : null;
SmpState target = transition.getTarget().getId();
log.info("LISTENER transition: source {} target {}",
source, target);
}
// @Override
// public void stateChanged(org.springframework.statemachine.state.State<SmpState, SsnEvent> from, org.springframework.statemachine.state.State<SmpState, SsnEvent> to) {
// SmpState source = from != null ? from.getId() : null;
// SmpState target = to.getId();
// log.info("LISTENER stateChanged: source {} target {}",
// source, target);
// }
// @Override
// public void stateExited(org.springframework.statemachine.state.State<SmpState, SsnEvent> state) {
// SmpState whichOne = state != null ? state.getId() : null;
// log.info("LISTENER stateExited: {}", whichOne);
// }
// @Override
// public void transitionEnded(Transition<SmpState, SsnEvent> transition) {
// SmpState source = transition.getSource() != null ? transition.getSource().getId() : null;
// SmpState target = transition.getTarget().getId();
// log.info("LISTENER transitionEnded: source {} target {}",
// source, target);
// }
@Override
public void stateMachineError(StateMachine<SmpState, SsnEvent> stateMachine, Exception exception) {
log.info("LISTENER stateMachineError: {}", (ExceptionUtils.getStackTrace(exception)));
}
// @Override
// public void transitionStarted(Transition<SmpState, SsnEvent> transition) {
// SmpState source = transition.getSource() != null ? transition.getSource().getId() : null;
// SmpState target = transition.getTarget().getId();
// log.info("LISTENER transitionStarted: source {} target {}",
// source, target);
// }
};
config
.withConfiguration()
.machineId("smp-test-machine")
.listener(loggingChangeStateListener)
// .taskExecutor(taskExecutor)
;
}
}

View file

@ -0,0 +1,143 @@
package ru.spcex.clearing.session.teststate.config;
import java.util.Arrays;
import java.util.HashSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.EnableStateMachineFactory;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
import org.springframework.statemachine.transition.Transition;
import ru.spcex.clearing.session.state.DataEnum;
import ru.spcex.clearing.session.state.SsnEvent;
import ru.spcex.platform.utils.log.ExceptionUtils;
@Configuration
@EnableStateMachineFactory(name = "simpleTestMachineInterceptor")
public class TestSimpleStateMachineInterceptorConfig extends EnumStateMachineConfigurerAdapter<SmpState, SsnEvent> {
private final Logger log = LoggerFactory.getLogger(getClass());
@Autowired
@Qualifier("myStateMachineTaskExecutor")
private TaskExecutor taskExecutor;
@Override
public void configure(StateMachineStateConfigurer<SmpState, SsnEvent> states) throws Exception {
states
.withStates()
.initial(SmpState.Begin, context -> log.info("Begin entry"))
.states(new HashSet<>(Arrays.asList(
SmpState.Begin,
SmpState.WaitForSdf
)))
.end(SmpState.Finish);
}
@Override
public void configure(StateMachineTransitionConfigurer<SmpState, SsnEvent> transitions) throws Exception {
transitions
.withExternal()
.source(SmpState.Begin)
.target(SmpState.WaitForSdf)
.action(act("action Begin -> Wait for sdf"))
;
transitions
.withExternal()
.source(SmpState.WaitForSdf)
.target(SmpState.Finish)
.guard(context -> {
log.info("guard check running...");
return true;
})
.action(context -> {
log.info("action WaitForSdf -> Finish for sdf");
Object o = context.getExtendedState().get(DataEnum.sessionId, Long.class);
if (o != null) {
log.info("{} was not null: {}", DataEnum.sessionId, o);
}
});
}
private Action<SmpState, SsnEvent> act(String actionName) {
return ctx -> log.info(actionName);
}
@Override
public void configure(StateMachineConfigurationConfigurer<SmpState, SsnEvent> config) throws Exception {
StateMachineListenerAdapter<SmpState, SsnEvent> loggingChangeStateListener = new StateMachineListenerAdapter<>() {
// @Override
// public void stateEntered(org.springframework.statemachine.state.State<SmpState, SsnEvent> state) {
// SmpState enteredState = state != null ? state.getId() : null;
// log.info(String.format("LISTENER stateEntered: %s", enteredState));
// }
// @Override
// public void eventNotAccepted(Message<SsnEvent> event) {
// SsnEvent payload = event != null ? event.getPayload() : null;
// log.info(String.format("LISTENER eventNotAccepted: %s", payload));
// }
@Override
public void transition(Transition<SmpState, SsnEvent> transition) {
SmpState source = transition.getSource() != null ? transition.getSource().getId() : null;
SmpState target = transition.getTarget().getId();
log.info("LISTENER transition: source {} target {}",
source, target);
}
// @Override
// public void stateChanged(org.springframework.statemachine.state.State<SmpState, SsnEvent> from, org.springframework.statemachine.state.State<SmpState, SsnEvent> to) {
// SmpState source = from != null ? from.getId() : null;
// SmpState target = to.getId();
// log.info("LISTENER stateChanged: source {} target {}",
// source, target);
// }
// @Override
// public void stateExited(org.springframework.statemachine.state.State<SmpState, SsnEvent> state) {
// SmpState whichOne = state != null ? state.getId() : null;
// log.info("LISTENER stateExited: {}", whichOne);
// }
// @Override
// public void transitionEnded(Transition<SmpState, SsnEvent> transition) {
// SmpState source = transition.getSource() != null ? transition.getSource().getId() : null;
// SmpState target = transition.getTarget().getId();
// log.info("LISTENER transitionEnded: source {} target {}",
// source, target);
// }
@Override
public void stateMachineError(StateMachine<SmpState, SsnEvent> stateMachine, Exception exception) {
log.info("LISTENER stateMachineError: {}", (ExceptionUtils.getStackTrace(exception)));
}
// @Override
// public void transitionStarted(Transition<SmpState, SsnEvent> transition) {
// SmpState source = transition.getSource() != null ? transition.getSource().getId() : null;
// SmpState target = transition.getTarget().getId();
// log.info("LISTENER transitionStarted: source {} target {}",
// source, target);
// }
};
config
.withConfiguration()
.machineId("smp-test-machine")
// .listener(loggingChangeStateListener)
// .taskExecutor(taskExecutor)
;
}
}

View file

@ -0,0 +1,41 @@
package ru.spcex.clearing.session.teststate.config;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.statemachine.support.DefaultExtendedState;
import org.springframework.statemachine.support.DefaultStateMachineContext;
import ru.spcex.clearing.session.state.DataEnum;
import ru.spcex.clearing.session.state.SsnEvent;
@Configuration
public class TestSimpleStateMachineRestoreConfig {
private final Logger log = LoggerFactory.getLogger(getClass());
@Autowired
@Qualifier("simpleTestMachineInterceptor")
private StateMachineFactory<SmpState, SsnEvent> smpStateMachineInterceptorFactory;
@Bean("testRestoredStateMachine")
public StateMachine<SmpState, SsnEvent> restoredStateMachine() {
StateMachine<SmpState, SsnEvent> sm = smpStateMachineInterceptorFactory.getStateMachine();
sm.stop();
DefaultExtendedState state = new DefaultExtendedState();
Map<Object, Object> vars = state.getVariables();
vars.put(DataEnum.sessionId, 1L);
DefaultStateMachineContext<SmpState, SsnEvent> ctx = new DefaultStateMachineContext<>(
SmpState.WaitForSdf,
null,
null,
state
);
sm.getStateMachineAccessor().doWithAllRegions(a -> a.resetStateMachine(ctx));
return sm;
}
}