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