1 package org.metricshub.ipmi.core.sm;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 import java.io.IOException;
26 import java.net.InetAddress;
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.concurrent.CopyOnWriteArrayList;
30
31 import org.metricshub.ipmi.core.coding.rmcp.RmcpClassOfMessage;
32 import org.metricshub.ipmi.core.coding.rmcp.RmcpDecoder;
33 import org.metricshub.ipmi.core.coding.rmcp.RmcpMessage;
34 import org.metricshub.ipmi.core.common.Constants;
35 import org.metricshub.ipmi.core.sm.actions.MessageAction;
36 import org.metricshub.ipmi.core.sm.actions.StateMachineAction;
37 import org.metricshub.ipmi.core.sm.events.StateMachineEvent;
38 import org.metricshub.ipmi.core.sm.states.State;
39 import org.metricshub.ipmi.core.sm.states.Uninitialized;
40 import org.metricshub.ipmi.core.transport.Messenger;
41 import org.metricshub.ipmi.core.transport.UdpListener;
42 import org.metricshub.ipmi.core.transport.UdpMessage;
43
44
45
46
47
48 public class StateMachine implements UdpListener {
49
50 private final List<MachineObserver> observers = new CopyOnWriteArrayList<MachineObserver>();
51
52
53
54
55
56
57
58 private final List<StateMachineAction> pendingActions = new ArrayList<StateMachineAction>();
59
60 private volatile State current;
61
62 private Messenger messenger;
63 private volatile InetAddress remoteMachineAddress;
64 private volatile int remoteMachinePort;
65
66 private volatile boolean initialized;
67
68 public State getCurrent() {
69 return current;
70 }
71
72 public void setCurrent(State current) {
73 this.current = current;
74 current.onEnter(this);
75 }
76
77
78
79
80
81
82
83
84 public StateMachine(Messenger messenger) {
85 this.messenger = messenger;
86 initialized = false;
87 }
88
89
90
91
92
93
94
95
96
97 public void sendMessage(byte[] message) throws IOException {
98 UdpMessage udpMessage = new UdpMessage();
99 udpMessage.setAddress(getRemoteMachineAddress());
100 udpMessage.setPort(getRemoteMachinePort());
101 udpMessage.setMessage(message);
102 messenger.send(udpMessage);
103 }
104
105 public InetAddress getRemoteMachineAddress() {
106 return remoteMachineAddress;
107 }
108
109 public int getRemoteMachinePort() {
110 return remoteMachinePort;
111 }
112
113
114
115
116
117
118
119
120 public void doExternalAction(StateMachineAction action) {
121 if (action instanceof MessageAction && Thread.holdsLock(this)) {
122 pendingActions.add(action);
123 } else {
124 notifyObservers(action);
125 }
126 }
127
128 private void notifyObservers(StateMachineAction action) {
129 for (MachineObserver observer : observers) {
130 if (observer != null) {
131 observer.notify(action);
132 }
133 }
134 }
135
136 private void dispatch(List<StateMachineAction> actions) {
137 for (StateMachineAction action : actions) {
138 notifyObservers(action);
139 }
140 }
141
142
143 private List<StateMachineAction> drainPendingActions() {
144 List<StateMachineAction> actions = new ArrayList<StateMachineAction>(pendingActions);
145 pendingActions.clear();
146 return actions;
147 }
148
149
150
151
152
153
154
155
156
157
158 public void start(InetAddress address, int port) {
159 messenger.register(this);
160 remoteMachineAddress = address;
161 this.remoteMachinePort = port;
162 setCurrent(new Uninitialized());
163 initialized = true;
164 }
165
166
167
168
169
170
171
172 public void stop() {
173 messenger.unregister(this);
174 initialized = false;
175 current = new Uninitialized();
176 }
177
178
179
180
181
182
183 public boolean isActive() {
184 return initialized;
185 }
186
187
188
189
190
191
192
193
194
195
196
197
198
199 public void doTransition(StateMachineEvent event) {
200 List<StateMachineAction> actions;
201 synchronized (this) {
202 if (!initialized) {
203 throw new NullPointerException("State machine not started");
204 }
205 current.doTransition(this, event);
206 actions = drainPendingActions();
207 }
208 dispatch(actions);
209 }
210
211 @Override
212 public void notifyMessage(UdpMessage message) {
213 if (!message.getAddress().equals(getRemoteMachineAddress()) || message.getPort() != getRemoteMachinePort()) {
214 return;
215 }
216 List<StateMachineAction> actions;
217 synchronized (this) {
218 try {
219 RmcpMessage rmcpMessage = RmcpDecoder.decode(message.getMessage());
220 if (rmcpMessage.getClassOfMessage() == RmcpClassOfMessage.Ipmi) {
221 current.doAction(this, rmcpMessage);
222 }
223 } catch (RuntimeException e) {
224
225
226 }
227 actions = drainPendingActions();
228 }
229 dispatch(actions);
230 }
231
232
233
234
235
236
237
238
239
240 public void register(MachineObserver observer) {
241 observers.add(observer);
242 }
243 }