spring-projects / spring-statemachine

Spring Statemachine is a framework for application developers to use state machine concepts with Spring.
1.51k stars 598 forks source link

UmlStateMachineModelFactory doesn't resolve actions and guard by StateMachineComponentResolver #1114

Open maykon-oliveira opened 9 months ago

maykon-oliveira commented 9 months ago
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineModelConfigurer;
import org.springframework.statemachine.config.model.DefaultStateMachineComponentResolver;
import org.springframework.statemachine.config.model.StateMachineComponentResolver;
import org.springframework.statemachine.config.model.StateMachineModelFactory;
import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.uml.UmlStateMachineModelFactory;

@Configuration
@EnableStateMachine
public class StateMachineConfiguration extends StateMachineConfigurerAdapter<String, String> {
    @Override
    public void configure(StateMachineModelConfigurer<String, String> model) throws Exception {
        model.withModel().factory(modelFactory());
    }

    @Bean
    public StateMachineModelFactory<String, String> modelFactory() {
        UmlStateMachineModelFactory factory =
            new UmlStateMachineModelFactory("classpath:state-machine.uml");
       // actions and guards registered by DefaultStateMachineComponentResolver isn't resolved
        factory.setStateMachineComponentResolver(stateMachineComponentResolver());
        // if uncomment, it works
        // factory.registerGuard("myGuard", myGuard());
        return factory;
    }

    @Bean
    public StateMachineComponentResolver<String, String> stateMachineComponentResolver() {
        DefaultStateMachineComponentResolver<String, String> resolver =
            new DefaultStateMachineComponentResolver<>();
        resolver.registerGuard("myGuard", myGuard());
        return resolver;
    }

    public Guard<String, String> myGuard() {
        return context -> false;
    }
}

Running the application configured like I did before, I'm getting this error.

***************************
APPLICATION FAILED TO START
***************************

Description:

A component required a bean named 'myGuard' that could not be found.

Action:

Consider defining a bean named 'myGuard' in your configuration.

But, if you register the guard direct by UmlStateMachineModelFactory factory, it works well.

I think the problem is here AbstractStateMachineModelFactory#resolveAction(id).

public Action<S, E> resolveAction(String id) {
       // throw the exception. So this.stateMachineComponentResolver.resolveAction(id) isn't called.
        Action<S, E> a = this.internalResolver.resolveAction(id);
        if (a == null && this.stateMachineComponentResolver != null) {
            a = this.stateMachineComponentResolver.resolveAction(id);
        }

        if (a == null) {
            throw new RuntimeException("Can't resolve action with id " + id + " either from registered actions nor beanfactory");
        } else {
            return a;
        }
    }