Yuta HIGUCHI

DeviceStore update

- Add off-line/remove handling to Gossip~
- Backport lock scope to Simple~

Change-Id: I5b4c8e12738ef78920341fb8699c4b07bde8712a
...@@ -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 *
......