blob: 50f1038815e220d2d1a77eac0c380f071fc1955b [file] [log] [blame]
Brian O'Connor66630c82014-10-02 21:08:19 -07001package org.onlab.onos.net.intent.impl;
2
alshabib8ca53902014-10-07 13:11:17 -07003import static com.google.common.base.Preconditions.checkNotNull;
4import static java.util.concurrent.Executors.newSingleThreadExecutor;
5import static org.onlab.onos.net.intent.IntentState.COMPILING;
6import static org.onlab.onos.net.intent.IntentState.FAILED;
7import static org.onlab.onos.net.intent.IntentState.INSTALLED;
8import static org.onlab.onos.net.intent.IntentState.INSTALLING;
9import static org.onlab.onos.net.intent.IntentState.RECOMPILING;
10import static org.onlab.onos.net.intent.IntentState.WITHDRAWING;
11import static org.onlab.onos.net.intent.IntentState.WITHDRAWN;
12import static org.onlab.util.Tools.namedThreads;
13import static org.slf4j.LoggerFactory.getLogger;
14
15import java.util.ArrayList;
Brian O'Connorcb900f42014-10-07 21:55:33 -070016import java.util.Iterator;
alshabib8ca53902014-10-07 13:11:17 -070017import java.util.List;
18import java.util.Map;
19import java.util.Objects;
20import java.util.concurrent.ConcurrentHashMap;
21import java.util.concurrent.ConcurrentMap;
22import java.util.concurrent.ExecutorService;
Brian O'Connorcb900f42014-10-07 21:55:33 -070023import java.util.concurrent.Future;
alshabib8ca53902014-10-07 13:11:17 -070024
Brian O'Connor66630c82014-10-02 21:08:19 -070025import org.apache.felix.scr.annotations.Activate;
26import org.apache.felix.scr.annotations.Component;
27import org.apache.felix.scr.annotations.Deactivate;
28import org.apache.felix.scr.annotations.Reference;
29import org.apache.felix.scr.annotations.ReferenceCardinality;
30import org.apache.felix.scr.annotations.Service;
31import org.onlab.onos.event.AbstractListenerRegistry;
32import org.onlab.onos.event.EventDeliveryService;
Brian O'Connorcb900f42014-10-07 21:55:33 -070033import org.onlab.onos.net.flow.CompletedBatchOperation;
Brian O'Connor66630c82014-10-02 21:08:19 -070034import org.onlab.onos.net.intent.InstallableIntent;
35import org.onlab.onos.net.intent.Intent;
36import org.onlab.onos.net.intent.IntentCompiler;
37import org.onlab.onos.net.intent.IntentEvent;
38import org.onlab.onos.net.intent.IntentException;
39import org.onlab.onos.net.intent.IntentExtensionService;
40import org.onlab.onos.net.intent.IntentId;
41import org.onlab.onos.net.intent.IntentInstaller;
42import org.onlab.onos.net.intent.IntentListener;
43import org.onlab.onos.net.intent.IntentOperations;
44import org.onlab.onos.net.intent.IntentService;
45import org.onlab.onos.net.intent.IntentState;
46import org.onlab.onos.net.intent.IntentStore;
47import org.onlab.onos.net.intent.IntentStoreDelegate;
48import org.slf4j.Logger;
49
Brian O'Connorcb900f42014-10-07 21:55:33 -070050import com.google.common.collect.ImmutableList;
alshabib8ca53902014-10-07 13:11:17 -070051import com.google.common.collect.ImmutableMap;
Brian O'Connorcb900f42014-10-07 21:55:33 -070052import com.google.common.collect.Lists;
Brian O'Connor66630c82014-10-02 21:08:19 -070053
54/**
55 * An implementation of Intent Manager.
56 */
57@Component(immediate = true)
58@Service
59public class IntentManager
60 implements IntentService, IntentExtensionService {
61 private final Logger log = getLogger(getClass());
62
63 public static final String INTENT_NULL = "Intent cannot be null";
64 public static final String INTENT_ID_NULL = "Intent ID cannot be null";
65
66 // Collections for compiler, installer, and listener are ONOS instance local
67 private final ConcurrentMap<Class<? extends Intent>,
68 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
69 private final ConcurrentMap<Class<? extends InstallableIntent>,
70 IntentInstaller<? extends InstallableIntent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070071
72 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -070073 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070074
Brian O'Connorcb900f42014-10-07 21:55:33 -070075 private ExecutorService executor;
76 private ExecutorService monitorExecutor;
tom85258ee2014-10-07 00:10:02 -070077
Brian O'Connor66630c82014-10-02 21:08:19 -070078 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
tom95329eb2014-10-06 08:40:06 -070079 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
Brian O'Connor66630c82014-10-02 21:08:19 -070080
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 protected IntentStore store;
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -070085 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -070086
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -070088 protected EventDeliveryService eventDispatcher;
89
90 @Activate
91 public void activate() {
92 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -070093 trackerService.setDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -070094 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Brian O'Connorcb900f42014-10-07 21:55:33 -070095 executor = newSingleThreadExecutor(namedThreads("onos-intents"));
96 monitorExecutor = newSingleThreadExecutor(namedThreads("onos-intent-monitor"));
Brian O'Connor66630c82014-10-02 21:08:19 -070097 log.info("Started");
98 }
99
100 @Deactivate
101 public void deactivate() {
102 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700103 trackerService.unsetDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700104 eventDispatcher.removeSink(IntentEvent.class);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700105 executor.shutdown();
106 monitorExecutor.shutdown();
Brian O'Connor66630c82014-10-02 21:08:19 -0700107 log.info("Stopped");
108 }
109
110 @Override
111 public void submit(Intent intent) {
112 checkNotNull(intent, INTENT_NULL);
113 registerSubclassCompilerIfNeeded(intent);
114 IntentEvent event = store.createIntent(intent);
tom85258ee2014-10-07 00:10:02 -0700115 if (event != null) {
116 eventDispatcher.post(event);
117 executor.execute(new IntentTask(COMPILING, intent));
118 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700119 }
120
121 @Override
122 public void withdraw(Intent intent) {
123 checkNotNull(intent, INTENT_NULL);
tom85258ee2014-10-07 00:10:02 -0700124 executor.execute(new IntentTask(WITHDRAWING, intent));
Brian O'Connor66630c82014-10-02 21:08:19 -0700125 }
126
127 // FIXME: implement this method
128 @Override
129 public void execute(IntentOperations operations) {
130 throw new UnsupportedOperationException("execute() is not implemented yet");
131 }
132
133 @Override
134 public Iterable<Intent> getIntents() {
135 return store.getIntents();
136 }
137
138 @Override
139 public long getIntentCount() {
140 return store.getIntentCount();
141 }
142
143 @Override
144 public Intent getIntent(IntentId id) {
145 checkNotNull(id, INTENT_ID_NULL);
146 return store.getIntent(id);
147 }
148
149 @Override
150 public IntentState getIntentState(IntentId id) {
151 checkNotNull(id, INTENT_ID_NULL);
152 return store.getIntentState(id);
153 }
154
155 @Override
156 public void addListener(IntentListener listener) {
157 listenerRegistry.addListener(listener);
158 }
159
160 @Override
161 public void removeListener(IntentListener listener) {
162 listenerRegistry.removeListener(listener);
163 }
164
165 @Override
166 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
167 compilers.put(cls, compiler);
168 }
169
170 @Override
171 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
172 compilers.remove(cls);
173 }
174
175 @Override
176 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
177 return ImmutableMap.copyOf(compilers);
178 }
179
180 @Override
181 public <T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
182 installers.put(cls, installer);
183 }
184
185 @Override
186 public <T extends InstallableIntent> void unregisterInstaller(Class<T> cls) {
187 installers.remove(cls);
188 }
189
190 @Override
191 public Map<Class<? extends InstallableIntent>, IntentInstaller<? extends InstallableIntent>> getInstallers() {
192 return ImmutableMap.copyOf(installers);
193 }
194
195 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700196 * Returns the corresponding intent compiler to the specified intent.
197 *
198 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700199 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700200 * @return intent compiler corresponding to the specified intent
201 */
202 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
203 @SuppressWarnings("unchecked")
204 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
205 if (compiler == null) {
206 throw new IntentException("no compiler for class " + intent.getClass());
207 }
208 return compiler;
209 }
210
211 /**
212 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700213 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700214 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700215 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700216 * @return intent installer corresponding to the specified installable intent
217 */
218 private <T extends InstallableIntent> IntentInstaller<T> getInstaller(T intent) {
219 @SuppressWarnings("unchecked")
220 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
221 if (installer == null) {
222 throw new IntentException("no installer for class " + intent.getClass());
223 }
224 return installer;
225 }
226
227 /**
tom85258ee2014-10-07 00:10:02 -0700228 * Compiles the specified intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700229 *
tom85258ee2014-10-07 00:10:02 -0700230 * @param intent intent to be compiled
Brian O'Connor66630c82014-10-02 21:08:19 -0700231 */
tom85258ee2014-10-07 00:10:02 -0700232 private void executeCompilingPhase(Intent intent) {
233 // Indicate that the intent is entering the compiling phase.
234 store.setState(intent, COMPILING);
235
236 try {
237 // Compile the intent into installable derivatives.
238 List<InstallableIntent> installable = compileIntent(intent);
239
240 // If all went well, associate the resulting list of installable
241 // intents with the top-level intent and proceed to install.
242 store.addInstallableIntents(intent.id(), installable);
243 executeInstallingPhase(intent);
244
245 } catch (Exception e) {
tom53945d52014-10-07 11:01:36 -0700246 log.warn("Unable to compile intent {} due to: {}", intent.id(), e);
247
tom85258ee2014-10-07 00:10:02 -0700248 // If compilation failed, mark the intent as failed.
249 store.setState(intent, FAILED);
250 }
251 }
252
Brian O'Connorcb900f42014-10-07 21:55:33 -0700253 /**
254 * Compiles an intent recursively.
255 *
256 * @param intent intent
257 * @return result of compilation
258 */
tom85258ee2014-10-07 00:10:02 -0700259 private List<InstallableIntent> compileIntent(Intent intent) {
Brian O'Connorcb900f42014-10-07 21:55:33 -0700260 if (intent instanceof InstallableIntent) {
261 return ImmutableList.of((InstallableIntent) intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700262 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700263
264 List<InstallableIntent> installable = new ArrayList<>();
265 // TODO do we need to registerSubclassCompiler?
266 for (Intent compiled : getCompiler(intent).compile(intent)) {
267 installable.addAll(compileIntent(compiled));
268 }
269
tom85258ee2014-10-07 00:10:02 -0700270 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700271 }
272
273 /**
tom85258ee2014-10-07 00:10:02 -0700274 * Installs all installable intents associated with the specified top-level
275 * intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700276 *
tom85258ee2014-10-07 00:10:02 -0700277 * @param intent intent to be installed
Brian O'Connor66630c82014-10-02 21:08:19 -0700278 */
tom85258ee2014-10-07 00:10:02 -0700279 private void executeInstallingPhase(Intent intent) {
280 // Indicate that the intent is entering the installing phase.
281 store.setState(intent, INSTALLING);
282
Brian O'Connorcb900f42014-10-07 21:55:33 -0700283 List<Future<CompletedBatchOperation>> installFutures = Lists.newArrayList();
tom85258ee2014-10-07 00:10:02 -0700284 try {
285 List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
286 if (installables != null) {
287 for (InstallableIntent installable : installables) {
288 registerSubclassInstallerIfNeeded(installable);
tom53945d52014-10-07 11:01:36 -0700289 trackerService.addTrackedResources(intent.id(),
290 installable.requiredLinks());
Brian O'Connorcb900f42014-10-07 21:55:33 -0700291 Future<CompletedBatchOperation> future = getInstaller(installable).install(installable);
292 installFutures.add(future);
tom85258ee2014-10-07 00:10:02 -0700293 }
tom95329eb2014-10-06 08:40:06 -0700294 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700295 // FIXME we have to wait for the installable intents
296 //eventDispatcher.post(store.setState(intent, INSTALLED));
297 monitorExecutor.execute(new IntentInstallMonitor(intent, installFutures, INSTALLED));
tom85258ee2014-10-07 00:10:02 -0700298 } catch (Exception e) {
tom53945d52014-10-07 11:01:36 -0700299 log.warn("Unable to install intent {} due to: {}", intent.id(), e);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700300 uninstallIntent(intent, RECOMPILING);
tom53945d52014-10-07 11:01:36 -0700301
tom85258ee2014-10-07 00:10:02 -0700302 // If compilation failed, kick off the recompiling phase.
Brian O'Connorcb900f42014-10-07 21:55:33 -0700303 // FIXME
304 //executeRecompilingPhase(intent);
tom85258ee2014-10-07 00:10:02 -0700305 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700306 }
307
308 /**
tom85258ee2014-10-07 00:10:02 -0700309 * Recompiles the specified intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700310 *
tom85258ee2014-10-07 00:10:02 -0700311 * @param intent intent to be recompiled
Brian O'Connor66630c82014-10-02 21:08:19 -0700312 */
tom85258ee2014-10-07 00:10:02 -0700313 private void executeRecompilingPhase(Intent intent) {
314 // Indicate that the intent is entering the recompiling phase.
315 store.setState(intent, RECOMPILING);
316
317 try {
318 // Compile the intent into installable derivatives.
319 List<InstallableIntent> installable = compileIntent(intent);
320
321 // If all went well, compare the existing list of installable
322 // intents with the newly compiled list. If they are the same,
323 // bail, out since the previous approach was determined not to
324 // be viable.
325 List<InstallableIntent> originalInstallable =
326 store.getInstallableIntents(intent.id());
327
328 if (Objects.equals(originalInstallable, installable)) {
329 eventDispatcher.post(store.setState(intent, FAILED));
330 } else {
331 // Otherwise, re-associate the newly compiled installable intents
332 // with the top-level intent and kick off installing phase.
333 store.addInstallableIntents(intent.id(), installable);
334 executeInstallingPhase(intent);
335 }
336 } catch (Exception e) {
tom53945d52014-10-07 11:01:36 -0700337 log.warn("Unable to recompile intent {} due to: {}", intent.id(), e);
338
tom85258ee2014-10-07 00:10:02 -0700339 // If compilation failed, mark the intent as failed.
340 eventDispatcher.post(store.setState(intent, FAILED));
341 }
342 }
343
344 /**
345 * Uninstalls the specified intent by uninstalling all of its associated
346 * installable derivatives.
347 *
348 * @param intent intent to be installed
349 */
350 private void executeWithdrawingPhase(Intent intent) {
351 // Indicate that the intent is being withdrawn.
352 store.setState(intent, WITHDRAWING);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700353 uninstallIntent(intent, WITHDRAWN);
tom85258ee2014-10-07 00:10:02 -0700354
355 // If all went well, disassociate the top-level intent with its
356 // installable derivatives and mark it as withdrawn.
Brian O'Connorcb900f42014-10-07 21:55:33 -0700357 // FIXME need to clean up
358 //store.removeInstalledIntents(intent.id());
359 // FIXME
360 //eventDispatcher.post(store.setState(intent, WITHDRAWN));
Brian O'Connor66630c82014-10-02 21:08:19 -0700361 }
362
363 /**
tom53945d52014-10-07 11:01:36 -0700364 * Uninstalls all installable intents associated with the given intent.
365 *
366 * @param intent intent to be uninstalled
367 */
Brian O'Connorcb900f42014-10-07 21:55:33 -0700368 private void uninstallIntent(Intent intent, IntentState nextState) {
369 List<Future<CompletedBatchOperation>> uninstallFutures = Lists.newArrayList();
tom53945d52014-10-07 11:01:36 -0700370 try {
371 List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
372 if (installables != null) {
373 for (InstallableIntent installable : installables) {
Brian O'Connorcb900f42014-10-07 21:55:33 -0700374 Future<CompletedBatchOperation> future = getInstaller(installable).uninstall(installable);
375 uninstallFutures.add(future);
tom53945d52014-10-07 11:01:36 -0700376 }
377 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700378 monitorExecutor.execute(new IntentInstallMonitor(intent, uninstallFutures, nextState));
tom53945d52014-10-07 11:01:36 -0700379 } catch (IntentException e) {
380 log.warn("Unable to uninstall intent {} due to: {}", intent.id(), e);
381 }
382 }
383
384 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700385 * Registers an intent compiler of the specified intent if an intent compiler
386 * for the intent is not registered. This method traverses the class hierarchy of
387 * the intent. Once an intent compiler for a parent type is found, this method
388 * registers the found intent compiler.
389 *
390 * @param intent intent
391 */
392 private void registerSubclassCompilerIfNeeded(Intent intent) {
393 if (!compilers.containsKey(intent.getClass())) {
394 Class<?> cls = intent.getClass();
395 while (cls != Object.class) {
396 // As long as we're within the Intent class descendants
397 if (Intent.class.isAssignableFrom(cls)) {
398 IntentCompiler<?> compiler = compilers.get(cls);
399 if (compiler != null) {
400 compilers.put(intent.getClass(), compiler);
401 return;
402 }
403 }
404 cls = cls.getSuperclass();
405 }
406 }
407 }
408
409 /**
410 * Registers an intent installer of the specified intent if an intent installer
411 * for the intent is not registered. This method traverses the class hierarchy of
412 * the intent. Once an intent installer for a parent type is found, this method
413 * registers the found intent installer.
414 *
415 * @param intent intent
416 */
417 private void registerSubclassInstallerIfNeeded(InstallableIntent intent) {
418 if (!installers.containsKey(intent.getClass())) {
419 Class<?> cls = intent.getClass();
420 while (cls != Object.class) {
421 // As long as we're within the InstallableIntent class descendants
422 if (InstallableIntent.class.isAssignableFrom(cls)) {
423 IntentInstaller<?> installer = installers.get(cls);
424 if (installer != null) {
425 installers.put(intent.getClass(), installer);
426 return;
427 }
428 }
429 cls = cls.getSuperclass();
430 }
431 }
432 }
433
Brian O'Connor66630c82014-10-02 21:08:19 -0700434 // Store delegate to re-post events emitted from the store.
435 private class InternalStoreDelegate implements IntentStoreDelegate {
436 @Override
437 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700438 eventDispatcher.post(event);
439 if (event.type() == IntentEvent.Type.SUBMITTED) {
440 executor.execute(new IntentTask(COMPILING, event.subject()));
441 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700442 }
443 }
444
tom95329eb2014-10-06 08:40:06 -0700445 // Topology change delegate
446 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
447 @Override
tom85258ee2014-10-07 00:10:02 -0700448 public void triggerCompile(Iterable<IntentId> intentIds,
449 boolean compileAllFailed) {
450 // Attempt recompilation of the specified intents first.
tom95329eb2014-10-06 08:40:06 -0700451 for (IntentId intentId : intentIds) {
tom53945d52014-10-07 11:01:36 -0700452 Intent intent = getIntent(intentId);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700453 uninstallIntent(intent, RECOMPILING);
alshabib8ca53902014-10-07 13:11:17 -0700454
Brian O'Connorcb900f42014-10-07 21:55:33 -0700455 //FIXME
456 //executeRecompilingPhase(intent);
tom85258ee2014-10-07 00:10:02 -0700457 }
458
459 if (compileAllFailed) {
460 // If required, compile all currently failed intents.
461 for (Intent intent : getIntents()) {
462 if (getIntentState(intent.id()) == FAILED) {
463 executeCompilingPhase(intent);
464 }
465 }
tom95329eb2014-10-06 08:40:06 -0700466 }
467 }
tom95329eb2014-10-06 08:40:06 -0700468 }
tom85258ee2014-10-07 00:10:02 -0700469
470 // Auxiliary runnable to perform asynchronous tasks.
471 private class IntentTask implements Runnable {
472 private final IntentState state;
473 private final Intent intent;
474
475 public IntentTask(IntentState state, Intent intent) {
476 this.state = state;
477 this.intent = intent;
478 }
479
480 @Override
481 public void run() {
482 if (state == COMPILING) {
483 executeCompilingPhase(intent);
484 } else if (state == RECOMPILING) {
485 executeRecompilingPhase(intent);
486 } else if (state == WITHDRAWING) {
487 executeWithdrawingPhase(intent);
488 }
489 }
490 }
491
Brian O'Connorcb900f42014-10-07 21:55:33 -0700492 private class IntentInstallMonitor implements Runnable {
493
494 private final Intent intent;
495 private final List<Future<CompletedBatchOperation>> futures;
496 private final IntentState nextState;
497
498 public IntentInstallMonitor(Intent intent,
499 List<Future<CompletedBatchOperation>> futures, IntentState nextState) {
500 this.intent = intent;
501 this.futures = futures;
502 this.nextState = nextState;
503 }
504
505 private void updateIntent(Intent intent) {
506 if (nextState == RECOMPILING) {
507 executor.execute(new IntentTask(nextState, intent));
508 } else if (nextState == INSTALLED || nextState == WITHDRAWN) {
509 eventDispatcher.post(store.setState(intent, nextState));
510 } else {
511 log.warn("Invalid next intent state {} for intent {}", nextState, intent);
512 }
513 }
514
515 @Override
516 public void run() {
517 for (Iterator<Future<CompletedBatchOperation>> i = futures.iterator(); i.hasNext();) {
518 Future<CompletedBatchOperation> future = i.next();
519 if (future.isDone()) {
520 // TODO: we may want to get the future here
521 i.remove();
522 }
523 }
524 if (futures.isEmpty()) {
525 updateIntent(intent);
526 } else {
527 // resubmit ourselves if we are not done yet
528 monitorExecutor.submit(this);
529 }
530 }
531 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700532}