Yuta HIGUCHI
Committed by Yuta Higuchi

IntentStore: add batch write API

Change-Id: I9d397e9dc3dc6e9ccd21ac6ddacaece79214c470
...@@ -15,8 +15,17 @@ ...@@ -15,8 +15,17 @@
15 */ 15 */
16 package org.onlab.onos.net.intent; 16 package org.onlab.onos.net.intent;
17 17
18 +import static com.google.common.base.Preconditions.checkArgument;
19 +import static com.google.common.base.Preconditions.checkNotNull;
20 +
21 +import org.onlab.onos.net.intent.IntentStore.BatchWrite.Operation;
18 import org.onlab.onos.store.Store; 22 import org.onlab.onos.store.Store;
19 23
24 +import com.google.common.base.MoreObjects;
25 +import com.google.common.collect.ImmutableList;
26 +
27 +import java.util.ArrayList;
28 +import java.util.Collections;
20 import java.util.List; 29 import java.util.List;
21 30
22 /** 31 /**
...@@ -110,4 +119,165 @@ public interface IntentStore extends Store<IntentEvent, IntentStoreDelegate> { ...@@ -110,4 +119,165 @@ public interface IntentStore extends Store<IntentEvent, IntentStoreDelegate> {
110 */ 119 */
111 void removeInstalledIntents(IntentId intentId); 120 void removeInstalledIntents(IntentId intentId);
112 121
122 +
123 + /**
124 + * Returns a new empty batch write operation buider.
125 + *
126 + * @return BatchWrite
127 + */
128 + default BatchWrite newBatchWrite() {
129 + return new BatchWrite();
130 + }
131 +
132 + // default implementation simply executes them sequentially.
133 + // Store implementation should override and implement actual batch write.
134 + /**
135 + * Execute writes in a batch.
136 + *
137 + * @param batch BatchWrite to execute
138 + * @return failed operations
139 + */
140 + default List<Operation> batchWrite(BatchWrite batch) {
141 + List<Operation> failed = new ArrayList<>();
142 + for (Operation op : batch.operations) {
143 + switch (op.type) {
144 + case CREATE_INTENT:
145 + checkArgument(op.args.size() == 1,
146 + "CREATE_INTENT takes 1 argument. %s", op);
147 + Intent intent = (Intent) op.args.get(0);
148 + if (createIntent(intent) == null) {
149 + failed.add(op);
150 + }
151 + break;
152 +
153 + case REMOVE_INTENT:
154 + checkArgument(op.args.size() == 1,
155 + "REMOVE_INTENT takes 1 argument. %s", op);
156 + IntentId intentId = (IntentId) op.args.get(0);
157 + removeIntent(intentId);
158 + break;
159 +
160 + case REMOVE_INSTALLED:
161 + checkArgument(op.args.size() == 1,
162 + "REMOVE_INSTALLED takes 1 argument. %s", op);
163 + intentId = (IntentId) op.args.get(0);
164 + removeInstalledIntents(intentId);
165 + break;
166 +
167 + case SET_INSTALLABLE:
168 + checkArgument(op.args.size() == 2,
169 + "SET_INSTALLABLE takes 2 arguments. %s", op);
170 + intentId = (IntentId) op.args.get(0);
171 + @SuppressWarnings("unchecked")
172 + List<Intent> installableIntents = (List<Intent>) op.args.get(1);
173 + setInstallableIntents(intentId, installableIntents);
174 + break;
175 +
176 + case SET_STATE:
177 + checkArgument(op.args.size() == 2,
178 + "SET_STATE takes 2 arguments. %s", op);
179 + intent = (Intent) op.args.get(0);
180 + IntentState newState = (IntentState) op.args.get(1);
181 + setState(intent, newState);
182 + break;
183 +
184 + default:
185 + break;
186 + }
187 + }
188 + return failed;
189 + }
190 +
191 + public static class BatchWrite {
192 +
193 + public enum OpType {
194 + CREATE_INTENT,
195 + REMOVE_INTENT,
196 + SET_STATE,
197 + SET_INSTALLABLE,
198 + REMOVE_INSTALLED
199 + }
200 +
201 + List<Operation> operations = new ArrayList<>();
202 +
203 + public List<Operation> operations() {
204 + return Collections.unmodifiableList(operations);
205 + }
206 +
207 + public boolean isEmpty() {
208 + return operations.isEmpty();
209 + }
210 +
211 + public BatchWrite createIntent(Intent intent) {
212 + operations.add(Operation.of(OpType.CREATE_INTENT,
213 + ImmutableList.of(intent)));
214 + return this;
215 + }
216 +
217 + public BatchWrite removeIntent(IntentId intentId) {
218 + operations.add(Operation.of(OpType.REMOVE_INTENT,
219 + ImmutableList.of(intentId)));
220 + return this;
221 + }
222 +
223 + public BatchWrite setState(Intent intent, IntentState newState) {
224 + operations.add(Operation.of(OpType.SET_STATE,
225 + ImmutableList.of(intent, newState)));
226 + return this;
227 + }
228 +
229 + public BatchWrite setInstallableIntents(IntentId intentId, List<Intent> installableIntents) {
230 + operations.add(Operation.of(OpType.SET_INSTALLABLE,
231 + ImmutableList.of(intentId, installableIntents)));
232 + return this;
233 + }
234 +
235 + public BatchWrite removeInstalledIntents(IntentId intentId) {
236 + operations.add(Operation.of(OpType.REMOVE_INSTALLED,
237 + ImmutableList.of(intentId)));
238 + return this;
239 + }
240 +
241 + @Override
242 + public String toString() {
243 + return MoreObjects.toStringHelper(getClass())
244 + .add("operations", operations)
245 + .toString();
246 + }
247 +
248 + public static class Operation {
249 + final OpType type;
250 + final ImmutableList<Object> args;
251 +
252 + public static Operation of(OpType type, List<Object> args) {
253 + return new Operation(type, args);
254 + }
255 +
256 + public Operation(OpType type, List<Object> args) {
257 + this.type = checkNotNull(type);
258 + this.args = ImmutableList.copyOf(args);
259 + }
260 +
261 + public OpType type() {
262 + return type;
263 + }
264 +
265 + public ImmutableList<Object> args() {
266 + return args;
267 + }
268 +
269 + @SuppressWarnings("unchecked")
270 + public <T> T arg(int i) {
271 + return (T) args.get(i);
272 + }
273 +
274 + @Override
275 + public String toString() {
276 + return MoreObjects.toStringHelper(getClass())
277 + .add("type", type)
278 + .add("args", args)
279 + .toString();
280 + }
281 + }
282 + }
113 } 283 }
......
...@@ -18,6 +18,9 @@ package org.onlab.onos.store.intent.impl; ...@@ -18,6 +18,9 @@ package org.onlab.onos.store.intent.impl;
18 import com.codahale.metrics.Timer; 18 import com.codahale.metrics.Timer;
19 import com.codahale.metrics.Timer.Context; 19 import com.codahale.metrics.Timer.Context;
20 import com.google.common.base.Verify; 20 import com.google.common.base.Verify;
21 +import com.google.common.cache.CacheBuilder;
22 +import com.google.common.cache.CacheLoader;
23 +import com.google.common.cache.LoadingCache;
21 import com.google.common.collect.ImmutableSet; 24 import com.google.common.collect.ImmutableSet;
22 25
23 import org.apache.felix.scr.annotations.Activate; 26 import org.apache.felix.scr.annotations.Activate;
...@@ -34,22 +37,28 @@ import org.onlab.onos.net.intent.IntentId; ...@@ -34,22 +37,28 @@ import org.onlab.onos.net.intent.IntentId;
34 import org.onlab.onos.net.intent.IntentState; 37 import org.onlab.onos.net.intent.IntentState;
35 import org.onlab.onos.net.intent.IntentStore; 38 import org.onlab.onos.net.intent.IntentStore;
36 import org.onlab.onos.net.intent.IntentStoreDelegate; 39 import org.onlab.onos.net.intent.IntentStoreDelegate;
40 +import org.onlab.onos.net.intent.IntentStore.BatchWrite.Operation;
37 import org.onlab.onos.store.AbstractStore; 41 import org.onlab.onos.store.AbstractStore;
38 import org.onlab.onos.store.serializers.KryoNamespaces; 42 import org.onlab.onos.store.serializers.KryoNamespaces;
39 import org.onlab.onos.store.serializers.KryoSerializer; 43 import org.onlab.onos.store.serializers.KryoSerializer;
40 import org.onlab.onos.store.serializers.StoreSerializer; 44 import org.onlab.onos.store.serializers.StoreSerializer;
45 +import org.onlab.onos.store.service.BatchWriteRequest;
46 +import org.onlab.onos.store.service.BatchWriteRequest.Builder;
47 +import org.onlab.onos.store.service.BatchWriteResult;
41 import org.onlab.onos.store.service.DatabaseAdminService; 48 import org.onlab.onos.store.service.DatabaseAdminService;
42 import org.onlab.onos.store.service.DatabaseService; 49 import org.onlab.onos.store.service.DatabaseService;
43 import org.onlab.onos.store.service.impl.CMap; 50 import org.onlab.onos.store.service.impl.CMap;
44 import org.onlab.util.KryoNamespace; 51 import org.onlab.util.KryoNamespace;
45 import org.slf4j.Logger; 52 import org.slf4j.Logger;
46 53
54 +import java.util.ArrayList;
47 import java.util.EnumSet; 55 import java.util.EnumSet;
48 import java.util.List; 56 import java.util.List;
49 import java.util.Map; 57 import java.util.Map;
50 import java.util.Set; 58 import java.util.Set;
51 import java.util.concurrent.ConcurrentHashMap; 59 import java.util.concurrent.ConcurrentHashMap;
52 60
61 +import static com.google.common.base.Preconditions.checkArgument;
53 import static com.google.common.base.Preconditions.checkState; 62 import static com.google.common.base.Preconditions.checkState;
54 import static org.onlab.onos.net.intent.IntentState.*; 63 import static org.onlab.onos.net.intent.IntentState.*;
55 import static org.slf4j.LoggerFactory.getLogger; 64 import static org.slf4j.LoggerFactory.getLogger;
...@@ -70,15 +79,21 @@ public class DistributedIntentStore ...@@ -70,15 +79,21 @@ public class DistributedIntentStore
70 private final Logger log = getLogger(getClass()); 79 private final Logger log = getLogger(getClass());
71 80
72 // Assumption: IntentId will not have synonyms 81 // Assumption: IntentId will not have synonyms
82 + private static final String INTENTS_TABLE = "intents";
73 private CMap<IntentId, Intent> intents; 83 private CMap<IntentId, Intent> intents;
84 +
85 + private static final String STATES_TABLE = "intent-states";
74 private CMap<IntentId, IntentState> states; 86 private CMap<IntentId, IntentState> states;
75 87
76 // TODO left behind transient state issue: ONOS-103 88 // TODO left behind transient state issue: ONOS-103
77 // Map to store instance local intermediate state transition 89 // Map to store instance local intermediate state transition
78 private transient Map<IntentId, IntentState> transientStates = new ConcurrentHashMap<>(); 90 private transient Map<IntentId, IntentState> transientStates = new ConcurrentHashMap<>();
79 91
92 + private static final String INSTALLABLE_TABLE = "installable-intents";
80 private CMap<IntentId, List<Intent>> installable; 93 private CMap<IntentId, List<Intent>> installable;
81 94
95 + private LoadingCache<IntentId, String> keyCache;
96 +
82 private StoreSerializer serializer; 97 private StoreSerializer serializer;
83 98
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
...@@ -137,13 +152,23 @@ public class DistributedIntentStore ...@@ -137,13 +152,23 @@ public class DistributedIntentStore
137 } 152 }
138 }; 153 };
139 154
140 - intents = new CMap<>(dbAdminService, dbService, "intents", serializer); 155 + keyCache = CacheBuilder.newBuilder()
156 + .softValues()
157 + .build(new CacheLoader<IntentId, String>() {
158 +
159 + @Override
160 + public String load(IntentId key) {
161 + return key.toString();
162 + }
163 + });
164 +
165 + intents = new IntentIdMap<>(dbAdminService, dbService, INTENTS_TABLE, serializer);
141 166
142 - states = new CMap<>(dbAdminService, dbService, "intent-states", serializer); 167 + states = new IntentIdMap<>(dbAdminService, dbService, STATES_TABLE, serializer);
143 168
144 transientStates.clear(); 169 transientStates.clear();
145 170
146 - installable = new CMap<>(dbAdminService, dbService, "installable-intents", serializer); 171 + installable = new IntentIdMap<>(dbAdminService, dbService, INSTALLABLE_TABLE, serializer);
147 172
148 log.info("Started"); 173 log.info("Started");
149 } 174 }
...@@ -351,4 +376,101 @@ public class DistributedIntentStore ...@@ -351,4 +376,101 @@ public class DistributedIntentStore
351 stopTimer(timer); 376 stopTimer(timer);
352 } 377 }
353 } 378 }
379 +
380 + protected String strIntentId(IntentId key) {
381 + return keyCache.getUnchecked(key);
382 + }
383 +
384 + /**
385 + * Distributed Map from IntentId to some value.
386 + *
387 + * @param <V> Map value type
388 + */
389 + final class IntentIdMap<V> extends CMap<IntentId, V> {
390 +
391 + /**
392 + * Creates a IntentIdMap instance.
393 + *
394 + * @param dbAdminService DatabaseAdminService to use for this instance
395 + * @param dbService DatabaseService to use for this instance
396 + * @param tableName table which this Map corresponds to
397 + * @param serializer Value serializer
398 + */
399 + public IntentIdMap(DatabaseAdminService dbAdminService,
400 + DatabaseService dbService,
401 + String tableName,
402 + StoreSerializer serializer) {
403 + super(dbAdminService, dbService, tableName, serializer);
404 + }
405 +
406 + @Override
407 + protected String sK(IntentId key) {
408 + return strIntentId(key);
409 + }
410 + }
411 +
412 + @Override
413 + public List<Operation> batchWrite(BatchWrite batch) {
414 +
415 + List<Operation> failed = new ArrayList<>();
416 + final Builder builder = BatchWriteRequest.newBuilder();
417 +
418 + for (Operation op : batch.operations()) {
419 + switch (op.type()) {
420 + case CREATE_INTENT:
421 + checkArgument(op.args().size() == 1,
422 + "CREATE_INTENT takes 1 argument. %s", op);
423 + Intent intent = op.arg(0);
424 + builder.putIfAbsent(INTENTS_TABLE, strIntentId(intent.id()), serializer.encode(intent));
425 + builder.putIfAbsent(STATES_TABLE, strIntentId(intent.id()), serializer.encode(SUBMITTED));
426 + break;
427 +
428 + case REMOVE_INTENT:
429 + checkArgument(op.args().size() == 1,
430 + "REMOVE_INTENT takes 1 argument. %s", op);
431 + IntentId intentId = (IntentId) op.arg(0);
432 + builder.remove(INTENTS_TABLE, strIntentId(intentId));
433 + builder.remove(STATES_TABLE, strIntentId(intentId));
434 + builder.remove(INSTALLABLE_TABLE, strIntentId(intentId));
435 + break;
436 +
437 + case SET_STATE:
438 + checkArgument(op.args().size() == 2,
439 + "SET_STATE takes 2 arguments. %s", op);
440 + intent = op.arg(0);
441 + IntentState newState = op.arg(1);
442 + builder.put(STATES_TABLE, strIntentId(intent.id()), serializer.encode(newState));
443 + break;
444 +
445 + case SET_INSTALLABLE:
446 + checkArgument(op.args().size() == 2,
447 + "SET_INSTALLABLE takes 2 arguments. %s", op);
448 + intentId = op.arg(0);
449 + List<Intent> installableIntents = op.arg(1);
450 + builder.put(INSTALLABLE_TABLE, strIntentId(intentId), serializer.encode(installableIntents));
451 + break;
452 +
453 + case REMOVE_INSTALLED:
454 + checkArgument(op.args().size() == 1,
455 + "REMOVE_INSTALLED takes 1 argument. %s", op);
456 + intentId = op.arg(0);
457 + builder.remove(INSTALLABLE_TABLE, strIntentId(intentId));
458 + break;
459 +
460 + default:
461 + log.warn("Unknown Operation encountered: {}", op);
462 + failed.add(op);
463 + break;
464 + }
465 + }
466 +
467 + BatchWriteResult batchWriteResult = dbService.batchWrite(builder.build());
468 + if (batchWriteResult.isSuccessful()) {
469 + // no-failure (except for invalid input)
470 + return failed;
471 + } else {
472 + // everything failed
473 + return batch.operations();
474 + }
475 + }
354 } 476 }
......
...@@ -18,6 +18,7 @@ package org.onlab.onos.store.intent.impl; ...@@ -18,6 +18,7 @@ package org.onlab.onos.store.intent.impl;
18 import com.codahale.metrics.Timer; 18 import com.codahale.metrics.Timer;
19 import com.codahale.metrics.Timer.Context; 19 import com.codahale.metrics.Timer.Context;
20 import com.google.common.base.Verify; 20 import com.google.common.base.Verify;
21 +import com.google.common.collect.ImmutableList;
21 import com.google.common.collect.ImmutableSet; 22 import com.google.common.collect.ImmutableSet;
22 import com.hazelcast.core.EntryAdapter; 23 import com.hazelcast.core.EntryAdapter;
23 import com.hazelcast.core.EntryEvent; 24 import com.hazelcast.core.EntryEvent;
...@@ -25,6 +26,7 @@ import com.hazelcast.core.EntryListener; ...@@ -25,6 +26,7 @@ import com.hazelcast.core.EntryListener;
25 import com.hazelcast.core.IMap; 26 import com.hazelcast.core.IMap;
26 import com.hazelcast.core.Member; 27 import com.hazelcast.core.Member;
27 28
29 +import org.apache.commons.lang3.tuple.Pair;
28 import org.apache.felix.scr.annotations.Activate; 30 import org.apache.felix.scr.annotations.Activate;
29 import org.apache.felix.scr.annotations.Component; 31 import org.apache.felix.scr.annotations.Component;
30 import org.apache.felix.scr.annotations.Deactivate; 32 import org.apache.felix.scr.annotations.Deactivate;
...@@ -38,6 +40,7 @@ import org.onlab.onos.net.intent.IntentEvent; ...@@ -38,6 +40,7 @@ import org.onlab.onos.net.intent.IntentEvent;
38 import org.onlab.onos.net.intent.IntentId; 40 import org.onlab.onos.net.intent.IntentId;
39 import org.onlab.onos.net.intent.IntentState; 41 import org.onlab.onos.net.intent.IntentState;
40 import org.onlab.onos.net.intent.IntentStore; 42 import org.onlab.onos.net.intent.IntentStore;
43 +import org.onlab.onos.net.intent.IntentStore.BatchWrite.Operation;
41 import org.onlab.onos.net.intent.IntentStoreDelegate; 44 import org.onlab.onos.net.intent.IntentStoreDelegate;
42 import org.onlab.onos.store.hz.AbstractHazelcastStore; 45 import org.onlab.onos.store.hz.AbstractHazelcastStore;
43 import org.onlab.onos.store.hz.SMap; 46 import org.onlab.onos.store.hz.SMap;
...@@ -46,12 +49,16 @@ import org.onlab.onos.store.serializers.KryoSerializer; ...@@ -46,12 +49,16 @@ import org.onlab.onos.store.serializers.KryoSerializer;
46 import org.onlab.util.KryoNamespace; 49 import org.onlab.util.KryoNamespace;
47 import org.slf4j.Logger; 50 import org.slf4j.Logger;
48 51
52 +import java.util.ArrayList;
49 import java.util.EnumSet; 53 import java.util.EnumSet;
50 import java.util.List; 54 import java.util.List;
51 import java.util.Map; 55 import java.util.Map;
52 import java.util.Set; 56 import java.util.Set;
53 import java.util.concurrent.ConcurrentHashMap; 57 import java.util.concurrent.ConcurrentHashMap;
58 +import java.util.concurrent.ExecutionException;
59 +import java.util.concurrent.Future;
54 60
61 +import static com.google.common.base.Preconditions.checkArgument;
55 import static com.google.common.base.Preconditions.checkState; 62 import static com.google.common.base.Preconditions.checkState;
56 import static org.onlab.onos.net.intent.IntentState.*; 63 import static org.onlab.onos.net.intent.IntentState.*;
57 import static org.slf4j.LoggerFactory.getLogger; 64 import static org.slf4j.LoggerFactory.getLogger;
...@@ -341,6 +348,207 @@ public class HazelcastIntentStore ...@@ -341,6 +348,207 @@ public class HazelcastIntentStore
341 } 348 }
342 } 349 }
343 350
351 + @Override
352 + public List<Operation> batchWrite(BatchWrite batch) {
353 + // Hazelcast version will never fail for conditional failure now.
354 + List<Operation> failed = new ArrayList<>();
355 +
356 + List<Pair<Operation, List<Future<?>>>> futures = new ArrayList<>(batch.operations().size());
357 +
358 + for (Operation op : batch.operations()) {
359 + switch (op.type()) {
360 + case CREATE_INTENT:
361 + checkArgument(op.args().size() == 1,
362 + "CREATE_INTENT takes 1 argument. %s", op);
363 + Intent intent = op.arg(0);
364 + futures.add(Pair.of(op,
365 + ImmutableList.of(intents.putAsync(intent.id(), intent),
366 + states.putAsync(intent.id(), SUBMITTED))));
367 + break;
368 +
369 + case REMOVE_INTENT:
370 + checkArgument(op.args().size() == 1,
371 + "REMOVE_INTENT takes 1 argument. %s", op);
372 + IntentId intentId = (IntentId) op.arg(0);
373 + futures.add(Pair.of(op,
374 + ImmutableList.of(intents.removeAsync(intentId),
375 + states.removeAsync(intentId),
376 + installable.removeAsync(intentId))));
377 + break;
378 +
379 + case SET_STATE:
380 + checkArgument(op.args().size() == 2,
381 + "SET_STATE takes 2 arguments. %s", op);
382 + intent = op.arg(0);
383 + IntentState newState = op.arg(1);
384 + futures.add(Pair.of(op,
385 + ImmutableList.of(states.putAsync(intent.id(), newState))));
386 + break;
387 +
388 + case SET_INSTALLABLE:
389 + checkArgument(op.args().size() == 2,
390 + "SET_INSTALLABLE takes 2 arguments. %s", op);
391 + intentId = op.arg(0);
392 + List<Intent> installableIntents = op.arg(1);
393 + futures.add(Pair.of(op,
394 + ImmutableList.of(installable.putAsync(intentId, installableIntents))));
395 + break;
396 +
397 + case REMOVE_INSTALLED:
398 + checkArgument(op.args().size() == 1,
399 + "REMOVE_INSTALLED takes 1 argument. %s", op);
400 + intentId = op.arg(0);
401 + futures.add(Pair.of(op,
402 + ImmutableList.of(installable.removeAsync(intentId))));
403 + break;
404 +
405 + default:
406 + log.warn("Unknown Operation encountered: {}", op);
407 + failed.add(op);
408 + break;
409 + }
410 + }
411 +
412 + // verify result
413 + for (Pair<Operation, List<Future<?>>> future : futures) {
414 + final Operation op = future.getLeft();
415 + final List<Future<?>> subops = future.getRight();
416 +
417 + switch (op.type()) {
418 +
419 + case CREATE_INTENT:
420 + {
421 + Intent intent = op.arg(0);
422 + IntentState newIntentState = SUBMITTED;
423 +
424 + try {
425 + Intent prevIntent = (Intent) subops.get(0).get();
426 + IntentState prevIntentState = (IntentState) subops.get(1).get();
427 +
428 + if (prevIntent != null || prevIntentState != null) {
429 + log.warn("Overwriting existing Intent: {}@{} with {}@{}",
430 + prevIntent, prevIntentState,
431 + intent, newIntentState);
432 + }
433 + } catch (InterruptedException e) {
434 + log.error("Batch write was interrupted while processing {}", op, e);
435 + failed.add(op);
436 + Thread.currentThread().interrupt();
437 + } catch (ExecutionException e) {
438 + log.error("Batch write failed processing {}", op, e);
439 + failed.add(op);
440 + }
441 + break;
442 + }
443 +
444 + case REMOVE_INTENT:
445 + {
446 + IntentId intentId = op.arg(0);
447 +
448 + try {
449 + Intent prevIntent = (Intent) subops.get(0).get();
450 + IntentState prevIntentState = (IntentState) subops.get(1).get();
451 + @SuppressWarnings("unchecked")
452 + List<Intent> prevInstallable = (List<Intent>) subops.get(2).get();
453 +
454 + if (prevIntent == null) {
455 + log.warn("Intent {} was already removed.", intentId);
456 + }
457 + if (prevIntentState == null) {
458 + log.warn("Intent {} state was already removed", intentId);
459 + }
460 + if (prevInstallable == null) {
461 + log.info("Intent {} installable was already removed", intentId);
462 + }
463 + } catch (InterruptedException e) {
464 + log.error("Batch write was interrupted while processing {}", op, e);
465 + failed.add(op);
466 + Thread.currentThread().interrupt();
467 + } catch (ExecutionException e) {
468 + log.error("Batch write failed processing {}", op, e);
469 + failed.add(op);
470 + }
471 + break;
472 + }
473 +
474 + case SET_STATE:
475 + {
476 + Intent intent = op.arg(0);
477 + IntentId intentId = intent.id();
478 + IntentState newState = op.arg(1);
479 +
480 + try {
481 + IntentState prevIntentState = (IntentState) subops.get(0).get();
482 + log.trace("{} - {} -> {}", intentId, prevIntentState, newState);
483 + // TODO sanity check and log?
484 + } catch (InterruptedException e) {
485 + log.error("Batch write was interrupted while processing {}", op, e);
486 + failed.add(op);
487 + Thread.currentThread().interrupt();
488 + } catch (ExecutionException e) {
489 + log.error("Batch write failed processing {}", op, e);
490 + failed.add(op);
491 + }
492 + break;
493 + }
494 +
495 + case SET_INSTALLABLE:
496 + {
497 + IntentId intentId = op.arg(0);
498 + List<Intent> installableIntents = op.arg(1);
499 +
500 + try {
501 + @SuppressWarnings("unchecked")
502 + List<Intent> prevInstallable = (List<Intent>) subops.get(0).get();
503 +
504 + if (prevInstallable != null) {
505 + log.warn("Overwriting Intent {} installable {} -> {}",
506 + intentId, prevInstallable, installableIntents);
507 + }
508 + } catch (InterruptedException e) {
509 + log.error("Batch write was interrupted while processing {}", op, e);
510 + failed.add(op);
511 + Thread.currentThread().interrupt();
512 + } catch (ExecutionException e) {
513 + log.error("Batch write failed processing {}", op, e);
514 + failed.add(op);
515 + }
516 + break;
517 + }
518 +
519 + case REMOVE_INSTALLED:
520 + {
521 + IntentId intentId = op.arg(0);
522 +
523 + try {
524 + @SuppressWarnings("unchecked")
525 + List<Intent> prevInstallable = (List<Intent>) subops.get(0).get();
526 +
527 + if (prevInstallable == null) {
528 + log.warn("Intent {} installable was already removed", intentId);
529 + }
530 + } catch (InterruptedException e) {
531 + log.error("Batch write was interrupted while processing {}", op, e);
532 + failed.add(op);
533 + Thread.currentThread().interrupt();
534 + } catch (ExecutionException e) {
535 + log.error("Batch write failed processing {}", op, e);
536 + failed.add(op);
537 + }
538 + break;
539 + }
540 +
541 + default:
542 + log.warn("Unknown Operation encountered: {}", op);
543 + if (!failed.contains(op)) {
544 + failed.add(op);
545 + }
546 + break;
547 + }
548 + }
549 + return failed;
550 + }
551 +
344 public final class RemoteIntentStateListener extends EntryAdapter<IntentId, IntentState> { 552 public final class RemoteIntentStateListener extends EntryAdapter<IntentId, IntentState> {
345 553
346 @Override 554 @Override
......