001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.mina.statemachine; 021 022import static org.junit.Assert.assertEquals; 023import static org.junit.Assert.assertTrue; 024 025import java.util.LinkedList; 026 027import org.apache.mina.statemachine.annotation.Transition; 028import org.apache.mina.statemachine.annotation.Transitions; 029import org.apache.mina.statemachine.annotation.OnEntry; 030import org.apache.mina.statemachine.annotation.OnExit; 031import org.apache.mina.statemachine.context.StateContext; 032import org.apache.mina.statemachine.event.Event; 033import org.apache.mina.statemachine.transition.MethodSelfTransition; 034import org.apache.mina.statemachine.transition.MethodTransition; 035import org.junit.Test; 036 037/** 038 * Tests {@link StateMachineProxyBuilder}. 039 * 040 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 041 */ 042public class StateMachineProxyBuilderTest { 043 @Test 044 public void testReentrantStateMachine() throws Exception { 045 ReentrantStateMachineHandler handler = new ReentrantStateMachineHandler(); 046 047 State s1 = new State("s1"); 048 State s2 = new State("s2"); 049 State s3 = new State("s3"); 050 051 s1.addTransition(new MethodTransition("call1", s2, handler)); 052 s2.addTransition(new MethodTransition("call2", s3, handler)); 053 s3.addTransition(new MethodTransition("call3", handler)); 054 055 StateMachine sm = new StateMachine(new State[] { s1, s2, s3 }, "s1"); 056 Reentrant reentrant = new StateMachineProxyBuilder().create(Reentrant.class, sm); 057 reentrant.call1(reentrant); 058 assertTrue(handler.finished); 059 } 060 061 @Test 062 public void testTapeDeckStateMachine() throws Exception { 063 TapeDeckStateMachineHandler handler = new TapeDeckStateMachineHandler(); 064 065 State parent = new State("parent"); 066 State s1 = new State("s1", parent); 067 State s2 = new State("s2", parent); 068 State s3 = new State("s3", parent); 069 State s4 = new State("s4", parent); 070 State s5 = new State("s5", parent); 071 072 parent.addTransition(new MethodTransition("*", "error", handler)); 073 s1.addTransition(new MethodTransition("insert", s2, "inserted", handler)); 074 s2.addTransition(new MethodTransition("start", s3, "playing", handler)); 075 s3.addTransition(new MethodTransition("stop", s4, "stopped", handler)); 076 s3.addTransition(new MethodTransition("pause", s5, "paused", handler)); 077 s4.addTransition(new MethodTransition("eject", s1, "ejected", handler)); 078 s5.addTransition(new MethodTransition("pause", s3, "playing", handler)); 079 080 s2.addOnEntrySelfTransaction(new MethodSelfTransition("onEntryS2", handler)); 081 s2.addOnExitSelfTransaction(new MethodSelfTransition("onExitS2", handler)); 082 083 s3.addOnEntrySelfTransaction(new MethodSelfTransition("onEntryS3", handler)); 084 s3.addOnExitSelfTransaction(new MethodSelfTransition("onExitS3", handler)); 085 086 s4.addOnEntrySelfTransaction(new MethodSelfTransition("onEntryS4", handler)); 087 s4.addOnExitSelfTransaction(new MethodSelfTransition("onExitS4", handler)); 088 089 StateMachine sm = new StateMachine(new State[] { s1, s2, s3, s4, s5 }, "s1"); 090 TapeDeck player = new StateMachineProxyBuilder().create(TapeDeck.class, sm); 091 player.insert("Kings of convenience - Riot on an empty street"); 092 player.start(); 093 player.pause(); 094 player.pause(); 095 player.eject(); 096 player.stop(); 097 player.eject(); 098 099 LinkedList<String> messages = handler.messages; 100 assertEquals("Tape 'Kings of convenience - Riot on an empty street' inserted", messages.removeFirst()); 101 assertEquals("S2 entered", messages.removeFirst()); 102 assertEquals("Playing", messages.removeFirst()); 103 assertEquals("S2 exited", messages.removeFirst()); 104 assertEquals("S3 entered with stateContext", messages.removeFirst()); 105 assertEquals("Paused", messages.removeFirst()); 106 assertEquals("S3 exited with stateContext", messages.removeFirst()); 107 assertEquals("Playing", messages.removeFirst()); 108 assertEquals("S3 entered with stateContext", messages.removeFirst()); 109 assertEquals("Error: Cannot eject at this time", messages.removeFirst()); 110 assertEquals("Stopped", messages.removeFirst()); 111 assertEquals("S3 exited with stateContext", messages.removeFirst()); 112 assertEquals("S4 entered with stateContext and state", messages.removeFirst()); 113 assertEquals("Tape ejected", messages.removeFirst()); 114 assertEquals("S4 exited with stateContext and state", messages.removeFirst()); 115 116 assertTrue(messages.isEmpty()); 117 } 118 119 @Test 120 public void testTapeDeckStateMachineAnnotations() throws Exception { 121 TapeDeckStateMachineHandler handler = new TapeDeckStateMachineHandler(); 122 123 StateMachine sm = StateMachineFactory.getInstance(Transition.class).create(TapeDeckStateMachineHandler.S1, 124 handler); 125 126 TapeDeck player = new StateMachineProxyBuilder().create(TapeDeck.class, sm); 127 player.insert("Kings of convenience - Riot on an empty street"); 128 player.start(); 129 player.pause(); 130 player.pause(); 131 player.eject(); 132 player.stop(); 133 player.eject(); 134 135 LinkedList<String> messages = handler.messages; 136 assertEquals("Tape 'Kings of convenience - Riot on an empty street' inserted", messages.removeFirst()); 137 assertEquals("S2 entered", messages.removeFirst()); 138 assertEquals("Playing", messages.removeFirst()); 139 assertEquals("S2 exited", messages.removeFirst()); 140 assertEquals("S3 entered with stateContext", messages.removeFirst()); 141 assertEquals("Paused", messages.removeFirst()); 142 assertEquals("S3 exited with stateContext", messages.removeFirst()); 143 assertEquals("Playing", messages.removeFirst()); 144 assertEquals("S3 entered with stateContext", messages.removeFirst()); 145 assertEquals("Error: Cannot eject at this time", messages.removeFirst()); 146 assertEquals("Stopped", messages.removeFirst()); 147 assertEquals("S3 exited with stateContext", messages.removeFirst()); 148 assertEquals("S4 entered with stateContext and state", messages.removeFirst()); 149 assertEquals("Tape ejected", messages.removeFirst()); 150 assertEquals("S4 exited with stateContext and state", messages.removeFirst()); 151 152 assertTrue(messages.isEmpty()); 153 } 154 155 public interface Reentrant { 156 void call1(Reentrant proxy); 157 158 void call2(Reentrant proxy); 159 160 void call3(Reentrant proxy); 161 } 162 163 public static class ReentrantStateMachineHandler { 164 private boolean finished = false; 165 166 public void call1(Reentrant proxy) { 167 proxy.call2(proxy); 168 } 169 170 public void call2(Reentrant proxy) { 171 proxy.call3(proxy); 172 } 173 174 public void call3(Reentrant proxy) { 175 finished = true; 176 } 177 } 178 179 public interface TapeDeck { 180 void insert(String name); 181 182 void eject(); 183 184 void start(); 185 186 void pause(); 187 188 void stop(); 189 } 190 191 public static class TapeDeckStateMachineHandler { 192 @org.apache.mina.statemachine.annotation.State 193 public static final String PARENT = "parent"; 194 195 @org.apache.mina.statemachine.annotation.State(PARENT) 196 public static final String S1 = "s1"; 197 198 @org.apache.mina.statemachine.annotation.State(PARENT) 199 public static final String S2 = "s2"; 200 201 @org.apache.mina.statemachine.annotation.State(PARENT) 202 public static final String S3 = "s3"; 203 204 @org.apache.mina.statemachine.annotation.State(PARENT) 205 public static final String S4 = "s4"; 206 207 @org.apache.mina.statemachine.annotation.State(PARENT) 208 public static final String S5 = "s5"; 209 210 private LinkedList<String> messages = new LinkedList<String>(); 211 212 @OnEntry(S2) 213 public void onEntryS2() { 214 messages.add("S2 entered"); 215 } 216 217 @OnExit(S2) 218 public void onExitS2() { 219 messages.add("S2 exited"); 220 } 221 222 @OnEntry(S3) 223 public void onEntryS3(StateContext stateContext) { 224 messages.add("S3 entered with stateContext"); 225 } 226 227 @OnExit(S3) 228 public void onExitS3(StateContext stateContext) { 229 messages.add("S3 exited with stateContext"); 230 } 231 232 @OnEntry(S4) 233 public void onEntryS4(StateContext stateContext, State state) { 234 messages.add("S4 entered with stateContext and state"); 235 } 236 237 @OnExit(S4) 238 public void onExitS4(StateContext stateContext, State state) { 239 messages.add("S4 exited with stateContext and state"); 240 } 241 242 @Transition(on = "insert", in = "s1", next = "s2") 243 public void inserted(String name) { 244 messages.add("Tape '" + name + "' inserted"); 245 } 246 247 @Transition(on = "eject", in = "s4", next = "s1") 248 public void ejected() { 249 messages.add("Tape ejected"); 250 } 251 252 @Transitions({ @Transition(on = "start", in = "s2", next = "s3"), 253 @Transition(on = "pause", in = "s5", next = "s3") }) 254 public void playing() { 255 messages.add("Playing"); 256 } 257 258 @Transition(on = "pause", in = "s3", next = "s5") 259 public void paused() { 260 messages.add("Paused"); 261 } 262 263 @Transition(on = "stop", in = "s3", next = "s4") 264 public void stopped() { 265 messages.add("Stopped"); 266 } 267 268 @Transition(on = "*", in = "parent") 269 public void error(Event event) { 270 messages.add("Error: Cannot " + event.getId() + " at this time"); 271 } 272 } 273}