Added newValue and oldValue methods to MapEvent.
Change-Id: Ibaffc8079de03b1f4623044ec53c949831ea8cd1
diff --git a/core/api/src/main/java/org/onosproject/store/primitives/ConsistentMapBackedJavaMap.java b/core/api/src/main/java/org/onosproject/store/primitives/ConsistentMapBackedJavaMap.java
new file mode 100644
index 0000000..0b67b62
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/store/primitives/ConsistentMapBackedJavaMap.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.Versioned;
+
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Maps;
+
+/**
+ * Standard java {@link Map} backed by a {@link ConsistentMap}.
+ *
+ * @param <K> key type
+ * @param <V> value type
+ */
+public final class ConsistentMapBackedJavaMap<K, V> implements Map<K, V> {
+
+ private final ConsistentMap<K, V> backingMap;
+
+ public ConsistentMapBackedJavaMap(ConsistentMap<K, V> backingMap) {
+ this.backingMap = backingMap;
+ }
+
+ @Override
+ public int size() {
+ return backingMap.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return backingMap.isEmpty();
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return backingMap.containsKey((K) key);
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ return backingMap.containsValue((V) value);
+ }
+
+ @Override
+ public V get(Object key) {
+ return Versioned.valueOrNull(backingMap.get((K) key));
+ }
+
+ @Override
+ public V getOrDefault(Object key, V defaultValue) {
+ return Versioned.valueOrElse(backingMap.get((K) key), defaultValue);
+ }
+
+ @Override
+ public V put(K key, V value) {
+ return Versioned.valueOrNull(backingMap.put(key, value));
+ }
+
+ @Override
+ public V putIfAbsent(K key, V value) {
+ return Versioned.valueOrNull(backingMap.putIfAbsent(key, value));
+ }
+
+ @Override
+ public V remove(Object key) {
+ return Versioned.valueOrNull(backingMap.remove((K) key));
+ }
+
+ @Override
+ public boolean remove(Object key, Object value) {
+ return backingMap.remove((K) key, (V) value);
+ }
+
+ @Override
+ public V replace(K key, V value) {
+ return Versioned.valueOrNull(backingMap.replace(key, value));
+ }
+
+ @Override
+ public boolean replace(K key, V oldValue, V newValue) {
+ return backingMap.replace(key, oldValue, newValue);
+ }
+
+ @Override
+ public void putAll(Map<? extends K, ? extends V> m) {
+ m.forEach((k, v) -> {
+ backingMap.put(k, v);
+ });
+ }
+
+ @Override
+ public void clear() {
+ backingMap.clear();
+ }
+
+ @Override
+ public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ return Versioned.valueOrNull(backingMap.compute(key, remappingFunction));
+ }
+
+ @Override
+ public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
+ return Versioned.valueOrNull(backingMap.computeIfAbsent(key, mappingFunction));
+ }
+
+ @Override
+ public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ return Versioned.valueOrNull(backingMap.computeIfPresent(key, remappingFunction));
+ }
+
+ @Override
+ public Set<K> keySet() {
+ return backingMap.keySet();
+ }
+
+ @Override
+ public Collection<V> values() {
+ return Collections2.transform(backingMap.values(), v -> v.value());
+ }
+
+ @Override
+ public Set<java.util.Map.Entry<K, V>> entrySet() {
+ return backingMap.entrySet()
+ .stream()
+ .map(entry -> Maps.immutableEntry(entry.getKey(), entry.getValue().value()))
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public void forEach(BiConsumer<? super K, ? super V> action) {
+ entrySet().forEach(e -> action.accept(e.getKey(), e.getValue()));
+ }
+
+ @Override
+ public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
+ return computeIfPresent(key, (k, v) -> v == null ? value : remappingFunction.apply(v, value));
+ }
+}
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/store/service/MapEvent.java b/core/api/src/main/java/org/onosproject/store/service/MapEvent.java
index 6e67135..3462e21 100644
--- a/core/api/src/main/java/org/onosproject/store/service/MapEvent.java
+++ b/core/api/src/main/java/org/onosproject/store/service/MapEvent.java
@@ -50,21 +50,24 @@
private final String name;
private final Type type;
private final K key;
- private final Versioned<V> value;
+ private final Versioned<V> newValue;
+ private final Versioned<V> oldValue;
/**
* Creates a new event object.
*
* @param name map name
- * @param type type of event
* @param key key the event concerns
- * @param value value key is mapped to
+ * @param currentValue new value key is mapped to
+ * @param previousValue value that was replaced
*/
- public MapEvent(String name, Type type, K key, Versioned<V> value) {
+ public MapEvent(String name, K key, Versioned<V> currentValue, Versioned<V> previousValue) {
this.name = name;
- this.type = type;
this.key = key;
- this.value = value;
+ this.newValue = currentValue;
+ this.oldValue = previousValue;
+ this.type = currentValue != null ?
+ previousValue != null ? Type.UPDATE : Type.INSERT : Type.REMOVE;
}
/**
@@ -100,9 +103,30 @@
* the new value.
*
* @return the value
+ * @deprecated use {@link #newValue()} or {@link #oldValue()} instead.
*/
+ @Deprecated
public Versioned<V> value() {
- return value;
+ return type == Type.REMOVE ? oldValue() : newValue();
+ }
+
+ /**
+ * Returns the new value in the map associated with the key. If {@link #type()} returns {@code REMOVE},
+ * this method will return {@code null}.
+ *
+ * @return the new value for key
+ */
+ public Versioned<V> newValue() {
+ return newValue;
+ }
+
+ /**
+ * Returns the value associated with the key, before it was updated.
+ *
+ * @return previous value in map for the key
+ */
+ public Versioned<V> oldValue() {
+ return oldValue;
}
@Override
@@ -115,12 +139,13 @@
return Objects.equals(this.name, that.name) &&
Objects.equals(this.type, that.type) &&
Objects.equals(this.key, that.key) &&
- Objects.equals(this.value, that.value);
+ Objects.equals(this.newValue, that.newValue) &&
+ Objects.equals(this.oldValue, that.oldValue);
}
@Override
public int hashCode() {
- return Objects.hash(name, type, key, value);
+ return Objects.hash(name, type, key, newValue, oldValue);
}
@Override
@@ -129,7 +154,8 @@
.add("name", name)
.add("type", type)
.add("key", key)
- .add("value", value)
+ .add("newValue", newValue)
+ .add("oldValue", oldValue)
.toString();
}
}
diff --git a/core/api/src/test/java/org/onosproject/store/service/MapEventTest.java b/core/api/src/test/java/org/onosproject/store/service/MapEventTest.java
index 47fba6c..1015e1b 100644
--- a/core/api/src/test/java/org/onosproject/store/service/MapEventTest.java
+++ b/core/api/src/test/java/org/onosproject/store/service/MapEventTest.java
@@ -16,6 +16,7 @@
package org.onosproject.store.service;
import com.google.common.testing.EqualsTester;
+
import org.junit.Test;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -26,13 +27,14 @@
*/
public class MapEventTest {
- private final Versioned<Integer> vStats = new Versioned<>(2, 1);
+ private final Versioned<Integer> vStatsNew = new Versioned<>(2, 2);
+ private final Versioned<Integer> vStatsOld = new Versioned<>(1, 1);
- private final MapEvent<String, Integer> stats1 = new MapEvent<>("a", MapEvent.Type.INSERT, "1", vStats);
+ private final MapEvent<String, Integer> stats1 = new MapEvent<>("a", "1", vStatsNew, null);
- private final MapEvent<String, Integer> stats2 = new MapEvent<>("a", MapEvent.Type.REMOVE, "1", vStats);
+ private final MapEvent<String, Integer> stats2 = new MapEvent<>("a", "1", null, vStatsOld);
- private final MapEvent<String, Integer> stats3 = new MapEvent<>("a", MapEvent.Type.UPDATE, "1", vStats);
+ private final MapEvent<String, Integer> stats3 = new MapEvent<>("a", "1", vStatsNew, vStatsOld);
/**
* Tests the creation of the MapEvent object.
@@ -42,7 +44,23 @@
assertThat(stats1.name(), is("a"));
assertThat(stats1.type(), is(MapEvent.Type.INSERT));
assertThat(stats1.key(), is("1"));
- assertThat(stats1.value(), is(vStats));
+ assertThat(stats1.value(), is(vStatsNew));
+ assertThat(stats1.newValue(), is(vStatsNew));
+ assertThat(stats1.oldValue(), is((Versioned<Integer>) null));
+
+ assertThat(stats2.name(), is("a"));
+ assertThat(stats2.type(), is(MapEvent.Type.REMOVE));
+ assertThat(stats2.key(), is("1"));
+ assertThat(stats2.value(), is(vStatsOld));
+ assertThat(stats2.newValue(), is((Versioned<Integer>) null));
+ assertThat(stats2.oldValue(), is(vStatsOld));
+
+ assertThat(stats3.name(), is("a"));
+ assertThat(stats3.type(), is(MapEvent.Type.UPDATE));
+ assertThat(stats3.key(), is("1"));
+ assertThat(stats3.value(), is(vStatsNew));
+ assertThat(stats3.newValue(), is(vStatsNew));
+ assertThat(stats3.oldValue(), is(vStatsOld));
}
/**
diff --git a/core/api/src/test/java/org/onosproject/store/service/TestConsistentMap.java b/core/api/src/test/java/org/onosproject/store/service/TestConsistentMap.java
index 4133095..f7857ad 100644
--- a/core/api/src/test/java/org/onosproject/store/service/TestConsistentMap.java
+++ b/core/api/src/test/java/org/onosproject/store/service/TestConsistentMap.java
@@ -21,15 +21,18 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.onosproject.core.ApplicationId;
+import org.onosproject.store.primitives.ConsistentMapBackedJavaMap;
-import static org.onosproject.store.service.MapEvent.Type.*;
+import com.google.common.base.Objects;
/**
* Test implementation of the consistent map.
@@ -37,7 +40,7 @@
public final class TestConsistentMap<K, V> extends ConsistentMapAdapter<K, V> {
private final List<MapEventListener<K, V>> listeners;
- private final Map<K, V> map;
+ private final Map<K, Versioned<V>> map;
private final String mapName;
private final AtomicLong counter = new AtomicLong(0);
@@ -54,9 +57,9 @@
/**
* Notify all listeners of an event.
*/
- private void notifyListeners(String mapName, MapEvent.Type type,
- K key, Versioned<V> value) {
- MapEvent<K, V> event = new MapEvent<>(mapName, type, key, value);
+ private void notifyListeners(String mapName,
+ K key, Versioned<V> newvalue, Versioned<V> oldValue) {
+ MapEvent<K, V> event = new MapEvent<>(mapName, key, newvalue, oldValue);
listeners.forEach(
listener -> listener.event(event)
);
@@ -84,71 +87,103 @@
@Override
public Versioned<V> get(K key) {
- V value = map.get(key);
- if (value != null) {
- return version(value);
- } else {
- return null;
- }
+ return map.get(key);
}
@Override
public Versioned<V> computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
- Versioned<V> result = version(map.computeIfAbsent(key, mappingFunction));
- notifyListeners(mapName, INSERT, key, result);
+ AtomicBoolean updated = new AtomicBoolean(false);
+ Versioned<V> result = map.compute(key, (k, v) -> {
+ if (v == null) {
+ updated.set(true);
+ return version(mappingFunction.apply(key));
+ }
+ return v;
+ });
+ if (updated.get()) {
+ notifyListeners(mapName, key, result, null);
+ }
return result;
}
@Override
public Versioned<V> compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
- return version(map.compute(key, remappingFunction));
+ AtomicBoolean updated = new AtomicBoolean(false);
+ AtomicReference<Versioned<V>> previousValue = new AtomicReference<>();
+ Versioned<V> result = map.compute(key, (k, v) -> {
+ updated.set(true);
+ previousValue.set(v);
+ return version(remappingFunction.apply(k, Versioned.valueOrNull(v)));
+ });
+ if (updated.get()) {
+ notifyListeners(mapName, key, result, previousValue.get());
+ }
+ return result;
}
@Override
public Versioned<V> computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
- return version(map.computeIfPresent(key, remappingFunction));
- }
-
- @Override
- public Versioned<V> computeIf(K key, Predicate<? super V> condition,
- BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
- return version(map.compute(key, (k, existingValue) -> {
- if (condition.test(existingValue)) {
- return remappingFunction.apply(k, existingValue);
- } else {
- return existingValue;
+ AtomicBoolean updated = new AtomicBoolean(false);
+ AtomicReference<Versioned<V>> previousValue = new AtomicReference<>();
+ Versioned<V> result = map.compute(key, (k, v) -> {
+ if (v != null) {
+ updated.set(true);
+ previousValue.set(v);
+ return version(remappingFunction.apply(k, v.value()));
}
- }));
- }
-
- @Override
- public Versioned<V> put(K key, V value) {
- Versioned<V> result = version(value);
- if (map.put(key, value) == null) {
- notifyListeners(mapName, INSERT, key, result);
- } else {
- notifyListeners(mapName, UPDATE, key, result);
+ return v;
+ });
+ if (updated.get()) {
+ notifyListeners(mapName, key, result, previousValue.get());
}
return result;
}
@Override
- public Versioned<V> putAndGet(K key, V value) {
- Versioned<V> result = version(map.put(key, value));
- notifyListeners(mapName, UPDATE, key, result);
+ public Versioned<V> computeIf(K key, Predicate<? super V> condition,
+ BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ AtomicBoolean updated = new AtomicBoolean(false);
+ AtomicReference<Versioned<V>> previousValue = new AtomicReference<>();
+ Versioned<V> result = map.compute(key, (k, v) -> {
+ if (condition.test(Versioned.valueOrNull(v))) {
+ previousValue.set(v);
+ updated.set(true);
+ return version(remappingFunction.apply(k, Versioned.valueOrNull(v)));
+ }
+ return v;
+ });
+ if (updated.get()) {
+ notifyListeners(mapName, key, result, previousValue.get());
+ }
return result;
}
@Override
+ public Versioned<V> put(K key, V value) {
+ Versioned<V> newValue = version(value);
+ Versioned<V> previousValue = map.put(key, newValue);
+ notifyListeners(mapName, key, newValue, previousValue);
+ return previousValue;
+ }
+
+ @Override
+ public Versioned<V> putAndGet(K key, V value) {
+ Versioned<V> newValue = version(value);
+ Versioned<V> previousValue = map.put(key, newValue);
+ notifyListeners(mapName, key, newValue, previousValue);
+ return newValue;
+ }
+
+ @Override
public Versioned<V> remove(K key) {
- Versioned<V> result = version(map.remove(key));
- notifyListeners(mapName, REMOVE, key, result);
+ Versioned<V> result = map.remove(key);
+ notifyListeners(mapName, key, null, result);
return result;
}
@Override
public void clear() {
- map.clear();
+ map.keySet().forEach(this::remove);
}
@Override
@@ -158,70 +193,85 @@
@Override
public Collection<Versioned<V>> values() {
- return map
- .values()
+ return map.values()
.stream()
- .map(this::version)
.collect(Collectors.toList());
}
@Override
public Set<Map.Entry<K, Versioned<V>>> entrySet() {
- return super.entrySet();
+ return map.entrySet();
}
@Override
public Versioned<V> putIfAbsent(K key, V value) {
- Versioned<V> result = version(map.putIfAbsent(key, value));
- if (map.get(key).equals(value)) {
- notifyListeners(mapName, INSERT, key, result);
+ Versioned<V> newValue = version(value);
+ Versioned<V> result = map.putIfAbsent(key, newValue);
+ if (result == null) {
+ notifyListeners(mapName, key, newValue, result);
}
return result;
}
@Override
public boolean remove(K key, V value) {
- boolean removed = map.remove(key, value);
- if (removed) {
- notifyListeners(mapName, REMOVE, key, null);
+ Versioned<V> existingValue = map.get(key);
+ if (Objects.equal(Versioned.valueOrNull(existingValue), value)) {
+ map.remove(key);
+ notifyListeners(mapName, key, null, existingValue);
+ return true;
}
- return removed;
+ return false;
}
@Override
public boolean remove(K key, long version) {
- boolean removed = map.remove(key, version);
- if (removed) {
- notifyListeners(mapName, REMOVE, key, null);
+ Versioned<V> existingValue = map.get(key);
+ if (existingValue == null) {
+ return false;
}
- return removed;
+ if (existingValue.version() == version) {
+ map.remove(key);
+ notifyListeners(mapName, key, null, existingValue);
+ return true;
+ }
+ return false;
}
@Override
public Versioned<V> replace(K key, V value) {
- Versioned<V> result = version(map.replace(key, value));
- if (map.get(key).equals(value)) {
- notifyListeners(mapName, UPDATE, key, result);
+ Versioned<V> existingValue = map.get(key);
+ if (existingValue == null) {
+ return null;
}
+ Versioned<V> newValue = version(value);
+ Versioned<V> result = map.put(key, newValue);
+ notifyListeners(mapName, key, newValue, result);
return result;
}
@Override
public boolean replace(K key, V oldValue, V newValue) {
- boolean replaced = map.replace(key, oldValue, newValue);
- if (replaced) {
- notifyListeners(mapName, REMOVE, key, null);
+ Versioned<V> existingValue = map.get(key);
+ if (existingValue == null || !existingValue.value().equals(oldValue)) {
+ return false;
}
- return replaced;
+ Versioned<V> value = version(newValue);
+ Versioned<V> result = map.put(key, value);
+ notifyListeners(mapName, key, value, result);
+ return true;
}
@Override
public boolean replace(K key, long oldVersion, V newValue) {
- boolean replaced = map.replace(key, map.get(key), newValue);
- if (replaced) {
- notifyListeners(mapName, REMOVE, key, null);
+ Versioned<V> existingValue = map.get(key);
+ if (existingValue == null || existingValue.version() != oldVersion) {
+ return false;
}
- return replaced;
+ Versioned<V> value = version(newValue);
+ Versioned<V> result = map.put(key, value);
+ notifyListeners(mapName, key, value, result);
+ return true;
}
@Override
@@ -236,7 +286,7 @@
@Override
public Map<K, V> asJavaMap() {
- return map;
+ return new ConsistentMapBackedJavaMap<>(this);
}
public static Builder builder() {