8
8
import org .apache .logging .log4j .Logger ;
9
9
import org .apache .logging .log4j .Marker ;
10
10
import org .apache .logging .log4j .MarkerManager ;
11
+ import org .apache .logging .log4j .message .StringMapMessage ;
11
12
import prt .exceptions .*;
12
- import java .io .Serializable ;
13
13
14
14
/**
15
15
* A prt.Monitor encapsulates a state machine.
16
16
*
17
17
*/
18
- public abstract class Monitor <StateKey extends Enum <StateKey >> implements Consumer <PEvent <?>>, Serializable {
19
- private static final Logger logger = LogManager .getLogger (Monitor . class );
18
+ public abstract class Monitor <StateKey extends Enum <StateKey >> implements Consumer <PEvent <?>> {
19
+ private final Logger logger = LogManager .getLogger (this . getClass () );
20
20
private static final Marker PROCESSING_MARKER = MarkerManager .getMarker ("EVENT_PROCESSING" );
21
21
private static final Marker TRANSITIONING_MARKER = MarkerManager .getMarker ("STATE_TRANSITIONING" );
22
22
23
- private StateKey startStateKey ;
24
- private StateKey currentStateKey ;
23
+ @ SuppressWarnings ("OptionalUsedAsFieldOrParameterType" )
24
+ private Optional <State <StateKey >> startState ;
25
+ private State <StateKey > currentState ;
25
26
26
- private transient EnumMap <StateKey , State <StateKey >> states ; // All registered states
27
+ private EnumMap <StateKey , State <StateKey >> states ; // All registered states
27
28
private StateKey [] stateUniverse ; // all possible states
28
29
29
30
/**
@@ -43,17 +44,6 @@ protected void addState(State<StateKey> s) {
43
44
throw new RuntimeException ("prt.Monitor is already running; no new states may be added." );
44
45
}
45
46
46
- registerState (s );
47
-
48
- if (s .isInitialState ()) {
49
- if (startStateKey != null ) {
50
- throw new RuntimeException ("Initial state already set to " + startStateKey );
51
- }
52
- startStateKey = s .getKey ();
53
- }
54
- }
55
-
56
- protected void registerState (State <StateKey > s ) {
57
47
if (states == null ) {
58
48
states = new EnumMap <>((Class <StateKey >) s .getKey ().getClass ());
59
49
stateUniverse = s .getKey ().getDeclaringClass ().getEnumConstants ();
@@ -63,14 +53,21 @@ protected void registerState(State<StateKey> s) {
63
53
throw new RuntimeException ("prt.State already present" );
64
54
}
65
55
states .put (s .getKey (), s );
56
+
57
+ if (s .isInitialState ()) {
58
+ if (startState .isPresent ()) {
59
+ throw new RuntimeException ("Initial state already set to " + startState .get ().getKey ());
60
+ }
61
+ startState = Optional .of (s );
62
+ }
66
63
}
67
64
68
65
public StateKey getCurrentState () {
69
66
if (!isRunning ) {
70
67
throw new RuntimeException ("prt.Monitor is not running (did you call ready()?)" );
71
68
}
72
69
73
- return currentStateKey ;
70
+ return currentState . getKey () ;
74
71
}
75
72
76
73
/**
@@ -142,11 +139,10 @@ public void accept(PEvent<?> p) throws UnhandledEventException {
142
139
throw new RuntimeException ("prt.Monitor is not running (did you call ready()?)" );
143
140
}
144
141
145
- // logger.info(PROCESSING_MARKER, new StringMapMessage().with("event", p));
142
+ logger .info (PROCESSING_MARKER , new StringMapMessage ().with ("event" , p ));
146
143
147
144
// XXX: We can technically avoid this downcast, but to fulfill the interface for Consumer<T>
148
145
// this method cannot accept a type parameter, so this can't be a TransitionableConsumer<P>.
149
- State <StateKey > currentState = states .get (currentStateKey );
150
146
Optional <State .TransitionableConsumer <Object >> oc = currentState .getHandler (p .getClass ());
151
147
if (oc .isEmpty ()) {
152
148
logger .atFatal ().log (currentState + " missing event handler for " + p .getClass ().getSimpleName ());
@@ -161,20 +157,19 @@ public void accept(PEvent<?> p) throws UnhandledEventException {
161
157
* entry handler, and updating internal bookkeeping.
162
158
* @param s The new state.
163
159
*/
164
- private <P > void handleTransition (State <StateKey > s , P payload ) {
160
+ private <P > void handleTransition (State <StateKey > s , Optional < P > payload ) {
165
161
if (!isRunning ) {
166
162
throw new RuntimeException ("prt.Monitor is not running (did you call ready()?)" );
167
163
}
168
164
169
- // logger.info(TRANSITIONING_MARKER, new StringMapMessage().with("state", s));
165
+ logger .info (TRANSITIONING_MARKER , new StringMapMessage ().with ("state" , s ));
170
166
171
- State <StateKey > currentState = states .get (currentStateKey );
172
167
currentState .getOnExit ().ifPresent (Runnable ::run );
173
168
currentState = s ;
174
- currentStateKey = s .getKey ();
175
169
176
170
currentState .getOnEntry ().ifPresent (handler -> {
177
- invokeWithTrampoline (handler , payload );
171
+ Object p = payload .orElse (null );
172
+ invokeWithTrampoline (handler , p );
178
173
});
179
174
}
180
175
@@ -204,7 +199,7 @@ private <P> void invokeWithTrampoline(State.TransitionableConsumer<P> handler, P
204
199
* must be a handler of zero parameters, will be invoked.
205
200
*/
206
201
public void ready () {
207
- readyImpl (null );
202
+ readyImpl (Optional . empty () );
208
203
}
209
204
210
205
/**
@@ -213,10 +208,10 @@ public void ready() {
213
208
* @param payload The argument to the initial state's entry handler.
214
209
*/
215
210
public <P > void ready (P payload ) {
216
- readyImpl (payload );
211
+ readyImpl (Optional . of ( payload ) );
217
212
}
218
213
219
- private <P > void readyImpl (P payload ) {
214
+ private <P > void readyImpl (Optional < P > payload ) {
220
215
if (isRunning ) {
221
216
throw new RuntimeException ("prt.Monitor is already running." );
222
217
}
@@ -229,27 +224,27 @@ private <P> void readyImpl(P payload) {
229
224
230
225
isRunning = true ;
231
226
232
- currentStateKey = startStateKey ;
233
- State <StateKey > currentState = states .get (currentStateKey );
227
+ currentState = startState .orElseThrow (() ->
228
+ new RuntimeException (
229
+ "No initial state set (did you specify an initial state, or is the machine halted?)" ));
234
230
235
231
currentState .getOnEntry ().ifPresent (handler -> {
236
- invokeWithTrampoline (handler , payload );
232
+ Object p = payload .orElse (null );
233
+ invokeWithTrampoline (handler , p );
237
234
});
238
235
}
239
236
240
237
/**
241
238
* Instantiates a new prt.Monitor; users should provide domain-specific functionality in a subclass.
242
239
*/
243
240
protected Monitor () {
244
- startStateKey = null ;
241
+ startState = Optional . empty () ;
245
242
isRunning = false ;
246
243
247
244
states = null ; // We need a concrete class to instantiate an EnumMap; do this lazily on the first addState() call.
248
- currentStateKey = null ; // So long as we have not yet readied, this will be null!
245
+ currentState = null ; // So long as we have not yet readied, this will be null!
249
246
}
250
247
251
248
public abstract List <Class <? extends PEvent <?>>> getEventTypes ();
252
249
253
- public abstract void reInitializeMonitor ();
254
-
255
250
}
0 commit comments