blob: 0178730fa294219802d2163315b37a789ad73a51 [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;
24import java.util.function.BiFunction;
Madan Jampani1d3b6172016-04-28 13:22:57 -070025import java.util.function.Consumer;
Madan Jampani551d0d22016-02-01 12:51:48 -080026import java.util.function.Function;
27import java.util.function.Predicate;
28import java.util.stream.Collectors;
29
Madan Jampanie9c9a712016-04-18 10:58:04 -070030import org.onlab.util.Tools;
Madan Jampani74da78b2016-02-09 21:18:36 -080031import org.onosproject.store.primitives.TransactionId;
Madan Jampani551d0d22016-02-01 12:51:48 -080032import org.onosproject.store.service.AsyncConsistentMap;
33import org.onosproject.store.service.MapEvent;
34import org.onosproject.store.service.MapEventListener;
Madan Jampani74da78b2016-02-09 21:18:36 -080035import org.onosproject.store.service.MapTransaction;
Madan Jampani551d0d22016-02-01 12:51:48 -080036import org.onosproject.store.service.Versioned;
Madan Jampani551d0d22016-02-01 12:51:48 -080037import com.google.common.collect.Maps;
38
39/**
40 * An {@code AsyncConsistentMap} that maps its operations to operations on a
41 * differently typed {@code AsyncConsistentMap} by transcoding operation inputs and outputs.
42 *
43 * @param <K2> key type of other map
44 * @param <V2> value type of other map
45 * @param <K1> key type of this map
46 * @param <V1> value type of this map
47 */
48public class TranscodingAsyncConsistentMap<K1, V1, K2, V2> implements AsyncConsistentMap<K1, V1> {
49
50 private final AsyncConsistentMap<K2, V2> backingMap;
51 private final Function<K1, K2> keyEncoder;
52 private final Function<K2, K1> keyDecoder;
53 private final Function<V2, V1> valueDecoder;
54 private final Function<V1, V2> valueEncoder;
55 private final Function<Versioned<V2>, Versioned<V1>> versionedValueTransform;
56 private final Map<MapEventListener<K1, V1>, InternalBackingMapEventListener> listeners =
57 Maps.newIdentityHashMap();
58
59 public TranscodingAsyncConsistentMap(AsyncConsistentMap<K2, V2> backingMap,
60 Function<K1, K2> keyEncoder,
61 Function<K2, K1> keyDecoder,
62 Function<V1, V2> valueEncoder,
63 Function<V2, V1> valueDecoder) {
64 this.backingMap = backingMap;
65 this.keyEncoder = k -> k == null ? null : keyEncoder.apply(k);
Madan Jampani1e8e89c2016-02-02 08:53:56 -080066 this.keyDecoder = k -> k == null ? null : keyDecoder.apply(k);
Madan Jampani551d0d22016-02-01 12:51:48 -080067 this.valueEncoder = v -> v == null ? null : valueEncoder.apply(v);
Madan Jampani1e8e89c2016-02-02 08:53:56 -080068 this.valueDecoder = v -> v == null ? null : valueDecoder.apply(v);
Madan Jampani551d0d22016-02-01 12:51:48 -080069 this.versionedValueTransform = v -> v == null ? null : v.map(valueDecoder);
70 }
71
72 @Override
73 public String name() {
74 return backingMap.name();
75 }
76
77 @Override
78 public CompletableFuture<Integer> size() {
79 return backingMap.size();
80 }
81
82 @Override
83 public CompletableFuture<Boolean> containsKey(K1 key) {
Madan Jampanie9c9a712016-04-18 10:58:04 -070084 try {
85 return backingMap.containsKey(keyEncoder.apply(key));
86 } catch (Exception e) {
87 return Tools.exceptionalFuture(e);
88 }
Madan Jampani551d0d22016-02-01 12:51:48 -080089 }
90
91 @Override
92 public CompletableFuture<Boolean> containsValue(V1 value) {
Madan Jampanie9c9a712016-04-18 10:58:04 -070093 try {
94 return backingMap.containsValue(valueEncoder.apply(value));
95 } catch (Exception e) {
96 return Tools.exceptionalFuture(e);
97 }
Madan Jampani551d0d22016-02-01 12:51:48 -080098 }
99
100 @Override
101 public CompletableFuture<Versioned<V1>> get(K1 key) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700102 try {
103 return backingMap.get(keyEncoder.apply(key)).thenApply(versionedValueTransform);
104 } catch (Exception e) {
105 return Tools.exceptionalFuture(e);
106 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800107 }
108
109 @Override
110 public CompletableFuture<Versioned<V1>> computeIf(K1 key,
111 Predicate<? super V1> condition,
112 BiFunction<? super K1, ? super V1, ? extends V1> remappingFunction) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700113 try {
114 return backingMap.computeIf(keyEncoder.apply(key),
115 v -> condition.test(valueDecoder.apply(v)),
116 (k, v) -> valueEncoder.apply(remappingFunction.apply(keyDecoder.apply(k),
117 valueDecoder.apply(v))))
118 .thenApply(versionedValueTransform);
119 } catch (Exception e) {
120 return Tools.exceptionalFuture(e);
121 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800122 }
123
124 @Override
125 public CompletableFuture<Versioned<V1>> put(K1 key, V1 value) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700126 try {
127 return backingMap.put(keyEncoder.apply(key), valueEncoder.apply(value))
128 .thenApply(versionedValueTransform);
129 } catch (Exception e) {
130 return Tools.exceptionalFuture(e);
131 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800132 }
133
134 @Override
135 public CompletableFuture<Versioned<V1>> putAndGet(K1 key, V1 value) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700136 try {
137 return backingMap.putAndGet(keyEncoder.apply(key), valueEncoder.apply(value))
138 .thenApply(versionedValueTransform);
139 } catch (Exception e) {
140 return Tools.exceptionalFuture(e);
141 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800142 }
143
144 @Override
145 public CompletableFuture<Versioned<V1>> remove(K1 key) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700146 try {
147 return backingMap.remove(keyEncoder.apply(key)).thenApply(versionedValueTransform);
148 } catch (Exception e) {
149 return Tools.exceptionalFuture(e);
150 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800151 }
152
153 @Override
154 public CompletableFuture<Void> clear() {
155 return backingMap.clear();
156 }
157
158 @Override
159 public CompletableFuture<Set<K1>> keySet() {
160 return backingMap.keySet()
161 .thenApply(s -> s.stream().map(keyDecoder).collect(Collectors.toSet()));
162 }
163
164 @Override
165 public CompletableFuture<Collection<Versioned<V1>>> values() {
166 return backingMap.values()
167 .thenApply(c -> c.stream().map(versionedValueTransform).collect(Collectors.toList()));
168 }
169
170 @Override
171 public CompletableFuture<Set<Entry<K1, Versioned<V1>>>> entrySet() {
172 return backingMap.entrySet()
173 .thenApply(s -> s.stream()
174 .map(e -> Maps.immutableEntry(keyDecoder.apply(e.getKey()),
175 versionedValueTransform.apply(e.getValue())))
176 .collect(Collectors.toSet()));
177 }
178
179 @Override
180 public CompletableFuture<Versioned<V1>> putIfAbsent(K1 key, V1 value) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700181 try {
182 return backingMap.putIfAbsent(keyEncoder.apply(key), valueEncoder.apply(value))
183 .thenApply(versionedValueTransform);
184 } catch (Exception e) {
185 return Tools.exceptionalFuture(e);
186 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800187 }
188
189 @Override
190 public CompletableFuture<Boolean> remove(K1 key, V1 value) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700191 try {
192 return backingMap.remove(keyEncoder.apply(key), valueEncoder.apply(value));
193 } catch (Exception e) {
194 return Tools.exceptionalFuture(e);
195 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800196 }
197
198 @Override
199 public CompletableFuture<Boolean> remove(K1 key, long version) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700200 try {
201 return backingMap.remove(keyEncoder.apply(key), version);
202 } catch (Exception e) {
203 return Tools.exceptionalFuture(e);
204 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800205 }
206
207 @Override
208 public CompletableFuture<Versioned<V1>> replace(K1 key, V1 value) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700209 try {
210 return backingMap.replace(keyEncoder.apply(key), valueEncoder.apply(value))
211 .thenApply(versionedValueTransform);
212 } catch (Exception e) {
213 return Tools.exceptionalFuture(e);
214 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800215 }
216
217 @Override
218 public CompletableFuture<Boolean> replace(K1 key, V1 oldValue, V1 newValue) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700219 try {
220 return backingMap.replace(keyEncoder.apply(key),
221 valueEncoder.apply(oldValue),
222 valueEncoder.apply(newValue));
223 } catch (Exception e) {
224 return Tools.exceptionalFuture(e);
225 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800226 }
227
228 @Override
229 public CompletableFuture<Boolean> replace(K1 key, long oldVersion, V1 newValue) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700230 try {
231 return backingMap.replace(keyEncoder.apply(key), oldVersion, valueEncoder.apply(newValue));
232 } catch (Exception e) {
233 return Tools.exceptionalFuture(e);
234 }
Madan Jampani551d0d22016-02-01 12:51:48 -0800235 }
236
237 @Override
238 public CompletableFuture<Void> addListener(MapEventListener<K1, V1> listener) {
239 synchronized (listeners) {
240 InternalBackingMapEventListener backingMapListener =
241 listeners.computeIfAbsent(listener, k -> new InternalBackingMapEventListener(listener));
242 return backingMap.addListener(backingMapListener);
243 }
244 }
245
246 @Override
247 public CompletableFuture<Void> removeListener(MapEventListener<K1, V1> listener) {
248 InternalBackingMapEventListener backingMapListener = listeners.remove(listener);
249 if (backingMapListener != null) {
250 return backingMap.removeListener(backingMapListener);
251 } else {
252 return CompletableFuture.completedFuture(null);
253 }
254 }
255
Madan Jampani74da78b2016-02-09 21:18:36 -0800256 @Override
257 public CompletableFuture<Boolean> prepare(MapTransaction<K1, V1> transaction) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700258 try {
259 return backingMap.prepare(transaction.map(keyEncoder, valueEncoder));
260 } catch (Exception e) {
261 return Tools.exceptionalFuture(e);
262 }
Madan Jampani74da78b2016-02-09 21:18:36 -0800263 }
264
265 @Override
266 public CompletableFuture<Void> commit(TransactionId transactionId) {
267 return backingMap.commit(transactionId);
268 }
269
270 @Override
271 public CompletableFuture<Void> rollback(TransactionId transactionId) {
272 return backingMap.rollback(transactionId);
273 }
274
Madan Jampani542d9e22016-04-05 15:39:55 -0700275 @Override
276 public CompletableFuture<Boolean> prepareAndCommit(MapTransaction<K1, V1> transaction) {
Madan Jampanie9c9a712016-04-18 10:58:04 -0700277 try {
278 return backingMap.prepareAndCommit(transaction.map(keyEncoder, valueEncoder));
279 } catch (Exception e) {
280 return Tools.exceptionalFuture(e);
281 }
Madan Jampani542d9e22016-04-05 15:39:55 -0700282 }
283
Madan Jampani1d3b6172016-04-28 13:22:57 -0700284 @Override
285 public void addStatusChangeListener(Consumer<Status> listener) {
286 backingMap.addStatusChangeListener(listener);
287 }
288
289 @Override
290 public void removeStatusChangeListener(Consumer<Status> listener) {
291 backingMap.removeStatusChangeListener(listener);
292 }
293
294 @Override
295 public Collection<Consumer<Status>> statusChangeListeners() {
296 return backingMap.statusChangeListeners();
297 }
298
Madan Jampani551d0d22016-02-01 12:51:48 -0800299 private class InternalBackingMapEventListener implements MapEventListener<K2, V2> {
300
301 private final MapEventListener<K1, V1> listener;
302
303 InternalBackingMapEventListener(MapEventListener<K1, V1> listener) {
304 this.listener = listener;
305 }
306
307 @Override
308 public void event(MapEvent<K2, V2> event) {
309 listener.event(new MapEvent<K1, V1>(event.name(),
310 keyDecoder.apply(event.key()),
Madan Jampania9673fd2016-02-02 13:01:29 -0800311 event.newValue() != null ? event.newValue().map(valueDecoder) : null,
312 event.oldValue() != null ? event.oldValue().map(valueDecoder) : null));
Madan Jampani551d0d22016-02-01 12:51:48 -0800313 }
314 }
315}