Madan Jampani

ONOS-4423: Support for invalidating cached map entries when a client session is suspended

Change-Id: Icb5e73dc7a37d9459d26cd3a5c9ca1e1a05b0436
...@@ -25,6 +25,7 @@ import java.util.concurrent.ExecutionException; ...@@ -25,6 +25,7 @@ import java.util.concurrent.ExecutionException;
25 import java.util.concurrent.TimeUnit; 25 import java.util.concurrent.TimeUnit;
26 import java.util.concurrent.TimeoutException; 26 import java.util.concurrent.TimeoutException;
27 import java.util.function.BiFunction; 27 import java.util.function.BiFunction;
28 +import java.util.function.Consumer;
28 import java.util.function.Function; 29 import java.util.function.Function;
29 import java.util.function.Predicate; 30 import java.util.function.Predicate;
30 31
...@@ -187,6 +188,21 @@ public class DefaultConsistentMap<K, V> extends Synchronous<AsyncConsistentMap<K ...@@ -187,6 +188,21 @@ public class DefaultConsistentMap<K, V> extends Synchronous<AsyncConsistentMap<K
187 } 188 }
188 189
189 @Override 190 @Override
191 + public void addStatusChangeListener(Consumer<Status> listener) {
192 + asyncMap.addStatusChangeListener(listener);
193 + }
194 +
195 + @Override
196 + public void removeStatusChangeListener(Consumer<Status> listener) {
197 + asyncMap.removeStatusChangeListener(listener);
198 + }
199 +
200 + @Override
201 + public Collection<Consumer<Status>> statusChangeListeners() {
202 + return asyncMap.statusChangeListeners();
203 + }
204 +
205 + @Override
190 public Map<K, V> asJavaMap() { 206 public Map<K, V> asJavaMap() {
191 synchronized (this) { 207 synchronized (this) {
192 if (javaMap == null) { 208 if (javaMap == null) {
......
...@@ -15,7 +15,10 @@ ...@@ -15,7 +15,10 @@
15 */ 15 */
16 package org.onosproject.store.service; 16 package org.onosproject.store.service;
17 17
18 +import java.util.Collection;
19 +import java.util.Collections;
18 import java.util.concurrent.CompletableFuture; 20 import java.util.concurrent.CompletableFuture;
21 +import java.util.function.Consumer;
19 22
20 import org.onosproject.core.ApplicationId; 23 import org.onosproject.core.ApplicationId;
21 24
...@@ -74,6 +77,29 @@ public interface DistributedPrimitive { ...@@ -74,6 +77,29 @@ public interface DistributedPrimitive {
74 TRANSACTION_CONTEXT 77 TRANSACTION_CONTEXT
75 } 78 }
76 79
80 + /**
81 + * Status of distributed primitive.
82 + */
83 + public enum Status {
84 +
85 + /**
86 + * Signifies a state wherein the primitive is operating correctly and is capable of meeting the advertised
87 + * consistency and reliability guarantees.
88 + */
89 + ACTIVE,
90 +
91 + /**
92 + * Signifies a state wherein the primitive is temporarily incapable of providing the advertised
93 + * consistency properties.
94 + */
95 + SUSPENDED,
96 +
97 + /**
98 + * Signifies a state wherein the primitive has been shutdown and therefore cannot perform its functions.
99 + */
100 + INACTIVE
101 + }
102 +
77 static final long DEFAULT_OPERTATION_TIMEOUT_MILLIS = 60000L; 103 static final long DEFAULT_OPERTATION_TIMEOUT_MILLIS = 60000L;
78 104
79 /** 105 /**
...@@ -107,4 +133,24 @@ public interface DistributedPrimitive { ...@@ -107,4 +133,24 @@ public interface DistributedPrimitive {
107 default CompletableFuture<Void> destroy() { 133 default CompletableFuture<Void> destroy() {
108 return CompletableFuture.completedFuture(null); 134 return CompletableFuture.completedFuture(null);
109 } 135 }
136 +
137 + /**
138 + * Registers a listener to be called when the primitive's status changes.
139 + * @param listener The listener to be called when the status changes.
140 + */
141 + default void addStatusChangeListener(Consumer<Status> listener) {}
142 +
143 + /**
144 + * Unregisters a previously registered listener to be called when the primitive's status changes.
145 + * @param listener The listener to unregister
146 + */
147 + default void removeStatusChangeListener(Consumer<Status> listener) {}
148 +
149 + /**
150 + * Returns the collection of status change listeners previously registered.
151 + * @return collection of status change listeners
152 + */
153 + default Collection<Consumer<Status>> statusChangeListeners() {
154 + return Collections.emptyList();
155 + }
110 } 156 }
......
...@@ -15,18 +15,25 @@ ...@@ -15,18 +15,25 @@
15 */ 15 */
16 package org.onosproject.store.primitives.impl; 16 package org.onosproject.store.primitives.impl;
17 17
18 +import static org.slf4j.LoggerFactory.getLogger;
19 +
18 import java.util.concurrent.CompletableFuture; 20 import java.util.concurrent.CompletableFuture;
19 import java.util.function.BiFunction; 21 import java.util.function.BiFunction;
22 +import java.util.function.Consumer;
20 import java.util.function.Predicate; 23 import java.util.function.Predicate;
21 24
22 import org.onosproject.store.service.AsyncConsistentMap; 25 import org.onosproject.store.service.AsyncConsistentMap;
23 import org.onosproject.store.service.MapEventListener; 26 import org.onosproject.store.service.MapEventListener;
24 import org.onosproject.store.service.Versioned; 27 import org.onosproject.store.service.Versioned;
28 +import org.slf4j.Logger;
25 29
26 import com.google.common.cache.CacheBuilder; 30 import com.google.common.cache.CacheBuilder;
27 import com.google.common.cache.CacheLoader; 31 import com.google.common.cache.CacheLoader;
28 import com.google.common.cache.LoadingCache; 32 import com.google.common.cache.LoadingCache;
29 33
34 +import static org.onosproject.store.service.DistributedPrimitive.Status.INACTIVE;
35 +import static org.onosproject.store.service.DistributedPrimitive.Status.SUSPENDED;
36 +
30 /** 37 /**
31 * {@code AsyncConsistentMap} that caches entries on read. 38 * {@code AsyncConsistentMap} that caches entries on read.
32 * <p> 39 * <p>
...@@ -39,20 +46,13 @@ import com.google.common.cache.LoadingCache; ...@@ -39,20 +46,13 @@ import com.google.common.cache.LoadingCache;
39 * @param <V> value type 46 * @param <V> value type
40 */ 47 */
41 public class CachingAsyncConsistentMap<K, V> extends DelegatingAsyncConsistentMap<K, V> { 48 public class CachingAsyncConsistentMap<K, V> extends DelegatingAsyncConsistentMap<K, V> {
42 - private int maxCacheSize = 10000; 49 + private static final int DEFAULT_CACHE_SIZE = 10000;
50 + private final Logger log = getLogger(getClass());
43 51
44 - private final LoadingCache<K, CompletableFuture<Versioned<V>>> cache = 52 + private final LoadingCache<K, CompletableFuture<Versioned<V>>> cache;
45 - CacheBuilder.newBuilder()
46 - .maximumSize(maxCacheSize)
47 - .build(new CacheLoader<K, CompletableFuture<Versioned<V>>>() {
48 - @Override
49 - public CompletableFuture<Versioned<V>> load(K key)
50 - throws Exception {
51 - return CachingAsyncConsistentMap.super.get(key);
52 - }
53 - });
54 53
55 - private final MapEventListener<K, V> cacheInvalidator = event -> cache.invalidate(event.key()); 54 + private final MapEventListener<K, V> cacheInvalidator;
55 + private final Consumer<Status> statusListener;
56 56
57 /** 57 /**
58 * Default constructor. 58 * Default constructor.
...@@ -60,24 +60,36 @@ public class CachingAsyncConsistentMap<K, V> extends DelegatingAsyncConsistentMa ...@@ -60,24 +60,36 @@ public class CachingAsyncConsistentMap<K, V> extends DelegatingAsyncConsistentMa
60 * @param backingMap a distributed, strongly consistent map for backing 60 * @param backingMap a distributed, strongly consistent map for backing
61 */ 61 */
62 public CachingAsyncConsistentMap(AsyncConsistentMap<K, V> backingMap) { 62 public CachingAsyncConsistentMap(AsyncConsistentMap<K, V> backingMap) {
63 - super(backingMap); 63 + this(backingMap, DEFAULT_CACHE_SIZE);
64 - super.addListener(cacheInvalidator);
65 } 64 }
66 65
67 /** 66 /**
68 - * Constructor to configure cache size of LoadingCache. 67 + * Constructor to configure cache size.
69 * 68 *
70 * @param backingMap a distributed, strongly consistent map for backing 69 * @param backingMap a distributed, strongly consistent map for backing
71 * @param cacheSize the maximum size of the cache 70 * @param cacheSize the maximum size of the cache
72 */ 71 */
73 public CachingAsyncConsistentMap(AsyncConsistentMap<K, V> backingMap, int cacheSize) { 72 public CachingAsyncConsistentMap(AsyncConsistentMap<K, V> backingMap, int cacheSize) {
74 super(backingMap); 73 super(backingMap);
74 + cache = CacheBuilder.newBuilder()
75 + .maximumSize(cacheSize)
76 + .build(CacheLoader.from(CachingAsyncConsistentMap.super::get));
77 + cacheInvalidator = event -> cache.invalidate(event.key());
78 + statusListener = status -> {
79 + log.debug("{} status changed to {}", this.name(), status);
80 + // If the status of the underlying map is SUSPENDED or INACTIVE
81 + // we can no longer guarantee that the cache will be in sync.
82 + if (status == SUSPENDED || status == INACTIVE) {
83 + cache.invalidateAll();
84 + }
85 + };
75 super.addListener(cacheInvalidator); 86 super.addListener(cacheInvalidator);
76 - maxCacheSize = cacheSize; 87 + super.addStatusChangeListener(statusListener);
77 } 88 }
78 89
79 @Override 90 @Override
80 public CompletableFuture<Void> destroy() { 91 public CompletableFuture<Void> destroy() {
92 + super.removeStatusChangeListener(statusListener);
81 return super.destroy().thenCompose(v -> removeListener(cacheInvalidator)); 93 return super.destroy().thenCompose(v -> removeListener(cacheInvalidator));
82 } 94 }
83 95
......
...@@ -24,6 +24,7 @@ import java.util.Objects; ...@@ -24,6 +24,7 @@ import java.util.Objects;
24 import java.util.Set; 24 import java.util.Set;
25 import java.util.concurrent.CompletableFuture; 25 import java.util.concurrent.CompletableFuture;
26 import java.util.function.BiFunction; 26 import java.util.function.BiFunction;
27 +import java.util.function.Consumer;
27 import java.util.function.Predicate; 28 import java.util.function.Predicate;
28 29
29 import org.onosproject.core.ApplicationId; 30 import org.onosproject.core.ApplicationId;
...@@ -32,7 +33,6 @@ import org.onosproject.store.service.AsyncConsistentMap; ...@@ -32,7 +33,6 @@ import org.onosproject.store.service.AsyncConsistentMap;
32 import org.onosproject.store.service.MapEventListener; 33 import org.onosproject.store.service.MapEventListener;
33 import org.onosproject.store.service.MapTransaction; 34 import org.onosproject.store.service.MapTransaction;
34 import org.onosproject.store.service.Versioned; 35 import org.onosproject.store.service.Versioned;
35 -
36 import com.google.common.base.MoreObjects; 36 import com.google.common.base.MoreObjects;
37 37
38 /** 38 /**
...@@ -183,6 +183,21 @@ public class DelegatingAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, ...@@ -183,6 +183,21 @@ public class DelegatingAsyncConsistentMap<K, V> implements AsyncConsistentMap<K,
183 } 183 }
184 184
185 @Override 185 @Override
186 + public void addStatusChangeListener(Consumer<Status> listener) {
187 + delegateMap.addStatusChangeListener(listener);
188 + }
189 +
190 + @Override
191 + public void removeStatusChangeListener(Consumer<Status> listener) {
192 + delegateMap.removeStatusChangeListener(listener);
193 + }
194 +
195 + @Override
196 + public Collection<Consumer<Status>> statusChangeListeners() {
197 + return delegateMap.statusChangeListeners();
198 + }
199 +
200 + @Override
186 public String toString() { 201 public String toString() {
187 return MoreObjects.toStringHelper(getClass()) 202 return MoreObjects.toStringHelper(getClass())
188 .add("delegateMap", delegateMap) 203 .add("delegateMap", delegateMap)
......
...@@ -27,6 +27,7 @@ import java.util.concurrent.CompletableFuture; ...@@ -27,6 +27,7 @@ import java.util.concurrent.CompletableFuture;
27 import java.util.concurrent.atomic.AtomicBoolean; 27 import java.util.concurrent.atomic.AtomicBoolean;
28 import java.util.concurrent.atomic.AtomicInteger; 28 import java.util.concurrent.atomic.AtomicInteger;
29 import java.util.function.BiFunction; 29 import java.util.function.BiFunction;
30 +import java.util.function.Consumer;
30 import java.util.function.Predicate; 31 import java.util.function.Predicate;
31 import java.util.stream.Collectors; 32 import java.util.stream.Collectors;
32 33
...@@ -38,7 +39,6 @@ import org.onosproject.store.service.AsyncConsistentMap; ...@@ -38,7 +39,6 @@ import org.onosproject.store.service.AsyncConsistentMap;
38 import org.onosproject.store.service.MapEventListener; 39 import org.onosproject.store.service.MapEventListener;
39 import org.onosproject.store.service.MapTransaction; 40 import org.onosproject.store.service.MapTransaction;
40 import org.onosproject.store.service.Versioned; 41 import org.onosproject.store.service.Versioned;
41 -
42 import com.google.common.collect.Lists; 42 import com.google.common.collect.Lists;
43 import com.google.common.collect.Maps; 43 import com.google.common.collect.Maps;
44 import com.google.common.collect.Sets; 44 import com.google.common.collect.Sets;
...@@ -254,6 +254,21 @@ public class PartitionedAsyncConsistentMap<K, V> implements AsyncConsistentMap<K ...@@ -254,6 +254,21 @@ public class PartitionedAsyncConsistentMap<K, V> implements AsyncConsistentMap<K
254 .thenApply(list -> list.stream().reduce(Boolean::logicalAnd).orElse(true)); 254 .thenApply(list -> list.stream().reduce(Boolean::logicalAnd).orElse(true));
255 } 255 }
256 256
257 + @Override
258 + public void addStatusChangeListener(Consumer<Status> listener) {
259 + partitions.values().forEach(map -> map.addStatusChangeListener(listener));
260 + }
261 +
262 + @Override
263 + public void removeStatusChangeListener(Consumer<Status> listener) {
264 + partitions.values().forEach(map -> map.removeStatusChangeListener(listener));
265 + }
266 +
267 + @Override
268 + public Collection<Consumer<Status>> statusChangeListeners() {
269 + throw new UnsupportedOperationException();
270 + }
271 +
257 /** 272 /**
258 * Returns the map (partition) to which the specified key maps. 273 * Returns the map (partition) to which the specified key maps.
259 * @param key key 274 * @param key key
......
...@@ -23,6 +23,7 @@ import io.atomix.catalyst.transport.Transport; ...@@ -23,6 +23,7 @@ import io.atomix.catalyst.transport.Transport;
23 import io.atomix.catalyst.util.concurrent.CatalystThreadFactory; 23 import io.atomix.catalyst.util.concurrent.CatalystThreadFactory;
24 import io.atomix.copycat.client.ConnectionStrategies; 24 import io.atomix.copycat.client.ConnectionStrategies;
25 import io.atomix.copycat.client.CopycatClient; 25 import io.atomix.copycat.client.CopycatClient;
26 +import io.atomix.copycat.client.CopycatClient.State;
26 import io.atomix.copycat.client.RecoveryStrategies; 27 import io.atomix.copycat.client.RecoveryStrategies;
27 import io.atomix.copycat.client.RetryStrategies; 28 import io.atomix.copycat.client.RetryStrategies;
28 import io.atomix.copycat.client.ServerSelectionStrategies; 29 import io.atomix.copycat.client.ServerSelectionStrategies;
...@@ -36,6 +37,8 @@ import io.atomix.variables.DistributedLong; ...@@ -36,6 +37,8 @@ import io.atomix.variables.DistributedLong;
36 import java.util.Collection; 37 import java.util.Collection;
37 import java.util.Set; 38 import java.util.Set;
38 import java.util.concurrent.CompletableFuture; 39 import java.util.concurrent.CompletableFuture;
40 +import java.util.function.Consumer;
41 +import java.util.function.Function;
39 42
40 import org.onlab.util.HexString; 43 import org.onlab.util.HexString;
41 import org.onosproject.store.primitives.DistributedPrimitiveCreator; 44 import org.onosproject.store.primitives.DistributedPrimitiveCreator;
...@@ -48,6 +51,7 @@ import org.onosproject.store.service.AsyncAtomicValue; ...@@ -48,6 +51,7 @@ import org.onosproject.store.service.AsyncAtomicValue;
48 import org.onosproject.store.service.AsyncConsistentMap; 51 import org.onosproject.store.service.AsyncConsistentMap;
49 import org.onosproject.store.service.AsyncDistributedSet; 52 import org.onosproject.store.service.AsyncDistributedSet;
50 import org.onosproject.store.service.AsyncLeaderElector; 53 import org.onosproject.store.service.AsyncLeaderElector;
54 +import org.onosproject.store.service.DistributedPrimitive.Status;
51 import org.onosproject.store.service.DistributedQueue; 55 import org.onosproject.store.service.DistributedQueue;
52 import org.onosproject.store.service.Serializer; 56 import org.onosproject.store.service.Serializer;
53 import org.slf4j.Logger; 57 import org.slf4j.Logger;
...@@ -71,6 +75,18 @@ public class StoragePartitionClient implements DistributedPrimitiveCreator, Mana ...@@ -71,6 +75,18 @@ public class StoragePartitionClient implements DistributedPrimitiveCreator, Mana
71 private final Supplier<AsyncConsistentMap<String, byte[]>> onosAtomicValuesMap = 75 private final Supplier<AsyncConsistentMap<String, byte[]>> onosAtomicValuesMap =
72 Suppliers.memoize(() -> newAsyncConsistentMap(ATOMIC_VALUES_CONSISTENT_MAP_NAME, 76 Suppliers.memoize(() -> newAsyncConsistentMap(ATOMIC_VALUES_CONSISTENT_MAP_NAME,
73 Serializer.using(KryoNamespaces.BASIC))); 77 Serializer.using(KryoNamespaces.BASIC)));
78 + Function<State, Status> mapper = state -> {
79 + switch (state) {
80 + case CONNECTED:
81 + return Status.ACTIVE;
82 + case SUSPENDED:
83 + return Status.SUSPENDED;
84 + case CLOSED:
85 + return Status.INACTIVE;
86 + default:
87 + throw new IllegalStateException("Unknown state " + state);
88 + }
89 + };
74 90
75 public StoragePartitionClient(StoragePartition partition, 91 public StoragePartitionClient(StoragePartition partition,
76 io.atomix.catalyst.serializer.Serializer serializer, 92 io.atomix.catalyst.serializer.Serializer serializer,
...@@ -90,7 +106,8 @@ public class StoragePartitionClient implements DistributedPrimitiveCreator, Mana ...@@ -90,7 +106,8 @@ public class StoragePartitionClient implements DistributedPrimitiveCreator, Mana
90 transport, 106 transport,
91 serializer.clone(), 107 serializer.clone(),
92 StoragePartition.RESOURCE_TYPES); 108 StoragePartition.RESOURCE_TYPES);
93 - copycatClient.onStateChange(state -> log.info("Client state {}", state)); 109 + copycatClient.onStateChange(state -> log.debug("Partition {} client state"
110 + + " changed to {}", partition.getId(), state));
94 client = new AtomixClient(new ResourceClient(copycatClient)); 111 client = new AtomixClient(new ResourceClient(copycatClient));
95 } 112 }
96 return client.open().whenComplete((r, e) -> { 113 return client.open().whenComplete((r, e) -> {
...@@ -109,9 +126,14 @@ public class StoragePartitionClient implements DistributedPrimitiveCreator, Mana ...@@ -109,9 +126,14 @@ public class StoragePartitionClient implements DistributedPrimitiveCreator, Mana
109 126
110 @Override 127 @Override
111 public <K, V> AsyncConsistentMap<K, V> newAsyncConsistentMap(String name, Serializer serializer) { 128 public <K, V> AsyncConsistentMap<K, V> newAsyncConsistentMap(String name, Serializer serializer) {
129 + AtomixConsistentMap atomixConsistentMap = client.getResource(name, AtomixConsistentMap.class).join();
130 + Consumer<State> statusListener = state -> {
131 + atomixConsistentMap.statusChangeListeners()
132 + .forEach(listener -> listener.accept(mapper.apply(state)));
133 + };
134 + copycatClient.onStateChange(statusListener);
112 AsyncConsistentMap<String, byte[]> rawMap = 135 AsyncConsistentMap<String, byte[]> rawMap =
113 - new DelegatingAsyncConsistentMap<String, byte[]>(client.getResource(name, AtomixConsistentMap.class) 136 + new DelegatingAsyncConsistentMap<String, byte[]>(atomixConsistentMap) {
114 - .join()) {
115 @Override 137 @Override
116 public String name() { 138 public String name() {
117 return name; 139 return name;
...@@ -139,9 +161,7 @@ public class StoragePartitionClient implements DistributedPrimitiveCreator, Mana ...@@ -139,9 +161,7 @@ public class StoragePartitionClient implements DistributedPrimitiveCreator, Mana
139 161
140 @Override 162 @Override
141 public <V> AsyncAtomicValue<V> newAsyncAtomicValue(String name, Serializer serializer) { 163 public <V> AsyncAtomicValue<V> newAsyncAtomicValue(String name, Serializer serializer) {
142 - return new DefaultAsyncAtomicValue<>(name, 164 + return new DefaultAsyncAtomicValue<>(name, serializer, onosAtomicValuesMap.get());
143 - serializer,
144 - onosAtomicValuesMap.get());
145 } 165 }
146 166
147 @Override 167 @Override
......
...@@ -22,6 +22,7 @@ import java.util.Map.Entry; ...@@ -22,6 +22,7 @@ import java.util.Map.Entry;
22 import java.util.Set; 22 import java.util.Set;
23 import java.util.concurrent.CompletableFuture; 23 import java.util.concurrent.CompletableFuture;
24 import java.util.function.BiFunction; 24 import java.util.function.BiFunction;
25 +import java.util.function.Consumer;
25 import java.util.function.Function; 26 import java.util.function.Function;
26 import java.util.function.Predicate; 27 import java.util.function.Predicate;
27 import java.util.stream.Collectors; 28 import java.util.stream.Collectors;
...@@ -33,7 +34,6 @@ import org.onosproject.store.service.MapEvent; ...@@ -33,7 +34,6 @@ import org.onosproject.store.service.MapEvent;
33 import org.onosproject.store.service.MapEventListener; 34 import org.onosproject.store.service.MapEventListener;
34 import org.onosproject.store.service.MapTransaction; 35 import org.onosproject.store.service.MapTransaction;
35 import org.onosproject.store.service.Versioned; 36 import org.onosproject.store.service.Versioned;
36 -
37 import com.google.common.collect.Maps; 37 import com.google.common.collect.Maps;
38 38
39 /** 39 /**
...@@ -281,6 +281,21 @@ public class TranscodingAsyncConsistentMap<K1, V1, K2, V2> implements AsyncConsi ...@@ -281,6 +281,21 @@ public class TranscodingAsyncConsistentMap<K1, V1, K2, V2> implements AsyncConsi
281 } 281 }
282 } 282 }
283 283
284 + @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 +
284 private class InternalBackingMapEventListener implements MapEventListener<K2, V2> { 299 private class InternalBackingMapEventListener implements MapEventListener<K2, V2> {
285 300
286 private final MapEventListener<K1, V1> listener; 301 private final MapEventListener<K1, V1> listener;
......
...@@ -28,6 +28,7 @@ import java.util.Set; ...@@ -28,6 +28,7 @@ import java.util.Set;
28 import java.util.concurrent.CompletableFuture; 28 import java.util.concurrent.CompletableFuture;
29 import java.util.concurrent.atomic.AtomicReference; 29 import java.util.concurrent.atomic.AtomicReference;
30 import java.util.function.BiFunction; 30 import java.util.function.BiFunction;
31 +import java.util.function.Consumer;
31 import java.util.function.Predicate; 32 import java.util.function.Predicate;
32 33
33 import org.onlab.util.Match; 34 import org.onlab.util.Match;
...@@ -53,7 +54,7 @@ import org.onosproject.store.service.MapEvent; ...@@ -53,7 +54,7 @@ import org.onosproject.store.service.MapEvent;
53 import org.onosproject.store.service.MapEventListener; 54 import org.onosproject.store.service.MapEventListener;
54 import org.onosproject.store.service.MapTransaction; 55 import org.onosproject.store.service.MapTransaction;
55 import org.onosproject.store.service.Versioned; 56 import org.onosproject.store.service.Versioned;
56 - 57 +import com.google.common.collect.ImmutableSet;
57 import com.google.common.collect.Sets; 58 import com.google.common.collect.Sets;
58 59
59 /** 60 /**
...@@ -63,6 +64,7 @@ import com.google.common.collect.Sets; ...@@ -63,6 +64,7 @@ import com.google.common.collect.Sets;
63 public class AtomixConsistentMap extends AbstractResource<AtomixConsistentMap> 64 public class AtomixConsistentMap extends AbstractResource<AtomixConsistentMap>
64 implements AsyncConsistentMap<String, byte[]> { 65 implements AsyncConsistentMap<String, byte[]> {
65 66
67 + private final Set<Consumer<Status>> statusChangeListeners = Sets.newCopyOnWriteArraySet();
66 private final Set<MapEventListener<String, byte[]>> mapEventListeners = Sets.newCopyOnWriteArraySet(); 68 private final Set<MapEventListener<String, byte[]>> mapEventListeners = Sets.newCopyOnWriteArraySet();
67 69
68 public static final String CHANGE_SUBJECT = "changeEvents"; 70 public static final String CHANGE_SUBJECT = "changeEvents";
...@@ -291,4 +293,19 @@ public class AtomixConsistentMap extends AbstractResource<AtomixConsistentMap> ...@@ -291,4 +293,19 @@ public class AtomixConsistentMap extends AbstractResource<AtomixConsistentMap>
291 public CompletableFuture<Boolean> prepareAndCommit(MapTransaction<String, byte[]> transaction) { 293 public CompletableFuture<Boolean> prepareAndCommit(MapTransaction<String, byte[]> transaction) {
292 return submit(new TransactionPrepareAndCommit(transaction)).thenApply(v -> v == PrepareResult.OK); 294 return submit(new TransactionPrepareAndCommit(transaction)).thenApply(v -> v == PrepareResult.OK);
293 } 295 }
296 +
297 + @Override
298 + public void addStatusChangeListener(Consumer<Status> listener) {
299 + statusChangeListeners.add(listener);
300 + }
301 +
302 + @Override
303 + public void removeStatusChangeListener(Consumer<Status> listener) {
304 + statusChangeListeners.remove(listener);
305 + }
306 +
307 + @Override
308 + public Collection<Consumer<Status>> statusChangeListeners() {
309 + return ImmutableSet.copyOf(statusChangeListeners);
310 + }
294 } 311 }
...\ No newline at end of file ...\ No newline at end of file
......