Committed by
Brian O'Connor
Notification support for Consistent datastructures (ConsitentMap and DistributedSet)
Change-Id: If74cdc411c79c42c7643420e6369cf656849bb6a
Showing
21 changed files
with
585 additions
and
93 deletions
... | @@ -288,4 +288,19 @@ public interface AsyncConsistentMap<K, V> { | ... | @@ -288,4 +288,19 @@ public interface AsyncConsistentMap<K, V> { |
288 | * @return optional updated value. Will be empty if update did not happen. | 288 | * @return optional updated value. Will be empty if update did not happen. |
289 | */ | 289 | */ |
290 | CompletableFuture<Optional<Versioned<V>>> replaceAndGet(K key, long oldVersion, V newValue); | 290 | CompletableFuture<Optional<Versioned<V>>> replaceAndGet(K key, long oldVersion, V newValue); |
291 | + | ||
292 | + /** | ||
293 | + * Registers the specified listener to be notified whenever the map is updated. | ||
294 | + * | ||
295 | + * @param listener listener to notify about map events | ||
296 | + */ | ||
297 | + void addListener(MapEventListener<K, V> listener); | ||
298 | + | ||
299 | + /** | ||
300 | + * Unregisters the specified listener such that it will no longer | ||
301 | + * receive map change notifications. | ||
302 | + * | ||
303 | + * @param listener listener to unregister | ||
304 | + */ | ||
305 | + void removeListener(MapEventListener<K, V> listener); | ||
291 | } | 306 | } | ... | ... |
... | @@ -289,4 +289,19 @@ public interface ConsistentMap<K, V> { | ... | @@ -289,4 +289,19 @@ public interface ConsistentMap<K, V> { |
289 | * @return optional new value. Will be empty if replace did not happen | 289 | * @return optional new value. Will be empty if replace did not happen |
290 | */ | 290 | */ |
291 | Optional<Versioned<V>> replaceAndGet(K key, long oldVersion, V newValue); | 291 | Optional<Versioned<V>> replaceAndGet(K key, long oldVersion, V newValue); |
292 | + | ||
293 | + /** | ||
294 | + * Registers the specified listener to be notified whenever the map is updated. | ||
295 | + * | ||
296 | + * @param listener listener to notify about map events | ||
297 | + */ | ||
298 | + void addListener(MapEventListener<K, V> listener); | ||
299 | + | ||
300 | + /** | ||
301 | + * Unregisters the specified listener such that it will no longer | ||
302 | + * receive map change notifications. | ||
303 | + * | ||
304 | + * @param listener listener to unregister | ||
305 | + */ | ||
306 | + void removeListener(MapEventListener<K, V> listener); | ||
292 | } | 307 | } | ... | ... |
1 | +package org.onosproject.store.service; | ||
2 | + | ||
3 | +import java.util.Set; | ||
4 | + | ||
5 | +/** | ||
6 | + * A distributed collection designed for holding unique elements. | ||
7 | + * | ||
8 | + * @param <E> set entry type | ||
9 | + */ | ||
10 | +public interface DistributedSet<E> extends Set<E> { | ||
11 | + | ||
12 | + /** | ||
13 | + * Registers the specified listener to be notified whenever | ||
14 | + * the set is updated. | ||
15 | + * | ||
16 | + * @param listener listener to notify about set update events | ||
17 | + */ | ||
18 | + void addListener(SetEventListener<E> listener); | ||
19 | + | ||
20 | + /** | ||
21 | + * Unregisters the specified listener. | ||
22 | + * | ||
23 | + * @param listener listener to unregister. | ||
24 | + */ | ||
25 | + void removeListener(SetEventListener<E> listener); | ||
26 | +} |
... | @@ -15,14 +15,12 @@ | ... | @@ -15,14 +15,12 @@ |
15 | */ | 15 | */ |
16 | package org.onosproject.store.service; | 16 | package org.onosproject.store.service; |
17 | 17 | ||
18 | -import java.util.Set; | ||
19 | - | ||
20 | /** | 18 | /** |
21 | * Builder for distributed set. | 19 | * Builder for distributed set. |
22 | * | 20 | * |
23 | * @param <E> type set elements. | 21 | * @param <E> type set elements. |
24 | */ | 22 | */ |
25 | -public interface SetBuilder<E> { | 23 | +public interface DistributedSetBuilder<E> { |
26 | 24 | ||
27 | /** | 25 | /** |
28 | * Sets the name of the set. | 26 | * Sets the name of the set. |
... | @@ -34,9 +32,9 @@ public interface SetBuilder<E> { | ... | @@ -34,9 +32,9 @@ public interface SetBuilder<E> { |
34 | * </p> | 32 | * </p> |
35 | * | 33 | * |
36 | * @param name name of the set | 34 | * @param name name of the set |
37 | - * @return this SetBuilder | 35 | + * @return this DistributedSetBuilder |
38 | */ | 36 | */ |
39 | - SetBuilder<E> withName(String name); | 37 | + DistributedSetBuilder<E> withName(String name); |
40 | 38 | ||
41 | /** | 39 | /** |
42 | * Sets a serializer that can be used to serialize | 40 | * Sets a serializer that can be used to serialize |
... | @@ -48,18 +46,36 @@ public interface SetBuilder<E> { | ... | @@ -48,18 +46,36 @@ public interface SetBuilder<E> { |
48 | * </p> | 46 | * </p> |
49 | * | 47 | * |
50 | * @param serializer serializer | 48 | * @param serializer serializer |
51 | - * @return this SetBuilder | 49 | + * @return this DistributedSetBuilder |
52 | */ | 50 | */ |
53 | - SetBuilder<E> withSerializer(Serializer serializer); | 51 | + DistributedSetBuilder<E> withSerializer(Serializer serializer); |
54 | 52 | ||
55 | /** | 53 | /** |
56 | * Disables set updates. | 54 | * Disables set updates. |
57 | * <p> | 55 | * <p> |
58 | * Attempt to update the built set will throw {@code UnsupportedOperationException}. | 56 | * Attempt to update the built set will throw {@code UnsupportedOperationException}. |
59 | * | 57 | * |
60 | - * @return this SetBuilder | 58 | + * @return this DistributedSetBuilder |
59 | + */ | ||
60 | + DistributedSetBuilder<E> withUpdatesDisabled(); | ||
61 | + | ||
62 | + /** | ||
63 | + * Disables distribution of set entries across multiple database partitions. | ||
64 | + * <p> | ||
65 | + * When partitioning is disabled, the returned set will have a single partition | ||
66 | + * that spans the entire cluster. Furthermore, the changes made to the set are | ||
67 | + * ephemeral and do not survive a full cluster restart. | ||
68 | + * </p> | ||
69 | + * <p> | ||
70 | + * Disabling partitions is more appropriate when the returned set is used for | ||
71 | + * simple coordination activities and not for long term data persistence. | ||
72 | + * </p> | ||
73 | + * <p> | ||
74 | + * Note: By default partitions are enabled and entries in the set are durable. | ||
75 | + * </p> | ||
76 | + * @return this DistributedSetBuilder | ||
61 | */ | 77 | */ |
62 | - SetBuilder<E> withUpdatesDisabled(); | 78 | + DistributedSetBuilder<E> withPartitionsDisabled(); |
63 | 79 | ||
64 | /** | 80 | /** |
65 | * Builds an set based on the configuration options | 81 | * Builds an set based on the configuration options |
... | @@ -68,5 +84,5 @@ public interface SetBuilder<E> { | ... | @@ -68,5 +84,5 @@ public interface SetBuilder<E> { |
68 | * @return new set | 84 | * @return new set |
69 | * @throws java.lang.RuntimeException if a mandatory parameter is missing | 85 | * @throws java.lang.RuntimeException if a mandatory parameter is missing |
70 | */ | 86 | */ |
71 | - Set<E> build(); | 87 | + DistributedSet<E> build(); |
72 | } | 88 | } | ... | ... |
1 | +package org.onosproject.store.service; | ||
2 | + | ||
3 | +import java.util.Objects; | ||
4 | + | ||
5 | +import com.google.common.base.MoreObjects; | ||
6 | + | ||
7 | +/** | ||
8 | + * Representation of a ConsistentMap update notification. | ||
9 | + * | ||
10 | + * @param <K> key type | ||
11 | + * @param <V> value type | ||
12 | + */ | ||
13 | +public class MapEvent<K, V> { | ||
14 | + | ||
15 | + /** | ||
16 | + * MapEvent type. | ||
17 | + */ | ||
18 | + public enum Type { | ||
19 | + /** | ||
20 | + * Entry inserted into the map. | ||
21 | + */ | ||
22 | + INSERT, | ||
23 | + | ||
24 | + /** | ||
25 | + * Existing map entry updated. | ||
26 | + */ | ||
27 | + UPDATE, | ||
28 | + | ||
29 | + /** | ||
30 | + * Entry removed from map. | ||
31 | + */ | ||
32 | + REMOVE | ||
33 | + } | ||
34 | + | ||
35 | + private final String name; | ||
36 | + private final Type type; | ||
37 | + private final K key; | ||
38 | + private final Versioned<V> value; | ||
39 | + | ||
40 | + /** | ||
41 | + * Creates a new event object. | ||
42 | + * | ||
43 | + * @param name map name | ||
44 | + * @param type the type of the event | ||
45 | + * @param key the key the event concerns | ||
46 | + * @param value the value related to the key, or null for remove events | ||
47 | + */ | ||
48 | + public MapEvent(String name, Type type, K key, Versioned<V> value) { | ||
49 | + this.name = name; | ||
50 | + this.type = type; | ||
51 | + this.key = key; | ||
52 | + this.value = value; | ||
53 | + } | ||
54 | + | ||
55 | + /** | ||
56 | + * Returns the map name. | ||
57 | + * | ||
58 | + * @return name of map | ||
59 | + */ | ||
60 | + public String name() { | ||
61 | + return name; | ||
62 | + } | ||
63 | + | ||
64 | + /** | ||
65 | + * Returns the type of the event. | ||
66 | + * | ||
67 | + * @return the type of the event | ||
68 | + */ | ||
69 | + public Type type() { | ||
70 | + return type; | ||
71 | + } | ||
72 | + | ||
73 | + /** | ||
74 | + * Returns the key this event concerns. | ||
75 | + * | ||
76 | + * @return the key | ||
77 | + */ | ||
78 | + public K key() { | ||
79 | + return key; | ||
80 | + } | ||
81 | + | ||
82 | + /** | ||
83 | + * Returns the value associated with this event. If type is REMOVE, | ||
84 | + * this is the value that was removed. If type is INSERT/UPDATE, this is | ||
85 | + * the new value. | ||
86 | + * | ||
87 | + * @return the value | ||
88 | + */ | ||
89 | + public Versioned<V> value() { | ||
90 | + return value; | ||
91 | + } | ||
92 | + | ||
93 | + @Override | ||
94 | + public boolean equals(Object o) { | ||
95 | + if (!(o instanceof MapEvent)) { | ||
96 | + return false; | ||
97 | + } | ||
98 | + | ||
99 | + MapEvent<K, V> that = (MapEvent) o; | ||
100 | + return Objects.equals(this.name, that.name) && | ||
101 | + Objects.equals(this.type, that.type) && | ||
102 | + Objects.equals(this.key, that.key) && | ||
103 | + Objects.equals(this.value, that.value); | ||
104 | + } | ||
105 | + | ||
106 | + @Override | ||
107 | + public int hashCode() { | ||
108 | + return Objects.hash(type, key, value); | ||
109 | + } | ||
110 | + | ||
111 | + @Override | ||
112 | + public String toString() { | ||
113 | + return MoreObjects.toStringHelper(getClass()) | ||
114 | + .add("name", name) | ||
115 | + .add("type", type) | ||
116 | + .add("key", key) | ||
117 | + .add("value", value) | ||
118 | + .toString(); | ||
119 | + } | ||
120 | +} |
1 | +package org.onosproject.store.service; | ||
2 | + | ||
3 | +/** | ||
4 | + * Listener to be notified about updates to a ConsitentMap. | ||
5 | + */ | ||
6 | +public interface MapEventListener<K, V> { | ||
7 | + /** | ||
8 | + * Reacts to the specified event. | ||
9 | + * | ||
10 | + * @param event the event | ||
11 | + */ | ||
12 | + void event(MapEvent<K, V> event); | ||
13 | +} |
... | @@ -16,8 +16,13 @@ | ... | @@ -16,8 +16,13 @@ |
16 | 16 | ||
17 | package org.onosproject.store.service; | 17 | package org.onosproject.store.service; |
18 | 18 | ||
19 | +import java.util.Arrays; | ||
20 | +import java.util.List; | ||
21 | + | ||
19 | import org.onlab.util.KryoNamespace; | 22 | import org.onlab.util.KryoNamespace; |
20 | 23 | ||
24 | +import com.google.common.collect.Lists; | ||
25 | + | ||
21 | /** | 26 | /** |
22 | * Interface for serialization for store artifacts. | 27 | * Interface for serialization for store artifacts. |
23 | */ | 28 | */ |
... | @@ -45,16 +50,32 @@ public interface Serializer { | ... | @@ -45,16 +50,32 @@ public interface Serializer { |
45 | * @return Serializer instance | 50 | * @return Serializer instance |
46 | */ | 51 | */ |
47 | static Serializer using(KryoNamespace kryo) { | 52 | static Serializer using(KryoNamespace kryo) { |
53 | + return using(Arrays.asList(kryo)); | ||
54 | + } | ||
55 | + | ||
56 | + static Serializer using(List<KryoNamespace> namespaces, Class<?>... classes) { | ||
57 | + KryoNamespace.Builder builder = new KryoNamespace.Builder(); | ||
58 | + namespaces.forEach(builder::register); | ||
59 | + Lists.newArrayList(classes).forEach(builder::register); | ||
60 | + builder.register(MapEvent.class, MapEvent.Type.class); | ||
61 | + KryoNamespace namespace = builder.build(); | ||
48 | return new Serializer() { | 62 | return new Serializer() { |
49 | @Override | 63 | @Override |
50 | public <T> byte[] encode(T object) { | 64 | public <T> byte[] encode(T object) { |
51 | - return kryo.serialize(object); | 65 | + return namespace.serialize(object); |
52 | } | 66 | } |
53 | 67 | ||
54 | @Override | 68 | @Override |
55 | public <T> T decode(byte[] bytes) { | 69 | public <T> T decode(byte[] bytes) { |
56 | - return kryo.deserialize(bytes); | 70 | + return namespace.deserialize(bytes); |
57 | } | 71 | } |
58 | }; | 72 | }; |
59 | } | 73 | } |
74 | + | ||
75 | + static Serializer forTypes(Class<?>... classes) { | ||
76 | + return using(KryoNamespace.newBuilder() | ||
77 | + .register(classes) | ||
78 | + .register(MapEvent.class, MapEvent.Type.class) | ||
79 | + .build()); | ||
80 | + } | ||
60 | } | 81 | } | ... | ... |
1 | +package org.onosproject.store.service; | ||
2 | + | ||
3 | +import java.util.Objects; | ||
4 | + | ||
5 | +import com.google.common.base.MoreObjects; | ||
6 | + | ||
7 | +/** | ||
8 | + * Representation of a DistributedSet update notification. | ||
9 | + * | ||
10 | + * @param <E> element type | ||
11 | + */ | ||
12 | +public class SetEvent<E> { | ||
13 | + | ||
14 | + /** | ||
15 | + * SetEvent type. | ||
16 | + */ | ||
17 | + public enum Type { | ||
18 | + /** | ||
19 | + * Entry added to the set. | ||
20 | + */ | ||
21 | + ADD, | ||
22 | + | ||
23 | + /** | ||
24 | + * Entry removed from the set. | ||
25 | + */ | ||
26 | + REMOVE | ||
27 | + } | ||
28 | + | ||
29 | + private final String name; | ||
30 | + private final Type type; | ||
31 | + private final E entry; | ||
32 | + | ||
33 | + /** | ||
34 | + * Creates a new event object. | ||
35 | + * | ||
36 | + * @param name set name | ||
37 | + * @param type the type of the event | ||
38 | + * @param entry the entry the event concerns | ||
39 | + */ | ||
40 | + public SetEvent(String name, Type type, E entry) { | ||
41 | + this.name = name; | ||
42 | + this.type = type; | ||
43 | + this.entry = entry; | ||
44 | + } | ||
45 | + | ||
46 | + /** | ||
47 | + * Returns the set name. | ||
48 | + * | ||
49 | + * @return name of set | ||
50 | + */ | ||
51 | + public String name() { | ||
52 | + return name; | ||
53 | + } | ||
54 | + | ||
55 | + /** | ||
56 | + * Returns the type of the event. | ||
57 | + * | ||
58 | + * @return the type of the event | ||
59 | + */ | ||
60 | + public Type type() { | ||
61 | + return type; | ||
62 | + } | ||
63 | + | ||
64 | + /** | ||
65 | + * Returns the entry this event concerns. | ||
66 | + * | ||
67 | + * @return the entry | ||
68 | + */ | ||
69 | + public E entry() { | ||
70 | + return entry; | ||
71 | + } | ||
72 | + | ||
73 | + @Override | ||
74 | + public boolean equals(Object o) { | ||
75 | + if (!(o instanceof SetEvent)) { | ||
76 | + return false; | ||
77 | + } | ||
78 | + | ||
79 | + SetEvent<E> that = (SetEvent) o; | ||
80 | + return Objects.equals(this.name, that.name) && | ||
81 | + Objects.equals(this.type, that.type) && | ||
82 | + Objects.equals(this.entry, that.entry); | ||
83 | + } | ||
84 | + | ||
85 | + @Override | ||
86 | + public int hashCode() { | ||
87 | + return Objects.hash(name, type, entry); | ||
88 | + } | ||
89 | + | ||
90 | + @Override | ||
91 | + public String toString() { | ||
92 | + return MoreObjects.toStringHelper(getClass()) | ||
93 | + .add("name", name) | ||
94 | + .add("type", type) | ||
95 | + .add("entry", entry) | ||
96 | + .toString(); | ||
97 | + } | ||
98 | +} |
... | @@ -53,7 +53,7 @@ public interface StorageService { | ... | @@ -53,7 +53,7 @@ public interface StorageService { |
53 | * @param <E> set element type | 53 | * @param <E> set element type |
54 | * @return builder for an distributed set | 54 | * @return builder for an distributed set |
55 | */ | 55 | */ |
56 | - <E> SetBuilder<E> setBuilder(); | 56 | + <E> DistributedSetBuilder<E> setBuilder(); |
57 | 57 | ||
58 | /** | 58 | /** |
59 | * Creates a new AtomicCounterBuilder. | 59 | * Creates a new AtomicCounterBuilder. | ... | ... |
... | @@ -42,19 +42,24 @@ import org.apache.felix.scr.annotations.Deactivate; | ... | @@ -42,19 +42,24 @@ import org.apache.felix.scr.annotations.Deactivate; |
42 | import org.apache.felix.scr.annotations.Reference; | 42 | import org.apache.felix.scr.annotations.Reference; |
43 | import org.apache.felix.scr.annotations.ReferenceCardinality; | 43 | import org.apache.felix.scr.annotations.ReferenceCardinality; |
44 | import org.apache.felix.scr.annotations.Service; | 44 | import org.apache.felix.scr.annotations.Service; |
45 | + | ||
46 | +import static org.onlab.util.Tools.groupedThreads; | ||
47 | + | ||
45 | import org.onosproject.cluster.ClusterService; | 48 | import org.onosproject.cluster.ClusterService; |
46 | import org.onosproject.core.IdGenerator; | 49 | import org.onosproject.core.IdGenerator; |
47 | import org.onosproject.store.cluster.impl.ClusterDefinitionManager; | 50 | import org.onosproject.store.cluster.impl.ClusterDefinitionManager; |
48 | import org.onosproject.store.cluster.impl.NodeInfo; | 51 | import org.onosproject.store.cluster.impl.NodeInfo; |
49 | import org.onosproject.store.cluster.messaging.ClusterCommunicationService; | 52 | import org.onosproject.store.cluster.messaging.ClusterCommunicationService; |
53 | +import org.onosproject.store.cluster.messaging.MessageSubject; | ||
50 | import org.onosproject.store.ecmap.EventuallyConsistentMapBuilderImpl; | 54 | import org.onosproject.store.ecmap.EventuallyConsistentMapBuilderImpl; |
51 | import org.onosproject.store.service.AtomicCounterBuilder; | 55 | import org.onosproject.store.service.AtomicCounterBuilder; |
52 | import org.onosproject.store.service.ConsistentMapBuilder; | 56 | import org.onosproject.store.service.ConsistentMapBuilder; |
53 | import org.onosproject.store.service.ConsistentMapException; | 57 | import org.onosproject.store.service.ConsistentMapException; |
54 | import org.onosproject.store.service.EventuallyConsistentMapBuilder; | 58 | import org.onosproject.store.service.EventuallyConsistentMapBuilder; |
59 | +import org.onosproject.store.service.MapEvent; | ||
55 | import org.onosproject.store.service.MapInfo; | 60 | import org.onosproject.store.service.MapInfo; |
56 | import org.onosproject.store.service.PartitionInfo; | 61 | import org.onosproject.store.service.PartitionInfo; |
57 | -import org.onosproject.store.service.SetBuilder; | 62 | +import org.onosproject.store.service.DistributedSetBuilder; |
58 | import org.onosproject.store.service.StorageAdminService; | 63 | import org.onosproject.store.service.StorageAdminService; |
59 | import org.onosproject.store.service.StorageService; | 64 | import org.onosproject.store.service.StorageService; |
60 | import org.onosproject.store.service.Transaction; | 65 | import org.onosproject.store.service.Transaction; |
... | @@ -69,6 +74,7 @@ import java.util.Map; | ... | @@ -69,6 +74,7 @@ import java.util.Map; |
69 | import java.util.Set; | 74 | import java.util.Set; |
70 | import java.util.concurrent.CompletableFuture; | 75 | import java.util.concurrent.CompletableFuture; |
71 | import java.util.concurrent.ExecutionException; | 76 | import java.util.concurrent.ExecutionException; |
77 | +import java.util.concurrent.ExecutorService; | ||
72 | import java.util.concurrent.Executors; | 78 | import java.util.concurrent.Executors; |
73 | import java.util.concurrent.TimeUnit; | 79 | import java.util.concurrent.TimeUnit; |
74 | import java.util.concurrent.TimeoutException; | 80 | import java.util.concurrent.TimeoutException; |
... | @@ -93,12 +99,16 @@ public class DatabaseManager implements StorageService, StorageAdminService { | ... | @@ -93,12 +99,16 @@ public class DatabaseManager implements StorageService, StorageAdminService { |
93 | private static final int DATABASE_OPERATION_TIMEOUT_MILLIS = 5000; | 99 | private static final int DATABASE_OPERATION_TIMEOUT_MILLIS = 5000; |
94 | 100 | ||
95 | private ClusterCoordinator coordinator; | 101 | private ClusterCoordinator coordinator; |
96 | - private PartitionedDatabase partitionedDatabase; | 102 | + protected PartitionedDatabase partitionedDatabase; |
97 | - private Database inMemoryDatabase; | 103 | + protected Database inMemoryDatabase; |
98 | 104 | ||
99 | private TransactionManager transactionManager; | 105 | private TransactionManager transactionManager; |
100 | private final IdGenerator transactionIdGenerator = () -> RandomUtils.nextLong(); | 106 | private final IdGenerator transactionIdGenerator = () -> RandomUtils.nextLong(); |
101 | 107 | ||
108 | + private ExecutorService eventDispatcher; | ||
109 | + | ||
110 | + private final Set<DefaultAsyncConsistentMap> maps = Sets.newCopyOnWriteArraySet(); | ||
111 | + | ||
102 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 112 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
103 | protected ClusterService clusterService; | 113 | protected ClusterService clusterService; |
104 | 114 | ||
... | @@ -187,7 +197,10 @@ public class DatabaseManager implements StorageService, StorageAdminService { | ... | @@ -187,7 +197,10 @@ public class DatabaseManager implements StorageService, StorageAdminService { |
187 | 197 | ||
188 | Futures.getUnchecked(status); | 198 | Futures.getUnchecked(status); |
189 | 199 | ||
190 | - transactionManager = new TransactionManager(partitionedDatabase); | 200 | + transactionManager = new TransactionManager(partitionedDatabase, consistentMapBuilder()); |
201 | + | ||
202 | + eventDispatcher = Executors.newSingleThreadExecutor( | ||
203 | + groupedThreads("onos/store/manager", "map-event-dispatcher")); | ||
191 | log.info("Started"); | 204 | log.info("Started"); |
192 | } | 205 | } |
193 | 206 | ||
... | @@ -213,13 +226,14 @@ public class DatabaseManager implements StorageService, StorageAdminService { | ... | @@ -213,13 +226,14 @@ public class DatabaseManager implements StorageService, StorageAdminService { |
213 | log.info("Successfully closed databases."); | 226 | log.info("Successfully closed databases."); |
214 | } | 227 | } |
215 | }); | 228 | }); |
229 | + maps.forEach(map -> clusterCommunicator.removeSubscriber(mapUpdatesSubject(map.name()))); | ||
230 | + eventDispatcher.shutdown(); | ||
216 | log.info("Stopped"); | 231 | log.info("Stopped"); |
217 | } | 232 | } |
218 | 233 | ||
219 | @Override | 234 | @Override |
220 | public TransactionContextBuilder transactionContextBuilder() { | 235 | public TransactionContextBuilder transactionContextBuilder() { |
221 | - return new DefaultTransactionContextBuilder( | 236 | + return new DefaultTransactionContextBuilder(this, transactionIdGenerator.getNewId()); |
222 | - inMemoryDatabase, partitionedDatabase, transactionIdGenerator.getNewId()); | ||
223 | } | 237 | } |
224 | 238 | ||
225 | @Override | 239 | @Override |
... | @@ -296,12 +310,12 @@ public class DatabaseManager implements StorageService, StorageAdminService { | ... | @@ -296,12 +310,12 @@ public class DatabaseManager implements StorageService, StorageAdminService { |
296 | 310 | ||
297 | @Override | 311 | @Override |
298 | public <K, V> ConsistentMapBuilder<K, V> consistentMapBuilder() { | 312 | public <K, V> ConsistentMapBuilder<K, V> consistentMapBuilder() { |
299 | - return new DefaultConsistentMapBuilder<>(inMemoryDatabase, partitionedDatabase); | 313 | + return new DefaultConsistentMapBuilder<>(this); |
300 | } | 314 | } |
301 | 315 | ||
302 | @Override | 316 | @Override |
303 | - public <E> SetBuilder<E> setBuilder() { | 317 | + public <E> DistributedSetBuilder<E> setBuilder() { |
304 | - return new DefaultSetBuilder<>(partitionedDatabase); | 318 | + return new DefaultDistributedSetBuilder<>(this); |
305 | } | 319 | } |
306 | 320 | ||
307 | @Override | 321 | @Override |
... | @@ -370,4 +384,20 @@ public class DatabaseManager implements StorageService, StorageAdminService { | ... | @@ -370,4 +384,20 @@ public class DatabaseManager implements StorageService, StorageAdminService { |
370 | public void redriveTransactions() { | 384 | public void redriveTransactions() { |
371 | getTransactions().stream().forEach(transactionManager::execute); | 385 | getTransactions().stream().forEach(transactionManager::execute); |
372 | } | 386 | } |
387 | + | ||
388 | + protected <K, V> void registerMap(DefaultAsyncConsistentMap<K, V> map) { | ||
389 | + // TODO: Support different local instances of the same map. | ||
390 | + if (!maps.add(map)) { | ||
391 | + throw new IllegalStateException("Map by name " + map.name() + " already exists"); | ||
392 | + } | ||
393 | + | ||
394 | + clusterCommunicator.<MapEvent<K, V>>addSubscriber(mapUpdatesSubject(map.name()), | ||
395 | + map.serializer()::decode, | ||
396 | + map::notifyLocalListeners, | ||
397 | + eventDispatcher); | ||
398 | + } | ||
399 | + | ||
400 | + protected static MessageSubject mapUpdatesSubject(String mapName) { | ||
401 | + return new MessageSubject(mapName + "-map-updates"); | ||
402 | + } | ||
373 | } | 403 | } |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -17,6 +17,7 @@ | ... | @@ -17,6 +17,7 @@ |
17 | package org.onosproject.store.consistent.impl; | 17 | package org.onosproject.store.consistent.impl; |
18 | 18 | ||
19 | import static com.google.common.base.Preconditions.*; | 19 | import static com.google.common.base.Preconditions.*; |
20 | +import static org.slf4j.LoggerFactory.getLogger; | ||
20 | 21 | ||
21 | import java.util.Collection; | 22 | import java.util.Collection; |
22 | import java.util.Map; | 23 | import java.util.Map; |
... | @@ -24,8 +25,10 @@ import java.util.Map.Entry; | ... | @@ -24,8 +25,10 @@ import java.util.Map.Entry; |
24 | import java.util.Objects; | 25 | import java.util.Objects; |
25 | import java.util.Optional; | 26 | import java.util.Optional; |
26 | import java.util.concurrent.CompletableFuture; | 27 | import java.util.concurrent.CompletableFuture; |
28 | +import java.util.concurrent.CopyOnWriteArraySet; | ||
27 | import java.util.concurrent.atomic.AtomicReference; | 29 | import java.util.concurrent.atomic.AtomicReference; |
28 | import java.util.function.BiFunction; | 30 | import java.util.function.BiFunction; |
31 | +import java.util.function.Consumer; | ||
29 | import java.util.function.Function; | 32 | import java.util.function.Function; |
30 | import java.util.function.Predicate; | 33 | import java.util.function.Predicate; |
31 | import java.util.stream.Collectors; | 34 | import java.util.stream.Collectors; |
... | @@ -36,8 +39,11 @@ import org.onlab.util.HexString; | ... | @@ -36,8 +39,11 @@ import org.onlab.util.HexString; |
36 | import org.onlab.util.Tools; | 39 | import org.onlab.util.Tools; |
37 | import org.onosproject.store.service.AsyncConsistentMap; | 40 | import org.onosproject.store.service.AsyncConsistentMap; |
38 | import org.onosproject.store.service.ConsistentMapException; | 41 | import org.onosproject.store.service.ConsistentMapException; |
42 | +import org.onosproject.store.service.MapEvent; | ||
43 | +import org.onosproject.store.service.MapEventListener; | ||
39 | import org.onosproject.store.service.Serializer; | 44 | import org.onosproject.store.service.Serializer; |
40 | import org.onosproject.store.service.Versioned; | 45 | import org.onosproject.store.service.Versioned; |
46 | +import org.slf4j.Logger; | ||
41 | 47 | ||
42 | import com.google.common.cache.CacheBuilder; | 48 | import com.google.common.cache.CacheBuilder; |
43 | import com.google.common.cache.CacheLoader; | 49 | import com.google.common.cache.CacheLoader; |
... | @@ -56,6 +62,11 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> | ... | @@ -56,6 +62,11 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> |
56 | private final Database database; | 62 | private final Database database; |
57 | private final Serializer serializer; | 63 | private final Serializer serializer; |
58 | private final boolean readOnly; | 64 | private final boolean readOnly; |
65 | + private final Consumer<MapEvent<K, V>> eventPublisher; | ||
66 | + | ||
67 | + private final Set<MapEventListener<K, V>> listeners = new CopyOnWriteArraySet<>(); | ||
68 | + | ||
69 | + private final Logger log = getLogger(getClass()); | ||
59 | 70 | ||
60 | private static final String ERROR_NULL_KEY = "Key cannot be null"; | 71 | private static final String ERROR_NULL_KEY = "Key cannot be null"; |
61 | private static final String ERROR_NULL_VALUE = "Null values are not allowed"; | 72 | private static final String ERROR_NULL_VALUE = "Null values are not allowed"; |
... | @@ -77,11 +88,29 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> | ... | @@ -77,11 +88,29 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> |
77 | public DefaultAsyncConsistentMap(String name, | 88 | public DefaultAsyncConsistentMap(String name, |
78 | Database database, | 89 | Database database, |
79 | Serializer serializer, | 90 | Serializer serializer, |
80 | - boolean readOnly) { | 91 | + boolean readOnly, |
92 | + Consumer<MapEvent<K, V>> eventPublisher) { | ||
81 | this.name = checkNotNull(name, "map name cannot be null"); | 93 | this.name = checkNotNull(name, "map name cannot be null"); |
82 | this.database = checkNotNull(database, "database cannot be null"); | 94 | this.database = checkNotNull(database, "database cannot be null"); |
83 | this.serializer = checkNotNull(serializer, "serializer cannot be null"); | 95 | this.serializer = checkNotNull(serializer, "serializer cannot be null"); |
84 | this.readOnly = readOnly; | 96 | this.readOnly = readOnly; |
97 | + this.eventPublisher = eventPublisher; | ||
98 | + } | ||
99 | + | ||
100 | + /** | ||
101 | + * Returns this map name. | ||
102 | + * @return map name | ||
103 | + */ | ||
104 | + public String name() { | ||
105 | + return name; | ||
106 | + } | ||
107 | + | ||
108 | + /** | ||
109 | + * Returns the serializer for map entries. | ||
110 | + * @return map entry serializer | ||
111 | + */ | ||
112 | + public Serializer serializer() { | ||
113 | + return serializer; | ||
85 | } | 114 | } |
86 | 115 | ||
87 | @Override | 116 | @Override |
... | @@ -139,6 +168,7 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> | ... | @@ -139,6 +168,7 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> |
139 | checkNotNull(key, ERROR_NULL_KEY); | 168 | checkNotNull(key, ERROR_NULL_KEY); |
140 | checkNotNull(condition, "predicate function cannot be null"); | 169 | checkNotNull(condition, "predicate function cannot be null"); |
141 | checkNotNull(remappingFunction, "Remapping function cannot be null"); | 170 | checkNotNull(remappingFunction, "Remapping function cannot be null"); |
171 | + AtomicReference<MapEvent<K, V>> mapEvent = new AtomicReference<>(); | ||
142 | return get(key).thenCompose(r1 -> { | 172 | return get(key).thenCompose(r1 -> { |
143 | V existingValue = r1 == null ? null : r1.value(); | 173 | V existingValue = r1 == null ? null : r1.value(); |
144 | // if the condition evaluates to false, return existing value. | 174 | // if the condition evaluates to false, return existing value. |
... | @@ -160,6 +190,7 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> | ... | @@ -160,6 +190,7 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> |
160 | if (r1 != null) { | 190 | if (r1 != null) { |
161 | return remove(key, r1.version()).thenApply(result -> { | 191 | return remove(key, r1.version()).thenApply(result -> { |
162 | if (result) { | 192 | if (result) { |
193 | + mapEvent.set(new MapEvent<>(name, MapEvent.Type.REMOVE, key, r1)); | ||
163 | return null; | 194 | return null; |
164 | } else { | 195 | } else { |
165 | throw new ConsistentMapException.ConcurrentModification(); | 196 | throw new ConsistentMapException.ConcurrentModification(); |
... | @@ -174,6 +205,7 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> | ... | @@ -174,6 +205,7 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> |
174 | return replaceAndGet(key, r1.version(), computedValue.get()) | 205 | return replaceAndGet(key, r1.version(), computedValue.get()) |
175 | .thenApply(v -> { | 206 | .thenApply(v -> { |
176 | if (v.isPresent()) { | 207 | if (v.isPresent()) { |
208 | + mapEvent.set(new MapEvent<>(name, MapEvent.Type.UPDATE, key, v.get())); | ||
177 | return v.get(); | 209 | return v.get(); |
178 | } else { | 210 | } else { |
179 | throw new ConsistentMapException.ConcurrentModification(); | 211 | throw new ConsistentMapException.ConcurrentModification(); |
... | @@ -184,12 +216,13 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> | ... | @@ -184,12 +216,13 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> |
184 | if (!result.isPresent()) { | 216 | if (!result.isPresent()) { |
185 | throw new ConsistentMapException.ConcurrentModification(); | 217 | throw new ConsistentMapException.ConcurrentModification(); |
186 | } else { | 218 | } else { |
219 | + mapEvent.set(new MapEvent<>(name, MapEvent.Type.INSERT, key, result.get())); | ||
187 | return result.get(); | 220 | return result.get(); |
188 | } | 221 | } |
189 | }); | 222 | }); |
190 | } | 223 | } |
191 | } | 224 | } |
192 | - }); | 225 | + }).whenComplete((result, error) -> notifyListeners(mapEvent.get())); |
193 | } | 226 | } |
194 | 227 | ||
195 | @Override | 228 | @Override |
... | @@ -370,4 +403,35 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> | ... | @@ -370,4 +403,35 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> |
370 | throw new UnsupportedOperationException(); | 403 | throw new UnsupportedOperationException(); |
371 | } | 404 | } |
372 | } | 405 | } |
406 | + | ||
407 | + @Override | ||
408 | + public void addListener(MapEventListener<K, V> listener) { | ||
409 | + listeners.add(listener); | ||
410 | + } | ||
411 | + | ||
412 | + @Override | ||
413 | + public void removeListener(MapEventListener<K, V> listener) { | ||
414 | + listeners.remove(listener); | ||
415 | + } | ||
416 | + | ||
417 | + protected void notifyListeners(MapEvent<K, V> event) { | ||
418 | + try { | ||
419 | + if (event != null) { | ||
420 | + notifyLocalListeners(event); | ||
421 | + notifyRemoteListeners(event); | ||
422 | + } | ||
423 | + } catch (Exception e) { | ||
424 | + log.warn("Failure notifying listeners about {}", event, e); | ||
425 | + } | ||
426 | + } | ||
427 | + | ||
428 | + protected void notifyLocalListeners(MapEvent<K, V> event) { | ||
429 | + listeners.forEach(listener -> listener.event(event)); | ||
430 | + } | ||
431 | + | ||
432 | + protected void notifyRemoteListeners(MapEvent<K, V> event) { | ||
433 | + if (eventPublisher != null) { | ||
434 | + eventPublisher.accept(event); | ||
435 | + } | ||
436 | + } | ||
373 | } | 437 | } |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -28,10 +28,9 @@ import java.util.function.Function; | ... | @@ -28,10 +28,9 @@ import java.util.function.Function; |
28 | import java.util.function.Predicate; | 28 | import java.util.function.Predicate; |
29 | import java.util.Set; | 29 | import java.util.Set; |
30 | 30 | ||
31 | -import org.onosproject.store.service.AsyncConsistentMap; | ||
32 | import org.onosproject.store.service.ConsistentMap; | 31 | import org.onosproject.store.service.ConsistentMap; |
33 | import org.onosproject.store.service.ConsistentMapException; | 32 | import org.onosproject.store.service.ConsistentMapException; |
34 | -import org.onosproject.store.service.Serializer; | 33 | +import org.onosproject.store.service.MapEventListener; |
35 | import org.onosproject.store.service.Versioned; | 34 | import org.onosproject.store.service.Versioned; |
36 | 35 | ||
37 | /** | 36 | /** |
... | @@ -45,13 +44,14 @@ public class DefaultConsistentMap<K, V> implements ConsistentMap<K, V> { | ... | @@ -45,13 +44,14 @@ public class DefaultConsistentMap<K, V> implements ConsistentMap<K, V> { |
45 | 44 | ||
46 | private static final int OPERATION_TIMEOUT_MILLIS = 5000; | 45 | private static final int OPERATION_TIMEOUT_MILLIS = 5000; |
47 | 46 | ||
48 | - private final AsyncConsistentMap<K, V> asyncMap; | 47 | + private final DefaultAsyncConsistentMap<K, V> asyncMap; |
49 | 48 | ||
50 | - public DefaultConsistentMap(String name, | 49 | + public String name() { |
51 | - Database database, | 50 | + return asyncMap.name(); |
52 | - Serializer serializer, | 51 | + } |
53 | - boolean readOnly) { | 52 | + |
54 | - asyncMap = new DefaultAsyncConsistentMap<>(name, database, serializer, readOnly); | 53 | + public DefaultConsistentMap(DefaultAsyncConsistentMap<K, V> asyncMap) { |
54 | + this.asyncMap = asyncMap; | ||
55 | } | 55 | } |
56 | 56 | ||
57 | @Override | 57 | @Override |
... | @@ -190,4 +190,14 @@ public class DefaultConsistentMap<K, V> implements ConsistentMap<K, V> { | ... | @@ -190,4 +190,14 @@ public class DefaultConsistentMap<K, V> implements ConsistentMap<K, V> { |
190 | } | 190 | } |
191 | } | 191 | } |
192 | } | 192 | } |
193 | + | ||
194 | + @Override | ||
195 | + public void addListener(MapEventListener<K, V> listener) { | ||
196 | + asyncMap.addListener(listener); | ||
197 | + } | ||
198 | + | ||
199 | + @Override | ||
200 | + public void removeListener(MapEventListener<K, V> listener) { | ||
201 | + asyncMap.addListener(listener); | ||
202 | + } | ||
193 | } | 203 | } |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultConsistentMapBuilder.java
... | @@ -6,6 +6,7 @@ import static com.google.common.base.Preconditions.checkState; | ... | @@ -6,6 +6,7 @@ import static com.google.common.base.Preconditions.checkState; |
6 | import org.onosproject.store.service.AsyncConsistentMap; | 6 | import org.onosproject.store.service.AsyncConsistentMap; |
7 | import org.onosproject.store.service.ConsistentMap; | 7 | import org.onosproject.store.service.ConsistentMap; |
8 | import org.onosproject.store.service.ConsistentMapBuilder; | 8 | import org.onosproject.store.service.ConsistentMapBuilder; |
9 | +import org.onosproject.store.service.MapEvent; | ||
9 | import org.onosproject.store.service.Serializer; | 10 | import org.onosproject.store.service.Serializer; |
10 | 11 | ||
11 | /** | 12 | /** |
... | @@ -20,12 +21,10 @@ public class DefaultConsistentMapBuilder<K, V> implements ConsistentMapBuilder<K | ... | @@ -20,12 +21,10 @@ public class DefaultConsistentMapBuilder<K, V> implements ConsistentMapBuilder<K |
20 | private String name; | 21 | private String name; |
21 | private boolean partitionsEnabled = true; | 22 | private boolean partitionsEnabled = true; |
22 | private boolean readOnly = false; | 23 | private boolean readOnly = false; |
23 | - private final Database partitionedDatabase; | 24 | + private final DatabaseManager manager; |
24 | - private final Database inMemoryDatabase; | ||
25 | 25 | ||
26 | - public DefaultConsistentMapBuilder(Database inMemoryDatabase, Database partitionedDatabase) { | 26 | + public DefaultConsistentMapBuilder(DatabaseManager manager) { |
27 | - this.inMemoryDatabase = inMemoryDatabase; | 27 | + this.manager = manager; |
28 | - this.partitionedDatabase = partitionedDatabase; | ||
29 | } | 28 | } |
30 | 29 | ||
31 | @Override | 30 | @Override |
... | @@ -60,21 +59,25 @@ public class DefaultConsistentMapBuilder<K, V> implements ConsistentMapBuilder<K | ... | @@ -60,21 +59,25 @@ public class DefaultConsistentMapBuilder<K, V> implements ConsistentMapBuilder<K |
60 | 59 | ||
61 | @Override | 60 | @Override |
62 | public ConsistentMap<K, V> build() { | 61 | public ConsistentMap<K, V> build() { |
63 | - checkState(validInputs()); | 62 | + return new DefaultConsistentMap<>(buildAndRegisterMap()); |
64 | - return new DefaultConsistentMap<>( | ||
65 | - name, | ||
66 | - partitionsEnabled ? partitionedDatabase : inMemoryDatabase, | ||
67 | - serializer, | ||
68 | - readOnly); | ||
69 | } | 63 | } |
70 | 64 | ||
71 | @Override | 65 | @Override |
72 | public AsyncConsistentMap<K, V> buildAsyncMap() { | 66 | public AsyncConsistentMap<K, V> buildAsyncMap() { |
67 | + return buildAndRegisterMap(); | ||
68 | + } | ||
69 | + | ||
70 | + private DefaultAsyncConsistentMap<K, V> buildAndRegisterMap() { | ||
73 | checkState(validInputs()); | 71 | checkState(validInputs()); |
74 | - return new DefaultAsyncConsistentMap<>( | 72 | + DefaultAsyncConsistentMap<K, V> asyncMap = new DefaultAsyncConsistentMap<>( |
75 | name, | 73 | name, |
76 | - partitionsEnabled ? partitionedDatabase : inMemoryDatabase, | 74 | + partitionsEnabled ? manager.partitionedDatabase : manager.inMemoryDatabase, |
77 | serializer, | 75 | serializer, |
78 | - readOnly); | 76 | + readOnly, |
77 | + event -> manager.clusterCommunicator.<MapEvent<K, V>>broadcast(event, | ||
78 | + DatabaseManager.mapUpdatesSubject(name), | ||
79 | + serializer::encode)); | ||
80 | + manager.registerMap(asyncMap); | ||
81 | + return asyncMap; | ||
79 | } | 82 | } |
80 | } | 83 | } |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -17,11 +17,17 @@ package org.onosproject.store.consistent.impl; | ... | @@ -17,11 +17,17 @@ package org.onosproject.store.consistent.impl; |
17 | 17 | ||
18 | import java.util.Collection; | 18 | import java.util.Collection; |
19 | import java.util.Iterator; | 19 | import java.util.Iterator; |
20 | +import java.util.Map; | ||
20 | import java.util.Set; | 21 | import java.util.Set; |
21 | 22 | ||
22 | import org.onosproject.store.service.ConsistentMap; | 23 | import org.onosproject.store.service.ConsistentMap; |
23 | -import org.onosproject.store.service.Serializer; | 24 | +import org.onosproject.store.service.DistributedSet; |
25 | +import org.onosproject.store.service.MapEvent; | ||
26 | +import org.onosproject.store.service.MapEventListener; | ||
27 | +import org.onosproject.store.service.SetEvent; | ||
28 | +import org.onosproject.store.service.SetEventListener; | ||
24 | 29 | ||
30 | +import com.google.common.collect.Maps; | ||
25 | import com.google.common.collect.Sets; | 31 | import com.google.common.collect.Sets; |
26 | 32 | ||
27 | /** | 33 | /** |
... | @@ -29,12 +35,15 @@ import com.google.common.collect.Sets; | ... | @@ -29,12 +35,15 @@ import com.google.common.collect.Sets; |
29 | 35 | ||
30 | * @param <E> set element type | 36 | * @param <E> set element type |
31 | */ | 37 | */ |
32 | -public class DefaultDistributedSet<E> implements Set<E> { | 38 | +public class DefaultDistributedSet<E> implements DistributedSet<E> { |
33 | 39 | ||
40 | + private final String name; | ||
34 | private final ConsistentMap<E, Boolean> backingMap; | 41 | private final ConsistentMap<E, Boolean> backingMap; |
42 | + private final Map<SetEventListener<E>, MapEventListener<E, Boolean>> listenerMapping = Maps.newIdentityHashMap(); | ||
35 | 43 | ||
36 | - public DefaultDistributedSet(String name, Database database, Serializer serializer, boolean readOnly) { | 44 | + public DefaultDistributedSet(String name, ConsistentMap<E, Boolean> backingMap) { |
37 | - backingMap = new DefaultConsistentMap<>(name, database, serializer, readOnly); | 45 | + this.name = name; |
46 | + this.backingMap = backingMap; | ||
38 | } | 47 | } |
39 | 48 | ||
40 | @Override | 49 | @Override |
... | @@ -76,7 +85,7 @@ public class DefaultDistributedSet<E> implements Set<E> { | ... | @@ -76,7 +85,7 @@ public class DefaultDistributedSet<E> implements Set<E> { |
76 | @SuppressWarnings("unchecked") | 85 | @SuppressWarnings("unchecked") |
77 | @Override | 86 | @Override |
78 | public boolean remove(Object o) { | 87 | public boolean remove(Object o) { |
79 | - return backingMap.remove((E) o, true); | 88 | + return backingMap.remove((E) o) != null; |
80 | } | 89 | } |
81 | 90 | ||
82 | @Override | 91 | @Override |
... | @@ -119,4 +128,26 @@ public class DefaultDistributedSet<E> implements Set<E> { | ... | @@ -119,4 +128,26 @@ public class DefaultDistributedSet<E> implements Set<E> { |
119 | public void clear() { | 128 | public void clear() { |
120 | backingMap.clear(); | 129 | backingMap.clear(); |
121 | } | 130 | } |
131 | + | ||
132 | + @Override | ||
133 | + public void addListener(SetEventListener<E> listener) { | ||
134 | + MapEventListener<E, Boolean> mapEventListener = mapEvent -> { | ||
135 | + if (mapEvent.type() == MapEvent.Type.INSERT) { | ||
136 | + listener.event(new SetEvent<>(name, SetEvent.Type.ADD, mapEvent.key())); | ||
137 | + } else if (mapEvent.type() == MapEvent.Type.REMOVE) { | ||
138 | + listener.event(new SetEvent<>(name, SetEvent.Type.REMOVE, mapEvent.key())); | ||
139 | + } | ||
140 | + }; | ||
141 | + if (listenerMapping.putIfAbsent(listener, mapEventListener) == null) { | ||
142 | + backingMap.addListener(mapEventListener); | ||
143 | + } | ||
144 | + } | ||
145 | + | ||
146 | + @Override | ||
147 | + public void removeListener(SetEventListener<E> listener) { | ||
148 | + MapEventListener<E, Boolean> mapEventListener = listenerMapping.remove(listener); | ||
149 | + if (mapEventListener != null) { | ||
150 | + backingMap.removeListener(mapEventListener); | ||
151 | + } | ||
152 | + } | ||
122 | } | 153 | } | ... | ... |
... | @@ -15,58 +15,52 @@ | ... | @@ -15,58 +15,52 @@ |
15 | */ | 15 | */ |
16 | package org.onosproject.store.consistent.impl; | 16 | package org.onosproject.store.consistent.impl; |
17 | 17 | ||
18 | -import static com.google.common.base.Preconditions.checkArgument; | 18 | +import org.onosproject.store.service.ConsistentMapBuilder; |
19 | -import static com.google.common.base.Preconditions.checkNotNull; | 19 | +import org.onosproject.store.service.DistributedSet; |
20 | -import static com.google.common.base.Preconditions.checkState; | ||
21 | - | ||
22 | -import java.util.Set; | ||
23 | - | ||
24 | import org.onosproject.store.service.Serializer; | 20 | import org.onosproject.store.service.Serializer; |
25 | -import org.onosproject.store.service.SetBuilder; | 21 | +import org.onosproject.store.service.DistributedSetBuilder; |
26 | 22 | ||
27 | /** | 23 | /** |
28 | - * Default Set builder. | 24 | + * Default distributed set builder. |
29 | * | 25 | * |
30 | * @param <E> type for set elements | 26 | * @param <E> type for set elements |
31 | */ | 27 | */ |
32 | -public class DefaultSetBuilder<E> implements SetBuilder<E> { | 28 | +public class DefaultDistributedSetBuilder<E> implements DistributedSetBuilder<E> { |
33 | 29 | ||
34 | - private Serializer serializer; | ||
35 | private String name; | 30 | private String name; |
36 | - private final Database database; | 31 | + private ConsistentMapBuilder<E, Boolean> mapBuilder; |
37 | - private boolean readOnly; | ||
38 | 32 | ||
39 | - public DefaultSetBuilder(Database database) { | 33 | + public DefaultDistributedSetBuilder(DatabaseManager manager) { |
40 | - this.database = checkNotNull(database); | 34 | + this.mapBuilder = manager.consistentMapBuilder(); |
41 | } | 35 | } |
42 | 36 | ||
43 | @Override | 37 | @Override |
44 | - public SetBuilder<E> withName(String name) { | 38 | + public DistributedSetBuilder<E> withName(String name) { |
45 | - checkArgument(name != null && !name.isEmpty()); | 39 | + mapBuilder.withName(name); |
46 | this.name = name; | 40 | this.name = name; |
47 | return this; | 41 | return this; |
48 | } | 42 | } |
49 | 43 | ||
50 | @Override | 44 | @Override |
51 | - public SetBuilder<E> withSerializer(Serializer serializer) { | 45 | + public DistributedSetBuilder<E> withSerializer(Serializer serializer) { |
52 | - checkArgument(serializer != null); | 46 | + mapBuilder.withSerializer(serializer); |
53 | - this.serializer = serializer; | ||
54 | return this; | 47 | return this; |
55 | } | 48 | } |
56 | 49 | ||
57 | @Override | 50 | @Override |
58 | - public SetBuilder<E> withUpdatesDisabled() { | 51 | + public DistributedSetBuilder<E> withUpdatesDisabled() { |
59 | - readOnly = true; | 52 | + mapBuilder.withUpdatesDisabled(); |
60 | return this; | 53 | return this; |
61 | } | 54 | } |
62 | 55 | ||
63 | - private boolean validInputs() { | 56 | + @Override |
64 | - return name != null && serializer != null; | 57 | + public DistributedSetBuilder<E> withPartitionsDisabled() { |
58 | + mapBuilder.withPartitionsDisabled(); | ||
59 | + return this; | ||
65 | } | 60 | } |
66 | 61 | ||
67 | @Override | 62 | @Override |
68 | - public Set<E> build() { | 63 | + public DistributedSet<E> build() { |
69 | - checkState(validInputs()); | 64 | + return new DefaultDistributedSet<E>(name, mapBuilder.build()); |
70 | - return new DefaultDistributedSet<>(name, database, serializer, readOnly); | ||
71 | } | 65 | } |
72 | } | 66 | } | ... | ... |
... | @@ -18,9 +18,11 @@ package org.onosproject.store.consistent.impl; | ... | @@ -18,9 +18,11 @@ package org.onosproject.store.consistent.impl; |
18 | 18 | ||
19 | import java.util.List; | 19 | import java.util.List; |
20 | import java.util.Map; | 20 | import java.util.Map; |
21 | +import java.util.function.Supplier; | ||
21 | 22 | ||
22 | import static com.google.common.base.Preconditions.*; | 23 | import static com.google.common.base.Preconditions.*; |
23 | 24 | ||
25 | +import org.onosproject.store.service.ConsistentMapBuilder; | ||
24 | import org.onosproject.store.service.DatabaseUpdate; | 26 | import org.onosproject.store.service.DatabaseUpdate; |
25 | import org.onosproject.store.service.Serializer; | 27 | import org.onosproject.store.service.Serializer; |
26 | import org.onosproject.store.service.TransactionContext; | 28 | import org.onosproject.store.service.TransactionContext; |
... | @@ -41,10 +43,14 @@ public class DefaultTransactionContext implements TransactionContext { | ... | @@ -41,10 +43,14 @@ public class DefaultTransactionContext implements TransactionContext { |
41 | private boolean isOpen = false; | 43 | private boolean isOpen = false; |
42 | private final Database database; | 44 | private final Database database; |
43 | private final long transactionId; | 45 | private final long transactionId; |
46 | + private final Supplier<ConsistentMapBuilder> mapBuilderSupplier; | ||
44 | 47 | ||
45 | - public DefaultTransactionContext(Database database, long transactionId) { | 48 | + public DefaultTransactionContext(long transactionId, |
46 | - this.database = checkNotNull(database); | 49 | + Database database, |
50 | + Supplier<ConsistentMapBuilder> mapBuilderSupplier) { | ||
47 | this.transactionId = transactionId; | 51 | this.transactionId = transactionId; |
52 | + this.database = checkNotNull(database); | ||
53 | + this.mapBuilderSupplier = checkNotNull(mapBuilderSupplier); | ||
48 | } | 54 | } |
49 | 55 | ||
50 | @Override | 56 | @Override |
... | @@ -72,7 +78,7 @@ public class DefaultTransactionContext implements TransactionContext { | ... | @@ -72,7 +78,7 @@ public class DefaultTransactionContext implements TransactionContext { |
72 | checkNotNull(serializer); | 78 | checkNotNull(serializer); |
73 | return txMaps.computeIfAbsent(mapName, name -> new DefaultTransactionalMap<>( | 79 | return txMaps.computeIfAbsent(mapName, name -> new DefaultTransactionalMap<>( |
74 | name, | 80 | name, |
75 | - new DefaultConsistentMap<>(name, database, serializer, false), | 81 | + mapBuilderSupplier.get().withName(name).withSerializer(serializer).build(), |
76 | this, | 82 | this, |
77 | serializer)); | 83 | serializer)); |
78 | } | 84 | } |
... | @@ -85,6 +91,7 @@ public class DefaultTransactionContext implements TransactionContext { | ... | @@ -85,6 +91,7 @@ public class DefaultTransactionContext implements TransactionContext { |
85 | List<DatabaseUpdate> updates = Lists.newLinkedList(); | 91 | List<DatabaseUpdate> updates = Lists.newLinkedList(); |
86 | txMaps.values() | 92 | txMaps.values() |
87 | .forEach(m -> { updates.addAll(m.prepareDatabaseUpdates()); }); | 93 | .forEach(m -> { updates.addAll(m.prepareDatabaseUpdates()); }); |
94 | + // FIXME: Updates made via transactional context currently do not result in notifications. (ONOS-2097) | ||
88 | database.prepareAndCommit(new DefaultTransaction(transactionId, updates)); | 95 | database.prepareAndCommit(new DefaultTransaction(transactionId, updates)); |
89 | } catch (Exception e) { | 96 | } catch (Exception e) { |
90 | abort(); | 97 | abort(); | ... | ... |
... | @@ -10,14 +10,11 @@ import org.onosproject.store.service.TransactionContextBuilder; | ... | @@ -10,14 +10,11 @@ import org.onosproject.store.service.TransactionContextBuilder; |
10 | public class DefaultTransactionContextBuilder implements TransactionContextBuilder { | 10 | public class DefaultTransactionContextBuilder implements TransactionContextBuilder { |
11 | 11 | ||
12 | private boolean partitionsEnabled = true; | 12 | private boolean partitionsEnabled = true; |
13 | - private final Database partitionedDatabase; | 13 | + private final DatabaseManager manager; |
14 | - private final Database inMemoryDatabase; | ||
15 | private final long transactionId; | 14 | private final long transactionId; |
16 | 15 | ||
17 | - public DefaultTransactionContextBuilder( | 16 | + public DefaultTransactionContextBuilder(DatabaseManager manager, long transactionId) { |
18 | - Database inMemoryDatabase, Database partitionedDatabase, long transactionId) { | 17 | + this.manager = manager; |
19 | - this.partitionedDatabase = partitionedDatabase; | ||
20 | - this.inMemoryDatabase = inMemoryDatabase; | ||
21 | this.transactionId = transactionId; | 18 | this.transactionId = transactionId; |
22 | } | 19 | } |
23 | 20 | ||
... | @@ -30,8 +27,9 @@ public class DefaultTransactionContextBuilder implements TransactionContextBuild | ... | @@ -30,8 +27,9 @@ public class DefaultTransactionContextBuilder implements TransactionContextBuild |
30 | @Override | 27 | @Override |
31 | public TransactionContext build() { | 28 | public TransactionContext build() { |
32 | return new DefaultTransactionContext( | 29 | return new DefaultTransactionContext( |
33 | - partitionsEnabled ? partitionedDatabase : inMemoryDatabase, | 30 | + transactionId, |
34 | - transactionId); | 31 | + partitionsEnabled ? manager.partitionedDatabase : manager.inMemoryDatabase, |
32 | + () -> partitionsEnabled ? manager.consistentMapBuilder() | ||
33 | + : manager.consistentMapBuilder().withPartitionsDisabled()); | ||
35 | } | 34 | } |
36 | - | ||
37 | } | 35 | } | ... | ... |
... | @@ -50,6 +50,7 @@ public class PartitionedDatabase implements Database { | ... | @@ -50,6 +50,7 @@ public class PartitionedDatabase implements Database { |
50 | private final List<Database> partitions; | 50 | private final List<Database> partitions; |
51 | private final AtomicBoolean isOpen = new AtomicBoolean(false); | 51 | private final AtomicBoolean isOpen = new AtomicBoolean(false); |
52 | private static final String DB_NOT_OPEN = "Partitioned Database is not open"; | 52 | private static final String DB_NOT_OPEN = "Partitioned Database is not open"; |
53 | + private TransactionManager transactionManager; | ||
53 | 54 | ||
54 | public PartitionedDatabase( | 55 | public PartitionedDatabase( |
55 | String name, | 56 | String name, |
... | @@ -285,7 +286,10 @@ public class PartitionedDatabase implements Database { | ... | @@ -285,7 +286,10 @@ public class PartitionedDatabase implements Database { |
285 | subTransactions.entrySet().iterator().next(); | 286 | subTransactions.entrySet().iterator().next(); |
286 | return entry.getKey().prepareAndCommit(entry.getValue()); | 287 | return entry.getKey().prepareAndCommit(entry.getValue()); |
287 | } else { | 288 | } else { |
288 | - return new TransactionManager(this).execute(transaction); | 289 | + if (transactionManager != null) { |
290 | + throw new IllegalStateException("TransactionManager is not initialized"); | ||
291 | + } | ||
292 | + return transactionManager.execute(transaction); | ||
289 | } | 293 | } |
290 | } | 294 | } |
291 | 295 | ||
... | @@ -387,4 +391,8 @@ public class PartitionedDatabase implements Database { | ... | @@ -387,4 +391,8 @@ public class PartitionedDatabase implements Database { |
387 | perPartitionUpdates.forEach((k, v) -> subTransactions.put(k, new DefaultTransaction(transaction.id(), v))); | 391 | perPartitionUpdates.forEach((k, v) -> subTransactions.put(k, new DefaultTransaction(transaction.id(), v))); |
388 | return subTransactions; | 392 | return subTransactions; |
389 | } | 393 | } |
394 | + | ||
395 | + protected void setTransactionManager(TransactionManager tranasactionManager) { | ||
396 | + this.transactionManager = transactionManager; | ||
397 | + } | ||
390 | } | 398 | } | ... | ... |
... | @@ -17,6 +17,7 @@ package org.onosproject.store.consistent.impl; | ... | @@ -17,6 +17,7 @@ package org.onosproject.store.consistent.impl; |
17 | 17 | ||
18 | import static com.google.common.base.Preconditions.checkNotNull; | 18 | import static com.google.common.base.Preconditions.checkNotNull; |
19 | 19 | ||
20 | +import java.util.Arrays; | ||
20 | import java.util.Collection; | 21 | import java.util.Collection; |
21 | import java.util.concurrent.CompletableFuture; | 22 | import java.util.concurrent.CompletableFuture; |
22 | import java.util.stream.Collectors; | 23 | import java.util.stream.Collectors; |
... | @@ -26,6 +27,7 @@ import org.apache.commons.lang3.tuple.Pair; | ... | @@ -26,6 +27,7 @@ import org.apache.commons.lang3.tuple.Pair; |
26 | import org.onlab.util.KryoNamespace; | 27 | import org.onlab.util.KryoNamespace; |
27 | import org.onosproject.store.serializers.KryoNamespaces; | 28 | import org.onosproject.store.serializers.KryoNamespaces; |
28 | import org.onosproject.store.service.AsyncConsistentMap; | 29 | import org.onosproject.store.service.AsyncConsistentMap; |
30 | +import org.onosproject.store.service.ConsistentMapBuilder; | ||
29 | import org.onosproject.store.service.DatabaseUpdate; | 31 | import org.onosproject.store.service.DatabaseUpdate; |
30 | import org.onosproject.store.service.Serializer; | 32 | import org.onosproject.store.service.Serializer; |
31 | import org.onosproject.store.service.Transaction; | 33 | import org.onosproject.store.service.Transaction; |
... | @@ -49,7 +51,7 @@ public class TransactionManager { | ... | @@ -49,7 +51,7 @@ public class TransactionManager { |
49 | .register(ImmutablePair.class) | 51 | .register(ImmutablePair.class) |
50 | .build(); | 52 | .build(); |
51 | 53 | ||
52 | - private final Serializer serializer = Serializer.using(KRYO_NAMESPACE); | 54 | + private final Serializer serializer = Serializer.using(Arrays.asList(KRYO_NAMESPACE)); |
53 | private final Database database; | 55 | private final Database database; |
54 | private final AsyncConsistentMap<Long, Transaction> transactions; | 56 | private final AsyncConsistentMap<Long, Transaction> transactions; |
55 | 57 | ||
... | @@ -58,9 +60,11 @@ public class TransactionManager { | ... | @@ -58,9 +60,11 @@ public class TransactionManager { |
58 | * | 60 | * |
59 | * @param database database | 61 | * @param database database |
60 | */ | 62 | */ |
61 | - public TransactionManager(Database database) { | 63 | + public TransactionManager(Database database, ConsistentMapBuilder<Long, Transaction> mapBuilder) { |
62 | this.database = checkNotNull(database, "database cannot be null"); | 64 | this.database = checkNotNull(database, "database cannot be null"); |
63 | - this.transactions = new DefaultAsyncConsistentMap<>("onos-transactions", this.database, serializer, false); | 65 | + this.transactions = mapBuilder.withName("onos-transactions") |
66 | + .withSerializer(serializer) | ||
67 | + .buildAsyncMap(); | ||
64 | } | 68 | } |
65 | 69 | ||
66 | /** | 70 | /** | ... | ... |
... | @@ -170,6 +170,8 @@ import org.onosproject.net.resource.link.MplsLabel; | ... | @@ -170,6 +170,8 @@ import org.onosproject.net.resource.link.MplsLabel; |
170 | import org.onosproject.net.resource.link.MplsLabelResourceAllocation; | 170 | import org.onosproject.net.resource.link.MplsLabelResourceAllocation; |
171 | import org.onosproject.net.resource.link.MplsLabelResourceRequest; | 171 | import org.onosproject.net.resource.link.MplsLabelResourceRequest; |
172 | import org.onosproject.store.Timestamp; | 172 | import org.onosproject.store.Timestamp; |
173 | +import org.onosproject.store.service.MapEvent; | ||
174 | +import org.onosproject.store.service.SetEvent; | ||
173 | import org.onosproject.store.service.Versioned; | 175 | import org.onosproject.store.service.Versioned; |
174 | 176 | ||
175 | import java.net.URI; | 177 | import java.net.URI; |
... | @@ -408,6 +410,10 @@ public final class KryoNamespaces { | ... | @@ -408,6 +410,10 @@ public final class KryoNamespaces { |
408 | .register(new HostLocationSerializer(), HostLocation.class) | 410 | .register(new HostLocationSerializer(), HostLocation.class) |
409 | .register(new DefaultOutboundPacketSerializer(), DefaultOutboundPacket.class) | 411 | .register(new DefaultOutboundPacketSerializer(), DefaultOutboundPacket.class) |
410 | .register(Versioned.class) | 412 | .register(Versioned.class) |
413 | + .register(MapEvent.class) | ||
414 | + .register(MapEvent.Type.class) | ||
415 | + .register(SetEvent.class) | ||
416 | + .register(SetEvent.Type.class) | ||
411 | .register(DefaultGroupId.class) | 417 | .register(DefaultGroupId.class) |
412 | .register(Annotations.class) | 418 | .register(Annotations.class) |
413 | .register(OmsPort.class) | 419 | .register(OmsPort.class) | ... | ... |
-
Please register or login to post a comment