Mina状态机StateMachine()

接下来我们要把这样的方式用在通信中。用通信的方式来模拟录放机的按钮。

我们先看状态的定义:

001 package com.a2.desktop.example10.mina.statemachine;
002  
003 import static org.apache.mina.statemachine.event.IoHandlerEvents.EXCEPTION_CAUGHT;
004 import static org.apache.mina.statemachine.event.IoHandlerEvents.MESSAGE_RECEIVED;
005 import static org.apache.mina.statemachine.event.IoHandlerEvents.SESSION_OPENED;
006  
007 import org.apache.mina.core.future.IoFutureListener;
008 import org.apache.mina.core.session.IoSession;
009  
010 import org.apache.mina.statemachine.StateControl;
011 import org.apache.mina.statemachine.annotation.IoHandlerTransition;
012 import org.apache.mina.statemachine.annotation.IoHandlerTransitions;
013 import org.apache.mina.statemachine.annotation.State;
014 import org.apache.mina.statemachine.context.AbstractStateContext;
015 import org.apache.mina.statemachine.context.StateContext;
016 import org.apache.mina.statemachine.event.Event;
017  
018 public class TapeDeckServer {
019  
020     @State
021     public static final String ROOT = "Root";
022     @State(ROOT)
023     // 表示继承关系
024     public static final String EMPTY = "Empty";
025     @State(ROOT)
026     public static final String LOADED = "Loaded";
027     @State(ROOT)
028     public static final String PLAYING = "Playing";
029     @State(ROOT)
030     public static final String PAUSED = "Paused";
031  
032     private final String[] tapes = { "盖世英雄-王力宏""唯一-王力宏" };
033  
034     static class TapeDeckContext extends AbstractStateContext {
035         public String tapeName;
036     }
037      
038      @IoHandlerTransition(on = SESSION_OPENED, in = EMPTY)
039         public void connect(IoSession session) {
040             session.write("+ Greetings from your tape deck!");
041         }
042          
043         @IoHandlerTransition(on = MESSAGE_RECEIVED, in = EMPTY, next = LOADED)
044         public void loadTape(TapeDeckContext context, IoSession session, LoadCommand cmd) {
045       
046             if (cmd.getTapeNumber() < 1 || cmd.getTapeNumber() > tapes.length) {
047                 session.write("- Unknown tape number: " + cmd.getTapeNumber());
048                 StateControl.breakAndGotoNext(EMPTY);
049             else {
050                 context.tapeName = tapes[cmd.getTapeNumber() - 1];
051                 session.write("+ \"" + context.tapeName + "\" loaded");
052             }
053         }
054  
055         @IoHandlerTransitions({
056             @IoHandlerTransition(on = MESSAGE_RECEIVED, in = LOADED, next = PLAYING),
057             @IoHandlerTransition(on = MESSAGE_RECEIVED, in = PAUSED, next = PLAYING)
058         })
059         public void playTape(TapeDeckContext context, IoSession session, PlayCommand cmd) {
060             session.write("+ Playing \"" + context.tapeName + "\"");
061         }
062          
063         @IoHandlerTransition(on = MESSAGE_RECEIVED, in = PLAYING, next = PAUSED)
064         public void pauseTape(TapeDeckContext context, IoSession session, PauseCommand cmd) {
065             session.write("+ \"" + context.tapeName + "\" paused");
066         }
067          
068         @IoHandlerTransition(on = MESSAGE_RECEIVED, in = PLAYING, next = LOADED)
069         public void stopTape(TapeDeckContext context, IoSession session, StopCommand cmd) {
070             session.write("+ \"" + context.tapeName + "\" stopped");
071         }
072          
073         @IoHandlerTransition(on = MESSAGE_RECEIVED, in = LOADED, next = EMPTY)
074         public void ejectTape(TapeDeckContext context, IoSession session, EjectCommand cmd) {
075             session.write("+ \"" + context.tapeName + "\" ejected");
076             context.tapeName = null;
077         }
078          
079         @IoHandlerTransition(on = MESSAGE_RECEIVED, in = ROOT)
080         public void listTapes(IoSession session, ListCommand cmd) {
081             StringBuilder response = new StringBuilder("+ (");
082             for (int i = 0; i < tapes.length; i++) {
083                 response.append(i + 1).append(": ");
084                 response.append('"').append(tapes[i]).append('"');
085                 if (i < tapes.length - 1) {
086                     response.append(", ");
087                 }
088             }
089             response.append(')');
090             session.write(response);
091         }
092          
093         @IoHandlerTransition(on = MESSAGE_RECEIVED, in = ROOT)
094         public void info(TapeDeckContext context, IoSession session, InfoCommand cmd) {
095             String state = context.getCurrentState().getId().toLowerCase();
096             if (context.tapeName == null) {
097                 session.write("+ Tape deck is " + state + "");
098             else {
099                 session.write("+ Tape deck is " + state
100                         ". Current tape: \"" + context.tapeName + "\"");
101             }
102         }
103          
104         @IoHandlerTransition(on = MESSAGE_RECEIVED, in = ROOT)
105         public void quit(TapeDeckContext context, IoSession session, QuitCommand cmd) {
106             session.write("+ Bye! Please come back!").addListener(IoFutureListener.CLOSE);
107         }
108          
109         @IoHandlerTransition(on = MESSAGE_RECEIVED, in = ROOT, weight = 10)
110         public void error(Event event, StateContext context, IoSession session, Command cmd) {
111             session.write("- Cannot " + cmd.getName()
112                     " while " + context.getCurrentState().getId().toLowerCase());
113         }
114          
115         @IoHandlerTransition(on = EXCEPTION_CAUGHT, in = ROOT)
116         public void commandSyntaxError(IoSession session, CommandSyntaxException e) {
117             session.write("- " + e.getMessage());
118         }
119          
120         @IoHandlerTransition(on = EXCEPTION_CAUGHT, in = ROOT, weight = 10)
121         public void exceptionCaught(IoSession session, Exception e) {
122             e.printStackTrace();
123             session.close(true);
124         }
125          
126         @IoHandlerTransition(in = ROOT, weight = 100)
127         public void unhandledEvent() {
128         }
129          
130 }

命令的抽象类:

1 package com.a2.desktop.example10.mina.statemachine;
2  
3 public abstract class Command {
4      public abstract String getName();
5 }

以下是各类命令,实现形式相似:

01 package com.a2.desktop.example10.mina.statemachine;
02  
03 public class LoadCommand extends Command {
04      
05     public static final String NAME = "load";
06  
07     private final int tapeNumber;
08  
09     public LoadCommand(int tapeNumber) {
10         this.tapeNumber = tapeNumber;
11     }
12  
13     public int getTapeNumber() {
14         return tapeNumber;
15     }
16  
17     @Override
18     public String getName() {
19         return NAME;
20     }
21  
22 }
23  
24 package com.a2.desktop.example10.mina.statemachine;
25  
26 public class PlayCommand extends Command {
27  
28     public static final String NAME = "play";
29  
30     @Override
31     public String getName() {
32         return NAME;
33     }
34 }
35 //以下省略各种命令…

下面是解码器,继承了文本的解码方式:

01 package com.a2.desktop.example10.mina.statemachine;
02  
03 import java.nio.charset.Charset;
04 import java.util.LinkedList;
05  
06 import org.apache.mina.core.buffer.IoBuffer;
07 import org.apache.mina.core.filterchain.IoFilter.NextFilter;
08 import org.apache.mina.core.session.IoSession;
09 import org.apache.mina.filter.codec.ProtocolDecoderOutput;
10 import org.apache.mina.filter.codec.textline.LineDelimiter;
11 import org.apache.mina.filter.codec.textline.TextLineDecoder;
12  
13 public class CommandDecoder extends TextLineDecoder {
14  
15      public CommandDecoder() {
16             super(Charset.forName("UTF8"), LineDelimiter.WINDOWS);
17         }
18          
19         private Object parseCommand(String line) throws CommandSyntaxException {
20             String[] temp = line.split(" +"2);
21             String cmd = temp[0].toLowerCase();
22             String arg = temp.length > 1 ? temp[1] : null;
23              
24             if (LoadCommand.NAME.equals(cmd)) {
25                 if (arg == null) {
26                     throw new CommandSyntaxException("No tape number specified");
27                 }
28                 try {
29                     return new LoadCommand(Integer.parseInt(arg));
30                 catch (NumberFormatException nfe) {
31                     throw new CommandSyntaxException("Illegal tape number: " + arg);
32                 }
33             else if (PlayCommand.NAME.equals(cmd)) {
34                 return new PlayCommand();
35             else if (PauseCommand.NAME.equals(cmd)) {
36                 return new PauseCommand();
37             else if (StopCommand.NAME.equals(cmd)) {
38                 return new StopCommand();
39             else if (ListCommand.NAME.equals(cmd)) {
40                 return new ListCommand();
41             else if (EjectCommand.NAME.equals(cmd)) {
42                 return new EjectCommand();
43             else if (QuitCommand.NAME.equals(cmd)) {
44                 return new QuitCommand();
45             else if (InfoCommand.NAME.equals(cmd)) {
46                 return new InfoCommand();
47             }
48              
49             throw new CommandSyntaxException("Unknown command: " + cmd);
50         }
51  
52         @Override
53         public void decode(IoSession session, IoBuffer in, final ProtocolDecoderOutput out)
54                 throws Exception {
55              
56             final LinkedList<String> lines = new LinkedList<String>();
57             super.decode(session, in, new ProtocolDecoderOutput() {
58                 public void write(Object message) {
59                     lines.add((String) message);
60                 }
61                 public void flush(NextFilter nextFilter, IoSession session) {}
62             });
63              
64             for (String s: lines) {
65                 out.write(parseCommand(s));
66             }
67         }
68 }

处理异常类:

01 package com.a2.desktop.example10.mina.statemachine;
02  
03 import org.apache.mina.filter.codec.ProtocolDecoderException;
04  
05 /**
06  * Exception thrown by CommandDecoder when a line cannot be decoded as a Command
07  * object.
08  *
09  */
10 public class CommandSyntaxException extends ProtocolDecoderException {
11     private final String message;
12  
13     public CommandSyntaxException(String message) {
14         super(message);
15         this.message = message;
16     }
17  
18     @Override
19     public String getMessage() {
20         return message;
21     }
22 }
测试类:
01 package com.a2.desktop.example10.mina.statemachine;
02  
03 import java.net.InetSocketAddress;
04  
05 import org.apache.mina.core.service.IoHandler;
06 import org.apache.mina.filter.codec.ProtocolCodecFilter;
07 import org.apache.mina.filter.codec.textline.TextLineEncoder;
08 import org.apache.mina.filter.logging.LoggingFilter;
09 import org.apache.mina.statemachine.StateMachine;
10 import org.apache.mina.statemachine.StateMachineFactory;
11 import org.apache.mina.statemachine.StateMachineProxyBuilder;
12 import org.apache.mina.statemachine.annotation.IoHandlerTransition;
13 import org.apache.mina.statemachine.context.IoSessionStateContextLookup;
14 import org.apache.mina.statemachine.context.StateContext;
15 import org.apache.mina.statemachine.context.StateContextFactory;
16 import org.apache.mina.transport.socket.SocketAcceptor;
17 import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
18  
19 public class TestMain {
20      private static final int PORT = 8082;
21          
22         private static IoHandler createIoHandler() {
23             StateMachine sm = StateMachineFactory.getInstance(
24                     IoHandlerTransition.class).create(TapeDeckServer.EMPTY,
25                     new TapeDeckServer());
26  
27             return new StateMachineProxyBuilder().setStateContextLookup(
28                     new IoSessionStateContextLookup(new StateContextFactory() {
29                         public StateContext create() {
30                             return new TapeDeckServer.TapeDeckContext();
31                         }
32                     })).create(IoHandler.class, sm);
33         }
34          
35         public static void main(String[] args) throws Exception {
36             SocketAcceptor acceptor = new NioSocketAcceptor();
37             acceptor.setReuseAddress(true);
38             ProtocolCodecFilter pcf = new ProtocolCodecFilter(
39                     new TextLineEncoder(), new CommandDecoder());
40             acceptor.getFilterChain().addLast("log1"new LoggingFilter("log1"));
41             acceptor.getFilterChain().addLast("codec", pcf);
42             acceptor.getFilterChain().addLast("log2"new LoggingFilter("log2"));
43             acceptor.setHandler(createIoHandler());
44             acceptor.bind(new InetSocketAddress(PORT));
45         }
46 }

启动测试类,用telnet去连,然后输入各种命令,效果如下:

更详细的代码可以参阅: org.apache.mina.example.tapedeck

代码其实都不难,只是我们需要灵活的将状态机这样的模式运用到自己的项目中去,通过状态之间的有规则的切换来控制更复杂的业务逻辑。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值