blob: 511086f6755c4c2920cd069b3a7c7b9c51d40a80 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.net.intent.impl;
Brian O'Connor66630c82014-10-02 21:08:19 -070017
Brian O'Connor03406a42015-02-03 17:28:57 -080018import java.util.ArrayList;
19import java.util.Collection;
20import java.util.Collections;
21import java.util.EnumSet;
22import java.util.LinkedList;
23import java.util.List;
24import java.util.Map;
25import java.util.Optional;
26import java.util.concurrent.ConcurrentHashMap;
27import java.util.concurrent.ConcurrentMap;
28import java.util.concurrent.ExecutionException;
29import java.util.concurrent.ExecutorService;
30import java.util.concurrent.Future;
31import java.util.concurrent.TimeUnit;
32import java.util.concurrent.TimeoutException;
33import java.util.stream.Collectors;
34
Brian O'Connor66630c82014-10-02 21:08:19 -070035import org.apache.felix.scr.annotations.Activate;
36import org.apache.felix.scr.annotations.Component;
37import org.apache.felix.scr.annotations.Deactivate;
38import org.apache.felix.scr.annotations.Reference;
39import org.apache.felix.scr.annotations.ReferenceCardinality;
40import org.apache.felix.scr.annotations.Service;
Brian O'Connorabafb502014-12-02 22:26:20 -080041import org.onosproject.core.CoreService;
42import org.onosproject.core.IdGenerator;
43import org.onosproject.event.AbstractListenerRegistry;
44import org.onosproject.event.EventDeliveryService;
45import org.onosproject.net.flow.CompletedBatchOperation;
46import org.onosproject.net.flow.FlowRuleBatchOperation;
47import org.onosproject.net.flow.FlowRuleService;
48import org.onosproject.net.intent.Intent;
49import org.onosproject.net.intent.IntentBatchDelegate;
Brian O'Connorabafb502014-12-02 22:26:20 -080050import org.onosproject.net.intent.IntentCompiler;
Brian O'Connorcff03322015-02-03 15:28:59 -080051import org.onosproject.net.intent.IntentData;
Brian O'Connorabafb502014-12-02 22:26:20 -080052import org.onosproject.net.intent.IntentEvent;
53import org.onosproject.net.intent.IntentException;
54import org.onosproject.net.intent.IntentExtensionService;
55import org.onosproject.net.intent.IntentId;
56import org.onosproject.net.intent.IntentInstaller;
57import org.onosproject.net.intent.IntentListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080058import org.onosproject.net.intent.IntentService;
59import org.onosproject.net.intent.IntentState;
60import org.onosproject.net.intent.IntentStore;
Brian O'Connorabafb502014-12-02 22:26:20 -080061import org.onosproject.net.intent.IntentStoreDelegate;
Brian O'Connor66630c82014-10-02 21:08:19 -070062import org.slf4j.Logger;
63
Brian O'Connor03406a42015-02-03 17:28:57 -080064import com.google.common.collect.ImmutableList;
65import com.google.common.collect.ImmutableMap;
66import com.google.common.collect.Lists;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070067
Brian O'Connorfa81eae2014-10-30 13:20:05 -070068import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080069import static java.util.concurrent.Executors.newFixedThreadPool;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070070import static org.onlab.util.Tools.namedThreads;
Brian O'Connor03406a42015-02-03 17:28:57 -080071import static org.onosproject.net.intent.IntentState.FAILED;
Brian O'Connor03406a42015-02-03 17:28:57 -080072import static org.onosproject.net.intent.IntentState.INSTALLING;
73import static org.onosproject.net.intent.IntentState.INSTALL_REQ;
Brian O'Connor03406a42015-02-03 17:28:57 -080074import static org.onosproject.net.intent.IntentState.WITHDRAW_REQ;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070075import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070076
77/**
78 * An implementation of Intent Manager.
79 */
80@Component(immediate = true)
81@Service
82public class IntentManager
83 implements IntentService, IntentExtensionService {
Sho SHIMIZU8b5051d2014-11-05 11:24:13 -080084 private static final Logger log = getLogger(IntentManager.class);
Brian O'Connor66630c82014-10-02 21:08:19 -070085
86 public static final String INTENT_NULL = "Intent cannot be null";
87 public static final String INTENT_ID_NULL = "Intent ID cannot be null";
88
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080089 private static final int NUM_THREADS = 12;
90
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080091 private static final EnumSet<IntentState> RECOMPILE
92 = EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080093
94
Brian O'Connor66630c82014-10-02 21:08:19 -070095 // Collections for compiler, installer, and listener are ONOS instance local
96 private final ConcurrentMap<Class<? extends Intent>,
97 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
Thomas Vachuskab97cf282014-10-20 23:31:12 -070098 private final ConcurrentMap<Class<? extends Intent>,
99 IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700100
101 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -0700102 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700103
Brian O'Connor520c0522014-11-23 23:50:47 -0800104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected CoreService coreService;
Brian O'Connor66630c82014-10-02 21:08:19 -0700106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 protected IntentStore store;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -0700111 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -0700112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -0700114 protected EventDeliveryService eventDispatcher;
115
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 protected FlowRuleService flowRuleService;
118
Brian O'Connor520c0522014-11-23 23:50:47 -0800119
120 private ExecutorService executor;
Brian O'Connor520c0522014-11-23 23:50:47 -0800121
122 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
123 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
124 private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
125 private IdGenerator idGenerator;
126
Brian O'Connorb499b352015-02-03 16:46:15 -0800127 private final IntentAccumulator accumulator = new IntentAccumulator(batchDelegate);
Brian O'Connorcff03322015-02-03 15:28:59 -0800128
Brian O'Connor66630c82014-10-02 21:08:19 -0700129 @Activate
130 public void activate() {
131 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700132 trackerService.setDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700133 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Yuta HIGUCHI7a9fddd2014-12-03 15:30:25 -0800134 executor = newFixedThreadPool(NUM_THREADS, namedThreads("onos-intent-%d"));
Brian O'Connor520c0522014-11-23 23:50:47 -0800135 idGenerator = coreService.getIdGenerator("intent-ids");
136 Intent.bindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700137 log.info("Started");
138 }
139
140 @Deactivate
141 public void deactivate() {
142 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700143 trackerService.unsetDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700144 eventDispatcher.removeSink(IntentEvent.class);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700145 executor.shutdown();
Brian O'Connor520c0522014-11-23 23:50:47 -0800146 Intent.unbindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700147 log.info("Stopped");
148 }
149
150 @Override
151 public void submit(Intent intent) {
152 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800153 IntentData data = new IntentData(intent, IntentState.INSTALL_REQ, null);
154 //FIXME timestamp?
155 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700156 }
157
158 @Override
159 public void withdraw(Intent intent) {
160 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800161 IntentData data = new IntentData(intent, IntentState.WITHDRAW_REQ, null);
162 //FIXME timestamp?
163 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700164 }
165
Brian O'Connor66630c82014-10-02 21:08:19 -0700166 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700167 public Iterable<Intent> getIntents() {
168 return store.getIntents();
169 }
170
171 @Override
172 public long getIntentCount() {
173 return store.getIntentCount();
174 }
175
176 @Override
177 public Intent getIntent(IntentId id) {
178 checkNotNull(id, INTENT_ID_NULL);
179 return store.getIntent(id);
180 }
181
182 @Override
183 public IntentState getIntentState(IntentId id) {
184 checkNotNull(id, INTENT_ID_NULL);
185 return store.getIntentState(id);
186 }
187
188 @Override
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700189 public List<Intent> getInstallableIntents(IntentId intentId) {
190 checkNotNull(intentId, INTENT_ID_NULL);
191 return store.getInstallableIntents(intentId);
192 }
193
194 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700195 public void addListener(IntentListener listener) {
196 listenerRegistry.addListener(listener);
197 }
198
199 @Override
200 public void removeListener(IntentListener listener) {
201 listenerRegistry.removeListener(listener);
202 }
203
204 @Override
205 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
206 compilers.put(cls, compiler);
207 }
208
209 @Override
210 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
211 compilers.remove(cls);
212 }
213
214 @Override
215 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
216 return ImmutableMap.copyOf(compilers);
217 }
218
219 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700220 public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700221 installers.put(cls, installer);
222 }
223
224 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700225 public <T extends Intent> void unregisterInstaller(Class<T> cls) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700226 installers.remove(cls);
227 }
228
229 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700230 public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
Brian O'Connor66630c82014-10-02 21:08:19 -0700231 return ImmutableMap.copyOf(installers);
232 }
233
234 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700235 * Returns the corresponding intent compiler to the specified intent.
236 *
237 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700238 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700239 * @return intent compiler corresponding to the specified intent
240 */
241 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
242 @SuppressWarnings("unchecked")
243 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
244 if (compiler == null) {
245 throw new IntentException("no compiler for class " + intent.getClass());
246 }
247 return compiler;
248 }
249
250 /**
251 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700252 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700253 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700254 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700255 * @return intent installer corresponding to the specified installable intent
256 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700257 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700258 @SuppressWarnings("unchecked")
259 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
260 if (installer == null) {
261 throw new IntentException("no installer for class " + intent.getClass());
262 }
263 return installer;
264 }
265
266 /**
Brian O'Connorcb900f42014-10-07 21:55:33 -0700267 * Compiles an intent recursively.
268 *
269 * @param intent intent
270 * @return result of compilation
271 */
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800272 private List<Intent> compileIntent(Intent intent, List<Intent> previousInstallables) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700273 if (intent.isInstallable()) {
274 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700275 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700276
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700277 registerSubclassCompilerIfNeeded(intent);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700278 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700279 List<Intent> installable = new ArrayList<>();
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800280 for (Intent compiled : getCompiler(intent).compile(intent, previousInstallables, null)) {
281 installable.addAll(compileIntent(compiled, previousInstallables));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700282 }
tom85258ee2014-10-07 00:10:02 -0700283 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700284 }
285
286 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700287 * Uninstalls all installable intents associated with the given intent.
288 *
Brian O'Connor427a1762014-11-19 18:40:32 -0800289 * @param intent intent
290 * @param installables installable intents
291 * @return list of batches to uninstall intent
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700292 */
Brian O'Connor427a1762014-11-19 18:40:32 -0800293 private List<FlowRuleBatchOperation> uninstallIntent(Intent intent, List<Intent> installables) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700294 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
Brian O'Connor427a1762014-11-19 18:40:32 -0800295 for (Intent installable : installables) {
296 trackerService.removeTrackedResources(intent.id(),
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800297 installable.resources());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700298 try {
299 batches.addAll(getInstaller(installable).uninstall(installable));
300 } catch (IntentException e) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800301 log.warn("Unable to uninstall intent {} due to:", intent.id(), e);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700302 // TODO: this should never happen. but what if it does?
303 }
304 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800305 return batches;
tom85258ee2014-10-07 00:10:02 -0700306 }
307
308 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700309 * Registers an intent compiler of the specified intent if an intent compiler
310 * for the intent is not registered. This method traverses the class hierarchy of
311 * the intent. Once an intent compiler for a parent type is found, this method
312 * registers the found intent compiler.
313 *
314 * @param intent intent
315 */
316 private void registerSubclassCompilerIfNeeded(Intent intent) {
317 if (!compilers.containsKey(intent.getClass())) {
318 Class<?> cls = intent.getClass();
319 while (cls != Object.class) {
320 // As long as we're within the Intent class descendants
321 if (Intent.class.isAssignableFrom(cls)) {
322 IntentCompiler<?> compiler = compilers.get(cls);
323 if (compiler != null) {
324 compilers.put(intent.getClass(), compiler);
325 return;
326 }
327 }
328 cls = cls.getSuperclass();
329 }
330 }
331 }
332
333 /**
334 * Registers an intent installer of the specified intent if an intent installer
335 * for the intent is not registered. This method traverses the class hierarchy of
336 * the intent. Once an intent installer for a parent type is found, this method
337 * registers the found intent installer.
338 *
339 * @param intent intent
340 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700341 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700342 if (!installers.containsKey(intent.getClass())) {
343 Class<?> cls = intent.getClass();
344 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700345 // As long as we're within the Intent class descendants
346 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700347 IntentInstaller<?> installer = installers.get(cls);
348 if (installer != null) {
349 installers.put(intent.getClass(), installer);
350 return;
351 }
352 }
353 cls = cls.getSuperclass();
354 }
355 }
356 }
357
Brian O'Connor66630c82014-10-02 21:08:19 -0700358 // Store delegate to re-post events emitted from the store.
359 private class InternalStoreDelegate implements IntentStoreDelegate {
360 @Override
361 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700362 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700363 }
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800364
365 @Override
Brian O'Connorcff03322015-02-03 15:28:59 -0800366 public void process(IntentData data) {
367 accumulator.add(data);
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800368 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700369 }
370
Brian O'Connor72a034c2014-11-26 18:24:23 -0800371 private void buildAndSubmitBatches(Iterable<IntentId> intentIds,
372 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800373 // Attempt recompilation of the specified intents first.
374 for (IntentId id : intentIds) {
375 Intent intent = store.getIntent(id);
376 if (intent == null) {
377 continue;
378 }
Brian O'Connor03406a42015-02-03 17:28:57 -0800379 submit(intent);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800380 }
381
382 if (compileAllFailed) {
383 // If required, compile all currently failed intents.
384 for (Intent intent : getIntents()) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800385 IntentState state = getIntentState(intent.id());
386 if (RECOMPILE.contains(state)) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800387 if (state == WITHDRAW_REQ) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800388 withdraw(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800389 } else {
Brian O'Connor03406a42015-02-03 17:28:57 -0800390 submit(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800391 }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800392 }
393 }
394 }
395
Brian O'Connorb499b352015-02-03 16:46:15 -0800396 //FIXME
397// for (ApplicationId appId : batches.keySet()) {
398// if (batchService.isLocalLeader(appId)) {
399// execute(batches.get(appId).build());
400// }
401// }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800402 }
403
tom95329eb2014-10-06 08:40:06 -0700404 // Topology change delegate
405 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
406 @Override
tom85258ee2014-10-07 00:10:02 -0700407 public void triggerCompile(Iterable<IntentId> intentIds,
408 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800409 buildAndSubmitBatches(intentIds, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700410 }
tom95329eb2014-10-06 08:40:06 -0700411 }
tom85258ee2014-10-07 00:10:02 -0700412
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800413 // TODO: simplify the branching statements
Brian O'Connorb499b352015-02-03 16:46:15 -0800414 private IntentUpdate createIntentUpdate(IntentData intentData) {
415 IntentData currentState = store.getIntentData(intentData.key());
416 switch (intentData.state()) {
417 case INSTALL_REQ:
418 return new InstallRequest(intentData.intent(), currentState);
419 case WITHDRAW_REQ:
420 return new WithdrawRequest(intentData.intent(), currentState);
421 // fallthrough
422 case COMPILING:
423 case INSTALLING:
424 case INSTALLED:
425 case RECOMPILING:
426 case WITHDRAWING:
427 case WITHDRAWN:
428 case FAILED:
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800429 default:
430 // illegal state
431 return new DoNothing();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700432 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700433 }
434
Brian O'Connorcff03322015-02-03 15:28:59 -0800435 // TODO pull out the IntentUpdate inner classes
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800436 private class InstallRequest implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800437
438 private final Intent intent;
Brian O'Connorb499b352015-02-03 16:46:15 -0800439 private final IntentData currentState;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800440
Brian O'Connorb499b352015-02-03 16:46:15 -0800441 InstallRequest(Intent intent, IntentData currentState) {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800442 this.intent = checkNotNull(intent);
Brian O'Connorb499b352015-02-03 16:46:15 -0800443 this.currentState = currentState;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800444 }
445
446 @Override
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800447 public Optional<IntentUpdate> execute() {
Brian O'Connorb499b352015-02-03 16:46:15 -0800448 return Optional.of(new Compiling(intent)); //FIXME
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800449 }
450 }
451
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800452 private class WithdrawRequest implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800453
454 private final Intent intent;
Brian O'Connorb499b352015-02-03 16:46:15 -0800455 private final IntentData currentState;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800456
Brian O'Connorb499b352015-02-03 16:46:15 -0800457 WithdrawRequest(Intent intent, IntentData currentState) {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800458 this.intent = checkNotNull(intent);
Brian O'Connorb499b352015-02-03 16:46:15 -0800459 this.currentState = currentState;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800460 }
461
462 @Override
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800463 public Optional<IntentUpdate> execute() {
Brian O'Connorb499b352015-02-03 16:46:15 -0800464 return Optional.of(new Withdrawing(intent, currentState.installables())); //FIXME
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800465 }
466 }
467
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800468 private class Compiling implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800469
470 private final Intent intent;
471
472 Compiling(Intent intent) {
473 this.intent = checkNotNull(intent);
474 }
475
476 @Override
477 public Optional<IntentUpdate> execute() {
478 try {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800479 return Optional.of(new Installing(intent, compileIntent(intent, null)));
480 } catch (PathNotFoundException e) {
Sho SHIMIZUfd0cd8c2015-01-28 08:16:58 -0800481 log.debug("Path not found for intent {}", intent);
Sho SHIMIZUc708f3b2015-02-04 09:19:01 -0800482 // TODO: revisit to implement failure handling
483 return Optional.of(new DoNothing());
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800484 } catch (IntentException e) {
Sho SHIMIZUfd0cd8c2015-01-28 08:16:58 -0800485 log.warn("Unable to compile intent {} due to:", intent.id(), e);
Sho SHIMIZUc708f3b2015-02-04 09:19:01 -0800486 // TODO: revisit to implement failure handling
487 return Optional.of(new DoNothing());
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800488 }
489 }
490 }
491
492 // TODO: better naming because install() method actually generate FlowRuleBatchOperations
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800493 private class Installing implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800494
495 private final Intent intent;
496 private final List<Intent> installables;
497
498 Installing(Intent intent, List<Intent> installables) {
499 this.intent = checkNotNull(intent);
500 this.installables = ImmutableList.copyOf(checkNotNull(installables));
501 }
502
503 @Override
504 public Optional<IntentUpdate> execute() {
Sho SHIMIZU8d9d1362015-02-04 12:28:15 -0800505 try {
506 List<FlowRuleBatchOperation> converted = convert(installables);
507 // TODO: call FlowRuleService API to push FlowRules and track resources,
508 // which the submitted intent will use.
509 return Optional.of(new Installed(intent, installables, converted));
510 } catch (FlowRuleBatchOperationConvertionException e) {
511 log.warn("Unable to install intent {} due to:", intent.id(), e.getCause());
512 return Optional.of(new InstallingFailed(intent, installables, e.converted()));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700513 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800514 }
515 }
516
Sho SHIMIZU8d9d1362015-02-04 12:28:15 -0800517 private List<FlowRuleBatchOperation> convert(List<Intent> installables) {
518 List<FlowRuleBatchOperation> batches = new ArrayList<>(installables.size());
519 for (Intent installable : installables) {
520 try {
521 registerSubclassInstallerIfNeeded(installable);
522 batches.addAll(getInstaller(installable).install(installable));
523 } catch (Exception e) { // TODO this should be IntentException
524 throw new FlowRuleBatchOperationConvertionException(batches, e);
525 }
526 }
527 return batches;
528 }
529
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800530 private class Withdrawing implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800531
532 private final Intent intent;
533 private final List<Intent> installables;
534
535 Withdrawing(Intent intent, List<Intent> installables) {
536 this.intent = checkNotNull(intent);
537 this.installables = ImmutableList.copyOf(installables);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800538 }
539
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800540 @Override
541 public Optional<IntentUpdate> execute() {
542 List<FlowRuleBatchOperation> batches = uninstallIntent(intent, installables);
543
544 return Optional.of(new Withdrawn(intent, installables, batches));
545 }
546 }
547
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800548 private class Installed implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800549
550 private final Intent intent;
551 private final List<Intent> installables;
552 private IntentState intentState;
553 private final List<FlowRuleBatchOperation> batches;
554 private int currentBatch = 0;
555
556 Installed(Intent intent, List<Intent> installables, List<FlowRuleBatchOperation> batches) {
557 this.intent = checkNotNull(intent);
558 this.installables = ImmutableList.copyOf(checkNotNull(installables));
559 this.batches = new LinkedList<>(checkNotNull(batches));
560 this.intentState = INSTALLING;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700561 }
562
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800563 @Override
564 public void batchSuccess() {
Ray Milkey93508c22014-12-02 11:35:56 -0800565 currentBatch++;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800566 }
567
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800568 @Override
569 public List<Intent> allInstallables() {
570 return installables;
571 }
572
573 @Override
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800574 public FlowRuleBatchOperation currentBatch() {
575 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
576 }
577
578 @Override
579 public void batchFailed() {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800580 for (int i = batches.size() - 1; i >= currentBatch; i--) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800581 batches.remove(i);
582 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800583 intentState = FAILED;
584 batches.addAll(uninstallIntent(intent, installables));
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800585
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800586 // TODO we might want to try to recompile the new intent
Brian O'Connor427a1762014-11-19 18:40:32 -0800587 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800588 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800589
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800590 private class Withdrawn implements CompletedIntentUpdate {
Brian O'Connor427a1762014-11-19 18:40:32 -0800591
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800592 private final Intent intent;
593 private final List<Intent> installables;
594 private final List<FlowRuleBatchOperation> batches;
595 private int currentBatch;
596
597 Withdrawn(Intent intent, List<Intent> installables, List<FlowRuleBatchOperation> batches) {
598 this.intent = checkNotNull(intent);
599 this.installables = ImmutableList.copyOf(installables);
600 this.batches = new LinkedList<>(batches);
601 this.currentBatch = 0;
602 }
603
604 @Override
605 public List<Intent> allInstallables() {
606 return installables;
607 }
608
609 @Override
610 public void batchSuccess() {
611 currentBatch++;
612 }
613
614 @Override
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800615 public FlowRuleBatchOperation currentBatch() {
616 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
617 }
618
619 @Override
620 public void batchFailed() {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800621 for (int i = batches.size() - 1; i >= currentBatch; i--) {
622 batches.remove(i);
Brian O'Connor427a1762014-11-19 18:40:32 -0800623 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800624 batches.addAll(uninstallIntent(intent, installables));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700625 }
626 }
627
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800628 private class InstallingFailed implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800629
630 private final Intent intent;
631 private final List<Intent> installables;
632 private final List<FlowRuleBatchOperation> batches;
633 private int currentBatch = 0;
634
635 InstallingFailed(Intent intent, List<Intent> installables, List<FlowRuleBatchOperation> batches) {
636 this.intent = checkNotNull(intent);
637 this.installables = ImmutableList.copyOf(checkNotNull(installables));
638 this.batches = new LinkedList<>(checkNotNull(batches));
639 }
640
641 @Override
642 public List<Intent> allInstallables() {
643 return installables;
644 }
645
646 @Override
647 public void batchSuccess() {
648 currentBatch++;
649 }
650
651 @Override
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800652 public FlowRuleBatchOperation currentBatch() {
653 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
654 }
655
656 @Override
657 public void batchFailed() {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800658 for (int i = batches.size() - 1; i >= currentBatch; i--) {
659 batches.remove(i);
660 }
661 batches.addAll(uninstallIntent(intent, installables));
662
663 // TODO we might want to try to recompile the new intent
664 }
665 }
666
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800667 private class IntentBatchPreprocess implements Runnable {
668
669 // TODO make this configurable
670 private static final int TIMEOUT_PER_OP = 500; // ms
671 protected static final int MAX_ATTEMPTS = 3;
672
Brian O'Connorb499b352015-02-03 16:46:15 -0800673 protected final Collection<IntentData> ops;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800674
675 // future holding current FlowRuleBatch installation result
676 protected final long startTime = System.currentTimeMillis();
677 protected final long endTime;
678
Brian O'Connorb499b352015-02-03 16:46:15 -0800679 private IntentBatchPreprocess(Collection<IntentData> ops, long endTime) {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800680 this.ops = checkNotNull(ops);
681 this.endTime = endTime;
682 }
683
Brian O'Connorb499b352015-02-03 16:46:15 -0800684 public IntentBatchPreprocess(Collection<IntentData> ops) {
685 this(ops, System.currentTimeMillis() + ops.size() * TIMEOUT_PER_OP);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800686 }
687
688 // FIXME compute reasonable timeouts
689 protected long calculateTimeoutLimit() {
Brian O'Connorb499b352015-02-03 16:46:15 -0800690 return System.currentTimeMillis() + ops.size() * TIMEOUT_PER_OP;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800691 }
692
693 @Override
694 public void run() {
695 try {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800696 List<IntentUpdate> updates = createIntentUpdates();
697
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800698 new IntentBatchApplyFirst(ops, processIntentUpdates(updates), endTime, 0, null).run();
699 } catch (Exception e) {
700 log.error("Error submitting batches:", e);
701 // FIXME incomplete Intents should be cleaned up
702 // (transition to FAILED, etc.)
703
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800704 // the batch has failed
705 // TODO: maybe we should do more?
706 log.error("Walk the plank, matey...");
Brian O'Connorb499b352015-02-03 16:46:15 -0800707 //FIXME
708// batchService.removeIntentOperations(ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800709 }
710 }
711
712 private List<IntentUpdate> createIntentUpdates() {
Brian O'Connorb499b352015-02-03 16:46:15 -0800713 return ops.stream()
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800714 .map(IntentManager.this::createIntentUpdate)
715 .collect(Collectors.toList());
716 }
717
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800718 private List<CompletedIntentUpdate> processIntentUpdates(List<IntentUpdate> updates) {
719 // start processing each Intents
720 List<CompletedIntentUpdate> completed = new ArrayList<>();
721 for (IntentUpdate update : updates) {
722 Optional<IntentUpdate> phase = Optional.of(update);
723 IntentUpdate previous = update;
724 while (true) {
725 if (!phase.isPresent()) {
726 // FIXME: not type safe cast
727 completed.add((CompletedIntentUpdate) previous);
728 break;
729 }
730 previous = phase.get();
731 phase = previous.execute();
732 }
733 }
734
735 return completed;
736 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800737 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800738
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800739 // TODO: better naming
740 private class IntentBatchApplyFirst extends IntentBatchPreprocess {
741
742 protected final List<CompletedIntentUpdate> intentUpdates;
743 protected final int installAttempt;
744 protected Future<CompletedBatchOperation> future;
745
Brian O'Connorb499b352015-02-03 16:46:15 -0800746 IntentBatchApplyFirst(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800747 long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
748 super(operations, endTime);
749 this.intentUpdates = ImmutableList.copyOf(intentUpdates);
750 this.future = future;
751 this.installAttempt = installAttempt;
752 }
753
754 @Override
755 public void run() {
756 Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
757 new IntentBatchProcessFutures(ops, intentUpdates, endTime, installAttempt, future).run();
Brian O'Connorcb900f42014-10-07 21:55:33 -0700758 }
759
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700760 /**
Brian O'Connor427a1762014-11-19 18:40:32 -0800761 * Builds and applies the next batch, and returns the future.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700762 *
763 * @return Future for next batch
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700764 */
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800765 protected Future<CompletedBatchOperation> applyNextBatch(List<CompletedIntentUpdate> updates) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800766 //TODO test this. (also, maybe save this batch)
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800767
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800768 FlowRuleBatchOperation batch = createFlowRuleBatchOperation(updates);
Ray Milkey93508c22014-12-02 11:35:56 -0800769 if (batch.size() > 0) {
770 //FIXME apply batch might throw an exception
771 return flowRuleService.applyBatch(batch);
772 } else {
Ray Milkey93508c22014-12-02 11:35:56 -0800773 return null;
774 }
775 }
776
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800777 private FlowRuleBatchOperation createFlowRuleBatchOperation(List<CompletedIntentUpdate> intentUpdates) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800778 FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList(), null, 0);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800779 for (CompletedIntentUpdate update : intentUpdates) {
780 FlowRuleBatchOperation currentBatch = update.currentBatch();
781 if (currentBatch != null) {
782 batch.addAll(currentBatch);
783 }
784 }
785 return batch;
786 }
787
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800788 protected void abandonShip() {
789 // the batch has failed
790 // TODO: maybe we should do more?
791 log.error("Walk the plank, matey...");
792 future = null;
Brian O'Connorb499b352015-02-03 16:46:15 -0800793 //FIXME
794// batchService.removeIntentOperations(ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800795 }
796 }
797
798 // TODO: better naming
799 private class IntentBatchProcessFutures extends IntentBatchApplyFirst {
800
Brian O'Connorb499b352015-02-03 16:46:15 -0800801 IntentBatchProcessFutures(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800802 long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
803 super(operations, intentUpdates, endTime, installAttempt, future);
804 }
805
806 @Override
807 public void run() {
808 try {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800809 Future<CompletedBatchOperation> future = processFutures();
810 if (future == null) {
811 // there are no outstanding batches; we are done
Brian O'Connorb499b352015-02-03 16:46:15 -0800812 //FIXME
813 return; //?
814// batchService.removeIntentOperations(ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800815 } else if (System.currentTimeMillis() > endTime) {
816 // - cancel current FlowRuleBatch and resubmit again
817 retry();
818 } else {
819 // we are not done yet, yield the thread by resubmitting ourselves
820 executor.submit(new IntentBatchProcessFutures(ops, intentUpdates, endTime, installAttempt, future));
821 }
822 } catch (Exception e) {
823 log.error("Error submitting batches:", e);
824 // FIXME incomplete Intents should be cleaned up
825 // (transition to FAILED, etc.)
826 abandonShip();
827 }
828 }
829
830 /**
831 * Iterate through the pending futures, and remove them when they have completed.
832 */
833 private Future<CompletedBatchOperation> processFutures() {
834 try {
835 CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
836 updateBatches(completed);
837 return applyNextBatch(intentUpdates);
838 } catch (TimeoutException | InterruptedException te) {
839 log.trace("Installation of intents are still pending: {}", ops);
840 return future;
841 } catch (ExecutionException e) {
842 log.warn("Execution of batch failed: {}", ops, e);
843 abandonShip();
844 return future;
845 }
846 }
847
Ray Milkey93508c22014-12-02 11:35:56 -0800848 private void updateBatches(CompletedBatchOperation completed) {
849 if (completed.isSuccess()) {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800850 for (CompletedIntentUpdate update : intentUpdates) {
Ray Milkey93508c22014-12-02 11:35:56 -0800851 update.batchSuccess();
852 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700853 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -0800854 // entire batch has been reverted...
Ray Milkey93508c22014-12-02 11:35:56 -0800855 log.debug("Failed items: {}", completed.failedItems());
856 log.debug("Failed ids: {}", completed.failedIds());
Brian O'Connor427a1762014-11-19 18:40:32 -0800857
858 for (Long id : completed.failedIds()) {
859 IntentId targetId = IntentId.valueOf(id);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800860 for (CompletedIntentUpdate update : intentUpdates) {
861 for (Intent intent : update.allInstallables()) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800862 if (intent.id().equals(targetId)) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800863 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -0800864 break;
865 }
866 }
867 }
868 // don't increment the non-failed items, as they have been reverted.
869 }
870 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700871 }
872
Brian O'Connor427a1762014-11-19 18:40:32 -0800873 private void retry() {
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800874 log.debug("Execution timed out, retrying.");
Brian O'Connor427a1762014-11-19 18:40:32 -0800875 if (future.cancel(true)) { // cancel success; batch is reverted
876 // reset the timer
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800877 long timeLimit = calculateTimeoutLimit();
878 int attempts = installAttempt + 1;
879 if (attempts == MAX_ATTEMPTS) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800880 log.warn("Install request timed out: {}", ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800881 for (CompletedIntentUpdate update : intentUpdates) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800882 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -0800883 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800884 } else if (attempts > MAX_ATTEMPTS) {
Ray Milkey93508c22014-12-02 11:35:56 -0800885 abandonShip();
886 return;
Sho SHIMIZU55a55182015-02-03 22:32:56 -0800887 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800888 Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
889 executor.submit(new IntentBatchProcessFutures(ops, intentUpdates, timeLimit, attempts, future));
Brian O'Connor427a1762014-11-19 18:40:32 -0800890 } else {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800891 log.error("Cancelling FlowRuleBatch failed.");
Ray Milkey93508c22014-12-02 11:35:56 -0800892 abandonShip();
Brian O'Connor427a1762014-11-19 18:40:32 -0800893 }
894 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700895 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700896
897 private class InternalBatchDelegate implements IntentBatchDelegate {
898 @Override
Brian O'Connorb499b352015-02-03 16:46:15 -0800899 public void execute(Collection<IntentData> operations) {
900 log.info("Execute {} operation(s).", operations.size());
901 log.debug("Execute operations: {}", operations);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800902 executor.execute(new IntentBatchPreprocess(operations));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700903 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700904 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700905}