blob: b4f73133a45cf733e3e9443dff7373aa29acb21b [file] [log] [blame]
Sho SHIMIZU11812fc2014-08-21 14:51:17 -07001package net.onrc.onos.core.newintent;
2
3import com.google.common.collect.ImmutableMap;
4import com.hazelcast.core.EntryEvent;
5import com.hazelcast.core.EntryListener;
6import net.onrc.onos.api.newintent.InstallableIntent;
7import net.onrc.onos.api.newintent.Intent;
8import net.onrc.onos.api.newintent.IntentCompiler;
9import net.onrc.onos.api.newintent.IntentEvent;
10import net.onrc.onos.api.newintent.IntentEventListener;
11import net.onrc.onos.api.newintent.IntentException;
12import net.onrc.onos.api.newintent.IntentId;
13import net.onrc.onos.api.newintent.IntentInstaller;
14import net.onrc.onos.api.newintent.IntentOperations;
15import net.onrc.onos.api.newintent.IntentState;
16import net.onrc.onos.core.datagrid.ISharedCollectionsService;
17
18import java.util.ArrayList;
19import java.util.Collection;
20import java.util.HashSet;
21import java.util.List;
22import java.util.Map;
23import java.util.Set;
24import java.util.concurrent.ConcurrentHashMap;
25import java.util.concurrent.ConcurrentMap;
26import java.util.concurrent.CopyOnWriteArrayList;
27
28import static com.google.common.base.Preconditions.checkNotNull;
29import static net.onrc.onos.api.newintent.IntentState.COMPILED;
30import static net.onrc.onos.api.newintent.IntentState.FAILED;
31import static net.onrc.onos.api.newintent.IntentState.INSTALLED;
32import static net.onrc.onos.api.newintent.IntentState.SUBMITTED;
33import static net.onrc.onos.api.newintent.IntentState.WITHDRAWING;
34import static net.onrc.onos.api.newintent.IntentState.WITHDRAWN;
35
36/**
37 * An implementation of Intent Manager.
38 */
39public class IntentManagerRuntime implements IntentManager {
40 // Collections for intent, installable intent, and intent state are globally shared
41 private final IntentMap<IntentEvent> intentEvents;
42 private final IntentMap<IntentCompilationResult> installableIntents;
43
44 // Collections for compiler, installer, and listener are ONOS instance local
45 private final ConcurrentMap<Class<? extends Intent>,
46 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
47 private final ConcurrentMap<Class<? extends InstallableIntent>,
48 IntentInstaller<? extends InstallableIntent>> installers = new ConcurrentHashMap<>();
49 private final CopyOnWriteArrayList<IntentEventListener> listeners = new CopyOnWriteArrayList<>();
50
51 /**
52 * Constructs a Intent Manager runtime with the specified shared collections service.
53 *
54 * @param collectionsService shared collections service
55 */
56 public IntentManagerRuntime(ISharedCollectionsService collectionsService) {
57 checkNotNull(collectionsService);
58
59 this.intentEvents = new IntentMap<>("intentState", IntentEvent.class, collectionsService);
60 this.installableIntents =
61 new IntentMap<>("installableIntents", IntentCompilationResult.class, collectionsService);
62
63 this.intentEvents.addListener(new InternalEntryListener(new InternalIntentEventListener()));
64 }
65
66 @Override
67 public void submit(Intent intent) {
68 registerSubclassCompilerIfNeeded(intent);
69 setState(intent, SUBMITTED);
70 }
71
72 @Override
73 public void withdraw(Intent intent) {
74 setState(intent, WITHDRAWING);
75 }
76
77 // FIXME: implement this method
78 @Override
79 public void execute(IntentOperations operations) {
80 throw new UnsupportedOperationException("execute() is not implemented yet");
81 }
82
83 @Override
84 public Set<Intent> getIntents() {
85 Collection<IntentEvent> events = intentEvents.values();
86 Set<Intent> intents = new HashSet<>(events.size());
87 for (IntentEvent event: events) {
88 intents.add(event.getIntent());
89 }
90 return intents;
91 }
92
93 @Override
94 public Intent getIntent(IntentId id) {
95 IntentEvent event = intentEvents.get(id);
96 if (event == null) {
97 return null;
98 }
99 return event.getIntent();
100 }
101
102 @Override
103 public IntentState getIntentState(IntentId id) {
104 IntentEvent event = intentEvents.get(id);
105 if (event == null) {
106 return null;
107 }
108 return event.getState();
109 }
110
111 @Override
112 public void addListener(IntentEventListener listener) {
113 listeners.add(listener);
114 }
115
116 @Override
117 public void removeListener(IntentEventListener listener) {
118 listeners.remove(listener);
119 }
120
121 @Override
122 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
123 compilers.put(cls, compiler);
124 }
125
126 @Override
127 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
128 compilers.remove(cls);
129 }
130
131 @Override
132 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
133 return ImmutableMap.copyOf(compilers);
134 }
135
136 @Override
137 public <T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
138 installers.put(cls, installer);
139 }
140
141 @Override
142 public <T extends InstallableIntent> void unregisterInstaller(Class<T> cls) {
143 installers.remove(cls);
144 }
145
146 @Override
147 public Map<Class<? extends InstallableIntent>, IntentInstaller<? extends InstallableIntent>> getInstallers() {
148 return ImmutableMap.copyOf(installers);
149 }
150
151 /**
152 * Sets the state of the specified intent to the new state.
153 *
154 * @param intent intent whose state is to be changed
155 * @param newState new state
156 */
157 private void setState(Intent intent, IntentState newState) {
158 IntentState oldState = getIntentState(intent.getId());
159 IntentEvent event = new IntentEvent(intent, newState, oldState, System.currentTimeMillis());
160 intentEvents.put(intent.getId(), event);
161 }
162
163 /**
164 * Invokes all of registered intent event listener.
165 *
166 * @param event event supplied to a listener as an argument
167 */
168 private void invokeListeners(IntentEvent event) {
169 for (IntentEventListener listener: listeners) {
170 listener.event(event);
171 }
172 }
173
174 /**
175 * Returns the corresponding intent compiler to the specified intent.
176 *
177 * @param intent intent
178 * @param <T> the type of intent
179 * @return intent compiler corresponding to the specified intent
180 */
181 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
182 @SuppressWarnings("unchecked")
183 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
184 if (compiler == null) {
185 throw new IntentException("no compiler for class " + intent.getClass());
186 }
187 return compiler;
188 }
189
190 /**
191 * Returns the corresponding intent installer to the specified installable intent.
192 * @param intent intent
193 * @param <T> the type of installable intent
194 * @return intent installer corresponding to the specified installable intent
195 */
196 private <T extends InstallableIntent> IntentInstaller<T> getInstaller(T intent) {
197 @SuppressWarnings("unchecked")
198 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
199 if (installer == null) {
200 throw new IntentException("no installer for class " + intent.getClass());
201 }
202 return installer;
203 }
204
205 /**
206 * Compiles an intent.
207 *
208 * @param intent intent
209 */
210 private void compileIntent(Intent intent) {
211 // FIXME: To make SDN-IP workable ASAP, only single level compilation is implemented
212 // TODO: implement compilation traversing tree structure
213 List<InstallableIntent> installable = new ArrayList<>();
214 for (Intent compiled : getCompiler(intent).compile(intent)) {
215 installable.add((InstallableIntent) compiled);
216 }
217 installableIntents.put(intent.getId(), new IntentCompilationResult(installable));
218 setState(intent, COMPILED);
219 }
220
221 /**
222 * Installs an intent.
223 *
224 * @param intent intent
225 */
226 private void installIntent(Intent intent) {
227 IntentCompilationResult compiled = installableIntents.get(intent.getId());
228 for (InstallableIntent installable: compiled.getResult()) {
229 registerSubclassInstallerIfNeeded(installable);
230 getInstaller(installable).install(installable);
231 }
232
233 setState(intent, INSTALLED);
234 }
235
236 /**
237 * Uninstalls an intent.
238 *
239 * @param intent intent
240 */
241 private void uninstallIntent(Intent intent) {
242 IntentCompilationResult compiled = installableIntents.get(intent.getId());
243 for (InstallableIntent installable: compiled.getResult()) {
244 getInstaller(installable).remove(installable);
245 }
246
247 installableIntents.remove(intent.getId());
248 setState(intent, WITHDRAWN);
249 }
250
251 /**
252 * Registers an intent compiler of the specified intent if an intent compiler
253 * for the intent is not registered. This method traverses the class hierarchy of
254 * the intent. Once an intent compiler for a parent type is found, this method
255 * registers the found intent compiler.
256 *
257 * @param intent intent
258 */
259 @SuppressWarnings("unchecked")
260 private void registerSubclassCompilerIfNeeded(Intent intent) {
261 if (!compilers.containsKey(intent.getClass())) {
262 Class<?> cls = intent.getClass();
263 while (cls != Object.class) {
264 // As long as we're within the Intent class descendants
265 if (Intent.class.isAssignableFrom(cls)) {
266 IntentCompiler<?> compiler = compilers.get(cls);
267 if (compiler != null) {
268 compilers.put(intent.getClass(), compiler);
269 return;
270 }
271 }
272 cls = cls.getSuperclass();
273 }
274 }
275 }
276
277 /**
278 * Registers an intent installer of the specified intent if an intent installer
279 * for the intent is not registered. This method traverses the class hierarchy of
280 * the intent. Once an intent installer for a parent type is found, this method
281 * registers the found intent installer.
282 *
283 * @param intent intent
284 */
285 @SuppressWarnings("unchecked")
286 private void registerSubclassInstallerIfNeeded(InstallableIntent intent) {
287 if (!installers.containsKey(intent.getClass())) {
288 Class<?> cls = intent.getClass();
289 while (cls != Object.class) {
290 // As long as we're within the InstallableIntent class descendants
291 if (InstallableIntent.class.isAssignableFrom(cls)) {
292 IntentInstaller<?> installer = installers.get(cls);
293 if (installer != null) {
294 installers.put(intent.getClass(), installer);
295 return;
296 }
297 }
298 cls = cls.getSuperclass();
299 }
300 }
301 }
302
303 /**
304 * Destroys underlying {@link IntentMap IntentMaps}.
305 * This method is only for testing purpose.
306 */
307 void destroy() {
308 intentEvents.destroy();
309 installableIntents.destroy();
310 }
311
312 /**
313 * An entry listener used internally.
314 *
315 * This listener is a kind of bridge of listener mechanism
316 * between {@link IntentMap} and {@link IntentEventListener}.
317 */
318 private static class InternalEntryListener implements EntryListener<IntentId, IntentEvent> {
319 private final IntentEventListener listener;
320
321 public InternalEntryListener(IntentEventListener listener) {
322 this.listener = listener;
323 }
324
325 @Override
326 public void entryAdded(EntryEvent<IntentId, IntentEvent> event) {
327 listener.event(event.getValue());
328 }
329
330 @Override
331 public void entryRemoved(EntryEvent<IntentId, IntentEvent> event) {
332 listener.event(event.getValue());
333 }
334
335 @Override
336 public void entryUpdated(EntryEvent<IntentId, IntentEvent> event) {
337 listener.event(event.getValue());
338 }
339
340 @Override
341 public void entryEvicted(EntryEvent<IntentId, IntentEvent> event) {
342 // no-op
343 }
344 }
345
346 /**
347 * An intent event listener used internally.
348 *
349 * event() method handles state transition of submitted intents.
350 */
351 private class InternalIntentEventListener implements IntentEventListener {
352 @Override
353 public void event(IntentEvent event) {
354 invokeListeners(event);
355 Intent intent = event.getIntent();
356
357 try {
358 switch (event.getState()) {
359 case SUBMITTED:
360 compileIntent(intent);
361 break;
362 case COMPILED:
363 installIntent(intent);
364 break;
365 case INSTALLED:
366 break;
367 case WITHDRAWING:
368 uninstallIntent(intent);
369 break;
370 case WITHDRAWN:
371 break;
372 case FAILED:
373 break;
374 default:
375 throw new IllegalStateException(
376 "the state of IntentEvent is illegal: " + event.getState());
377 }
378 } catch (IntentException e) {
379 setState(intent, FAILED);
380 }
381 }
382 }
383}