blob: 52cf210b7ec4e7fdd893c626f5c2f9889418419a [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
Jonathan Hart46bf89b2017-02-27 15:56:42 -080019import com.google.common.collect.Maps;
20import org.onlab.util.Tools;
21import org.onosproject.store.primitives.MapUpdate;
22import org.onosproject.store.primitives.TransactionId;
23import org.onosproject.store.service.AsyncConsistentMap;
24import org.onosproject.store.service.MapEvent;
25import org.onosproject.store.service.MapEventListener;
26import org.onosproject.store.service.TransactionLog;
27import org.onosproject.store.service.Version;
28import org.onosproject.store.service.Versioned;
29
Madan Jampani551d0d22016-02-01 12:51:48 -080030import java.util.Collection;
31import java.util.Map;
32import java.util.Map.Entry;
33import java.util.Set;
34import java.util.concurrent.CompletableFuture;
Madan Jampani0463cf92016-05-04 14:46:08 -070035import java.util.concurrent.Executor;
Madan Jampani551d0d22016-02-01 12:51:48 -080036import java.util.function.BiFunction;
Madan Jampani1d3b6172016-04-28 13:22:57 -070037import java.util.function.Consumer;
Madan Jampani551d0d22016-02-01 12:51:48 -080038import java.util.function.Function;
39import java.util.function.Predicate;
40import java.util.stream.Collectors;
41
Madan Jampani551d0d22016-02-01 12:51:48 -080042/**
43 * An {@code AsyncConsistentMap} that maps its operations to operations on a
44 * differently typed {@code AsyncConsistentMap} by transcoding operation inputs and outputs.
45 *
46 * @param <K2> key type of other map
47 * @param <V2> value type of other map
48 * @param <K1> key type of this map
49 * @param <V1> value type of this map
50 */
51public class TranscodingAsyncConsistentMap<K1, V1, K2, V2> implements AsyncConsistentMap<K1, V1> {
52
53 private final AsyncConsistentMap<K2, V2> backingMap;
54 private final Function<K1, K2> keyEncoder;
55 private final Function<K2, K1> keyDecoder;
56 private final Function<V2, V1> valueDecoder;
57 private final Function<V1, V2> valueEncoder;
58 private final Function<Versioned<V2>, Versioned<V1>> versionedValueTransform;
59 private final Map<MapEventListener<K1, V1>, InternalBackingMapEventListener> listeners =
60 Maps.newIdentityHashMap();
61
62 public TranscodingAsyncConsistentMap(AsyncConsistentMap<K2, V2> backingMap,
63 Function<K1, K2> keyEncoder,
64 Function<K2, K1> keyDecoder,
65 Function<V1, V2> valueEncoder,
66 Function<V2, V1> valueDecoder) {
67 this.backingMap = backingMap;
68 this.keyEncoder = k -> k == null ? null : keyEncoder.apply(k);
Madan Jampani1e8e89c2016-02-02 08:53:56 -080069 this.keyDecoder = k -> k == null ? null : keyDecoder.apply(k);
Madan Jampani551d0d22016-02-01 12:51:48 -080070 this.valueEncoder = v -> v == null ? null : valueEncoder.apply(v);
Madan Jampani1e8e89c2016-02-02 08:53:56 -080071 this.valueDecoder = v -> v == null ? null : valueDecoder.apply(v);
Madan Jampani551d0d22016-02-01 12:51:48 -080072 this.versionedValueTransform = v -> v == null ? null : v.map(valueDecoder);
73 }
74
75 @Override
76 public String name() {
77 return backingMap.name();
78 }
79
80 @Override
81 public CompletableFuture<Integer> size() {
82 return backingMap.size();
83 }
84
85 @Override
86 public CompletableFuture<Boolean> containsKey(K1 key) {
Madan Jampanie9c9a712016-04-18 10:58:04 -070087 try {
88 return backingMap.containsKey(keyEncoder.apply(key));
89 } catch (Exception e) {
90 return Tools.exceptionalFuture(e);
91 }
Madan Jampani551d0d22016-02-01 12:51:48 -080092 }
93
94 @Override
95 public CompletableFuture<Boolean> containsValue(V1 value) {
Madan Jampanie9c9a712016-04-18 10:58:04 -070096 try {
97 return backingMap.containsValue(valueEncoder.apply(value));
98 } catch (Exception e) {
99 return Tools.exceptionalFuture(e);
100 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800101 }
102
103 @Override
104 public CompletableFuture<Versioned<V1>> get(K1 key) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700105 try {
106 return backingMap.get(keyEncoder.apply(key)).thenApply(versionedValueTransform);
107 } catch (Exception e) {
108 return Tools.exceptionalFuture(e);
109 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800110 }
111
112 @Override
Jordan Haltermanf6272442017-04-20 02:18:08 -0700113 public CompletableFuture<Versioned<V1>> getOrDefault(K1 key, V1 defaultValue) {
114 try {
115 return backingMap.getOrDefault(keyEncoder.apply(key), valueEncoder.apply(defaultValue))
116 .thenApply(versionedValueTransform);
117 } catch (Exception e) {
118 return Tools.exceptionalFuture(e);
119 }
120 }
121
122 @Override
Madan Jampani551d0d22016-02-01 12:51:48 -0800123 public CompletableFuture<Versioned<V1>> computeIf(K1 key,
124 Predicate<? super V1> condition,
125 BiFunction<? super K1, ? super V1, ? extends V1> remappingFunction) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700126 try {
127 return backingMap.computeIf(keyEncoder.apply(key),
128 v -> condition.test(valueDecoder.apply(v)),
129 (k, v) -> valueEncoder.apply(remappingFunction.apply(keyDecoder.apply(k),
130 valueDecoder.apply(v))))
131 .thenApply(versionedValueTransform);
132 } catch (Exception e) {
133 return Tools.exceptionalFuture(e);
134 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800135 }
136
137 @Override
138 public CompletableFuture<Versioned<V1>> put(K1 key, V1 value) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700139 try {
140 return backingMap.put(keyEncoder.apply(key), valueEncoder.apply(value))
141 .thenApply(versionedValueTransform);
142 } catch (Exception e) {
143 return Tools.exceptionalFuture(e);
144 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800145 }
146
147 @Override
148 public CompletableFuture<Versioned<V1>> putAndGet(K1 key, V1 value) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700149 try {
150 return backingMap.putAndGet(keyEncoder.apply(key), valueEncoder.apply(value))
151 .thenApply(versionedValueTransform);
152 } catch (Exception e) {
153 return Tools.exceptionalFuture(e);
154 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800155 }
156
157 @Override
158 public CompletableFuture<Versioned<V1>> remove(K1 key) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700159 try {
160 return backingMap.remove(keyEncoder.apply(key)).thenApply(versionedValueTransform);
161 } catch (Exception e) {
162 return Tools.exceptionalFuture(e);
163 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800164 }
165
166 @Override
167 public CompletableFuture<Void> clear() {
168 return backingMap.clear();
169 }
170
171 @Override
172 public CompletableFuture<Set<K1>> keySet() {
173 return backingMap.keySet()
174 .thenApply(s -> s.stream().map(keyDecoder).collect(Collectors.toSet()));
175 }
176
177 @Override
178 public CompletableFuture<Collection<Versioned<V1>>> values() {
179 return backingMap.values()
180 .thenApply(c -> c.stream().map(versionedValueTransform).collect(Collectors.toList()));
181 }
182
183 @Override
184 public CompletableFuture<Set<Entry<K1, Versioned<V1>>>> entrySet() {
185 return backingMap.entrySet()
186 .thenApply(s -> s.stream()
187 .map(e -> Maps.immutableEntry(keyDecoder.apply(e.getKey()),
188 versionedValueTransform.apply(e.getValue())))
189 .collect(Collectors.toSet()));
190 }
191
192 @Override
193 public CompletableFuture<Versioned<V1>> putIfAbsent(K1 key, V1 value) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700194 try {
195 return backingMap.putIfAbsent(keyEncoder.apply(key), valueEncoder.apply(value))
196 .thenApply(versionedValueTransform);
197 } catch (Exception e) {
198 return Tools.exceptionalFuture(e);
199 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800200 }
201
202 @Override
203 public CompletableFuture<Boolean> remove(K1 key, V1 value) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700204 try {
205 return backingMap.remove(keyEncoder.apply(key), valueEncoder.apply(value));
206 } catch (Exception e) {
207 return Tools.exceptionalFuture(e);
208 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800209 }
210
211 @Override
212 public CompletableFuture<Boolean> remove(K1 key, long version) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700213 try {
214 return backingMap.remove(keyEncoder.apply(key), version);
215 } catch (Exception e) {
216 return Tools.exceptionalFuture(e);
217 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800218 }
219
220 @Override
221 public CompletableFuture<Versioned<V1>> replace(K1 key, V1 value) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700222 try {
223 return backingMap.replace(keyEncoder.apply(key), valueEncoder.apply(value))
224 .thenApply(versionedValueTransform);
225 } catch (Exception e) {
226 return Tools.exceptionalFuture(e);
227 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800228 }
229
230 @Override
231 public CompletableFuture<Boolean> replace(K1 key, V1 oldValue, V1 newValue) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700232 try {
233 return backingMap.replace(keyEncoder.apply(key),
234 valueEncoder.apply(oldValue),
235 valueEncoder.apply(newValue));
236 } catch (Exception e) {
237 return Tools.exceptionalFuture(e);
238 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800239 }
240
241 @Override
242 public CompletableFuture<Boolean> replace(K1 key, long oldVersion, V1 newValue) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700243 try {
244 return backingMap.replace(keyEncoder.apply(key), oldVersion, valueEncoder.apply(newValue));
245 } catch (Exception e) {
246 return Tools.exceptionalFuture(e);
247 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800248 }
249
250 @Override
Madan Jampani0463cf92016-05-04 14:46:08 -0700251 public CompletableFuture<Void> addListener(MapEventListener<K1, V1> listener, Executor executor) {
Madan Jampani551d0d22016-02-01 12:51:48 -0800252 synchronized (listeners) {
253 InternalBackingMapEventListener backingMapListener =
254 listeners.computeIfAbsent(listener, k -> new InternalBackingMapEventListener(listener));
Madan Jampani0463cf92016-05-04 14:46:08 -0700255 return backingMap.addListener(backingMapListener, executor);
Madan Jampani551d0d22016-02-01 12:51:48 -0800256 }
257 }
258
259 @Override
260 public CompletableFuture<Void> removeListener(MapEventListener<K1, V1> listener) {
Jonathan Hart46bf89b2017-02-27 15:56:42 -0800261 synchronized (listeners) {
262 InternalBackingMapEventListener backingMapListener = listeners.remove(listener);
263 if (backingMapListener != null) {
264 return backingMap.removeListener(backingMapListener);
265 } else {
266 return CompletableFuture.completedFuture(null);
267 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800268 }
269 }
270
Madan Jampani74da78b2016-02-09 21:18:36 -0800271 @Override
Jordan Halterman948d6592017-04-20 17:18:24 -0700272 public CompletableFuture<Version> begin(TransactionId transactionId) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700273 try {
Jordan Halterman948d6592017-04-20 17:18:24 -0700274 return backingMap.begin(transactionId);
275 } catch (Exception e) {
276 return Tools.exceptionalFuture(e);
277 }
278 }
279
280 @Override
281 public CompletableFuture<Boolean> prepare(TransactionLog<MapUpdate<K1, V1>> transactionLog) {
282 try {
283 return backingMap.prepare(transactionLog.map(record -> record.map(keyEncoder, valueEncoder)));
284 } catch (Exception e) {
285 return Tools.exceptionalFuture(e);
286 }
287 }
288
289 @Override
290 public CompletableFuture<Boolean> prepareAndCommit(TransactionLog<MapUpdate<K1, V1>> transactionLog) {
291 try {
292 return backingMap.prepareAndCommit(transactionLog.map(record -> record.map(keyEncoder, valueEncoder)));
Madan Jampanie9c9a712016-04-18 10:58:04 -0700293 } catch (Exception e) {
294 return Tools.exceptionalFuture(e);
295 }
Madan Jampani74da78b2016-02-09 21:18:36 -0800296 }
297
298 @Override
299 public CompletableFuture<Void> commit(TransactionId transactionId) {
Jordan Halterman948d6592017-04-20 17:18:24 -0700300 try {
301 return backingMap.commit(transactionId);
302 } catch (Exception e) {
303 return Tools.exceptionalFuture(e);
304 }
Madan Jampani74da78b2016-02-09 21:18:36 -0800305 }
306
307 @Override
308 public CompletableFuture<Void> rollback(TransactionId transactionId) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700309 try {
Jordan Halterman948d6592017-04-20 17:18:24 -0700310 return backingMap.rollback(transactionId);
Madan Jampanie9c9a712016-04-18 10:58:04 -0700311 } catch (Exception e) {
312 return Tools.exceptionalFuture(e);
313 }
Madan Jampani542d9e22016-04-05 15:39:55 -0700314 }
315
Madan Jampani1d3b6172016-04-28 13:22:57 -0700316 @Override
317 public void addStatusChangeListener(Consumer<Status> listener) {
318 backingMap.addStatusChangeListener(listener);
319 }
320
321 @Override
322 public void removeStatusChangeListener(Consumer<Status> listener) {
323 backingMap.removeStatusChangeListener(listener);
324 }
325
326 @Override
327 public Collection<Consumer<Status>> statusChangeListeners() {
328 return backingMap.statusChangeListeners();
329 }
330
Madan Jampani551d0d22016-02-01 12:51:48 -0800331 private class InternalBackingMapEventListener implements MapEventListener<K2, V2> {
332
333 private final MapEventListener<K1, V1> listener;
334
335 InternalBackingMapEventListener(MapEventListener<K1, V1> listener) {
336 this.listener = listener;
337 }
338
339 @Override
340 public void event(MapEvent<K2, V2> event) {
341 listener.event(new MapEvent<K1, V1>(event.name(),
342 keyDecoder.apply(event.key()),
Madan Jampania9673fd2016-02-02 13:01:29 -0800343 event.newValue() != null ? event.newValue().map(valueDecoder) : null,
344 event.oldValue() != null ? event.oldValue().map(valueDecoder) : null));
Madan Jampani551d0d22016-02-01 12:51:48 -0800345 }
346 }
347}