1

次のステートマシンがあります。

@Configuration
@EnableStateMachineFactory
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<GameStep, GameEventType> {

    @Autowired
    private GameStateService gameService;

    @Autowired
    private WordsGameService wordsGameService;

    @Override
    public void configure(StateMachineStateConfigurer<GameStep, GameEventType> states) throws Exception {
        states.withStates().initial(GAME_STARTED, initGame())
                .state(WAITING_FOR_PLAYERS, Arrays.asList(initPlayers(), setPlayersConfirmedFalse()), Collections.singletonList(informPlayers()))
                .state(WORDS_STARTED, setPlayersConfirmedFalse(), informPlayers())
                .state(WORDS_ENDED, null, informPlayers())
                .state(EQUATIONS_STARTED, setPlayersConfirmedFalse(), informPlayers())
                .state(EQUATIONS_ENDED, null, informPlayers())
        ;
    }

    @Bean
    public Action<GameStep, GameEventType> initGame() {
        return context -> {
            GameState game = gameService.createNewGameState(null, null);
            context.getExtendedState().getVariables().put("game", game);
        };
    }

    @Bean
    public Action<GameStep, GameEventType> setPlayersConfirmedFalse() {
        return context -> {
            GameState game = (GameState) context.getExtendedState().getVariables().get("game");
            game.getFirstPlayer().setConfirmedToPlay(false);
            game.getSecondPlayer().setConfirmedToPlay(false);
        };
    }

    @Bean
    public Action<GameStep, GameEventType> initPlayers() {
        return context -> {
            PlayersJoinedMessage message = context.getMessageHeaders().get("message", PlayersJoinedMessage.class);
            GameState game = getGameFromHeader(context);
            game.setFirstPlayer(new Player(message.getFirstPlayer(), PlayerPosition.FIRST));
            game.setSecondPlayer(new Player(message.getSecondPlayer(), PlayerPosition.SECOND));
        };
    }

    private GameState getGameFromHeader(StateContext<GameStep, GameEventType> context) {
        return (GameState) context.getExtendedState().getVariables().get("game");
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<GameStep, GameEventType> transitions)
            throws Exception {
        transitions
                .withExternal().source(GAME_STARTED).target(WAITING_FOR_PLAYERS).event(PLAYERS_JOINED)
                .and()
                .withExternal().source(WAITING_FOR_PLAYERS).target(WORDS_STARTED).event(START_CONFIRMATION).guard(bothPlayersConfirmed())
                .and()
                .withExternal().source(WORDS_STARTED).target(WORDS_ENDED).event(SEND_WORD).guard(sendWord())
                .and()
                .withExternal().source(WORDS_STARTED).target(WORDS_ENDED).event(TIME_EXPIRED)
                .and()
                .withInternal().source(WORDS_STARTED).action(timerAction()).timerOnce(5000)
                .and()
                .withExternal().source(WORDS_ENDED).target(EQUATIONS_STARTED).event(TIME_EXPIRED)
                .and()
                .withInternal().source(WORDS_ENDED).action(timerAction()).timerOnce(5000)
                .and()
                .withExternal().source(EQUATIONS_STARTED).target(EQUATIONS_ENDED).event(SEND_EQUATION).guard(bothPlayersConfirmed())
                .and()
                .withExternal().source(EQUATIONS_STARTED).target(EQUATIONS_ENDED).event(TIME_EXPIRED)
                .and()
                .withInternal().source(EQUATIONS_STARTED).action(timerAction()).timerOnce(155000)
        //more games

        ;
    }

    @Bean
    public Guard<GameStep, GameEventType> sendWord() {
        return context -> {
            ChangeGameMessage message = context.getMessage().getHeaders().get("message", ChangeGameMessage.class);
            GameState gameState = getGameFromHeader(context);
            int points = wordsGameService.checkWord(gameState.getGame().getWordsGame(), message.getGameEvent().getMessage().split(","));
            Player player = gameState.getPlayerByName(message.getPlayer());
            player.setWordsPoints(points);

            return gameState.getFirstPlayer().getWordsPoints() != null && gameState.getSecondPlayer().getWordsPoints() != null;
        };
    }

    @Override
    public void configure(StateMachineConfigurationConfigurer<GameStep, GameEventType> config)
            throws Exception {
        config
                .withConfiguration()
                .autoStartup(true)
//                .taskExecutor(new SimpleAsyncTaskExecutor())
//                .taskScheduler(new ConcurrentTaskScheduler())
                .listener(listener());
    }

    @Bean
    public StateMachineListener<GameStep, GameEventType> listener() {
        return new StateMachineListenerAdapter<GameStep, GameEventType>() {
            @Override
            public void stateChanged(State<GameStep, GameEventType> from, State<GameStep, GameEventType> to) {
                System.out.println("State changed to " + to.getId());
            }
        };
    }

    @Bean
    public Guard<GameStep, GameEventType> bothPlayersConfirmed() {
        return context -> {
            ChangeGameMessage message = context.getMessage().getHeaders().get("message", ChangeGameMessage.class);
            GameState gameState = getGameFromHeader(context);
            Player player = gameState.getPlayerByName(message.getPlayer());
            player.setConfirmedToPlay(true);
            return gameState.getFirstPlayer().getConfirmedToPlay() && gameState.getSecondPlayer().getConfirmedToPlay();
        };
    }

    @Bean
    public Action<GameStep, GameEventType> timerAction() {
        return context -> {
            System.out.println("timer event");
//            informPlayers().execute(context);
            context.getStateMachine().sendEvent(TIME_EXPIRED);
        };
    }

    @Bean
    public Action<GameStep, GameEventType> informPlayers() {
        return new Action<GameStep, GameEventType>() {

            @Autowired
            private SimpMessagingTemplate messagingTemplate;

            @Override
            public void execute(StateContext<GameStep, GameEventType> context) {
                GameState game = context.getExtendedState().get("game", GameState.class);
                messagingTemplate.convertAndSendToUser(game.getFirstPlayer().getName(), "/queue/eureka", game);
                messagingTemplate.convertAndSendToUser(game.getSecondPlayer().getName(), "/queue/eureka", game);
            }
        };
    }
//
//    @Bean(name = StateMachineSystemConstants.TASK_EXECUTOR_BEAN_NAME)
//    public TaskExecutor taskExecutor() {
//        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
//        taskExecutor.setCorePoolSize(5);
//        return taskExecutor;
//    }
}

問題は、timerAction()トリガーされた場合、アクションinformPlayers()が中断されることです。ガードが true を返しinformPlayers()たときに状態が遷移する場合は正しく呼び出されるため、遷移の方法に問題があります。スタックトレース :bothPlayersConfirmed()timerOnce()

State changed to EQUATIONS_ENDED
timer event
2016-06-24 16:43:39.670 ERROR 83865 --- [pool-2-thread-1] o.s.statemachine.state.ObjectState       : Action execution resulted error

org.springframework.messaging.MessageDeliveryException: nested exception is java.lang.InterruptedException
    at org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler$SystemStompConnectionHandler.forward(StompBrokerRelayMessageHandler.java:951)
    at org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler.handleMessageInternal(StompBrokerRelayMessageHandler.java:521)
    at org.springframework.messaging.simp.broker.AbstractBrokerMessageHandler.handleMessage(AbstractBrokerMessageHandler.java:238)
    at org.springframework.messaging.support.ExecutorSubscribableChannel$SendTask.run(ExecutorSubscribableChannel.java:135)
    at org.springframework.messaging.support.ExecutorSubscribableChannel.sendInternal(ExecutorSubscribableChannel.java:91)
    at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:117)
    at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:104)
    at org.springframework.messaging.simp.SimpMessagingTemplate.sendInternal(SimpMessagingTemplate.java:184)
    at org.springframework.messaging.simp.SimpMessagingTemplate.doSend(SimpMessagingTemplate.java:159)
    at org.springframework.messaging.simp.SimpMessagingTemplate.doSend(SimpMessagingTemplate.java:47)
    at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:105)
    at org.springframework.messaging.simp.user.UserDestinationMessageHandler.handleMessage(UserDestinationMessageHandler.java:219)
    at org.springframework.messaging.support.ExecutorSubscribableChannel$SendTask.run(ExecutorSubscribableChannel.java:135)
    at org.springframework.messaging.support.ExecutorSubscribableChannel.sendInternal(ExecutorSubscribableChannel.java:91)
    at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:117)
    at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:104)
    at org.springframework.messaging.simp.SimpMessagingTemplate.sendInternal(SimpMessagingTemplate.java:184)
    at org.springframework.messaging.simp.SimpMessagingTemplate.doSend(SimpMessagingTemplate.java:159)
    at org.springframework.messaging.simp.SimpMessagingTemplate.doSend(SimpMessagingTemplate.java:47)
    at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:105)
    at org.springframework.messaging.core.AbstractMessageSendingTemplate.convertAndSend(AbstractMessageSendingTemplate.java:143)
    at org.springframework.messaging.simp.SimpMessagingTemplate.convertAndSendToUser(SimpMessagingTemplate.java:224)
    at org.springframework.messaging.simp.SimpMessagingTemplate.convertAndSendToUser(SimpMessagingTemplate.java:215)
    at org.springframework.messaging.simp.SimpMessagingTemplate.convertAndSendToUser(SimpMessagingTemplate.java:201)
    at com.slagalica.configuration.StateMachineConfig$3.execute(StateMachineConfig.java:190)
    at org.springframework.statemachine.state.ObjectState.exit(ObjectState.java:131)
    at org.springframework.statemachine.support.AbstractStateMachine.exitFromState(AbstractStateMachine.java:1036)
    at org.springframework.statemachine.support.AbstractStateMachine.exitCurrentState(AbstractStateMachine.java:991)
    at org.springframework.statemachine.support.AbstractStateMachine.setCurrentState(AbstractStateMachine.java:868)
    at org.springframework.statemachine.support.AbstractStateMachine.switchToState(AbstractStateMachine.java:752)
    at org.springframework.statemachine.support.AbstractStateMachine.access$200(AbstractStateMachine.java:72)
    at org.springframework.statemachine.support.AbstractStateMachine$2.transit(AbstractStateMachine.java:293)
    at org.springframework.statemachine.support.DefaultStateMachineExecutor.handleTriggerTrans(DefaultStateMachineExecutor.java:213)
    at org.springframework.statemachine.support.DefaultStateMachineExecutor.processTriggerQueue(DefaultStateMachineExecutor.java:356)
    at org.springframework.statemachine.support.DefaultStateMachineExecutor.access$100(DefaultStateMachineExecutor.java:57)
    at org.springframework.statemachine.support.DefaultStateMachineExecutor$1.run(DefaultStateMachineExecutor.java:242)
    at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
    at org.springframework.statemachine.support.DefaultStateMachineExecutor.scheduleEventQueueProcessing(DefaultStateMachineExecutor.java:261)
    at org.springframework.statemachine.support.DefaultStateMachineExecutor.access$500(DefaultStateMachineExecutor.java:57)
    at org.springframework.statemachine.support.DefaultStateMachineExecutor$1.run(DefaultStateMachineExecutor.java:255)
    at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
    at org.springframework.statemachine.support.DefaultStateMachineExecutor.scheduleEventQueueProcessing(DefaultStateMachineExecutor.java:261)
    at org.springframework.statemachine.support.DefaultStateMachineExecutor.access$500(DefaultStateMachineExecutor.java:57)
    at org.springframework.statemachine.support.DefaultStateMachineExecutor$2.triggered(DefaultStateMachineExecutor.java:423)
    at org.springframework.statemachine.trigger.CompositeTriggerListener.triggered(CompositeTriggerListener.java:34)
    at org.springframework.statemachine.trigger.TimerTrigger.notifyTriggered(TimerTrigger.java:115)
    at org.springframework.statemachine.trigger.TimerTrigger.access$000(TimerTrigger.java:33)
    at org.springframework.statemachine.trigger.TimerTrigger$1.run(TimerTrigger.java:109)
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
    at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.InterruptedException: null
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2151)
    at reactor.rx.Promise.await(Promise.java:465)
    at reactor.rx.Promise.await(Promise.java:440)
    at org.springframework.messaging.tcp.reactor.AbstractPromiseToListenableFutureAdapter.get(AbstractPromiseToListenableFutureAdapter.java:76)
    at org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler$SystemStompConnectionHandler.forward(StompBrokerRelayMessageHandler.java:946)
    ... 56 common frames omitted

State changed to EQUATIONS_ENDED
4

1 に答える 1

1

説明をありがとう(それは良いキャッチでした)、私はこのhttps://github.com/spring-projects/spring-statemachine/issues/233の gh issue を作成しました。

簡単に言えば、先に進む前に終了アクションを実行できるタイムアウトを許可するための、より優れたユーザーレベルのフックを提供する必要があります。

于 2016-07-07T08:32:19.177 に答える