我们来以一个简单示例来演示一下 mina-statemachine 是如何工作的。下图演示了一个典型的录音机的状态机。圆圈表示状态,箭头表示转换。每个转换以一个触发该转换的事件名作为标签。
首先,录音机处于 Empty 状态。插入一个磁带后 load 事件被触发,录音机转为 Loaded 状态。在 Loaded 状态中,eject 事件将会触发其移回 Empty 状态,而 play 事件则会触发其移向 Playing 状态。以此类推...我认为你可以判断出剩下的逻辑。
现在写一些代码。外界将会只能看到 TapeDeck 接口:public interface TapeDeck {
void load(String nameOfTape);
void eject();
void start();
void pause();
void stop();
}public class TapeDeckHandler {
@State public static final String EMPTY = "Empty";
@State public static final String LOADED = "Loaded";
@State public static final String PLAYING = "Playing";
@State public static final String PAUSED = "Paused";
}public class TapeDeckHandler {
@State public static final String EMPTY = "Empty";
@State public static final String LOADED = "Loaded";
@State public static final String PLAYING = "Playing";
@State public static final String PAUSED = "Paused";
@Transition(on = "load", in = EMPTY, next = LOADED)
public void loadTape(String nameOfTape) {
System.out.println("Tape ‘" + nameOfTape + "‘ loaded");
}
@Transitions({
@Transition(on = "play", in = LOADED, next = PLAYING),
@Transition(on = "play", in = PAUSED, next = PLAYING)
})
public void playTape() {
System.out.println("Playing tape");
}
@Transition(on = "pause", in = PLAYING, next = PAUSED)
public void pauseTape() {
System.out.println("Tape paused");
}
@Transition(on = "stop", in = PLAYING, next = LOADED)
public void stopTape() {
System.out.println("Tape stopped");
}
@Transition(on = "eject", in = LOADED, next = EMPTY)
public void ejectTape() {
System.out.println("Tape ejected");
}
}@Transition(on = "load", in = EMPTY, next = LOADED)
public void loadTape(String nameOfTape) {@Transitions({
@Transition(on = "play", in = LOADED, next = PLAYING),
@Transition(on = "play", in = PAUSED, next = PLAYING)
})
public void playTape() {public static void main(String[] args) {
TapeDeckHandler handler = new TapeDeckHandler();
StateMachine sm = StateMachineFactory.getInstance(Transition.class).create(TapeDeckHandler.EMPTY, handler);
TapeDeck deck = new StateMachineProxyBuilder().create(TapeDeck.class, sm);
deck.load("The Knife - Silent Shout");
deck.play();
deck.pause();
deck.play();
deck.stop();
deck.eject();
}TapeDeckHandler handler = new TapeDeckHandler(); StateMachine sm = StateMachineFactory.getInstance(Transition.class).create(TapeDeckHandler.EMPTY, handler);
TapeDeck deck = new StateMachineProxyBuilder().create(TapeDeck.class, sm);
Tape ‘The Knife - Silent Shout‘ loaded Playing tape Tape paused Playing tape Tape stopped Tape ejected
@Transition(on = "foo")
public void someMethod(One one, Two two, Three three) { ... }someMethod(a, b, c);
@Transition(on = "foo")
public void someMethod(Two two) { ... }@Transition(on = "foo")
public void someMethod() { ... }@Transition(on = "foo")
public void someMethod(Event event, StateContext context, One one, Two two, Three three) { ... }
@Transition(on = "foo")
public void someMethod(Event event, One one, Two two, Three three) { ... }
@Transition(on = "foo")
public void someMethod(StateContext context, One one, Two two, Three three) { ... }@Transition(on = "foo")
public void someMethod(MyStateContext context, Two two) { ... }// All method arguments matches all event arguments directly
@Transition(on = "messageReceived")
public void messageReceived(ArrayList l, Integer i) { ... }
// Matches since ((a instanceof List && b instanceof Number) == true)
@Transition(on = "messageReceived")
public void messageReceived(List l, Number n) { ... }
// Matches since ((b instanceof Number) == true)
@Transition(on = "messageReceived")
public void messageReceived(Number n) { ... }
// Methods with no arguments always matches
@Transition(on = "messageReceived")
public void messageReceived() { ... }
// Methods only interested in the current Event or StateContext always matches
@Transition(on = "messageReceived")
public void messageReceived(StateContext context) { ... }
// Matches since ((a instanceof Collection) == true)
@Transition(on = "messageReceived")
public void messageReceived(Event event, Collection c) { ... }// Incorrect ordering
@Transition(on = "messageReceived")
public void messageReceived(Integer i, List l) { ... }
// ((a instanceof LinkedList) == false)
@Transition(on = "messageReceived")
public void messageReceived(LinkedList l, Number n) { ... }
// Event must be first argument
@Transition(on = "messageReceived")
public void messageReceived(ArrayList l, Event event) { ... }
// StateContext must be second argument if Event is used
@Transition(on = "messageReceived")
public void messageReceived(Event event, ArrayList l, StateContext context) { ... }
// Event must come before StateContext
@Transition(on = "messageReceived")
public void messageReceived(StateContext context, Event event) { ... }@State public static final String A = "A"; @State(A) public static final String B = "A->B"; @State(A) public static final String C = "A->C"; @State(B) public static final String D = "A->B->D"; @State(C) public static final String E = "A->C->E";
public static void main(String[] args) {
...
deck.load("The Knife - Silent Shout");
deck.play();
deck.pause();
deck.play();
deck.stop();
deck.eject();
deck.play();
}
...
Tape stopped
Tape ejected
Exception in thread "main" o.a.m.sm.event.UnhandledEventException:
Unhandled event: org.apache.mina.statemachine.event.Event@15eb0a9[id=play,...]
at org.apache.mina.statemachine.StateMachine.handle(StateMachine.java:285)
at org.apache.mina.statemachine.StateMachine.processEvents(StateMachine.java:142)
...@Transitions({
@Transition(on = "*", in = EMPTY, weight = 100),
@Transition(on = "*", in = LOADED, weight = 100),
@Transition(on = "*", in = PLAYING, weight = 100),
@Transition(on = "*", in = PAUSED, weight = 100)
})
public void error(Event event) {
System.out.println("Cannot ‘" + event.getId() + "‘ at this time");
}... Tape stopped Tape ejected Cannot ‘play‘ at this time.
public static class TapeDeckHandler {
@State public static final String ROOT = "Root";
@State(ROOT) public static final String EMPTY = "Empty";
@State(ROOT) public static final String LOADED = "Loaded";
@State(ROOT) public static final String PLAYING = "Playing";
@State(ROOT) public static final String PAUSED = "Paused";
...
@Transition(on = "*", in = ROOT)
public void error(Event event) {
System.out.println("Cannot ‘" + event.getId() + "‘ at this time");
}
}telnet localhost 12345 S: + Greetings from your tape deck! C: list S: + (1: "The Knife - Silent Shout", 2: "Kings of convenience - Riot on an empty street") C: load 1 S: + "The Knife - Silent Shout" loaded C: play S: + Playing "The Knife - Silent Shout" C: pause S: + "The Knife - Silent Shout" paused C: play S: + Playing "The Knife - Silent Shout" C: info S: + Tape deck is playing. Current tape: "The Knife - Silent Shout" C: eject S: - Cannot eject while playing C: stop S: + "The Knife - Silent Shout" stopped C: eject S: + "The Knife - Silent Shout" ejected C: quit S: + Bye! Please come back!
@State public static final String ROOT = "Root"; @State(ROOT) public static final String EMPTY = "Empty"; @State(ROOT) public static final String LOADED = "Loaded"; @State(ROOT) public static final String PLAYING = "Playing"; @State(ROOT) public static final String PAUSED = "Paused";
@IoHandlerTransitions({
@IoHandlerTransition(on = MESSAGE_RECEIVED, in = LOADED, next = PLAYING),
@IoHandlerTransition(on = MESSAGE_RECEIVED, in = PAUSED, next = PLAYING)
})
public void playTape(TapeDeckContext context, IoSession session, PlayCommand cmd) {
session.write("+ Playing \"" + context.tapeName + "\"");
}import static org.apache.mina.statemachine.event.IoHandlerEvents.*;
static class TapeDeckContext extends AbstractStateContext {
public String tapeName;
}@IoHandlerTransition(on = MESSAGE_RECEIVED, in = EMPTY, next = LOADED)
public void loadTape(TapeDeckContext context, IoSession session, LoadCommand cmd) {
if (cmd.getTapeNumber() < 1 || cmd.getTapeNumber() > tapes.length) {
session.write("- Unknown tape number: " + cmd.getTapeNumber());
StateControl.breakAndGotoNext(EMPTY);
} else {
context.tapeName = tapes[cmd.getTapeNumber() - 1];
session.write("+ \"" + context.tapeName + "\" loaded");
}
}StateControl.breakAndGotoNext(EMPTY);
@IoHandlerTransition(on = SESSION_OPENED, in = EMPTY)
public void connect(IoSession session) {
session.write("+ Greetings from your tape deck!");
}@IoHandlerTransition(on = MESSAGE_RECEIVED, in = ROOT, weight = 10)
public void error(Event event, StateContext context, IoSession session, Command cmd) {
session.write("- Cannot " + cmd.getName() + " while "
+ context.getCurrentState().getId().toLowerCase());
}private static IoHandler createIoHandler() {
StateMachine sm = StateMachineFactory.getInstance(IoHandlerTransition.class).create(EMPTY, new TapeDeckServer());
return new StateMachineProxyBuilder().setStateContextLookup(
new IoSessionStateContextLookup(new StateContextFactory() {
public StateContext create() {
return new TapeDeckContext();
}
})).create(IoHandler.class, sm);
}
// This code will work with MINA 1.0/1.1:
public static void main(String[] args) throws Exception {
SocketAcceptor acceptor = new SocketAcceptor();
SocketAcceptorConfig config = new SocketAcceptorConfig();
config.setReuseAddress(true);
ProtocolCodecFilter pcf = new ProtocolCodecFilter(
new TextLineEncoder(), new CommandDecoder());
config.getFilterChain().addLast("codec", pcf);
acceptor.bind(new InetSocketAddress(12345), createIoHandler(), config);
}
// This code will work with MINA trunk:
public static void main(String[] args) throws Exception {
SocketAcceptor acceptor = new NioSocketAcceptor();
acceptor.setReuseAddress(true);
ProtocolCodecFilter pcf = new ProtocolCodecFilter(
new TextLineEncoder(), new CommandDecoder());
acceptor.getFilterChain().addLast("codec", pcf);
acceptor.setHandler(createIoHandler());
acceptor.setLocalAddress(new InetSocketAddress(PORT));
acceptor.bind();
}《Apache MINA 2.0 用户指南》第十四章:状态机
原文:http://blog.csdn.net/defonds/article/details/18267605