1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package com.tomgibara.pronto.control.impl;
19
20 import java.util.Set;
21 import java.util.logging.Level;
22
23 import com.tomgibara.pronto.control.SignalControllerSettings;
24 import com.tomgibara.pronto.state.PathType;
25 import com.tomgibara.pronto.state.ProntoStateException;
26 import com.tomgibara.pronto.state.StateEngine;
27 import com.tomgibara.pronto.util.Waiter;
28
29 /**
30 * A ControllerPart implementation that provides functionality for the
31 * SignalControllerSettings interface.
32 *
33 * @param <S>
34 * the type of state
35 * @param <L>
36 * the type of label
37 * @param <P>
38 * the type of parameter
39 *
40 * @author Tom Gibara
41 *
42 */
43
44 public class SignalControllerPart<S, L, P> implements ControllerPart<S, L, P> {
45
46
47
48 /**
49 * The lock that must be held when performing any action that is dependent
50 * upon the object's lifespan.
51 */
52 private final Object lock = new Object();
53
54 /**
55 * The controller that aggregates this part.
56 */
57 private final ControllerImpl<S, L, P> controller;
58
59 /**
60 * The settings used to construct this part. Currently unused because there
61 * are no settings.
62 */
63 private final SignalControllerSettings settings;
64
65 /**
66 * The thread that is registered as a shutdown hook to perform termination
67 * on the state engine. The value of this field is used to record whether
68 * the part has been started (null=stopped, not-null=started).
69 */
70 private SignalThread thread = null;
71
72 /**
73 * Indicates that the termination state transitions have been completed.
74 */
75 private boolean finished = false;
76
77
78
79 /**
80 * Creates a new SignalControllerPart.
81 *
82 * @param controller
83 * the controller that aggregates this part
84 * @param settings
85 * the settings for this part
86 */
87
88 public SignalControllerPart(final ControllerImpl<S, L, P> controller, final SignalControllerSettings settings) {
89 this.controller = controller;
90 this.settings = settings;
91 }
92
93
94
95 /**
96 * Causes a shutdown hook to be registered for the purpose of performing
97 * termination state transitions.
98 */
99
100 public void start() {
101 synchronized (lock) {
102 if (thread != null) return;
103
104 thread = new SignalThread();
105 Runtime.getRuntime().addShutdownHook(thread);
106 }
107 }
108
109 /**
110 * If a shutdown hook as been registered, the hook is deregistered and the
111 * method returns immediately unless JVM shutdown is in progress in which
112 * case the method blocks (subject to the specified timeout) until
113 * termination state transitions have been completed.
114 *
115 * @param timeout
116 * the (approx.) number of milliseconds for which a call to the
117 * method will block, or 0L.
118 * @return true if the part was able to confirm that all operation had
119 * ceased
120 */
121
122 public boolean stop(final long timeout) {
123 synchronized (lock) {
124 if (thread == null) return true;
125
126 try {
127 Runtime.getRuntime().removeShutdownHook(thread);
128 return true;
129 } catch (IllegalStateException e) {
130
131
132 return new Waiter(lock).waitForCondition(timeout, new Waiter.Condition() {
133 public boolean isMet() {
134 return finished;
135 }
136 });
137 } finally {
138 thread = null;
139 }
140 }
141 }
142
143 public ControllerImpl<S, L, P> getController() {
144 return controller;
145 }
146
147
148
149 private void terminate() {
150 StateEngine<S, L, P> engine = controller.engine;
151 Set<S> terminalStates = engine.getGraph().getTerminalStates();
152 if (terminalStates.size() != 1) return;
153 S terminalState = terminalStates.iterator().next();
154
155 if (engine.getState() == terminalState) return;
156 try {
157 engine.pathTransition(terminalState, null, PathType.strictlyUniqueStates, null);
158 } catch (ProntoStateException e) {
159 ControlFactoryImpl.LOGGER.log(Level.WARNING, "State transition failed in response to signal", e);
160 }
161 }
162
163
164
165 /**
166 * An instance of this thread remains registered as a shutdown hook while
167 * the part is running. Its sole duty is to perform the termination
168 * transitions on the state engine.
169 */
170
171 class SignalThread extends Thread {
172
173 public SignalThread() {
174 super("pronto controller shutdown");
175 }
176
177 @Override
178 public void run() {
179 synchronized (lock) {
180 try {
181 terminate();
182 } finally {
183 finished = true;
184 }
185 }
186 }
187
188 }
189
190 }