blob: c9aaba5a902c7b02a6da803d1235479eda87c668 [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 SHIMIZU1f5e5912015-08-10 17:00:00 -070025import org.onosproject.net.newresource.ResourcePath;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070026import org.onosproject.net.newresource.ResourceStore;
27import org.onosproject.store.serializers.KryoNamespaces;
28import org.onosproject.store.service.ConsistentMap;
29import org.onosproject.store.service.Serializer;
30import org.onosproject.store.service.StorageService;
31import org.onosproject.store.service.TransactionContext;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070032import org.onosproject.store.service.TransactionalMap;
33import org.onosproject.store.service.Versioned;
34import org.slf4j.Logger;
35import org.slf4j.LoggerFactory;
36
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070037import java.util.ArrayList;
38import java.util.Arrays;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070039import java.util.Collection;
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070040import java.util.Collections;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070041import java.util.Iterator;
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070042import java.util.LinkedHashSet;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070043import java.util.List;
44import java.util.Map;
45import java.util.Optional;
46import java.util.stream.Collectors;
47
48import static com.google.common.base.Preconditions.checkArgument;
49import static com.google.common.base.Preconditions.checkNotNull;
50
51/**
52 * Implementation of ResourceStore using TransactionalMap.
53 */
Sho SHIMIZU9a2b8292015-10-28 13:00:16 -070054@Component(immediate = true)
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070055@Service
56@Beta
57public class ConsistentResourceStore implements ResourceStore {
58 private static final Logger log = LoggerFactory.getLogger(ConsistentResourceStore.class);
59
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070060 private static final String CONSUMER_MAP = "onos-resource-consumers";
61 private static final String CHILD_MAP = "onos-resource-children";
62 private static final Serializer SERIALIZER = Serializer.using(
63 Arrays.asList(KryoNamespaces.BASIC, KryoNamespaces.API));
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070064
65 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
66 protected StorageService service;
67
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070068 private ConsistentMap<ResourcePath, ResourceConsumer> consumerMap;
69 private ConsistentMap<ResourcePath, List<ResourcePath>> childMap;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070070
71 @Activate
72 public void activate() {
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070073 consumerMap = service.<ResourcePath, ResourceConsumer>consistentMapBuilder()
74 .withName(CONSUMER_MAP)
75 .withSerializer(SERIALIZER)
76 .build();
77 childMap = service.<ResourcePath, List<ResourcePath>>consistentMapBuilder()
78 .withName(CHILD_MAP)
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070079 .withSerializer(SERIALIZER)
80 .build();
81 }
82
83 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -070084 public Optional<ResourceConsumer> getConsumer(ResourcePath resource) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070085 checkNotNull(resource);
86
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070087 Versioned<ResourceConsumer> consumer = consumerMap.get(resource);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070088 if (consumer == null) {
89 return Optional.empty();
90 }
91
92 return Optional.of(consumer.value());
93 }
94
95 @Override
Sho SHIMIZU83e17a02015-08-20 14:07:05 -070096 public boolean register(List<ResourcePath> resources) {
97 checkNotNull(resources);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070098
99 TransactionContext tx = service.transactionContextBuilder().build();
100 tx.begin();
101
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800102 TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap =
103 tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700104
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800105 Map<ResourcePath, List<ResourcePath>> resourceMap = resources.stream()
106 .filter(x -> x.parent().isPresent())
107 .collect(Collectors.groupingBy(x -> x.parent().get()));
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700108
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800109 for (Map.Entry<ResourcePath, List<ResourcePath>> entry: resourceMap.entrySet()) {
110 if (!isRegistered(childTxMap, entry.getKey())) {
111 return abortTransaction(tx);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700112 }
113
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800114 if (!appendValues(childTxMap, entry.getKey(), entry.getValue())) {
115 return abortTransaction(tx);
116 }
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700117 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800118
119 return tx.commit();
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700120 }
121
122 @Override
Sho SHIMIZU83e17a02015-08-20 14:07:05 -0700123 public boolean unregister(List<ResourcePath> resources) {
124 checkNotNull(resources);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700125
126 TransactionContext tx = service.transactionContextBuilder().build();
127 tx.begin();
128
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800129 TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap =
130 tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
131 TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap =
132 tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700133
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800134 Map<ResourcePath, List<ResourcePath>> resourceMap = resources.stream()
135 .filter(x -> x.parent().isPresent())
136 .collect(Collectors.groupingBy(x -> x.parent().get()));
Sho SHIMIZU83e17a02015-08-20 14:07:05 -0700137
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800138 // even if one of the resources is allocated to a consumer,
139 // all unregistrations are regarded as failure
140 for (Map.Entry<ResourcePath, List<ResourcePath>> entry: resourceMap.entrySet()) {
141 if (entry.getValue().stream().anyMatch(x -> consumerTxMap.get(x) != null)) {
142 return abortTransaction(tx);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700143 }
144
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800145 if (!removeValues(childTxMap, entry.getKey(), entry.getValue())) {
146 return abortTransaction(tx);
147 }
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700148 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800149
150 return tx.commit();
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700151 }
152
153 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700154 public boolean allocate(List<ResourcePath> resources, ResourceConsumer consumer) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700155 checkNotNull(resources);
156 checkNotNull(consumer);
157
158 TransactionContext tx = service.transactionContextBuilder().build();
159 tx.begin();
160
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800161 TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap =
162 tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
163 TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap =
164 tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700165
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800166 for (ResourcePath resource: resources) {
167 if (!isRegistered(childTxMap, resource)) {
168 return abortTransaction(tx);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700169 }
Sho SHIMIZUd29847f2015-08-13 09:10:59 -0700170
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800171 ResourceConsumer oldValue = consumerTxMap.put(resource, consumer);
172 if (oldValue != null) {
173 return abortTransaction(tx);
174 }
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700175 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800176
177 return tx.commit();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700178 }
179
180 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700181 public boolean release(List<ResourcePath> resources, List<ResourceConsumer> consumers) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700182 checkNotNull(resources);
183 checkNotNull(consumers);
184 checkArgument(resources.size() == consumers.size());
185
186 TransactionContext tx = service.transactionContextBuilder().build();
187 tx.begin();
188
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800189 TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap =
190 tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER);
191 Iterator<ResourcePath> resourceIte = resources.iterator();
192 Iterator<ResourceConsumer> consumerIte = consumers.iterator();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700193
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800194 while (resourceIte.hasNext() && consumerIte.hasNext()) {
195 ResourcePath resource = resourceIte.next();
196 ResourceConsumer consumer = consumerIte.next();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700197
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800198 // if this single release fails (because the resource is allocated to another consumer,
199 // the whole release fails
200 if (!consumerTxMap.remove(resource, consumer)) {
201 return abortTransaction(tx);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700202 }
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700203 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800204
205 return tx.commit();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700206 }
207
208 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700209 public Collection<ResourcePath> getResources(ResourceConsumer consumer) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700210 checkNotNull(consumer);
211
212 // NOTE: getting all entries may become performance bottleneck
213 // TODO: revisit for better backend data structure
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700214 return consumerMap.entrySet().stream()
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700215 .filter(x -> x.getValue().value().equals(consumer))
216 .map(Map.Entry::getKey)
217 .collect(Collectors.toList());
218 }
219
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700220 @Override
Sho SHIMIZUe7f4f3f2015-10-13 16:27:25 -0700221 public Collection<ResourcePath> getChildResources(ResourcePath parent) {
222 checkNotNull(parent);
223
224 Versioned<List<ResourcePath>> children = childMap.get(parent);
225 if (children == null) {
226 return Collections.emptyList();
227 }
228
229 return children.value();
230 }
231
232 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700233 public <T> Collection<ResourcePath> getAllocatedResources(ResourcePath parent, Class<T> cls) {
234 checkNotNull(parent);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700235 checkNotNull(cls);
236
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700237 Versioned<List<ResourcePath>> children = childMap.get(parent);
238 if (children == null) {
239 return Collections.emptyList();
240 }
241
242 return children.value().stream()
243 .filter(x -> x.lastComponent().getClass().equals(cls))
244 .filter(consumerMap::containsKey)
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700245 .collect(Collectors.toList());
246 }
Sho SHIMIZUd29847f2015-08-13 09:10:59 -0700247
248 /**
249 * Abort the transaction.
250 *
251 * @param tx transaction context
252 * @return always false
253 */
254 private boolean abortTransaction(TransactionContext tx) {
255 tx.abort();
256 return false;
257 }
258
259 /**
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700260 * Appends the values to the existing values associated with the specified key.
Sho SHIMIZU4568c412015-08-21 16:39:07 -0700261 * If the map already has all the given values, appending will not happen.
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700262 *
263 * @param map map holding multiple values for a key
264 * @param key key specifying values
265 * @param values values to be appended
266 * @param <K> type of the key
267 * @param <V> type of the element of the list
268 * @return true if the operation succeeds, false otherwise.
269 */
Sho SHIMIZU2a1b2332015-08-18 22:40:12 -0700270 private <K, V> boolean appendValues(TransactionalMap<K, List<V>> map, K key, List<V> values) {
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700271 List<V> oldValues = map.get(key);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700272 if (oldValues == null) {
Sho SHIMIZU4568c412015-08-21 16:39:07 -0700273 return map.replace(key, oldValues, new ArrayList<>(values));
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700274 }
275
Sho SHIMIZU4568c412015-08-21 16:39:07 -0700276 LinkedHashSet<V> oldSet = new LinkedHashSet<>(oldValues);
277 if (oldSet.containsAll(values)) {
278 // don't write to map because all values are already stored
279 return true;
280 }
281
282 oldSet.addAll(values);
283 return map.replace(key, oldValues, new ArrayList<>(oldSet));
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700284 }
285
286 /**
Sho SHIMIZUba1f83b2015-10-14 08:11:20 -0700287 * Removes the values from the existing values associated with the specified key.
Sho SHIMIZU5618ee52015-08-21 17:19:44 -0700288 * If the map doesn't contain the given values, removal will not happen.
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700289 *
290 * @param map map holding multiple values for a key
291 * @param key key specifying values
292 * @param values values to be removed
293 * @param <K> type of the key
294 * @param <V> type of the element of the list
295 * @return true if the operation succeeds, false otherwise
296 */
297 private <K, V> boolean removeValues(TransactionalMap<K, List<V>> map, K key, List<V> values) {
298 List<V> oldValues = map.get(key);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700299 if (oldValues == null) {
Sho SHIMIZU5618ee52015-08-21 17:19:44 -0700300 return map.replace(key, oldValues, new ArrayList<>());
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700301 }
302
Sho SHIMIZU5618ee52015-08-21 17:19:44 -0700303 LinkedHashSet<V> oldSet = new LinkedHashSet<>(oldValues);
304 if (values.stream().allMatch(x -> !oldSet.contains(x))) {
305 // don't write map because none of the values are stored
306 return true;
307 }
308
309 oldSet.removeAll(values);
310 return map.replace(key, oldValues, new ArrayList<>(oldSet));
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700311 }
312
313 /**
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700314 * Checks if the specified resource is registered as a child of a resource in the map.
315 *
316 * @param map map storing parent - child relationship of resources
317 * @param resource resource to be checked
318 * @return true if the resource is registered, false otherwise.
319 */
320 private boolean isRegistered(TransactionalMap<ResourcePath, List<ResourcePath>> map, ResourcePath resource) {
321 // root is always regarded to be registered
Sho SHIMIZU01120782015-08-21 15:48:43 -0700322 if (resource.isRoot()) {
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700323 return true;
324 }
325
326 List<ResourcePath> value = map.get(resource.parent().get());
327 return value != null && value.contains(resource);
328 }
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700329}