blob: e0df8c69b0f548a4405fa0878b35bd8aa3c1ebb6 [file] [log] [blame]
Ray Milkey6f440332015-07-31 10:40:53 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Ray Milkey6f440332015-07-31 10:40:53 -07003 *
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.service;
17
18import java.util.Collection;
Ray Milkey6f440332015-07-31 10:40:53 -070019import java.util.LinkedList;
20import java.util.List;
21import java.util.Map;
22import java.util.Set;
Jordan Halterman00e92da2018-05-22 23:05:52 -070023import java.util.concurrent.ConcurrentHashMap;
Madan Jampanif95290a2016-01-27 21:06:11 -080024import java.util.concurrent.atomic.AtomicBoolean;
alshabibe1248b62015-08-20 17:21:55 -070025import java.util.concurrent.atomic.AtomicLong;
Madan Jampanif95290a2016-01-27 21:06:11 -080026import java.util.concurrent.atomic.AtomicReference;
Ray Milkey6f440332015-07-31 10:40:53 -070027import java.util.function.BiFunction;
28import java.util.function.Function;
29import java.util.function.Predicate;
30import java.util.stream.Collectors;
pierventree97f4832020-07-13 14:07:05 +020031import java.util.stream.Stream;
Ray Milkey6f440332015-07-31 10:40:53 -070032
Madan Jampanif95290a2016-01-27 21:06:11 -080033import org.onosproject.store.primitives.ConsistentMapBackedJavaMap;
Madan Jampania090a112016-01-18 16:38:17 -080034
Madan Jampanif95290a2016-01-27 21:06:11 -080035import com.google.common.base.Objects;
Ray Milkey6f440332015-07-31 10:40:53 -070036
37/**
38 * Test implementation of the consistent map.
39 */
40public final class TestConsistentMap<K, V> extends ConsistentMapAdapter<K, V> {
41
42 private final List<MapEventListener<K, V>> listeners;
Madan Jampanif95290a2016-01-27 21:06:11 -080043 private final Map<K, Versioned<V>> map;
Ray Milkey6f440332015-07-31 10:40:53 -070044 private final String mapName;
alshabibe1248b62015-08-20 17:21:55 -070045 private final AtomicLong counter = new AtomicLong(0);
Charles Chan593acf92017-11-22 13:55:41 -080046 private final Serializer serializer;
pierventrebb5110d2020-09-22 12:51:06 +020047 private Map<K, V> javaMap;
48
Ray Milkey6f440332015-07-31 10:40:53 -070049
Charles Chan593acf92017-11-22 13:55:41 -080050 private TestConsistentMap(String mapName, Serializer serializer) {
Jordan Halterman00e92da2018-05-22 23:05:52 -070051 map = new ConcurrentHashMap<>();
Ray Milkey6f440332015-07-31 10:40:53 -070052 listeners = new LinkedList<>();
53 this.mapName = mapName;
Charles Chan593acf92017-11-22 13:55:41 -080054 this.serializer = serializer;
Ray Milkey6f440332015-07-31 10:40:53 -070055 }
56
57 private Versioned<V> version(V v) {
alshabibe1248b62015-08-20 17:21:55 -070058 return new Versioned<>(v, counter.incrementAndGet(), System.currentTimeMillis());
Ray Milkey6f440332015-07-31 10:40:53 -070059 }
60
61 /**
62 * Notify all listeners of an event.
63 */
Madan Jampanif95290a2016-01-27 21:06:11 -080064 private void notifyListeners(String mapName,
65 K key, Versioned<V> newvalue, Versioned<V> oldValue) {
66 MapEvent<K, V> event = new MapEvent<>(mapName, key, newvalue, oldValue);
Ray Milkey6f440332015-07-31 10:40:53 -070067 listeners.forEach(
68 listener -> listener.event(event)
69 );
70 }
71
72 @Override
73 public int size() {
74 return map.size();
75 }
76
77 @Override
78 public boolean isEmpty() {
79 return map.isEmpty();
80 }
81
82 @Override
83 public boolean containsKey(K key) {
84 return map.containsKey(key);
85 }
86
87 @Override
88 public boolean containsValue(V value) {
89 return map.containsValue(value);
90 }
91
92 @Override
93 public Versioned<V> get(K key) {
Madan Jampanif95290a2016-01-27 21:06:11 -080094 return map.get(key);
Ray Milkey6f440332015-07-31 10:40:53 -070095 }
96
97 @Override
98 public Versioned<V> computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
Madan Jampanif95290a2016-01-27 21:06:11 -080099 AtomicBoolean updated = new AtomicBoolean(false);
100 Versioned<V> result = map.compute(key, (k, v) -> {
101 if (v == null) {
102 updated.set(true);
103 return version(mappingFunction.apply(key));
104 }
105 return v;
106 });
107 if (updated.get()) {
108 notifyListeners(mapName, key, result, null);
109 }
Ray Milkey6f440332015-07-31 10:40:53 -0700110 return result;
111 }
112
113 @Override
114 public Versioned<V> compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Madan Jampanif95290a2016-01-27 21:06:11 -0800115 AtomicBoolean updated = new AtomicBoolean(false);
116 AtomicReference<Versioned<V>> previousValue = new AtomicReference<>();
117 Versioned<V> result = map.compute(key, (k, v) -> {
118 updated.set(true);
Charles Chan593acf92017-11-22 13:55:41 -0800119 previousValue.set(serializer.decode(serializer.encode(v)));
Madan Jampanif95290a2016-01-27 21:06:11 -0800120 return version(remappingFunction.apply(k, Versioned.valueOrNull(v)));
121 });
122 if (updated.get()) {
123 notifyListeners(mapName, key, result, previousValue.get());
124 }
125 return result;
Ray Milkey6f440332015-07-31 10:40:53 -0700126 }
127
128 @Override
129 public Versioned<V> computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Madan Jampanif95290a2016-01-27 21:06:11 -0800130 AtomicBoolean updated = new AtomicBoolean(false);
131 AtomicReference<Versioned<V>> previousValue = new AtomicReference<>();
132 Versioned<V> result = map.compute(key, (k, v) -> {
133 if (v != null) {
134 updated.set(true);
Charles Chan593acf92017-11-22 13:55:41 -0800135 previousValue.set(serializer.decode(serializer.encode(v)));
Madan Jampanif95290a2016-01-27 21:06:11 -0800136 return version(remappingFunction.apply(k, v.value()));
Brian O'Connorb2a383f2015-12-07 20:05:14 -0800137 }
Madan Jampanif95290a2016-01-27 21:06:11 -0800138 return v;
139 });
140 if (updated.get()) {
141 notifyListeners(mapName, key, result, previousValue.get());
alshabibe1248b62015-08-20 17:21:55 -0700142 }
Ray Milkey6f440332015-07-31 10:40:53 -0700143 return result;
144 }
145
146 @Override
Madan Jampanif95290a2016-01-27 21:06:11 -0800147 public Versioned<V> computeIf(K key, Predicate<? super V> condition,
148 BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
149 AtomicBoolean updated = new AtomicBoolean(false);
150 AtomicReference<Versioned<V>> previousValue = new AtomicReference<>();
151 Versioned<V> result = map.compute(key, (k, v) -> {
152 if (condition.test(Versioned.valueOrNull(v))) {
Charles Chan593acf92017-11-22 13:55:41 -0800153 previousValue.set(serializer.decode(serializer.encode(v)));
Madan Jampanif95290a2016-01-27 21:06:11 -0800154 updated.set(true);
155 return version(remappingFunction.apply(k, Versioned.valueOrNull(v)));
156 }
157 return v;
158 });
159 if (updated.get()) {
160 notifyListeners(mapName, key, result, previousValue.get());
161 }
Ray Milkey6f440332015-07-31 10:40:53 -0700162 return result;
163 }
164
165 @Override
Madan Jampanif95290a2016-01-27 21:06:11 -0800166 public Versioned<V> put(K key, V value) {
167 Versioned<V> newValue = version(value);
168 Versioned<V> previousValue = map.put(key, newValue);
169 notifyListeners(mapName, key, newValue, previousValue);
170 return previousValue;
171 }
172
173 @Override
174 public Versioned<V> putAndGet(K key, V value) {
175 Versioned<V> newValue = version(value);
176 Versioned<V> previousValue = map.put(key, newValue);
177 notifyListeners(mapName, key, newValue, previousValue);
178 return newValue;
179 }
180
181 @Override
Ray Milkey6f440332015-07-31 10:40:53 -0700182 public Versioned<V> remove(K key) {
Madan Jampanif95290a2016-01-27 21:06:11 -0800183 Versioned<V> result = map.remove(key);
184 notifyListeners(mapName, key, null, result);
Ray Milkey6f440332015-07-31 10:40:53 -0700185 return result;
186 }
187
188 @Override
189 public void clear() {
Thiago Santos01c93382017-10-20 14:22:57 -0700190 for (K key : map.keySet().stream().collect(Collectors.toList())) {
191 remove(key);
192 }
Ray Milkey6f440332015-07-31 10:40:53 -0700193 }
194
195 @Override
196 public Set<K> keySet() {
197 return map.keySet();
198 }
199
200 @Override
201 public Collection<Versioned<V>> values() {
pierventrebb5110d2020-09-22 12:51:06 +0200202 return map.values();
Ray Milkey6f440332015-07-31 10:40:53 -0700203 }
204
205 @Override
206 public Set<Map.Entry<K, Versioned<V>>> entrySet() {
Madan Jampanif95290a2016-01-27 21:06:11 -0800207 return map.entrySet();
Ray Milkey6f440332015-07-31 10:40:53 -0700208 }
209
210 @Override
211 public Versioned<V> putIfAbsent(K key, V value) {
Madan Jampanif95290a2016-01-27 21:06:11 -0800212 Versioned<V> newValue = version(value);
213 Versioned<V> result = map.putIfAbsent(key, newValue);
214 if (result == null) {
215 notifyListeners(mapName, key, newValue, result);
Ray Milkey6f440332015-07-31 10:40:53 -0700216 }
217 return result;
218 }
219
220 @Override
221 public boolean remove(K key, V value) {
Madan Jampanif95290a2016-01-27 21:06:11 -0800222 Versioned<V> existingValue = map.get(key);
223 if (Objects.equal(Versioned.valueOrNull(existingValue), value)) {
224 map.remove(key);
225 notifyListeners(mapName, key, null, existingValue);
226 return true;
Ray Milkey6f440332015-07-31 10:40:53 -0700227 }
Madan Jampanif95290a2016-01-27 21:06:11 -0800228 return false;
Ray Milkey6f440332015-07-31 10:40:53 -0700229 }
230
231 @Override
232 public boolean remove(K key, long version) {
Madan Jampanif95290a2016-01-27 21:06:11 -0800233 Versioned<V> existingValue = map.get(key);
234 if (existingValue == null) {
235 return false;
Ray Milkey6f440332015-07-31 10:40:53 -0700236 }
Madan Jampanif95290a2016-01-27 21:06:11 -0800237 if (existingValue.version() == version) {
238 map.remove(key);
239 notifyListeners(mapName, key, null, existingValue);
240 return true;
241 }
242 return false;
Ray Milkey6f440332015-07-31 10:40:53 -0700243 }
244
245 @Override
Jihwan Kim9887ad92015-12-12 00:23:57 +0900246 public Versioned<V> replace(K key, V value) {
Madan Jampanif95290a2016-01-27 21:06:11 -0800247 Versioned<V> existingValue = map.get(key);
248 if (existingValue == null) {
249 return null;
Jihwan Kim9887ad92015-12-12 00:23:57 +0900250 }
Madan Jampanif95290a2016-01-27 21:06:11 -0800251 Versioned<V> newValue = version(value);
252 Versioned<V> result = map.put(key, newValue);
253 notifyListeners(mapName, key, newValue, result);
Jihwan Kim9887ad92015-12-12 00:23:57 +0900254 return result;
255 }
256
257 @Override
Ray Milkey6f440332015-07-31 10:40:53 -0700258 public boolean replace(K key, V oldValue, V newValue) {
Madan Jampanif95290a2016-01-27 21:06:11 -0800259 Versioned<V> existingValue = map.get(key);
260 if (existingValue == null || !existingValue.value().equals(oldValue)) {
261 return false;
Ray Milkey6f440332015-07-31 10:40:53 -0700262 }
Madan Jampanif95290a2016-01-27 21:06:11 -0800263 Versioned<V> value = version(newValue);
264 Versioned<V> result = map.put(key, value);
265 notifyListeners(mapName, key, value, result);
266 return true;
Ray Milkey6f440332015-07-31 10:40:53 -0700267 }
268
269 @Override
270 public boolean replace(K key, long oldVersion, V newValue) {
Madan Jampanif95290a2016-01-27 21:06:11 -0800271 Versioned<V> existingValue = map.get(key);
272 if (existingValue == null || existingValue.version() != oldVersion) {
273 return false;
Ray Milkey6f440332015-07-31 10:40:53 -0700274 }
Madan Jampanif95290a2016-01-27 21:06:11 -0800275 Versioned<V> value = version(newValue);
276 Versioned<V> result = map.put(key, value);
277 notifyListeners(mapName, key, value, result);
278 return true;
Ray Milkey6f440332015-07-31 10:40:53 -0700279 }
280
281 @Override
pierventree97f4832020-07-13 14:07:05 +0200282 public Stream<Map.Entry<K, Versioned<V>>> stream() {
283 return map.entrySet().stream();
284 }
285
286 @Override
Ray Milkey6f440332015-07-31 10:40:53 -0700287 public void addListener(MapEventListener<K, V> listener) {
288 listeners.add(listener);
289 }
290
291 @Override
292 public void removeListener(MapEventListener<K, V> listener) {
293 listeners.remove(listener);
294 }
295
alshabibe1248b62015-08-20 17:21:55 -0700296 @Override
297 public Map<K, V> asJavaMap() {
pierventrebb5110d2020-09-22 12:51:06 +0200298 synchronized (this) {
299 if (javaMap == null) {
300 javaMap = new ConsistentMapBackedJavaMap<>(this);
301 }
302 }
303 return javaMap;
alshabibe1248b62015-08-20 17:21:55 -0700304 }
305
Ray Milkey6f440332015-07-31 10:40:53 -0700306 public static Builder builder() {
307 return new Builder();
308 }
309
Madan Jampani538be742016-02-10 14:55:38 -0800310 public static class Builder<K, V> extends ConsistentMapBuilder<K, V> {
Flavio Castro41b1f3a2015-07-31 13:51:32 -0700311
312 @Override
Ray Milkey6f440332015-07-31 10:40:53 -0700313 public ConsistentMap<K, V> build() {
Charles Chan593acf92017-11-22 13:55:41 -0800314 return new TestConsistentMap<>(name(), serializer());
Ray Milkey6f440332015-07-31 10:40:53 -0700315 }
316
317 @Override
318 public AsyncConsistentMap<K, V> buildAsyncMap() {
319 return null;
320 }
321
322 }
323
324}