blob: 2bb4e1d919b8d78bcd7710e4980e98366d19826f [file] [log] [blame]
Jonathan Hartaa380972014-04-03 10:24:46 -07001package net.onrc.onos.core.intent;
Toshio Koideb609b3b2014-02-14 18:25:52 -08002
3import java.util.Collection;
4import java.util.EventListener;
5import java.util.HashMap;
6import java.util.HashSet;
Toshio Koideb609b3b2014-02-14 18:25:52 -08007import java.util.LinkedList;
Toshio Koideb609b3b2014-02-14 18:25:52 -08008import java.util.Map.Entry;
9
Jonathan Hartaa380972014-04-03 10:24:46 -070010import net.onrc.onos.core.intent.Intent.IntentState;
11import net.onrc.onos.core.intent.runtime.IntentStateList;
Brian O'Connora581b9d2014-06-15 23:32:36 -070012
Ray Milkey0b122ed2014-04-14 10:06:03 -070013import org.slf4j.Logger;
14import org.slf4j.LoggerFactory;
Toshio Koideb609b3b2014-02-14 18:25:52 -080015
16/**
Brian O'Connora581b9d2014-06-15 23:32:36 -070017 * The IntentMap contains all of the Intents in the system. It is also
18 * responsible for handling notifications to interested listeners when
19 * Intents are changed/updated.
Toshio Koideb609b3b2014-02-14 18:25:52 -080020 */
21public class IntentMap {
Brian O'Connora581b9d2014-06-15 23:32:36 -070022 private final HashSet<ChangedListener> listeners = new HashSet<>();
23 private final HashMap<String, Intent> intents = new HashMap<>();
24 private final LinkedList<ChangedEvent> events = new LinkedList<>();
Ray Milkey0b122ed2014-04-14 10:06:03 -070025 private static final Logger log = LoggerFactory.getLogger(IntentMap.class);
Toshio Koidedf2eab92014-02-20 11:24:59 -080026
Ray Milkey269ffb92014-04-03 14:43:30 -070027 public enum ChangedEventType {
28 /**
29 * Added new intent.
30 */
31 ADDED,
Toshio Koideb609b3b2014-02-14 18:25:52 -080032
Ray Milkey269ffb92014-04-03 14:43:30 -070033 /**
34 * Removed existing intent.
35 * The specified intent is an instance of Intent class (not a child class)
36 * Only id and state are valid.
37 */
38 REMOVED,
Toshio Koideb609b3b2014-02-14 18:25:52 -080039
Ray Milkey269ffb92014-04-03 14:43:30 -070040 /**
41 * Changed state of existing intent.
42 * The specified intent is an instance of Intent class (not a child class)
43 * Only id and state are valid.
44 */
45 STATE_CHANGED,
46 }
Toshio Koideb609b3b2014-02-14 18:25:52 -080047
Brian O'Connora581b9d2014-06-15 23:32:36 -070048 /**
49 * This class is a wrapper for Intent event notifications.
50 */
Pavlin Radoslavovfee80982014-04-10 12:12:04 -070051 public static class ChangedEvent {
Brian O'Connora581b9d2014-06-15 23:32:36 -070052
53 /**
54 * Constructor.
55 *
56 * @param eventType the type of event
57 * @param intent the affected Intent
58 */
Ray Milkey269ffb92014-04-03 14:43:30 -070059 public ChangedEvent(ChangedEventType eventType, Intent intent) {
60 this.eventType = eventType;
61 this.intent = intent;
62 }
Toshio Koideb609b3b2014-02-14 18:25:52 -080063
Ray Milkey269ffb92014-04-03 14:43:30 -070064 public ChangedEventType eventType;
65 public Intent intent;
Pavlin Radoslavovb6309292014-04-11 03:25:47 -070066
67 /**
68 * Get a string representation of the object.
69 * <p/>
70 * The string has the following form:
71 * [eventType=XXX intent=XXX]
72 *
73 * @return a string representation of the object.
74 */
75 @Override
76 public String toString() {
77 StringBuilder ret = new StringBuilder();
78 ret.append("[eventType=");
79 ret.append(this.eventType.toString());
80 ret.append(" intent=");
81 ret.append(this.intent.toString());
82 ret.append("]");
83 return ret.toString();
84 }
Ray Milkey269ffb92014-04-03 14:43:30 -070085 }
Toshio Koideb609b3b2014-02-14 18:25:52 -080086
Brian O'Connora581b9d2014-06-15 23:32:36 -070087 /**
88 * An interface to listen to Intent updates.
89 */
Ray Milkey269ffb92014-04-03 14:43:30 -070090 public interface ChangedListener extends EventListener {
91 void intentsChange(LinkedList<ChangedEvent> events);
92 }
Toshio Koideb609b3b2014-02-14 18:25:52 -080093
Ray Milkey269ffb92014-04-03 14:43:30 -070094 //================================================================================
95 // public methods
96 //================================================================================
Toshio Koideb609b3b2014-02-14 18:25:52 -080097
Brian O'Connora581b9d2014-06-15 23:32:36 -070098 /**
99 * Performs the specified operations on a list of Intents.
100 *
101 * @param operations list of Intents and associated operations
102 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700103 public void executeOperations(IntentOperationList operations) {
104 for (IntentOperation operation : operations) {
Toshio Koide3818b4a2014-06-26 16:15:52 -0700105 if (operation == null) {
106 log.error("executeOperation: null intent operation");
107 continue;
108 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700109 switch (operation.operator) {
Brian O'Connora581b9d2014-06-15 23:32:36 -0700110 case ADD:
111 handleAddOperation(operation);
112 break;
113 case REMOVE:
114 handleRemoveOperation(operation);
115 break;
116 case ERROR:
117 handleErrorOperation(operation);
118 break;
119 default:
Toshio Koide3818b4a2014-06-26 16:15:52 -0700120 log.error("Unknown intent operation: {}", operation.operator);
Brian O'Connora581b9d2014-06-15 23:32:36 -0700121 break;
Ray Milkey269ffb92014-04-03 14:43:30 -0700122 }
123 }
124 notifyEvents();
125 }
Toshio Koidedf2eab92014-02-20 11:24:59 -0800126
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700127 /**
128 * Purge all Intents that are in a state allowing them to be removed.
129 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700130 public void purge() {
131 LinkedList<String> removeIds = new LinkedList<>();
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700132
133 // Collect the IDs of all intents that can be removed
Ray Milkey269ffb92014-04-03 14:43:30 -0700134 for (Entry<String, Intent> entry : intents.entrySet()) {
135 Intent intent = entry.getValue();
136 if (intent.getState() == IntentState.DEL_ACK
137 || intent.getState() == IntentState.INST_NACK) {
138 removeIds.add(intent.getId());
139 }
140 }
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700141
142 purge(removeIds);
143 }
144
145 /**
146 * Purge a collection of Intents specified by Intent IDs.
147 *
148 * NOTE: The caller needs to make sure those intents are in a state
149 * that allows them to be removed.
150 *
151 * @param removeIds the collection of Intent IDs to purge.
152 */
153 public void purge(Collection<String> removeIds) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700154 for (String intentId : removeIds) {
155 removeIntent(intentId);
156 }
157 notifyEvents();
158 }
Toshio Koidedf2eab92014-02-20 11:24:59 -0800159
Brian O'Connora581b9d2014-06-15 23:32:36 -0700160 /**
161 * Update the states of intents in the map to match those provided.
162 *
163 * @param states list of new Intent states
164 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700165 public void changeStates(IntentStateList states) {
166 for (Entry<String, IntentState> state : states.entrySet()) {
167 setState(state.getKey(), state.getValue());
168 }
169 notifyEvents();
170 }
Toshio Koideb609b3b2014-02-14 18:25:52 -0800171
Brian O'Connora581b9d2014-06-15 23:32:36 -0700172 /**
173 * Get an Intent from the map.
174 *
175 * @param intentId ID of requested Intent
176 * @return Intent with specified ID
177 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700178 public Intent getIntent(String intentId) {
179 return intents.get(intentId);
180 }
Toshio Koideb609b3b2014-02-14 18:25:52 -0800181
Brian O'Connora581b9d2014-06-15 23:32:36 -0700182 /**
183 * Get all Intents from the map.
184 *
185 * @return a collection including all Intents
186 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700187 public Collection<Intent> getAllIntents() {
188 return intents.values();
189 }
Toshio Koideb609b3b2014-02-14 18:25:52 -0800190
Brian O'Connora581b9d2014-06-15 23:32:36 -0700191 /**
192 * Add a change listener.
193 *
194 * @param listener new listener to change events
195 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700196 public void addChangeListener(ChangedListener listener) {
197 listeners.add(listener);
198 }
Toshio Koidedf2eab92014-02-20 11:24:59 -0800199
Brian O'Connora581b9d2014-06-15 23:32:36 -0700200 /**
201 * Remove a change listener.
202 *
203 * @param listener the listener to be removed
204 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700205 public void removeChangedListener(ChangedListener listener) {
206 listeners.remove(listener);
207 }
Toshio Koidedf2eab92014-02-20 11:24:59 -0800208
Ray Milkey269ffb92014-04-03 14:43:30 -0700209 //================================================================================
210 // methods that affect intents map (protected)
211 //================================================================================
Toshio Koidedf2eab92014-02-20 11:24:59 -0800212
Brian O'Connora581b9d2014-06-15 23:32:36 -0700213 /**
214 * Put an intent in the map.
215 *
216 * @param intent intent to be added
217 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700218 protected void putIntent(Intent intent) {
Ray Milkeyb29e6262014-04-09 16:02:14 -0700219 if (intents.containsKey(intent.getId())) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700220 removeIntent(intent.getId());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700221 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700222 intents.put(intent.getId(), intent);
223 events.add(new ChangedEvent(ChangedEventType.ADDED, intent));
224 }
Toshio Koidedf2eab92014-02-20 11:24:59 -0800225
Brian O'Connora581b9d2014-06-15 23:32:36 -0700226 /**
227 * Remove an intent from the map.
228 *
229 * @param intentId intent to be removed
230 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700231 protected void removeIntent(String intentId) {
232 Intent intent = intents.remove(intentId);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700233 if (intent == null) {
234 return;
235 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700236 events.add(new ChangedEvent(ChangedEventType.REMOVED, intent));
237 }
Toshio Koidedf2eab92014-02-20 11:24:59 -0800238
Brian O'Connora581b9d2014-06-15 23:32:36 -0700239 /**
240 * Change the state of an Intent in the map.
241 *
242 * @param intentId ID of the Intent to change
243 * @param state new state for the specified Intent
244 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700245 protected void setState(String intentId, IntentState state) {
246 Intent intent = intents.get(intentId);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700247 if (intent == null) {
248 return;
249 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700250 intent.setState(state);
251 events.add(new ChangedEvent(ChangedEventType.STATE_CHANGED, intent));
252 }
Toshio Koidedf2eab92014-02-20 11:24:59 -0800253
Ray Milkey269ffb92014-04-03 14:43:30 -0700254 //================================================================================
255 // helper methods (protected)
256 //================================================================================
Toshio Koidedf2eab92014-02-20 11:24:59 -0800257
Brian O'Connora581b9d2014-06-15 23:32:36 -0700258 /**
259 * Helper to add Intents to the map.
260 *
261 * @param operation the Intent to be added
262 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700263 protected void handleAddOperation(IntentOperation operation) {
264 putIntent(operation.intent);
265 }
Toshio Koidedf2eab92014-02-20 11:24:59 -0800266
Brian O'Connora581b9d2014-06-15 23:32:36 -0700267 /**
268 * Helper to remove Intents from the map.
269 *
270 * @param operation the Intent to be removed
271 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700272 protected void handleRemoveOperation(IntentOperation operation) {
Toshio Koide3818b4a2014-06-26 16:15:52 -0700273 if (operation == null) {
274 log.error("handleRemoveOperation: null operation");
Ray Milkey1aa71f82014-04-08 16:23:24 -0700275 return;
Ray Milkey269ffb92014-04-03 14:43:30 -0700276 }
Toshio Koide3818b4a2014-06-26 16:15:52 -0700277 if (operation.intent == null) {
278 log.error("handleRemoveOperation: null intent");
279 return;
280 }
281 String intentId = operation.intent.getId();
282 Intent intent = getIntent(intentId);
283 if (intent == null) {
284 log.error("handleRemoveOperation: Intent ID {} doesn't exist", intentId);
285 return;
286 }
287 setState(intent.getId(), IntentState.DEL_REQ);
Ray Milkey269ffb92014-04-03 14:43:30 -0700288 }
Toshio Koidedf2eab92014-02-20 11:24:59 -0800289
Brian O'Connora581b9d2014-06-15 23:32:36 -0700290 /**
291 * Helper to handle Intents in the error state.
292 *
293 * @param operation the Intent to be resolved
294 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700295 protected void handleErrorOperation(IntentOperation operation) {
296 //TODO put error message into the intent
Toshio Koidedf2eab92014-02-20 11:24:59 -0800297
Ray Milkey269ffb92014-04-03 14:43:30 -0700298 ErrorIntent errorIntent = (ErrorIntent) operation.intent;
299 Intent targetIntent = intents.get(errorIntent.getId());
300 if (targetIntent == null) {
301 // TODO error handling
302 return;
303 }
Toshio Koidedf2eab92014-02-20 11:24:59 -0800304
Ray Milkey269ffb92014-04-03 14:43:30 -0700305 switch (targetIntent.getState()) {
Brian O'Connora581b9d2014-06-15 23:32:36 -0700306 case CREATED:
307 case INST_REQ:
308 case INST_ACK:
309 case REROUTE_REQ:
310 setState(targetIntent.getId(), IntentState.INST_NACK);
311 break;
312 case DEL_REQ:
313 setState(targetIntent.getId(), IntentState.DEL_PENDING);
314 break;
315 case INST_NACK:
316 case DEL_PENDING:
317 case DEL_ACK:
318 // do nothing
319 break;
320 default:
321 log.error("Unknown intent state {}", targetIntent.getState());
322 break;
Ray Milkey269ffb92014-04-03 14:43:30 -0700323 }
324 }
325
Brian O'Connora581b9d2014-06-15 23:32:36 -0700326 /**
327 * Notify all registered listeners of events in queue.
328 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700329 protected void notifyEvents() {
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700330 if (events.isEmpty()) {
331 return;
332 }
333
Ray Milkey269ffb92014-04-03 14:43:30 -0700334 for (ChangedListener listener : listeners) {
335 listener.intentsChange(events);
336 }
337 events.clear();
338 }
Toshio Koideb609b3b2014-02-14 18:25:52 -0800339}