blob: c2f167a4b7ff9b3904ad2807d9c9519c8538158f [file] [log] [blame]
Madan Jampani551d0d22016-02-01 12:51:48 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Madan Jampani551d0d22016-02-01 12:51:48 -08003 *
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 */
16
17package org.onosproject.store.primitives.impl;
18
19import java.util.Collection;
20import java.util.Map;
21import java.util.Map.Entry;
22import java.util.Set;
23import java.util.concurrent.CompletableFuture;
Madan Jampani0463cf92016-05-04 14:46:08 -070024import java.util.concurrent.Executor;
Madan Jampani551d0d22016-02-01 12:51:48 -080025import java.util.function.BiFunction;
Madan Jampani1d3b6172016-04-28 13:22:57 -070026import java.util.function.Consumer;
Madan Jampani551d0d22016-02-01 12:51:48 -080027import java.util.function.Function;
28import java.util.function.Predicate;
29import java.util.stream.Collectors;
30
Madan Jampanie9c9a712016-04-18 10:58:04 -070031import org.onlab.util.Tools;
Jordan Halterman948d6592017-04-20 17:18:24 -070032import org.onosproject.store.primitives.MapUpdate;
Madan Jampani74da78b2016-02-09 21:18:36 -080033import org.onosproject.store.primitives.TransactionId;
Madan Jampani551d0d22016-02-01 12:51:48 -080034import org.onosproject.store.service.AsyncConsistentMap;
35import org.onosproject.store.service.MapEvent;
36import org.onosproject.store.service.MapEventListener;
Jordan Halterman948d6592017-04-20 17:18:24 -070037import org.onosproject.store.service.TransactionLog;
38import org.onosproject.store.service.Version;
Madan Jampani551d0d22016-02-01 12:51:48 -080039import org.onosproject.store.service.Versioned;
Madan Jampani0463cf92016-05-04 14:46:08 -070040
Madan Jampani551d0d22016-02-01 12:51:48 -080041import com.google.common.collect.Maps;
42
43/**
44 * An {@code AsyncConsistentMap} that maps its operations to operations on a
45 * differently typed {@code AsyncConsistentMap} by transcoding operation inputs and outputs.
46 *
47 * @param <K2> key type of other map
48 * @param <V2> value type of other map
49 * @param <K1> key type of this map
50 * @param <V1> value type of this map
51 */
52public class TranscodingAsyncConsistentMap<K1, V1, K2, V2> implements AsyncConsistentMap<K1, V1> {
53
54 private final AsyncConsistentMap<K2, V2> backingMap;
55 private final Function<K1, K2> keyEncoder;
56 private final Function<K2, K1> keyDecoder;
57 private final Function<V2, V1> valueDecoder;
58 private final Function<V1, V2> valueEncoder;
59 private final Function<Versioned<V2>, Versioned<V1>> versionedValueTransform;
60 private final Map<MapEventListener<K1, V1>, InternalBackingMapEventListener> listeners =
61 Maps.newIdentityHashMap();
62
63 public TranscodingAsyncConsistentMap(AsyncConsistentMap<K2, V2> backingMap,
64 Function<K1, K2> keyEncoder,
65 Function<K2, K1> keyDecoder,
66 Function<V1, V2> valueEncoder,
67 Function<V2, V1> valueDecoder) {
68 this.backingMap = backingMap;
69 this.keyEncoder = k -> k == null ? null : keyEncoder.apply(k);
Madan Jampani1e8e89c2016-02-02 08:53:56 -080070 this.keyDecoder = k -> k == null ? null : keyDecoder.apply(k);
Madan Jampani551d0d22016-02-01 12:51:48 -080071 this.valueEncoder = v -> v == null ? null : valueEncoder.apply(v);
Madan Jampani1e8e89c2016-02-02 08:53:56 -080072 this.valueDecoder = v -> v == null ? null : valueDecoder.apply(v);
Madan Jampani551d0d22016-02-01 12:51:48 -080073 this.versionedValueTransform = v -> v == null ? null : v.map(valueDecoder);
74 }
75
76 @Override
77 public String name() {
78 return backingMap.name();
79 }
80
81 @Override
82 public CompletableFuture<Integer> size() {
83 return backingMap.size();
84 }
85
86 @Override
87 public CompletableFuture<Boolean> containsKey(K1 key) {
Madan Jampanie9c9a712016-04-18 10:58:04 -070088 try {
89 return backingMap.containsKey(keyEncoder.apply(key));
90 } catch (Exception e) {
91 return Tools.exceptionalFuture(e);
92 }
Madan Jampani551d0d22016-02-01 12:51:48 -080093 }
94
95 @Override
96 public CompletableFuture<Boolean> containsValue(V1 value) {
Madan Jampanie9c9a712016-04-18 10:58:04 -070097 try {
98 return backingMap.containsValue(valueEncoder.apply(value));
99 } catch (Exception e) {
100 return Tools.exceptionalFuture(e);
101 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800102 }
103
104 @Override
105 public CompletableFuture<Versioned<V1>> get(K1 key) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700106 try {
107 return backingMap.get(keyEncoder.apply(key)).thenApply(versionedValueTransform);
108 } catch (Exception e) {
109 return Tools.exceptionalFuture(e);
110 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800111 }
112
113 @Override
Jordan Haltermanf6272442017-04-20 02:18:08 -0700114 public CompletableFuture<Versioned<V1>> getOrDefault(K1 key, V1 defaultValue) {
115 try {
116 return backingMap.getOrDefault(keyEncoder.apply(key), valueEncoder.apply(defaultValue))
117 .thenApply(versionedValueTransform);
118 } catch (Exception e) {
119 return Tools.exceptionalFuture(e);
120 }
121 }
122
123 @Override
Madan Jampani551d0d22016-02-01 12:51:48 -0800124 public CompletableFuture<Versioned<V1>> computeIf(K1 key,
125 Predicate<? super V1> condition,
126 BiFunction<? super K1, ? super V1, ? extends V1> remappingFunction) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700127 try {
128 return backingMap.computeIf(keyEncoder.apply(key),
129 v -> condition.test(valueDecoder.apply(v)),
130 (k, v) -> valueEncoder.apply(remappingFunction.apply(keyDecoder.apply(k),
131 valueDecoder.apply(v))))
132 .thenApply(versionedValueTransform);
133 } catch (Exception e) {
134 return Tools.exceptionalFuture(e);
135 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800136 }
137
138 @Override
139 public CompletableFuture<Versioned<V1>> put(K1 key, V1 value) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700140 try {
141 return backingMap.put(keyEncoder.apply(key), valueEncoder.apply(value))
142 .thenApply(versionedValueTransform);
143 } catch (Exception e) {
144 return Tools.exceptionalFuture(e);
145 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800146 }
147
148 @Override
149 public CompletableFuture<Versioned<V1>> putAndGet(K1 key, V1 value) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700150 try {
151 return backingMap.putAndGet(keyEncoder.apply(key), valueEncoder.apply(value))
152 .thenApply(versionedValueTransform);
153 } catch (Exception e) {
154 return Tools.exceptionalFuture(e);
155 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800156 }
157
158 @Override
159 public CompletableFuture<Versioned<V1>> remove(K1 key) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700160 try {
161 return backingMap.remove(keyEncoder.apply(key)).thenApply(versionedValueTransform);
162 } catch (Exception e) {
163 return Tools.exceptionalFuture(e);
164 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800165 }
166
167 @Override
168 public CompletableFuture<Void> clear() {
169 return backingMap.clear();
170 }
171
172 @Override
173 public CompletableFuture<Set<K1>> keySet() {
174 return backingMap.keySet()
175 .thenApply(s -> s.stream().map(keyDecoder).collect(Collectors.toSet()));
176 }
177
178 @Override
179 public CompletableFuture<Collection<Versioned<V1>>> values() {
180 return backingMap.values()
181 .thenApply(c -> c.stream().map(versionedValueTransform).collect(Collectors.toList()));
182 }
183
184 @Override
185 public CompletableFuture<Set<Entry<K1, Versioned<V1>>>> entrySet() {
186 return backingMap.entrySet()
187 .thenApply(s -> s.stream()
188 .map(e -> Maps.immutableEntry(keyDecoder.apply(e.getKey()),
189 versionedValueTransform.apply(e.getValue())))
190 .collect(Collectors.toSet()));
191 }
192
193 @Override
194 public CompletableFuture<Versioned<V1>> putIfAbsent(K1 key, V1 value) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700195 try {
196 return backingMap.putIfAbsent(keyEncoder.apply(key), valueEncoder.apply(value))
197 .thenApply(versionedValueTransform);
198 } catch (Exception e) {
199 return Tools.exceptionalFuture(e);
200 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800201 }
202
203 @Override
204 public CompletableFuture<Boolean> remove(K1 key, V1 value) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700205 try {
206 return backingMap.remove(keyEncoder.apply(key), valueEncoder.apply(value));
207 } catch (Exception e) {
208 return Tools.exceptionalFuture(e);
209 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800210 }
211
212 @Override
213 public CompletableFuture<Boolean> remove(K1 key, long version) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700214 try {
215 return backingMap.remove(keyEncoder.apply(key), version);
216 } catch (Exception e) {
217 return Tools.exceptionalFuture(e);
218 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800219 }
220
221 @Override
222 public CompletableFuture<Versioned<V1>> replace(K1 key, V1 value) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700223 try {
224 return backingMap.replace(keyEncoder.apply(key), valueEncoder.apply(value))
225 .thenApply(versionedValueTransform);
226 } catch (Exception e) {
227 return Tools.exceptionalFuture(e);
228 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800229 }
230
231 @Override
232 public CompletableFuture<Boolean> replace(K1 key, V1 oldValue, V1 newValue) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700233 try {
234 return backingMap.replace(keyEncoder.apply(key),
235 valueEncoder.apply(oldValue),
236 valueEncoder.apply(newValue));
237 } catch (Exception e) {
238 return Tools.exceptionalFuture(e);
239 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800240 }
241
242 @Override
243 public CompletableFuture<Boolean> replace(K1 key, long oldVersion, V1 newValue) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700244 try {
245 return backingMap.replace(keyEncoder.apply(key), oldVersion, valueEncoder.apply(newValue));
246 } catch (Exception e) {
247 return Tools.exceptionalFuture(e);
248 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800249 }
250
251 @Override
Madan Jampani0463cf92016-05-04 14:46:08 -0700252 public CompletableFuture<Void> addListener(MapEventListener<K1, V1> listener, Executor executor) {
Madan Jampani551d0d22016-02-01 12:51:48 -0800253 synchronized (listeners) {
254 InternalBackingMapEventListener backingMapListener =
255 listeners.computeIfAbsent(listener, k -> new InternalBackingMapEventListener(listener));
Madan Jampani0463cf92016-05-04 14:46:08 -0700256 return backingMap.addListener(backingMapListener, executor);
Madan Jampani551d0d22016-02-01 12:51:48 -0800257 }
258 }
259
260 @Override
261 public CompletableFuture<Void> removeListener(MapEventListener<K1, V1> listener) {
262 InternalBackingMapEventListener backingMapListener = listeners.remove(listener);
263 if (backingMapListener != null) {
264 return backingMap.removeListener(backingMapListener);
265 } else {
266 return CompletableFuture.completedFuture(null);
267 }
268 }
269
Madan Jampani74da78b2016-02-09 21:18:36 -0800270 @Override
Jordan Halterman948d6592017-04-20 17:18:24 -0700271 public CompletableFuture<Version> begin(TransactionId transactionId) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700272 try {
Jordan Halterman948d6592017-04-20 17:18:24 -0700273 return backingMap.begin(transactionId);
274 } catch (Exception e) {
275 return Tools.exceptionalFuture(e);
276 }
277 }
278
279 @Override
280 public CompletableFuture<Boolean> prepare(TransactionLog<MapUpdate<K1, V1>> transactionLog) {
281 try {
282 return backingMap.prepare(transactionLog.map(record -> record.map(keyEncoder, valueEncoder)));
283 } catch (Exception e) {
284 return Tools.exceptionalFuture(e);
285 }
286 }
287
288 @Override
289 public CompletableFuture<Boolean> prepareAndCommit(TransactionLog<MapUpdate<K1, V1>> transactionLog) {
290 try {
291 return backingMap.prepareAndCommit(transactionLog.map(record -> record.map(keyEncoder, valueEncoder)));
Madan Jampanie9c9a712016-04-18 10:58:04 -0700292 } catch (Exception e) {
293 return Tools.exceptionalFuture(e);
294 }
Madan Jampani74da78b2016-02-09 21:18:36 -0800295 }
296
297 @Override
298 public CompletableFuture<Void> commit(TransactionId transactionId) {
Jordan Halterman948d6592017-04-20 17:18:24 -0700299 try {
300 return backingMap.commit(transactionId);
301 } catch (Exception e) {
302 return Tools.exceptionalFuture(e);
303 }
Madan Jampani74da78b2016-02-09 21:18:36 -0800304 }
305
306 @Override
307 public CompletableFuture<Void> rollback(TransactionId transactionId) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700308 try {
Jordan Halterman948d6592017-04-20 17:18:24 -0700309 return backingMap.rollback(transactionId);
Madan Jampanie9c9a712016-04-18 10:58:04 -0700310 } catch (Exception e) {
311 return Tools.exceptionalFuture(e);
312 }
Madan Jampani542d9e22016-04-05 15:39:55 -0700313 }
314
Madan Jampani1d3b6172016-04-28 13:22:57 -0700315 @Override
316 public void addStatusChangeListener(Consumer<Status> listener) {
317 backingMap.addStatusChangeListener(listener);
318 }
319
320 @Override
321 public void removeStatusChangeListener(Consumer<Status> listener) {
322 backingMap.removeStatusChangeListener(listener);
323 }
324
325 @Override
326 public Collection<Consumer<Status>> statusChangeListeners() {
327 return backingMap.statusChangeListeners();
328 }
329
Madan Jampani551d0d22016-02-01 12:51:48 -0800330 private class InternalBackingMapEventListener implements MapEventListener<K2, V2> {
331
332 private final MapEventListener<K1, V1> listener;
333
334 InternalBackingMapEventListener(MapEventListener<K1, V1> listener) {
335 this.listener = listener;
336 }
337
338 @Override
339 public void event(MapEvent<K2, V2> event) {
340 listener.event(new MapEvent<K1, V1>(event.name(),
341 keyDecoder.apply(event.key()),
Madan Jampania9673fd2016-02-02 13:01:29 -0800342 event.newValue() != null ? event.newValue().map(valueDecoder) : null,
343 event.oldValue() != null ? event.oldValue().map(valueDecoder) : null));
Madan Jampani551d0d22016-02-01 12:51:48 -0800344 }
345 }
346}