Reworked AbstractDistributedStore RemoteEventHandler to allow delegating various…
… events to accommodate cache-specific behaviours.
Showing
4 changed files
with
116 additions
and
36 deletions
... | @@ -8,6 +8,11 @@ import org.onlab.onos.event.Event; | ... | @@ -8,6 +8,11 @@ import org.onlab.onos.event.Event; |
8 | */ | 8 | */ |
9 | public interface StoreDelegate<E extends Event> { | 9 | public interface StoreDelegate<E extends Event> { |
10 | 10 | ||
11 | + /** | ||
12 | + * Notifies the delegate via the specified event. | ||
13 | + * | ||
14 | + * @param event store generated event | ||
15 | + */ | ||
11 | void notify(E event); | 16 | void notify(E event); |
12 | 17 | ||
13 | } | 18 | } | ... | ... |
... | @@ -160,7 +160,7 @@ public class DistributedDeviceManagerTest { | ... | @@ -160,7 +160,7 @@ public class DistributedDeviceManagerTest { |
160 | public void deviceDisconnected() { | 160 | public void deviceDisconnected() { |
161 | connectDevice(DID1, SW1); | 161 | connectDevice(DID1, SW1); |
162 | connectDevice(DID2, SW1); | 162 | connectDevice(DID2, SW1); |
163 | - validateEvents(DEVICE_ADDED, DEVICE_ADDED); | 163 | + validateEvents(DEVICE_ADDED, DEVICE_ADDED, DEVICE_ADDED, DEVICE_ADDED); |
164 | assertTrue("device should be available", service.isAvailable(DID1)); | 164 | assertTrue("device should be available", service.isAvailable(DID1)); |
165 | 165 | ||
166 | // Disconnect | 166 | // Disconnect |
... | @@ -179,7 +179,7 @@ public class DistributedDeviceManagerTest { | ... | @@ -179,7 +179,7 @@ public class DistributedDeviceManagerTest { |
179 | @Test | 179 | @Test |
180 | public void deviceUpdated() { | 180 | public void deviceUpdated() { |
181 | connectDevice(DID1, SW1); | 181 | connectDevice(DID1, SW1); |
182 | - validateEvents(DEVICE_ADDED); | 182 | + validateEvents(DEVICE_ADDED, DEVICE_ADDED); |
183 | 183 | ||
184 | connectDevice(DID1, SW2); | 184 | connectDevice(DID1, SW2); |
185 | validateEvents(DEVICE_UPDATED); | 185 | validateEvents(DEVICE_UPDATED); |
... | @@ -199,7 +199,7 @@ public class DistributedDeviceManagerTest { | ... | @@ -199,7 +199,7 @@ public class DistributedDeviceManagerTest { |
199 | pds.add(new DefaultPortDescription(P2, true)); | 199 | pds.add(new DefaultPortDescription(P2, true)); |
200 | pds.add(new DefaultPortDescription(P3, true)); | 200 | pds.add(new DefaultPortDescription(P3, true)); |
201 | providerService.updatePorts(DID1, pds); | 201 | providerService.updatePorts(DID1, pds); |
202 | - validateEvents(DEVICE_ADDED, PORT_ADDED, PORT_ADDED, PORT_ADDED); | 202 | + validateEvents(DEVICE_ADDED, DEVICE_ADDED, PORT_ADDED, PORT_ADDED, PORT_ADDED); |
203 | pds.clear(); | 203 | pds.clear(); |
204 | 204 | ||
205 | pds.add(new DefaultPortDescription(P1, false)); | 205 | pds.add(new DefaultPortDescription(P1, false)); |
... | @@ -215,7 +215,7 @@ public class DistributedDeviceManagerTest { | ... | @@ -215,7 +215,7 @@ public class DistributedDeviceManagerTest { |
215 | pds.add(new DefaultPortDescription(P1, true)); | 215 | pds.add(new DefaultPortDescription(P1, true)); |
216 | pds.add(new DefaultPortDescription(P2, true)); | 216 | pds.add(new DefaultPortDescription(P2, true)); |
217 | providerService.updatePorts(DID1, pds); | 217 | providerService.updatePorts(DID1, pds); |
218 | - validateEvents(DEVICE_ADDED, PORT_ADDED, PORT_ADDED); | 218 | + validateEvents(DEVICE_ADDED, DEVICE_ADDED, PORT_ADDED, PORT_ADDED); |
219 | 219 | ||
220 | providerService.portStatusChanged(DID1, new DefaultPortDescription(P1, false)); | 220 | providerService.portStatusChanged(DID1, new DefaultPortDescription(P1, false)); |
221 | validateEvents(PORT_UPDATED); | 221 | validateEvents(PORT_UPDATED); |
... | @@ -230,7 +230,7 @@ public class DistributedDeviceManagerTest { | ... | @@ -230,7 +230,7 @@ public class DistributedDeviceManagerTest { |
230 | pds.add(new DefaultPortDescription(P1, true)); | 230 | pds.add(new DefaultPortDescription(P1, true)); |
231 | pds.add(new DefaultPortDescription(P2, true)); | 231 | pds.add(new DefaultPortDescription(P2, true)); |
232 | providerService.updatePorts(DID1, pds); | 232 | providerService.updatePorts(DID1, pds); |
233 | - validateEvents(DEVICE_ADDED, PORT_ADDED, PORT_ADDED); | 233 | + validateEvents(DEVICE_ADDED, DEVICE_ADDED, PORT_ADDED, PORT_ADDED); |
234 | assertEquals("wrong port count", 2, service.getPorts(DID1).size()); | 234 | assertEquals("wrong port count", 2, service.getPorts(DID1).size()); |
235 | 235 | ||
236 | Port port = service.getPort(DID1, P1); | 236 | Port port = service.getPort(DID1, P1); |
... | @@ -244,10 +244,10 @@ public class DistributedDeviceManagerTest { | ... | @@ -244,10 +244,10 @@ public class DistributedDeviceManagerTest { |
244 | connectDevice(DID2, SW2); | 244 | connectDevice(DID2, SW2); |
245 | assertEquals("incorrect device count", 2, service.getDeviceCount()); | 245 | assertEquals("incorrect device count", 2, service.getDeviceCount()); |
246 | admin.removeDevice(DID1); | 246 | admin.removeDevice(DID1); |
247 | + validateEvents(DEVICE_ADDED, DEVICE_ADDED, DEVICE_ADDED, DEVICE_ADDED, DEVICE_REMOVED, DEVICE_REMOVED); | ||
247 | assertNull("device should not be found", service.getDevice(DID1)); | 248 | assertNull("device should not be found", service.getDevice(DID1)); |
248 | assertNotNull("device should be found", service.getDevice(DID2)); | 249 | assertNotNull("device should be found", service.getDevice(DID2)); |
249 | assertEquals("incorrect device count", 1, service.getDeviceCount()); | 250 | assertEquals("incorrect device count", 1, service.getDeviceCount()); |
250 | - | ||
251 | } | 251 | } |
252 | 252 | ||
253 | protected void validateEvents(Enum... types) { | 253 | protected void validateEvents(Enum... types) { | ... | ... |
... | @@ -7,7 +7,6 @@ import com.google.common.collect.ImmutableSet; | ... | @@ -7,7 +7,6 @@ import com.google.common.collect.ImmutableSet; |
7 | import com.google.common.collect.ImmutableSet.Builder; | 7 | import com.google.common.collect.ImmutableSet.Builder; |
8 | import com.hazelcast.core.IMap; | 8 | import com.hazelcast.core.IMap; |
9 | import com.hazelcast.core.ISet; | 9 | import com.hazelcast.core.ISet; |
10 | - | ||
11 | import org.apache.felix.scr.annotations.Activate; | 10 | import org.apache.felix.scr.annotations.Activate; |
12 | import org.apache.felix.scr.annotations.Component; | 11 | import org.apache.felix.scr.annotations.Component; |
13 | import org.apache.felix.scr.annotations.Deactivate; | 12 | import org.apache.felix.scr.annotations.Deactivate; |
... | @@ -38,6 +37,7 @@ import java.util.List; | ... | @@ -38,6 +37,7 @@ import java.util.List; |
38 | import java.util.Map; | 37 | import java.util.Map; |
39 | import java.util.Objects; | 38 | import java.util.Objects; |
40 | import java.util.Set; | 39 | import java.util.Set; |
40 | + | ||
41 | import static com.google.common.base.Preconditions.checkArgument; | 41 | import static com.google.common.base.Preconditions.checkArgument; |
42 | import static com.google.common.cache.CacheBuilder.newBuilder; | 42 | import static com.google.common.cache.CacheBuilder.newBuilder; |
43 | import static org.onlab.onos.net.device.DeviceEvent.Type.*; | 43 | import static org.onlab.onos.net.device.DeviceEvent.Type.*; |
... | @@ -82,7 +82,7 @@ public class DistributedDeviceStore | ... | @@ -82,7 +82,7 @@ public class DistributedDeviceStore |
82 | = new OptionalCacheLoader<>(storeService, rawDevices); | 82 | = new OptionalCacheLoader<>(storeService, rawDevices); |
83 | devices = new AbsentInvalidatingLoadingCache<>(newBuilder().build(deviceLoader)); | 83 | devices = new AbsentInvalidatingLoadingCache<>(newBuilder().build(deviceLoader)); |
84 | // refresh/populate cache based on notification from other instance | 84 | // refresh/populate cache based on notification from other instance |
85 | - rawDevices.addEntryListener(new RemoteEventHandler<>(devices), includeValue); | 85 | + rawDevices.addEntryListener(new RemoteDeviceEventHandler(devices), includeValue); |
86 | 86 | ||
87 | // TODO cache availableDevices | 87 | // TODO cache availableDevices |
88 | availableDevices = theInstance.getSet("availableDevices"); | 88 | availableDevices = theInstance.getSet("availableDevices"); |
... | @@ -92,35 +92,25 @@ public class DistributedDeviceStore | ... | @@ -92,35 +92,25 @@ public class DistributedDeviceStore |
92 | = new OptionalCacheLoader<>(storeService, rawDevicePorts); | 92 | = new OptionalCacheLoader<>(storeService, rawDevicePorts); |
93 | devicePorts = new AbsentInvalidatingLoadingCache<>(newBuilder().build(devicePortLoader)); | 93 | devicePorts = new AbsentInvalidatingLoadingCache<>(newBuilder().build(devicePortLoader)); |
94 | // refresh/populate cache based on notification from other instance | 94 | // refresh/populate cache based on notification from other instance |
95 | - rawDevicePorts.addEntryListener(new RemoteEventHandler<>(devicePorts), includeValue); | 95 | + rawDevicePorts.addEntryListener(new RemotePortEventHandler(devicePorts), includeValue); |
96 | + | ||
97 | + loadDeviceCache(); | ||
96 | 98 | ||
97 | log.info("Started"); | 99 | log.info("Started"); |
98 | } | 100 | } |
99 | 101 | ||
100 | @Deactivate | 102 | @Deactivate |
101 | public void deactivate() { | 103 | public void deactivate() { |
102 | - | ||
103 | log.info("Stopped"); | 104 | log.info("Stopped"); |
104 | } | 105 | } |
105 | 106 | ||
106 | @Override | 107 | @Override |
107 | public int getDeviceCount() { | 108 | public int getDeviceCount() { |
108 | - // TODO IMap size or cache size? | 109 | + return devices.asMap().size(); |
109 | - return rawDevices.size(); | ||
110 | } | 110 | } |
111 | 111 | ||
112 | @Override | 112 | @Override |
113 | public Iterable<Device> getDevices() { | 113 | public Iterable<Device> getDevices() { |
114 | -// TODO Revisit if we ever need to do this. | ||
115 | -// log.info("{}:{}", rawMap.size(), cache.size()); | ||
116 | -// if (rawMap.size() != cache.size()) { | ||
117 | -// for (Entry<byte[], byte[]> e : rawMap.entrySet()) { | ||
118 | -// final DeviceId key = deserialize(e.getKey()); | ||
119 | -// final DefaultDevice val = deserialize(e.getValue()); | ||
120 | -// cache.put(key, val); | ||
121 | -// } | ||
122 | -// } | ||
123 | - | ||
124 | // TODO builder v.s. copyOf. Guava semms to be using copyOf? | 114 | // TODO builder v.s. copyOf. Guava semms to be using copyOf? |
125 | Builder<Device> builder = ImmutableSet.builder(); | 115 | Builder<Device> builder = ImmutableSet.builder(); |
126 | for (Optional<DefaultDevice> e : devices.asMap().values()) { | 116 | for (Optional<DefaultDevice> e : devices.asMap().values()) { |
... | @@ -131,6 +121,17 @@ public class DistributedDeviceStore | ... | @@ -131,6 +121,17 @@ public class DistributedDeviceStore |
131 | return builder.build(); | 121 | return builder.build(); |
132 | } | 122 | } |
133 | 123 | ||
124 | + private void loadDeviceCache() { | ||
125 | + log.info("{}:{}", rawDevices.size(), devices.size()); | ||
126 | + if (rawDevices.size() != devices.size()) { | ||
127 | + for (Map.Entry<byte[], byte[]> e : rawDevices.entrySet()) { | ||
128 | + final DeviceId key = deserialize(e.getKey()); | ||
129 | + final DefaultDevice val = deserialize(e.getValue()); | ||
130 | + devices.put(key, Optional.of(val)); | ||
131 | + } | ||
132 | + } | ||
133 | + } | ||
134 | + | ||
134 | @Override | 135 | @Override |
135 | public Device getDevice(DeviceId deviceId) { | 136 | public Device getDevice(DeviceId deviceId) { |
136 | // TODO revisit if ignoring exception is safe. | 137 | // TODO revisit if ignoring exception is safe. |
... | @@ -162,7 +163,7 @@ public class DistributedDeviceStore | ... | @@ -162,7 +163,7 @@ public class DistributedDeviceStore |
162 | 163 | ||
163 | availableDevices.add(deviceIdBytes); | 164 | availableDevices.add(deviceIdBytes); |
164 | } | 165 | } |
165 | - return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, device, null); | 166 | + return new DeviceEvent(DEVICE_ADDED, device, null); |
166 | } | 167 | } |
167 | 168 | ||
168 | // Updates the device and returns the appropriate event if necessary. | 169 | // Updates the device and returns the appropriate event if necessary. |
... | @@ -343,5 +344,48 @@ public class DistributedDeviceStore | ... | @@ -343,5 +344,48 @@ public class DistributedDeviceStore |
343 | } | 344 | } |
344 | } | 345 | } |
345 | 346 | ||
347 | + private class RemoteDeviceEventHandler extends RemoteEventHandler<DeviceId, DefaultDevice> { | ||
348 | + public RemoteDeviceEventHandler(LoadingCache<DeviceId, Optional<DefaultDevice>> cache) { | ||
349 | + super(cache); | ||
350 | + } | ||
351 | + | ||
352 | + @Override | ||
353 | + protected void onAdd(DeviceId deviceId, DefaultDevice device) { | ||
354 | + delegate.notify(new DeviceEvent(DEVICE_ADDED, device)); | ||
355 | + } | ||
356 | + | ||
357 | + @Override | ||
358 | + protected void onRemove(DeviceId deviceId, DefaultDevice device) { | ||
359 | + delegate.notify(new DeviceEvent(DEVICE_REMOVED, device)); | ||
360 | + } | ||
361 | + | ||
362 | + @Override | ||
363 | + protected void onUpdate(DeviceId deviceId, DefaultDevice device) { | ||
364 | + delegate.notify(new DeviceEvent(DEVICE_UPDATED, device)); | ||
365 | + } | ||
366 | + } | ||
367 | + | ||
368 | + private class RemotePortEventHandler extends RemoteEventHandler<DeviceId, Map<PortNumber, Port>> { | ||
369 | + public RemotePortEventHandler(LoadingCache<DeviceId, Optional<Map<PortNumber, Port>>> cache) { | ||
370 | + super(cache); | ||
371 | + } | ||
372 | + | ||
373 | + @Override | ||
374 | + protected void onAdd(DeviceId deviceId, Map<PortNumber, Port> ports) { | ||
375 | +// delegate.notify(new DeviceEvent(PORT_ADDED, getDevice(deviceId))); | ||
376 | + } | ||
377 | + | ||
378 | + @Override | ||
379 | + protected void onRemove(DeviceId deviceId, Map<PortNumber, Port> ports) { | ||
380 | +// delegate.notify(new DeviceEvent(PORT_REMOVED, getDevice(deviceId))); | ||
381 | + } | ||
382 | + | ||
383 | + @Override | ||
384 | + protected void onUpdate(DeviceId deviceId, Map<PortNumber, Port> ports) { | ||
385 | +// delegate.notify(new DeviceEvent(PORT_UPDATED, getDevice(deviceId))); | ||
386 | + } | ||
387 | + } | ||
388 | + | ||
389 | + | ||
346 | // TODO cache serialized DeviceID if we suffer from serialization cost | 390 | // TODO cache serialized DeviceID if we suffer from serialization cost |
347 | } | 391 | } | ... | ... |
... | @@ -6,7 +6,6 @@ import com.hazelcast.core.EntryAdapter; | ... | @@ -6,7 +6,6 @@ import com.hazelcast.core.EntryAdapter; |
6 | import com.hazelcast.core.EntryEvent; | 6 | import com.hazelcast.core.EntryEvent; |
7 | import com.hazelcast.core.HazelcastInstance; | 7 | import com.hazelcast.core.HazelcastInstance; |
8 | import com.hazelcast.core.MapEvent; | 8 | import com.hazelcast.core.MapEvent; |
9 | - | ||
10 | import org.apache.felix.scr.annotations.Activate; | 9 | import org.apache.felix.scr.annotations.Activate; |
11 | import org.apache.felix.scr.annotations.Component; | 10 | import org.apache.felix.scr.annotations.Component; |
12 | import org.apache.felix.scr.annotations.Reference; | 11 | import org.apache.felix.scr.annotations.Reference; |
... | @@ -25,7 +24,7 @@ import static org.slf4j.LoggerFactory.getLogger; | ... | @@ -25,7 +24,7 @@ import static org.slf4j.LoggerFactory.getLogger; |
25 | */ | 24 | */ |
26 | @Component(componentAbstract = true) | 25 | @Component(componentAbstract = true) |
27 | public abstract class AbstractDistributedStore<E extends Event, D extends StoreDelegate<E>> | 26 | public abstract class AbstractDistributedStore<E extends Event, D extends StoreDelegate<E>> |
28 | - extends AbstractStore<E, D> { | 27 | + extends AbstractStore<E, D> { |
29 | 28 | ||
30 | protected final Logger log = getLogger(getClass()); | 29 | protected final Logger log = getLogger(getClass()); |
31 | 30 | ||
... | @@ -67,7 +66,7 @@ public abstract class AbstractDistributedStore<E extends Event, D extends StoreD | ... | @@ -67,7 +66,7 @@ public abstract class AbstractDistributedStore<E extends Event, D extends StoreD |
67 | * @param <K> IMap key type after deserialization | 66 | * @param <K> IMap key type after deserialization |
68 | * @param <V> IMap value type after deserialization | 67 | * @param <V> IMap value type after deserialization |
69 | */ | 68 | */ |
70 | - public final class RemoteEventHandler<K, V> extends EntryAdapter<byte[], byte[]> { | 69 | + public class RemoteEventHandler<K, V> extends EntryAdapter<byte[], byte[]> { |
71 | 70 | ||
72 | private LoadingCache<K, Optional<V>> cache; | 71 | private LoadingCache<K, Optional<V>> cache; |
73 | 72 | ||
... | @@ -86,26 +85,58 @@ public abstract class AbstractDistributedStore<E extends Event, D extends StoreD | ... | @@ -86,26 +85,58 @@ public abstract class AbstractDistributedStore<E extends Event, D extends StoreD |
86 | } | 85 | } |
87 | 86 | ||
88 | @Override | 87 | @Override |
88 | + public void entryAdded(EntryEvent<byte[], byte[]> event) { | ||
89 | + K key = deserialize(event.getKey()); | ||
90 | + V newVal = deserialize(event.getValue()); | ||
91 | + Optional<V> newValue = Optional.of(newVal); | ||
92 | + cache.asMap().putIfAbsent(key, newValue); | ||
93 | + onAdd(key, newVal); | ||
94 | + } | ||
95 | + | ||
96 | + @Override | ||
89 | public void entryUpdated(EntryEvent<byte[], byte[]> event) { | 97 | public void entryUpdated(EntryEvent<byte[], byte[]> event) { |
90 | - K key = storeService.<K>deserialize(event.getKey()); | 98 | + K key = deserialize(event.getKey()); |
91 | - final V oldVal = storeService.<V>deserialize(event.getOldValue()); | 99 | + V oldVal = deserialize(event.getOldValue()); |
92 | Optional<V> oldValue = Optional.fromNullable(oldVal); | 100 | Optional<V> oldValue = Optional.fromNullable(oldVal); |
93 | - final V newVal = storeService.<V>deserialize(event.getValue()); | 101 | + V newVal = deserialize(event.getValue()); |
94 | Optional<V> newValue = Optional.of(newVal); | 102 | Optional<V> newValue = Optional.of(newVal); |
95 | cache.asMap().replace(key, oldValue, newValue); | 103 | cache.asMap().replace(key, oldValue, newValue); |
104 | + onUpdate(key, newVal); | ||
96 | } | 105 | } |
97 | 106 | ||
98 | @Override | 107 | @Override |
99 | public void entryRemoved(EntryEvent<byte[], byte[]> event) { | 108 | public void entryRemoved(EntryEvent<byte[], byte[]> event) { |
100 | - cache.invalidate(storeService.<K>deserialize(event.getKey())); | 109 | + K key = deserialize(event.getKey()); |
110 | + V val = deserialize(event.getValue()); | ||
111 | + cache.invalidate(key); | ||
112 | + onRemove(key, val); | ||
101 | } | 113 | } |
102 | 114 | ||
103 | - @Override | 115 | + /** |
104 | - public void entryAdded(EntryEvent<byte[], byte[]> event) { | 116 | + * Cache entry addition hook. |
105 | - K key = storeService.<K>deserialize(event.getKey()); | 117 | + * |
106 | - final V newVal = storeService.<V>deserialize(event.getValue()); | 118 | + * @param key new key |
107 | - Optional<V> newValue = Optional.of(newVal); | 119 | + * @param newVal new value |
108 | - cache.asMap().putIfAbsent(key, newValue); | 120 | + */ |
121 | + protected void onAdd(K key, V newVal) { | ||
122 | + } | ||
123 | + | ||
124 | + /** | ||
125 | + * Cache entry update hook. | ||
126 | + * | ||
127 | + * @param key new key | ||
128 | + * @param newVal new value | ||
129 | + */ | ||
130 | + protected void onUpdate(K key, V newVal) { | ||
131 | + } | ||
132 | + | ||
133 | + /** | ||
134 | + * Cache entry remove hook. | ||
135 | + * | ||
136 | + * @param key new key | ||
137 | + * @param val old value | ||
138 | + */ | ||
139 | + protected void onRemove(K key, V val) { | ||
109 | } | 140 | } |
110 | } | 141 | } |
111 | 142 | ... | ... |
-
Please register or login to post a comment