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