View Javadoc

    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one
    3    *  or more contributor license agreements.  See the NOTICE file
    4    *  distributed with this work for additional information
    5    *  regarding copyright ownership.  The ASF licenses this file
    6    *  to you under the Apache License, Version 2.0 (the
    7    *  "License"); you may not use this file except in compliance
    8    *  with the License.  You may obtain a copy of the License at
    9    *
    10   *    http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   *  Unless required by applicable law or agreed to in writing,
    13   *  software distributed under the License is distributed on an
    14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    15   *  KIND, either express or implied.  See the License for the
    16   *  specific language governing permissions and limitations
    17   *  under the License.
    18   *
    19   */
    20  package org.apache.mina.statemachine;
    21
    22  import static org.junit.Assert.assertEquals;
    23  import static org.junit.Assert.assertTrue;
    24
    25  import java.util.LinkedList;
    26
    27  import org.apache.mina.statemachine.annotation.Transition;
    28  import org.apache.mina.statemachine.annotation.Transitions;
    29  import org.apache.mina.statemachine.annotation.OnEntry;
    30  import org.apache.mina.statemachine.annotation.OnExit;
    31  import org.apache.mina.statemachine.context.StateContext;
    32  import org.apache.mina.statemachine.event.Event;
    33  import org.apache.mina.statemachine.transition.MethodSelfTransition;
    34  import org.apache.mina.statemachine.transition.MethodTransition;
    35  import org.junit.Test;
    36
    37  /**
    38   * Tests {@link StateMachineProxyBuilder}.
    39   *
    40   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
    41   */
    42  public class StateMachineProxyBuilderTest {
    43      @Test
    44      public void testReentrantStateMachine() throws Exception {
    45          ReentrantStateMachineHandler handler = new ReentrantStateMachineHandler();
    46
    47          State s1 = new State("s1");
    48          State s2 = new State("s2");
    49          State s3 = new State("s3");
    50
    51          s1.addTransition(new MethodTransition("call1", s2, handler));
    52          s2.addTransition(new MethodTransition("call2", s3, handler));
    53          s3.addTransition(new MethodTransition("call3", handler));
    54
    55          StateMachine sm = new StateMachine(new State[] { s1, s2, s3 }, "s1");
    56          Reentrant reentrant = new StateMachineProxyBuilder().create(Reentrant.class, sm);
    57          reentrant.call1(reentrant);
    58          assertTrue(handler.finished);
    59      }
    60
    61      @Test
    62      public void testTapeDeckStateMachine() throws Exception {
    63          TapeDeckStateMachineHandler handler = new TapeDeckStateMachineHandler();
    64
    65          State parent = new State("parent");
    66          State s1 = new State("s1", parent);
    67          State s2 = new State("s2", parent);
    68          State s3 = new State("s3", parent);
    69          State s4 = new State("s4", parent);
    70          State s5 = new State("s5", parent);
    71
    72          parent.addTransition(new MethodTransition("*", "error", handler));
    73          s1.addTransition(new MethodTransition("insert", s2, "inserted", handler));
    74          s2.addTransition(new MethodTransition("start", s3, "playing", handler));
    75          s3.addTransition(new MethodTransition("stop", s4, "stopped", handler));
    76          s3.addTransition(new MethodTransition("pause", s5, "paused", handler));
    77          s4.addTransition(new MethodTransition("eject", s1, "ejected", handler));
    78          s5.addTransition(new MethodTransition("pause", s3, "playing", handler));
    79
    80          s2.addOnEntrySelfTransaction(new MethodSelfTransition("onEntryS2", handler));
    81          s2.addOnExitSelfTransaction(new MethodSelfTransition("onExitS2", handler));
    82
    83          s3.addOnEntrySelfTransaction(new MethodSelfTransition("onEntryS3", handler));
    84          s3.addOnExitSelfTransaction(new MethodSelfTransition("onExitS3", handler));
    85
    86          s4.addOnEntrySelfTransaction(new MethodSelfTransition("onEntryS4", handler));
    87          s4.addOnExitSelfTransaction(new MethodSelfTransition("onExitS4", handler));
    88
    89          StateMachine sm = new StateMachine(new State[] { s1, s2, s3, s4, s5 }, "s1");
    90          TapeDeck player = new StateMachineProxyBuilder().create(TapeDeck.class, sm);
    91          player.insert("Kings of convenience - Riot on an empty street");
    92          player.start();
    93          player.pause();
    94          player.pause();
    95          player.eject();
    96          player.stop();
    97          player.eject();
    98
    99          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 }