1

我刚刚开始研究这个主题。我尝试将 Spring 状态机与 Spring Web MVC 应用程序集成,其中我从控制器发送事件。

我的问题是,机器正在为某些事件过渡,而对于某些事件却不是。我试图打印日志。在那里我可以看到事件通用消息排队但没有发生转换。请帮忙!

我在这里给出我的日志的一部分-

2016-01-06 12:51:32 DEBUG AbstractStateMachine:557 - Queue event GenericMessage [payload=LOGIN_CLICKED, headers={timestamp=1452064892305, id=1e223e37-4eb3-95e4-ec32-8447d57616f5}]

这里 LOGIN_CLICKED 是我的事件,它应该使我的机器从 WAITING_USER_INPUT 状态变为 LOGIN_PAGE 状态。但它没有发生。

以下是所有相关文件:

状态.java

package com.psl.model;

public enum States {

    START, WAITING_CUSTOMER_INPUT, LOGIN_PAGE, SIGNUP_PAGE, CUSTOMER_AUTHENTICATED, CUSTOMER_SIGNEDUP, APPLICATION_UI_PAGE, END

}

事件.java

package com.psl.model;

public enum Events {

    INITIAL_TRANSITION, LOGIN_CLICKED, SIGNUP_CLICKED, USER_NOT_FOUND, USER_FOUND, VALIDATION_FAIL, VALIDATION_SUCCESS, GO_HOME, LOG_OUT

}

StateMachineConfig.java

package com.psl.statemachine;

import java.util.EnumSet;

import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;

import com.psl.model.Events;
import com.psl.model.States;

@Configuration
@EnableStateMachine
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<States, Events>{

    @Override
    public void configure(StateMachineStateConfigurer<States, Events> states)
            throws Exception {
        // TODO Auto-generated method stub

        states.withStates().initial(States.START).states(EnumSet.allOf(States.class));
    }

    @Override
    public void configure(
            StateMachineTransitionConfigurer<States, Events> transitions)
            throws Exception {
        // TODO Auto-generated method stub

        transitions
        .withExternal().source(States.START).target(States.WAITING_CUSTOMER_INPUT).event(Events.INITIAL_TRANSITION).and()
        .withExternal().source(States.WAITING_CUSTOMER_INPUT).target(States.LOGIN_PAGE).event(Events.LOGIN_CLICKED).and()
        .withExternal().source(States.WAITING_CUSTOMER_INPUT).target(States.SIGNUP_PAGE).event(Events.SIGNUP_CLICKED).and()
        .withExternal().source(States.LOGIN_PAGE).target(States.LOGIN_PAGE).event(Events.USER_NOT_FOUND).and()
        .withExternal().source(States.SIGNUP_PAGE).target(States.SIGNUP_PAGE).event(Events.VALIDATION_FAIL).and()
        .withExternal().source(States.LOGIN_PAGE).target(States.CUSTOMER_AUTHENTICATED).event(Events.USER_FOUND).and()
        .withExternal().source(States.SIGNUP_PAGE).target(States.CUSTOMER_SIGNEDUP).event(Events.VALIDATION_SUCCESS).and()
        .withExternal().source(States.CUSTOMER_AUTHENTICATED).target(States.APPLICATION_UI_PAGE).event(Events.GO_HOME).and()
        .withExternal().source(States.CUSTOMER_SIGNEDUP).target(States.APPLICATION_UI_PAGE).event(Events.GO_HOME).and()
        .withExternal().source(States.APPLICATION_UI_PAGE).target(States.END).event(Events.LOG_OUT);

        /*transitions
        .withExternal().source(States.START).target(States.WAITING_CUSTOMER_INPUT).event(Events.INITIAL_TRANSITION).and()
        .withExternal().source(States.WAITING_CUSTOMER_INPUT).target(States.LOGIN_PAGE).event(Events.LOGIN_CLICKED).and()
        .withExternal().source(States.LOGIN_PAGE).target(States.CUSTOMER_AUTHENTICATED).event(Events.USER_FOUND).and()
        .withExternal().source(States.CUSTOMER_AUTHENTICATED).target(States.APPLICATION_UI_PAGE).event(Events.GO_HOME).and()
        .withExternal().source(States.APPLICATION_UI_PAGE).target(States.END).event(Events.LOG_OUT);*/

        /*transitions
        .withExternal().source(States.START).target(States.WAITING_CUSTOMER_INPUT).event(Events.INITIAL_TRANSITION).and()
        .withExternal().source(States.WAITING_CUSTOMER_INPUT).target(States.SIGNUP_PAGE).event(Events.SIGNUP_CLICKED).and()
        .withExternal().source(States.SIGNUP_PAGE).target(States.CUSTOMER_SIGNEDUP).event(Events.VALIDATION_SUCCESS).and()
        .withExternal().source(States.CUSTOMER_SIGNEDUP).target(States.APPLICATION_UI_PAGE).event(Events.GO_HOME).and()
        .withExternal().source(States.APPLICATION_UI_PAGE).target(States.END).event(Events.LOG_OUT).and()
        .withExternal().source(States.END).target(States.WAITING_CUSTOMER_INPUT).event(Events.INITIAL_TRANSITION);*/

    }

}

OnTransitionActions.java

package com.psl.statemachine;

import org.apache.log4j.Logger;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.statemachine.annotation.WithStateMachine;

@WithStateMachine
public class OnTransitionActions {

    static final Logger LOGGER2 = Logger.getLogger("stateMachineLogFile");

    @OnTransition(source = "START", target = "WAITING_CUSTOMER_INPUT")
    public void method1(){
        LOGGER2.info("Transition: START -> WAITING_CUSTOMER_INPUT");
        LOGGER2.info("Current State: WAITING_CUSTOMER_INPUT");

        System.out.println("*********************Current State: WAITING_CUSTOMER_INPUT*********************");
    }

    @OnTransition(source = "WAITING_CUSTOMER_INPUT", target = "LOGIN_PAGE")
    public void method2(){
        LOGGER2.info("Transition: WAITING_CUSTOMER_INPUT -> LOGIN_PAGE");
        LOGGER2.info("Current State: LOGIN_PAGE");

        System.out.println("*********************Current State: LOGIN_PAGE*********************");
    }

    @OnTransition(source = "WAITING_CUSTOMER_INPUT", target = "SIGNUP_PAGE")
    public void method3(){
        LOGGER2.info("Transition: WAITING_CUSTOMER_INPUT -> SIGNUP_PAGE");
        LOGGER2.info("Current State: SIGNUP_PAGE");

        System.out.println("*********************Current State: SIGNUP_PAGE*********************");
    }

    @OnTransition(source = "LOGIN_PAGE", target = "LOGIN_PAGE")
    public void method4(){
        LOGGER2.info("Transition: LOGIN_PAGE -> LOGIN_PAGE");
        LOGGER2.info("Current State: LOGIN_PAGE");

        System.out.println("*********************Current State: LOGIN_PAGE*********************");
    }

    @OnTransition(source = "SIGNUP_PAGE", target = "SIGNUP_PAGE")
    public void method5(){
        LOGGER2.info("Transition: SIGNUP_PAGE -> SIGNUP_PAGE");
        LOGGER2.info("Current State: SIGNUP_PAGE");

        System.out.println("*********************Current State: SIGNUP_PAGE*********************");
    }

    @OnTransition(source = "LOGIN_PAGE", target = "CUSTOMER_AUTHENTICATED")
    public void method6(){
        LOGGER2.info("Transition: LOGIN_PAGE -> CUSTOMER_AUTHENTICATED");
        LOGGER2.info("Current State: CUSTOMER_AUTHENTICATED");

        System.out.println("*********************Current State: CUSTOMER_AUTHENTICATED*********************");
    }


    @OnTransition(source = "SIGNUP_PAGE", target = "CUSTOMER_SIGNEDUP")
    public void method7(){
        LOGGER2.info("Transition: SIGNUP_PAGE -> CUSTOMER_SIGNEDUP");
        LOGGER2.info("Current State: CUSTOMER_SIGNEDUP");

        System.out.println("*********************Current State: CUSTOMER_SIGNEDUP*********************");
    }


    @OnTransition(source = "CUSTOMER_AUTHENTICATED", target = "APPLICATION_UI_PAGE")
    public void method8(){
        LOGGER2.info("Transition: CUSTOMER_AUTHENTICATED -> APPLICATION_UI_PAGE");
        LOGGER2.info("Current State: APPLICATION_UI_PAGE");

        System.out.println("*********************Current State: APPLICATION_UI_PAGE*********************");
    }

    @OnTransition(source = "CUSTOMER_SIGNEDUP", target = "APPLICATION_UI_PAGE")
    public void method9(){
        LOGGER2.info("Transition: CUSTOMER_SIGNEDUP -> APPLICATION_UI_PAGE");
        LOGGER2.info("Current State: APPLICATION_UI_PAGE");

        System.out.println("*********************Current State: APPLICATION_UI_PAGE*********************");
    }

    @OnTransition(source = "APPLICATION_UI_PAGE", target = "END")
    public void method10(){
        LOGGER2.info("Transition: APPLICATION_UI_PAGE -> END");
        LOGGER2.info("Current State: END");

        System.out.println("*********************Current State: END*********************");
    }

    @OnTransition(source = "END", target = "WAITING_CUSTOMER_INPUT")
    public void method11(){
        LOGGER2.info("Transition: END -> WAITING_CUSTOMER_INPUT");
        LOGGER2.info("Current State: WAITING_CUSTOMER_INPUT");

        System.out.println("*********************Current State: WAITING_CUSTOMER_INPUT*********************");
    }

}

RunStateMachine.java

package com.psl.statemachine;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.statemachine.StateMachine;

import com.psl.model.Events;
import com.psl.model.States;

public class RunStateMachine {

    @Autowired
    StateMachine<States, Events> stateMachine;

    @Bean
    public StateMachineEventListener stateMachineEventListener(){
        StateMachineEventListener stateMachineEventListener = new StateMachineEventListener();
        stateMachine.addStateListener(stateMachineEventListener);
        return stateMachineEventListener;
    }

    public void fireEvent(Events event){
        stateMachine.start();
        stateMachine.sendEvent(event);

        /*
         * stateMachine.stop();
        if(event == Events.LOG_OUT)
            stateMachine.stop();*/
    }

}

如上所示,我通过RunStateMachine.java发送事件。我从我的 MVC 控制器调用 fireEvent 方法,如下所示:

登录控制器.java

package com.psl.controller;

import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.psl.dao.IUserDao;
import com.psl.model.Events;
import com.psl.statemachine.RunStateMachine;

@Controller
@Scope(value = "session")
public class LoginController {

    static final Logger LOGGER1 = Logger.getLogger("appLogFile");

    //Creating bean for RunStateMachine class
    private ApplicationContext statemachine_context = new ClassPathXmlApplicationContext("statemachine-config.xml");
    RunStateMachine machine = (RunStateMachine) statemachine_context.getBean("runStateMachine");


    private ApplicationContext jdbc_context = new ClassPathXmlApplicationContext(
            "spring-jdbc-config.xml");

    @RequestMapping(value = "/Login", method = RequestMethod.GET)
    public String login(Model model){

        //sending event to the machine
        machine.fireEvent(Events.LOGIN_CLICKED);

        return "LoginPage";
    }

    @RequestMapping(value = "/Login", method = RequestMethod.POST)
    public String manuallyProcessLogin(HttpServletRequest request){

        //get from form
        String name = request.getParameter("username");
        String pass = request.getParameter("password");
        LOGGER1.info("Username = " + name + "\nPassword = " + pass);

        //check into database
        IUserDao dao = (IUserDao) jdbc_context.getBean("dao");
        boolean ifAuth = dao.authenticateUser(name, pass);


        LOGGER1.info("*****************LOGIN POST METHOD**************");
        if(ifAuth){
            machine.fireEvent(Events.USER_FOUND);
            //statemachine.sendEvent(Events.USER_FOUND);
            String uname = (String) request.getSession().getAttribute("user");
            if(uname == null){
                //add new uname
                request.getSession().setAttribute("user", name);
            }
            return "LoginSuccess";
        }

        machine.fireEvent(Events.USER_NOT_FOUND);
        return "LoginPage";
    }

    @RequestMapping(value = "/Logout", method = RequestMethod.GET)
    public String logout(HttpServletRequest request, Model model) {

        //manually logout
        request.getSession().invalidate();

        machine.fireEvent(Events.LOG_OUT);

        return "LogoutSuccess";
    }

}

这是状态机配置文件:

statemachine-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">

        <context:component-scan base-package="com.psl.statemachine"></context:component-scan>
        <bean id="runStateMachine" class="com.psl.statemachine.RunStateMachine" scope="singleton"></bean>

    </beans>

这是web.xml/Welcome ,我在其中声明了 spring 安全性的自定义过滤器,该过滤器/Login在上面的控制器中过滤并发送所有请求:

<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">
    <display-name>Archetype Created Web Application</display-name>

    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- Manually apply filter for security -->
    <filter>
        <filter-name>manualSecurityFilter</filter-name>
        <filter-class>com.psl.filters.ManualSecurityFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>manualSecurityFilter</filter-name>
        <url-pattern>/Welcome</url-pattern>
    </filter-mapping>


</web-app>

更新:解决了!! 刚刚发现多个 MVC 控制器不适用于状态机!!!当我将所有 RequestMapping 方法放在 1 个控制器中时,它就起作用了。

4

0 回答 0