Hi!
It looks like I misunderstand something in methodology of a state machine. Please take a look at a simple example:
package test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.squirrelframework.foundation.fsm.StateMachineBuilderFactory;
import org.squirrelframework.foundation.fsm.StateMachineConfiguration;
import org.squirrelframework.foundation.fsm.TransitionType;
import org.squirrelframework.foundation.fsm.UntypedStateMachineBuilder;
import org.squirrelframework.foundation.fsm.annotation.State;
import org.squirrelframework.foundation.fsm.annotation.StateMachineParameters;
import org.squirrelframework.foundation.fsm.annotation.States;
import org.squirrelframework.foundation.fsm.annotation.Transit;
import org.squirrelframework.foundation.fsm.annotation.Transitions;
import org.squirrelframework.foundation.fsm.impl.AbstractUntypedStateMachine;
import test.TestFSM.CharContext;
import test.TestFSM.TokenState;
import test.TestFSM.CharClass;
@States({
@State(name="NO_TOKENS", entryCallMethod="cleanupTokens", initialState = true),
@State(name="OUT_OF_TOKENS"),
@State(name="IN_WORD"),
@State(name="IN_NUMBER"),
@State(name="STOP", isFinal = true)
})
@Transitions({
@Transit(from="NO_TOKENS", on = "ALPHA", to="IN_WORD", callMethod = "startToken"),
@Transit(from="NO_TOKENS", on = "DIGIT", to="IN_NUMBER", callMethod = "startToken"),
@Transit(from="NO_TOKENS", on = "EOL", to="STOP"),
@Transit(from="NO_TOKENS", on = "SPACE", to="NO_TOKENS", type = TransitionType.INTERNAL),
@Transit(from="OUT_OF_TOKENS", on = "ALPHA", to="IN_WORD", callMethod = "startToken"),
@Transit(from="OUT_OF_TOKENS", on = "DIGIT", to="IN_NUMBER", callMethod = "startToken"),
@Transit(from="OUT_OF_TOKENS", on = "EOL", to="STOP"),
@Transit(from="OUT_OF_TOKENS", on = "SPACE", to="OUT_OF_TOKENS", type = TransitionType.INTERNAL),
@Transit(from="IN_WORD", on = "ALPHA", to="IN_WORD", type = TransitionType.INTERNAL),
@Transit(from="IN_WORD", on = "DIGIT", to="IN_WORD", type = TransitionType.INTERNAL), // oh really?
@Transit(from="IN_WORD", on = "EOL", to="STOP", callMethod = "completeWordToken"),
@Transit(from="IN_WORD", on = "SPACE", to="OUT_OF_TOKENS", callMethod = "completeWordToken"),
@Transit(from="IN_NUMBER", on = "ALPHA", to="IN_WORD", callMethod = "completeNumberToken"),
@Transit(from="IN_NUMBER", on = "DIGIT", to="IN_NUMBER", type = TransitionType.INTERNAL),
@Transit(from="IN_NUMBER", on = "EOL", to="STOP", callMethod = "completeNumberToken"),
@Transit(from="IN_NUMBER", on = "SPACE", to="OUT_OF_TOKENS", callMethod = "completeNumberToken"),
})
@StateMachineParameters(stateType = TokenState.class, eventType = CharClass.class, contextType = CharContext.class)
public class TestFSM extends AbstractUntypedStateMachine {
public enum CharClass {
ALPHA,
DIGIT,
SPACE,
EOL,
}
public enum TokenState {
NO_TOKENS,
OUT_OF_TOKENS,
IN_WORD,
IN_NUMBER,
STOP,
}
static class CharContext {
}
private final static Logger logger = LoggerFactory.getLogger(TestFSM.class);
protected void cleanupTokens(final TokenState _from, final TokenState _to, final CharClass _class, final CharContext _ctx) {
logger.info("cleanupTokens");
}
protected void startToken(final TokenState _from, final TokenState _to, final CharClass _class, final CharContext _ctx) {
logger.info("startToken");
}
protected void completeWordToken(final TokenState _from, final TokenState _to, final CharClass _class, final CharContext _ctx) {
logger.info("completeWordToken");
}
protected void completeNumberToken(final TokenState _from, final TokenState _to, final CharClass _class, final CharContext _ctx) {
logger.info("completeNumberToken");
}
public static void main(String[] args) {
final UntypedStateMachineBuilder builder =
StateMachineBuilderFactory.create(TestFSM.class, TokenState.class, CharClass.class, CharContext.class);
CharClass[][] addresses = {
{ CharClass.ALPHA, CharClass.ALPHA, CharClass.ALPHA, CharClass.SPACE, CharClass.DIGIT, CharClass.DIGIT, CharClass.ALPHA },
{ CharClass.SPACE, CharClass.ALPHA, CharClass.ALPHA, CharClass.ALPHA, CharClass.DIGIT, CharClass.DIGIT, CharClass.ALPHA },
{ CharClass.SPACE, CharClass.ALPHA, CharClass.ALPHA, CharClass.ALPHA, CharClass.DIGIT, CharClass.DIGIT, CharClass.SPACE },
};
for(final CharClass[] address: addresses) {
final CharContext ctx = new CharContext();
final boolean isDebug = false; // set to true, and the machine will break just right on the first event
final TestFSM fsm = builder.newUntypedStateMachine(TokenState.NO_TOKENS, StateMachineConfiguration.create().setDebugEnabled(isDebug));
fsm.start(ctx);
for(final CharClass event: address) {
fsm.fire(event, ctx);
}
fsm.fire(CharClass.EOL, ctx);
logger.info("Processed an address");
}
}
}
===
This example works pretty well and produces expected output. But if isDebug is set to true, then the first invocation of fsm.fire() dies with the following output:
==
2014-03-26 18:46:23,382 INFO [main] (TestFSM.java:76) - cleanupTokens
2014-03-26 18:46:23,454 INFO [main] (StateMachineLogger.java:66) - TestFSM(BywcpHEV51): Transition from "NO_TOKENS" on "ALPHA" with context "test.TestFSM$CharContext@74b1896c" begin.
2014-03-26 18:46:23,457 INFO [main] (StateMachineLogger.java:94) - Before execute method call action "startToken:0" (1 of 1).
2014-03-26 18:46:23,458 INFO [main] (TestFSM.java:80) - startToken
2014-03-26 18:46:23,462 ERROR [main] (StateMachineLogger.java:85) - TestFSM(BywcpHEV51): Transition from "NO_TOKENS" to "NO_TOKENS" on "ALPHA" caused exception.
org.squirrelframework.foundation.exception.SquirrelRuntimeException: 00010012 : couldn't invoke 'public abstract void org.squirrelframework.foundation.fsm.ActionExecutionService$AfterExecActionListener.afterExecute(org.squirrelframework.foundation.fsm.ActionExecutionService$AfterExecActionEvent)' with [org.squirrelframework.foundation.fsm.impl.AbstractExecutionService$AfterExecActionEventImpl@635194a1] on org.squirrelframework.foundation.fsm.impl.AbstractStateMachine$5@cd2b045: null.
at org.squirrelframework.foundation.util.ReflectUtils.invoke(ReflectUtils.java:337)
at org.squirrelframework.foundation.event.ListenerMethod.invokeMethod(ListenerMethod.java:63)
at org.squirrelframework.foundation.event.PolymEventDispatcher.fireEvent(PolymEventDispatcher.java:68)
at org.squirrelframework.foundation.component.impl.AbstractSubject.fireEvent(AbstractSubject.java:84)
at org.squirrelframework.foundation.fsm.impl.AbstractExecutionService.doExecute(AbstractExecutionService.java:93)
at org.squirrelframework.foundation.fsm.impl.AbstractExecutionService.executeActions(AbstractExecutionService.java:131)
at org.squirrelframework.foundation.fsm.impl.AbstractExecutionService.execute(AbstractExecutionService.java:139)
at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.processEvent(AbstractStateMachine.java:195)
at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.processEvents(AbstractStateMachine.java:246)
at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.internalFire(AbstractStateMachine.java:279)
at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.fire(AbstractStateMachine.java:299)
at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.fire(AbstractStateMachine.java:310)
at org.squirrelframework.foundation.fsm.impl.AbstractUntypedStateMachine.fire(AbstractUntypedStateMachine.java:26)
at test.TestFSM.main(TestFSM.java:105)
org.squirrelframework.foundation.exception.SquirrelRuntimeException: 00010012 : couldn't invoke 'public abstract void org.squirrelframework.foundation.fsm.ActionExecutionService$AfterExecActionListener.afterExecute(org.squirrelframework.foundation.fsm.ActionExecutionService$AfterExecActionEvent)' with [org.squirrelframework.foundation.fsm.impl.AbstractExecutionService$AfterExecActionEventImpl@635194a1] on org.squirrelframework.foundation.fsm.impl.AbstractStateMachine$5@cd2b045: null.
at org.squirrelframework.foundation.util.ReflectUtils.invoke(ReflectUtils.java:337)
at org.squirrelframework.foundation.event.ListenerMethod.invokeMethod(ListenerMethod.java:63)
at org.squirrelframework.foundation.event.PolymEventDispatcher.fireEvent(PolymEventDispatcher.java:68)
at org.squirrelframework.foundation.component.impl.AbstractSubject.fireEvent(AbstractSubject.java:84)
at org.squirrelframework.foundation.fsm.impl.AbstractExecutionService.doExecute(AbstractExecutionService.java:93)
at org.squirrelframework.foundation.fsm.impl.AbstractExecutionService.executeActions(AbstractExecutionService.java:131)
at org.squirrelframework.foundation.fsm.impl.AbstractExecutionService.execute(AbstractExecutionService.java:139)
at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.processEvent(AbstractStateMachine.java:195)
at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.processEvents(AbstractStateMachine.java:246)
at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.internalFire(AbstractStateMachine.java:279)
at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.fire(AbstractStateMachine.java:299)
at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.fire(AbstractStateMachine.java:310)
at org.squirrelframework.foundation.fsm.impl.AbstractUntypedStateMachine.fire(AbstractUntypedStateMachine.java:26)
at test.TestFSM.main(TestFSM.java:105)
Exception in thread "main" org.squirrelframework.foundation.exception.TransitionException: 00010017 : Transition from 'NO_TOKENS' to 'IN_WORD' on 'ALPHA' with context 'test.TestFSM$CharContext@74b1896c' when invoking action 'UNKNOWN' caused exception '00010012 : couldn't invoke 'public abstract void org.squirrelframework.foundation.fsm.ActionExecutionService$AfterExecActionListener.afterExecute(org.squirrelframework.foundation.fsm.ActionExecutionService$AfterExecActionEvent)' with [org.squirrelframework.foundation.fsm.impl.AbstractExecutionService$AfterExecActionEventImpl@635194a1] on org.squirrelframework.foundation.fsm.impl.AbstractStateMachine$5@cd2b045: null.'.
at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.processEvent(AbstractStateMachine.java:217)
at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.processEvents(AbstractStateMachine.java:246)
at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.internalFire(AbstractStateMachine.java:279)
at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.fire(AbstractStateMachine.java:299)
at org.squirrelframework.foundation.fsm.impl.AbstractStateMachine.fire(AbstractStateMachine.java:310)
at org.squirrelframework.foundation.fsm.impl.AbstractUntypedStateMachine.fire(AbstractUntypedStateMachine.java:26)
at test.TestFSM.main(TestFSM.java:105)
==
Please help me to understand what's wrong with the SM.
Thank you in advance,
Alexey Morozov