All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.squirrelframework.foundation.fsm.impl.StateMachineBuilderImpl Maven / Gradle / Ivy

package org.squirrelframework.foundation.fsm.impl;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.squirrelframework.foundation.component.SquirrelInstanceProvider;
import org.squirrelframework.foundation.component.SquirrelPostProcessor;
import org.squirrelframework.foundation.component.SquirrelPostProcessorProvider;
import org.squirrelframework.foundation.component.SquirrelProvider;
import org.squirrelframework.foundation.exception.SquirrelRuntimeException;
import org.squirrelframework.foundation.fsm.*;
import org.squirrelframework.foundation.fsm.annotation.*;
import org.squirrelframework.foundation.fsm.builder.*;
import org.squirrelframework.foundation.fsm.jmx.ManagementService;
import org.squirrelframework.foundation.util.DuplicateChecker;
import org.squirrelframework.foundation.util.ReflectUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicReference;

public class StateMachineBuilderImpl, S, E, C> implements StateMachineBuilder {

    static {
        DuplicateChecker.checkDuplicate(StateMachineBuilder.class);
    }
    
    private static final Logger logger = LoggerFactory.getLogger(StateMachineBuilderImpl.class);
    
    private final Map> states = Maps.newConcurrentMap();
    
    private final Class stateMachineImplClazz;
    
    private final Class stateClazz;
    
    private final Class eventClazz;
    
    private final Class contextClazz;
    
    private boolean prepared = false;
    
    private final Constructor constructor;
    
    private final Method postConstructMethod;
    
    protected final Converter stateConverter;
    
    protected final Converter eventConverter;
    
    private final Class[] methodCallParamTypes;
    
    private Map stateAliasToDescription = null;
    
    private final MvelScriptManager scriptManager;
    
    private E startEvent, finishEvent, terminateEvent;
    
    private final ExecutionContext executionContext;
    
    private final List> deferBoundActionInfoList = Lists.newArrayList();
    
    private boolean isScanAnnotations = true;

    private final Class[] extraParamTypes;

    private StateMachineConfiguration defaultConfiguration = StateMachineConfiguration.getInstance();

    private ManagementService managementService;
    
    @SuppressWarnings("unchecked")
    private StateMachineBuilderImpl(Class stateMachineImplClazz, Class stateClazz, 
            Class eventClazz, Class contextClazz, Class... extraParamTypes) {
        Preconditions.checkArgument(isInstantiableType(stateMachineImplClazz), "The state machine class \""
                + stateMachineImplClazz.getName() + "\" cannot be instantiated.");
        Preconditions.checkArgument(isStateMachineType(stateMachineImplClazz), 
            "The implementation class of state machine \"" + stateMachineImplClazz.getName() + 
            "\" must be extended from AbstractStateMachine.class.");
        
        this.stateMachineImplClazz = stateMachineImplClazz;
        this.extraParamTypes = extraParamTypes!=null ? extraParamTypes : new Class[0];
        
        StateMachineParameters genericsParameters = findAnnotation(StateMachineParameters.class);
        if(stateClazz==Object.class && genericsParameters!=null) {
            this.stateClazz = (Class) genericsParameters.stateType();
        } else {
            this.stateClazz = stateClazz;
        }
        if(eventClazz==Object.class && genericsParameters!=null) {
            this.eventClazz = (Class) genericsParameters.eventType();
        } else {
            this.eventClazz = eventClazz;
        }
        if(contextClazz==Object.class && genericsParameters!=null) {
            this.contextClazz = (Class) genericsParameters.contextType();
        } else {
            this.contextClazz = contextClazz;
        }
        
        this.stateConverter = ConverterProvider.INSTANCE.getConverter(this.stateClazz);
        this.eventConverter = ConverterProvider.INSTANCE.getConverter(this.eventClazz);
        this.scriptManager = SquirrelProvider.getInstance().newInstance(MvelScriptManager.class);
        
        boolean contextInsensitive = findAnnotation(ContextInsensitive.class)!=null;
        methodCallParamTypes = contextInsensitive ? 
                new Class[]{this.stateClazz, this.stateClazz, this.eventClazz} : 
                new Class[]{this.stateClazz, this.stateClazz, this.eventClazz, this.contextClazz};
        
        Constructor fsmConstructor;
        try {
            fsmConstructor = ReflectUtils.getConstructor(stateMachineImplClazz, this.extraParamTypes);
        } catch(Exception e1) {
            try {
                fsmConstructor = ReflectUtils.getConstructor(stateMachineImplClazz, new Class[0]);
            } catch(Exception e2) {
                throw new IllegalArgumentException("Cannot find matched constructor for \'"+stateMachineImplClazz.getName()+"\'.");
            }
        }
        this.constructor = fsmConstructor;
        
        Method postInit = null;
        try {
            postInit = ReflectUtils.getMethod(stateMachineImplClazz, "postConstruct", this.extraParamTypes);
        } catch (Exception e) {}
        this.postConstructMethod = postInit;
        
        this.executionContext = new ExecutionContext(scriptManager, stateMachineImplClazz, methodCallParamTypes);
        // after initialized state machine builder
        defineContextEvent();
    }
    
    private void defineContextEvent() {
        ContextEvent contextEvent = findAnnotation(ContextEvent.class);
        if(contextEvent!=null) {
            Preconditions.checkState(eventConverter!=null, "Do not register event converter");
            if(!contextEvent.startEvent().isEmpty()) {
                defineStartEvent(eventConverter.convertFromString(contextEvent.startEvent()));
            }
            if(!contextEvent.finishEvent().isEmpty()) {
                defineFinishEvent(eventConverter.convertFromString(contextEvent.finishEvent()));
            }
            if(!contextEvent.terminateEvent().isEmpty()) {
                defineTerminateEvent(eventConverter.convertFromString(contextEvent.terminateEvent()));
            }
        }
    }
    
    private  M findAnnotation(final Class annotationClass) {
        final AtomicReference genericsParametersRef = new AtomicReference();;
        walkThroughStateMachineClass(new Function, Boolean>() {
            @Override
            public Boolean apply(Class input) {
                M anno = input.getAnnotation(annotationClass);
                if(anno!=null) {
                    genericsParametersRef.set(anno);
                    return false;
                }
                return true;
            }
        });
        M genericsParameters = genericsParametersRef.get();
        return genericsParameters;
    }
    
    private void checkState() {
        if(prepared) {
            throw new IllegalStateException("The state machine builder has been freezed and " +
                    "cannot be changed anymore.");
        }
    }
    
    @Override
    public ExternalTransitionBuilder externalTransition() {
        checkState();
        return externalTransition(TransitionPriority.NORMAL);
    }

    @Override
    public MultiTransitionBuilder externalTransitions() {
        checkState();
        return transitions(TransitionPriority.NORMAL);
    }
    
    @Override
    public ExternalTransitionBuilder transition() {
        checkState();
        return externalTransition(TransitionPriority.NORMAL);
    }

    @Override
    public MultiTransitionBuilder transitions() {
        checkState();
        return transitions(TransitionPriority.NORMAL);
    }

    @Override
    public DeferBoundActionBuilder transit() {
        checkState();
        return FSM.newDeferBoundActionBuilder(deferBoundActionInfoList, executionContext);
    }
    
    @Override
    public LocalTransitionBuilder localTransition() {
        checkState();
        return localTransition(TransitionPriority.NORMAL);
    }

    @Override
    public MultiTransitionBuilder localTransitions() {
        checkState();
        return localTransitions(TransitionPriority.NORMAL);
    }

    @Override
    public InternalTransitionBuilder internalTransition() {
        checkState();
        return internalTransition(TransitionPriority.NORMAL);
    }

    @Override
    public ExternalTransitionBuilder externalTransition(int priority) {
        checkState();
        return FSM.newExternalTransitionBuilder(states, priority, executionContext);
    }

    @Override
    public MultiTransitionBuilder externalTransitions(int priority) {
        checkState();
        return transitions(priority);
    }

    @Override
    public ExternalTransitionBuilder transition(int priority) {
        checkState();
        return externalTransition(priority);
    }

    @Override
    public MultiTransitionBuilder transitions(int priority) {
        checkState();
        return FSM.newMultiTransitionBuilder(states, TransitionType.EXTERNAL, priority, executionContext);
    }
    
    @Override
    public LocalTransitionBuilder localTransition(int priority) {
        checkState();
        return FSM.newLocalTransitionBuilder(states, priority, executionContext);
    }

    @Override
    public MultiTransitionBuilder localTransitions(int priority) {
        checkState();
        return FSM.newMultiTransitionBuilder(states, TransitionType.LOCAL, priority, executionContext);
    }

    @Override
    public InternalTransitionBuilder internalTransition(int priority) {
        checkState();
        return FSM.newInternalTransitionBuilder(states, priority, executionContext);
    }
    
    private void addStateEntryExitMethodCallAction(String methodName, Class[] parameterTypes, 
            MutableState mutableState, boolean isEntryAction) {
        Method method = findMethodCallActionInternal(stateMachineImplClazz, methodName, parameterTypes);
        if(method!=null) {
            int weight = Action.EXTENSION_WEIGHT;
            if(methodName.startsWith("before")) {
                weight = Action.BEFORE_WEIGHT;
            } else if(methodName.startsWith("after")) {
                weight = Action.AFTER_WEIGHT;
            }
            Action methodCallAction = FSM.newMethodCallAction(method, weight, executionContext);
            if(isEntryAction) {
                mutableState.addEntryAction(methodCallAction);
            } else {
                mutableState.addExitAction(methodCallAction);
            }
        }
    }
    
    private void addTransitionMethodCallAction(String methodName, Class[] parameterTypes, 
            MutableTransition mutableTransition) {
        Method method = findMethodCallActionInternal(stateMachineImplClazz, methodName, parameterTypes);
        if(method!=null) {
            Action methodCallAction = FSM.newMethodCallAction(method, Action.EXTENSION_WEIGHT, executionContext);
            mutableTransition.addAction(methodCallAction);
        }
    }
    
    private boolean isDeferBoundAction(Transit transit) {
        return "*".equals(transit.from()) || "*".equals(transit.to()) || "*".equals(transit.on());
    }
    
    private Action warpConditionalAction(Action action, final Condition condition) {
        return new ActionWrapper(action) {
            @Override
            public void execute(S from, S to, E event, C context, T stateMachine) {
                if(condition.isSatisfied(context)) {
                    super.execute(from, to, event, context, stateMachine);
                }
            }
        };
    }
    
    private void buildDeferBoundAction(Transit transit) {
        S from = "*".equals(transit.from()) ? null : 
            stateConverter.convertFromString(parseStateId(transit.from()));
        S to  = "*".equals(transit.to()) ? null :
            stateConverter.convertFromString(parseStateId(transit.to()));
        E event = "*".equals(transit.on()) ? null :
            eventConverter.convertFromString(transit.on());
        DeferBoundActionInfo deferBoundActionInfo = 
                new DeferBoundActionInfo(from, to, event);
        if(!transit.callMethod().isEmpty()) {
            Action action = FSM.newMethodCallActionProxy(transit.callMethod(), executionContext);
            if(!transit.whenMvel().isEmpty()) {
                final Condition condition = FSM.newMvelCondition(transit.whenMvel(), scriptManager);
                action = warpConditionalAction(action, condition);
            }
            if(transit.when()!=Conditions.Always.class) {
                @SuppressWarnings("unchecked")
                final Condition condition = ReflectUtils.newInstance(transit.when());
                action = warpConditionalAction(action, condition);
            }
            deferBoundActionInfo.setActions(Collections.singletonList(action));
        }
        
        deferBoundActionInfoList.add(deferBoundActionInfo);
    }
    
    @SuppressWarnings("unchecked")
    private void buildDeclareTransition(Transit transit) {
        if(transit==null) return;
        
        Preconditions.checkState(stateConverter!=null, "Do not register state converter");
        Preconditions.checkState(eventConverter!=null, "Do not register event converter");
        
        // if not explicit specify 'from', 'to' and 'event', it is declaring a defer bound action.
        if(isDeferBoundAction(transit)) {
            buildDeferBoundAction(transit);
            return;
        }
        
        Preconditions.checkArgument(isInstantiableType(transit.when()), 
                "Condition \'when\' should be concrete class or static inner class.");
        Preconditions.checkArgument(
                transit.type()!=TransitionType.INTERNAL || transit.from().equals(transit.to()),
                "Internal transition must transit to the same source state.");
        
        S fromState = stateConverter.convertFromString(parseStateId(transit.from()));
        Preconditions.checkNotNull(fromState, "Cannot convert state of name \""+fromState+"\".");
        S toState = stateConverter.convertFromString(parseStateId(transit.to()));
        E event = eventConverter.convertFromString(transit.on());
        Preconditions.checkNotNull(event, "Cannot convert event of name \""+event+"\".");
        
        // check exited transition which satisfied the criteria
        if(states.get(fromState)!=null) {
            MutableState theFromState = states.get(fromState);
            for(ImmutableTransition t : theFromState.getAllTransitions()) {
                if(t.isMatch(fromState, toState, event, transit.priority(), transit.when(), transit.type())) {
                    MutableTransition mutableTransition = (MutableTransition)t;
                    String callMethodExpression = transit.callMethod();
                    if(callMethodExpression!=null && callMethodExpression.length()>0) {
                        Action methodCallAction = FSM.newMethodCallActionProxy(callMethodExpression, executionContext);
                        mutableTransition.addAction(methodCallAction);
                    }
                    return;
                }
            }
        }
        
        // if no existed transition is matched then create a new transition
        final To toBuilder;
        if(transit.type()==TransitionType.INTERNAL) {
            InternalTransitionBuilder transitionBuilder =
                    FSM.newInternalTransitionBuilder(states, transit.priority(), executionContext);
            toBuilder = transitionBuilder.within(fromState);
        } else {
            ExternalTransitionBuilder transitionBuilder = (transit.type()==TransitionType.LOCAL) ?
                    FSM.newLocalTransitionBuilder(states, transit.priority(), executionContext) :
                        FSM.newExternalTransitionBuilder(states, transit.priority(), executionContext);
            From fromBuilder = transitionBuilder.from(fromState);
            boolean isTargetFinal = transit.isTargetFinal() || FSM.getState(states, toState).isFinalState();
            toBuilder = isTargetFinal ? fromBuilder.toFinal(toState) : fromBuilder.to(toState);
        } 
        On onBuilder = toBuilder.on(event);
        Condition c = null;
        try {
            if(transit.when()!=Conditions.Always.class) {
                Constructor constructor = transit.when().getDeclaredConstructor();
                constructor.setAccessible(true);
                c = (Condition)constructor.newInstance();
            } else if(StringUtils.isNotEmpty(transit.whenMvel())) {
                c = FSM.newMvelCondition(transit.whenMvel(), scriptManager);
            }
        } catch (Exception e) {
            logger.error("Instantiate Condition \""+transit.when().getName()+"\" failed.");
            c = Conditions.never();
        } 
        When whenBuilder = c!=null ? onBuilder.when(c) : onBuilder;
        
        if(!Strings.isNullOrEmpty(transit.callMethod())) {
            Action methodCallAction = FSM.newMethodCallActionProxy(transit.callMethod(), executionContext);
            whenBuilder.perform(methodCallAction);
        }
    }
    
    private String parseStateId(String value) {
        return (value!=null && value.startsWith("#")) ? 
                stateAliasToDescription.get(value.substring(1)) : value;
    }
    
    private void buildDeclareState(State state) {
        if(state==null) return;
        
        Preconditions.checkState(stateConverter!=null, "Do not register state converter");
        S stateId = stateConverter.convertFromString(state.name());
        Preconditions.checkNotNull(stateId, "Cannot convert state of name \""+state.name()+"\".");
        MutableState newState = defineState(stateId);
        newState.setCompositeType(state.compositeType());
        if(!newState.isParallelState()) {
            newState.setHistoryType(state.historyType());
        }
        newState.setFinal(state.isFinal());
        
        if(!Strings.isNullOrEmpty(state.parent())) {
            S parentStateId = stateConverter.convertFromString(parseStateId(state.parent()));
            MutableState parentState = defineState(parentStateId);
            newState.setParentState(parentState);
            parentState.addChildState(newState);
            if(!parentState.isParallelState() && state.initialState()) {
                parentState.setInitialState(newState);
            }
        }
        
        if(!Strings.isNullOrEmpty(state.entryCallMethod())) {
            Action methodCallAction = FSM.newMethodCallActionProxy(state.entryCallMethod(), executionContext);
            onEntry(stateId).perform(methodCallAction);
        }
        
        if(!Strings.isNullOrEmpty(state.exitCallMethod())) {
            Action methodCallAction = FSM.newMethodCallActionProxy(state.exitCallMethod(), executionContext);
            onExit(stateId).perform(methodCallAction);
        }
        rememberStateAlias(state);
    }
    
    private void rememberStateAlias(State state) {
        if(Strings.isNullOrEmpty(state.alias())) return;
        if(stateAliasToDescription==null) 
            stateAliasToDescription=Maps.newHashMap();
        if(!stateAliasToDescription.containsKey(state.alias())) {
            stateAliasToDescription.put(state.alias(), state.name());
        } else {
            throw new RuntimeException("Cannot define duplicate state alias \""+
                    state.alias()+"\" for state \""+state.name()+"\" and "+
                    stateAliasToDescription.get(state.alias())+"\".");
        }
    }
    
    private void walkThroughStateMachineClass(Function, Boolean> func) {
        Stack> stack = new Stack>();
        stack.push(stateMachineImplClazz);
        while(!stack.isEmpty()) {
            Class k = stack.pop();
            boolean isContinue = func.apply(k);
            if(!isContinue) break;
            for(Class i : k.getInterfaces()) {
                if(isStateMachineInterface(i)) {stack.push(i);}
            }
            if(isStateMachineType(k.getSuperclass())) {
                stack.push(k.getSuperclass());
            }
        }
    }
    
    private void verifyStateMachineDefinition() {
        for(MutableState state : states.values()) {
            state.verify();
        }
    }
    
    private void installDeferBoundActions() {
        if(deferBoundActionInfoList.isEmpty()) 
            return;
        for(DeferBoundActionInfo deferBoundActionInfo : deferBoundActionInfoList) {
            installDeferBoundAction(deferBoundActionInfo);
        }
    }
    
    private void installDeferBoundAction(DeferBoundActionInfo deferBoundActionInfo) {
        for(MutableState mutableState : states.values()) {
            if(!deferBoundActionInfo.isFromStateMatch(mutableState.getStateId())) {
                continue;
            }
            for(ImmutableTransition transition : mutableState.getAllTransitions()) {
                if(deferBoundActionInfo.isToStateMatch(transition.getTargetState().getStateId())
                        && deferBoundActionInfo.isEventStateMatch(transition.getEvent())) {
                    MutableTransition mutableTransition = (MutableTransition)transition;
                    mutableTransition.addActions(deferBoundActionInfo.getActions());
                }
            }
        }
    }
    
    private synchronized void prepare() {
        if(prepared) return;
        
        if(isScanAnnotations) {
            // 1. install all the declare states, states must be installed before installing transition and extension methods
            walkThroughStateMachineClass(new DeclareStateFunction());
            // 2. install all the declare transitions
            walkThroughStateMachineClass(new DeclareTransitionFunction());
            // 2.5 install all the defer bound actions
            installDeferBoundActions();
        }
        // 3. install all the extension method call when state machine builder freeze
        installExtensionMethods();
        // 4. prioritize transitions
        prioritizeTransitions();
        // 5. install final state actions
        installFinalStateActions();
        // 6. verify correctness of state machine
        verifyStateMachineDefinition();
        // 7. proxy untyped states
        proxyUntypedStates();
        prepared = true;
    }
    
    @SuppressWarnings("unchecked")
    private void proxyUntypedStates() {
        if(UntypedStateMachine.class.isAssignableFrom(stateMachineImplClazz)) {
            Map> untypedStates = Maps.newHashMap();
            for(final MutableState state : states.values()) {
                UntypedMutableState untypedState = (UntypedMutableState) Proxy.newProxyInstance(
                        UntypedMutableState.class.getClassLoader(), 
                        new Class[]{UntypedMutableState.class, UntypedImmutableState.class}, 
                        new InvocationHandler() {
                            @Override
                            public Object invoke(Object proxy, Method method, Object[] args)
                                    throws Throwable {
                                if (method.getName().equals("getStateId")) {
                                    return state.getStateId();
                                } else if(method.getName().equals("getThis")) {
                                    return state.getThis();
                                } else if(method.getName().equals("equals")) {
                                    return state.equals(args[0]);
                                } else if(method.getName().equals("hashCode")) {
                                    return state.hashCode();
                                } 
                                return method.invoke(state, args);
                            }
                        });
                untypedStates.put(state.getStateId(), MutableState.class.cast(untypedState));
            }
            states.clear();
            states.putAll(untypedStates);
        }
    }
    
    private String[] getEntryExitStateMethodNames(ImmutableState state, boolean isEntry) {
        String prefix = (isEntry ? "entry" : "exit");
        String postfix = (isEntry ? "EntryAny" : "ExitAny");
        
        return new String[]{
                "before" + postfix,
                prefix + ((stateConverter!=null && !state.isFinalState()) ? 
                stateConverter.convertToString(state.getStateId()) : StringUtils.capitalize(state.toString())),
                "after" + postfix
        };
    }
    
    private String[] getTransitionMethodNames(ImmutableTransition transition) {
        ImmutableState fromState = transition.getSourceState();
        ImmutableState toState = transition.getTargetState();
        E event = transition.getEvent();
        String fromStateName = stateConverter!=null ? stateConverter.convertToString(fromState.getStateId()) : 
            StringUtils.capitalize(fromState.toString());
        String toStateName = (stateConverter!=null && !toState.isFinalState()) ? 
                stateConverter.convertToString(toState.getStateId()) : StringUtils.capitalize(toState.toString());
        String eventName = eventConverter!=null ? eventConverter.convertToString(event) : 
            StringUtils.capitalize(event.toString());
        String conditionName = transition.getCondition().name();
        
        return new String[] { 
                "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
        };
    }
    
    private void installExtensionMethods() {
        for(MutableState state : states.values()) {
            // Ignore all the transition start from a final state
            if(state.isFinalState()) continue;
            
            // state exit extension method
            String[] exitMethodCallCandidates = getEntryExitStateMethodNames(state, false);
            for (String exitMethodCallCandidate : exitMethodCallCandidates) {
                addStateEntryExitMethodCallAction(exitMethodCallCandidate,
                        methodCallParamTypes, state, false);
            }
            
            // transition extension methods
            for(ImmutableTransition transition : state.getAllTransitions()) {
                String[] transitionMethodCallCandidates = getTransitionMethodNames(transition);
                for (String transitionMethodCallCandidate : transitionMethodCallCandidates) {
                    addTransitionMethodCallAction(transitionMethodCallCandidate, methodCallParamTypes,
                            (MutableTransition) transition);
                }
            }
            
            // state entry extension method
            String[] entryMethodCallCandidates = getEntryExitStateMethodNames(state, true);
            for (String entryMethodCallCandidate : entryMethodCallCandidates) {
                addStateEntryExitMethodCallAction(entryMethodCallCandidate,
                        methodCallParamTypes, state, true);
            }
        }
    }
    
    private void prioritizeTransitions() {
        for(MutableState state : states.values()) {
            if(state.isFinalState()) continue;
            state.prioritizeTransitions();
        }
    }
    
    private void installFinalStateActions() {
        for(MutableState state : states.values()) {
            if(!state.isFinalState()) continue;
            // defensive code: final state cannot be exited anymore
            state.addExitAction(new AnonymousAction() {
                @Override
                public void execute(S from, S to, E event, C context, T stateMachine) {
                    throw new IllegalStateException("Final state cannot be exited anymore.");
                }
                
                @Override
                public String name() {
                    return "__FINAL_STATE_ACTION_GUARD";
                }
            });
            
        }
    }
    
    private boolean isInstantiableType(Class type) {
        return type != null && !type.isInterface() && !Modifier.isAbstract(type.getModifiers()) &&
                ( type.getEnclosingClass() == null || Modifier.isStatic(type.getModifiers()) );
    }
    
    private boolean isStateMachineType(Class stateMachineClazz) {
        return stateMachineClazz!= null && AbstractStateMachine.class != stateMachineClazz &&
                AbstractStateMachine.class.isAssignableFrom(stateMachineClazz);
    }
    
    private boolean isStateMachineInterface(Class stateMachineClazz) {
        return stateMachineClazz!= null && stateMachineClazz.isInterface() && 
                StateMachine.class.isAssignableFrom(stateMachineClazz);
    }
    
    private static Method searchMethod(Class targetClass, Class superClass, 
            String methodName, Class[] parameterTypes) {
        if(superClass.isAssignableFrom(targetClass)) {
            Class clazz = targetClass;
            while(!superClass.equals(clazz)) {
                try {
                    return clazz.getDeclaredMethod(methodName, parameterTypes);
                } catch (NoSuchMethodException e) {
                    clazz = clazz.getSuperclass();
                }
            }
        }
        return null;
    }
    
    static Method findMethodCallActionInternal(Class target, String methodName, Class[] parameterTypes) {
        return searchMethod(target, AbstractStateMachine.class, methodName, parameterTypes);
    }
    
    @Override
    public T newStateMachine(S initialStateId) {
        return newStateMachine(initialStateId, new Object[0]);
    }
    
    @Override
    public T newStateMachine(S initialStateId, Object... extraParams) {
        return newStateMachine(initialStateId, defaultConfiguration, extraParams);
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public T newStateMachine(S initialStateId, StateMachineConfiguration configuration, Object... extraParams) {
        if(!prepared) prepare();
        if(!isValidState(initialStateId)) {
            throw new IllegalArgumentException(getClass()+" cannot find Initial state \'"+ 
                    initialStateId+"\' in state machine.");
        }
        
        Class[] constParamTypes = constructor.getParameterTypes();
        final T stateMachine;
        try {
            if(constParamTypes==null || constParamTypes.length==0) {
                stateMachine = ReflectUtils.newInstance(constructor);
            } else { 
                stateMachine = ReflectUtils.newInstance(constructor, extraParams);
            }
        } catch(SquirrelRuntimeException e) {
            throw new IllegalStateException(
                    "New state machine instance failed.", e.getTargetException());
        }
                
        final AbstractStateMachine stateMachineImpl = (AbstractStateMachine)stateMachine;
        stateMachineImpl.prePostConstruct(initialStateId, states, configuration, new Runnable() {
            @Override
            public void run() {
                stateMachineImpl.setStartEvent(startEvent);
                stateMachineImpl.setFinishEvent(finishEvent);
                stateMachineImpl.setTerminateEvent(terminateEvent);
                stateMachineImpl.setExtraParamTypes(extraParamTypes);
                
                stateMachineImpl.setTypeOfStateMachine(stateMachineImplClazz);
                stateMachineImpl.setTypeOfState(stateClazz);
                stateMachineImpl.setTypeOfEvent(eventClazz);
                stateMachineImpl.setTypeOfContext(contextClazz);
                stateMachineImpl.setScriptManager(scriptManager);
            }
        });
        
        if(postConstructMethod!=null && extraParamTypes.length==extraParams.length) {
            try {
                ReflectUtils.invoke(postConstructMethod, stateMachine, extraParams);
            } catch(SquirrelRuntimeException e) {
                throw new IllegalStateException(
                        "Invoke state machine postConstruct method failed.", e.getTargetException());
            }
        } 
        postProcessStateMachine((Class)stateMachineImplClazz, stateMachine);
        
        if(configuration.isRemoteMonitorEnabled()) {
            getManagementService().register(stateMachine);
        }
        return stateMachine;
    }

    private ManagementService getManagementService() {
        if(managementService==null) {
            managementService = new ManagementService();
        }
        return managementService;
    }

    private boolean isValidState(S initialStateId) {
        return initialStateId!=null && states.get(initialStateId) != null;
    }
    
    private T postProcessStateMachine(Class clz, T component) {
        if(component!=null) {
            List> postProcessors = 
                    SquirrelPostProcessorProvider.getInstance().getCallablePostProcessors(clz);
            for(SquirrelPostProcessor postProcessor : postProcessors) {
                postProcessor.postProcess(component);
            }
        }
        return component;
    }
    
    @Override
    public MutableState defineState(S stateId) {
        checkState();
        return FSM.getState(states, stateId);
    }
    
    @Override
    public MutableState defineFinalState(S stateId) {
        checkState();
        MutableState newState = defineState(stateId);
        newState.setFinal(true);
        return newState;
    }
    
    @Override
    public MutableState defineLinkedState(final S stateId, 
            final StateMachineBuilder, S, E, C> linkedStateMachineBuilder, 
            final S initialLinkedState, final Object... extraParams) {
        checkState();
        MutableState state = states.get(stateId);
        if(state==null) {
            MutableLinkedState linkedState = FSM.newLinkedState(stateId);
            SquirrelInstanceProvider> provider = new SquirrelInstanceProvider>() {
                @Override
                public StateMachine get() {
                    return linkedStateMachineBuilder.newStateMachine(initialLinkedState, extraParams);
                }
            };
            linkedState.setLinkedStateMachineProvider(provider);
            states.put(stateId, linkedState);
            state = linkedState;
        }
        return state;
    }
    
    @Override
    public MutableState defineTimedState(S stateId,
            long initialDelay, long timeInterval, E autoEvent, C autoContext) {
        checkState();
        MutableState state = states.get(stateId);
        if(state==null) {
            MutableTimedState timedState = FSM.newTimedState(stateId);
            timedState.setAutoFireContext(autoContext);
            timedState.setAutoFireEvent(autoEvent);
            timedState.setInitialDelay(initialDelay);
            timedState.setTimeInterval(timeInterval);
            states.put(stateId, timedState);
            state = timedState;
        }
        return state;
    }

    @Override
    public void defineSequentialStatesOn(S parentStateId, S... childStateIds) {
        checkState();
        defineSequentialStatesOn(parentStateId, HistoryType.NONE, childStateIds);
    }

    @Override
    public void defineNoInitSequentialStatesOn(S parentStateId, S... childStateIds) {
        checkState();
        defineNoInitSequentialStatesOn(parentStateId, HistoryType.NONE, childStateIds);
    }

    @Override
    public void defineNoInitSequentialStatesOn(S parentStateId, HistoryType historyType, S... childStateIds) {
        checkState();
        defineChildStatesOn(parentStateId, StateCompositeType.SEQUENTIAL, historyType, true, childStateIds);
    }
    
    @Override
    public void defineSequentialStatesOn(S parentStateId, HistoryType historyType, S... childStateIds) {
        checkState();
        defineChildStatesOn(parentStateId, StateCompositeType.SEQUENTIAL, historyType, false, childStateIds);
    }
    
    @Override
    public void defineParallelStatesOn(S parentStateId, S... childStateIds) {
        checkState();
        defineChildStatesOn(parentStateId, StateCompositeType.PARALLEL, HistoryType.NONE, true, childStateIds);
    }

    private void defineChildStatesOn(S parentStateId, StateCompositeType compositeType,
                                     HistoryType historyType, boolean ignoreInitialState, S... childStateIds) {
        checkState();
        if(childStateIds!=null && childStateIds.length>0) {
            MutableState parentState = FSM.getState(states, parentStateId);
            parentState.setCompositeType(compositeType);
            parentState.setHistoryType(historyType);
            for(int i=0, size=childStateIds.length; i childState = FSM.getState(states, childStateIds[i]);
                if(!ignoreInitialState && i==0) {
                    parentState.setInitialState(childState);
                }
                childState.setParentState(parentState);
                parentState.addChildState(childState);
            }
        }
    }

    @Override
    public EntryExitActionBuilder onEntry(S stateId) {
        checkState();
        MutableState state = FSM.getState(states, stateId);
        return FSM.newEntryExitActionBuilder(state, true, executionContext);
    }

    @Override
    public EntryExitActionBuilder onExit(S stateId) {
        checkState();
        MutableState state = FSM.getState(states, stateId);
        return FSM.newEntryExitActionBuilder(state, false, executionContext);
    }

    private class DeclareTransitionFunction implements Function, Boolean> {
        @Override
        public Boolean apply(Class k) {
            buildDeclareTransition(k.getAnnotation(Transit.class));
            Transitions transitions = k.getAnnotation(Transitions.class);
            if(transitions!=null && transitions.value()!=null) {
                for(Transit t : transitions.value()) {
                    StateMachineBuilderImpl.this.buildDeclareTransition(t);
                }
            }
            return true;
        }
    }
    
    private class DeclareStateFunction implements Function, Boolean> {
        @Override
        public Boolean apply(Class k) {
            buildDeclareState(k.getAnnotation(State.class));
            States states = k.getAnnotation(States.class);
            if(states!=null && states.value()!=null) {
                for(State s : states.value()) {
                    StateMachineBuilderImpl.this.buildDeclareState(s);
                }
            }
            return true;
        }
    }

    @Override
    public void defineFinishEvent(E finishEvent) {
        checkState();
        this.finishEvent = finishEvent;
    }

    @Override
    public void defineStartEvent(E startEvent) {
        checkState();
        this.startEvent = startEvent;
    }

    @Override
    public void defineTerminateEvent(E terminateEvent) {
        checkState();
        this.terminateEvent = terminateEvent;
    }
    
    void setScanAnnotations(boolean isScanAnnotations) {
        this.isScanAnnotations = isScanAnnotations;
    }

    @Override
    public void setStateMachineConfiguration(StateMachineConfiguration configure) {
        checkState();
        this.defaultConfiguration = configure;
    }
}