DeviceStore update
- Add off-line/remove handling to Gossip~ - Backport lock scope to Simple~ Change-Id: I5b4c8e12738ef78920341fb8699c4b07bde8712a
Showing
2 changed files
with
300 additions
and
142 deletions
... | @@ -2,7 +2,8 @@ package org.onlab.onos.store.device.impl; | ... | @@ -2,7 +2,8 @@ package org.onlab.onos.store.device.impl; |
2 | 2 | ||
3 | import com.google.common.collect.FluentIterable; | 3 | import com.google.common.collect.FluentIterable; |
4 | import com.google.common.collect.ImmutableList; | 4 | import com.google.common.collect.ImmutableList; |
5 | - | 5 | +import com.google.common.collect.Maps; |
6 | +import com.google.common.collect.Sets; | ||
6 | import org.apache.commons.lang3.concurrent.ConcurrentException; | 7 | import org.apache.commons.lang3.concurrent.ConcurrentException; |
7 | import org.apache.commons.lang3.concurrent.ConcurrentInitializer; | 8 | import org.apache.commons.lang3.concurrent.ConcurrentInitializer; |
8 | import org.apache.felix.scr.annotations.Activate; | 9 | import org.apache.felix.scr.annotations.Activate; |
... | @@ -59,7 +60,7 @@ import static org.onlab.onos.net.DefaultAnnotations.merge; | ... | @@ -59,7 +60,7 @@ import static org.onlab.onos.net.DefaultAnnotations.merge; |
59 | import static org.onlab.onos.net.DefaultAnnotations.union; | 60 | import static org.onlab.onos.net.DefaultAnnotations.union; |
60 | import static com.google.common.base.Verify.verify; | 61 | import static com.google.common.base.Verify.verify; |
61 | 62 | ||
62 | -// TODO: implement remove event handling and call *Internal | 63 | +// TODO: give me a better name |
63 | /** | 64 | /** |
64 | * Manages inventory of infrastructure devices using gossip protocol to distribute | 65 | * Manages inventory of infrastructure devices using gossip protocol to distribute |
65 | * information. | 66 | * information. |
... | @@ -79,14 +80,18 @@ public class GossipDeviceStore | ... | @@ -79,14 +80,18 @@ public class GossipDeviceStore |
79 | // collection of Description given from various providers | 80 | // collection of Description given from various providers |
80 | private final ConcurrentMap<DeviceId, | 81 | private final ConcurrentMap<DeviceId, |
81 | ConcurrentMap<ProviderId, DeviceDescriptions>> | 82 | ConcurrentMap<ProviderId, DeviceDescriptions>> |
82 | - deviceDescs = new ConcurrentHashMap<>(); | 83 | + deviceDescs = Maps.newConcurrentMap(); |
83 | 84 | ||
84 | // cache of Device and Ports generated by compositing descriptions from providers | 85 | // cache of Device and Ports generated by compositing descriptions from providers |
85 | - private final ConcurrentMap<DeviceId, Device> devices = new ConcurrentHashMap<>(); | 86 | + private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap(); |
86 | - private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = new ConcurrentHashMap<>(); | 87 | + private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = Maps.newConcurrentMap(); |
88 | + | ||
89 | + // to be updated under Device lock | ||
90 | + private final Map<DeviceId, Timestamp> offline = Maps.newHashMap(); | ||
91 | + private final Map<DeviceId, Timestamp> removalRequest = Maps.newHashMap(); | ||
87 | 92 | ||
88 | // available(=UP) devices | 93 | // available(=UP) devices |
89 | - private final Set<DeviceId> availableDevices = new HashSet<>(); | 94 | + private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet(); |
90 | 95 | ||
91 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 96 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
92 | protected ClockService clockService; | 97 | protected ClockService clockService; |
... | @@ -121,7 +126,8 @@ public class GossipDeviceStore | ... | @@ -121,7 +126,8 @@ public class GossipDeviceStore |
121 | } | 126 | } |
122 | 127 | ||
123 | @Override | 128 | @Override |
124 | - public synchronized DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId, | 129 | + public synchronized DeviceEvent createOrUpdateDevice(ProviderId providerId, |
130 | + DeviceId deviceId, | ||
125 | DeviceDescription deviceDescription) { | 131 | DeviceDescription deviceDescription) { |
126 | Timestamp newTimestamp = clockService.getTimestamp(deviceId); | 132 | Timestamp newTimestamp = clockService.getTimestamp(deviceId); |
127 | final Timestamped<DeviceDescription> deltaDesc = new Timestamped<>(deviceDescription, newTimestamp); | 133 | final Timestamped<DeviceDescription> deltaDesc = new Timestamped<>(deviceDescription, newTimestamp); |
... | @@ -133,22 +139,26 @@ public class GossipDeviceStore | ... | @@ -133,22 +139,26 @@ public class GossipDeviceStore |
133 | return event; | 139 | return event; |
134 | } | 140 | } |
135 | 141 | ||
136 | - private DeviceEvent createOrUpdateDeviceInternal(ProviderId providerId, DeviceId deviceId, | 142 | + private DeviceEvent createOrUpdateDeviceInternal(ProviderId providerId, |
137 | - Timestamped<DeviceDescription> deltaDesc) { | 143 | + DeviceId deviceId, |
144 | + Timestamped<DeviceDescription> deltaDesc) { | ||
138 | 145 | ||
139 | // Collection of DeviceDescriptions for a Device | 146 | // Collection of DeviceDescriptions for a Device |
140 | ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs | 147 | ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs |
141 | = getDeviceDescriptions(deviceId); | 148 | = getDeviceDescriptions(deviceId); |
142 | 149 | ||
143 | - | ||
144 | - DeviceDescriptions descs | ||
145 | - = createIfAbsentUnchecked(providerDescs, providerId, | ||
146 | - new InitDeviceDescs(deltaDesc)); | ||
147 | - | ||
148 | - // update description | ||
149 | synchronized (providerDescs) { | 150 | synchronized (providerDescs) { |
150 | // locking per device | 151 | // locking per device |
151 | 152 | ||
153 | + if (isDeviceRemoved(deviceId, deltaDesc.timestamp())) { | ||
154 | + log.debug("Ignoring outdated event: {}", deltaDesc); | ||
155 | + return null; | ||
156 | + } | ||
157 | + | ||
158 | + DeviceDescriptions descs | ||
159 | + = createIfAbsentUnchecked(providerDescs, providerId, | ||
160 | + new InitDeviceDescs(deltaDesc)); | ||
161 | + | ||
152 | final Device oldDevice = devices.get(deviceId); | 162 | final Device oldDevice = devices.get(deviceId); |
153 | final Device newDevice; | 163 | final Device newDevice; |
154 | 164 | ||
... | @@ -163,18 +173,18 @@ public class GossipDeviceStore | ... | @@ -163,18 +173,18 @@ public class GossipDeviceStore |
163 | } | 173 | } |
164 | if (oldDevice == null) { | 174 | if (oldDevice == null) { |
165 | // ADD | 175 | // ADD |
166 | - return createDevice(providerId, newDevice); | 176 | + return createDevice(providerId, newDevice, deltaDesc.timestamp()); |
167 | } else { | 177 | } else { |
168 | // UPDATE or ignore (no change or stale) | 178 | // UPDATE or ignore (no change or stale) |
169 | - return updateDevice(providerId, oldDevice, newDevice); | 179 | + return updateDevice(providerId, oldDevice, newDevice, deltaDesc.timestamp()); |
170 | } | 180 | } |
171 | } | 181 | } |
172 | } | 182 | } |
173 | 183 | ||
174 | // Creates the device and returns the appropriate event if necessary. | 184 | // Creates the device and returns the appropriate event if necessary. |
175 | - // Guarded by deviceDescs value (=locking Device) | 185 | + // Guarded by deviceDescs value (=Device lock) |
176 | private DeviceEvent createDevice(ProviderId providerId, | 186 | private DeviceEvent createDevice(ProviderId providerId, |
177 | - Device newDevice) { | 187 | + Device newDevice, Timestamp timestamp) { |
178 | 188 | ||
179 | // update composed device cache | 189 | // update composed device cache |
180 | Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice); | 190 | Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice); |
... | @@ -183,16 +193,17 @@ public class GossipDeviceStore | ... | @@ -183,16 +193,17 @@ public class GossipDeviceStore |
183 | providerId, oldDevice, newDevice); | 193 | providerId, oldDevice, newDevice); |
184 | 194 | ||
185 | if (!providerId.isAncillary()) { | 195 | if (!providerId.isAncillary()) { |
186 | - availableDevices.add(newDevice.id()); | 196 | + markOnline(newDevice.id(), timestamp); |
187 | } | 197 | } |
188 | 198 | ||
189 | return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null); | 199 | return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null); |
190 | } | 200 | } |
191 | 201 | ||
192 | // Updates the device and returns the appropriate event if necessary. | 202 | // Updates the device and returns the appropriate event if necessary. |
193 | - // Guarded by deviceDescs value (=locking Device) | 203 | + // Guarded by deviceDescs value (=Device lock) |
194 | private DeviceEvent updateDevice(ProviderId providerId, | 204 | private DeviceEvent updateDevice(ProviderId providerId, |
195 | - Device oldDevice, Device newDevice) { | 205 | + Device oldDevice, |
206 | + Device newDevice, Timestamp newTimestamp) { | ||
196 | 207 | ||
197 | // We allow only certain attributes to trigger update | 208 | // We allow only certain attributes to trigger update |
198 | if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) || | 209 | if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) || |
... | @@ -207,14 +218,14 @@ public class GossipDeviceStore | ... | @@ -207,14 +218,14 @@ public class GossipDeviceStore |
207 | , newDevice); | 218 | , newDevice); |
208 | } | 219 | } |
209 | if (!providerId.isAncillary()) { | 220 | if (!providerId.isAncillary()) { |
210 | - availableDevices.add(newDevice.id()); | 221 | + markOnline(newDevice.id(), newTimestamp); |
211 | } | 222 | } |
212 | return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null); | 223 | return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null); |
213 | } | 224 | } |
214 | 225 | ||
215 | // Otherwise merely attempt to change availability if primary provider | 226 | // Otherwise merely attempt to change availability if primary provider |
216 | if (!providerId.isAncillary()) { | 227 | if (!providerId.isAncillary()) { |
217 | - boolean added = availableDevices.add(newDevice.id()); | 228 | + boolean added = markOnline(newDevice.id(), newTimestamp); |
218 | return !added ? null : | 229 | return !added ? null : |
219 | new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null); | 230 | new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null); |
220 | } | 231 | } |
... | @@ -223,11 +234,29 @@ public class GossipDeviceStore | ... | @@ -223,11 +234,29 @@ public class GossipDeviceStore |
223 | 234 | ||
224 | @Override | 235 | @Override |
225 | public DeviceEvent markOffline(DeviceId deviceId) { | 236 | public DeviceEvent markOffline(DeviceId deviceId) { |
226 | - ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs | 237 | + Timestamp timestamp = clockService.getTimestamp(deviceId); |
238 | + return markOfflineInternal(deviceId, timestamp); | ||
239 | + } | ||
240 | + | ||
241 | + private DeviceEvent markOfflineInternal(DeviceId deviceId, Timestamp timestamp) { | ||
242 | + | ||
243 | + Map<ProviderId, DeviceDescriptions> providerDescs | ||
227 | = getDeviceDescriptions(deviceId); | 244 | = getDeviceDescriptions(deviceId); |
228 | 245 | ||
229 | // locking device | 246 | // locking device |
230 | synchronized (providerDescs) { | 247 | synchronized (providerDescs) { |
248 | + | ||
249 | + // accept off-line if given timestamp is newer than | ||
250 | + // the latest Timestamp from Primary provider | ||
251 | + DeviceDescriptions primDescs = getPrimaryDescriptions(providerDescs); | ||
252 | + Timestamp lastTimestamp = primDescs.getLatestTimestamp(); | ||
253 | + if (timestamp.compareTo(lastTimestamp) <= 0) { | ||
254 | + // outdated event ignore | ||
255 | + return null; | ||
256 | + } | ||
257 | + | ||
258 | + offline.put(deviceId, timestamp); | ||
259 | + | ||
231 | Device device = devices.get(deviceId); | 260 | Device device = devices.get(deviceId); |
232 | if (device == null) { | 261 | if (device == null) { |
233 | return null; | 262 | return null; |
... | @@ -236,15 +265,37 @@ public class GossipDeviceStore | ... | @@ -236,15 +265,37 @@ public class GossipDeviceStore |
236 | if (removed) { | 265 | if (removed) { |
237 | // TODO: broadcast ... DOWN only? | 266 | // TODO: broadcast ... DOWN only? |
238 | return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null); | 267 | return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null); |
239 | - | ||
240 | } | 268 | } |
241 | return null; | 269 | return null; |
242 | } | 270 | } |
243 | } | 271 | } |
244 | 272 | ||
273 | + /** | ||
274 | + * Marks the device as available if the given timestamp is not outdated, | ||
275 | + * compared to the time the device has been marked offline. | ||
276 | + * | ||
277 | + * @param deviceId identifier of the device | ||
278 | + * @param timestamp of the event triggering this change. | ||
279 | + * @return true if availability change request was accepted and changed the state | ||
280 | + */ | ||
281 | + // Guarded by deviceDescs value (=Device lock) | ||
282 | + private boolean markOnline(DeviceId deviceId, Timestamp timestamp) { | ||
283 | + // accept on-line if given timestamp is newer than | ||
284 | + // the latest offline request Timestamp | ||
285 | + Timestamp offlineTimestamp = offline.get(deviceId); | ||
286 | + if (offlineTimestamp == null || | ||
287 | + offlineTimestamp.compareTo(timestamp) < 0) { | ||
288 | + | ||
289 | + offline.remove(deviceId); | ||
290 | + return availableDevices.add(deviceId); | ||
291 | + } | ||
292 | + return false; | ||
293 | + } | ||
294 | + | ||
245 | @Override | 295 | @Override |
246 | - public synchronized List<DeviceEvent> updatePorts(ProviderId providerId, DeviceId deviceId, | 296 | + public synchronized List<DeviceEvent> updatePorts(ProviderId providerId, |
247 | - List<PortDescription> portDescriptions) { | 297 | + DeviceId deviceId, |
298 | + List<PortDescription> portDescriptions) { | ||
248 | Timestamp newTimestamp = clockService.getTimestamp(deviceId); | 299 | Timestamp newTimestamp = clockService.getTimestamp(deviceId); |
249 | 300 | ||
250 | List<Timestamped<PortDescription>> deltaDescs = new ArrayList<>(portDescriptions.size()); | 301 | List<Timestamped<PortDescription>> deltaDescs = new ArrayList<>(portDescriptions.size()); |
... | @@ -252,7 +303,8 @@ public class GossipDeviceStore | ... | @@ -252,7 +303,8 @@ public class GossipDeviceStore |
252 | deltaDescs.add(new Timestamped<PortDescription>(e, newTimestamp)); | 303 | deltaDescs.add(new Timestamped<PortDescription>(e, newTimestamp)); |
253 | } | 304 | } |
254 | 305 | ||
255 | - List<DeviceEvent> events = updatePortsInternal(providerId, deviceId, deltaDescs); | 306 | + List<DeviceEvent> events = updatePortsInternal(providerId, deviceId, |
307 | + new Timestamped<>(portDescriptions, newTimestamp)); | ||
256 | if (!events.isEmpty()) { | 308 | if (!events.isEmpty()) { |
257 | // FIXME: broadcast deltaDesc, UP | 309 | // FIXME: broadcast deltaDesc, UP |
258 | log.debug("broadcast deltaDesc"); | 310 | log.debug("broadcast deltaDesc"); |
... | @@ -261,8 +313,9 @@ public class GossipDeviceStore | ... | @@ -261,8 +313,9 @@ public class GossipDeviceStore |
261 | 313 | ||
262 | } | 314 | } |
263 | 315 | ||
264 | - private List<DeviceEvent> updatePortsInternal(ProviderId providerId, DeviceId deviceId, | 316 | + private List<DeviceEvent> updatePortsInternal(ProviderId providerId, |
265 | - List<Timestamped<PortDescription>> deltaDescs) { | 317 | + DeviceId deviceId, |
318 | + Timestamped<List<PortDescription>> portDescriptions) { | ||
266 | 319 | ||
267 | Device device = devices.get(deviceId); | 320 | Device device = devices.get(deviceId); |
268 | checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); | 321 | checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); |
... | @@ -270,30 +323,41 @@ public class GossipDeviceStore | ... | @@ -270,30 +323,41 @@ public class GossipDeviceStore |
270 | ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId); | 323 | ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId); |
271 | checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId); | 324 | checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId); |
272 | 325 | ||
273 | - DeviceDescriptions descs = descsMap.get(providerId); | ||
274 | - // every provider must provide DeviceDescription. | ||
275 | - checkArgument(descs != null, | ||
276 | - "Device description for Device ID %s from Provider %s was not found", | ||
277 | - deviceId, providerId); | ||
278 | - | ||
279 | List<DeviceEvent> events = new ArrayList<>(); | 326 | List<DeviceEvent> events = new ArrayList<>(); |
280 | synchronized (descsMap) { | 327 | synchronized (descsMap) { |
328 | + | ||
329 | + if (isDeviceRemoved(deviceId, portDescriptions.timestamp())) { | ||
330 | + log.debug("Ignoring outdated events: {}", portDescriptions); | ||
331 | + return null; | ||
332 | + } | ||
333 | + | ||
334 | + DeviceDescriptions descs = descsMap.get(providerId); | ||
335 | + // every provider must provide DeviceDescription. | ||
336 | + checkArgument(descs != null, | ||
337 | + "Device description for Device ID %s from Provider %s was not found", | ||
338 | + deviceId, providerId); | ||
339 | + | ||
281 | Map<PortNumber, Port> ports = getPortMap(deviceId); | 340 | Map<PortNumber, Port> ports = getPortMap(deviceId); |
282 | 341 | ||
342 | + final Timestamp newTimestamp = portDescriptions.timestamp(); | ||
343 | + | ||
283 | // Add new ports | 344 | // Add new ports |
284 | Set<PortNumber> processed = new HashSet<>(); | 345 | Set<PortNumber> processed = new HashSet<>(); |
285 | - for (Timestamped<PortDescription> deltaDesc : deltaDescs) { | 346 | + for (PortDescription portDescription : portDescriptions.value()) { |
286 | - final PortNumber number = deltaDesc.value().portNumber(); | 347 | + final PortNumber number = portDescription.portNumber(); |
348 | + processed.add(number); | ||
349 | + | ||
287 | final Port oldPort = ports.get(number); | 350 | final Port oldPort = ports.get(number); |
288 | final Port newPort; | 351 | final Port newPort; |
289 | 352 | ||
353 | + | ||
290 | final Timestamped<PortDescription> existingPortDesc = descs.getPortDesc(number); | 354 | final Timestamped<PortDescription> existingPortDesc = descs.getPortDesc(number); |
291 | if (existingPortDesc == null || | 355 | if (existingPortDesc == null || |
292 | - deltaDesc == existingPortDesc || | 356 | + newTimestamp.compareTo(existingPortDesc.timestamp()) >= 0) { |
293 | - deltaDesc.isNewer(existingPortDesc)) { | ||
294 | // on new port or valid update | 357 | // on new port or valid update |
295 | // update description | 358 | // update description |
296 | - descs.putPortDesc(deltaDesc); | 359 | + descs.putPortDesc(new Timestamped<>(portDescription, |
360 | + portDescriptions.timestamp())); | ||
297 | newPort = composePort(device, number, descsMap); | 361 | newPort = composePort(device, number, descsMap); |
298 | } else { | 362 | } else { |
299 | // outdated event, ignored. | 363 | // outdated event, ignored. |
... | @@ -303,7 +367,6 @@ public class GossipDeviceStore | ... | @@ -303,7 +367,6 @@ public class GossipDeviceStore |
303 | events.add(oldPort == null ? | 367 | events.add(oldPort == null ? |
304 | createPort(device, newPort, ports) : | 368 | createPort(device, newPort, ports) : |
305 | updatePort(device, oldPort, newPort, ports)); | 369 | updatePort(device, oldPort, newPort, ports)); |
306 | - processed.add(number); | ||
307 | } | 370 | } |
308 | 371 | ||
309 | events.addAll(pruneOldPorts(device, ports, processed)); | 372 | events.addAll(pruneOldPorts(device, ports, processed)); |
... | @@ -313,7 +376,7 @@ public class GossipDeviceStore | ... | @@ -313,7 +376,7 @@ public class GossipDeviceStore |
313 | 376 | ||
314 | // Creates a new port based on the port description adds it to the map and | 377 | // Creates a new port based on the port description adds it to the map and |
315 | // Returns corresponding event. | 378 | // Returns corresponding event. |
316 | - // Guarded by deviceDescs value (=locking Device) | 379 | + // Guarded by deviceDescs value (=Device lock) |
317 | private DeviceEvent createPort(Device device, Port newPort, | 380 | private DeviceEvent createPort(Device device, Port newPort, |
318 | Map<PortNumber, Port> ports) { | 381 | Map<PortNumber, Port> ports) { |
319 | ports.put(newPort.number(), newPort); | 382 | ports.put(newPort.number(), newPort); |
... | @@ -322,7 +385,7 @@ public class GossipDeviceStore | ... | @@ -322,7 +385,7 @@ public class GossipDeviceStore |
322 | 385 | ||
323 | // Checks if the specified port requires update and if so, it replaces the | 386 | // Checks if the specified port requires update and if so, it replaces the |
324 | // existing entry in the map and returns corresponding event. | 387 | // existing entry in the map and returns corresponding event. |
325 | - // Guarded by deviceDescs value (=locking Device) | 388 | + // Guarded by deviceDescs value (=Device lock) |
326 | private DeviceEvent updatePort(Device device, Port oldPort, | 389 | private DeviceEvent updatePort(Device device, Port oldPort, |
327 | Port newPort, | 390 | Port newPort, |
328 | Map<PortNumber, Port> ports) { | 391 | Map<PortNumber, Port> ports) { |
... | @@ -337,7 +400,7 @@ public class GossipDeviceStore | ... | @@ -337,7 +400,7 @@ public class GossipDeviceStore |
337 | 400 | ||
338 | // Prunes the specified list of ports based on which ports are in the | 401 | // Prunes the specified list of ports based on which ports are in the |
339 | // processed list and returns list of corresponding events. | 402 | // processed list and returns list of corresponding events. |
340 | - // Guarded by deviceDescs value (=locking Device) | 403 | + // Guarded by deviceDescs value (=Device lock) |
341 | private List<DeviceEvent> pruneOldPorts(Device device, | 404 | private List<DeviceEvent> pruneOldPorts(Device device, |
342 | Map<PortNumber, Port> ports, | 405 | Map<PortNumber, Port> ports, |
343 | Set<PortNumber> processed) { | 406 | Set<PortNumber> processed) { |
... | @@ -389,13 +452,19 @@ public class GossipDeviceStore | ... | @@ -389,13 +452,19 @@ public class GossipDeviceStore |
389 | ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId); | 452 | ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId); |
390 | checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId); | 453 | checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId); |
391 | 454 | ||
392 | - DeviceDescriptions descs = descsMap.get(providerId); | ||
393 | - // assuming all providers must to give DeviceDescription | ||
394 | - checkArgument(descs != null, | ||
395 | - "Device description for Device ID %s from Provider %s was not found", | ||
396 | - deviceId, providerId); | ||
397 | - | ||
398 | synchronized (descsMap) { | 455 | synchronized (descsMap) { |
456 | + | ||
457 | + if (isDeviceRemoved(deviceId, deltaDesc.timestamp())) { | ||
458 | + log.debug("Ignoring outdated event: {}", deltaDesc); | ||
459 | + return null; | ||
460 | + } | ||
461 | + | ||
462 | + DeviceDescriptions descs = descsMap.get(providerId); | ||
463 | + // assuming all providers must to give DeviceDescription | ||
464 | + checkArgument(descs != null, | ||
465 | + "Device description for Device ID %s from Provider %s was not found", | ||
466 | + deviceId, providerId); | ||
467 | + | ||
399 | ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId); | 468 | ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId); |
400 | final PortNumber number = deltaDesc.value().portNumber(); | 469 | final PortNumber number = deltaDesc.value().portNumber(); |
401 | final Port oldPort = ports.get(number); | 470 | final Port oldPort = ports.get(number); |
... | @@ -443,19 +512,51 @@ public class GossipDeviceStore | ... | @@ -443,19 +512,51 @@ public class GossipDeviceStore |
443 | } | 512 | } |
444 | 513 | ||
445 | @Override | 514 | @Override |
446 | - public DeviceEvent removeDevice(DeviceId deviceId) { | 515 | + public synchronized DeviceEvent removeDevice(DeviceId deviceId) { |
447 | - ConcurrentMap<ProviderId, DeviceDescriptions> descs = getDeviceDescriptions(deviceId); | 516 | + Timestamp timestamp = clockService.getTimestamp(deviceId); |
517 | + DeviceEvent event = removeDeviceInternal(deviceId, timestamp); | ||
518 | + // TODO: broadcast removal event | ||
519 | + return event; | ||
520 | + } | ||
521 | + | ||
522 | + private DeviceEvent removeDeviceInternal(DeviceId deviceId, | ||
523 | + Timestamp timestamp) { | ||
524 | + | ||
525 | + Map<ProviderId, DeviceDescriptions> descs = getDeviceDescriptions(deviceId); | ||
448 | synchronized (descs) { | 526 | synchronized (descs) { |
527 | + // accept removal request if given timestamp is newer than | ||
528 | + // the latest Timestamp from Primary provider | ||
529 | + DeviceDescriptions primDescs = getPrimaryDescriptions(descs); | ||
530 | + Timestamp lastTimestamp = primDescs.getLatestTimestamp(); | ||
531 | + if (timestamp.compareTo(lastTimestamp) <= 0) { | ||
532 | + // outdated event ignore | ||
533 | + return null; | ||
534 | + } | ||
535 | + removalRequest.put(deviceId, timestamp); | ||
536 | + | ||
449 | Device device = devices.remove(deviceId); | 537 | Device device = devices.remove(deviceId); |
450 | // should DEVICE_REMOVED carry removed ports? | 538 | // should DEVICE_REMOVED carry removed ports? |
451 | - devicePorts.get(deviceId).clear(); | 539 | + Map<PortNumber, Port> ports = devicePorts.get(deviceId); |
452 | - availableDevices.remove(deviceId); | 540 | + if (ports != null) { |
541 | + ports.clear(); | ||
542 | + } | ||
543 | + markOfflineInternal(deviceId, timestamp); | ||
453 | descs.clear(); | 544 | descs.clear(); |
454 | return device == null ? null : | 545 | return device == null ? null : |
455 | new DeviceEvent(DEVICE_REMOVED, device, null); | 546 | new DeviceEvent(DEVICE_REMOVED, device, null); |
456 | } | 547 | } |
457 | } | 548 | } |
458 | 549 | ||
550 | + private boolean isDeviceRemoved(DeviceId deviceId, Timestamp timestampToCheck) { | ||
551 | + Timestamp removalTimestamp = removalRequest.get(deviceId); | ||
552 | + if (removalTimestamp != null && | ||
553 | + removalTimestamp.compareTo(timestampToCheck) >= 0) { | ||
554 | + // removalRequest is more recent | ||
555 | + return true; | ||
556 | + } | ||
557 | + return false; | ||
558 | + } | ||
559 | + | ||
459 | /** | 560 | /** |
460 | * Returns a Device, merging description given from multiple Providers. | 561 | * Returns a Device, merging description given from multiple Providers. |
461 | * | 562 | * |
... | @@ -472,7 +573,7 @@ public class GossipDeviceStore | ... | @@ -472,7 +573,7 @@ public class GossipDeviceStore |
472 | 573 | ||
473 | DeviceDescriptions desc = providerDescs.get(primary); | 574 | DeviceDescriptions desc = providerDescs.get(primary); |
474 | 575 | ||
475 | - DeviceDescription base = desc.getDeviceDesc().value(); | 576 | + final DeviceDescription base = desc.getDeviceDesc().value(); |
476 | Type type = base.type(); | 577 | Type type = base.type(); |
477 | String manufacturer = base.manufacturer(); | 578 | String manufacturer = base.manufacturer(); |
478 | String hwVersion = base.hwVersion(); | 579 | String hwVersion = base.hwVersion(); |
... | @@ -545,7 +646,7 @@ public class GossipDeviceStore | ... | @@ -545,7 +646,7 @@ public class GossipDeviceStore |
545 | * @return primary ProviderID, or randomly chosen one if none exists | 646 | * @return primary ProviderID, or randomly chosen one if none exists |
546 | */ | 647 | */ |
547 | private ProviderId pickPrimaryPID( | 648 | private ProviderId pickPrimaryPID( |
548 | - ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) { | 649 | + Map<ProviderId, DeviceDescriptions> providerDescs) { |
549 | ProviderId fallBackPrimary = null; | 650 | ProviderId fallBackPrimary = null; |
550 | for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) { | 651 | for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) { |
551 | if (!e.getKey().isAncillary()) { | 652 | if (!e.getKey().isAncillary()) { |
... | @@ -558,6 +659,12 @@ public class GossipDeviceStore | ... | @@ -558,6 +659,12 @@ public class GossipDeviceStore |
558 | return fallBackPrimary; | 659 | return fallBackPrimary; |
559 | } | 660 | } |
560 | 661 | ||
662 | + private DeviceDescriptions getPrimaryDescriptions( | ||
663 | + Map<ProviderId, DeviceDescriptions> providerDescs) { | ||
664 | + ProviderId pid = pickPrimaryPID(providerDescs); | ||
665 | + return providerDescs.get(pid); | ||
666 | + } | ||
667 | + | ||
561 | public static final class InitDeviceDescs | 668 | public static final class InitDeviceDescs |
562 | implements ConcurrentInitializer<DeviceDescriptions> { | 669 | implements ConcurrentInitializer<DeviceDescriptions> { |
563 | 670 | ||
... | @@ -586,6 +693,16 @@ public class GossipDeviceStore | ... | @@ -586,6 +693,16 @@ public class GossipDeviceStore |
586 | this.portDescs = new ConcurrentHashMap<>(); | 693 | this.portDescs = new ConcurrentHashMap<>(); |
587 | } | 694 | } |
588 | 695 | ||
696 | + Timestamp getLatestTimestamp() { | ||
697 | + Timestamp latest = deviceDesc.get().timestamp(); | ||
698 | + for (Timestamped<PortDescription> desc : portDescs.values()) { | ||
699 | + if (desc.timestamp().compareTo(latest) > 0) { | ||
700 | + latest = desc.timestamp(); | ||
701 | + } | ||
702 | + } | ||
703 | + return latest; | ||
704 | + } | ||
705 | + | ||
589 | public Timestamped<DeviceDescription> getDeviceDesc() { | 706 | public Timestamped<DeviceDescription> getDeviceDesc() { |
590 | return deviceDesc.get(); | 707 | return deviceDesc.get(); |
591 | } | 708 | } | ... | ... |
... | @@ -2,6 +2,8 @@ package org.onlab.onos.store.trivial.impl; | ... | @@ -2,6 +2,8 @@ package org.onlab.onos.store.trivial.impl; |
2 | 2 | ||
3 | import com.google.common.collect.FluentIterable; | 3 | import com.google.common.collect.FluentIterable; |
4 | import com.google.common.collect.ImmutableList; | 4 | import com.google.common.collect.ImmutableList; |
5 | +import com.google.common.collect.Maps; | ||
6 | +import com.google.common.collect.Sets; | ||
5 | 7 | ||
6 | import org.apache.commons.lang3.concurrent.ConcurrentException; | 8 | import org.apache.commons.lang3.concurrent.ConcurrentException; |
7 | import org.apache.commons.lang3.concurrent.ConcurrentInitializer; | 9 | import org.apache.commons.lang3.concurrent.ConcurrentInitializer; |
... | @@ -32,7 +34,6 @@ import org.onlab.util.NewConcurrentHashMap; | ... | @@ -32,7 +34,6 @@ import org.onlab.util.NewConcurrentHashMap; |
32 | import org.slf4j.Logger; | 34 | import org.slf4j.Logger; |
33 | 35 | ||
34 | import java.util.ArrayList; | 36 | import java.util.ArrayList; |
35 | -import java.util.Collection; | ||
36 | import java.util.Collections; | 37 | import java.util.Collections; |
37 | import java.util.HashSet; | 38 | import java.util.HashSet; |
38 | import java.util.Iterator; | 39 | import java.util.Iterator; |
... | @@ -48,6 +49,7 @@ import java.util.concurrent.atomic.AtomicReference; | ... | @@ -48,6 +49,7 @@ import java.util.concurrent.atomic.AtomicReference; |
48 | import static com.google.common.base.Preconditions.checkArgument; | 49 | import static com.google.common.base.Preconditions.checkArgument; |
49 | import static com.google.common.base.Preconditions.checkNotNull; | 50 | import static com.google.common.base.Preconditions.checkNotNull; |
50 | import static com.google.common.base.Predicates.notNull; | 51 | import static com.google.common.base.Predicates.notNull; |
52 | +import static com.google.common.base.Verify.verify; | ||
51 | import static org.onlab.onos.net.device.DeviceEvent.Type.*; | 53 | import static org.onlab.onos.net.device.DeviceEvent.Type.*; |
52 | import static org.slf4j.LoggerFactory.getLogger; | 54 | import static org.slf4j.LoggerFactory.getLogger; |
53 | import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked; | 55 | import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked; |
... | @@ -71,14 +73,14 @@ public class SimpleDeviceStore | ... | @@ -71,14 +73,14 @@ public class SimpleDeviceStore |
71 | // collection of Description given from various providers | 73 | // collection of Description given from various providers |
72 | private final ConcurrentMap<DeviceId, | 74 | private final ConcurrentMap<DeviceId, |
73 | ConcurrentMap<ProviderId, DeviceDescriptions>> | 75 | ConcurrentMap<ProviderId, DeviceDescriptions>> |
74 | - deviceDescs = new ConcurrentHashMap<>(); | 76 | + deviceDescs = Maps.newConcurrentMap(); |
75 | 77 | ||
76 | // cache of Device and Ports generated by compositing descriptions from providers | 78 | // cache of Device and Ports generated by compositing descriptions from providers |
77 | - private final ConcurrentMap<DeviceId, Device> devices = new ConcurrentHashMap<>(); | 79 | + private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap(); |
78 | - private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = new ConcurrentHashMap<>(); | 80 | + private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = Maps.newConcurrentMap(); |
79 | 81 | ||
80 | // available(=UP) devices | 82 | // available(=UP) devices |
81 | - private final Set<DeviceId> availableDevices = new HashSet<>(); | 83 | + private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet(); |
82 | 84 | ||
83 | 85 | ||
84 | @Activate | 86 | @Activate |
... | @@ -88,6 +90,10 @@ public class SimpleDeviceStore | ... | @@ -88,6 +90,10 @@ public class SimpleDeviceStore |
88 | 90 | ||
89 | @Deactivate | 91 | @Deactivate |
90 | public void deactivate() { | 92 | public void deactivate() { |
93 | + deviceDescs.clear(); | ||
94 | + devices.clear(); | ||
95 | + devicePorts.clear(); | ||
96 | + availableDevices.clear(); | ||
91 | log.info("Stopped"); | 97 | log.info("Stopped"); |
92 | } | 98 | } |
93 | 99 | ||
... | @@ -107,45 +113,54 @@ public class SimpleDeviceStore | ... | @@ -107,45 +113,54 @@ public class SimpleDeviceStore |
107 | } | 113 | } |
108 | 114 | ||
109 | @Override | 115 | @Override |
110 | - public synchronized DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId, | 116 | + public DeviceEvent createOrUpdateDevice(ProviderId providerId, |
117 | + DeviceId deviceId, | ||
111 | DeviceDescription deviceDescription) { | 118 | DeviceDescription deviceDescription) { |
119 | + | ||
112 | ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs | 120 | ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs |
113 | = getDeviceDescriptions(deviceId); | 121 | = getDeviceDescriptions(deviceId); |
114 | 122 | ||
115 | - Device oldDevice = devices.get(deviceId); | 123 | + synchronized (providerDescs) { |
124 | + // locking per device | ||
116 | 125 | ||
117 | - DeviceDescriptions descs | 126 | + DeviceDescriptions descs |
118 | - = createIfAbsentUnchecked(providerDescs, providerId, | 127 | + = createIfAbsentUnchecked(providerDescs, providerId, |
119 | - new InitDeviceDescs(deviceDescription)); | 128 | + new InitDeviceDescs(deviceDescription)); |
120 | 129 | ||
121 | - // update description | 130 | + Device oldDevice = devices.get(deviceId); |
122 | - descs.putDeviceDesc(deviceDescription); | 131 | + // update description |
123 | - Device newDevice = composeDevice(deviceId, providerDescs); | 132 | + descs.putDeviceDesc(deviceDescription); |
133 | + Device newDevice = composeDevice(deviceId, providerDescs); | ||
124 | 134 | ||
125 | - if (oldDevice == null) { | 135 | + if (oldDevice == null) { |
126 | - // ADD | 136 | + // ADD |
127 | - return createDevice(providerId, newDevice); | 137 | + return createDevice(providerId, newDevice); |
128 | - } else { | 138 | + } else { |
129 | - // UPDATE or ignore (no change or stale) | 139 | + // UPDATE or ignore (no change or stale) |
130 | - return updateDevice(providerId, oldDevice, newDevice); | 140 | + return updateDevice(providerId, oldDevice, newDevice); |
141 | + } | ||
131 | } | 142 | } |
132 | } | 143 | } |
133 | 144 | ||
134 | // Creates the device and returns the appropriate event if necessary. | 145 | // Creates the device and returns the appropriate event if necessary. |
146 | + // Guarded by deviceDescs value (=Device lock) | ||
135 | private DeviceEvent createDevice(ProviderId providerId, Device newDevice) { | 147 | private DeviceEvent createDevice(ProviderId providerId, Device newDevice) { |
136 | 148 | ||
137 | // update composed device cache | 149 | // update composed device cache |
138 | - synchronized (this) { | 150 | + Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice); |
139 | - devices.putIfAbsent(newDevice.id(), newDevice); | 151 | + verify(oldDevice == null, |
140 | - if (!providerId.isAncillary()) { | 152 | + "Unexpected Device in cache. PID:%s [old=%s, new=%s]", |
141 | - availableDevices.add(newDevice.id()); | 153 | + providerId, oldDevice, newDevice); |
142 | - } | 154 | + |
155 | + if (!providerId.isAncillary()) { | ||
156 | + availableDevices.add(newDevice.id()); | ||
143 | } | 157 | } |
144 | 158 | ||
145 | return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null); | 159 | return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null); |
146 | } | 160 | } |
147 | 161 | ||
148 | // Updates the device and returns the appropriate event if necessary. | 162 | // Updates the device and returns the appropriate event if necessary. |
163 | + // Guarded by deviceDescs value (=Device lock) | ||
149 | private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) { | 164 | private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) { |
150 | 165 | ||
151 | // We allow only certain attributes to trigger update | 166 | // We allow only certain attributes to trigger update |
... | @@ -153,70 +168,87 @@ public class SimpleDeviceStore | ... | @@ -153,70 +168,87 @@ public class SimpleDeviceStore |
153 | !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) || | 168 | !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) || |
154 | !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) { | 169 | !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) { |
155 | 170 | ||
156 | - synchronized (this) { | 171 | + boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice); |
157 | - devices.replace(newDevice.id(), oldDevice, newDevice); | 172 | + if (!replaced) { |
158 | - if (!providerId.isAncillary()) { | 173 | + verify(replaced, |
159 | - availableDevices.add(newDevice.id()); | 174 | + "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]", |
160 | - } | 175 | + providerId, oldDevice, devices.get(newDevice.id()) |
176 | + , newDevice); | ||
177 | + } | ||
178 | + if (!providerId.isAncillary()) { | ||
179 | + availableDevices.add(newDevice.id()); | ||
161 | } | 180 | } |
162 | return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null); | 181 | return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null); |
163 | } | 182 | } |
164 | 183 | ||
165 | // Otherwise merely attempt to change availability if primary provider | 184 | // Otherwise merely attempt to change availability if primary provider |
166 | if (!providerId.isAncillary()) { | 185 | if (!providerId.isAncillary()) { |
167 | - synchronized (this) { | ||
168 | boolean added = availableDevices.add(newDevice.id()); | 186 | boolean added = availableDevices.add(newDevice.id()); |
169 | return !added ? null : | 187 | return !added ? null : |
170 | new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null); | 188 | new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null); |
171 | - } | ||
172 | } | 189 | } |
173 | return null; | 190 | return null; |
174 | } | 191 | } |
175 | 192 | ||
176 | @Override | 193 | @Override |
177 | public DeviceEvent markOffline(DeviceId deviceId) { | 194 | public DeviceEvent markOffline(DeviceId deviceId) { |
178 | - synchronized (this) { | 195 | + ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs |
196 | + = getDeviceDescriptions(deviceId); | ||
197 | + | ||
198 | + // locking device | ||
199 | + synchronized (providerDescs) { | ||
179 | Device device = devices.get(deviceId); | 200 | Device device = devices.get(deviceId); |
180 | - boolean removed = (device != null) && availableDevices.remove(deviceId); | 201 | + if (device == null) { |
181 | - return !removed ? null : | 202 | + return null; |
182 | - new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null); | 203 | + } |
204 | + boolean removed = availableDevices.remove(deviceId); | ||
205 | + if (removed) { | ||
206 | + // TODO: broadcast ... DOWN only? | ||
207 | + return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null); | ||
208 | + } | ||
209 | + return null; | ||
183 | } | 210 | } |
184 | } | 211 | } |
185 | 212 | ||
186 | @Override | 213 | @Override |
187 | - public synchronized List<DeviceEvent> updatePorts(ProviderId providerId, DeviceId deviceId, | 214 | + public List<DeviceEvent> updatePorts(ProviderId providerId, |
188 | - List<PortDescription> portDescriptions) { | 215 | + DeviceId deviceId, |
216 | + List<PortDescription> portDescriptions) { | ||
189 | 217 | ||
190 | - // TODO: implement multi-provider | ||
191 | Device device = devices.get(deviceId); | 218 | Device device = devices.get(deviceId); |
192 | checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); | 219 | checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); |
193 | 220 | ||
194 | ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId); | 221 | ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId); |
195 | checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId); | 222 | checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId); |
196 | 223 | ||
197 | - DeviceDescriptions descs = descsMap.get(providerId); | ||
198 | - checkArgument(descs != null, | ||
199 | - "Device description for Device ID %s from Provider %s was not found", | ||
200 | - deviceId, providerId); | ||
201 | - | ||
202 | - | ||
203 | List<DeviceEvent> events = new ArrayList<>(); | 224 | List<DeviceEvent> events = new ArrayList<>(); |
204 | - synchronized (this) { | 225 | + synchronized (descsMap) { |
205 | - ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId); | 226 | + DeviceDescriptions descs = descsMap.get(providerId); |
227 | + // every provider must provide DeviceDescription. | ||
228 | + checkArgument(descs != null, | ||
229 | + "Device description for Device ID %s from Provider %s was not found", | ||
230 | + deviceId, providerId); | ||
231 | + | ||
232 | + Map<PortNumber, Port> ports = getPortMap(deviceId); | ||
206 | 233 | ||
207 | // Add new ports | 234 | // Add new ports |
208 | Set<PortNumber> processed = new HashSet<>(); | 235 | Set<PortNumber> processed = new HashSet<>(); |
209 | for (PortDescription portDescription : portDescriptions) { | 236 | for (PortDescription portDescription : portDescriptions) { |
210 | - PortNumber number = portDescription.portNumber(); | 237 | + final PortNumber number = portDescription.portNumber(); |
211 | - Port oldPort = ports.get(number); | 238 | + processed.add(portDescription.portNumber()); |
239 | + | ||
240 | + final Port oldPort = ports.get(number); | ||
241 | + final Port newPort; | ||
242 | + | ||
243 | +// event suppression hook? | ||
244 | + | ||
212 | // update description | 245 | // update description |
213 | descs.putPortDesc(portDescription); | 246 | descs.putPortDesc(portDescription); |
214 | - Port newPort = composePort(device, number, descsMap); | 247 | + newPort = composePort(device, number, descsMap); |
215 | 248 | ||
216 | events.add(oldPort == null ? | 249 | events.add(oldPort == null ? |
217 | - createPort(device, newPort, ports) : | 250 | + createPort(device, newPort, ports) : |
218 | - updatePort(device, oldPort, newPort, ports)); | 251 | + updatePort(device, oldPort, newPort, ports)); |
219 | - processed.add(portDescription.portNumber()); | ||
220 | } | 252 | } |
221 | 253 | ||
222 | events.addAll(pruneOldPorts(device, ports, processed)); | 254 | events.addAll(pruneOldPorts(device, ports, processed)); |
... | @@ -226,17 +258,19 @@ public class SimpleDeviceStore | ... | @@ -226,17 +258,19 @@ public class SimpleDeviceStore |
226 | 258 | ||
227 | // Creates a new port based on the port description adds it to the map and | 259 | // Creates a new port based on the port description adds it to the map and |
228 | // Returns corresponding event. | 260 | // Returns corresponding event. |
261 | + // Guarded by deviceDescs value (=Device lock) | ||
229 | private DeviceEvent createPort(Device device, Port newPort, | 262 | private DeviceEvent createPort(Device device, Port newPort, |
230 | - ConcurrentMap<PortNumber, Port> ports) { | 263 | + Map<PortNumber, Port> ports) { |
231 | ports.put(newPort.number(), newPort); | 264 | ports.put(newPort.number(), newPort); |
232 | return new DeviceEvent(PORT_ADDED, device, newPort); | 265 | return new DeviceEvent(PORT_ADDED, device, newPort); |
233 | } | 266 | } |
234 | 267 | ||
235 | // Checks if the specified port requires update and if so, it replaces the | 268 | // Checks if the specified port requires update and if so, it replaces the |
236 | // existing entry in the map and returns corresponding event. | 269 | // existing entry in the map and returns corresponding event. |
270 | + // Guarded by deviceDescs value (=Device lock) | ||
237 | private DeviceEvent updatePort(Device device, Port oldPort, | 271 | private DeviceEvent updatePort(Device device, Port oldPort, |
238 | Port newPort, | 272 | Port newPort, |
239 | - ConcurrentMap<PortNumber, Port> ports) { | 273 | + Map<PortNumber, Port> ports) { |
240 | if (oldPort.isEnabled() != newPort.isEnabled() || | 274 | if (oldPort.isEnabled() != newPort.isEnabled() || |
241 | !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) { | 275 | !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) { |
242 | 276 | ||
... | @@ -248,6 +282,7 @@ public class SimpleDeviceStore | ... | @@ -248,6 +282,7 @@ public class SimpleDeviceStore |
248 | 282 | ||
249 | // Prunes the specified list of ports based on which ports are in the | 283 | // Prunes the specified list of ports based on which ports are in the |
250 | // processed list and returns list of corresponding events. | 284 | // processed list and returns list of corresponding events. |
285 | + // Guarded by deviceDescs value (=Device lock) | ||
251 | private List<DeviceEvent> pruneOldPorts(Device device, | 286 | private List<DeviceEvent> pruneOldPorts(Device device, |
252 | Map<PortNumber, Port> ports, | 287 | Map<PortNumber, Port> ports, |
253 | Set<PortNumber> processed) { | 288 | Set<PortNumber> processed) { |
... | @@ -264,12 +299,6 @@ public class SimpleDeviceStore | ... | @@ -264,12 +299,6 @@ public class SimpleDeviceStore |
264 | return events; | 299 | return events; |
265 | } | 300 | } |
266 | 301 | ||
267 | - private ConcurrentMap<ProviderId, DeviceDescriptions> getDeviceDescriptions( | ||
268 | - DeviceId deviceId) { | ||
269 | - return createIfAbsentUnchecked(deviceDescs, deviceId, | ||
270 | - NewConcurrentHashMap.<ProviderId, DeviceDescriptions>ifNeeded()); | ||
271 | - } | ||
272 | - | ||
273 | // Gets the map of ports for the specified device; if one does not already | 302 | // Gets the map of ports for the specified device; if one does not already |
274 | // exist, it creates and registers a new one. | 303 | // exist, it creates and registers a new one. |
275 | private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) { | 304 | private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) { |
... | @@ -277,8 +306,14 @@ public class SimpleDeviceStore | ... | @@ -277,8 +306,14 @@ public class SimpleDeviceStore |
277 | NewConcurrentHashMap.<PortNumber, Port>ifNeeded()); | 306 | NewConcurrentHashMap.<PortNumber, Port>ifNeeded()); |
278 | } | 307 | } |
279 | 308 | ||
309 | + private ConcurrentMap<ProviderId, DeviceDescriptions> getDeviceDescriptions( | ||
310 | + DeviceId deviceId) { | ||
311 | + return createIfAbsentUnchecked(deviceDescs, deviceId, | ||
312 | + NewConcurrentHashMap.<ProviderId, DeviceDescriptions>ifNeeded()); | ||
313 | + } | ||
314 | + | ||
280 | @Override | 315 | @Override |
281 | - public synchronized DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId, | 316 | + public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId, |
282 | PortDescription portDescription) { | 317 | PortDescription portDescription) { |
283 | Device device = devices.get(deviceId); | 318 | Device device = devices.get(deviceId); |
284 | checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); | 319 | checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); |
... | @@ -286,19 +321,22 @@ public class SimpleDeviceStore | ... | @@ -286,19 +321,22 @@ public class SimpleDeviceStore |
286 | ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId); | 321 | ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId); |
287 | checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId); | 322 | checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId); |
288 | 323 | ||
289 | - DeviceDescriptions descs = descsMap.get(providerId); | 324 | + synchronized (descsMap) { |
290 | - // assuming all providers must to give DeviceDescription | 325 | + DeviceDescriptions descs = descsMap.get(providerId); |
291 | - checkArgument(descs != null, | 326 | + // assuming all providers must to give DeviceDescription |
292 | - "Device description for Device ID %s from Provider %s was not found", | 327 | + checkArgument(descs != null, |
293 | - deviceId, providerId); | 328 | + "Device description for Device ID %s from Provider %s was not found", |
329 | + deviceId, providerId); | ||
294 | 330 | ||
295 | - synchronized (this) { | ||
296 | ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId); | 331 | ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId); |
297 | final PortNumber number = portDescription.portNumber(); | 332 | final PortNumber number = portDescription.portNumber(); |
298 | - Port oldPort = ports.get(number); | 333 | + final Port oldPort = ports.get(number); |
334 | + final Port newPort; | ||
335 | + | ||
299 | // update description | 336 | // update description |
300 | descs.putPortDesc(portDescription); | 337 | descs.putPortDesc(portDescription); |
301 | - Port newPort = composePort(device, number, descsMap); | 338 | + newPort = composePort(device, number, descsMap); |
339 | + | ||
302 | if (oldPort == null) { | 340 | if (oldPort == null) { |
303 | return createPort(device, newPort, ports); | 341 | return createPort(device, newPort, ports); |
304 | } else { | 342 | } else { |
... | @@ -333,7 +371,7 @@ public class SimpleDeviceStore | ... | @@ -333,7 +371,7 @@ public class SimpleDeviceStore |
333 | synchronized (descs) { | 371 | synchronized (descs) { |
334 | Device device = devices.remove(deviceId); | 372 | Device device = devices.remove(deviceId); |
335 | // should DEVICE_REMOVED carry removed ports? | 373 | // should DEVICE_REMOVED carry removed ports? |
336 | - ConcurrentMap<PortNumber, Port> ports = devicePorts.get(deviceId); | 374 | + Map<PortNumber, Port> ports = devicePorts.get(deviceId); |
337 | if (ports != null) { | 375 | if (ports != null) { |
338 | ports.clear(); | 376 | ports.clear(); |
339 | } | 377 | } |
... | @@ -360,14 +398,14 @@ public class SimpleDeviceStore | ... | @@ -360,14 +398,14 @@ public class SimpleDeviceStore |
360 | 398 | ||
361 | DeviceDescriptions desc = providerDescs.get(primary); | 399 | DeviceDescriptions desc = providerDescs.get(primary); |
362 | 400 | ||
363 | - // base | 401 | + final DeviceDescription base = desc.getDeviceDesc(); |
364 | - Type type = desc.getDeviceDesc().type(); | 402 | + Type type = base.type(); |
365 | - String manufacturer = desc.getDeviceDesc().manufacturer(); | 403 | + String manufacturer = base.manufacturer(); |
366 | - String hwVersion = desc.getDeviceDesc().hwVersion(); | 404 | + String hwVersion = base.hwVersion(); |
367 | - String swVersion = desc.getDeviceDesc().swVersion(); | 405 | + String swVersion = base.swVersion(); |
368 | - String serialNumber = desc.getDeviceDesc().serialNumber(); | 406 | + String serialNumber = base.serialNumber(); |
369 | DefaultAnnotations annotations = DefaultAnnotations.builder().build(); | 407 | DefaultAnnotations annotations = DefaultAnnotations.builder().build(); |
370 | - annotations = merge(annotations, desc.getDeviceDesc().annotations()); | 408 | + annotations = merge(annotations, base.annotations()); |
371 | 409 | ||
372 | for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) { | 410 | for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) { |
373 | if (e.getKey().equals(primary)) { | 411 | if (e.getKey().equals(primary)) { |
... | @@ -386,7 +424,14 @@ public class SimpleDeviceStore | ... | @@ -386,7 +424,14 @@ public class SimpleDeviceStore |
386 | hwVersion, swVersion, serialNumber, annotations); | 424 | hwVersion, swVersion, serialNumber, annotations); |
387 | } | 425 | } |
388 | 426 | ||
389 | - // probably want composePort"s" also | 427 | + /** |
428 | + * Returns a Port, merging description given from multiple Providers. | ||
429 | + * | ||
430 | + * @param device device the port is on | ||
431 | + * @param number port number | ||
432 | + * @param providerDescs Collection of Descriptions from multiple providers | ||
433 | + * @return Port instance | ||
434 | + */ | ||
390 | private Port composePort(Device device, PortNumber number, | 435 | private Port composePort(Device device, PortNumber number, |
391 | ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) { | 436 | ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) { |
392 | 437 | ||
... | @@ -441,7 +486,9 @@ public class SimpleDeviceStore | ... | @@ -441,7 +486,9 @@ public class SimpleDeviceStore |
441 | 486 | ||
442 | public static final class InitDeviceDescs | 487 | public static final class InitDeviceDescs |
443 | implements ConcurrentInitializer<DeviceDescriptions> { | 488 | implements ConcurrentInitializer<DeviceDescriptions> { |
489 | + | ||
444 | private final DeviceDescription deviceDesc; | 490 | private final DeviceDescription deviceDesc; |
491 | + | ||
445 | public InitDeviceDescs(DeviceDescription deviceDesc) { | 492 | public InitDeviceDescs(DeviceDescription deviceDesc) { |
446 | this.deviceDesc = checkNotNull(deviceDesc); | 493 | this.deviceDesc = checkNotNull(deviceDesc); |
447 | } | 494 | } |
... | @@ -456,8 +503,6 @@ public class SimpleDeviceStore | ... | @@ -456,8 +503,6 @@ public class SimpleDeviceStore |
456 | * Collection of Description of a Device and it's Ports given from a Provider. | 503 | * Collection of Description of a Device and it's Ports given from a Provider. |
457 | */ | 504 | */ |
458 | private static class DeviceDescriptions { | 505 | private static class DeviceDescriptions { |
459 | - // private final DeviceId id; | ||
460 | - // private final ProviderId pid; | ||
461 | 506 | ||
462 | private final AtomicReference<DeviceDescription> deviceDesc; | 507 | private final AtomicReference<DeviceDescription> deviceDesc; |
463 | private final ConcurrentMap<PortNumber, PortDescription> portDescs; | 508 | private final ConcurrentMap<PortNumber, PortDescription> portDescs; |
... | @@ -475,10 +520,6 @@ public class SimpleDeviceStore | ... | @@ -475,10 +520,6 @@ public class SimpleDeviceStore |
475 | return portDescs.get(number); | 520 | return portDescs.get(number); |
476 | } | 521 | } |
477 | 522 | ||
478 | - public Collection<PortDescription> getPortDescs() { | ||
479 | - return Collections.unmodifiableCollection(portDescs.values()); | ||
480 | - } | ||
481 | - | ||
482 | /** | 523 | /** |
483 | * Puts DeviceDescription, merging annotations as necessary. | 524 | * Puts DeviceDescription, merging annotations as necessary. |
484 | * | 525 | * | ... | ... |
-
Please register or login to post a comment