blob: 136e442d2935c8384c071a08792ca373aeec4fd5 [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) {
105 switch (operation.operator) {
Brian O'Connora581b9d2014-06-15 23:32:36 -0700106 case ADD:
107 handleAddOperation(operation);
108 break;
109 case REMOVE:
110 handleRemoveOperation(operation);
111 break;
112 case ERROR:
113 handleErrorOperation(operation);
114 break;
115 default:
116 log.error("Unknown intent operation {}", operation.operator);
117 break;
Ray Milkey269ffb92014-04-03 14:43:30 -0700118 }
119 }
120 notifyEvents();
121 }
Toshio Koidedf2eab92014-02-20 11:24:59 -0800122
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700123 /**
124 * Purge all Intents that are in a state allowing them to be removed.
125 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700126 public void purge() {
127 LinkedList<String> removeIds = new LinkedList<>();
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700128
129 // Collect the IDs of all intents that can be removed
Ray Milkey269ffb92014-04-03 14:43:30 -0700130 for (Entry<String, Intent> entry : intents.entrySet()) {
131 Intent intent = entry.getValue();
132 if (intent.getState() == IntentState.DEL_ACK
133 || intent.getState() == IntentState.INST_NACK) {
134 removeIds.add(intent.getId());
135 }
136 }
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700137
138 purge(removeIds);
139 }
140
141 /**
142 * Purge a collection of Intents specified by Intent IDs.
143 *
144 * NOTE: The caller needs to make sure those intents are in a state
145 * that allows them to be removed.
146 *
147 * @param removeIds the collection of Intent IDs to purge.
148 */
149 public void purge(Collection<String> removeIds) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700150 for (String intentId : removeIds) {
151 removeIntent(intentId);
152 }
153 notifyEvents();
154 }
Toshio Koidedf2eab92014-02-20 11:24:59 -0800155
Brian O'Connora581b9d2014-06-15 23:32:36 -0700156 /**
157 * Update the states of intents in the map to match those provided.
158 *
159 * @param states list of new Intent states
160 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700161 public void changeStates(IntentStateList states) {
162 for (Entry<String, IntentState> state : states.entrySet()) {
163 setState(state.getKey(), state.getValue());
164 }
165 notifyEvents();
166 }
Toshio Koideb609b3b2014-02-14 18:25:52 -0800167
Brian O'Connora581b9d2014-06-15 23:32:36 -0700168 /**
169 * Get an Intent from the map.
170 *
171 * @param intentId ID of requested Intent
172 * @return Intent with specified ID
173 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700174 public Intent getIntent(String intentId) {
175 return intents.get(intentId);
176 }
Toshio Koideb609b3b2014-02-14 18:25:52 -0800177
Brian O'Connora581b9d2014-06-15 23:32:36 -0700178 /**
179 * Get all Intents from the map.
180 *
181 * @return a collection including all Intents
182 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700183 public Collection<Intent> getAllIntents() {
184 return intents.values();
185 }
Toshio Koideb609b3b2014-02-14 18:25:52 -0800186
Brian O'Connora581b9d2014-06-15 23:32:36 -0700187 /**
188 * Add a change listener.
189 *
190 * @param listener new listener to change events
191 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700192 public void addChangeListener(ChangedListener listener) {
193 listeners.add(listener);
194 }
Toshio Koidedf2eab92014-02-20 11:24:59 -0800195
Brian O'Connora581b9d2014-06-15 23:32:36 -0700196 /**
197 * Remove a change listener.
198 *
199 * @param listener the listener to be removed
200 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700201 public void removeChangedListener(ChangedListener listener) {
202 listeners.remove(listener);
203 }
Toshio Koidedf2eab92014-02-20 11:24:59 -0800204
Ray Milkey269ffb92014-04-03 14:43:30 -0700205 //================================================================================
206 // methods that affect intents map (protected)
207 //================================================================================
Toshio Koidedf2eab92014-02-20 11:24:59 -0800208
Brian O'Connora581b9d2014-06-15 23:32:36 -0700209 /**
210 * Put an intent in the map.
211 *
212 * @param intent intent to be added
213 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700214 protected void putIntent(Intent intent) {
Ray Milkeyb29e6262014-04-09 16:02:14 -0700215 if (intents.containsKey(intent.getId())) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700216 removeIntent(intent.getId());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700217 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700218 intents.put(intent.getId(), intent);
219 events.add(new ChangedEvent(ChangedEventType.ADDED, intent));
220 }
Toshio Koidedf2eab92014-02-20 11:24:59 -0800221
Brian O'Connora581b9d2014-06-15 23:32:36 -0700222 /**
223 * Remove an intent from the map.
224 *
225 * @param intentId intent to be removed
226 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700227 protected void removeIntent(String intentId) {
228 Intent intent = intents.remove(intentId);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700229 if (intent == null) {
230 return;
231 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700232 events.add(new ChangedEvent(ChangedEventType.REMOVED, intent));
233 }
Toshio Koidedf2eab92014-02-20 11:24:59 -0800234
Brian O'Connora581b9d2014-06-15 23:32:36 -0700235 /**
236 * Change the state of an Intent in the map.
237 *
238 * @param intentId ID of the Intent to change
239 * @param state new state for the specified Intent
240 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700241 protected void setState(String intentId, IntentState state) {
242 Intent intent = intents.get(intentId);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700243 if (intent == null) {
244 return;
245 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700246 intent.setState(state);
247 events.add(new ChangedEvent(ChangedEventType.STATE_CHANGED, intent));
248 }
Toshio Koidedf2eab92014-02-20 11:24:59 -0800249
Ray Milkey269ffb92014-04-03 14:43:30 -0700250 //================================================================================
251 // helper methods (protected)
252 //================================================================================
Toshio Koidedf2eab92014-02-20 11:24:59 -0800253
Brian O'Connora581b9d2014-06-15 23:32:36 -0700254 /**
255 * Helper to add Intents to the map.
256 *
257 * @param operation the Intent to be added
258 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700259 protected void handleAddOperation(IntentOperation operation) {
260 putIntent(operation.intent);
261 }
Toshio Koidedf2eab92014-02-20 11:24:59 -0800262
Brian O'Connora581b9d2014-06-15 23:32:36 -0700263 /**
264 * Helper to remove Intents from the map.
265 *
266 * @param operation the Intent to be removed
267 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700268 protected void handleRemoveOperation(IntentOperation operation) {
269 Intent intent = getIntent(operation.intent.getId());
270 if (intent == null) {
271 // TODO error handling
Ray Milkey1aa71f82014-04-08 16:23:24 -0700272 return;
Ray Milkey269ffb92014-04-03 14:43:30 -0700273 } else {
274 setState(intent.getId(), IntentState.DEL_REQ);
275 }
276 }
Toshio Koidedf2eab92014-02-20 11:24:59 -0800277
Brian O'Connora581b9d2014-06-15 23:32:36 -0700278 /**
279 * Helper to handle Intents in the error state.
280 *
281 * @param operation the Intent to be resolved
282 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700283 protected void handleErrorOperation(IntentOperation operation) {
284 //TODO put error message into the intent
Toshio Koidedf2eab92014-02-20 11:24:59 -0800285
Ray Milkey269ffb92014-04-03 14:43:30 -0700286 ErrorIntent errorIntent = (ErrorIntent) operation.intent;
287 Intent targetIntent = intents.get(errorIntent.getId());
288 if (targetIntent == null) {
289 // TODO error handling
290 return;
291 }
Toshio Koidedf2eab92014-02-20 11:24:59 -0800292
Ray Milkey269ffb92014-04-03 14:43:30 -0700293 switch (targetIntent.getState()) {
Brian O'Connora581b9d2014-06-15 23:32:36 -0700294 case CREATED:
295 case INST_REQ:
296 case INST_ACK:
297 case REROUTE_REQ:
298 setState(targetIntent.getId(), IntentState.INST_NACK);
299 break;
300 case DEL_REQ:
301 setState(targetIntent.getId(), IntentState.DEL_PENDING);
302 break;
303 case INST_NACK:
304 case DEL_PENDING:
305 case DEL_ACK:
306 // do nothing
307 break;
308 default:
309 log.error("Unknown intent state {}", targetIntent.getState());
310 break;
Ray Milkey269ffb92014-04-03 14:43:30 -0700311 }
312 }
313
Brian O'Connora581b9d2014-06-15 23:32:36 -0700314 /**
315 * Notify all registered listeners of events in queue.
316 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700317 protected void notifyEvents() {
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700318 if (events.isEmpty()) {
319 return;
320 }
321
Ray Milkey269ffb92014-04-03 14:43:30 -0700322 for (ChangedListener listener : listeners) {
323 listener.intentsChange(events);
324 }
325 events.clear();
326 }
Toshio Koideb609b3b2014-02-14 18:25:52 -0800327}