blob: ef6d08666cf656b7bccf019a173cec5f32334834 [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.store.intent.impl;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070017
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
alshabiba9819bf2014-11-30 18:15:52 -080026import com.google.common.collect.Lists;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070027import org.apache.felix.scr.annotations.Activate;
28import org.apache.felix.scr.annotations.Component;
29import org.apache.felix.scr.annotations.Deactivate;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080030import org.apache.felix.scr.annotations.Reference;
31import org.apache.felix.scr.annotations.ReferenceCardinality;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070032import org.apache.felix.scr.annotations.Service;
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080033import org.onlab.metrics.MetricsService;
Brian O'Connorabafb502014-12-02 22:26:20 -080034import org.onosproject.core.MetricsHelper;
Sho SHIMIZU64ae11c2014-12-03 15:17:47 -080035import org.onosproject.net.intent.BatchWrite;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.net.intent.Intent;
37import org.onosproject.net.intent.IntentEvent;
38import org.onosproject.net.intent.IntentId;
39import org.onosproject.net.intent.IntentState;
40import org.onosproject.net.intent.IntentStore;
41import org.onosproject.net.intent.IntentStoreDelegate;
Sho SHIMIZU64ae11c2014-12-03 15:17:47 -080042import org.onosproject.net.intent.BatchWrite.Operation;
Brian O'Connorabafb502014-12-02 22:26:20 -080043import org.onosproject.store.AbstractStore;
44import org.onosproject.store.serializers.KryoNamespaces;
45import org.onosproject.store.serializers.KryoSerializer;
46import org.onosproject.store.serializers.StoreSerializer;
47import org.onosproject.store.service.BatchWriteRequest;
48import org.onosproject.store.service.BatchWriteRequest.Builder;
49import org.onosproject.store.service.BatchWriteResult;
50import org.onosproject.store.service.DatabaseAdminService;
51import org.onosproject.store.service.DatabaseService;
52import org.onosproject.store.service.impl.CMap;
Yuta HIGUCHI2befc662014-10-30 15:57:49 -070053import org.onlab.util.KryoNamespace;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070054import org.slf4j.Logger;
55
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080056import java.util.ArrayList;
Sho SHIMIZU2bb988b2015-01-20 13:45:35 -080057import java.util.Collections;
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080058import java.util.EnumSet;
Yuta HIGUCHIf5682452014-12-01 10:17:15 -080059import java.util.HashSet;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070060import java.util.List;
61import java.util.Map;
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080062import java.util.Set;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070063import java.util.concurrent.ConcurrentHashMap;
64
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080065import static com.google.common.base.Preconditions.checkArgument;
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -080066import static com.google.common.base.Preconditions.checkState;
Brian O'Connorabafb502014-12-02 22:26:20 -080067import static org.onosproject.net.intent.IntentState.*;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070068import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080069import static org.onlab.metrics.MetricsUtil.*;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070070
Yuta HIGUCHI9b108b32014-12-01 11:10:26 -080071@Component(immediate = true, enabled = false)
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070072@Service
73public class DistributedIntentStore
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080074 extends AbstractStore<IntentEvent, IntentStoreDelegate>
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080075 implements IntentStore, MetricsHelper {
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070076
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080077 /** Valid parking state, which can transition to INSTALLED. */
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080078 private static final Set<IntentState> PRE_INSTALLED = EnumSet.of(INSTALL_REQ, INSTALLED, FAILED);
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080079
80 /** Valid parking state, which can transition to WITHDRAWN. */
81 private static final Set<IntentState> PRE_WITHDRAWN = EnumSet.of(INSTALLED, FAILED);
82
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080083 private static final Set<IntentState> PARKING = EnumSet.of(INSTALL_REQ, INSTALLED, WITHDRAWN, FAILED);
Yuta HIGUCHIf5682452014-12-01 10:17:15 -080084
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070085 private final Logger log = getLogger(getClass());
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070086
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070087 // Assumption: IntentId will not have synonyms
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080088 private static final String INTENTS_TABLE = "intents";
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080089 private CMap<IntentId, Intent> intents;
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080090
91 private static final String STATES_TABLE = "intent-states";
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080092 private CMap<IntentId, IntentState> states;
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070093
Yuta HIGUCHI65934892014-12-04 17:47:44 -080094 // TODO transient state issue remains for this impl.: ONOS-103
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070095 // Map to store instance local intermediate state transition
96 private transient Map<IntentId, IntentState> transientStates = new ConcurrentHashMap<>();
97
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080098 private static final String INSTALLABLE_TABLE = "installable-intents";
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080099 private CMap<IntentId, List<Intent>> installable;
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700100
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800101 private LoadingCache<IntentId, String> keyCache;
102
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800103 private StoreSerializer serializer;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected DatabaseAdminService dbAdminService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected DatabaseService dbService;
110
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected MetricsService metricsService;
113
Yuta HIGUCHIc8f30262014-11-20 19:14:42 -0800114 // TODO make this configurable
115 private boolean onlyLogTransitionError = true;
116
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800117 private Timer createIntentTimer;
118 private Timer removeIntentTimer;
119 private Timer setInstallableIntentsTimer;
120 private Timer getInstallableIntentsTimer;
121 private Timer removeInstalledIntentsTimer;
122 private Timer setStateTimer;
123 private Timer getIntentCountTimer;
124 private Timer getIntentsTimer;
125 private Timer getIntentTimer;
126 private Timer getIntentStateTimer;
127
128
129 private Timer createResponseTimer(String methodName) {
130 return createTimer("IntentStore", methodName, "responseTime");
131 }
132
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700133 @Activate
134 public void activate() {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800135 createIntentTimer = createResponseTimer("createIntent");
136 removeIntentTimer = createResponseTimer("removeIntent");
137 setInstallableIntentsTimer = createResponseTimer("setInstallableIntents");
138 getInstallableIntentsTimer = createResponseTimer("getInstallableIntents");
139 removeInstalledIntentsTimer = createResponseTimer("removeInstalledIntents");
140 setStateTimer = createResponseTimer("setState");
141 getIntentCountTimer = createResponseTimer("getIntentCount");
142 getIntentsTimer = createResponseTimer("getIntents");
143 getIntentTimer = createResponseTimer("getIntent");
144 getIntentStateTimer = createResponseTimer("getIntentState");
145
Yuta HIGUCHI65934892014-12-04 17:47:44 -0800146 // We need a way to add serializer for intents which has been plugged-in.
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700147 // As a short term workaround, relax Kryo config to
148 // registrationRequired=false
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800149 serializer = new KryoSerializer() {
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700150
151 @Override
152 protected void setupKryoPool() {
153 serializerPool = KryoNamespace.newBuilder()
154 .setRegistrationRequired(false)
155 .register(KryoNamespaces.API)
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800156 .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
157 .build();
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700158 }
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700159 };
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700160
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800161 keyCache = CacheBuilder.newBuilder()
162 .softValues()
163 .build(new CacheLoader<IntentId, String>() {
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700164
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800165 @Override
166 public String load(IntentId key) {
167 return key.toString();
168 }
169 });
170
171 intents = new IntentIdMap<>(dbAdminService, dbService, INTENTS_TABLE, serializer);
172
173 states = new IntentIdMap<>(dbAdminService, dbService, STATES_TABLE, serializer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700174
175 transientStates.clear();
176
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800177 installable = new IntentIdMap<>(dbAdminService, dbService, INSTALLABLE_TABLE, serializer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700178
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700179 log.info("Started");
180 }
181
182 @Deactivate
183 public void deactivate() {
184 log.info("Stopped");
185 }
186
187 @Override
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800188 public MetricsService metricsService() {
189 return metricsService;
190 }
191
192 @Override
alshabiba9819bf2014-11-30 18:15:52 -0800193 public void createIntent(Intent intent) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800194 Context timer = startTimer(createIntentTimer);
195 try {
196 boolean absent = intents.putIfAbsent(intent.id(), intent);
197 if (!absent) {
198 // duplicate, ignore
alshabiba9819bf2014-11-30 18:15:52 -0800199 return;
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800200 } else {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800201 this.setState(intent, IntentState.INSTALL_REQ);
alshabiba9819bf2014-11-30 18:15:52 -0800202 return;
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800203 }
204 } finally {
205 stopTimer(timer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700206 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700207 }
208
209 @Override
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800210 public void removeIntent(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800211 Context timer = startTimer(removeIntentTimer);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800212 checkState(getIntentState(intentId) == WITHDRAWN,
213 "Intent state for {} is not WITHDRAWN.", intentId);
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800214 try {
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800215 intents.remove(intentId);
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800216 states.remove(intentId);
217 transientStates.remove(intentId);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800218 installable.remove(intentId);
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800219 } finally {
220 stopTimer(timer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700221 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700222 }
223
224 @Override
225 public long getIntentCount() {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800226 Context timer = startTimer(getIntentCountTimer);
227 try {
228 return intents.size();
229 } finally {
230 stopTimer(timer);
231 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700232 }
233
234 @Override
235 public Iterable<Intent> getIntents() {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800236 Context timer = startTimer(getIntentsTimer);
237 try {
238 return ImmutableSet.copyOf(intents.values());
239 } finally {
240 stopTimer(timer);
241 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700242 }
243
244 @Override
245 public Intent getIntent(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800246 Context timer = startTimer(getIntentTimer);
247 try {
248 return intents.get(intentId);
249 } finally {
250 stopTimer(timer);
251 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700252 }
253
254 @Override
255 public IntentState getIntentState(IntentId id) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800256 Context timer = startTimer(getIntentStateTimer);
257 try {
258 final IntentState localState = transientStates.get(id);
259 if (localState != null) {
260 return localState;
261 }
262 return states.get(id);
263 } finally {
264 stopTimer(timer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700265 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700266 }
267
Yuta HIGUCHIc8f30262014-11-20 19:14:42 -0800268 private void verify(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) {
269 if (onlyLogTransitionError) {
270 if (!expression) {
271 log.error(errorMessageTemplate.replace("%s", "{}"), errorMessageArgs);
272 }
273 } else {
274 Verify.verify(expression, errorMessageTemplate, errorMessageArgs);
275 }
276 }
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -0800277
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700278 @Override
alshabiba9819bf2014-11-30 18:15:52 -0800279 public void setState(Intent intent, IntentState state) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800280 Context timer = startTimer(setStateTimer);
281 try {
282 final IntentId id = intent.id();
283 IntentEvent.Type evtType = null;
284 final IntentState prevParking;
285 boolean transitionedToParking = true;
286 boolean updated;
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700287
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800288 // parking state transition
289 switch (state) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800290 case INSTALL_REQ:
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800291 prevParking = states.get(id);
292 if (prevParking == null) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800293 updated = states.putIfAbsent(id, INSTALL_REQ);
294 verify(updated, "Conditional replace %s => %s failed", prevParking, INSTALL_REQ);
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800295 } else {
296 verify(prevParking == WITHDRAWN,
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800297 "Illegal state transition attempted from %s to INSTALL_REQ",
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800298 prevParking);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800299 updated = states.replace(id, prevParking, INSTALL_REQ);
300 verify(updated, "Conditional replace %s => %s failed", prevParking, INSTALL_REQ);
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800301 }
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800302 evtType = IntentEvent.Type.INSTALL_REQ;
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800303 break;
304
305 case INSTALLED:
306 prevParking = states.get(id);
307 verify(PRE_INSTALLED.contains(prevParking),
308 "Illegal state transition attempted from %s to INSTALLED",
309 prevParking);
310 updated = states.replace(id, prevParking, INSTALLED);
311 verify(updated, "Conditional replace %s => %s failed", prevParking, INSTALLED);
312 evtType = IntentEvent.Type.INSTALLED;
313 break;
314
315 case FAILED:
316 prevParking = states.get(id);
317 updated = states.replace(id, prevParking, FAILED);
318 verify(updated, "Conditional replace %s => %s failed", prevParking, FAILED);
319 evtType = IntentEvent.Type.FAILED;
320 break;
321
322 case WITHDRAWN:
323 prevParking = states.get(id);
324 verify(PRE_WITHDRAWN.contains(prevParking),
325 "Illegal state transition attempted from %s to WITHDRAWN",
326 prevParking);
327 updated = states.replace(id, prevParking, WITHDRAWN);
328 verify(updated, "Conditional replace %s => %s failed", prevParking, WITHDRAWN);
329 evtType = IntentEvent.Type.WITHDRAWN;
330 break;
331
332 default:
333 transitionedToParking = false;
334 prevParking = null;
335 break;
Yuta HIGUCHI89a7f472014-11-21 14:50:24 -0800336 }
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800337 if (transitionedToParking) {
338 log.debug("Parking State change: {} {}=>{}", id, prevParking, state);
339 // remove instance local state
340 transientStates.remove(id);
341 } else {
342 // Update instance local state, which includes non-parking state transition
343 final IntentState prevTransient = transientStates.put(id, state);
344 log.debug("Transient State change: {} {}=>{}", id, prevTransient, state);
345 }
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800346
alshabiba9819bf2014-11-30 18:15:52 -0800347 if (evtType != null) {
348 notifyDelegate(new IntentEvent(evtType, intent));
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800349 }
alshabiba9819bf2014-11-30 18:15:52 -0800350 return;
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800351 } finally {
352 stopTimer(timer);
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700353 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700354 }
355
356 @Override
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700357 public void setInstallableIntents(IntentId intentId, List<Intent> result) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800358 Context timer = startTimer(setInstallableIntentsTimer);
359 try {
360 installable.put(intentId, result);
361 } finally {
362 stopTimer(timer);
363 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700364 }
365
366 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700367 public List<Intent> getInstallableIntents(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800368 Context timer = startTimer(getInstallableIntentsTimer);
369 try {
370 return installable.get(intentId);
371 } finally {
372 stopTimer(timer);
373 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700374 }
375
376 @Override
377 public void removeInstalledIntents(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800378 Context timer = startTimer(removeInstalledIntentsTimer);
379 try {
380 installable.remove(intentId);
381 } finally {
382 stopTimer(timer);
383 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700384 }
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800385
386 protected String strIntentId(IntentId key) {
387 return keyCache.getUnchecked(key);
388 }
389
390 /**
391 * Distributed Map from IntentId to some value.
392 *
393 * @param <V> Map value type
394 */
395 final class IntentIdMap<V> extends CMap<IntentId, V> {
396
397 /**
398 * Creates a IntentIdMap instance.
399 *
400 * @param dbAdminService DatabaseAdminService to use for this instance
401 * @param dbService DatabaseService to use for this instance
402 * @param tableName table which this Map corresponds to
403 * @param serializer Value serializer
404 */
405 public IntentIdMap(DatabaseAdminService dbAdminService,
406 DatabaseService dbService,
407 String tableName,
408 StoreSerializer serializer) {
409 super(dbAdminService, dbService, tableName, serializer);
410 }
411
412 @Override
413 protected String sK(IntentId key) {
414 return strIntentId(key);
415 }
416 }
417
418 @Override
419 public List<Operation> batchWrite(BatchWrite batch) {
Sho SHIMIZU2bb988b2015-01-20 13:45:35 -0800420 if (batch.isEmpty()) {
421 return Collections.emptyList();
422 }
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800423
424 List<Operation> failed = new ArrayList<>();
425 final Builder builder = BatchWriteRequest.newBuilder();
alshabiba9819bf2014-11-30 18:15:52 -0800426 List<IntentEvent> events = Lists.newArrayList();
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800427
Yuta HIGUCHIf5682452014-12-01 10:17:15 -0800428 final Set<IntentId> transitionedToParking = new HashSet<>();
429
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800430 for (Operation op : batch.operations()) {
431 switch (op.type()) {
432 case CREATE_INTENT:
433 checkArgument(op.args().size() == 1,
434 "CREATE_INTENT takes 1 argument. %s", op);
435 Intent intent = op.arg(0);
436 builder.putIfAbsent(INTENTS_TABLE, strIntentId(intent.id()), serializer.encode(intent));
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800437 builder.putIfAbsent(STATES_TABLE, strIntentId(intent.id()), serializer.encode(INSTALL_REQ));
438 events.add(IntentEvent.getEvent(INSTALL_REQ, intent));
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800439 break;
440
441 case REMOVE_INTENT:
442 checkArgument(op.args().size() == 1,
443 "REMOVE_INTENT takes 1 argument. %s", op);
444 IntentId intentId = (IntentId) op.arg(0);
445 builder.remove(INTENTS_TABLE, strIntentId(intentId));
446 builder.remove(STATES_TABLE, strIntentId(intentId));
447 builder.remove(INSTALLABLE_TABLE, strIntentId(intentId));
448 break;
449
450 case SET_STATE:
451 checkArgument(op.args().size() == 2,
452 "SET_STATE takes 2 arguments. %s", op);
453 intent = op.arg(0);
454 IntentState newState = op.arg(1);
455 builder.put(STATES_TABLE, strIntentId(intent.id()), serializer.encode(newState));
Yuta HIGUCHIf5682452014-12-01 10:17:15 -0800456 if (PARKING.contains(newState)) {
457 transitionedToParking.add(intent.id());
Yuta HIGUCHI5cd352d2014-12-01 20:16:02 -0800458 events.add(IntentEvent.getEvent(newState, intent));
Yuta HIGUCHIf5682452014-12-01 10:17:15 -0800459 } else {
460 transitionedToParking.remove(intent.id());
461 }
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800462 break;
463
464 case SET_INSTALLABLE:
465 checkArgument(op.args().size() == 2,
466 "SET_INSTALLABLE takes 2 arguments. %s", op);
467 intentId = op.arg(0);
468 List<Intent> installableIntents = op.arg(1);
469 builder.put(INSTALLABLE_TABLE, strIntentId(intentId), serializer.encode(installableIntents));
470 break;
471
472 case REMOVE_INSTALLED:
473 checkArgument(op.args().size() == 1,
474 "REMOVE_INSTALLED takes 1 argument. %s", op);
475 intentId = op.arg(0);
476 builder.remove(INSTALLABLE_TABLE, strIntentId(intentId));
477 break;
478
479 default:
480 log.warn("Unknown Operation encountered: {}", op);
481 failed.add(op);
482 break;
483 }
484 }
485
486 BatchWriteResult batchWriteResult = dbService.batchWrite(builder.build());
487 if (batchWriteResult.isSuccessful()) {
488 // no-failure (except for invalid input)
Yuta HIGUCHIf5682452014-12-01 10:17:15 -0800489 transitionedToParking.forEach((intentId) -> transientStates.remove(intentId));
alshabiba9819bf2014-11-30 18:15:52 -0800490 notifyDelegate(events);
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800491 return failed;
492 } else {
493 // everything failed
494 return batch.operations();
495 }
496 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700497}