Ver código fonte

Abstract state stuff into new ServerStatus class, provide much more useful debugging and enforce legal state transitions

Issue 1767

git-svn-id: http://svn.dmdirc.com/trunk@4766 00569f92-eb28-0410-84fd-f71c24880f
tags/0.6.3m1rc1
Chris Smith 15 anos atrás
pai
commit
377b454d0a

+ 31
- 28
src/com/dmdirc/Server.java Ver arquivo

@@ -99,7 +99,8 @@ public final class Server extends WritableFrameContainer implements Serializable
99 99
     private transient Identity profile;
100 100
 
101 101
     /** The current state of this server. */
102
-    private ServerState myState = ServerState.DISCONNECTED;
102
+    private final ServerStatus myState = new ServerStatus();
103
+
103 104
     /** The timer we're using to delay reconnects. */
104 105
     private Timer reconnectTimer;
105 106
 
@@ -238,7 +239,7 @@ public final class Server extends WritableFrameContainer implements Serializable
238 239
         assert profile != null;
239 240
 
240 241
         synchronized (this) {
241
-            switch (myState) {
242
+            switch (myState.getState()) {
242 243
                 case RECONNECT_WAIT:
243 244
                     reconnectTimer.cancel();
244 245
                     break;
@@ -262,7 +263,7 @@ public final class Server extends WritableFrameContainer implements Serializable
262 263
                         + "is still connected.\n\nMy state:" + myState);
263 264
             }
264 265
             
265
-            myState = ServerState.CONNECTING;
266
+            myState.transition(ServerState.CONNECTING);
266 267
         
267 268
             ActionManager.processEvent(CoreActionType.SERVER_CONNECTING, null, this);
268 269
 
@@ -327,7 +328,7 @@ public final class Server extends WritableFrameContainer implements Serializable
327 328
      */
328 329
     public void reconnect(final String reason) {
329 330
         synchronized (this) {
330
-            if (myState == ServerState.CLOSING) {
331
+            if (myState.getState() == ServerState.CLOSING) {
331 332
                 return;
332 333
             }
333 334
         
@@ -358,7 +359,7 @@ public final class Server extends WritableFrameContainer implements Serializable
358 359
      */
359 360
     public void disconnect(final String reason) {
360 361
         synchronized (this) {
361
-            switch (myState) {
362
+            switch (myState.getState()) {
362 363
             case CLOSING:
363 364
             case DISCONNECTED:
364 365
             case TRANSIENTLY_DISCONNECTED:
@@ -370,13 +371,13 @@ public final class Server extends WritableFrameContainer implements Serializable
370 371
                 break;
371 372
             }
372 373
 
373
-            myState = ServerState.DISCONNECTING;
374
+            myState.transition(ServerState.DISCONNECTING);
374 375
         
375 376
             removeInvites();
376 377
             updateIcon();
377 378
 
378 379
             if (parser == null) {
379
-                myState = ServerState.DISCONNECTED;
380
+                myState.transition(ServerState.DISCONNECTED);
380 381
             } else {
381 382
                 parser.disconnect(reason);
382 383
             }
@@ -401,7 +402,7 @@ public final class Server extends WritableFrameContainer implements Serializable
401 402
     @Precondition("The server state is transiently disconnected")
402 403
     private void doDelayedReconnect() {
403 404
         synchronized (this) {
404
-            if (myState != ServerState.TRANSIENTLY_DISCONNECTED) {
405
+            if (myState.getState() != ServerState.TRANSIENTLY_DISCONNECTED) {
405 406
                 throw new IllegalStateException("doDelayedReconnect when not "
406 407
                         + "transiently disconnected\n\nState: " + myState);
407 408
             }
@@ -416,15 +417,15 @@ public final class Server extends WritableFrameContainer implements Serializable
416 417
                 @Override
417 418
                 public void run() {
418 419
                     synchronized (Server.this) {
419
-                        if (myState == ServerState.RECONNECT_WAIT) {
420
-                            myState = ServerState.TRANSIENTLY_DISCONNECTED;
420
+                        if (myState.getState() == ServerState.RECONNECT_WAIT) {
421
+                            myState.transition(ServerState.TRANSIENTLY_DISCONNECTED);
421 422
                             reconnect();
422 423
                         }
423 424
                     }
424 425
                 }
425 426
             }, delay);
426 427
 
427
-            myState = ServerState.RECONNECT_WAIT;
428
+            myState.transition(ServerState.RECONNECT_WAIT);
428 429
             updateIcon();
429 430
         }
430 431
     }
@@ -560,7 +561,7 @@ public final class Server extends WritableFrameContainer implements Serializable
560 561
      */
561 562
     public void addChannel(final ChannelInfo chan) {
562 563
         synchronized (this) {
563
-            if (myState == ServerState.CLOSING) {
564
+            if (myState.getState() == ServerState.CLOSING) {
564 565
                 // Can't join channels while the server is closing
565 566
                 return;
566 567
             }
@@ -585,7 +586,7 @@ public final class Server extends WritableFrameContainer implements Serializable
585 586
      */
586 587
     public void addQuery(final String host) {
587 588
         synchronized (this) {
588
-            if (myState == ServerState.CLOSING) {
589
+            if (myState.getState() == ServerState.CLOSING) {
589 590
                 // Can't open queries while the server is closing
590 591
                 return;
591 592
             }
@@ -660,7 +661,7 @@ public final class Server extends WritableFrameContainer implements Serializable
660 661
      * Updates this server's icon.
661 662
      */
662 663
     private void updateIcon() {
663
-        final String icon = myState == ServerState.CONNECTED
664
+        final String icon = myState.getState() == ServerState.CONNECTED
664 665
                     ? serverInfo.getSSL() ? "secure-server" : "server"
665 666
                     : "server-disconnected";
666 667
         setIcon(icon);
@@ -709,7 +710,7 @@ public final class Server extends WritableFrameContainer implements Serializable
709 710
      */
710 711
     public void join(final String channel) {
711 712
         synchronized (this) {
712
-            if (myState == ServerState.CONNECTED) {
713
+            if (myState.getState() == ServerState.CONNECTED) {
713 714
                 removeInvites(channel);
714 715
 
715 716
                 if (hasChannel(channel)) {
@@ -728,7 +729,7 @@ public final class Server extends WritableFrameContainer implements Serializable
728 729
     @Override
729 730
     public void sendLine(final String line) {
730 731
         synchronized (this) {
731
-            if (parser != null && myState == ServerState.CONNECTED) {
732
+            if (parser != null && myState.getState() == ServerState.CONNECTED) {
732 733
                 parser.sendLine(window.getTranscoder().encode(line));
733 734
             }
734 735
         }
@@ -877,7 +878,7 @@ public final class Server extends WritableFrameContainer implements Serializable
877 878
      * @return This server's state
878 879
      */
879 880
     public ServerState getState() {
880
-        return myState;
881
+        return myState.getState();
881 882
     }
882 883
 
883 884
     /** {@inheritDoc} */
@@ -895,7 +896,7 @@ public final class Server extends WritableFrameContainer implements Serializable
895 896
                 disconnect();
896 897
             }
897 898
 
898
-            myState = ServerState.CLOSING;
899
+            myState.transition(ServerState.CLOSING);
899 900
             closeChannels();
900 901
             closeQueries();
901 902
             removeInvites();
@@ -1138,15 +1139,16 @@ public final class Server extends WritableFrameContainer implements Serializable
1138 1139
         eventHandler.unregisterCallbacks();
1139 1140
 
1140 1141
         synchronized (this) {
1141
-            if (myState == ServerState.CLOSING || myState == ServerState.DISCONNECTED) {
1142
+            if (myState.getState() == ServerState.CLOSING
1143
+                    || myState.getState() == ServerState.DISCONNECTED) {
1142 1144
                 // This has been triggered via .disconect()
1143 1145
                 return;
1144 1146
             }
1145 1147
             
1146
-            if (myState == ServerState.DISCONNECTING) {
1147
-                myState = ServerState.DISCONNECTED;
1148
+            if (myState.getState() == ServerState.DISCONNECTING) {
1149
+                myState.transition(ServerState.DISCONNECTED);
1148 1150
             } else {
1149
-                myState = ServerState.TRANSIENTLY_DISCONNECTED;
1151
+                myState.transition(ServerState.TRANSIENTLY_DISCONNECTED);
1150 1152
             }
1151 1153
 
1152 1154
             updateIcon();
@@ -1168,7 +1170,7 @@ public final class Server extends WritableFrameContainer implements Serializable
1168 1170
 
1169 1171
             if (getConfigManager().getOptionBool(DOMAIN_GENERAL,
1170 1172
                     "reconnectondisconnect", false)
1171
-                    && myState == ServerState.TRANSIENTLY_DISCONNECTED) {
1173
+                    && myState.getState() == ServerState.TRANSIENTLY_DISCONNECTED) {
1172 1174
                 doDelayedReconnect();
1173 1175
             }        
1174 1176
         }
@@ -1182,16 +1184,17 @@ public final class Server extends WritableFrameContainer implements Serializable
1182 1184
     @Precondition("The current server state is CONNECTING")
1183 1185
     public void onConnectError(final ParserError errorInfo) {
1184 1186
         synchronized (this) {
1185
-            if (myState == ServerState.CLOSING || myState == ServerState.DISCONNECTING) {
1187
+            if (myState.getState() == ServerState.CLOSING
1188
+                    || myState.getState() == ServerState.DISCONNECTING) {
1186 1189
                 // Do nothing
1187 1190
                 return;
1188
-            } else if (myState != ServerState.CONNECTING) {
1191
+            } else if (myState.getState() != ServerState.CONNECTING) {
1189 1192
                 // Shouldn't happen
1190 1193
                 throw new IllegalStateException("Connect error when not "
1191 1194
                         + "connecting\n\nState: " + myState);
1192 1195
             }
1193 1196
 
1194
-            myState = ServerState.TRANSIENTLY_DISCONNECTED;
1197
+            myState.transition(ServerState.TRANSIENTLY_DISCONNECTED);
1195 1198
         
1196 1199
             updateIcon();
1197 1200
 
@@ -1251,13 +1254,13 @@ public final class Server extends WritableFrameContainer implements Serializable
1251 1254
     @Precondition("State is CONNECTING")
1252 1255
     public void onPost005() {
1253 1256
         synchronized (this) {
1254
-            if (myState != ServerState.CONNECTING) {
1257
+            if (myState.getState() != ServerState.CONNECTING) {
1255 1258
                 // Shouldn't happen
1256 1259
                 throw new IllegalStateException("Received onPost005 while not "
1257 1260
                         + "connecting\n\nState: " + myState);
1258 1261
             }
1259 1262
             
1260
-            myState = ServerState.CONNECTED;
1263
+            myState.transition(ServerState.CONNECTED);
1261 1264
         
1262 1265
             updateIcon();
1263 1266
 

+ 69
- 8
src/com/dmdirc/ServerState.java Ver arquivo

@@ -22,20 +22,81 @@
22 22
 
23 23
 package com.dmdirc;
24 24
 
25
-/** An enumeration of possible states for servers. */
25
+import java.util.Arrays;
26
+import java.util.List;
27
+
28
+/**
29
+ * An enumeration of possible states for servers.
30
+ */
26 31
 public enum ServerState {
32
+
27 33
     /** Indicates the client is in the process of connecting. */
28
-    CONNECTING,
34
+    CONNECTING(
35
+            "CONNECTED",                 // Connection attempt succeeded
36
+            "TRANSIENTLY_DISCONNECTED",  // Connection attempt failed
37
+            "DISCONNECTING",             // User ordered a disconnection
38
+            "CLOSING"                    // DMDirc is closing
39
+    ),
40
+
29 41
     /** Indicates the client has connected to the server. */
30
-    CONNECTED,
42
+    CONNECTED(
43
+            "DISCONNECTING",             // User ordered a disconnection
44
+            "TRANSIENTLY_DISCONNECTED",  // Server caused a disconnection
45
+            "CLOSING"                    // DMDirc is closing
46
+    ),
47
+
31 48
     /** Indicates that we've been temporarily disconnected. */
32
-    TRANSIENTLY_DISCONNECTED,
49
+    TRANSIENTLY_DISCONNECTED(
50
+            "CONNECTING",                // User forced a connect attempt
51
+            "RECONNECT_WAIT",            // Waiting for auto-reconnect
52
+            "CLOSING"                    // DMDirc is closing
53
+    ),
54
+
33 55
     /** Indicates that the user has told us to disconnect. */
34
-    DISCONNECTED,
56
+    DISCONNECTED(
57
+            "CONNECTING",                // User forced a connect attempt
58
+            "CLOSING"                    // DMDirc is closing
59
+    ),
60
+
35 61
     /** In the process of disconnecting. */
36
-    DISCONNECTING,
62
+    DISCONNECTING(
63
+            "DISCONNECTED",              // Socket closed
64
+            "CLOSING"                    // DMDirc is closing
65
+    ),
66
+
37 67
     /** Indicates we're waiting for the auto-reconnect timer to fire. */
38
-    RECONNECT_WAIT,
68
+    RECONNECT_WAIT(
69
+            "CONNECTING",                // User forced a connect attempt
70
+            "TRANSIENTLY_DISCONNECTED",  // Reconnect timer expired
71
+            "CLOSING"                    // DMDirc is closing
72
+    ),
73
+
39 74
     /** Indicates that the server frame and its children are closing. */
40
-    CLOSING,
75
+    CLOSING;
76
+
77
+    /** The allowed transitions from this state. */
78
+    private final List<String> transitions;
79
+
80
+    /**
81
+     * Creates a new instance of ServerState.
82
+     *
83
+     * @since 0.6.3
84
+     * @param transitions The names of the states to which a transition is
85
+     * allowed from this state
86
+     */
87
+    ServerState(final String ... transitions) {
88
+        this.transitions = Arrays.asList(transitions);
89
+    }
90
+
91
+    /**
92
+     * Determines whether a transition from this state to the specified state
93
+     * would be legal.
94
+     *
95
+     * @since 0.6.3
96
+     * @param state The state that is being transitioned to
97
+     * @return True if the transition is allowed, false otherwise.
98
+     */
99
+    public boolean canTransitionTo(final ServerState state) {
100
+        return transitions.contains(state.name());
101
+    }
41 102
 }

+ 111
- 0
src/com/dmdirc/ServerStatus.java Ver arquivo

@@ -0,0 +1,111 @@
1
+/*
2
+ * Copyright (c) 2006-2008 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc;
24
+
25
+import com.dmdirc.util.RollingList;
26
+
27
+/**
28
+ * Describes the status of a server and manages transitions between different
29
+ * states.
30
+ *
31
+ * @since 0.6.3
32
+ * @author chris
33
+ */
34
+public class ServerStatus {
35
+
36
+    /** The current state of the server. */
37
+    protected ServerState state = ServerState.DISCONNECTED;
38
+
39
+    /** A history of transactions for debugging purposes. */
40
+    protected RollingList<String> history = new RollingList<String>(10);
41
+
42
+    /**
43
+     * Transitions the status of this object to the specified state.
44
+     *
45
+     * @param newState The state to transition to
46
+     */
47
+    public synchronized void transition(final ServerState newState) {
48
+        addHistoryEntry(state, newState);
49
+
50
+        if (state.canTransitionTo(newState)) {
51
+            state = newState;
52
+        } else {
53
+            throw new IllegalArgumentException("Illegal server state "
54
+                    + "transition\n\n" + getTransitionHistory());
55
+        }
56
+    }
57
+
58
+    /**
59
+     * Retrieves the current state of this status object.
60
+     *
61
+     * @return This object's current state
62
+     */
63
+    public synchronized ServerState getState() {
64
+        return state;
65
+    }
66
+
67
+    /**
68
+     * Adds a history entry to this status object. The history entry contains
69
+     * the name of the states being transitioned between, the details of the
70
+     * method (and class and line) which initiated the transition, and the
71
+     * name of the thread in which the transition is occuring.
72
+     *
73
+     * @param fromState The state which is being transitioned from
74
+     * @param toState The state which is being transitioned to
75
+     */
76
+    protected void addHistoryEntry(final ServerState fromState, final ServerState toState) {
77
+        final StringBuilder builder = new StringBuilder();
78
+        builder.append(fromState.name());
79
+        builder.append('→');
80
+        builder.append(toState.name());
81
+        builder.append(' ');
82
+        builder.append(Thread.currentThread().getStackTrace()[2].toString());
83
+        builder.append(" [");
84
+        builder.append(Thread.currentThread().getName());
85
+        builder.append(']');
86
+
87
+        history.add(builder.toString());
88
+    }
89
+
90
+    /**
91
+     * Retrieves the transition history of this status object as a string.
92
+     *
93
+     * @see #addHistoryEntry(com.dmdirc.ServerState, com.dmdirc.ServerState)
94
+     * @return A line feed ('\n') delimited string containing one entry for
95
+     * each of the entries in this status's transition history.
96
+     */
97
+    public String getTransitionHistory() {
98
+        final StringBuilder builder = new StringBuilder();
99
+
100
+        for (String line : history.getList()) {
101
+            if (builder.length() > 0) {
102
+                builder.append('\n');
103
+            }
104
+
105
+            builder.append(line);
106
+        }
107
+
108
+        return builder.toString();
109
+    }
110
+
111
+}

Carregando…
Cancelar
Salvar