blob: 3a3919324fa2a0bfdda2e375dc4f803ae4e779a4 [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 */
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070016package org.onlab.onos.store.intent.impl;
17
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080018import com.codahale.metrics.Timer;
19import com.codahale.metrics.Timer.Context;
Yuta HIGUCHIc8f30262014-11-20 19:14:42 -080020import com.google.common.base.Verify;
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080021import com.google.common.cache.CacheBuilder;
22import com.google.common.cache.CacheLoader;
23import com.google.common.cache.LoadingCache;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070024import com.google.common.collect.ImmutableSet;
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070025
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070026import org.apache.felix.scr.annotations.Activate;
27import org.apache.felix.scr.annotations.Component;
28import org.apache.felix.scr.annotations.Deactivate;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080029import org.apache.felix.scr.annotations.Reference;
30import org.apache.felix.scr.annotations.ReferenceCardinality;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070031import org.apache.felix.scr.annotations.Service;
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080032import org.onlab.metrics.MetricsService;
33import org.onlab.onos.core.MetricsHelper;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070034import org.onlab.onos.net.intent.Intent;
35import org.onlab.onos.net.intent.IntentEvent;
36import org.onlab.onos.net.intent.IntentId;
37import org.onlab.onos.net.intent.IntentState;
38import org.onlab.onos.net.intent.IntentStore;
39import org.onlab.onos.net.intent.IntentStoreDelegate;
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080040import org.onlab.onos.net.intent.IntentStore.BatchWrite.Operation;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080041import org.onlab.onos.store.AbstractStore;
Yuta HIGUCHI2befc662014-10-30 15:57:49 -070042import org.onlab.onos.store.serializers.KryoNamespaces;
43import org.onlab.onos.store.serializers.KryoSerializer;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080044import org.onlab.onos.store.serializers.StoreSerializer;
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080045import org.onlab.onos.store.service.BatchWriteRequest;
46import org.onlab.onos.store.service.BatchWriteRequest.Builder;
47import org.onlab.onos.store.service.BatchWriteResult;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080048import org.onlab.onos.store.service.DatabaseAdminService;
49import org.onlab.onos.store.service.DatabaseService;
50import org.onlab.onos.store.service.impl.CMap;
Yuta HIGUCHI2befc662014-10-30 15:57:49 -070051import org.onlab.util.KryoNamespace;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070052import org.slf4j.Logger;
53
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080054import java.util.ArrayList;
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080055import java.util.EnumSet;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070056import java.util.List;
57import java.util.Map;
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080058import java.util.Set;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070059import java.util.concurrent.ConcurrentHashMap;
60
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080061import static com.google.common.base.Preconditions.checkArgument;
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -080062import static com.google.common.base.Preconditions.checkState;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070063import static org.onlab.onos.net.intent.IntentState.*;
64import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080065import static org.onlab.metrics.MetricsUtil.*;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070066
Yuta HIGUCHIc8f30262014-11-20 19:14:42 -080067@Component(immediate = true, enabled = true)
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070068@Service
69public class DistributedIntentStore
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080070 extends AbstractStore<IntentEvent, IntentStoreDelegate>
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080071 implements IntentStore, MetricsHelper {
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070072
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080073 /** Valid parking state, which can transition to INSTALLED. */
Yuta HIGUCHI89a7f472014-11-21 14:50:24 -080074 private static final Set<IntentState> PRE_INSTALLED = EnumSet.of(SUBMITTED, INSTALLED, FAILED);
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080075
76 /** Valid parking state, which can transition to WITHDRAWN. */
77 private static final Set<IntentState> PRE_WITHDRAWN = EnumSet.of(INSTALLED, FAILED);
78
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070079 private final Logger log = getLogger(getClass());
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070080
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070081 // Assumption: IntentId will not have synonyms
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080082 private static final String INTENTS_TABLE = "intents";
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080083 private CMap<IntentId, Intent> intents;
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080084
85 private static final String STATES_TABLE = "intent-states";
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080086 private CMap<IntentId, IntentState> states;
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070087
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080088 // TODO left behind transient state issue: ONOS-103
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070089 // Map to store instance local intermediate state transition
90 private transient Map<IntentId, IntentState> transientStates = new ConcurrentHashMap<>();
91
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080092 private static final String INSTALLABLE_TABLE = "installable-intents";
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080093 private CMap<IntentId, List<Intent>> installable;
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070094
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080095 private LoadingCache<IntentId, String> keyCache;
96
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080097 private StoreSerializer serializer;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected DatabaseAdminService dbAdminService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected DatabaseService dbService;
104
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected MetricsService metricsService;
107
Yuta HIGUCHIc8f30262014-11-20 19:14:42 -0800108 // TODO make this configurable
109 private boolean onlyLogTransitionError = true;
110
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800111 private Timer createIntentTimer;
112 private Timer removeIntentTimer;
113 private Timer setInstallableIntentsTimer;
114 private Timer getInstallableIntentsTimer;
115 private Timer removeInstalledIntentsTimer;
116 private Timer setStateTimer;
117 private Timer getIntentCountTimer;
118 private Timer getIntentsTimer;
119 private Timer getIntentTimer;
120 private Timer getIntentStateTimer;
121
122
123 private Timer createResponseTimer(String methodName) {
124 return createTimer("IntentStore", methodName, "responseTime");
125 }
126
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700127 @Activate
128 public void activate() {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800129 createIntentTimer = createResponseTimer("createIntent");
130 removeIntentTimer = createResponseTimer("removeIntent");
131 setInstallableIntentsTimer = createResponseTimer("setInstallableIntents");
132 getInstallableIntentsTimer = createResponseTimer("getInstallableIntents");
133 removeInstalledIntentsTimer = createResponseTimer("removeInstalledIntents");
134 setStateTimer = createResponseTimer("setState");
135 getIntentCountTimer = createResponseTimer("getIntentCount");
136 getIntentsTimer = createResponseTimer("getIntents");
137 getIntentTimer = createResponseTimer("getIntent");
138 getIntentStateTimer = createResponseTimer("getIntentState");
139
Yuta HIGUCHI71fa4932014-10-28 22:27:49 -0700140 // FIXME: We need a way to add serializer for intents which has been plugged-in.
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700141 // As a short term workaround, relax Kryo config to
142 // registrationRequired=false
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800143 serializer = new KryoSerializer() {
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700144
145 @Override
146 protected void setupKryoPool() {
147 serializerPool = KryoNamespace.newBuilder()
148 .setRegistrationRequired(false)
149 .register(KryoNamespaces.API)
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800150 .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
151 .build();
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700152 }
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700153 };
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700154
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800155 keyCache = CacheBuilder.newBuilder()
156 .softValues()
157 .build(new CacheLoader<IntentId, String>() {
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700158
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800159 @Override
160 public String load(IntentId key) {
161 return key.toString();
162 }
163 });
164
165 intents = new IntentIdMap<>(dbAdminService, dbService, INTENTS_TABLE, serializer);
166
167 states = new IntentIdMap<>(dbAdminService, dbService, STATES_TABLE, serializer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700168
169 transientStates.clear();
170
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800171 installable = new IntentIdMap<>(dbAdminService, dbService, INSTALLABLE_TABLE, serializer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700172
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700173 log.info("Started");
174 }
175
176 @Deactivate
177 public void deactivate() {
178 log.info("Stopped");
179 }
180
181 @Override
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800182 public MetricsService metricsService() {
183 return metricsService;
184 }
185
186 @Override
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700187 public IntentEvent createIntent(Intent intent) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800188 Context timer = startTimer(createIntentTimer);
189 try {
190 boolean absent = intents.putIfAbsent(intent.id(), intent);
191 if (!absent) {
192 // duplicate, ignore
193 return null;
194 } else {
195 return this.setState(intent, IntentState.SUBMITTED);
196 }
197 } finally {
198 stopTimer(timer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700199 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700200 }
201
202 @Override
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800203 public void removeIntent(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800204 Context timer = startTimer(removeIntentTimer);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800205 checkState(getIntentState(intentId) == WITHDRAWN,
206 "Intent state for {} is not WITHDRAWN.", intentId);
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800207 try {
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800208 intents.remove(intentId);
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800209 states.remove(intentId);
210 transientStates.remove(intentId);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800211 installable.remove(intentId);
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800212 } finally {
213 stopTimer(timer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700214 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700215 }
216
217 @Override
218 public long getIntentCount() {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800219 Context timer = startTimer(getIntentCountTimer);
220 try {
221 return intents.size();
222 } finally {
223 stopTimer(timer);
224 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700225 }
226
227 @Override
228 public Iterable<Intent> getIntents() {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800229 Context timer = startTimer(getIntentsTimer);
230 try {
231 return ImmutableSet.copyOf(intents.values());
232 } finally {
233 stopTimer(timer);
234 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700235 }
236
237 @Override
238 public Intent getIntent(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800239 Context timer = startTimer(getIntentTimer);
240 try {
241 return intents.get(intentId);
242 } finally {
243 stopTimer(timer);
244 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700245 }
246
247 @Override
248 public IntentState getIntentState(IntentId id) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800249 Context timer = startTimer(getIntentStateTimer);
250 try {
251 final IntentState localState = transientStates.get(id);
252 if (localState != null) {
253 return localState;
254 }
255 return states.get(id);
256 } finally {
257 stopTimer(timer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700258 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700259 }
260
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800261 // FIXME temporary workaround until we fix our state machine
Yuta HIGUCHIc8f30262014-11-20 19:14:42 -0800262 private void verify(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) {
263 if (onlyLogTransitionError) {
264 if (!expression) {
265 log.error(errorMessageTemplate.replace("%s", "{}"), errorMessageArgs);
266 }
267 } else {
268 Verify.verify(expression, errorMessageTemplate, errorMessageArgs);
269 }
270 }
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -0800271
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700272 @Override
273 public IntentEvent setState(Intent intent, IntentState state) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800274 Context timer = startTimer(setStateTimer);
275 try {
276 final IntentId id = intent.id();
277 IntentEvent.Type evtType = null;
278 final IntentState prevParking;
279 boolean transitionedToParking = true;
280 boolean updated;
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700281
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800282 // parking state transition
283 switch (state) {
284 case SUBMITTED:
285 prevParking = states.get(id);
286 if (prevParking == null) {
287 updated = states.putIfAbsent(id, SUBMITTED);
288 verify(updated, "Conditional replace %s => %s failed", prevParking, SUBMITTED);
289 } else {
290 verify(prevParking == WITHDRAWN,
291 "Illegal state transition attempted from %s to SUBMITTED",
292 prevParking);
293 updated = states.replace(id, prevParking, SUBMITTED);
294 verify(updated, "Conditional replace %s => %s failed", prevParking, SUBMITTED);
295 }
296 evtType = IntentEvent.Type.SUBMITTED;
297 break;
298
299 case INSTALLED:
300 prevParking = states.get(id);
301 verify(PRE_INSTALLED.contains(prevParking),
302 "Illegal state transition attempted from %s to INSTALLED",
303 prevParking);
304 updated = states.replace(id, prevParking, INSTALLED);
305 verify(updated, "Conditional replace %s => %s failed", prevParking, INSTALLED);
306 evtType = IntentEvent.Type.INSTALLED;
307 break;
308
309 case FAILED:
310 prevParking = states.get(id);
311 updated = states.replace(id, prevParking, FAILED);
312 verify(updated, "Conditional replace %s => %s failed", prevParking, FAILED);
313 evtType = IntentEvent.Type.FAILED;
314 break;
315
316 case WITHDRAWN:
317 prevParking = states.get(id);
318 verify(PRE_WITHDRAWN.contains(prevParking),
319 "Illegal state transition attempted from %s to WITHDRAWN",
320 prevParking);
321 updated = states.replace(id, prevParking, WITHDRAWN);
322 verify(updated, "Conditional replace %s => %s failed", prevParking, WITHDRAWN);
323 evtType = IntentEvent.Type.WITHDRAWN;
324 break;
325
326 default:
327 transitionedToParking = false;
328 prevParking = null;
329 break;
Yuta HIGUCHI89a7f472014-11-21 14:50:24 -0800330 }
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800331 if (transitionedToParking) {
332 log.debug("Parking State change: {} {}=>{}", id, prevParking, state);
333 // remove instance local state
334 transientStates.remove(id);
335 } else {
336 // Update instance local state, which includes non-parking state transition
337 final IntentState prevTransient = transientStates.put(id, state);
338 log.debug("Transient State change: {} {}=>{}", id, prevTransient, state);
339 }
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800340
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800341 if (evtType == null) {
342 return null;
343 }
344 return new IntentEvent(evtType, intent);
345 } finally {
346 stopTimer(timer);
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700347 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700348 }
349
350 @Override
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700351 public void setInstallableIntents(IntentId intentId, List<Intent> result) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800352 Context timer = startTimer(setInstallableIntentsTimer);
353 try {
354 installable.put(intentId, result);
355 } finally {
356 stopTimer(timer);
357 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700358 }
359
360 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700361 public List<Intent> getInstallableIntents(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800362 Context timer = startTimer(getInstallableIntentsTimer);
363 try {
364 return installable.get(intentId);
365 } finally {
366 stopTimer(timer);
367 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700368 }
369
370 @Override
371 public void removeInstalledIntents(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800372 Context timer = startTimer(removeInstalledIntentsTimer);
373 try {
374 installable.remove(intentId);
375 } finally {
376 stopTimer(timer);
377 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700378 }
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800379
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 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700476}