Madan Jampani
Committed by Gerrit Code Review

ONOS-2280: Fix NPE in hosts EC map

Change-Id: I4cb74d7c9526dc0e836e1e2790748324f60183f5
...@@ -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 { 551 + }
544 - if (remoteValueDigest.isTombstone() 552 + if (remoteValueDigest != null && remoteValueDigest.isTombstone()) {
545 - && remoteValueDigest.timestamp().isNewerThan(localValue.timestamp())) { 553 + MapValue<V> previousValue = removeInternal(key,
546 - if (updateInternal(key, new MapValue<>(null, remoteValueDigest.timestamp()))) { 554 + Optional.empty(),
547 - externalEvents.add(new EventuallyConsistentMapEvent<>(REMOVE, key, null)); 555 + new MapValue<>(null, remoteValueDigest.timestamp()));
548 - } 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));
......