blob: da68bcf0b0f16deb006f65416e730e3a562c1f1f [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
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;
34import org.onlab.onos.core.MetricsHelper;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070035import org.onlab.onos.net.intent.Intent;
36import org.onlab.onos.net.intent.IntentEvent;
37import org.onlab.onos.net.intent.IntentId;
38import org.onlab.onos.net.intent.IntentState;
39import org.onlab.onos.net.intent.IntentStore;
40import org.onlab.onos.net.intent.IntentStoreDelegate;
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080041import org.onlab.onos.net.intent.IntentStore.BatchWrite.Operation;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080042import org.onlab.onos.store.AbstractStore;
Yuta HIGUCHI2befc662014-10-30 15:57:49 -070043import org.onlab.onos.store.serializers.KryoNamespaces;
44import org.onlab.onos.store.serializers.KryoSerializer;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080045import org.onlab.onos.store.serializers.StoreSerializer;
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080046import org.onlab.onos.store.service.BatchWriteRequest;
47import org.onlab.onos.store.service.BatchWriteRequest.Builder;
48import org.onlab.onos.store.service.BatchWriteResult;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080049import org.onlab.onos.store.service.DatabaseAdminService;
50import org.onlab.onos.store.service.DatabaseService;
51import org.onlab.onos.store.service.impl.CMap;
Yuta HIGUCHI2befc662014-10-30 15:57:49 -070052import org.onlab.util.KryoNamespace;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070053import org.slf4j.Logger;
54
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080055import java.util.ArrayList;
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080056import java.util.EnumSet;
Yuta HIGUCHIf5682452014-12-01 10:17:15 -080057import java.util.HashSet;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070058import java.util.List;
59import java.util.Map;
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080060import java.util.Set;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070061import java.util.concurrent.ConcurrentHashMap;
62
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080063import static com.google.common.base.Preconditions.checkArgument;
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -080064import static com.google.common.base.Preconditions.checkState;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070065import static org.onlab.onos.net.intent.IntentState.*;
66import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080067import static org.onlab.metrics.MetricsUtil.*;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070068
Yuta HIGUCHI9b108b32014-12-01 11:10:26 -080069@Component(immediate = true, enabled = false)
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070070@Service
71public class DistributedIntentStore
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080072 extends AbstractStore<IntentEvent, IntentStoreDelegate>
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080073 implements IntentStore, MetricsHelper {
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070074
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080075 /** Valid parking state, which can transition to INSTALLED. */
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080076 private static final Set<IntentState> PRE_INSTALLED = EnumSet.of(INSTALL_REQ, INSTALLED, FAILED);
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080077
78 /** Valid parking state, which can transition to WITHDRAWN. */
79 private static final Set<IntentState> PRE_WITHDRAWN = EnumSet.of(INSTALLED, FAILED);
80
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080081 private static final Set<IntentState> PARKING = EnumSet.of(INSTALL_REQ, INSTALLED, WITHDRAWN, FAILED);
Yuta HIGUCHIf5682452014-12-01 10:17:15 -080082
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070083 private final Logger log = getLogger(getClass());
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070084
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070085 // Assumption: IntentId will not have synonyms
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080086 private static final String INTENTS_TABLE = "intents";
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080087 private CMap<IntentId, Intent> intents;
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080088
89 private static final String STATES_TABLE = "intent-states";
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080090 private CMap<IntentId, IntentState> states;
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070091
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080092 // TODO left behind transient state issue: ONOS-103
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070093 // Map to store instance local intermediate state transition
94 private transient Map<IntentId, IntentState> transientStates = new ConcurrentHashMap<>();
95
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080096 private static final String INSTALLABLE_TABLE = "installable-intents";
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080097 private CMap<IntentId, List<Intent>> installable;
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070098
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080099 private LoadingCache<IntentId, String> keyCache;
100
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800101 private StoreSerializer serializer;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected DatabaseAdminService dbAdminService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected DatabaseService dbService;
108
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected MetricsService metricsService;
111
Yuta HIGUCHIc8f30262014-11-20 19:14:42 -0800112 // TODO make this configurable
113 private boolean onlyLogTransitionError = true;
114
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800115 private Timer createIntentTimer;
116 private Timer removeIntentTimer;
117 private Timer setInstallableIntentsTimer;
118 private Timer getInstallableIntentsTimer;
119 private Timer removeInstalledIntentsTimer;
120 private Timer setStateTimer;
121 private Timer getIntentCountTimer;
122 private Timer getIntentsTimer;
123 private Timer getIntentTimer;
124 private Timer getIntentStateTimer;
125
126
127 private Timer createResponseTimer(String methodName) {
128 return createTimer("IntentStore", methodName, "responseTime");
129 }
130
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700131 @Activate
132 public void activate() {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800133 createIntentTimer = createResponseTimer("createIntent");
134 removeIntentTimer = createResponseTimer("removeIntent");
135 setInstallableIntentsTimer = createResponseTimer("setInstallableIntents");
136 getInstallableIntentsTimer = createResponseTimer("getInstallableIntents");
137 removeInstalledIntentsTimer = createResponseTimer("removeInstalledIntents");
138 setStateTimer = createResponseTimer("setState");
139 getIntentCountTimer = createResponseTimer("getIntentCount");
140 getIntentsTimer = createResponseTimer("getIntents");
141 getIntentTimer = createResponseTimer("getIntent");
142 getIntentStateTimer = createResponseTimer("getIntentState");
143
Yuta HIGUCHI71fa4932014-10-28 22:27:49 -0700144 // FIXME: We need a way to add serializer for intents which has been plugged-in.
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700145 // As a short term workaround, relax Kryo config to
146 // registrationRequired=false
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800147 serializer = new KryoSerializer() {
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700148
149 @Override
150 protected void setupKryoPool() {
151 serializerPool = KryoNamespace.newBuilder()
152 .setRegistrationRequired(false)
153 .register(KryoNamespaces.API)
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800154 .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
155 .build();
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700156 }
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700157 };
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700158
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800159 keyCache = CacheBuilder.newBuilder()
160 .softValues()
161 .build(new CacheLoader<IntentId, String>() {
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700162
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800163 @Override
164 public String load(IntentId key) {
165 return key.toString();
166 }
167 });
168
169 intents = new IntentIdMap<>(dbAdminService, dbService, INTENTS_TABLE, serializer);
170
171 states = new IntentIdMap<>(dbAdminService, dbService, STATES_TABLE, serializer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700172
173 transientStates.clear();
174
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800175 installable = new IntentIdMap<>(dbAdminService, dbService, INSTALLABLE_TABLE, serializer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700176
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700177 log.info("Started");
178 }
179
180 @Deactivate
181 public void deactivate() {
182 log.info("Stopped");
183 }
184
185 @Override
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800186 public MetricsService metricsService() {
187 return metricsService;
188 }
189
190 @Override
alshabiba9819bf2014-11-30 18:15:52 -0800191 public void createIntent(Intent intent) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800192 Context timer = startTimer(createIntentTimer);
193 try {
194 boolean absent = intents.putIfAbsent(intent.id(), intent);
195 if (!absent) {
196 // duplicate, ignore
alshabiba9819bf2014-11-30 18:15:52 -0800197 return;
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800198 } else {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800199 this.setState(intent, IntentState.INSTALL_REQ);
alshabiba9819bf2014-11-30 18:15:52 -0800200 return;
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800201 }
202 } finally {
203 stopTimer(timer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700204 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700205 }
206
207 @Override
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800208 public void removeIntent(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800209 Context timer = startTimer(removeIntentTimer);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800210 checkState(getIntentState(intentId) == WITHDRAWN,
211 "Intent state for {} is not WITHDRAWN.", intentId);
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800212 try {
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800213 intents.remove(intentId);
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800214 states.remove(intentId);
215 transientStates.remove(intentId);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800216 installable.remove(intentId);
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800217 } finally {
218 stopTimer(timer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700219 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700220 }
221
222 @Override
223 public long getIntentCount() {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800224 Context timer = startTimer(getIntentCountTimer);
225 try {
226 return intents.size();
227 } finally {
228 stopTimer(timer);
229 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700230 }
231
232 @Override
233 public Iterable<Intent> getIntents() {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800234 Context timer = startTimer(getIntentsTimer);
235 try {
236 return ImmutableSet.copyOf(intents.values());
237 } finally {
238 stopTimer(timer);
239 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700240 }
241
242 @Override
243 public Intent getIntent(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800244 Context timer = startTimer(getIntentTimer);
245 try {
246 return intents.get(intentId);
247 } finally {
248 stopTimer(timer);
249 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700250 }
251
252 @Override
253 public IntentState getIntentState(IntentId id) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800254 Context timer = startTimer(getIntentStateTimer);
255 try {
256 final IntentState localState = transientStates.get(id);
257 if (localState != null) {
258 return localState;
259 }
260 return states.get(id);
261 } finally {
262 stopTimer(timer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700263 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700264 }
265
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800266 // FIXME temporary workaround until we fix our state machine
Yuta HIGUCHIc8f30262014-11-20 19:14:42 -0800267 private void verify(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) {
268 if (onlyLogTransitionError) {
269 if (!expression) {
270 log.error(errorMessageTemplate.replace("%s", "{}"), errorMessageArgs);
271 }
272 } else {
273 Verify.verify(expression, errorMessageTemplate, errorMessageArgs);
274 }
275 }
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -0800276
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700277 @Override
alshabiba9819bf2014-11-30 18:15:52 -0800278 public void setState(Intent intent, IntentState state) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800279 Context timer = startTimer(setStateTimer);
280 try {
281 final IntentId id = intent.id();
282 IntentEvent.Type evtType = null;
283 final IntentState prevParking;
284 boolean transitionedToParking = true;
285 boolean updated;
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700286
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800287 // parking state transition
288 switch (state) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800289 case INSTALL_REQ:
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800290 prevParking = states.get(id);
291 if (prevParking == null) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800292 updated = states.putIfAbsent(id, INSTALL_REQ);
293 verify(updated, "Conditional replace %s => %s failed", prevParking, INSTALL_REQ);
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800294 } else {
295 verify(prevParking == WITHDRAWN,
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800296 "Illegal state transition attempted from %s to INSTALL_REQ",
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800297 prevParking);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800298 updated = states.replace(id, prevParking, INSTALL_REQ);
299 verify(updated, "Conditional replace %s => %s failed", prevParking, INSTALL_REQ);
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800300 }
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800301 evtType = IntentEvent.Type.INSTALL_REQ;
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800302 break;
303
304 case INSTALLED:
305 prevParking = states.get(id);
306 verify(PRE_INSTALLED.contains(prevParking),
307 "Illegal state transition attempted from %s to INSTALLED",
308 prevParking);
309 updated = states.replace(id, prevParking, INSTALLED);
310 verify(updated, "Conditional replace %s => %s failed", prevParking, INSTALLED);
311 evtType = IntentEvent.Type.INSTALLED;
312 break;
313
314 case FAILED:
315 prevParking = states.get(id);
316 updated = states.replace(id, prevParking, FAILED);
317 verify(updated, "Conditional replace %s => %s failed", prevParking, FAILED);
318 evtType = IntentEvent.Type.FAILED;
319 break;
320
321 case WITHDRAWN:
322 prevParking = states.get(id);
323 verify(PRE_WITHDRAWN.contains(prevParking),
324 "Illegal state transition attempted from %s to WITHDRAWN",
325 prevParking);
326 updated = states.replace(id, prevParking, WITHDRAWN);
327 verify(updated, "Conditional replace %s => %s failed", prevParking, WITHDRAWN);
328 evtType = IntentEvent.Type.WITHDRAWN;
329 break;
330
331 default:
332 transitionedToParking = false;
333 prevParking = null;
334 break;
Yuta HIGUCHI89a7f472014-11-21 14:50:24 -0800335 }
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800336 if (transitionedToParking) {
337 log.debug("Parking State change: {} {}=>{}", id, prevParking, state);
338 // remove instance local state
339 transientStates.remove(id);
340 } else {
341 // Update instance local state, which includes non-parking state transition
342 final IntentState prevTransient = transientStates.put(id, state);
343 log.debug("Transient State change: {} {}=>{}", id, prevTransient, state);
344 }
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800345
alshabiba9819bf2014-11-30 18:15:52 -0800346 if (evtType != null) {
347 notifyDelegate(new IntentEvent(evtType, intent));
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800348 }
alshabiba9819bf2014-11-30 18:15:52 -0800349 return;
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800350 } finally {
351 stopTimer(timer);
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700352 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700353 }
354
355 @Override
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700356 public void setInstallableIntents(IntentId intentId, List<Intent> result) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800357 Context timer = startTimer(setInstallableIntentsTimer);
358 try {
359 installable.put(intentId, result);
360 } finally {
361 stopTimer(timer);
362 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700363 }
364
365 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700366 public List<Intent> getInstallableIntents(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800367 Context timer = startTimer(getInstallableIntentsTimer);
368 try {
369 return installable.get(intentId);
370 } finally {
371 stopTimer(timer);
372 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700373 }
374
375 @Override
376 public void removeInstalledIntents(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800377 Context timer = startTimer(removeInstalledIntentsTimer);
378 try {
379 installable.remove(intentId);
380 } finally {
381 stopTimer(timer);
382 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700383 }
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800384
385 protected String strIntentId(IntentId key) {
386 return keyCache.getUnchecked(key);
387 }
388
389 /**
390 * Distributed Map from IntentId to some value.
391 *
392 * @param <V> Map value type
393 */
394 final class IntentIdMap<V> extends CMap<IntentId, V> {
395
396 /**
397 * Creates a IntentIdMap instance.
398 *
399 * @param dbAdminService DatabaseAdminService to use for this instance
400 * @param dbService DatabaseService to use for this instance
401 * @param tableName table which this Map corresponds to
402 * @param serializer Value serializer
403 */
404 public IntentIdMap(DatabaseAdminService dbAdminService,
405 DatabaseService dbService,
406 String tableName,
407 StoreSerializer serializer) {
408 super(dbAdminService, dbService, tableName, serializer);
409 }
410
411 @Override
412 protected String sK(IntentId key) {
413 return strIntentId(key);
414 }
415 }
416
417 @Override
418 public List<Operation> batchWrite(BatchWrite batch) {
419
420 List<Operation> failed = new ArrayList<>();
421 final Builder builder = BatchWriteRequest.newBuilder();
alshabiba9819bf2014-11-30 18:15:52 -0800422 List<IntentEvent> events = Lists.newArrayList();
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800423
Yuta HIGUCHIf5682452014-12-01 10:17:15 -0800424 final Set<IntentId> transitionedToParking = new HashSet<>();
425
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800426 for (Operation op : batch.operations()) {
427 switch (op.type()) {
428 case CREATE_INTENT:
429 checkArgument(op.args().size() == 1,
430 "CREATE_INTENT takes 1 argument. %s", op);
431 Intent intent = op.arg(0);
432 builder.putIfAbsent(INTENTS_TABLE, strIntentId(intent.id()), serializer.encode(intent));
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800433 builder.putIfAbsent(STATES_TABLE, strIntentId(intent.id()), serializer.encode(INSTALL_REQ));
434 events.add(IntentEvent.getEvent(INSTALL_REQ, intent));
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800435 break;
436
437 case REMOVE_INTENT:
438 checkArgument(op.args().size() == 1,
439 "REMOVE_INTENT takes 1 argument. %s", op);
440 IntentId intentId = (IntentId) op.arg(0);
441 builder.remove(INTENTS_TABLE, strIntentId(intentId));
442 builder.remove(STATES_TABLE, strIntentId(intentId));
443 builder.remove(INSTALLABLE_TABLE, strIntentId(intentId));
444 break;
445
446 case SET_STATE:
447 checkArgument(op.args().size() == 2,
448 "SET_STATE takes 2 arguments. %s", op);
449 intent = op.arg(0);
450 IntentState newState = op.arg(1);
451 builder.put(STATES_TABLE, strIntentId(intent.id()), serializer.encode(newState));
Yuta HIGUCHIf5682452014-12-01 10:17:15 -0800452 if (PARKING.contains(newState)) {
453 transitionedToParking.add(intent.id());
Yuta HIGUCHI5cd352d2014-12-01 20:16:02 -0800454 events.add(IntentEvent.getEvent(newState, intent));
Yuta HIGUCHIf5682452014-12-01 10:17:15 -0800455 } else {
456 transitionedToParking.remove(intent.id());
457 }
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800458 break;
459
460 case SET_INSTALLABLE:
461 checkArgument(op.args().size() == 2,
462 "SET_INSTALLABLE takes 2 arguments. %s", op);
463 intentId = op.arg(0);
464 List<Intent> installableIntents = op.arg(1);
465 builder.put(INSTALLABLE_TABLE, strIntentId(intentId), serializer.encode(installableIntents));
466 break;
467
468 case REMOVE_INSTALLED:
469 checkArgument(op.args().size() == 1,
470 "REMOVE_INSTALLED takes 1 argument. %s", op);
471 intentId = op.arg(0);
472 builder.remove(INSTALLABLE_TABLE, strIntentId(intentId));
473 break;
474
475 default:
476 log.warn("Unknown Operation encountered: {}", op);
477 failed.add(op);
478 break;
479 }
480 }
481
482 BatchWriteResult batchWriteResult = dbService.batchWrite(builder.build());
483 if (batchWriteResult.isSuccessful()) {
484 // no-failure (except for invalid input)
Yuta HIGUCHIf5682452014-12-01 10:17:15 -0800485 transitionedToParking.forEach((intentId) -> transientStates.remove(intentId));
alshabiba9819bf2014-11-30 18:15:52 -0800486 notifyDelegate(events);
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800487 return failed;
488 } else {
489 // everything failed
alshabiba9819bf2014-11-30 18:15:52 -0800490 // FIXME what to do with events?
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800491 return batch.operations();
492 }
493 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700494}