blob: 8537cca3daa4750310a539519099b93704bfde01 [file] [log] [blame]
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -07001/*
2 * Copyright 2015 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.onosproject.store.newresource.impl;
17
18import com.google.common.annotations.Beta;
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Reference;
22import org.apache.felix.scr.annotations.ReferenceCardinality;
23import org.apache.felix.scr.annotations.Service;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070024import org.onosproject.net.newresource.ResourceConsumer;
Sho SHIMIZUfa62b472015-11-02 17:35:46 -080025import org.onosproject.net.newresource.ResourceEvent;
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -070026import org.onosproject.net.newresource.ResourcePath;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070027import org.onosproject.net.newresource.ResourceStore;
Sho SHIMIZUfa62b472015-11-02 17:35:46 -080028import org.onosproject.net.newresource.ResourceStoreDelegate;
29import org.onosproject.store.AbstractStore;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070030import org.onosproject.store.serializers.KryoNamespaces;
31import org.onosproject.store.service.ConsistentMap;
32import org.onosproject.store.service.Serializer;
33import org.onosproject.store.service.StorageService;
34import org.onosproject.store.service.TransactionContext;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070035import org.onosproject.store.service.TransactionalMap;
36import org.onosproject.store.service.Versioned;
37import org.slf4j.Logger;
38import org.slf4j.LoggerFactory;
39
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070040import java.util.ArrayList;
41import java.util.Arrays;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070042import java.util.Collection;
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070043import java.util.Collections;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070044import java.util.Iterator;
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070045import java.util.LinkedHashSet;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070046import java.util.List;
47import java.util.Map;
48import java.util.Optional;
49import java.util.stream.Collectors;
50
51import static com.google.common.base.Preconditions.checkArgument;
52import static com.google.common.base.Preconditions.checkNotNull;
Sho SHIMIZUfa62b472015-11-02 17:35:46 -080053import static org.onosproject.net.newresource.ResourceEvent.Type.*;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070054
55/**
56 * Implementation of ResourceStore using TransactionalMap.
57 */
Sho SHIMIZU9a2b8292015-10-28 13:00:16 -070058@Component(immediate = true)
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070059@Service
60@Beta
Sho SHIMIZUfa62b472015-11-02 17:35:46 -080061public class ConsistentResourceStore extends AbstractStore<ResourceEvent, ResourceStoreDelegate>
62 implements ResourceStore {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070063 private static final Logger log = LoggerFactory.getLogger(ConsistentResourceStore.class);
64
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070065 private static final String CONSUMER_MAP = "onos-resource-consumers";
66 private static final String CHILD_MAP = "onos-resource-children";
67 private static final Serializer SERIALIZER = Serializer.using(
68 Arrays.asList(KryoNamespaces.BASIC, KryoNamespaces.API));
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070069
70 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
71 protected StorageService service;
72
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070073 private ConsistentMap<ResourcePath, ResourceConsumer> consumerMap;
74 private ConsistentMap<ResourcePath, List<ResourcePath>> childMap;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070075
76 @Activate
77 public void activate() {
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070078 consumerMap = service.<ResourcePath, ResourceConsumer>consistentMapBuilder()
79 .withName(CONSUMER_MAP)
80 .withSerializer(SERIALIZER)
81 .build();
82 childMap = service.<ResourcePath, List<ResourcePath>>consistentMapBuilder()
83 .withName(CHILD_MAP)
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070084 .withSerializer(SERIALIZER)
85 .build();
86 }
87
88 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -070089 public Optional<ResourceConsumer> getConsumer(ResourcePath resource) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070090 checkNotNull(resource);
91
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070092 Versioned<ResourceConsumer> consumer = consumerMap.get(resource);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070093 if (consumer == null) {
94 return Optional.empty();
95 }
96
97 return Optional.of(consumer.value());
98 }
99
100 @Override
Sho SHIMIZU83e17a02015-08-20 14:07:05 -0700101 public boolean register(List<ResourcePath> resources) {
102 checkNotNull(resources);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700103
104 TransactionContext tx = service.transactionContextBuilder().build();
105 tx.begin();
106
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800107 TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap =
108 tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700109
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800110 Map<ResourcePath, List<ResourcePath>> resourceMap = resources.stream()
111 .filter(x -> x.parent().isPresent())
112 .collect(Collectors.groupingBy(x -> x.parent().get()));
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700113
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800114 for (Map.Entry<ResourcePath, List<ResourcePath>> entry: resourceMap.entrySet()) {
115 if (!isRegistered(childTxMap, entry.getKey())) {
116 return abortTransaction(tx);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700117 }
118
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800119 if (!appendValues(childTxMap, entry.getKey(), entry.getValue())) {
120 return abortTransaction(tx);
121 }
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700122 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800123
Sho SHIMIZUfa62b472015-11-02 17:35:46 -0800124 boolean success = tx.commit();
125 if (success) {
126 List<ResourceEvent> events = resources.stream()
127 .filter(x -> x.parent().isPresent())
128 .map(x -> new ResourceEvent(RESOURCE_ADDED, x))
129 .collect(Collectors.toList());
130 notifyDelegate(events);
131 }
132 return success;
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700133 }
134
135 @Override
Sho SHIMIZU83e17a02015-08-20 14:07:05 -0700136 public boolean unregister(List<ResourcePath> resources) {
137 checkNotNull(resources);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700138
139 TransactionContext tx = service.transactionContextBuilder().build();
140 tx.begin();
141
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800142 TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap =
143 tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
144 TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap =
145 tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700146
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800147 Map<ResourcePath, List<ResourcePath>> resourceMap = resources.stream()
148 .filter(x -> x.parent().isPresent())
149 .collect(Collectors.groupingBy(x -> x.parent().get()));
Sho SHIMIZU83e17a02015-08-20 14:07:05 -0700150
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800151 // even if one of the resources is allocated to a consumer,
152 // all unregistrations are regarded as failure
153 for (Map.Entry<ResourcePath, List<ResourcePath>> entry: resourceMap.entrySet()) {
154 if (entry.getValue().stream().anyMatch(x -> consumerTxMap.get(x) != null)) {
155 return abortTransaction(tx);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700156 }
157
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800158 if (!removeValues(childTxMap, entry.getKey(), entry.getValue())) {
159 return abortTransaction(tx);
160 }
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700161 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800162
Sho SHIMIZUfa62b472015-11-02 17:35:46 -0800163 boolean success = tx.commit();
164 if (success) {
165 List<ResourceEvent> events = resources.stream()
166 .filter(x -> x.parent().isPresent())
167 .map(x -> new ResourceEvent(RESOURCE_REMOVED, x))
168 .collect(Collectors.toList());
169 notifyDelegate(events);
170 }
171 return success;
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700172 }
173
174 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700175 public boolean allocate(List<ResourcePath> resources, ResourceConsumer consumer) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700176 checkNotNull(resources);
177 checkNotNull(consumer);
178
179 TransactionContext tx = service.transactionContextBuilder().build();
180 tx.begin();
181
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800182 TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap =
183 tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
184 TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap =
185 tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700186
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800187 for (ResourcePath resource: resources) {
188 if (!isRegistered(childTxMap, resource)) {
189 return abortTransaction(tx);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700190 }
Sho SHIMIZUd29847f2015-08-13 09:10:59 -0700191
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800192 ResourceConsumer oldValue = consumerTxMap.put(resource, consumer);
193 if (oldValue != null) {
194 return abortTransaction(tx);
195 }
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700196 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800197
198 return tx.commit();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700199 }
200
201 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700202 public boolean release(List<ResourcePath> resources, List<ResourceConsumer> consumers) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700203 checkNotNull(resources);
204 checkNotNull(consumers);
205 checkArgument(resources.size() == consumers.size());
206
207 TransactionContext tx = service.transactionContextBuilder().build();
208 tx.begin();
209
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800210 TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap =
211 tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER);
212 Iterator<ResourcePath> resourceIte = resources.iterator();
213 Iterator<ResourceConsumer> consumerIte = consumers.iterator();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700214
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800215 while (resourceIte.hasNext() && consumerIte.hasNext()) {
216 ResourcePath resource = resourceIte.next();
217 ResourceConsumer consumer = consumerIte.next();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700218
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800219 // if this single release fails (because the resource is allocated to another consumer,
220 // the whole release fails
221 if (!consumerTxMap.remove(resource, consumer)) {
222 return abortTransaction(tx);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700223 }
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700224 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800225
226 return tx.commit();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700227 }
228
229 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700230 public Collection<ResourcePath> getResources(ResourceConsumer consumer) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700231 checkNotNull(consumer);
232
233 // NOTE: getting all entries may become performance bottleneck
234 // TODO: revisit for better backend data structure
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700235 return consumerMap.entrySet().stream()
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700236 .filter(x -> x.getValue().value().equals(consumer))
237 .map(Map.Entry::getKey)
238 .collect(Collectors.toList());
239 }
240
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700241 @Override
Sho SHIMIZUe7f4f3f2015-10-13 16:27:25 -0700242 public Collection<ResourcePath> getChildResources(ResourcePath parent) {
243 checkNotNull(parent);
244
245 Versioned<List<ResourcePath>> children = childMap.get(parent);
246 if (children == null) {
247 return Collections.emptyList();
248 }
249
250 return children.value();
251 }
252
253 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700254 public <T> Collection<ResourcePath> getAllocatedResources(ResourcePath parent, Class<T> cls) {
255 checkNotNull(parent);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700256 checkNotNull(cls);
257
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700258 Versioned<List<ResourcePath>> children = childMap.get(parent);
259 if (children == null) {
260 return Collections.emptyList();
261 }
262
263 return children.value().stream()
264 .filter(x -> x.lastComponent().getClass().equals(cls))
265 .filter(consumerMap::containsKey)
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700266 .collect(Collectors.toList());
267 }
Sho SHIMIZUd29847f2015-08-13 09:10:59 -0700268
269 /**
270 * Abort the transaction.
271 *
272 * @param tx transaction context
273 * @return always false
274 */
275 private boolean abortTransaction(TransactionContext tx) {
276 tx.abort();
277 return false;
278 }
279
280 /**
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700281 * Appends the values to the existing values associated with the specified key.
Sho SHIMIZU4568c412015-08-21 16:39:07 -0700282 * If the map already has all the given values, appending will not happen.
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700283 *
284 * @param map map holding multiple values for a key
285 * @param key key specifying values
286 * @param values values to be appended
287 * @param <K> type of the key
288 * @param <V> type of the element of the list
289 * @return true if the operation succeeds, false otherwise.
290 */
Sho SHIMIZU2a1b2332015-08-18 22:40:12 -0700291 private <K, V> boolean appendValues(TransactionalMap<K, List<V>> map, K key, List<V> values) {
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700292 List<V> oldValues = map.get(key);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700293 if (oldValues == null) {
Sho SHIMIZU4568c412015-08-21 16:39:07 -0700294 return map.replace(key, oldValues, new ArrayList<>(values));
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700295 }
296
Sho SHIMIZU4568c412015-08-21 16:39:07 -0700297 LinkedHashSet<V> oldSet = new LinkedHashSet<>(oldValues);
298 if (oldSet.containsAll(values)) {
299 // don't write to map because all values are already stored
300 return true;
301 }
302
303 oldSet.addAll(values);
304 return map.replace(key, oldValues, new ArrayList<>(oldSet));
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700305 }
306
307 /**
Sho SHIMIZUba1f83b2015-10-14 08:11:20 -0700308 * Removes the values from the existing values associated with the specified key.
Sho SHIMIZU5618ee52015-08-21 17:19:44 -0700309 * If the map doesn't contain the given values, removal will not happen.
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700310 *
311 * @param map map holding multiple values for a key
312 * @param key key specifying values
313 * @param values values to be removed
314 * @param <K> type of the key
315 * @param <V> type of the element of the list
316 * @return true if the operation succeeds, false otherwise
317 */
318 private <K, V> boolean removeValues(TransactionalMap<K, List<V>> map, K key, List<V> values) {
319 List<V> oldValues = map.get(key);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700320 if (oldValues == null) {
Sho SHIMIZU5618ee52015-08-21 17:19:44 -0700321 return map.replace(key, oldValues, new ArrayList<>());
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700322 }
323
Sho SHIMIZU5618ee52015-08-21 17:19:44 -0700324 LinkedHashSet<V> oldSet = new LinkedHashSet<>(oldValues);
325 if (values.stream().allMatch(x -> !oldSet.contains(x))) {
326 // don't write map because none of the values are stored
327 return true;
328 }
329
330 oldSet.removeAll(values);
331 return map.replace(key, oldValues, new ArrayList<>(oldSet));
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700332 }
333
334 /**
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700335 * Checks if the specified resource is registered as a child of a resource in the map.
336 *
337 * @param map map storing parent - child relationship of resources
338 * @param resource resource to be checked
339 * @return true if the resource is registered, false otherwise.
340 */
341 private boolean isRegistered(TransactionalMap<ResourcePath, List<ResourcePath>> map, ResourcePath resource) {
342 // root is always regarded to be registered
Sho SHIMIZU01120782015-08-21 15:48:43 -0700343 if (resource.isRoot()) {
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700344 return true;
345 }
346
347 List<ResourcePath> value = map.get(resource.parent().get());
348 return value != null && value.contains(resource);
349 }
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700350}