Committed by
Gerrit Code Review
ONOS-2280: Fix NPE in hosts EC map
Change-Id: I4cb74d7c9526dc0e836e1e2790748324f60183f5
Showing
3 changed files
with
42 additions
and
35 deletions
... | @@ -47,6 +47,7 @@ import java.util.Collection; | ... | @@ -47,6 +47,7 @@ import java.util.Collection; |
47 | import java.util.Collections; | 47 | import java.util.Collections; |
48 | import java.util.List; | 48 | import java.util.List; |
49 | import java.util.Map; | 49 | import java.util.Map; |
50 | +import java.util.Objects; | ||
50 | import java.util.Optional; | 51 | import java.util.Optional; |
51 | import java.util.Set; | 52 | import java.util.Set; |
52 | import java.util.Timer; | 53 | import java.util.Timer; |
... | @@ -312,7 +313,9 @@ public class EventuallyConsistentMapImpl<K, V> | ... | @@ -312,7 +313,9 @@ public class EventuallyConsistentMapImpl<K, V> |
312 | public V remove(K key) { | 313 | public V remove(K key) { |
313 | checkState(!destroyed, destroyedMessage); | 314 | checkState(!destroyed, destroyedMessage); |
314 | checkNotNull(key, ERROR_NULL_KEY); | 315 | checkNotNull(key, ERROR_NULL_KEY); |
315 | - return removeInternal(key, Optional.empty()); | 316 | + MapValue<V> tombstone = new MapValue<>(null, timestampProvider.apply(key, null)); |
317 | + MapValue<V> previousValue = removeInternal(key, Optional.empty(), tombstone); | ||
318 | + return previousValue != null ? previousValue.get() : null; | ||
316 | } | 319 | } |
317 | 320 | ||
318 | @Override | 321 | @Override |
... | @@ -320,34 +323,39 @@ public class EventuallyConsistentMapImpl<K, V> | ... | @@ -320,34 +323,39 @@ public class EventuallyConsistentMapImpl<K, V> |
320 | checkState(!destroyed, destroyedMessage); | 323 | checkState(!destroyed, destroyedMessage); |
321 | checkNotNull(key, ERROR_NULL_KEY); | 324 | checkNotNull(key, ERROR_NULL_KEY); |
322 | checkNotNull(value, ERROR_NULL_VALUE); | 325 | checkNotNull(value, ERROR_NULL_VALUE); |
323 | - removeInternal(key, Optional.of(value)); | 326 | + MapValue<V> tombstone = new MapValue<>(null, timestampProvider.apply(key, null)); |
327 | + removeInternal(key, Optional.of(value), tombstone); | ||
324 | } | 328 | } |
325 | 329 | ||
326 | - private V removeInternal(K key, Optional<V> value) { | 330 | + private MapValue<V> removeInternal(K key, Optional<V> value, MapValue<V> tombstone) { |
327 | checkState(!destroyed, destroyedMessage); | 331 | checkState(!destroyed, destroyedMessage); |
328 | checkNotNull(key, ERROR_NULL_KEY); | 332 | checkNotNull(key, ERROR_NULL_KEY); |
329 | checkNotNull(value, ERROR_NULL_VALUE); | 333 | checkNotNull(value, ERROR_NULL_VALUE); |
330 | 334 | ||
331 | - MapValue<V> newValue = new MapValue<>(null, timestampProvider.apply(key, value.orElse(null))); | 335 | + checkState(tombstone.isTombstone()); |
332 | AtomicBoolean updated = new AtomicBoolean(false); | 336 | AtomicBoolean updated = new AtomicBoolean(false); |
333 | - AtomicReference<V> previousValue = new AtomicReference<>(); | 337 | + AtomicReference<MapValue<V>> previousValue = new AtomicReference<>(); |
334 | items.compute(key, (k, existing) -> { | 338 | items.compute(key, (k, existing) -> { |
335 | - if (existing != null && existing.isAlive()) { | 339 | + boolean valueMatches = true; |
336 | - updated.set(!value.isPresent() || value.get().equals(existing.get())); | 340 | + if (value.isPresent() && existing != null && existing.isAlive()) { |
337 | - previousValue.set(existing.get()); | 341 | + valueMatches = Objects.equals(value.get(), existing.get()); |
338 | } | 342 | } |
339 | - updated.set(existing == null || newValue.isNewerThan(existing)); | 343 | + updated.set(valueMatches && (existing == null || tombstone.isNewerThan(existing))); |
340 | - return updated.get() ? newValue : existing; | 344 | + if (updated.get()) { |
345 | + previousValue.set(existing); | ||
346 | + } | ||
347 | + return updated.get() ? tombstone : existing; | ||
341 | }); | 348 | }); |
342 | if (updated.get()) { | 349 | if (updated.get()) { |
343 | - notifyPeers(new UpdateEntry<>(key, newValue), peerUpdateFunction.apply(key, previousValue.get())); | 350 | + notifyPeers(new UpdateEntry<>(key, tombstone), peerUpdateFunction.apply(key, null)); |
344 | - notifyListeners(new EventuallyConsistentMapEvent<>(REMOVE, key, previousValue.get())); | 351 | + if (previousValue.get() != null && previousValue.get().isAlive()) { |
352 | + notifyListeners(new EventuallyConsistentMapEvent<>(REMOVE, key, previousValue.get().get())); | ||
353 | + } | ||
345 | if (persistent) { | 354 | if (persistent) { |
346 | - persistentStore.update(key, newValue); | 355 | + persistentStore.update(key, tombstone); |
347 | } | 356 | } |
348 | - return previousValue.get(); | ||
349 | } | 357 | } |
350 | - return null; | 358 | + return previousValue.get(); |
351 | } | 359 | } |
352 | 360 | ||
353 | @Override | 361 | @Override |
... | @@ -540,12 +548,13 @@ public class EventuallyConsistentMapImpl<K, V> | ... | @@ -540,12 +548,13 @@ public class EventuallyConsistentMapImpl<K, V> |
540 | if (remoteValueDigest == null || localValue.isNewerThan(remoteValueDigest.timestamp())) { | 548 | if (remoteValueDigest == null || localValue.isNewerThan(remoteValueDigest.timestamp())) { |
541 | // local value is more recent, push to sender | 549 | // local value is more recent, push to sender |
542 | queueUpdate(new UpdateEntry<>(key, localValue), ImmutableList.of(sender)); | 550 | queueUpdate(new UpdateEntry<>(key, localValue), ImmutableList.of(sender)); |
543 | - } else { | ||
544 | - if (remoteValueDigest.isTombstone() | ||
545 | - && remoteValueDigest.timestamp().isNewerThan(localValue.timestamp())) { | ||
546 | - if (updateInternal(key, new MapValue<>(null, remoteValueDigest.timestamp()))) { | ||
547 | - externalEvents.add(new EventuallyConsistentMapEvent<>(REMOVE, key, null)); | ||
548 | } | 551 | } |
552 | + if (remoteValueDigest != null && remoteValueDigest.isTombstone()) { | ||
553 | + MapValue<V> previousValue = removeInternal(key, | ||
554 | + Optional.empty(), | ||
555 | + new MapValue<>(null, remoteValueDigest.timestamp())); | ||
556 | + if (previousValue != null && previousValue.isAlive()) { | ||
557 | + externalEvents.add(new EventuallyConsistentMapEvent<>(REMOVE, key, previousValue.get())); | ||
549 | } | 558 | } |
550 | } | 559 | } |
551 | }); | 560 | }); |
... | @@ -559,10 +568,13 @@ public class EventuallyConsistentMapImpl<K, V> | ... | @@ -559,10 +568,13 @@ public class EventuallyConsistentMapImpl<K, V> |
559 | updates.forEach(update -> { | 568 | updates.forEach(update -> { |
560 | final K key = update.key(); | 569 | final K key = update.key(); |
561 | final MapValue<V> value = update.value(); | 570 | final MapValue<V> value = update.value(); |
562 | - | 571 | + if (value.isTombstone()) { |
563 | - if (updateInternal(key, value)) { | 572 | + MapValue<V> previousValue = removeInternal(key, Optional.empty(), value); |
564 | - final EventuallyConsistentMapEvent.Type type = value.isTombstone() ? REMOVE : PUT; | 573 | + if (previousValue != null && previousValue.get() != null) { |
565 | - notifyListeners(new EventuallyConsistentMapEvent<>(type, key, value.get())); | 574 | + notifyListeners(new EventuallyConsistentMapEvent<>(REMOVE, key, previousValue.get())); |
575 | + } | ||
576 | + } else if (updateInternal(key, value)) { | ||
577 | + notifyListeners(new EventuallyConsistentMapEvent<>(PUT, key, value.get())); | ||
566 | } | 578 | } |
567 | }); | 579 | }); |
568 | } | 580 | } | ... | ... |
1 | package org.onosproject.store.host.impl; | 1 | package org.onosproject.store.host.impl; |
2 | 2 | ||
3 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
3 | import static org.onosproject.net.DefaultAnnotations.merge; | 4 | import static org.onosproject.net.DefaultAnnotations.merge; |
4 | import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED; | 5 | import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED; |
5 | import static org.onosproject.net.host.HostEvent.Type.HOST_REMOVED; | 6 | import static org.onosproject.net.host.HostEvent.Type.HOST_REMOVED; |
... | @@ -11,7 +12,6 @@ import java.util.Collection; | ... | @@ -11,7 +12,6 @@ import java.util.Collection; |
11 | import java.util.Collections; | 12 | import java.util.Collections; |
12 | import java.util.Objects; | 13 | import java.util.Objects; |
13 | import java.util.Set; | 14 | import java.util.Set; |
14 | -import java.util.concurrent.ConcurrentHashMap; | ||
15 | import java.util.function.Predicate; | 15 | import java.util.function.Predicate; |
16 | import java.util.stream.Collectors; | 16 | import java.util.stream.Collectors; |
17 | 17 | ||
... | @@ -50,13 +50,9 @@ import org.slf4j.Logger; | ... | @@ -50,13 +50,9 @@ import org.slf4j.Logger; |
50 | 50 | ||
51 | import com.google.common.collect.HashMultimap; | 51 | import com.google.common.collect.HashMultimap; |
52 | import com.google.common.collect.ImmutableSet; | 52 | import com.google.common.collect.ImmutableSet; |
53 | -import com.google.common.collect.Multimap; | ||
54 | import com.google.common.collect.Multimaps; | 53 | import com.google.common.collect.Multimaps; |
55 | import com.google.common.collect.SetMultimap; | 54 | import com.google.common.collect.SetMultimap; |
56 | import com.google.common.collect.Sets; | 55 | import com.google.common.collect.Sets; |
57 | -import static com.google.common.collect.Multimaps.newSetMultimap; | ||
58 | -import static com.google.common.collect.Multimaps.synchronizedSetMultimap; | ||
59 | -import static com.google.common.collect.Sets.newConcurrentHashSet; | ||
60 | 56 | ||
61 | /** | 57 | /** |
62 | * Manages the inventory of hosts using a {@code EventuallyConsistentMap}. | 58 | * Manages the inventory of hosts using a {@code EventuallyConsistentMap}. |
... | @@ -76,9 +72,10 @@ public class ECHostStore | ... | @@ -76,9 +72,10 @@ public class ECHostStore |
76 | protected LogicalClockService clockService; | 72 | protected LogicalClockService clockService; |
77 | 73 | ||
78 | // Hosts tracked by their location | 74 | // Hosts tracked by their location |
79 | - private final Multimap<ConnectPoint, Host> locations | 75 | + private final SetMultimap<ConnectPoint, Host> locations = |
80 | - = synchronizedSetMultimap(newSetMultimap(new ConcurrentHashMap<>(), | 76 | + Multimaps.synchronizedSetMultimap( |
81 | - () -> newConcurrentHashSet())); | 77 | + HashMultimap.<ConnectPoint, Host>create()); |
78 | + | ||
82 | private final SetMultimap<ConnectPoint, PortAddresses> portAddresses = | 79 | private final SetMultimap<ConnectPoint, PortAddresses> portAddresses = |
83 | Multimaps.synchronizedSetMultimap( | 80 | Multimaps.synchronizedSetMultimap( |
84 | HashMultimap.<ConnectPoint, PortAddresses>create()); | 81 | HashMultimap.<ConnectPoint, PortAddresses>create()); |
... | @@ -252,7 +249,7 @@ public class ECHostStore | ... | @@ -252,7 +249,7 @@ public class ECHostStore |
252 | 249 | ||
253 | @Override | 250 | @Override |
254 | public void event(EventuallyConsistentMapEvent<HostId, DefaultHost> event) { | 251 | public void event(EventuallyConsistentMapEvent<HostId, DefaultHost> event) { |
255 | - DefaultHost host = event.value(); | 252 | + DefaultHost host = checkNotNull(event.value()); |
256 | if (event.type() == PUT) { | 253 | if (event.type() == PUT) { |
257 | locations.put(host.location(), host); | 254 | locations.put(host.location(), host); |
258 | } else if (event.type() == REMOVE) { | 255 | } else if (event.type() == REMOVE) { | ... | ... |
... | @@ -327,8 +327,6 @@ public class EventuallyConsistentMapImplTest { | ... | @@ -327,8 +327,6 @@ public class EventuallyConsistentMapImplTest { |
327 | listener.event(new EventuallyConsistentMapEvent<>( | 327 | listener.event(new EventuallyConsistentMapEvent<>( |
328 | EventuallyConsistentMapEvent.Type.REMOVE, KEY1, VALUE1)); | 328 | EventuallyConsistentMapEvent.Type.REMOVE, KEY1, VALUE1)); |
329 | listener.event(new EventuallyConsistentMapEvent<>( | 329 | listener.event(new EventuallyConsistentMapEvent<>( |
330 | - EventuallyConsistentMapEvent.Type.REMOVE, KEY1, null)); | ||
331 | - listener.event(new EventuallyConsistentMapEvent<>( | ||
332 | EventuallyConsistentMapEvent.Type.PUT, KEY1, VALUE1)); | 330 | EventuallyConsistentMapEvent.Type.PUT, KEY1, VALUE1)); |
333 | listener.event(new EventuallyConsistentMapEvent<>( | 331 | listener.event(new EventuallyConsistentMapEvent<>( |
334 | EventuallyConsistentMapEvent.Type.PUT, KEY2, VALUE2)); | 332 | EventuallyConsistentMapEvent.Type.PUT, KEY2, VALUE2)); | ... | ... |
-
Please register or login to post a comment