Squirrel 状态机

365 阅读16分钟

Squirrel User Guide

source:github.com/hekailiang/…

Get Starting

squirrel同时支持流式API和声明式的方式来声明一个state machine,同时让用户可以直接对action方法进行定义

  • State Machine interface 有四个泛型参数

    • T 代表state machine
    • S 代表 state
    • E代表 event
    • C代表外部的context
  • State Machine Builder

    • State machine builder用来生成state machine定义。StateMachineBuilder可以由StateMachineBuilderFactory。
    • StateMachineBuilder 是由 *TransitionBuilder(InternalTransitionBuilder/LocalTransitionBuilder/ExternalTransitionBuilder)组成,用来在state之间建立Transition。EntryExitActionBuilder用来建立进入或退出state的action。
    • 内部的状态在transition 创建或state action创建期间就隐式地建立了
    • 所有由state machine builder创建的state machine 实例都共享同一份定义,来对内存使用进行优化
    • State machine builder生成state machine定义是懒加载的。当builder创建其第一个state machine实例时,state machine的定义才会被生成。但是在state machine 定义生成之后,接下来的state machine实例的创建就会非常快。通常来说,state machine builder应当尽可能的重用

    为了创建一个state machine,我们需要首先创建state machine builder。例如:

    StateMachineBuilder<MyStateMachine, MyState, MyEvent, MyContext> builder =
        StateMachineBuilderFactory.create(MyStateMachine.class, MyState.class, MyEvent.class, MyContext.class);
    

    State machine builder的参数为state machine的类型(T),state(S),event(E)以及context(C)。

  • Fluent API

    在state machine builder创建之后,我们就可以使用流式API去定义state machine的state/transition/action

    builder.externalTransition().from(MyState.A).to(MyState.B).on(MyEvent.GoToB);
    

    创建了一个external transition,由state A到state B,收到event GoToB时被触发。

    builder.internalTransition(TransitionPriority.HIGH).within(MyState.A).on(MyEvent.WithinA).perform(myAction);
    

    在state A上创建了一个优先级设为high的internal transition,当收到event WithinA时 执行myAction。Internal transition意味着在transition完成后,不会发生state的退出和进入Transition的优先级被用来覆盖原始的transition当state machine扩展时。

    builder.externalTransition().from(MyState.C).to(MyState.D).on(MyEvent.GoToD).when(
        new Condition<MyContext>() {
            @Override
            public boolean isSatisfied(MyContext context) {
                return context!=null && context.getValue()>80;
            }
            
            @Override
            public String name() {
                return "MyCondition";
            }
    }).callMethod("myInternalTransitionCall");
    

    创建了一个从state C到state D的conditionaltransition,当event为GoToD且context满足其condition时,会调用action方法myInternalTransitionCall。你也可以在此处使用 MVEL (一种强大的表达式语言 )来对condition进行描述

    builder.externalTransition().from(MyState.C).to(MyState.D).on(MyEvent.GoToD).whenMvel(
        "MyCondition:::(context!=null && context.getValue()>80)").callMethod("myInternalTransitionCall");
    

    Note:字符:::用于分隔condition 名称和condition 表达式。context是预定义的,指向Context对象。

    builder.onEntry(MyState.A).perform(Lists.newArrayList(action1, action2))
    

    定义了多个state进入时的action

  • Method Call Action

    当定义transition或者state 进入/退出时,用户可以定义匿名action。然而,这样会使action代码分布在很多地方,使之难以管理。而且,其他用户也不能重写该action方法。因此squirrel支持对state machine class中的方法进行调用。

    StateMachineBuilder<...> builder = StateMachineBuilderFactory.create(
        MyStateMachine.class, MyState.class, MyEvent.class, MyContext.class);
    builder.externalTransition().from(A).to(B).on(toB).callMethod("fromAToB");
    
    // All transition action method stays with state machine class
    public class MyStateMachine<...> extends AbstractStateMachine<...> {
        protected void fromAToB(MyState from, MyState to, MyEvent event, MyContext context) {
            // this method will be called during transition from "A" to "B" on event "toB"
            // the action method parameters types and order should match
            ...
        }
    }
    

    而且,squirrel还支持通过Convention Over Configuration的方式进行定义action方法。意思就是只要方法名和参数符合要求,就会被添加到transition actions中。

    protected void transitFromAToBOnGoToB(MyState from, MyState to, MyEvent event, MyContext context)
    

    方法名符合transitFrom[SourceStateName]To[TargetStateName]On[EventName]且参数为[MyState, MyState, MyEvent, MyContext] 的方法会被将入到transition “A-(GoToB)->B” 的action 列表中。当由event GoToB触发state 从 AB时,该方法会被调用。

    protected void transitFromAnyToBOnGoToB(MyState from, MyState to, MyEvent event, MyContext context)
    

    transitFromAnyTo[TargetStateName]On[EventName],当由event GoToB触发使state从任何状态到B时,该方法会被调用。

    protected void exitA(MyState from, MyState to, MyEvent event, MyContext context)
    

    当退出state A时,exit[StateName] 会被执行。entry[StateName], beforeExitAny/afterExitAny and beforeEntryAny/afterEntryAny

    其他支持的一些命名模式

    transitFrom[fromStateName]To[toStateName]On[eventName]When[conditionName]
    transitFrom[fromStateName]To[toStateName]On[eventName]
    transitFromAnyTo[toStateName]On[eventName]
    transitFrom[fromStateName]ToAnyOn[eventName]
    transitFrom[fromStateName]To[toStateName]
    on[eventName]
    

    上面所列的方法还提供了类AOP的特性,来为squirrel state machine提供细粒度的扩展能力。更多信息,请查看"org.squirrelframework.foundation.fsm.ExtensionMethodCallTest“。从0.3.1开始,还可以使用流式API来定义这些类AOP的扩展方法。如

    // since 0.3.1
    // the same effect as add method transitFromAnyToCOnToC in your state machine
    builder.transit().fromAny().to("C").on("ToC").callMethod("fromAnyToC");
    // the same effect as add method transitFromBToAnyOnToC in your state machine
    builder.transit().from("B").toAny().on("ToC").callMethod("fromBToAny");
    // the same effect as add method transitFromBToAny in your state machine
    builder.transit().from("B").toAny().onAny().callMethod("fromBToAny");
    

    或者通过声明式注解,如

    // since 0.3.1
    @Transitions({
         @Transit(from="B", to="E", on="*",   callMethod="fromBToEOnAny"),
         @Transit(from="*", to="E", on="ToE", callMethod="fromAnyToEOnToE")
    })
    

    Note:这些action方法会被attached到已存在的且匹配的transition上,并不会创建任何新的transition。从0.3.4开始,可以通过下列的API一次定义多个transition。

    // transitions(A->B@A2B=>a2b, A->C@A2C=>a2c, A->D@A2D) will be defined at once
    builder.transitions().from(State._A).toAmong(State.B, State.C, State.D).
            onEach(Event.A2B, Event.A2C, Event.A2D).callMethod("a2b|a2c|_");
    
    // transitions(A->_A@A2ANY=>DecisionMaker, _A->A@ANY2A) will be defined at once
    builder.localTransitions().between(State.A).and(State._A).
            onMutual(Event.A2ANY, Event.ANY2A).
            perform( Lists.newArrayList(new DecisionMaker("SomeLocalState"), null) );
    
  • declarative Annotation

    还提供了一种声明式的方式来定义和扩展state machine。如下例

    @States({
        @State(name="A", entryCallMethod="entryStateA", exitCallMethod="exitStateA"),
        @State(name="B", entryCallMethod="entryStateB", exitCallMethod="exitStateB")
    })
    @Transitions({
        @Transit(from="A", to="B", on="GoToB", callMethod="stateAToStateBOnGotoB"),
        @Transit(from="A", to="A", on="WithinA", callMethod="stateAToStateAOnWithinA", type=TransitionType.INTERNAL)
    })
    interface MyStateMachine extends StateMachine<MyStateMachine, MyState, MyEvent, MyContext> {
        void entryStateA(MyState from, MyState to, MyEvent event, MyContext context);
        void stateAToStateBOnGotoB(MyState from, MyState to, MyEvent event, MyContext context)
        void stateAToStateAOnWithinA(MyState from, MyState to, MyEvent event, MyContext context)
        void exitStateA(MyState from, MyState to, MyEvent event, MyContext context);
        ...
    }
    

    这个注解可以添加在state machine的实现类上或者state machine 所implement的接口上。它可以与流式API混合使用,这以为着fluentAPI定义的state machine可以被这些注解所扩展。

  • Converters

    为了在@State@Transit中声明state和event,你需要为state类型和event类型实现相应的converter。converter必须实现接口Converter<T> 接口,用于将state/event 与 String互转。

    public interface Converter<T> extends SquirrelComponent {
        /**
        * Convert object to string.
        * @param obj converted object
        * @return string description of object
        */
        String convertToString(T obj);
    
        /**
        * Convert string to object.
        * @param name name of the object
        * @return converted object
        */
        T convertFromString(String name);
    }
    

    然后将这些converter注册到ConverterProvider

    ConverterProvider.INSTANCE.register(MyEvent.class, new MyEventConverter());
    ConverterProvider.INSTANCE.register(MyState.class, new MyStateConverter());
    

    **Note:**如果你只使用流式API定义state machine,那么就没有必须定义converter。如果event或state的类型是字符串或枚举,你也不需要实现并注册convert。

  • New State Machine Instance

    在定义完state machine的行为后,你就可以使用builder创建一个state machine 实例。请注意,一旦builder创建了state machine,builder就不能再定义state machine的任何新元素。

    T newStateMachine(S initialStateId, Object... extraParams);
    

    要从state machine builder中创建一个新的state machine实例,你需要传递下列参数

    1. initialStateId:State machine的初始化state
    2. extraParam:创建新的state machine实例所需的额外的参数。如果你需要传递额外参数,请确保StateMachineBuilderFactory在创建state machine builder的时候已经定义了额外参数的类型,否则额外参数会被忽略。有两种方式可以将额外参数传递到state machine。一种是通过state machine的构造器,这意味着你需要具有相同参数类型和顺序的构造器。另一种方式是定义一个名为postContruct方法,也是带有相同参数类型和顺序。

    如果不需要额外参数,你可以直接调用T newStateMachine(S initialStateId),如

    MyStateMachine stateMachine = builder.newStateMachine(MyState.Initial);
    
  • Trigger Transitions

    在state machine创建之后,你可以使用fire方法触发transition。

    stateMachine.fire(MyEvent.Prepare, new MyContext("Testing"));
    
  • Untyped State Machine

    为了简化state machine使用避免太多的泛型(如StateMachine<T,S,E,C>),这让代码阅读困难。就可以使用UntypedStateMachine ,其在transition action执行的时候也是类型安全的。

    enum TestEvent {
        toA, toB, toC, toD
    }
    
    @Transitions({
        @Transit(from="A", to="B", on="toB", callMethod="fromAToB"),
        @Transit(from="B", to="C", on="toC"),
        @Transit(from="C", to="D", on="toD")
    })
    @StateMachineParameters(stateType=String.class, eventType=TestEvent.class, contextType=Integer.class)
    class UntypedStateMachineSample extends AbstractUntypedStateMachine {
        // No need to specify constructor anymore since 0.2.9
        // protected UntypedStateMachineSample(ImmutableUntypedState initialState,
        //  Map<Object, ImmutableUntypedState> states) {
        //    super(initialState, states);
        // }
        
        protected void fromAToB(String from, String to, TestEvent event, Integer context) {
            // transition action still type safe ...
        }
    
        protected void transitFromDToAOntoA(String from, String to, TestEvent event, Integer context) {
            // transition action still type safe ...
        }
    }
    
    UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(
        UntypedStateMachineSample.class);
    // state machine builder not type safe anymore
    builder.externalTransition().from("D").to("A").on(TestEvent.toA);
    UntypedStateMachine fsm = builder.newStateMachine("A");
    

    要创建一个UntypedStateMachine,你首先需要通过StateMachineBuilderFactory 创建一个UntypedStateMachineBuilder StateMachineBuilderFactory 只需要一个参数,即state machine的类型。

    @StateMachineParameters用来生命state machine参数的类型。AbstractUntypedStateMachine是任何untyped state machine的基类。

  • Context Insensitive State Machine

    有时候state转换并不需要context,即transition只由event决定。这种情况下,你可以使用context insensitive state machine来简化方法参数调用。声明context insensitive state machine是非常简单的。你只需要在state machine实现类上添加@ContextInsensitive 注解。此后,参数 context就可以在transition 方法中省略掉了

    @ContextInsensitive
    public class ATMStateMachine extends AbstractStateMachine<ATMStateMachine, ATMState, String, Void> {
        // no need to add context parameter here anymore
        public void transitFromIdleToLoadingOnConnected(ATMState from, ATMState to, String event) {
            ...
        }
        public void entryLoading(ATMState from, ATMState to, String event) {
            ...
        }
    }
    
  • Transition Exception Handing

    当在state transition中发生异常时,执行的action 会被终止,state machine会进入error状态,这意味着state machine 实例不能再处理event了。如果你继续向state machine 实例发送 event,就会抛出IllegalStateException异常。在transition阶段(包括action执行以及外部listener调用)期间发生的异常都会包装为TransitionException,该异常为unchecked exception。当前,默认的异常处理方式是简单粗暴的,继续把异常往外扔,详情请查看AbstractStateMachine.afterTransitionCausedException方法。

    protected void afterTransitionCausedException(...) { throw e; }
    

    如果statemachine可以从该异常中恢复,你可以重写afterTransitionCausedException 方法,并添加自己相应的恢复逻辑。不要忘记将state machine的status重新设置。例如

    @Override
    protected void afterTransitionCausedException(Object fromState, Object toState, Object event, Object context) {
        Throwable targeException = getLastException().getTargetException();
        // recover from IllegalArgumentException thrown out from state 'A' to 'B' caused by event 'ToB'
        if(targeException instanceof IllegalArgumentException &&
                fromState.equals("A") && toState.equals("B") && event.equals("ToB")) {
            // do some error clean up job here
            // ...
            // after recovered from this exception, reset the state machine status back to normal
            setStatus(StateMachineStatus.IDLE);
        } else if(...) {
            // recover from other exception ...
        } else {
            super.afterTransitionCausedException(fromState, toState, event, context);
        }
    }
    

Advanced Feature

  • Define Hierarchical State

    一个分层的state可能包含嵌套的staet,子state可能又有嵌套的子state,这种嵌套深度可以很深。当一个分层state激活时,有且仅有一个child state被激活。分层state可以通过API或者注解进行定义。

    void defineSequentialStatesOn(S parentStateId, S... childStateIds);
    

    builder.defineSequentialStatesOn(State.A, State.BinA, StateCinA) 定义了两个子state:"BinA"和"CiaA"。第一个定义的子state将会是这个分层state "A"的初始状态。同样地,也可以通过注解定义

    @States({
        @State(name="A", entryMethodCall="entryA", exitMethodCall="exitA"),
        @State(parent="A", name="BinA", entryMethodCall="entryBinA", exitMethodCall="exitBinA", initialState=true),
        @State(parent="A", name="CinA", entryMethodCall="entryCinA", exitMethodCall="exitCinA")
    })
    
  • Define Parallel State

    Parallel state 封装了一系列的子state,当他们的父parent被激活时,它们都会被同时激活。Parallel state可通过API或注解进行定义

    或者

    @States({
        @State(name="Root", entryCallMethod="enterRoot", exitCallMethod="exitRoot", compositeType=StateCompositeType.PARALLEL),
        @State(parent="Root", name="RegionState1", entryCallMethod="enterRegionState1", exitCallMethod="exitRegionState1"),
        @State(parent="Root", name="RegionState2", entryCallMethod="enterRegionState2", exitCallMethod="exitRegionState2")
    })
    

    要想得到parallel state的子status列表

    stateMachine.getSubStatesOn(MyState.Root); // return list of current sub states of parallel state
    
  • Define Context Event

    squirrel定义三种类型的context event,Start/Terminate Event:当state machine start/terminate时会使用该event,例如,当state machine启动并进入初始state时,你可以看到初始状态的initial state中的event为所定义的start event。Finish Event:当所有parallel state都变为final state时,就会自动发出finish event。你可以基于finish event定义下面的transition。要定义context event,有两种方式,注解或者builder API

    @ContextEvent(finishEvent="Finish")
    static class ParallelStateMachine extends AbstractStateMachine<...> {
    }
    

    StateMachineBuilder<...> builder = StateMachineBuilderFactory.create(...);
    ...
    builder.defineFinishEvent(HEvent.Start);
    builder.defineTerminateEvent(HEvent.Terminate);
    builder.defineStartEvent(HEvent.Finish);
    
  • Using History States to Save and Restore the Current State

    History state让一个state machine记住其state configuration。一个以history state作为其target的transition会将state machine回滚到所记录的configuration。如果history的 'type' 是 shallow,state machine就需要再执行任何退出parent的transition之前,把direct active的child state记录下来。如果history的 'type' 是deep,state machine在执行任何退出parent的transition之前,都要把所有active的child state记录下来。可通过注解或者API方式进行定义。

    // defined history type of state "A" as "deep"
    builder.defineSequentialStatesOn(MyState.A, HistoryType.DEEP, MyState.A1, MyState.A2)
    

    或者

    @State(parent="A", name="A1", entryCallMethod="enterA1", exitCallMethod="exitA1", historyType=HistoryType.DEEP)
    
  • Transition Types

    根据UML规范,transition有下面三种类型

    • Internal Transition,如果被触发,不会在source state上发生 exit或enter,也就是不会引起state 变化。
    • Local Transition,如果被触发,不会exit composite(source)state,但是会exit并re-enter composite state中的子state。
    • External Transition,如果被触发,会退出composite state。

    squirrel支持API或注解方式去声明Transition的类型

    builder.externalTransition().from(MyState.A).to(MyState.B).on(MyEvent.A2B);
    builder.internalTransition().within(MyState.A).on(MyEvent.innerA);
    builder.localTransition().from(MyState.A).to(MyState.CinA).on(MyEvent.intoC)
    

    or

    @Transitions({
        @Transition(from="A", to="B", on="A2B"), //default value of transition type is EXTERNAL
        @Transition(from="A", on="innerA", type=TransitionType.INTERNAL),
        @Transition(from="A", to="CinA", on="intoC", type=TransitionType.LOCAL),
    })
    
  • Polymorphism Event Dispatch

    在state machine的生命周期中,会发出多种event

    State Machine Lifecycle Events
    |--StateMachineEvent                        /* Base event of all state machine event */
           |--StartEvent                            /* Fired when state machine started      */
           |--TerminateEvent                        /* Fired when state machine terminated   */
           |--TransitionEvent                       /* Base event of all transition event    */
                |--TransitionBeginEvent             /* Fired when transition began           */
                |--TransitionCompleteEvent          /* Fired when transition completed       */
                |--TransitionExceptionEvent         /* Fired when transition threw exception */
                |--TransitionDeclinedEvent          /* Fired when transition declined        */
                |--TransitionEndEvent               /* Fired when transition end no matter declined or complete */
    

    你可以添加一个监听器来监听StateMachineEvent,这意味着state machine生命周期中发出的所有的event都会被被该监听者监听到

    stateMachine.addStateMachineListener(new StateMachineListener<...>() {
            @Override
            public void stateMachineEvent(StateMachineEvent<...> event) {
                // ...
            }
    });
    

    你也可以通过StateMachine.addTransitionListener监听TransitionEvent,这意味在state transition中发出的所有event,包含TransitionBeginEvent, TransitionCompleteEvent 和TransitionEndEvent等都会被该监听者监听到。你还可以添加指定的Listener,如TransitionDeclinedListener 来监听TransitionDeclinedEvent。

  • Declarative Event Listener

    添加上面的event listener有时候会让用户苦恼,为了简化state machine使用,非侵入式的进行集成,squirrel提供了声明式的方式通过注解来添加event listener。

    static class ExternalModule {
        @OnTransitionEnd
        @ListenerOrder(10) // Since 0.3.1 ListenerOrder can be used to insure listener invoked orderly
        public void transitionEnd() {
            // method annotated with TransitionEnd will be invoked when transition end...
            // the method must be public and return nothing
        }
        
        @OnTransitionBegin
        public void transitionBegin(TestEvent event) {
            // method annotated with TransitionBegin will be invoked when transition begin...
        }
        
        // 'event'(E), 'from'(S), 'to'(S), 'context'(C) and 'stateMachine'(T) can be used in MVEL scripts
        @OnTransitionBegin(when="event.name().equals(\"toB\")")
        public void transitionBeginConditional() {
            // method will be invoked when transition begin while transition caused by event "toB"
        }
        
        @OnTransitionComplete
        public void transitionComplete(String from, String to, TestEvent event, Integer context) {
            // method annotated with TransitionComplete will be invoked when transition complete...
        }
        
        @OnTransitionDecline
        public void transitionDeclined(String from, TestEvent event, Integer context) {
            // method annotated with TransitionDecline will be invoked when transition declined...
        }
        
        @OnBeforeActionExecuted
        public void onBeforeActionExecuted(Object sourceState, Object targetState,
                Object event, Object context, int[] mOfN, Action<?, ?, ?,?> action) {
            // method annotated with OnAfterActionExecuted will be invoked before action invoked
        }
        
        @OnAfterActionExecuted
        public void onAfterActionExecuted(Object sourceState, Object targetState,
                Object event, Object context, int[] mOfN, Action<?, ?, ?,?> action) {
            // method annotated with OnAfterActionExecuted will be invoked after action invoked
        }
    
        @OnActionExecException
        public void onActionExecException(Action<?, ?, ?,?> action, TransitionException e) {
            // method annotated with OnActionExecException will be invoked when action thrown exception
        }
    }
    
    ExternalModule externalModule = new ExternalModule();
    fsm.addDeclarativeListener(externalModule);
    ...
    fsm.removeDeclarativeListener(externalModule);
    

    这样,外部模块的代码并不需要实现任何state machine listener接口,只需添加一些注解就可以,方法的参数也是类型安全的。你可以在 org.squirrelframework.foundation.fsm.StateMachineLogger找到相关示例。

  • Transition Extension Methods

    每一个transition在AbstractStateMachine 类上也有相应的扩展方法

    protected void afterTransitionCausedException(Exception e, S fromState, S toState, E event, C context) {
    }
    
    protected void beforeTransitionBegin(S fromState, E event, C context) {
    }
    
    protected void afterTransitionCompleted(S fromState, S toState, E event, C context) {
    }
    
    protected void afterTransitionEnd(S fromState, S toState, E event, C context) {
    }
    
    protected void afterTransitionDeclined(S fromState, E event, C context) {
    }
    
    protected void beforeActionInvoked(S fromState, S toState, E event, C context) {
    }
    

    通常,你可以将业务逻辑hook到这些扩展方法中,让这些event listener做为state machine的边界与外部模块进行交互(如,UI,审计等)。lir,你可以扩展afterTransitionCausedException 方法进行环境清理当执行transition发生异常时,并通知用户界面展示错误信息。

  • Weighted Action

    你可以定义action的权重来调整action执行的顺序。这些在state entry/exit 和state transition中的action 是根据权重值升序排列的。权重默认为0。你有两种方式定义action 权重

    通过在方法名称后加:和权重值来为方法指定权重

    // define state entry action 'goEntryD' weight -150
    @State(name="D", entryCallMethod="goEntryD:-150")
    // define transition action 'goAToC1' weight +150
    @Transit(from="A", to="C", on="ToC", callMethod="goAToC1:+150")
    

    另一种是重写Action类的weight方法

    Action<...> newAction = new Action<...>() {
        ...
        @Override
        public int weight() {
            return 100;
        }
    }
    

    squirrel还支持传统的权重声明方式,当action方法名称以before开头是,权重被设置为100,以after开头时,权重被设为-100。这意味着以before开头的方法会先被调用,以after开头的方法最后调用,"method1:ignore"意味着method1不会被调用。更多信息请查看 org.squirrelframework.foundation.fsm.WeightedActionTest'

  • Asynchronized Execution

    @AsyncExecute 可以注解在action 方法上,以声明式event listener上,代表action或event listener会以异步的方式执行,

    @ContextInsensitive
    @StateMachineParameters(stateType=String.class, eventType=String.class, contextType=Void.class)
    public class ConcurrentSimpleStateMachine extends AbstractUntypedStateMachine {
        // No need to specify constructor anymore since 0.2.9
        // protected ConcurrentSimpleStateMachine(ImmutableUntypedState initialState,
        //    Map<Object, ImmutableUntypedState> states) {
        //  super(initialState, states);
        // }
    
        @AsyncExecute
        protected void fromAToB(String from, String to, String event) {
            // this action method will be invoked asynchronously
        }
    }
    
    public class DeclarativeListener {
        @OnTransitionBegin
        @AsyncExecute
        public void onTransitionBegin(...) {
            // transition begin event will be dispatched asynchronously to this listener method
        }
    }
    

    异步执行的任务会提交到ExecutorService,你可以通过SquirrelSingletonProvider将你的ExecutorService实现注册。

    ExecutorService executorService = Executors.newFixedThreadPool(1);
    SquirrelSingletonProvider.getInstance().register(ExecutorService.class, executorService);
    

    如果没有ExecutorService实例注册,SquirrelConfiguration会提供一个默认的。

  • State Machine PostProcessor

    你可以为指定类型的state machine注册post processor,来在state machine初始化之后添加后续处理逻辑

    // 1 User defined a state machine interface
    interface MyStateMachine extends StateMachine<MyStateMachine, MyState, MyEvent, MyContext> {
    . . .
    }
    
    // 2 Both MyStateMachineImpl and MyStateMachineImplEx are implemented MyStateMachine
    class MyStateMachineImpl implements MyStateMachine {
        . . .
    }
    class MyStateMachineImplEx implements MyStateMachine {
        . . .
    }
    
    // 3 User define a state machine post processor
    MyStateMachinePostProcessor implements SquirrelPostProcessor<MyStateMachine> {
        void postProcess(MyStateMachine component) {
            . . .
        }
    }
    
    // 4 User register state machine post process
    SquirrelPostProcessorProvider.getInstance().register(MyStateMachine.class, MyStateMachinePostProcessor.class);
    

    对于该例子,当你创建MyStateMachineImpl 和MyStateMachineImplEx 实例的时候,注册的post processor :MyStateMachinePostProcessor 会被调用。

  • State Machine Export

    SCXMLVisitor可以将state machine以[SCXML]文档的形式导出

    SCXMLVisitor visitor = SquirrelProvider.getInstance().newInstance(SCXMLVisitor.class);
    stateMachine.accept(visitor);
    visitor.convertSCXMLFile("MyStateMachine", true);
    

    我们还可以调用StateMachine.exportXMLDefinition(true) 来导出漂亮的xml定义。DotVisitor可以用于生成state diagram,其可以使用GraphViz查看

    DotVisitor visitor = SquirrelProvider.getInstance().newInstance(DotVisitor.class);
    stateMachine.accept(visitor);
    visitor.convertDotFile("SnakeStateMachine");
    
  • State Machine Import

    由SCXMLVisitor 导出或手写的SCXML-similar 定义,可以使用UntypedStateMachineImporter进行导入,UntypedStateMachineImporter 会生成一个UntypedStateMachineImporter

    UntypedStateMachineBuilder builder = new UntypedStateMachineImporter().importDefinition(scxmlDef);
    ATMStateMachine stateMachine = builder.newAnyStateMachine(ATMState.Idle);
    
  • Save/Load State Machine Data

    你可以保存state machine的数据,当state machine处于idle状态时

    StateMachineData.Reader<MyStateMachine, MyState, MyEvent, MyContext>
        savedData = stateMachine.dumpSavedData();
    

    可以将保存的data 加载到另一个已经终止或者刚初始化的state machine中

    newStateMachineInstance.loadSavedData(savedData);
    

    Note

    State machine data 可以序列化或反序列化为Base64,通过ObjectSerializableSupport

  • State Machine Configuration

    当创建新的state machine 实例时,你可以通过StateMachineConfiguration配置其行为,如

    UntypedStateMachine fsm = builder.newUntypedStateMachine("a",
         StateMachineConfiguration.create().enableAutoStart(false)
                .setIdProvider(IdProvider.UUIDProvider.getInstance()),
         new Object[0]); // since 0.3.0
    fsm.fire(TestEvent.toA);
    

    上面的示例创建的state machine实例,UUID作为其identifier,并禁用了auto start。StateMachineConfigure 也可以设置在state machine builder上,意味着 所有通过builder.newStateMachine(S initialStateId)或者builder.newStateMachine(S initialStateId, Object... extraParams)创建的state machine 实例都会使用该配置。

  • State Machine Diagnose

    StateMachineLogger可用来观察state machine 内部的运行状态,如执行效率,action调用顺序,transition进度等等

    StateMachine<?,?,?,?> stateMachine = builder.newStateMachine(HState.A);
    StateMachineLogger fsmLogger = new StateMachineLogger(stateMachine);
    fsmLogger.startLogging();
    ...
    stateMachine.fire(HEvent.B2A, 1);
    ...
    fsmLogger.terminateLogging();
    -------------------------------------------------------------------------------------------
    Console Log:
    HierachicalStateMachine: Transition from "B2a" on "B2A" with context "1" begin.
    Before execute method call action "leftB2a" (1 of 6).
    Before execute method call action "exitB2" (2 of 6).
    ...
    Before execute method call action "entryA1" (6 of 6).
    HierachicalStateMachine: Transition from "B2a" to "A1" on "B2A" complete which took 2ms.
    ...
    

    v0.3.0起,可以使用将StateMachineConfiguration的debug设为true这种更简单的方式。

    StateMachine<?,?,?,?> stateMachine = builder.newStateMachine(HState.A,
            StateMachineConfiguration.create().enableDebugMode(true),
            new Object[0]);
    

    StateMachinePerformanceMonitor可以用来监视state machine执行效率信息,包括总transition时间,平均transition时间等等

    final UntypedStateMachine fsm = builder.newStateMachine("D");
    final StateMachinePerformanceMonitor performanceMonitor =
                new StateMachinePerformanceMonitor("Sample State Machine Performance Info");
    fsm.addDeclarativeListener(performanceMonitor);
    for (int i = 0; i < 10000; i++) {
        fsm.fire(FSMEvent.ToA, 10);
        fsm.fire(FSMEvent.ToB, 10);
        fsm.fire(FSMEvent.ToC, 10);
        fsm.fire(FSMEvent.ToD, 10);
    }
    fsm.removeDeclarativeListener(performanceMonitor);
    System.out.println(performanceMonitor.getPerfModel());
    -------------------------------------------------------------------------------------------
    Console Log:
    ========================== Sample State Machine Performance Info ==========================
    Total Transition Invoked: 40000
    Total Transition Failed: 0
    Total Transition Declained: 0
    Average Transition Comsumed: 0.0004ms
        Transition Key      Invoked Times   Average Time        Max Time    Min Time
        C--{ToD, 10}->D     10000           0.0007ms            5ms         0ms
        B--{ToC, 10}->C     10000           0.0001ms            1ms         0ms
        D--{ToA, 10}->A     10000           0.0009ms            7ms         0ms
        A--{ToB, 10}->B     10000           0.0000ms            1ms         0ms
    Total Action Invoked: 40000
    Total Action Failed: 0
    Average Action Execution Comsumed: 0.0000ms
        Action Key          Invoked Times   Average Time        Max Time    Min Time
        instan...Test$1     40000           0.0000ms            1ms         0ms
    ========================== Sample State Machine Performance Info ==========================
    
  • Timed State

    Timed state 是当进入该state后,可以延迟或周期性地触发指定的event。Timed task会被提交到ScheduledExecutorService。你可以将你的ScheduledExecutorService通过SquirrelSingletonProvider进行注册。如

    ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    SquirrelSingletonProvider.getInstance().register(ScheduledExecutorService.class, scheduler);
    

    如果没有ScheduledExecutorService进行注册,SquirrelConfiguration会提供一个默认的。Timed state 可通过state machine builder进行注册。如

    // after 50ms delay fire event "FIRST" every 100ms with null context
    builder.defineTimedState("A", 50, 100, "FIRST", null);
    builder.internalTransition().within("A").on("FIRST");
    

    Note:请确保在描述timed state的transition或entry/exit action之前就已经对其进行了定义。timeInterval等于小于0的只在intialDelay的时候执行一次。