我刚开始研究这个话题。我试图将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状态。但它没有发生。
以下是所有相关文件:
States.java
package com.psl.model;
public enum States {
START, WAITING_CUSTOMER_INPUT, LOGIN_PAGE, SIGNUP_PAGE, CUSTOMER_AUTHENTICATED, CUSTOMER_SIGNEDUP, APPLICATION_UI_PAGE, END
}
Events.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 方法,如下所示:
LoginController.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 ,其中我声明了针对Spring安全性的自定义过滤器,用于过滤并将 /Welcome
的所有请求发送到 {上面的控制器中的{1}} :
/Login
更新:已解决!! 刚刚发现多个MVC控制器不适用于State Machine!当我将所有RequestMapping方法放在1个控制器中时,它就可以工作。