NamingContext.getNameIfPresent should not fail if name is missing 01/8201/1
authorMarek Gradzki <[email protected]>
Thu, 24 Aug 2017 11:08:49 +0000 (13:08 +0200)
committerMarek Gradzki <[email protected]>
Thu, 24 Aug 2017 11:51:30 +0000 (13:51 +0200)
Also makes InterfaceChangeNotificationProducer notification translation
code more defensive.

The issue was revealed by HC2VPP-216 and HC2VPP-220.

Change-Id: I20792a51743ae621d86c1b9066d680bc2303ed82
Signed-off-by: Marek Gradzki <[email protected]>
v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/notification/InterfaceChangeNotificationProducer.java
vpp-common/vpp-translate-utils/src/main/java/io/fd/hc2vpp/common/translate/util/NamingContext.java
vpp-common/vpp-translate-utils/src/test/java/io/fd/hc2vpp/common/translate/util/NamingContextTest.java

index e971db2..44da827 100644 (file)
@@ -79,7 +79,13 @@ final class InterfaceChangeNotificationProducer implements ManagedNotificationPr
                 swInterfaceEventNotification -> {
                     LOG.trace("Interface notification received: {}", swInterfaceEventNotification);
                     // TODO HONEYCOMB-166 this should be lazy
-                    collector.onNotification(transformNotification(swInterfaceEventNotification));
+                    try {
+                        collector.onNotification(transformNotification(swInterfaceEventNotification));
+                    } catch (Exception e) {
+                        // There is no need to propagate exception to jvpp rx thread in case of unexpected failures.
+                        // We can't do much about it, so lets log the exception.
+                        LOG.warn("Failed to process interface notification {}", swInterfaceEventNotification, e);
+                    }
                 }
         );
     }
index 8e5c31a..60bd822 100644 (file)
@@ -22,8 +22,10 @@ import static com.google.common.base.Preconditions.checkState;
 import com.google.common.base.Optional;
 import io.fd.honeycomb.translate.MappingContext;
 import io.fd.honeycomb.translate.util.RWUtils;
+import java.util.List;
 import java.util.function.Supplier;
 import java.util.stream.Collector;
+import java.util.stream.Collectors;
 import javax.annotation.Nonnull;
 import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.Contexts;
 import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.NamingContextKey;
@@ -93,13 +95,21 @@ public final class NamingContext implements AutoCloseable {
     public synchronized Optional<String> getNameIfPresent(final int index,
                                                           @Nonnull final MappingContext mappingContext) {
         final Optional<Mappings> read = mappingContext.read(namingContextIid.child(Mappings.class));
+        if (!read.isPresent()) {
+            return Optional.absent();
+        }
 
-        return read.isPresent()
-                ? Optional.of(read.get().getMapping().stream()
+        final List<Mapping> mappings = read.get().getMapping().stream()
                 .filter(mapping -> mapping.getIndex().equals(index))
-                .collect(SINGLE_ITEM_COLLECTOR)
-                .getName())
-                : Optional.absent();
+                .collect(Collectors.toList());
+
+        if (mappings.size() > 1) {
+            throw new IllegalStateException("Multiple mappings defined with index=" + index + ": " + mappings);
+        } else if (mappings.size() == 1) {
+            return Optional.of(mappings.get(0).getName());
+        } else {
+            return Optional.absent();
+        }
     }
 
     /**
index c3c7533..1fe4da5 100644 (file)
@@ -18,11 +18,13 @@ package io.fd.hc2vpp.common.translate.util;
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
 import io.fd.honeycomb.test.tools.HoneycombTestRunner;
 import io.fd.honeycomb.test.tools.annotations.InjectTestData;
 import io.fd.honeycomb.test.tools.annotations.InjectablesProcessor;
@@ -37,14 +39,15 @@ import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.opendaylight.mdsal.binding.generator.impl.ModuleInfoBackedContext;
 import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.$YangModuleInfoImpl;
 import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.Contexts;
 import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.NamingContextKey;
 import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.Mappings;
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.MappingsBuilder;
 import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.Mapping;
 import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.MappingBuilder;
 import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.MappingKey;
-import org.opendaylight.mdsal.binding.generator.impl.ModuleInfoBackedContext;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 
@@ -112,6 +115,34 @@ public class NamingContextTest implements InjectablesProcessor {
                 .getIndex("non-existing", mappingContext, () -> new IllegalArgumentException("Non existing index"));
     }
 
+    @Test(expected = IllegalStateException.class)
+    public void getNameIfPresentFails() {
+        final Mapping mapping1 = mock(Mapping.class);
+        final Mapping mapping2 = mock(Mapping.class);
+        final Mappings mappings = new MappingsBuilder().setMapping(Lists.newArrayList(mapping1, mapping2)).build();
+        when(mappingContext.read(namingContextIid.child(Mappings.class))).thenReturn(Optional.of(mappings));
+
+        namingContext.getNameIfPresent(0, mappingContext);
+    }
+
+    @Test
+    public void getNameIfPresentReturnsAbsent() {
+        final Mapping mapping1 = new MappingBuilder().setIndex(1).setName(NAME_1).build();
+        final Mappings mappings = new MappingsBuilder().setMapping(Lists.newArrayList(mapping1)).build();
+        when(mappingContext.read(namingContextIid.child(Mappings.class))).thenReturn(Optional.of(mappings));
+
+        assertEquals(Optional.absent(), namingContext.getNameIfPresent(0, mappingContext));
+    }
+
+    @Test
+    public void getNameIfPresent() {
+        final Mapping mapping1 = new MappingBuilder().setIndex(1).setName(NAME_1).build();
+        final Mappings mappings = new MappingsBuilder().setMapping(Lists.newArrayList(mapping1)).build();
+        when(mappingContext.read(namingContextIid.child(Mappings.class))).thenReturn(Optional.of(mappings));
+
+        assertEquals(Optional.of(NAME_1), namingContext.getNameIfPresent(1, mappingContext));
+    }
+
     private Mapping filterForParent(final String parent) {
         return mappings.getMapping().stream()
                 .filter(mapping -> mapping.getName().equals(parent))