diff options
Diffstat (limited to 'infra/translate-impl/src')
7 files changed, 196 insertions, 26 deletions
diff --git a/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/GenericListWriter.java b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/GenericListWriter.java index 92467a8e8..5121eb793 100644 --- a/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/GenericListWriter.java +++ b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/GenericListWriter.java @@ -19,12 +19,13 @@ package io.fd.honeycomb.translate.impl.write; import static io.fd.honeycomb.translate.impl.write.GenericWriter.isUpdateSupported; import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer; +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; import io.fd.honeycomb.translate.util.RWUtils; import io.fd.honeycomb.translate.util.write.AbstractGenericWriter; import io.fd.honeycomb.translate.write.ListWriter; +import io.fd.honeycomb.translate.write.Validator; import io.fd.honeycomb.translate.write.WriteContext; import io.fd.honeycomb.translate.write.WriteFailedException; -import io.fd.honeycomb.translate.spi.write.WriterCustomizer; import javax.annotation.Nonnull; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.Identifiable; @@ -45,6 +46,13 @@ public final class GenericListWriter<D extends DataObject & Identifiable<K>, K e this.customizer = customizer; } + public GenericListWriter(@Nonnull final InstanceIdentifier<D> type, + @Nonnull final ListWriterCustomizer<D, K> customizer, + @Nonnull final Validator<D> validator) { + super(type, isUpdateSupported(customizer), validator); + this.customizer = customizer; + } + @Override protected void writeCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D data, @Nonnull final WriteContext ctx) throws WriteFailedException { @@ -79,22 +87,25 @@ public final class GenericListWriter<D extends DataObject & Identifiable<K>, K e @Override protected void writeCurrent(final InstanceIdentifier<D> id, final D data, final WriteContext ctx) throws WriteFailedException { - super.writeCurrent(getId(id, data), data, ctx); + super.writeCurrent(getManagedId(id, data), data, ctx); } @Override protected void updateCurrent(final InstanceIdentifier<D> id, final D dataBefore, final D dataAfter, final WriteContext ctx) throws WriteFailedException { - super.updateCurrent(getId(id, dataBefore), dataBefore, dataAfter, ctx); + super.updateCurrent(getManagedId(id, dataBefore), dataBefore, dataAfter, ctx); } @Override protected void deleteCurrent(final InstanceIdentifier<D> id, final D dataBefore, final WriteContext ctx) throws WriteFailedException { - super.deleteCurrent(getId(id, dataBefore), dataBefore, ctx); + super.deleteCurrent(getManagedId(id, dataBefore), dataBefore, ctx); } - private InstanceIdentifier<D> getId(final InstanceIdentifier<D> id, final D current) { + @Override + protected InstanceIdentifier<D> getManagedId(@Nonnull final InstanceIdentifier<? extends DataObject> currentId, + @Nonnull final D current) { + final InstanceIdentifier<D> id = (InstanceIdentifier<D>) currentId; // Make sure the key is present if (isWildcarded(id)) { return getSpecificId(id, current); diff --git a/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/GenericWriter.java b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/GenericWriter.java index 086936e38..bfdf072c0 100644 --- a/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/GenericWriter.java +++ b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/GenericWriter.java @@ -16,9 +16,10 @@ package io.fd.honeycomb.translate.impl.write; -import io.fd.honeycomb.translate.write.WriteContext; import io.fd.honeycomb.translate.spi.write.WriterCustomizer; import io.fd.honeycomb.translate.util.write.AbstractGenericWriter; +import io.fd.honeycomb.translate.write.Validator; +import io.fd.honeycomb.translate.write.WriteContext; import io.fd.honeycomb.translate.write.WriteFailedException; import javax.annotation.Nonnull; import org.opendaylight.yangtools.yang.binding.DataObject; @@ -39,7 +40,13 @@ public final class GenericWriter<D extends DataObject> extends AbstractGenericWr @Nonnull final WriterCustomizer<D> customizer) { super(type, isUpdateSupported(customizer)); this.customizer = customizer; + } + public GenericWriter(@Nonnull final InstanceIdentifier<D> type, + @Nonnull final WriterCustomizer<D> customizer, + @Nonnull final Validator<D> validator) { + super(type, isUpdateSupported(customizer), validator); + this.customizer = customizer; } static boolean isUpdateSupported(final @Nonnull WriterCustomizer<?> customizer) { diff --git a/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistry.java b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistry.java index 67a5da32a..9e36e593f 100644 --- a/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistry.java +++ b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistry.java @@ -28,12 +28,14 @@ import com.google.common.collect.Sets; import io.fd.honeycomb.translate.TranslationException; import io.fd.honeycomb.translate.util.RWUtils; import io.fd.honeycomb.translate.write.DataObjectUpdate; +import io.fd.honeycomb.translate.write.DataValidationFailedException; import io.fd.honeycomb.translate.write.WriteContext; import io.fd.honeycomb.translate.write.Writer; import io.fd.honeycomb.translate.write.registry.UpdateFailedException; import io.fd.honeycomb.translate.write.registry.WriterRegistry; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -76,6 +78,33 @@ final class FlatWriterRegistry implements WriterRegistry { } @Override + public void validateModifications(@Nonnull final DataObjectUpdates updates, @Nonnull final WriteContext ctx) + throws DataValidationFailedException { + // Optimization: validation order is not relevant, so do not merge deletes and updates. + validateModifications(updates.getDeletes(), ctx); + validateModifications(updates.getUpdates(), ctx); + } + + private void validateModifications( + @Nonnull final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates, + @Nonnull final WriteContext ctx) throws DataValidationFailedException { + if (updates.isEmpty()) { + return; + } + // Fail early if some handlers are missing. + checkAllTypesCanBeHandled(updates); + + // Validators do not modify anything, so order of validators is not important. + // We iterate over writersOrder instead of modifications for consistent handling of subtree writers case. + for (InstanceIdentifier<?> writerType : writersOrder) { + final Writer<?> writer = getWriter(writerType); + for (DataObjectUpdate singleUpdate : getWritersData(updates, writer, writerType, ctx)) { + writer.validate(singleUpdate.getId(), singleUpdate.getDataBefore(), singleUpdate.getDataAfter(), ctx); + } + } + } + + @Override public void processModifications(@Nonnull final DataObjectUpdates updates, @Nonnull final WriteContext ctx) throws TranslationException { if (updates.isEmpty()) { @@ -207,26 +236,11 @@ final class FlatWriterRegistry implements WriterRegistry { LOG.debug("Performing bulk update for: {}", updates.keySet()); // Iterate over all writers and call update if there are any related updates for (InstanceIdentifier<?> writerType : writersOrder) { - Collection<? extends DataObjectUpdate> writersData = updates.get(writerType); final Writer<?> writer = getWriter(writerType); - - if (writersData.isEmpty()) { - // If there are no data for current writer, but it is a SubtreeWriter and there are updates to - // its children, still invoke it with its root data - if (writer instanceof SubtreeWriter<?> && isAffected((SubtreeWriter<?>) writer, updates)) { - // Provide parent data for SubtreeWriter for further processing - writersData = getParentDataObjectUpdate(ctx, updates, writer); - } else { - // Skipping unaffected writer - // Alternative to this would be modification sort according to the order of writers - continue; - } - } - LOG.debug("Performing update for: {}", writerType); LOG.trace("Performing update with writer: {}", writer); - for (DataObjectUpdate singleUpdate : writersData) { + for (DataObjectUpdate singleUpdate : getWritersData(updates, writer, writerType, ctx)) { try { writer.processModification(singleUpdate.getId(), singleUpdate.getDataBefore(), singleUpdate.getDataAfter(), ctx); @@ -240,6 +254,29 @@ final class FlatWriterRegistry implements WriterRegistry { } } + private Collection<? extends DataObjectUpdate> getWritersData( + final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates, final Writer<?> writer, + final InstanceIdentifier<?> writerType, final WriteContext ctx) { + Collection<? extends DataObjectUpdate> writersData = updates.get(writerType); + + if (writersData.isEmpty()) { + // If there are no data for current writer, but it is a SubtreeWriter and there are updates to + // its children, still invoke it with its root data. + // Notice that child updates will be ignored if the set of all modifications + // contain both parent update and child updates. But this is ok, since all changes are already expressed in + // the parent update. + if (writer instanceof SubtreeWriter<?> && isAffected((SubtreeWriter<?>) writer, updates)) { + // Provide parent data for SubtreeWriter for further processing + writersData = getParentDataObjectUpdate(ctx, updates, writer); + } else { + // Skipping unaffected writer + // Alternative to this would be modification sort according to the order of writers + return Collections.emptyList(); + } + } + return writersData; + } + private void checkAllTypesCanBeHandled( @Nonnull final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates) { diff --git a/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/SubtreeWriter.java b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/SubtreeWriter.java index a1a5f3fd0..e510bc33c 100644 --- a/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/SubtreeWriter.java +++ b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/SubtreeWriter.java @@ -19,6 +19,7 @@ package io.fd.honeycomb.translate.impl.write.registry; import static com.google.common.base.Preconditions.checkArgument; import com.google.common.collect.Iterables; +import io.fd.honeycomb.translate.write.DataValidationFailedException; import io.fd.honeycomb.translate.write.WriteContext; import io.fd.honeycomb.translate.write.WriteFailedException; import io.fd.honeycomb.translate.write.Writer; @@ -68,6 +69,13 @@ final class SubtreeWriter<D extends DataObject> implements Writer<D> { } @Override + public void validate(@Nonnull final InstanceIdentifier<? extends DataObject> id, + @Nullable final DataObject dataBefore, @Nullable final DataObject dataAfter, + @Nonnull final WriteContext ctx) throws DataValidationFailedException { + delegate.validate(id, dataBefore, dataAfter, ctx); + } + + @Override public void processModification( @Nonnull final InstanceIdentifier<? extends DataObject> id, @Nullable final DataObject dataBefore, diff --git a/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/GenericListWriterTest.java b/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/GenericListWriterTest.java index 91785b25e..20cabc5b4 100644 --- a/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/GenericListWriterTest.java +++ b/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/GenericListWriterTest.java @@ -22,6 +22,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer; +import io.fd.honeycomb.translate.write.Validator; import io.fd.honeycomb.translate.write.WriteContext; import io.fd.honeycomb.translate.write.WriteFailedException; import java.util.Collections; @@ -42,7 +43,6 @@ public class GenericListWriterTest { private ListWriterCustomizer<IdentifiableDataObject, DataObjectIdentifier> customizer; @Mock private WriteContext ctx; - private GenericListWriter<IdentifiableDataObject, DataObjectIdentifier> writer; @Mock private IdentifiableDataObject before; @Mock @@ -51,11 +51,15 @@ public class GenericListWriterTest { private IdentifiableDataObject after; @Mock private DataObjectIdentifier keyAfter; + @Mock + private Validator<IdentifiableDataObject> validator; + + private GenericListWriter<IdentifiableDataObject, DataObjectIdentifier> writer; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - writer = new GenericListWriter<>(DATA_OBJECT_ID, customizer); + writer = new GenericListWriter<>(DATA_OBJECT_ID, customizer, validator); when(before.getKey()).thenReturn(beforeKey); when(after.getKey()).thenReturn(keyAfter); } @@ -106,4 +110,25 @@ public class GenericListWriterTest { writer = new GenericListWriter<>(DATA_OBJECT_ID, customizer); writer.deleteCurrentAttributes(DATA_OBJECT_ID, before, ctx); } + + @Test + public void testValidate() throws Exception { + assertEquals(DATA_OBJECT_ID, writer.getManagedDataObjectType()); + + final InstanceIdentifier<IdentifiableDataObject> keyedIdBefore = + (InstanceIdentifier<IdentifiableDataObject>) InstanceIdentifier.create(Collections + .singleton(new InstanceIdentifier.IdentifiableItem<>(IdentifiableDataObject.class, beforeKey))); + final InstanceIdentifier<IdentifiableDataObject> keyedIdAfter = + (InstanceIdentifier<IdentifiableDataObject>) InstanceIdentifier.create(Collections + .singleton(new InstanceIdentifier.IdentifiableItem<>(IdentifiableDataObject.class, keyAfter))); + + writer.validate(DATA_OBJECT_ID, before, after, ctx); + verify(validator).validateUpdate(keyedIdBefore, before, after, ctx); + + writer.validate(DATA_OBJECT_ID, before, null, ctx); + verify(validator).validateDelete(keyedIdBefore, before, ctx); + + writer.validate(DATA_OBJECT_ID, null, after, ctx); + verify(validator).validateWrite(keyedIdAfter, after, ctx); + } }
\ No newline at end of file diff --git a/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/GenericWriterTest.java b/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/GenericWriterTest.java index c9d381ad4..1c0927b89 100644 --- a/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/GenericWriterTest.java +++ b/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/GenericWriterTest.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; import io.fd.honeycomb.translate.spi.write.WriterCustomizer; +import io.fd.honeycomb.translate.write.Validator; import io.fd.honeycomb.translate.write.WriteContext; import io.fd.honeycomb.translate.write.WriteFailedException; import org.junit.Before; @@ -40,16 +41,19 @@ public class GenericWriterTest { private WriterCustomizer<DataObject> customizer; @Mock private WriteContext ctx; - private GenericWriter<DataObject> writer; @Mock private DataObject before; @Mock private DataObject after; + @Mock + private Validator<DataObject> validator; + + private GenericWriter<DataObject> writer; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - writer = new GenericWriter<>(DATA_OBJECT_ID, customizer); + writer = new GenericWriter<>(DATA_OBJECT_ID, customizer, validator); } @Test @@ -94,4 +98,17 @@ public class GenericWriterTest { assertTrue(GenericWriter.isUpdateSupported(new NoopWriters.DirectUpdateWriterCustomizer())); assertTrue(GenericWriter.isUpdateSupported(new NoopWriters.ParentImplDirectUpdateWriterCustomizer())); } + + @Test + public void testValidate() throws Exception { + assertEquals(DATA_OBJECT_ID, writer.getManagedDataObjectType()); + writer.validate(DATA_OBJECT_ID, before, after, ctx); + verify(validator).validateUpdate(DATA_OBJECT_ID, before, after, ctx); + + writer.validate(DATA_OBJECT_ID, before, null, ctx); + verify(validator).validateDelete(DATA_OBJECT_ID, before, ctx); + + writer.validate(DATA_OBJECT_ID, null, after, ctx); + verify(validator).validateWrite(DATA_OBJECT_ID, after, ctx); + } }
\ No newline at end of file diff --git a/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistryTest.java b/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistryTest.java index 7f4b93e01..e06197fae 100644 --- a/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistryTest.java +++ b/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistryTest.java @@ -365,6 +365,71 @@ public class FlatWriterRegistryTest { } } + @Test(expected = IllegalArgumentException.class) + public void testValidateMissingWriter() throws Exception { + final FlatWriterRegistry flatWriterRegistry = + new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1)); + + final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create(); + addUpdate(updates, DataObject1.class); + addUpdate(updates, DataObject2.class); + flatWriterRegistry.validateModifications(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx); + } + + @Test + public void testValidateSingleWriter() throws Exception { + final FlatWriterRegistry flatWriterRegistry = + new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2)); + + final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create(); + final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class); + final InstanceIdentifier<DataObject1> iid2 = InstanceIdentifier.create(DataObject1.class); + final DataObject1 dataObject = mock(DataObject1.class); + updates.put(DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject)); + updates.put(DataObject1.IID, DataObjectUpdate.create(iid2, dataObject, dataObject)); + flatWriterRegistry + .validateModifications(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx); + + verify(writer1).validate(iid, dataObject, dataObject, ctx); + verify(writer1).validate(iid2, dataObject, dataObject, ctx); + // Invoked when registry is being created + verifyNoMoreInteractions(writer1); + verifyZeroInteractions(writer2); + } + + @Test + public void testValidateMultipleWriters() throws Exception { + final FlatWriterRegistry flatWriterRegistry = + new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2)); + + final Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> deletes = HashMultimap.create(); + final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create(); + final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class); + final DataObject1 dataObject = mock(DataObject1.class); + // Writer 1 delete + deletes.put(DataObject1.IID, + ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid, dataObject, null))); + // Writer 1 create + updates.put(DataObject1.IID, DataObjectUpdate.create(iid, null, dataObject)); + final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class); + final DataObject2 dataObject2 = mock(DataObject2.class); + // Writer 2 delete + deletes.put(DataObject2.IID, + ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid2, dataObject2, null))); + // Writer 2 update + updates.put(DataObject2.IID, DataObjectUpdate.create(iid2, dataObject2, dataObject2)); + flatWriterRegistry.validateModifications(new WriterRegistry.DataObjectUpdates(updates, deletes), ctx); + + // Ignore order + verify(writer1).validate(iid, dataObject, null, ctx); + verify(writer1).validate(iid, null, dataObject, ctx); + verify(writer2).validate(iid2, dataObject2, null, ctx); + verify(writer2).validate(iid2, dataObject2, dataObject2, ctx); + + verifyNoMoreInteractions(writer1); + verifyNoMoreInteractions(writer2); + } + private <D extends DataObject> void addKeyedUpdate(final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates, final Class<D> type) throws Exception { final InstanceIdentifier<D> iid = (InstanceIdentifier<D>) type.getDeclaredField("IID").get(null); |