blob: 8728c5d3313c84ff2ccf8e25f5f2d1474d2f63ac [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;
32import org.onosproject.store.service.TransactionException;
33import org.onosproject.store.service.TransactionalMap;
34import org.onosproject.store.service.Versioned;
35import org.slf4j.Logger;
36import org.slf4j.LoggerFactory;
37
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070038import java.util.ArrayList;
39import java.util.Arrays;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070040import java.util.Collection;
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070041import java.util.Collections;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070042import java.util.Iterator;
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070043import java.util.LinkedHashSet;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070044import java.util.List;
45import java.util.Map;
46import java.util.Optional;
47import java.util.stream.Collectors;
48
49import static com.google.common.base.Preconditions.checkArgument;
50import static com.google.common.base.Preconditions.checkNotNull;
51
52/**
53 * Implementation of ResourceStore using TransactionalMap.
54 */
55@Component(immediate = true, enabled = false)
56@Service
57@Beta
58public class ConsistentResourceStore implements ResourceStore {
59 private static final Logger log = LoggerFactory.getLogger(ConsistentResourceStore.class);
60
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070061 private static final String CONSUMER_MAP = "onos-resource-consumers";
62 private static final String CHILD_MAP = "onos-resource-children";
63 private static final Serializer SERIALIZER = Serializer.using(
64 Arrays.asList(KryoNamespaces.BASIC, KryoNamespaces.API));
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070065
66 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
67 protected StorageService service;
68
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070069 private ConsistentMap<ResourcePath, ResourceConsumer> consumerMap;
70 private ConsistentMap<ResourcePath, List<ResourcePath>> childMap;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070071
72 @Activate
73 public void activate() {
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070074 consumerMap = service.<ResourcePath, ResourceConsumer>consistentMapBuilder()
75 .withName(CONSUMER_MAP)
76 .withSerializer(SERIALIZER)
77 .build();
78 childMap = service.<ResourcePath, List<ResourcePath>>consistentMapBuilder()
79 .withName(CHILD_MAP)
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070080 .withSerializer(SERIALIZER)
81 .build();
82 }
83
84 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -070085 public Optional<ResourceConsumer> getConsumer(ResourcePath resource) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070086 checkNotNull(resource);
87
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070088 Versioned<ResourceConsumer> consumer = consumerMap.get(resource);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070089 if (consumer == null) {
90 return Optional.empty();
91 }
92
93 return Optional.of(consumer.value());
94 }
95
96 @Override
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070097 public boolean register(ResourcePath resource, List<ResourcePath> children) {
98 checkNotNull(resource);
99 checkNotNull(children);
100
101 TransactionContext tx = service.transactionContextBuilder().build();
102 tx.begin();
103
104 try {
105 TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap =
106 tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
107
108 if (!isRegistered(childTxMap, resource)) {
109 return abortTransaction(tx);
110 }
111
112 if (!appendValue(childTxMap, resource, children)) {
113 return abortTransaction(tx);
114 }
115
116 return commitTransaction(tx);
117 } catch (TransactionException e) {
118 log.error("Exception thrown, abort the transaction", e);
119 return abortTransaction(tx);
120 }
121 }
122
123 @Override
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700124 public boolean unregister(ResourcePath resource, List<ResourcePath> children) {
125 checkNotNull(resource);
126 checkNotNull(children);
127
128 TransactionContext tx = service.transactionContextBuilder().build();
129 tx.begin();
130
131 try {
132 TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap =
133 tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
134 TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap =
135 tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER);
136
137 // even if one of the resources is allocated to a consumer,
138 // all unregistrations are regarded as failure
139 if (children.stream().anyMatch(x -> consumerTxMap.get(x) != null)) {
140 return abortTransaction(tx);
141 }
142
143 if (!removeValues(childTxMap, resource, children)) {
144 return abortTransaction(tx);
145 }
146
147 return commitTransaction(tx);
148 } catch (TransactionException e) {
149 log.error("Exception thrown, abort the transaction", e);
150 return abortTransaction(tx);
151 }
152 }
153
154 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700155 public boolean allocate(List<ResourcePath> resources, ResourceConsumer consumer) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700156 checkNotNull(resources);
157 checkNotNull(consumer);
158
159 TransactionContext tx = service.transactionContextBuilder().build();
160 tx.begin();
161
162 try {
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700163 TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap =
164 tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
165 TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap =
166 tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER);
167
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700168 for (ResourcePath resource: resources) {
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700169 if (!isRegistered(childTxMap, resource)) {
170 return abortTransaction(tx);
171 }
172
173 ResourceConsumer oldValue = consumerTxMap.put(resource, consumer);
174 if (oldValue != null) {
Sho SHIMIZUd29847f2015-08-13 09:10:59 -0700175 return abortTransaction(tx);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700176 }
177 }
Sho SHIMIZUd29847f2015-08-13 09:10:59 -0700178
179 return commitTransaction(tx);
Sho SHIMIZU264e4b72015-08-12 12:22:14 -0700180 } catch (TransactionException e) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700181 log.error("Exception thrown, abort the transaction", e);
Sho SHIMIZUd29847f2015-08-13 09:10:59 -0700182 return abortTransaction(tx);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700183 }
184 }
185
186 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700187 public boolean release(List<ResourcePath> resources, List<ResourceConsumer> consumers) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700188 checkNotNull(resources);
189 checkNotNull(consumers);
190 checkArgument(resources.size() == consumers.size());
191
192 TransactionContext tx = service.transactionContextBuilder().build();
193 tx.begin();
194
195 try {
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700196 TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap =
197 tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER);
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700198 Iterator<ResourcePath> resourceIte = resources.iterator();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700199 Iterator<ResourceConsumer> consumerIte = consumers.iterator();
200
201 while (resourceIte.hasNext() && consumerIte.hasNext()) {
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700202 ResourcePath resource = resourceIte.next();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700203 ResourceConsumer consumer = consumerIte.next();
204
205 // if this single release fails (because the resource is allocated to another consumer,
206 // the whole release fails
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700207 if (!consumerTxMap.remove(resource, consumer)) {
Sho SHIMIZUd29847f2015-08-13 09:10:59 -0700208 return abortTransaction(tx);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700209 }
210 }
211
Sho SHIMIZUd29847f2015-08-13 09:10:59 -0700212 return commitTransaction(tx);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700213 } catch (TransactionException e) {
214 log.error("Exception thrown, abort the transaction", e);
Sho SHIMIZUd29847f2015-08-13 09:10:59 -0700215 return abortTransaction(tx);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700216 }
217 }
218
219 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700220 public Collection<ResourcePath> getResources(ResourceConsumer consumer) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700221 checkNotNull(consumer);
222
223 // NOTE: getting all entries may become performance bottleneck
224 // TODO: revisit for better backend data structure
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700225 return consumerMap.entrySet().stream()
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700226 .filter(x -> x.getValue().value().equals(consumer))
227 .map(Map.Entry::getKey)
228 .collect(Collectors.toList());
229 }
230
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700231 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700232 public <T> Collection<ResourcePath> getAllocatedResources(ResourcePath parent, Class<T> cls) {
233 checkNotNull(parent);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700234 checkNotNull(cls);
235
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700236 Versioned<List<ResourcePath>> children = childMap.get(parent);
237 if (children == null) {
238 return Collections.emptyList();
239 }
240
241 return children.value().stream()
242 .filter(x -> x.lastComponent().getClass().equals(cls))
243 .filter(consumerMap::containsKey)
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700244 .collect(Collectors.toList());
245 }
Sho SHIMIZUd29847f2015-08-13 09:10:59 -0700246
247 /**
248 * Abort the transaction.
249 *
250 * @param tx transaction context
251 * @return always false
252 */
253 private boolean abortTransaction(TransactionContext tx) {
254 tx.abort();
255 return false;
256 }
257
258 /**
259 * Commit the transaction.
260 *
261 * @param tx transaction context
262 * @return always true
263 */
264 private boolean commitTransaction(TransactionContext tx) {
265 tx.commit();
266 return true;
267 }
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700268
269 /**
270 * Appends the values to the existing values associated with the specified key.
271 *
272 * @param map map holding multiple values for a key
273 * @param key key specifying values
274 * @param values values to be appended
275 * @param <K> type of the key
276 * @param <V> type of the element of the list
277 * @return true if the operation succeeds, false otherwise.
278 */
279 private <K, V> boolean appendValue(TransactionalMap<K, List<V>> map, K key, List<V> values) {
280 List<V> oldValues = map.get(key);
281 List<V> newValues;
282 if (oldValues == null) {
283 newValues = new ArrayList<>(values);
284 } else {
285 LinkedHashSet<V> newSet = new LinkedHashSet<>(oldValues);
286 newSet.addAll(values);
287 newValues = new ArrayList<>(newSet);
288 }
289
290 return map.replace(key, oldValues, newValues);
291 }
292
293 /**
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700294 * Removes teh values from the existing values associated with the specified key.
295 *
296 * @param map map holding multiple values for a key
297 * @param key key specifying values
298 * @param values values to be removed
299 * @param <K> type of the key
300 * @param <V> type of the element of the list
301 * @return true if the operation succeeds, false otherwise
302 */
303 private <K, V> boolean removeValues(TransactionalMap<K, List<V>> map, K key, List<V> values) {
304 List<V> oldValues = map.get(key);
305 List<V> newValues;
306 if (oldValues == null) {
307 newValues = new ArrayList<>();
308 } else {
309 LinkedHashSet<V> newSet = new LinkedHashSet<>(oldValues);
310 newSet.removeAll(values);
311 newValues = new ArrayList<>(newSet);
312 }
313
314 return map.replace(key, oldValues, newValues);
315 }
316
317 /**
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700318 * Checks if the specified resource is registered as a child of a resource in the map.
319 *
320 * @param map map storing parent - child relationship of resources
321 * @param resource resource to be checked
322 * @return true if the resource is registered, false otherwise.
323 */
324 private boolean isRegistered(TransactionalMap<ResourcePath, List<ResourcePath>> map, ResourcePath resource) {
325 // root is always regarded to be registered
326 if (!resource.parent().isPresent()) {
327 return true;
328 }
329
330 List<ResourcePath> value = map.get(resource.parent().get());
331 return value != null && value.contains(resource);
332 }
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700333}