blob: 10787282c6a1a1807d3bf71bc32a86fbbd2edfa0 [file] [log] [blame]
Brian O'Connor41718fc2014-10-30 16:57:21 -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 */
16package org.onlab.onos.store.resource.impl;
17
18import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
Yuta HIGUCHI35242292014-11-12 18:53:15 -080021import org.apache.felix.scr.annotations.Reference;
22import org.apache.felix.scr.annotations.ReferenceCardinality;
Brian O'Connor41718fc2014-10-30 16:57:21 -070023import org.apache.felix.scr.annotations.Service;
24import org.onlab.onos.net.Link;
Yuta HIGUCHI35242292014-11-12 18:53:15 -080025import org.onlab.onos.net.LinkKey;
Brian O'Connor41718fc2014-10-30 16:57:21 -070026import org.onlab.onos.net.intent.IntentId;
Yuta HIGUCHI35242292014-11-12 18:53:15 -080027import org.onlab.onos.net.link.LinkService;
Brian O'Connor41718fc2014-10-30 16:57:21 -070028import org.onlab.onos.net.resource.Bandwidth;
29import org.onlab.onos.net.resource.BandwidthResourceAllocation;
30import org.onlab.onos.net.resource.Lambda;
31import org.onlab.onos.net.resource.LambdaResourceAllocation;
32import org.onlab.onos.net.resource.LinkResourceAllocations;
33import org.onlab.onos.net.resource.LinkResourceStore;
34import org.onlab.onos.net.resource.ResourceAllocation;
35import org.onlab.onos.net.resource.ResourceType;
Yuta HIGUCHI35242292014-11-12 18:53:15 -080036import org.onlab.onos.store.serializers.KryoSerializer;
37import org.onlab.onos.store.serializers.StoreSerializer;
38import org.onlab.onos.store.service.BatchWriteRequest;
39import org.onlab.onos.store.service.BatchWriteRequest.Builder;
40import org.onlab.onos.store.service.BatchWriteResult;
41import org.onlab.onos.store.service.DatabaseAdminService;
Yuta HIGUCHI2fe63342014-11-17 21:33:38 -080042import org.onlab.onos.store.service.DatabaseException;
Yuta HIGUCHI35242292014-11-12 18:53:15 -080043import org.onlab.onos.store.service.DatabaseService;
44import org.onlab.onos.store.service.VersionedValue;
45import org.onlab.onos.store.service.WriteRequest;
46import org.onlab.onos.store.service.WriteResult;
Brian O'Connor41718fc2014-10-30 16:57:21 -070047import org.slf4j.Logger;
48
Yuta HIGUCHI35242292014-11-12 18:53:15 -080049import com.google.common.base.Function;
50import com.google.common.collect.FluentIterable;
51import com.google.common.collect.ImmutableSet;
52import com.google.common.collect.Sets;
53
54import java.util.ArrayList;
55import java.util.Collection;
Brian O'Connor41718fc2014-10-30 16:57:21 -070056import java.util.HashMap;
57import java.util.HashSet;
Yuta HIGUCHI35242292014-11-12 18:53:15 -080058import java.util.List;
Brian O'Connor41718fc2014-10-30 16:57:21 -070059import java.util.Map;
60import java.util.Set;
61
Yuta HIGUCHI35242292014-11-12 18:53:15 -080062import static com.google.common.base.Preconditions.checkArgument;
Brian O'Connor41718fc2014-10-30 16:57:21 -070063import static com.google.common.base.Preconditions.checkNotNull;
64import static com.google.common.base.Preconditions.checkState;
Yuta HIGUCHI35242292014-11-12 18:53:15 -080065import static com.google.common.base.Predicates.notNull;
66import static org.onlab.util.HexString.toHexString;
Brian O'Connor41718fc2014-10-30 16:57:21 -070067import static org.slf4j.LoggerFactory.getLogger;
68
69/**
Yuta HIGUCHI35242292014-11-12 18:53:15 -080070 * Manages link resources using database service.
Brian O'Connor41718fc2014-10-30 16:57:21 -070071 */
72@Component(immediate = true)
73@Service
74public class DistributedLinkResourceStore implements LinkResourceStore {
Yuta HIGUCHI35242292014-11-12 18:53:15 -080075
Brian O'Connor41718fc2014-10-30 16:57:21 -070076 private final Logger log = getLogger(getClass());
Yuta HIGUCHI35242292014-11-12 18:53:15 -080077
78 // FIXME: what is the Bandwidth unit?
79 private static final Bandwidth DEFAULT_BANDWIDTH = Bandwidth.valueOf(1_000);
80
81 // table to store current allocations
82 /** LinkKey -> List<LinkResourceAllocations>. */
83 private static final String LINK_RESOURCE_ALLOCATIONS = "LinkResourceAllocations";
84
85 /** IntentId -> LinkResourceAllocations. */
86 private static final String INTENT_ALLOCATIONS = "IntentAllocations";
87
88 private static final Bandwidth EMPTY_BW = Bandwidth.valueOf(0);
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected DatabaseAdminService databaseAdminService;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected DatabaseService databaseService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected LinkService linkService;
98
99 // Link annotation key name to use as bandwidth
100 private String bandwidthAnnotation = "bandwidth";
101 // Link annotation key name to use as max lamda
102 private String wavesAnnotation = "optical.waves";
103
104 private StoreSerializer serializer;
105
Brian O'Connor41718fc2014-10-30 16:57:21 -0700106
Yuta HIGUCHI45e5cd12014-11-24 14:57:13 -0800107 void createTable(String tableName) {
108 boolean tableReady = false;
109 do {
110 try {
111 if (!databaseAdminService.listTables().contains(tableName)) {
112 databaseAdminService.createTable(tableName);
113 }
114 tableReady = true;
115 } catch (DatabaseException e) {
116 log.debug("Failed creating table, retrying", e);
117 try {
118 Thread.sleep(200);
119 } catch (InterruptedException e1) {
120 throw new DatabaseException(e1);
121 }
122 }
123 } while (!tableReady);
124 }
125
Brian O'Connor41718fc2014-10-30 16:57:21 -0700126 @Activate
127 public void activate() {
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800128
129 serializer = new KryoSerializer();
130
Yuta HIGUCHI45e5cd12014-11-24 14:57:13 -0800131 createTable(LINK_RESOURCE_ALLOCATIONS);
132 createTable(INTENT_ALLOCATIONS);
Yuta HIGUCHI2fe63342014-11-17 21:33:38 -0800133
Brian O'Connor41718fc2014-10-30 16:57:21 -0700134 log.info("Started");
135 }
136
137 @Deactivate
138 public void deactivate() {
139 log.info("Stopped");
140 }
141
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800142 private Set<? extends ResourceAllocation> getResourceCapacity(ResourceType type, Link link) {
143 // TODO: plugin/provider mechanism to add resource type in the future?
144 if (type == ResourceType.BANDWIDTH) {
145 return ImmutableSet.of(getBandwidthResourceCapacity(link));
Brian O'Connor41718fc2014-10-30 16:57:21 -0700146 }
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800147 if (type == ResourceType.LAMBDA) {
148 return getLambdaResourceCapacity(link);
149 }
150 return null;
151 }
152
153 private Set<LambdaResourceAllocation> getLambdaResourceCapacity(Link link) {
154 // FIXME enumerate all the possible link/port lambdas
155 Set<LambdaResourceAllocation> allocations = new HashSet<>();
156 try {
157 final int waves = Integer.parseInt(link.annotations().value(wavesAnnotation));
158 for (int i = 1; i <= waves; i++) {
159 allocations.add(new LambdaResourceAllocation(Lambda.valueOf(i)));
160 }
161 } catch (NumberFormatException e) {
162 log.debug("No {} annotation on link %s", wavesAnnotation, link);
163 }
Brian O'Connor41718fc2014-10-30 16:57:21 -0700164 return allocations;
165 }
166
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800167 private BandwidthResourceAllocation getBandwidthResourceCapacity(Link link) {
168
169 // if Link annotation exist, use them
170 // if all fails, use DEFAULT_BANDWIDTH
171
172 Bandwidth bandwidth = null;
173 String strBw = link.annotations().value(bandwidthAnnotation);
174 if (strBw != null) {
175 try {
176 bandwidth = Bandwidth.valueOf(Double.parseDouble(strBw));
177 } catch (NumberFormatException e) {
178 // do nothings
179 bandwidth = null;
Brian O'Connor41718fc2014-10-30 16:57:21 -0700180 }
181 }
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800182
183 if (bandwidth == null) {
184 // fall back, use fixed default
185 bandwidth = DEFAULT_BANDWIDTH;
186 }
187 return new BandwidthResourceAllocation(bandwidth);
Brian O'Connor41718fc2014-10-30 16:57:21 -0700188 }
189
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800190 private Map<ResourceType, Set<? extends ResourceAllocation>> getResourceCapacity(Link link) {
191 Map<ResourceType, Set<? extends ResourceAllocation>> caps = new HashMap<>();
192 for (ResourceType type : ResourceType.values()) {
193 Set<? extends ResourceAllocation> cap = getResourceCapacity(type, link);
194 if (cap != null) {
195 caps.put(type, cap);
196 }
197 }
198 return caps;
199 }
200
201 @Override
202 public Set<ResourceAllocation> getFreeResources(Link link) {
203 Map<ResourceType, Set<? extends ResourceAllocation>> freeResources = getFreeResourcesEx(link);
204 Set<ResourceAllocation> allFree = new HashSet<>();
205 for (Set<? extends ResourceAllocation> r:freeResources.values()) {
206 allFree.addAll(r);
207 }
208 return allFree;
209 }
210
211 private Map<ResourceType, Set<? extends ResourceAllocation>> getFreeResourcesEx(Link link) {
212 // returns capacity - allocated
213
Brian O'Connor41718fc2014-10-30 16:57:21 -0700214 checkNotNull(link);
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800215 Map<ResourceType, Set<? extends ResourceAllocation>> free = new HashMap<>();
216 final Map<ResourceType, Set<? extends ResourceAllocation>> caps = getResourceCapacity(link);
217 final Iterable<LinkResourceAllocations> allocations = getAllocations(link);
218
219 for (ResourceType type : ResourceType.values()) {
220 // there should be class/category of resources
221 switch (type) {
Brian O'Connor41718fc2014-10-30 16:57:21 -0700222 case BANDWIDTH:
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800223 {
224 Set<? extends ResourceAllocation> bw = caps.get(ResourceType.BANDWIDTH);
225 if (bw == null || bw.isEmpty()) {
226 bw = Sets.newHashSet(new BandwidthResourceAllocation(EMPTY_BW));
227 }
228
229 BandwidthResourceAllocation cap = (BandwidthResourceAllocation) bw.iterator().next();
230 double freeBw = cap.bandwidth().toDouble();
231
232 // enumerate current allocations, subtracting resources
233 for (LinkResourceAllocations alloc : allocations) {
234 Set<ResourceAllocation> types = alloc.getResourceAllocation(link);
235 for (ResourceAllocation a : types) {
236 if (a instanceof BandwidthResourceAllocation) {
237 BandwidthResourceAllocation bwA = (BandwidthResourceAllocation) a;
238 freeBw -= bwA.bandwidth().toDouble();
239 }
240 }
241 }
242
243 free.put(type, Sets.newHashSet(new BandwidthResourceAllocation(Bandwidth.valueOf(freeBw))));
Brian O'Connor41718fc2014-10-30 16:57:21 -0700244 break;
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800245 }
246
Brian O'Connor41718fc2014-10-30 16:57:21 -0700247 case LAMBDA:
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800248 {
249 Set<? extends ResourceAllocation> lmd = caps.get(type);
250 if (lmd == null || lmd.isEmpty()) {
251 // nothing left
252 break;
253 }
254 Set<LambdaResourceAllocation> freeL = new HashSet<>();
255 for (ResourceAllocation r : lmd) {
256 if (r instanceof LambdaResourceAllocation) {
257 freeL.add((LambdaResourceAllocation) r);
258 }
259 }
260
261 // enumerate current allocations, removing resources
262 for (LinkResourceAllocations alloc : allocations) {
263 Set<ResourceAllocation> types = alloc.getResourceAllocation(link);
264 for (ResourceAllocation a : types) {
265 if (a instanceof LambdaResourceAllocation) {
266 freeL.remove(a);
267 }
268 }
269 }
270
271 free.put(type, freeL);
Brian O'Connor41718fc2014-10-30 16:57:21 -0700272 break;
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800273 }
274
Brian O'Connor41718fc2014-10-30 16:57:21 -0700275 default:
276 break;
277 }
278 }
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800279 return free;
Brian O'Connor41718fc2014-10-30 16:57:21 -0700280 }
281
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800282 private LinkResourceAllocations getIntentAllocations(IntentId id) {
283 VersionedValue vv
284 = databaseService.get(INTENT_ALLOCATIONS, toIntentDbKey(checkNotNull(id)));
285 if (vv == null || vv.value() == null) {
286 return null;
287 }
288 return decodeIntentAllocations(vv.value());
289 }
290
291 private Builder putIntentAllocations(Builder ctx,
292 IntentId id,
293 LinkResourceAllocations alloc) {
294 return ctx.put(INTENT_ALLOCATIONS,
295 toIntentDbKey(id),
296 encodeIntentAllocations(alloc));
297 }
298
299
300 @Override
301 public void allocateResources(LinkResourceAllocations allocations) {
302 checkNotNull(allocations);
303
304 Builder tx = BatchWriteRequest.newBuilder();
305
306 // TODO: Should IntentId -> Allocation be updated conditionally?
307 putIntentAllocations(tx, allocations.intendId(), allocations);
308
309 for (Link link : allocations.links()) {
310 allocateLinkResource(tx, link, allocations);
311 }
312
313 BatchWriteRequest batch = tx.build();
314// log.info("Intent: {}", databaseService.getAll(INTENT_ALLOCATIONS));
315// log.info("Link: {}", databaseService.getAll(LINK_RESOURCE_ALLOCATIONS));
316
317 BatchWriteResult result = databaseService.batchWrite(batch);
318 if (!result.isSuccessful()) {
319 log.error("Allocation Failed.");
320 if (log.isDebugEnabled()) {
321 logFailureDetail(batch, result);
322 }
323 // FIXME throw appropriate exception, with what failed.
324 checkState(result.isSuccessful(), "Allocation failed");
325 }
326 }
327
328 private void logFailureDetail(BatchWriteRequest batch,
329 BatchWriteResult result) {
330 for (int i = 0; i < batch.batchSize(); ++i) {
331 final WriteRequest req = batch.getAsList().get(i);
332 final WriteResult fail = result.getAsList().get(i);
333 switch (fail.status()) {
334 case ABORTED:
335 log.debug("ABORTED: {}@{}", req.key(), req.tableName());
Brian O'Connor41718fc2014-10-30 16:57:21 -0700336 break;
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800337 case PRECONDITION_VIOLATION:
338 switch (req.type()) {
339 case PUT_IF_ABSENT:
340 log.debug("{}: {}@{} : {}", req.type(),
341 req.key(), req.tableName(), fail.previousValue());
342 break;
343 case PUT_IF_VALUE:
344 case REMOVE_IF_VALUE:
345 log.debug("{}: {}@{} : was {}, expected {}", req.type(),
346 req.key(), req.tableName(),
347 fail.previousValue(),
348 toHexString(req.oldValue()));
349 break;
350 case PUT_IF_VERSION:
351 case REMOVE_IF_VERSION:
352 log.debug("{}: {}@{} : was {}, expected {}", req.type(),
353 req.key(), req.tableName(),
354 fail.previousValue().version(),
355 req.previousVersion());
356 break;
357 default:
358 log.error("Should never reach here.");
359 break;
360 }
Brian O'Connor41718fc2014-10-30 16:57:21 -0700361 break;
362 default:
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800363 log.error("Should never reach here.");
Brian O'Connor41718fc2014-10-30 16:57:21 -0700364 break;
365 }
366 }
Brian O'Connor41718fc2014-10-30 16:57:21 -0700367 }
368
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800369 private Builder allocateLinkResource(Builder builder, Link link,
370 LinkResourceAllocations allocations) {
Brian O'Connor41718fc2014-10-30 16:57:21 -0700371
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800372 // requested resources
373 Set<ResourceAllocation> reqs = allocations.getResourceAllocation(link);
Brian O'Connor41718fc2014-10-30 16:57:21 -0700374
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800375 Map<ResourceType, Set<? extends ResourceAllocation>> available = getFreeResourcesEx(link);
376 for (ResourceAllocation req : reqs) {
377 Set<? extends ResourceAllocation> avail = available.get(req.type());
378 if (req instanceof BandwidthResourceAllocation) {
379 // check if allocation should be accepted
380 if (avail.isEmpty()) {
381 checkState(!avail.isEmpty(),
382 "There's no Bandwidth resource on %s?",
383 link);
384 }
385 BandwidthResourceAllocation bw = (BandwidthResourceAllocation) avail.iterator().next();
386 double bwLeft = bw.bandwidth().toDouble();
387 bwLeft -= ((BandwidthResourceAllocation) req).bandwidth().toDouble();
388 if (bwLeft < 0) {
389 // FIXME throw appropriate Exception
390 checkState(bwLeft >= 0,
391 "There's no Bandwidth left on %s. %s",
392 link, bwLeft);
393 }
394 } else if (req instanceof LambdaResourceAllocation) {
395
396 // check if allocation should be accepted
397 if (!avail.contains(req)) {
398 // requested lambda was not available
399 // FIXME throw appropriate exception
400 checkState(avail.contains(req),
401 "Allocating %s on %s failed",
402 req, link);
403 }
Brian O'Connor41718fc2014-10-30 16:57:21 -0700404 }
Brian O'Connor41718fc2014-10-30 16:57:21 -0700405 }
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800406 // all requests allocatable => add allocation
407 final List<LinkResourceAllocations> before = getAllocations(link);
408 List<LinkResourceAllocations> after = new ArrayList<>(before.size());
409 after.addAll(before);
410 after.add(allocations);
411 replaceLinkAllocations(builder, LinkKey.linkKey(link), before, after);
412 return builder;
413 }
414
415 private Builder replaceLinkAllocations(Builder builder, LinkKey linkKey,
416 List<LinkResourceAllocations> before,
417 List<LinkResourceAllocations> after) {
418
419 byte[] oldValue = encodeLinkAllocations(before);
420 byte[] newValue = encodeLinkAllocations(after);
421 builder.putIfValueMatches(LINK_RESOURCE_ALLOCATIONS, toLinkDbKey(linkKey), oldValue, newValue);
422 return builder;
Brian O'Connor41718fc2014-10-30 16:57:21 -0700423 }
424
425 @Override
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800426 public void releaseResources(LinkResourceAllocations allocations) {
Brian O'Connor41718fc2014-10-30 16:57:21 -0700427 checkNotNull(allocations);
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800428
429 final IntentId intendId = allocations.intendId();
430 final String dbIntentId = toIntentDbKey(intendId);
431 final Collection<Link> links = allocations.links();
432
433 // TODO: does release must happen in a batch?
434 boolean success;
435 do {
436 Builder tx = BatchWriteRequest.newBuilder();
437
438 // TODO: Should IntentId -> Allocation be updated conditionally?
439 tx.remove(INTENT_ALLOCATIONS, dbIntentId);
440
441 for (Link link : links) {
442 final LinkKey linkId = LinkKey.linkKey(link);
443 final String dbLinkId = toLinkDbKey(linkId);
444 VersionedValue vv = databaseService.get(LINK_RESOURCE_ALLOCATIONS, dbLinkId);
445 if (vv == null || vv.value() == null) {
446 // something is wrong, but it is already freed
447 log.warn("There was no resource left to release on {}", linkId);
448 continue;
449 }
450 List<LinkResourceAllocations> before = decodeLinkAllocations(vv.value());
451 List<LinkResourceAllocations> after = new ArrayList<>(before);
452 after.remove(allocations);
453 byte[] oldValue = encodeLinkAllocations(before);
454 byte[] newValue = encodeLinkAllocations(after);
455 tx.putIfValueMatches(LINK_RESOURCE_ALLOCATIONS, dbLinkId, oldValue, newValue);
Brian O'Connor41718fc2014-10-30 16:57:21 -0700456 }
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800457
458 BatchWriteResult batchWrite = databaseService.batchWrite(tx.build());
459 success = batchWrite.isSuccessful();
460 } while (!success);
Brian O'Connor41718fc2014-10-30 16:57:21 -0700461 }
462
463 @Override
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800464 public LinkResourceAllocations getAllocations(IntentId intentId) {
Brian O'Connor41718fc2014-10-30 16:57:21 -0700465 checkNotNull(intentId);
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800466 VersionedValue vv = databaseService.get(INTENT_ALLOCATIONS, toIntentDbKey(intentId));
467 if (vv == null) {
468 // FIXME: should we return null or LinkResourceAllocations with nothing allocated?
469 return null;
Brian O'Connor41718fc2014-10-30 16:57:21 -0700470 }
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800471 LinkResourceAllocations allocations = decodeIntentAllocations(vv.value());
472 return allocations;
473 }
474
475 private String toLinkDbKey(LinkKey linkid) {
476 // introduce cache if necessary
477 return linkid.toString();
478 // TODO: Above is irreversible, if we need reverse conversion
479 // we may need something like below, due to String only limitation
480// byte[] bytes = serializer.encode(linkid);
481// StringBuilder builder = new StringBuilder(bytes.length * 4);
482// boolean isFirst = true;
483// for (byte b : bytes) {
484// if (!isFirst) {
485// builder.append(',');
486// }
487// builder.append(b);
488// isFirst = false;
489// }
490// return builder.toString();
491 }
492
493// private LinkKey toLinkKey(String linkKey) {
494// String[] bytes = linkKey.split(",");
495// ByteBuffer buf = ByteBuffer.allocate(bytes.length);
496// for (String bs : bytes) {
497// buf.put(Byte.parseByte(bs));
498// }
499// buf.flip();
500// return serializer.decode(buf);
501// }
502
503 private String toIntentDbKey(IntentId intentid) {
504 return intentid.toString();
505 }
506
507 private IntentId toIntentId(String intentid) {
508 checkArgument(intentid.startsWith("0x"));
509 return IntentId.valueOf(Long.parseLong(intentid.substring(2)));
510 }
511
512 private LinkResourceAllocations decodeIntentAllocations(byte[] bytes) {
513 return serializer.decode(bytes);
514 }
515
516 private byte[] encodeIntentAllocations(LinkResourceAllocations alloc) {
517 return serializer.encode(checkNotNull(alloc));
518 }
519
520 private List<LinkResourceAllocations> decodeLinkAllocations(byte[] bytes) {
521 return serializer.decode(bytes);
522 }
523
524 private byte[] encodeLinkAllocations(List<LinkResourceAllocations> alloc) {
525 return serializer.encode(checkNotNull(alloc));
Brian O'Connor41718fc2014-10-30 16:57:21 -0700526 }
527
528 @Override
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800529 public List<LinkResourceAllocations> getAllocations(Link link) {
530 checkNotNull(link);
531 final LinkKey key = LinkKey.linkKey(link);
532 final String dbKey = toLinkDbKey(key);
533 VersionedValue vv = databaseService.get(LINK_RESOURCE_ALLOCATIONS, dbKey);
534 if (vv == null) {
535 // write empty so that all other update can be replace operation
536 byte[] emptyList = encodeLinkAllocations(new ArrayList<>());
537 boolean written = databaseService.putIfAbsent(LINK_RESOURCE_ALLOCATIONS, dbKey, emptyList);
538 log.trace("Empty allocation write success? {}", written);
539 vv = databaseService.get(LINK_RESOURCE_ALLOCATIONS, dbKey);
540 if (vv == null) {
541 log.error("Failed to re-read allocation for {}", dbKey);
542 // note: cannot be Collections.emptyList();
543 return new ArrayList<>();
544 }
545 }
546 List<LinkResourceAllocations> allocations = decodeLinkAllocations(vv.value());
547 return allocations;
548 }
549
550 @Override
551 public Iterable<LinkResourceAllocations> getAllocations() {
552 //IntentId -> LinkResourceAllocations
553 Map<String, VersionedValue> all = databaseService.getAll(INTENT_ALLOCATIONS);
554
555 return FluentIterable.from(all.values())
556 .transform(new Function<VersionedValue, LinkResourceAllocations>() {
557
558 @Override
559 public LinkResourceAllocations apply(VersionedValue input) {
560 if (input == null || input.value() == null) {
561 return null;
562 }
563 return decodeIntentAllocations(input.value());
564 }
565 })
566 .filter(notNull());
Brian O'Connor41718fc2014-10-30 16:57:21 -0700567 }
568
569}