From e3c31cee916480b2d9d169c1f5afb1c42efaabe1 Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Fri, 29 Jul 2016 16:27:12 +0200 Subject: HONEYCOMB-130: Rename infra packages(remove vpp/v3po) Change-Id: Ic5b90e397e3743623d01b206bc60bc5c7df6b981 Signed-off-by: Maros Marsalek --- .../data/init/AbstractDataTreeConverter.java | 114 ++ .../honeycomb/data/init/DataTreeInitializer.java | 51 + .../honeycomb/data/init/InitializerRegistry.java | 33 + .../data/init/InitializerRegistryImpl.java | 52 + .../honeycomb/data/init/RestoringInitializer.java | 112 ++ .../vpp/data/init/AbstractDataTreeConverter.java | 114 -- .../v3po/vpp/data/init/DataTreeInitializer.java | 51 - .../v3po/vpp/data/init/InitializerRegistry.java | 33 - .../vpp/data/init/InitializerRegistryImpl.java | 52 - .../v3po/vpp/data/init/RestoringInitializer.java | 112 -- .../ConfigurationInitializerRegistryModule.java | 54 + ...figurationInitializerRegistryModuleFactory.java | 14 + .../rev160407/PersistedFileInitializerModule.java | 38 + .../PersistedFileInitializerModuleFactory.java | 13 + .../ConfigurationInitializerRegistryModule.java | 54 - ...figurationInitializerRegistryModuleFactory.java | 13 - .../rev160407/PersistedFileInitializerModule.java | 38 - .../PersistedFileInitializerModuleFactory.java | 13 - infra/cfg-init/src/main/yang/cfg-init.yang | 117 ++ infra/cfg-init/src/main/yang/vpp-cfg-init.yang | 117 -- .../data/init/InitializerRegistryImplTest.java | 66 + .../vpp/data/init/InitializerRegistryImplTest.java | 66 - .../io/fd/honeycomb/data/DataModification.java | 70 + .../fd/honeycomb/data/ModifiableDataManager.java | 33 + .../io/fd/honeycomb/data/ReadableDataManager.java | 40 + .../fd/honeycomb/v3po/data/DataModification.java | 70 - .../honeycomb/v3po/data/ModifiableDataManager.java | 33 - .../honeycomb/v3po/data/ReadableDataManager.java | 40 - infra/data-api/src/main/yang/data-api.yang | 4 +- infra/data-impl/pom.xml | 2 +- .../java/io/fd/honeycomb/data/impl/DataBroker.java | 198 +++ .../data/impl/ModifiableDataTreeDelegator.java | 236 ++++ .../data/impl/ModifiableDataTreeManager.java | 122 ++ .../fd/honeycomb/data/impl/ModificationDiff.java | 278 ++++ .../data/impl/PersistingDataTreeAdapter.java | 151 +++ .../honeycomb/data/impl/ReadOnlyTransaction.java | 119 ++ .../honeycomb/data/impl/ReadWriteTransaction.java | 101 ++ .../data/impl/ReadableDataTreeDelegator.java | 241 ++++ .../fd/honeycomb/data/impl/WriteTransaction.java | 177 +++ .../io/fd/honeycomb/v3po/data/impl/DataBroker.java | 198 --- .../data/impl/ModifiableDataTreeDelegator.java | 236 ---- .../v3po/data/impl/ModifiableDataTreeManager.java | 122 -- .../honeycomb/v3po/data/impl/ModificationDiff.java | 278 ---- .../v3po/data/impl/PersistingDataTreeAdapter.java | 151 --- .../v3po/data/impl/ReadOnlyTransaction.java | 119 -- .../v3po/data/impl/ReadWriteTransaction.java | 101 -- .../v3po/data/impl/ReadableDataTreeDelegator.java | 242 ---- .../honeycomb/v3po/data/impl/WriteTransaction.java | 177 --- .../data/impl/rev160411/ConfigDataTreeModule.java | 6 +- .../impl/rev160411/OperationalDataTreeModule.java | 4 +- .../rev160411/PersistingDataTreeAdapterModule.java | 2 +- .../io/fd/honeycomb/data/impl/DataBrokerTest.java | 111 ++ .../data/impl/ModifiableDataTreeDelegatorTest.java | 269 ++++ .../honeycomb/data/impl/ModificationDiffTest.java | 427 ++++++ .../data/impl/PersistingDataTreeAdapterTest.java | 69 + .../data/impl/ReadOnlyTransactionTest.java | 68 + .../data/impl/ReadWriteTransactionTest.java | 111 ++ .../data/impl/ReadableDataTreeDelegatorTest.java | 192 +++ .../honeycomb/data/impl/WriteTransactionTest.java | 128 ++ .../honeycomb/v3po/data/impl/DataBrokerTest.java | 111 -- .../data/impl/ModifiableDataTreeDelegatorTest.java | 269 ---- .../v3po/data/impl/ModificationDiffTest.java | 427 ------ .../data/impl/PersistingDataTreeAdapterTest.java | 69 - .../v3po/data/impl/ReadOnlyTransactionTest.java | 68 - .../v3po/data/impl/ReadWriteTransactionTest.java | 111 -- .../data/impl/ReadableDataTreeDelegatorTest.java | 192 --- .../v3po/data/impl/WriteTransactionTest.java | 128 -- .../src/main/config/context-datatree-config.xml | 6 +- infra/impl/src/main/config/default-config.xml | 12 +- infra/impl/src/main/config/initializer-config.xml | 18 +- .../impl/src/main/config/netconf-north-config.xml | 80 +- .../impl/src/main/config/restconf-north-config.xml | 2 +- .../impl/NorthboundFacadeHoneycombDOMBroker.java | 206 +++ .../impl/NorthboundFacadeHoneycombDOMBroker.java | 206 --- .../impl/rev141210/ContextDataBrokerModule.java | 26 + .../rev141210/ContextDataBrokerModuleFactory.java | 13 + .../honeycomb/impl/rev141210/DataBrokerModule.java | 34 + .../impl/rev141210/DataBrokerModuleFactory.java | 13 + .../honeycomb/impl/rev141210/HoneycombModule.java | 59 + .../impl/rev141210/HoneycombModuleFactory.java | 28 + .../impl/rev141210/NetconfBindingBrokerModule.java | 117 ++ .../NetconfBindingBrokerModuleFactory.java | 13 + .../rev141210/NetconfMonitoringReaderModule.java | 46 + .../NetconfMonitoringReaderModuleFactory.java | 13 + .../impl/rev141210/ContextDataBrokerModule.java | 26 - .../rev141210/ContextDataBrokerModuleFactory.java | 13 - .../yang/v3po/impl/rev141210/DataBrokerModule.java | 34 - .../impl/rev141210/DataBrokerModuleFactory.java | 13 - .../impl/rev141210/NetconfBindingBrokerModule.java | 117 -- .../NetconfBindingBrokerModuleFactory.java | 13 - .../rev141210/NetconfMonitoringReaderModule.java | 46 - .../NetconfMonitoringReaderModuleFactory.java | 13 - .../ns/yang/v3po/impl/rev141210/V3poModule.java | 60 - .../v3po/impl/rev141210/V3poModuleFactory.java | 28 - infra/impl/src/main/yang/honeycomb-impl.yang | 155 +++ infra/impl/src/main/yang/v3po-impl.yang | 155 --- .../v3po/impl/rev141210/V3poModuleFactoryTest.java | 26 - .../yang/v3po/impl/rev141210/V3poModuleTest.java | 70 - .../fd/honeycomb/data/impl/AbstractInfraTest.java | 106 ++ .../data/impl/HoneycombReadInfraTest.java | 460 +++++++ .../data/impl/HoneycombSubtreeReadInfraTest.java | 150 +++ .../data/impl/HoneycombWriteInfraTest.java | 522 ++++++++ .../test/java/io/fd/honeycomb/data/impl/Ids.java | 62 + .../v3po/data/impl/AbstractInfraTest.java | 106 -- .../v3po/data/impl/HoneycombReadInfraTest.java | 460 ------- .../data/impl/HoneycombSubtreeReadInfraTest.java | 150 --- .../v3po/data/impl/HoneycombWriteInfraTest.java | 521 -------- .../java/io/fd/honeycomb/v3po/data/impl/Ids.java | 62 - .../notification/ManagedNotificationProducer.java | 38 + .../notification/NotificationCollector.java | 35 + .../notification/NotificationProducer.java | 37 + .../notification/ManagedNotificationProducer.java | 38 - .../v3po/notification/NotificationCollector.java | 35 - .../v3po/notification/NotificationProducer.java | 37 - .../api/src/main/yang/notification-api.yang | 4 +- .../main/config/notification-to-netconf-config.xml | 4 +- .../impl/HoneycombNotificationCollector.java | 66 + .../impl/NotificationProducerRegistry.java | 112 ++ .../impl/NotificationProducerTracker.java | 109 ++ .../impl/HoneycombNotificationCollector.java | 66 - .../impl/NotificationProducerRegistry.java | 112 -- .../impl/NotificationProducerTracker.java | 109 -- .../HoneycombNotificationManagerModule.java | 10 +- ...ycombNotificationToNetconfTranslatorModule.java | 4 +- .../impl/HoneycombNotificationCollectorTest.java | 63 + .../impl/NotificationProducerRegistryTest.java | 98 ++ .../impl/NotificationProducerTrackerTest.java | 67 + .../impl/HoneycombNotificationCollectorTest.java | 63 - .../impl/NotificationProducerRegistryTest.java | 98 -- .../impl/NotificationProducerTrackerTest.java | 67 - infra/notification/pom.xml | 1 + infra/postman_rest_collection.json | 1386 -------------------- .../io/fd/honeycomb/translate/MappingContext.java | 66 + .../ModifiableSubtreeManagerRegistryBuilder.java | 77 ++ .../fd/honeycomb/translate/ModificationCache.java | 49 + .../honeycomb/translate/ModificationContext.java | 44 + .../io/fd/honeycomb/translate/SubtreeManager.java | 39 + .../translate/SubtreeManagerRegistryBuilder.java | 22 + .../honeycomb/translate/TranslationException.java | 38 + .../io/fd/honeycomb/translate/read/ListReader.java | 66 + .../fd/honeycomb/translate/read/ReadContext.java | 26 + .../translate/read/ReadFailedException.java | 61 + .../io/fd/honeycomb/translate/read/Reader.java | 76 ++ .../fd/honeycomb/translate/read/ReaderFactory.java | 38 + .../registry/ModifiableReaderRegistryBuilder.java | 41 + .../translate/read/registry/ReaderRegistry.java | 62 + .../read/registry/ReaderRegistryBuilder.java | 27 + .../translate/write/DataObjectUpdate.java | 114 ++ .../fd/honeycomb/translate/write/ListWriter.java | 33 + .../fd/honeycomb/translate/write/WriteContext.java | 50 + .../translate/write/WriteFailedException.java | 166 +++ .../io/fd/honeycomb/translate/write/Writer.java | 48 + .../honeycomb/translate/write/WriterFactory.java | 38 + .../registry/ModifiableWriterRegistryBuilder.java | 31 + .../translate/write/registry/WriterRegistry.java | 213 +++ .../write/registry/WriterRegistryBuilder.java | 27 + .../honeycomb/v3po/translate/MappingContext.java | 66 - .../ModifiableSubtreeManagerRegistryBuilder.java | 77 -- .../v3po/translate/ModificationCache.java | 49 - .../v3po/translate/ModificationContext.java | 44 - .../honeycomb/v3po/translate/SubtreeManager.java | 39 - .../translate/SubtreeManagerRegistryBuilder.java | 22 - .../v3po/translate/TranslationException.java | 38 - .../honeycomb/v3po/translate/read/ListReader.java | 66 - .../honeycomb/v3po/translate/read/ReadContext.java | 26 - .../v3po/translate/read/ReadFailedException.java | 61 - .../fd/honeycomb/v3po/translate/read/Reader.java | 76 -- .../v3po/translate/read/ReaderFactory.java | 38 - .../registry/ModifiableReaderRegistryBuilder.java | 41 - .../translate/read/registry/ReaderRegistry.java | 62 - .../read/registry/ReaderRegistryBuilder.java | 27 - .../v3po/translate/write/DataObjectUpdate.java | 114 -- .../honeycomb/v3po/translate/write/ListWriter.java | 33 - .../v3po/translate/write/WriteContext.java | 50 - .../v3po/translate/write/WriteFailedException.java | 166 --- .../fd/honeycomb/v3po/translate/write/Writer.java | 48 - .../v3po/translate/write/WriterFactory.java | 38 - .../registry/ModifiableWriterRegistryBuilder.java | 31 - .../translate/write/registry/WriterRegistry.java | 213 --- .../write/registry/WriterRegistryBuilder.java | 27 - .../translate-api/src/main/yang/translate-api.yang | 14 +- .../translate/ReadFailedExceptionTest.java | 50 + .../v3po/translate/ReadFailedExceptionTest.java | 50 - infra/translate-impl/pom.xml | 2 +- .../translate/impl/read/GenericListReader.java | 117 ++ .../translate/impl/read/GenericReader.java | 70 + .../translate/impl/write/GenericListWriter.java | 106 ++ .../translate/impl/write/GenericWriter.java | 61 + .../translate/impl/read/GenericListReader.java | 117 -- .../v3po/translate/impl/read/GenericReader.java | 70 - .../translate/impl/write/GenericListWriter.java | 106 -- .../v3po/translate/impl/write/GenericWriter.java | 61 - .../impl/write/GenericListWriterTest.java | 83 ++ .../translate/impl/write/GenericWriterTest.java | 64 + .../impl/write/GenericListWriterTest.java | 83 -- .../translate/impl/write/GenericWriterTest.java | 64 - .../translate/spi/read/ListReaderCustomizer.java | 63 + .../translate/spi/read/ReaderCustomizer.java | 63 + .../translate/spi/write/ListWriterCustomizer.java | 34 + .../translate/spi/write/WriterCustomizer.java | 75 ++ .../translate/spi/read/ListReaderCustomizer.java | 63 - .../v3po/translate/spi/read/ReaderCustomizer.java | 63 - .../translate/spi/write/ListWriterCustomizer.java | 34 - .../v3po/translate/spi/write/WriterCustomizer.java | 75 -- ...stractSubtreeManagerRegistryBuilderBuilder.java | 213 +++ .../io/fd/honeycomb/translate/util/JsonUtils.java | 102 ++ .../io/fd/honeycomb/translate/util/RWUtils.java | 186 +++ .../honeycomb/translate/util/ReflectionUtils.java | 79 ++ .../translate/util/TransactionMappingContext.java | 75 ++ .../translate/util/read/AbstractGenericReader.java | 90 ++ .../translate/util/read/BindingBrokerReader.java | 96 ++ .../util/read/KeepaliveReaderWrapper.java | 173 +++ .../translate/util/read/NoopReaderCustomizer.java | 34 + .../util/read/ReflexiveListReaderCustomizer.java | 65 + .../translate/util/read/ReflexiveReader.java | 57 + .../util/read/ReflexiveReaderCustomizer.java | 103 ++ .../util/read/registry/CompositeReader.java | 202 +++ .../read/registry/CompositeReaderRegistry.java | 117 ++ .../registry/CompositeReaderRegistryBuilder.java | 109 ++ .../util/read/registry/SubtreeReader.java | 250 ++++ .../util/read/registry/TypeHierarchy.java | 101 ++ .../util/write/AbstractGenericWriter.java | 137 ++ .../translate/util/write/NoopWriterRegistry.java | 40 + .../util/write/TransactionWriteContext.java | 121 ++ .../util/write/registry/FlatWriterRegistry.java | 315 +++++ .../write/registry/FlatWriterRegistryBuilder.java | 71 + .../util/write/registry/SubtreeWriter.java | 85 ++ ...stractSubtreeManagerRegistryBuilderBuilder.java | 213 --- .../honeycomb/v3po/translate/util/JsonUtils.java | 102 -- .../fd/honeycomb/v3po/translate/util/RWUtils.java | 186 --- .../v3po/translate/util/ReflectionUtils.java | 79 -- .../translate/util/TransactionMappingContext.java | 75 -- .../translate/util/read/AbstractGenericReader.java | 90 -- .../translate/util/read/BindingBrokerReader.java | 96 -- .../util/read/KeepaliveReaderWrapper.java | 173 --- .../translate/util/read/NoopReaderCustomizer.java | 34 - .../util/read/ReflexiveListReaderCustomizer.java | 65 - .../v3po/translate/util/read/ReflexiveReader.java | 57 - .../util/read/ReflexiveReaderCustomizer.java | 103 -- .../util/read/registry/CompositeReader.java | 202 --- .../read/registry/CompositeReaderRegistry.java | 117 -- .../registry/CompositeReaderRegistryBuilder.java | 109 -- .../util/read/registry/SubtreeReader.java | 250 ---- .../util/read/registry/TypeHierarchy.java | 101 -- .../util/write/AbstractGenericWriter.java | 137 -- .../translate/util/write/NoopWriterRegistry.java | 40 - .../util/write/TransactionWriteContext.java | 121 -- .../util/write/registry/FlatWriterRegistry.java | 315 ----- .../write/registry/FlatWriterRegistryBuilder.java | 71 - .../util/write/registry/SubtreeWriter.java | 85 -- .../rev160406/DelegatingReaderRegistryModule.java | 2 +- .../rev160406/DelegatingWriterRegistryModule.java | 2 +- .../utils/rev160406/NoopWriterRegistryModule.java | 6 +- .../rev160406/RealtimeMappingContextModule.java | 2 +- .../write/util/TransactionWriteContextTest.java | 131 ++ .../fd/honeycomb/translate/util/DataObjects.java | 52 + .../fd/honeycomb/translate/util/JsonUtilsTest.java | 109 ++ .../CompositeReaderRegistryBuilderTest.java | 114 ++ .../util/read/registry/SubtreeReaderTest.java | 124 ++ .../util/read/registry/TypeHierarchyTest.java | 72 + .../registry/FlatWriterRegistryBuilderTest.java | 131 ++ .../write/registry/FlatWriterRegistryTest.java | 265 ++++ .../util/write/registry/SubtreeWriterTest.java | 84 ++ .../write/util/TransactionWriteContextTest.java | 131 -- .../honeycomb/v3po/translate/util/DataObjects.java | 52 - .../v3po/translate/util/JsonUtilsTest.java | 109 -- .../CompositeReaderRegistryBuilderTest.java | 114 -- .../util/read/registry/SubtreeReaderTest.java | 124 -- .../util/read/registry/TypeHierarchyTest.java | 69 - .../registry/FlatWriterRegistryBuilderTest.java | 131 -- .../write/registry/FlatWriterRegistryTest.java | 264 ---- .../util/write/registry/SubtreeWriterTest.java | 84 -- 272 files changed, 12688 insertions(+), 14165 deletions(-) create mode 100644 infra/cfg-init/src/main/java/io/fd/honeycomb/data/init/AbstractDataTreeConverter.java create mode 100644 infra/cfg-init/src/main/java/io/fd/honeycomb/data/init/DataTreeInitializer.java create mode 100644 infra/cfg-init/src/main/java/io/fd/honeycomb/data/init/InitializerRegistry.java create mode 100644 infra/cfg-init/src/main/java/io/fd/honeycomb/data/init/InitializerRegistryImpl.java create mode 100644 infra/cfg-init/src/main/java/io/fd/honeycomb/data/init/RestoringInitializer.java delete mode 100644 infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/AbstractDataTreeConverter.java delete mode 100644 infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/DataTreeInitializer.java delete mode 100644 infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistry.java delete mode 100644 infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistryImpl.java delete mode 100644 infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/RestoringInitializer.java create mode 100644 infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/init/rev160407/ConfigurationInitializerRegistryModule.java create mode 100644 infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/init/rev160407/ConfigurationInitializerRegistryModuleFactory.java create mode 100644 infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/init/rev160407/PersistedFileInitializerModule.java create mode 100644 infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/init/rev160407/PersistedFileInitializerModuleFactory.java delete mode 100644 infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/ConfigurationInitializerRegistryModule.java delete mode 100644 infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/ConfigurationInitializerRegistryModuleFactory.java delete mode 100644 infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/PersistedFileInitializerModule.java delete mode 100644 infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/PersistedFileInitializerModuleFactory.java create mode 100644 infra/cfg-init/src/main/yang/cfg-init.yang delete mode 100644 infra/cfg-init/src/main/yang/vpp-cfg-init.yang create mode 100644 infra/cfg-init/src/test/java/io/fd/honeycomb/data/init/InitializerRegistryImplTest.java delete mode 100644 infra/cfg-init/src/test/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistryImplTest.java create mode 100644 infra/data-api/src/main/java/io/fd/honeycomb/data/DataModification.java create mode 100644 infra/data-api/src/main/java/io/fd/honeycomb/data/ModifiableDataManager.java create mode 100644 infra/data-api/src/main/java/io/fd/honeycomb/data/ReadableDataManager.java delete mode 100644 infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/DataModification.java delete mode 100644 infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/ModifiableDataManager.java delete mode 100644 infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/ReadableDataManager.java create mode 100644 infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/DataBroker.java create mode 100644 infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModifiableDataTreeDelegator.java create mode 100644 infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModifiableDataTreeManager.java create mode 100644 infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModificationDiff.java create mode 100644 infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/PersistingDataTreeAdapter.java create mode 100644 infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ReadOnlyTransaction.java create mode 100644 infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ReadWriteTransaction.java create mode 100644 infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ReadableDataTreeDelegator.java create mode 100644 infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/WriteTransaction.java delete mode 100644 infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataBroker.java delete mode 100644 infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java delete mode 100644 infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeManager.java delete mode 100644 infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModificationDiff.java delete mode 100644 infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java delete mode 100644 infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java delete mode 100644 infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadWriteTransaction.java delete mode 100644 infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegator.java delete mode 100644 infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java create mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/DataBrokerTest.java create mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ModifiableDataTreeDelegatorTest.java create mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ModificationDiffTest.java create mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/PersistingDataTreeAdapterTest.java create mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ReadOnlyTransactionTest.java create mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ReadWriteTransactionTest.java create mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ReadableDataTreeDelegatorTest.java create mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/WriteTransactionTest.java delete mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java delete mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java delete mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java delete mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapterTest.java delete mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java delete mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadWriteTransactionTest.java delete mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegatorTest.java delete mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java create mode 100644 infra/impl/src/main/java/io/fd/honeycomb/impl/NorthboundFacadeHoneycombDOMBroker.java delete mode 100644 infra/impl/src/main/java/io/fd/honeycomb/v3po/impl/NorthboundFacadeHoneycombDOMBroker.java create mode 100644 infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/ContextDataBrokerModule.java create mode 100644 infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/ContextDataBrokerModuleFactory.java create mode 100644 infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/DataBrokerModule.java create mode 100644 infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/DataBrokerModuleFactory.java create mode 100644 infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/HoneycombModule.java create mode 100644 infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/HoneycombModuleFactory.java create mode 100644 infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/NetconfBindingBrokerModule.java create mode 100644 infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/NetconfBindingBrokerModuleFactory.java create mode 100644 infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/NetconfMonitoringReaderModule.java create mode 100644 infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/NetconfMonitoringReaderModuleFactory.java delete mode 100644 infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModule.java delete mode 100644 infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModuleFactory.java delete mode 100644 infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/DataBrokerModule.java delete mode 100644 infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/DataBrokerModuleFactory.java delete mode 100644 infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfBindingBrokerModule.java delete mode 100644 infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfBindingBrokerModuleFactory.java delete mode 100644 infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfMonitoringReaderModule.java delete mode 100644 infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfMonitoringReaderModuleFactory.java delete mode 100644 infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModule.java delete mode 100644 infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactory.java create mode 100644 infra/impl/src/main/yang/honeycomb-impl.yang delete mode 100644 infra/impl/src/main/yang/v3po-impl.yang delete mode 100644 infra/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactoryTest.java delete mode 100644 infra/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleTest.java create mode 100644 infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/AbstractInfraTest.java create mode 100644 infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/HoneycombReadInfraTest.java create mode 100644 infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/HoneycombSubtreeReadInfraTest.java create mode 100644 infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/HoneycombWriteInfraTest.java create mode 100644 infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/Ids.java delete mode 100644 infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/AbstractInfraTest.java delete mode 100644 infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombReadInfraTest.java delete mode 100644 infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombSubtreeReadInfraTest.java delete mode 100644 infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombWriteInfraTest.java delete mode 100644 infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/Ids.java create mode 100644 infra/notification/api/src/main/java/io/fd/honeycomb/notification/ManagedNotificationProducer.java create mode 100644 infra/notification/api/src/main/java/io/fd/honeycomb/notification/NotificationCollector.java create mode 100644 infra/notification/api/src/main/java/io/fd/honeycomb/notification/NotificationProducer.java delete mode 100644 infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/ManagedNotificationProducer.java delete mode 100644 infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationCollector.java delete mode 100644 infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationProducer.java create mode 100644 infra/notification/impl/src/main/java/io/fd/honeycomb/notification/impl/HoneycombNotificationCollector.java create mode 100644 infra/notification/impl/src/main/java/io/fd/honeycomb/notification/impl/NotificationProducerRegistry.java create mode 100644 infra/notification/impl/src/main/java/io/fd/honeycomb/notification/impl/NotificationProducerTracker.java delete mode 100644 infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollector.java delete mode 100644 infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistry.java delete mode 100644 infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTracker.java create mode 100644 infra/notification/impl/src/test/java/io/fd/honeycomb/notification/impl/HoneycombNotificationCollectorTest.java create mode 100644 infra/notification/impl/src/test/java/io/fd/honeycomb/notification/impl/NotificationProducerRegistryTest.java create mode 100644 infra/notification/impl/src/test/java/io/fd/honeycomb/notification/impl/NotificationProducerTrackerTest.java delete mode 100644 infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollectorTest.java delete mode 100644 infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistryTest.java delete mode 100644 infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTrackerTest.java delete mode 100644 infra/postman_rest_collection.json create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/MappingContext.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/ModifiableSubtreeManagerRegistryBuilder.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/ModificationCache.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/ModificationContext.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/SubtreeManager.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/SubtreeManagerRegistryBuilder.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/TranslationException.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/ListReader.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/ReadContext.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/ReadFailedException.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/Reader.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/ReaderFactory.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/registry/ModifiableReaderRegistryBuilder.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/registry/ReaderRegistry.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/registry/ReaderRegistryBuilder.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/DataObjectUpdate.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/ListWriter.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/WriteContext.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/WriteFailedException.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/Writer.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/WriterFactory.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/registry/ModifiableWriterRegistryBuilder.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/registry/WriterRegistry.java create mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/registry/WriterRegistryBuilder.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/MappingContext.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModifiableSubtreeManagerRegistryBuilder.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModificationCache.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModificationContext.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/SubtreeManager.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/SubtreeManagerRegistryBuilder.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/TranslationException.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ListReader.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReadContext.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReadFailedException.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/Reader.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReaderFactory.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ModifiableReaderRegistryBuilder.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ReaderRegistry.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ReaderRegistryBuilder.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/DataObjectUpdate.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ListWriter.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriteContext.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriteFailedException.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/Writer.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterFactory.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/ModifiableWriterRegistryBuilder.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/WriterRegistry.java delete mode 100644 infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/WriterRegistryBuilder.java create mode 100644 infra/translate-api/src/test/java/io/fd/honeycomb/translate/ReadFailedExceptionTest.java delete mode 100644 infra/translate-api/src/test/java/io/fd/honeycomb/v3po/translate/ReadFailedExceptionTest.java create mode 100644 infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/read/GenericListReader.java create mode 100644 infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/read/GenericReader.java create mode 100644 infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/GenericListWriter.java create mode 100644 infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/GenericWriter.java delete mode 100644 infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/GenericListReader.java delete mode 100644 infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/GenericReader.java delete mode 100644 infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriter.java delete mode 100644 infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriter.java create mode 100644 infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/GenericListWriterTest.java create mode 100644 infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/GenericWriterTest.java delete mode 100644 infra/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriterTest.java delete mode 100644 infra/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriterTest.java create mode 100644 infra/translate-spi/src/main/java/io/fd/honeycomb/translate/spi/read/ListReaderCustomizer.java create mode 100644 infra/translate-spi/src/main/java/io/fd/honeycomb/translate/spi/read/ReaderCustomizer.java create mode 100644 infra/translate-spi/src/main/java/io/fd/honeycomb/translate/spi/write/ListWriterCustomizer.java create mode 100644 infra/translate-spi/src/main/java/io/fd/honeycomb/translate/spi/write/WriterCustomizer.java delete mode 100644 infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/ListReaderCustomizer.java delete mode 100644 infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/ReaderCustomizer.java delete mode 100644 infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/write/ListWriterCustomizer.java delete mode 100644 infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/write/WriterCustomizer.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/AbstractSubtreeManagerRegistryBuilderBuilder.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/JsonUtils.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/RWUtils.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/ReflectionUtils.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/TransactionMappingContext.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/AbstractGenericReader.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/BindingBrokerReader.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/KeepaliveReaderWrapper.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/NoopReaderCustomizer.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/ReflexiveListReaderCustomizer.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/ReflexiveReader.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/ReflexiveReaderCustomizer.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReader.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReaderRegistry.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReaderRegistryBuilder.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/SubtreeReader.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/TypeHierarchy.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/AbstractGenericWriter.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/NoopWriterRegistry.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/TransactionWriteContext.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/registry/FlatWriterRegistry.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/registry/FlatWriterRegistryBuilder.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/registry/SubtreeWriter.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/AbstractSubtreeManagerRegistryBuilderBuilder.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/JsonUtils.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/ReflectionUtils.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/TransactionMappingContext.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/AbstractGenericReader.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/BindingBrokerReader.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/KeepaliveReaderWrapper.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/NoopReaderCustomizer.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveListReaderCustomizer.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReader.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReaderCustomizer.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReader.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistry.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistryBuilder.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/SubtreeReader.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/TypeHierarchy.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/AbstractGenericWriter.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionWriteContext.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java delete mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriter.java create mode 100644 infra/translate-utils/src/test/java/io/fd/honeycomb/translate/impl/write/util/TransactionWriteContextTest.java create mode 100644 infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/DataObjects.java create mode 100644 infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/JsonUtilsTest.java create mode 100644 infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/read/registry/CompositeReaderRegistryBuilderTest.java create mode 100644 infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/read/registry/SubtreeReaderTest.java create mode 100644 infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/read/registry/TypeHierarchyTest.java create mode 100644 infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/write/registry/FlatWriterRegistryBuilderTest.java create mode 100644 infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/write/registry/FlatWriterRegistryTest.java create mode 100644 infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/write/registry/SubtreeWriterTest.java delete mode 100644 infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/util/TransactionWriteContextTest.java delete mode 100644 infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/DataObjects.java delete mode 100644 infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/JsonUtilsTest.java delete mode 100644 infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistryBuilderTest.java delete mode 100644 infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/SubtreeReaderTest.java delete mode 100644 infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/TypeHierarchyTest.java delete mode 100644 infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilderTest.java delete mode 100644 infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryTest.java delete mode 100644 infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriterTest.java (limited to 'infra') diff --git a/infra/cfg-init/src/main/java/io/fd/honeycomb/data/init/AbstractDataTreeConverter.java b/infra/cfg-init/src/main/java/io/fd/honeycomb/data/init/AbstractDataTreeConverter.java new file mode 100644 index 000000000..74cdc3058 --- /dev/null +++ b/infra/cfg-init/src/main/java/io/fd/honeycomb/data/init/AbstractDataTreeConverter.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.init; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.Beta; +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; +import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Base class for initializers which perform conversion between operational and config YANG model. + * + * @param Config data object + * @param Operational data object + */ +@Beta +public abstract class AbstractDataTreeConverter + implements DataTreeInitializer { + private static final Logger LOG = LoggerFactory.getLogger(AbstractDataTreeConverter.class); + + private final InstanceIdentifier idOper; + private final InstanceIdentifier idConfig; + private final DataBroker bindingDataBroker; + + public AbstractDataTreeConverter(final DataBroker bindingDataBroker, + final InstanceIdentifier operRootId, + final InstanceIdentifier cfgRootId) { + this.bindingDataBroker = checkNotNull(bindingDataBroker, "bindingDataBroker should not be null"); + this.idOper = checkNotNull(operRootId, "operRootId should not be null"); + this.idConfig = checkNotNull(cfgRootId, "cfgRootId should not be null"); + } + + @Override + public void close() throws Exception { + LOG.debug("AbstractDataTreeConverter.close()"); + // Not removing initialized data, since this works in cooperation with persistence, it could remove + // data restored by persistence or remove user configured data when shutting down HC + } + + @Override + public final void initialize() throws InitializeException { + LOG.debug("AbstractDataTreeConverter.initialize() from(oper): {}, to(cfg): {}", idOper, idConfig); + final Optional data = readData(); + + if (data.isPresent()) { + LOG.debug("Config initialization, operational data={}", data); + + final O operationalData = data.get(); + final C configData = convert(operationalData); + + try { + LOG.debug("Initializing config with data={}", configData); + writeData(configData); + LOG.info("Config initialization successful from(oper): {}, to(cfg): {}", idOper, idConfig); + } catch (TransactionCommitFailedException e) { + throw new InitializeException("Failed to perform config initialization", e); + } + } else { + LOG.info("Data is not present under: {}, no initial changes to config at: {}", idOper, idConfig); + } + } + + private Optional readData() { + try (ReadOnlyTransaction readTx = bindingDataBroker.newReadOnlyTransaction()) { + final CheckedFuture, org.opendaylight.controller.md.sal.common.api.data.ReadFailedException> + readFuture = readTx.read(LogicalDatastoreType.OPERATIONAL, idOper); + return readFuture.checkedGet(); + } catch (org.opendaylight.controller.md.sal.common.api.data.ReadFailedException e) { + LOG.warn("Failed to read operational state", e); + } + return Optional.absent(); + } + + private void writeData(final C configData) throws TransactionCommitFailedException { + final WriteTransaction writeTx = bindingDataBroker.newWriteOnlyTransaction(); + // Merge(instead of put) has to be used due to dynamic start, this might be executed multiple times + // and might overwrite config restored from persisted file with the same incomplete config. + // Making the entire configuration trigger VPP twice (on second persis ... and VPP does not like that + writeTx.merge(LogicalDatastoreType.CONFIGURATION, idConfig, configData); + writeTx.submit().checkedGet(); + } + + // TODO make this class concrete and use function dependency instead of abstract method + /** + * Converts operational data to config data for given root node + * @param operationalData data object representing operational data + * @return data object representing config data + */ + protected abstract C convert(final O operationalData); +} diff --git a/infra/cfg-init/src/main/java/io/fd/honeycomb/data/init/DataTreeInitializer.java b/infra/cfg-init/src/main/java/io/fd/honeycomb/data/init/DataTreeInitializer.java new file mode 100644 index 000000000..c0ff8c2be --- /dev/null +++ b/infra/cfg-init/src/main/java/io/fd/honeycomb/data/init/DataTreeInitializer.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.init; + +import com.google.common.annotations.Beta; + +/** + * Service for config data tree initialization. + * Implementation reads operational data and initializes config data tree. + * Initialization does not cause any change in VPP state, unlike ordinary writes to config. + */ +@Beta +public interface DataTreeInitializer extends AutoCloseable { + + /** + * Initializes config data tree for supported root node. + * @throws InitializeException if initialization failed + */ + void initialize() throws InitializeException; + + /** + * Removes all data managed by the initializer. + */ + @Override + void close() throws Exception; + + class InitializeException extends Exception { + + public InitializeException(final String message, final Throwable cause) { + super(message, cause); + } + + public InitializeException(final String msg) { + super(msg); + } + } +} diff --git a/infra/cfg-init/src/main/java/io/fd/honeycomb/data/init/InitializerRegistry.java b/infra/cfg-init/src/main/java/io/fd/honeycomb/data/init/InitializerRegistry.java new file mode 100644 index 000000000..b662a4fae --- /dev/null +++ b/infra/cfg-init/src/main/java/io/fd/honeycomb/data/init/InitializerRegistry.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.init; + +import com.google.common.annotations.Beta; + +/** + * Data tree initializer suitable as a holder for all other root initializers, providing initializeAll feature. + */ +@Beta +public interface InitializerRegistry extends DataTreeInitializer { + + /** + * Performs initialize on all registered root initializers. + * @throws if initialization failed + */ + @Override + void initialize() throws InitializeException; +} diff --git a/infra/cfg-init/src/main/java/io/fd/honeycomb/data/init/InitializerRegistryImpl.java b/infra/cfg-init/src/main/java/io/fd/honeycomb/data/init/InitializerRegistryImpl.java new file mode 100644 index 000000000..b83dd1e04 --- /dev/null +++ b/infra/cfg-init/src/main/java/io/fd/honeycomb/data/init/InitializerRegistryImpl.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.init; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.List; +import javax.annotation.Nonnull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class InitializerRegistryImpl implements InitializerRegistry { + private static final Logger LOG = LoggerFactory.getLogger(InitializerRegistryImpl.class); + private final List initializers; + + public InitializerRegistryImpl(@Nonnull List initializers) { + this.initializers = checkNotNull(initializers, "initializers should not be null"); + checkArgument(!initializers.contains(null), "initializers should not contain null elements"); + } + + @Override + public void close() throws Exception { + LOG.debug("InitializerRegistryImpl.close()"); + for (DataTreeInitializer initializer : initializers) { + initializer.close(); + } + } + + @Override + public void initialize() throws InitializeException { + // TODO check if readers are there + LOG.debug("InitializerRegistryImpl.initialize()"); + for (DataTreeInitializer initializer : initializers) { + initializer.initialize(); + } + } +} diff --git a/infra/cfg-init/src/main/java/io/fd/honeycomb/data/init/RestoringInitializer.java b/infra/cfg-init/src/main/java/io/fd/honeycomb/data/init/RestoringInitializer.java new file mode 100644 index 000000000..54c3df787 --- /dev/null +++ b/infra/cfg-init/src/main/java/io/fd/honeycomb/data/init/RestoringInitializer.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.init; + +import static com.google.common.base.Preconditions.checkArgument; + +import io.fd.honeycomb.translate.util.JsonUtils; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; +import org.opendaylight.controller.sal.core.api.model.SchemaService; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.init.rev160407.RestorationType; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RestoringInitializer implements DataTreeInitializer { + + private static final Logger LOG = LoggerFactory.getLogger(RestoringInitializer.class); + + private final SchemaService schemaService; + private final Path path; + private final DOMDataBroker dataTree; + private final RestorationType restorationType; + private final LogicalDatastoreType datastoreType; + + public RestoringInitializer(@Nonnull final SchemaService schemaService, + @Nonnull final Path path, + @Nonnull final DOMDataBroker dataTree, + @Nonnull final RestorationType restorationType, + @Nonnull final LogicalDatastoreType datastoreType) { + this.schemaService = schemaService; + this.datastoreType = datastoreType; + this.path = checkStorage(path); + this.dataTree = dataTree; + this.restorationType = restorationType; + } + + private Path checkStorage(final Path path) { + if (Files.exists(path)) { + checkArgument(!Files.isDirectory(path), "File %s is a directory", path); + checkArgument(Files.isReadable(path), "File %s is not readable", path); + } + + return path; + } + + @Override + public void initialize() throws InitializeException { + LOG.debug("Starting restoration of {} from {} using {}", dataTree, path, restorationType); + if(!Files.exists(path)) { + LOG.debug("Persist file {} does not exist. Skipping restoration", path); + return; + } + + try { + final ContainerNode containerNode = JsonUtils + .readJsonRoot(schemaService.getGlobalContext(), Files.newInputStream(path, StandardOpenOption.READ)); + + final DOMDataWriteTransaction domDataWriteTransaction = dataTree.newWriteOnlyTransaction(); + for (DataContainerChild dataContainerChild : containerNode + .getValue()) { + final YangInstanceIdentifier iid = YangInstanceIdentifier.create(dataContainerChild.getIdentifier()); + LOG.trace("Restoring {} from {}", iid, path); + + switch (restorationType) { + case Merge: + domDataWriteTransaction.merge(datastoreType, iid, dataContainerChild); + break; + case Put: + domDataWriteTransaction.put(datastoreType, iid, dataContainerChild); + break; + default: + throw new InitializeException( + "Unable to initialize data using " + restorationType + " restoration strategy. Unsupported"); + } + } + + // Block here to prevent subsequent initializers processing before context is fully restored + domDataWriteTransaction.submit().checkedGet(); + LOG.debug("Data from {} restored successfully", path); + + } catch (IOException | TransactionCommitFailedException e) { + throw new InitializeException("Unable to restore data from " + path, e); + } + } + + @Override + public void close() {} +} diff --git a/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/AbstractDataTreeConverter.java b/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/AbstractDataTreeConverter.java deleted file mode 100644 index f0058264e..000000000 --- a/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/AbstractDataTreeConverter.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.vpp.data.init; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.annotations.Beta; -import com.google.common.base.Optional; -import com.google.common.util.concurrent.CheckedFuture; -import org.opendaylight.controller.md.sal.binding.api.DataBroker; -import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; -import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Base class for initializers which perform conversion between operational and config YANG model. - * - * @param Config data object - * @param Operational data object - */ -@Beta -public abstract class AbstractDataTreeConverter - implements DataTreeInitializer { - private static final Logger LOG = LoggerFactory.getLogger(AbstractDataTreeConverter.class); - - private final InstanceIdentifier idOper; - private final InstanceIdentifier idConfig; - private final DataBroker bindingDataBroker; - - public AbstractDataTreeConverter(final DataBroker bindingDataBroker, - final InstanceIdentifier operRootId, - final InstanceIdentifier cfgRootId) { - this.bindingDataBroker = checkNotNull(bindingDataBroker, "bindingDataBroker should not be null"); - this.idOper = checkNotNull(operRootId, "operRootId should not be null"); - this.idConfig = checkNotNull(cfgRootId, "cfgRootId should not be null"); - } - - @Override - public void close() throws Exception { - LOG.debug("AbstractDataTreeConverter.close()"); - // Not removing initialized data, since this works in cooperation with persistence, it could remove - // data restored by persistence or remove user configured data when shutting down HC - } - - @Override - public final void initialize() throws InitializeException { - LOG.debug("AbstractDataTreeConverter.initialize() from(oper): {}, to(cfg): {}", idOper, idConfig); - final Optional data = readData(); - - if (data.isPresent()) { - LOG.debug("Config initialization, operational data={}", data); - - final O operationalData = data.get(); - final C configData = convert(operationalData); - - try { - LOG.debug("Initializing config with data={}", configData); - writeData(configData); - LOG.info("Config initialization successful from(oper): {}, to(cfg): {}", idOper, idConfig); - } catch (TransactionCommitFailedException e) { - throw new InitializeException("Failed to perform config initialization", e); - } - } else { - LOG.info("Data is not present under: {}, no initial changes to config at: {}", idOper, idConfig); - } - } - - private Optional readData() { - try (ReadOnlyTransaction readTx = bindingDataBroker.newReadOnlyTransaction()) { - final CheckedFuture, org.opendaylight.controller.md.sal.common.api.data.ReadFailedException> - readFuture = readTx.read(LogicalDatastoreType.OPERATIONAL, idOper); - return readFuture.checkedGet(); - } catch (org.opendaylight.controller.md.sal.common.api.data.ReadFailedException e) { - LOG.warn("Failed to read operational state", e); - } - return Optional.absent(); - } - - private void writeData(final C configData) throws TransactionCommitFailedException { - final WriteTransaction writeTx = bindingDataBroker.newWriteOnlyTransaction(); - // Merge(instead of put) has to be used due to dynamic start, this might be executed multiple times - // and might overwrite config restored from persisted file with the same incomplete config. - // Making the entire configuration trigger VPP twice (on second persis ... and VPP does not like that - writeTx.merge(LogicalDatastoreType.CONFIGURATION, idConfig, configData); - writeTx.submit().checkedGet(); - } - - // TODO make this class concrete and use function dependency instead of abstract method - /** - * Converts operational data to config data for given root node - * @param operationalData data object representing operational data - * @return data object representing config data - */ - protected abstract C convert(final O operationalData); -} diff --git a/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/DataTreeInitializer.java b/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/DataTreeInitializer.java deleted file mode 100644 index d760401f9..000000000 --- a/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/DataTreeInitializer.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.vpp.data.init; - -import com.google.common.annotations.Beta; - -/** - * Service for config data tree initialization. - * Implementation reads operational data and initializes config data tree. - * Initialization does not cause any change in VPP state, unlike ordinary writes to config. - */ -@Beta -public interface DataTreeInitializer extends AutoCloseable { - - /** - * Initializes config data tree for supported root node. - * @throws InitializeException if initialization failed - */ - void initialize() throws InitializeException; - - /** - * Removes all data managed by the initializer. - */ - @Override - void close() throws Exception; - - class InitializeException extends Exception { - - public InitializeException(final String message, final Throwable cause) { - super(message, cause); - } - - public InitializeException(final String msg) { - super(msg); - } - } -} diff --git a/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistry.java b/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistry.java deleted file mode 100644 index 8760f0f09..000000000 --- a/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistry.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.vpp.data.init; - -import com.google.common.annotations.Beta; - -/** - * Data tree initializer suitable as a holder for all other root initializers, providing initializeAll feature. - */ -@Beta -public interface InitializerRegistry extends DataTreeInitializer { - - /** - * Performs initialize on all registered root initializers. - * @throws if initialization failed - */ - @Override - void initialize() throws InitializeException; -} diff --git a/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistryImpl.java b/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistryImpl.java deleted file mode 100644 index e5220f7e8..000000000 --- a/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistryImpl.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.vpp.data.init; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.List; -import javax.annotation.Nonnull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class InitializerRegistryImpl implements InitializerRegistry { - private static final Logger LOG = LoggerFactory.getLogger(InitializerRegistryImpl.class); - private final List initializers; - - public InitializerRegistryImpl(@Nonnull List initializers) { - this.initializers = checkNotNull(initializers, "initializers should not be null"); - checkArgument(!initializers.contains(null), "initializers should not contain null elements"); - } - - @Override - public void close() throws Exception { - LOG.debug("InitializerRegistryImpl.close()"); - for (DataTreeInitializer initializer : initializers) { - initializer.close(); - } - } - - @Override - public void initialize() throws InitializeException { - // TODO check if readers are there - LOG.debug("InitializerRegistryImpl.initialize()"); - for (DataTreeInitializer initializer : initializers) { - initializer.initialize(); - } - } -} diff --git a/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/RestoringInitializer.java b/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/RestoringInitializer.java deleted file mode 100644 index abc3f54c0..000000000 --- a/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/RestoringInitializer.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.vpp.data.init; - -import static com.google.common.base.Preconditions.checkArgument; - -import io.fd.honeycomb.v3po.translate.util.JsonUtils; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import javax.annotation.Nonnull; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; -import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; -import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; -import org.opendaylight.controller.sal.core.api.model.SchemaService; -import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407.RestorationType; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class RestoringInitializer implements DataTreeInitializer { - - private static final Logger LOG = LoggerFactory.getLogger(RestoringInitializer.class); - - private final SchemaService schemaService; - private final Path path; - private final DOMDataBroker dataTree; - private final RestorationType restorationType; - private final LogicalDatastoreType datastoreType; - - public RestoringInitializer(@Nonnull final SchemaService schemaService, - @Nonnull final Path path, - @Nonnull final DOMDataBroker dataTree, - @Nonnull final RestorationType restorationType, - @Nonnull final LogicalDatastoreType datastoreType) { - this.schemaService = schemaService; - this.datastoreType = datastoreType; - this.path = checkStorage(path); - this.dataTree = dataTree; - this.restorationType = restorationType; - } - - private Path checkStorage(final Path path) { - if (Files.exists(path)) { - checkArgument(!Files.isDirectory(path), "File %s is a directory", path); - checkArgument(Files.isReadable(path), "File %s is not readable", path); - } - - return path; - } - - @Override - public void initialize() throws InitializeException { - LOG.debug("Starting restoration of {} from {} using {}", dataTree, path, restorationType); - if(!Files.exists(path)) { - LOG.debug("Persist file {} does not exist. Skipping restoration", path); - return; - } - - try { - final ContainerNode containerNode = JsonUtils - .readJsonRoot(schemaService.getGlobalContext(), Files.newInputStream(path, StandardOpenOption.READ)); - - final DOMDataWriteTransaction domDataWriteTransaction = dataTree.newWriteOnlyTransaction(); - for (DataContainerChild dataContainerChild : containerNode - .getValue()) { - final YangInstanceIdentifier iid = YangInstanceIdentifier.create(dataContainerChild.getIdentifier()); - LOG.trace("Restoring {} from {}", iid, path); - - switch (restorationType) { - case Merge: - domDataWriteTransaction.merge(datastoreType, iid, dataContainerChild); - break; - case Put: - domDataWriteTransaction.put(datastoreType, iid, dataContainerChild); - break; - default: - throw new InitializeException( - "Unable to initialize data using " + restorationType + " restoration strategy. Unsupported"); - } - } - - // Block here to prevent subsequent initializers processing before context is fully restored - domDataWriteTransaction.submit().checkedGet(); - LOG.debug("Data from {} restored successfully", path); - - } catch (IOException | TransactionCommitFailedException e) { - throw new InitializeException("Unable to restore data from " + path, e); - } - } - - @Override - public void close() {} -} diff --git a/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/init/rev160407/ConfigurationInitializerRegistryModule.java b/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/init/rev160407/ConfigurationInitializerRegistryModule.java new file mode 100644 index 000000000..3a90c5303 --- /dev/null +++ b/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/init/rev160407/ConfigurationInitializerRegistryModule.java @@ -0,0 +1,54 @@ +package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.init.rev160407; + +import io.fd.honeycomb.data.init.InitializerRegistry; +import io.fd.honeycomb.data.init.InitializerRegistryImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** +* Initializer registry, delegating initialization to a list of initializers +*/ +public class ConfigurationInitializerRegistryModule extends AbstractConfigurationInitializerRegistryModule { + + private static final Logger LOG = LoggerFactory.getLogger(ConfigurationInitializerRegistryModule.class); + + public ConfigurationInitializerRegistryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public ConfigurationInitializerRegistryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, ConfigurationInitializerRegistryModule oldModule, java.lang.AutoCloseable oldInstance) { + super(identifier, dependencyResolver, oldModule, oldInstance); + } + + @Override + public void customValidation() { + // add custom validation form module attributes here. + } + + @Override + public java.lang.AutoCloseable createInstance() { + LOG.info("Config initialization started"); + + final InitializerRegistry initializer = new InitializerRegistryImpl(getInitializersDependency()); + + try { + // Initialize contexts first so that other initializers can find any relevant mapping before initializing + // configuration to what is already in VPP + getPersistedContextInitializerDependency().initialize(); + LOG.info("Persisted context restored successfully"); + // Initialize all registered initializers + initializer.initialize(); + LOG.info("Configuration initialized successfully"); + // Initialize stored configuration on top + getPersistedConfigInitializerDependency().initialize(); + LOG.info("Persisted configuration restored successfully"); + } catch (Exception e) { + LOG.warn("Failed to initialize config", e); + } + + LOG.info("Honeycomb initialized"); + + return initializer; + } + +} diff --git a/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/init/rev160407/ConfigurationInitializerRegistryModuleFactory.java b/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/init/rev160407/ConfigurationInitializerRegistryModuleFactory.java new file mode 100644 index 000000000..461c1f01b --- /dev/null +++ b/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/init/rev160407/ConfigurationInitializerRegistryModuleFactory.java @@ -0,0 +1,14 @@ +/* +* Generated file +* +* Generated from: yang module name: vpp-cfg-init yang module local name: cfg-initializer-registry +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Wed May 18 14:43:49 CEST 2016 +* +* Do not modify this file unless it is present under src/main directory +*/ +package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.init.rev160407; +public class ConfigurationInitializerRegistryModuleFactory extends + AbstractConfigurationInitializerRegistryModuleFactory { + +} diff --git a/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/init/rev160407/PersistedFileInitializerModule.java b/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/init/rev160407/PersistedFileInitializerModule.java new file mode 100644 index 000000000..b57ded9fc --- /dev/null +++ b/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/init/rev160407/PersistedFileInitializerModule.java @@ -0,0 +1,38 @@ +package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.init.rev160407; + +import io.fd.honeycomb.data.init.RestoringInitializer; +import java.nio.file.InvalidPathException; +import java.nio.file.Paths; +import org.opendaylight.controller.config.api.JmxAttributeValidationException; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.api.rev160411.DatatreeType; + +/** +* Initializer restoring data from a persisted file +*/ +public class PersistedFileInitializerModule extends AbstractPersistedFileInitializerModule { + public PersistedFileInitializerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public PersistedFileInitializerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, PersistedFileInitializerModule oldModule, java.lang.AutoCloseable oldInstance) { + super(identifier, dependencyResolver, oldModule, oldInstance); + } + + @Override + public void customValidation() { + try { + Paths.get(getPersistFilePath()); + } catch (InvalidPathException e) { + throw new JmxAttributeValidationException("Invalid persist path", e, persistFilePathJmxAttribute); + } + } + + @Override + public java.lang.AutoCloseable createInstance() { + return new RestoringInitializer(getSchemaServiceDependency(), Paths.get(getPersistFilePath()), + getDomDataBrokerDependency(), getRestorationType(), + getDatastoreType() == DatatreeType.Config ? LogicalDatastoreType.CONFIGURATION : LogicalDatastoreType.OPERATIONAL); + } + +} diff --git a/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/init/rev160407/PersistedFileInitializerModuleFactory.java b/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/init/rev160407/PersistedFileInitializerModuleFactory.java new file mode 100644 index 000000000..7e703d558 --- /dev/null +++ b/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/init/rev160407/PersistedFileInitializerModuleFactory.java @@ -0,0 +1,13 @@ +/* +* Generated file +* +* Generated from: yang module name: vpp-cfg-init yang module local name: persisted-file-initializer +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Wed May 18 13:48:52 CEST 2016 +* +* Do not modify this file unless it is present under src/main directory +*/ +package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.init.rev160407; +public class PersistedFileInitializerModuleFactory extends AbstractPersistedFileInitializerModuleFactory { + +} diff --git a/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/ConfigurationInitializerRegistryModule.java b/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/ConfigurationInitializerRegistryModule.java deleted file mode 100644 index 3fff82e32..000000000 --- a/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/ConfigurationInitializerRegistryModule.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407; - -import io.fd.honeycomb.v3po.vpp.data.init.InitializerRegistry; -import io.fd.honeycomb.v3po.vpp.data.init.InitializerRegistryImpl; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** -* Initializer registry, delegating initialization to a list of initializers -*/ -public class ConfigurationInitializerRegistryModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407.AbstractConfigurationInitializerRegistryModule { - - private static final Logger LOG = LoggerFactory.getLogger(ConfigurationInitializerRegistryModule.class); - - public ConfigurationInitializerRegistryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { - super(identifier, dependencyResolver); - } - - public ConfigurationInitializerRegistryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407.ConfigurationInitializerRegistryModule oldModule, java.lang.AutoCloseable oldInstance) { - super(identifier, dependencyResolver, oldModule, oldInstance); - } - - @Override - public void customValidation() { - // add custom validation form module attributes here. - } - - @Override - public java.lang.AutoCloseable createInstance() { - LOG.info("VppConfigurationInitializerModule.createInstance(): initialization started"); - - final InitializerRegistry initializer = new InitializerRegistryImpl(getInitializersDependency()); - - try { - // Initialize contexts first so that other initializers can find any relevant mapping before initializing - // configuration to what is already in VPP - getPersistedContextInitializerDependency().initialize(); - LOG.info("Persisted context restored successfully"); - // Initialize all registered initializers - initializer.initialize(); - LOG.info("VPP configuration initialized successfully from VPP"); - // Initialize stored configuration on top - getPersistedConfigInitializerDependency().initialize(); - LOG.info("Persisted configuration restored successfully"); - } catch (Exception e) { - LOG.warn("Failed to initialize config", e); - } - - LOG.info("Honeycomb initialized"); - - return initializer; - } - -} diff --git a/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/ConfigurationInitializerRegistryModuleFactory.java b/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/ConfigurationInitializerRegistryModuleFactory.java deleted file mode 100644 index f07be953e..000000000 --- a/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/ConfigurationInitializerRegistryModuleFactory.java +++ /dev/null @@ -1,13 +0,0 @@ -/* -* Generated file -* -* Generated from: yang module name: vpp-cfg-init yang module local name: cfg-initializer-registry -* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator -* Generated at: Wed May 18 14:43:49 CEST 2016 -* -* Do not modify this file unless it is present under src/main directory -*/ -package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407; -public class ConfigurationInitializerRegistryModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407.AbstractConfigurationInitializerRegistryModuleFactory { - -} diff --git a/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/PersistedFileInitializerModule.java b/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/PersistedFileInitializerModule.java deleted file mode 100644 index 188d1641c..000000000 --- a/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/PersistedFileInitializerModule.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407; - -import io.fd.honeycomb.v3po.vpp.data.init.RestoringInitializer; -import java.nio.file.InvalidPathException; -import java.nio.file.Paths; -import org.opendaylight.controller.config.api.JmxAttributeValidationException; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.api.rev160411.DatatreeType; - -/** -* Initializer restoring data from a persisted file -*/ -public class PersistedFileInitializerModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407.AbstractPersistedFileInitializerModule { - public PersistedFileInitializerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { - super(identifier, dependencyResolver); - } - - public PersistedFileInitializerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407.PersistedFileInitializerModule oldModule, java.lang.AutoCloseable oldInstance) { - super(identifier, dependencyResolver, oldModule, oldInstance); - } - - @Override - public void customValidation() { - try { - Paths.get(getPersistFilePath()); - } catch (InvalidPathException e) { - throw new JmxAttributeValidationException("Invalid persist path", e, persistFilePathJmxAttribute); - } - } - - @Override - public java.lang.AutoCloseable createInstance() { - return new RestoringInitializer(getSchemaServiceDependency(), Paths.get(getPersistFilePath()), - getDomDataBrokerDependency(), getRestorationType(), - getDatastoreType() == DatatreeType.Config ? LogicalDatastoreType.CONFIGURATION : LogicalDatastoreType.OPERATIONAL); - } - -} diff --git a/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/PersistedFileInitializerModuleFactory.java b/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/PersistedFileInitializerModuleFactory.java deleted file mode 100644 index 1d7a3c9c6..000000000 --- a/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/PersistedFileInitializerModuleFactory.java +++ /dev/null @@ -1,13 +0,0 @@ -/* -* Generated file -* -* Generated from: yang module name: vpp-cfg-init yang module local name: persisted-file-initializer -* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator -* Generated at: Wed May 18 13:48:52 CEST 2016 -* -* Do not modify this file unless it is present under src/main directory -*/ -package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407; -public class PersistedFileInitializerModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407.AbstractPersistedFileInitializerModuleFactory { - -} diff --git a/infra/cfg-init/src/main/yang/cfg-init.yang b/infra/cfg-init/src/main/yang/cfg-init.yang new file mode 100644 index 000000000..8b1a4d9d1 --- /dev/null +++ b/infra/cfg-init/src/main/yang/cfg-init.yang @@ -0,0 +1,117 @@ +module cfg-init { + yang-version 1; + namespace "urn:honeycomb:params:xml:ns:yang:data:init"; + prefix "init"; + + import config { prefix config; revision-date 2013-04-05; } + import opendaylight-md-sal-dom {prefix dom;} + import translate-api { prefix tapi; revision-date 2016-04-06; } + import data-api { prefix dapi; revision-date 2016-04-11; } + + description + "This module contains initializers for config data tree"; + + revision "2016-04-07" { + description + "Initial revision"; + } + + identity cfg-initializer { + base "config:service-type"; + config:java-class io.fd.honeycomb.data.init.DataTreeInitializer; + } + + identity cfg-initializer-registry { + base config:module-type; + config:provided-service cfg-initializer; + config:java-name-prefix ConfigurationInitializerRegistry; + description "Initializer registry, delegating initialization to a list of initializers"; + } + + augment "/config:modules/config:module/config:configuration" { + case cfg-initializer-registry { + when "/config:modules/config:module/config:type = 'cfg-initializer-registry'"; + + list initializers { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity cfg-initializer; + } + } + } + + container persisted-context-initializer { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity cfg-initializer; + } + } + } + + container persisted-config-initializer { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity cfg-initializer; + } + } + } + } + } + + identity persisted-file-initializer { + base config:module-type; + config:provided-service cfg-initializer; + description "Initializer restoring data from a persisted file"; + } + + typedef restoration-type { + type enumeration { + enum merge; + enum put; + } + } + + augment "/config:modules/config:module/config:configuration" { + case persisted-file-initializer { + when "/config:modules/config:module/config:type = 'persisted-file-initializer'"; + + container dom-data-broker { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity dom:dom-async-data-broker; + } + } + } + + container schema-service { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity dom:schema-service; + } + } + } + + leaf persist-file-path { + type string; + mandatory true; + } + + leaf restoration-type { + type restoration-type; + default merge; + } + + + leaf datastore-type { + type dapi:datatree-type; + mandatory true; + } + } + } + +} \ No newline at end of file diff --git a/infra/cfg-init/src/main/yang/vpp-cfg-init.yang b/infra/cfg-init/src/main/yang/vpp-cfg-init.yang deleted file mode 100644 index 52750d926..000000000 --- a/infra/cfg-init/src/main/yang/vpp-cfg-init.yang +++ /dev/null @@ -1,117 +0,0 @@ -module vpp-cfg-init { - yang-version 1; - namespace "urn:honeycomb:params:xml:ns:yang:vpp:data:init"; - prefix "init"; - - import config { prefix config; revision-date 2013-04-05; } - import opendaylight-md-sal-dom {prefix dom;} - import translate-api { prefix tapi; revision-date 2016-04-06; } - import data-api { prefix dapi; revision-date 2016-04-11; } - - description - "This module contains initializers for VPP config data tree"; - - revision "2016-04-07" { - description - "Initial revision"; - } - - identity cfg-initializer { - base "config:service-type"; - config:java-class io.fd.honeycomb.v3po.vpp.data.init.DataTreeInitializer; - } - - identity cfg-initializer-registry { - base config:module-type; - config:provided-service cfg-initializer; - config:java-name-prefix ConfigurationInitializerRegistry; - description "Initializer registry, delegating initialization to a list of initializers"; - } - - augment "/config:modules/config:module/config:configuration" { - case cfg-initializer-registry { - when "/config:modules/config:module/config:type = 'cfg-initializer-registry'"; - - list initializers { - uses config:service-ref { - refine type { - mandatory true; - config:required-identity cfg-initializer; - } - } - } - - container persisted-context-initializer { - uses config:service-ref { - refine type { - mandatory true; - config:required-identity cfg-initializer; - } - } - } - - container persisted-config-initializer { - uses config:service-ref { - refine type { - mandatory true; - config:required-identity cfg-initializer; - } - } - } - } - } - - identity persisted-file-initializer { - base config:module-type; - config:provided-service cfg-initializer; - description "Initializer restoring data from a persisted file"; - } - - typedef restoration-type { - type enumeration { - enum merge; - enum put; - } - } - - augment "/config:modules/config:module/config:configuration" { - case persisted-file-initializer { - when "/config:modules/config:module/config:type = 'persisted-file-initializer'"; - - container dom-data-broker { - uses config:service-ref { - refine type { - mandatory true; - config:required-identity dom:dom-async-data-broker; - } - } - } - - container schema-service { - uses config:service-ref { - refine type { - mandatory true; - config:required-identity dom:schema-service; - } - } - } - - leaf persist-file-path { - type string; - mandatory true; - } - - leaf restoration-type { - type restoration-type; - default merge; - } - - - leaf datastore-type { - type dapi:datatree-type; - mandatory true; - } - } - } - -} \ No newline at end of file diff --git a/infra/cfg-init/src/test/java/io/fd/honeycomb/data/init/InitializerRegistryImplTest.java b/infra/cfg-init/src/test/java/io/fd/honeycomb/data/init/InitializerRegistryImplTest.java new file mode 100644 index 000000000..009b7c9b4 --- /dev/null +++ b/infra/cfg-init/src/test/java/io/fd/honeycomb/data/init/InitializerRegistryImplTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.init; + +import static org.mockito.Mockito.verify; +import static org.mockito.MockitoAnnotations.initMocks; + +import java.util.Arrays; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +public class InitializerRegistryImplTest { + + @Mock(name="dti1") + private DataTreeInitializer dti1; + @Mock(name="dti2") + private DataTreeInitializer dti2; + @Mock(name="dti3") + private DataTreeInitializer dti3; + + private InitializerRegistryImpl initializerRegistry; + + @Before + public void setUp() throws Exception { + initMocks(this); + initializerRegistry = new InitializerRegistryImpl(Arrays.asList(dti1, dti2, dti3)); + } + + @Test(expected = IllegalArgumentException.class) + public void testConstructorFailed() throws Exception { + new InitializerRegistryImpl(Arrays.asList(dti1, null)); + } + + @Test + public void testInitialize() throws Exception { + initializerRegistry.initialize(); + + verify(dti1).initialize(); + verify(dti2).initialize(); + verify(dti3).initialize(); + } + + @Test + public void testClose() throws Exception { + initializerRegistry.close(); + + verify(dti1).close(); + verify(dti2).close(); + verify(dti3).close(); + } +} \ No newline at end of file diff --git a/infra/cfg-init/src/test/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistryImplTest.java b/infra/cfg-init/src/test/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistryImplTest.java deleted file mode 100644 index d562fb60c..000000000 --- a/infra/cfg-init/src/test/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistryImplTest.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.vpp.data.init; - -import static org.mockito.Mockito.verify; -import static org.mockito.MockitoAnnotations.initMocks; - -import java.util.Arrays; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; - -public class InitializerRegistryImplTest { - - @Mock(name="dti1") - private DataTreeInitializer dti1; - @Mock(name="dti2") - private DataTreeInitializer dti2; - @Mock(name="dti3") - private DataTreeInitializer dti3; - - private InitializerRegistryImpl initializerRegistry; - - @Before - public void setUp() throws Exception { - initMocks(this); - initializerRegistry = new InitializerRegistryImpl(Arrays.asList(dti1, dti2, dti3)); - } - - @Test(expected = IllegalArgumentException.class) - public void testConstructorFailed() throws Exception { - new InitializerRegistryImpl(Arrays.asList(dti1, null)); - } - - @Test - public void testInitialize() throws Exception { - initializerRegistry.initialize(); - - verify(dti1).initialize(); - verify(dti2).initialize(); - verify(dti3).initialize(); - } - - @Test - public void testClose() throws Exception { - initializerRegistry.close(); - - verify(dti1).close(); - verify(dti2).close(); - verify(dti3).close(); - } -} \ No newline at end of file diff --git a/infra/data-api/src/main/java/io/fd/honeycomb/data/DataModification.java b/infra/data-api/src/main/java/io/fd/honeycomb/data/DataModification.java new file mode 100644 index 000000000..6e903a4e2 --- /dev/null +++ b/infra/data-api/src/main/java/io/fd/honeycomb/data/DataModification.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data; + +import com.google.common.annotations.Beta; +import io.fd.honeycomb.translate.TranslationException; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; + +/** + * Modification of a {@link ModifiableDataManager}. + */ +@Beta +public interface DataModification extends ReadableDataManager { + + /** + * Delete the node at specified path. + * + * @param path Node path + */ + void delete(YangInstanceIdentifier path); + + /** + * Merge the specified data with the currently-present data + * at specified path. + * + * @param path Node path + * @param data Data to be merged + */ + void merge(YangInstanceIdentifier path, NormalizedNode data); + + /** + * Replace the data at specified path with supplied data. + * + * @param path Node path + * @param data New node data + */ + void write(YangInstanceIdentifier path, NormalizedNode data); + + /** + * Alters data tree using this modification. + * + * @throws DataValidationFailedException if modification data is not valid + * @throws TranslationException if failed while updating data tree state + */ + void commit() throws DataValidationFailedException, TranslationException; + + /** + * Validate and prepare modification before commit. Besides commit, no further operation is expected after validate + * and the behaviour is undefined. + * + * @throws DataValidationFailedException if modification data is not valid + */ + void validate() throws DataValidationFailedException; +} diff --git a/infra/data-api/src/main/java/io/fd/honeycomb/data/ModifiableDataManager.java b/infra/data-api/src/main/java/io/fd/honeycomb/data/ModifiableDataManager.java new file mode 100644 index 000000000..7652ccf82 --- /dev/null +++ b/infra/data-api/src/main/java/io/fd/honeycomb/data/ModifiableDataManager.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data; + +import com.google.common.annotations.Beta; + +/** + * Facade over data tree that allows tree modification. + */ +@Beta +public interface ModifiableDataManager extends ReadableDataManager { + + /** + * Creates read-only snapshot of a ModifiableDataTree. + * + * @return modification + */ + DataModification newModification(); +} diff --git a/infra/data-api/src/main/java/io/fd/honeycomb/data/ReadableDataManager.java b/infra/data-api/src/main/java/io/fd/honeycomb/data/ReadableDataManager.java new file mode 100644 index 000000000..887732ad4 --- /dev/null +++ b/infra/data-api/src/main/java/io/fd/honeycomb/data/ReadableDataManager.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data; + +import com.google.common.annotations.Beta; +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +/** + * Facade over data tree that allows reading tree nodes. + */ +@Beta +public interface ReadableDataManager { + + /** + * Reads a particular node from the data tree. + * + * @param path Path of the node + * @return a CheckFuture containing the result of the read. + */ + CheckedFuture>, ReadFailedException> read(@Nonnull final YangInstanceIdentifier path); +} diff --git a/infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/DataModification.java b/infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/DataModification.java deleted file mode 100644 index d05c55716..000000000 --- a/infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/DataModification.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data; - -import com.google.common.annotations.Beta; -import io.fd.honeycomb.v3po.translate.TranslationException; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; - -/** - * Modification of a {@link ModifiableDataManager}. - */ -@Beta -public interface DataModification extends ReadableDataManager { - - /** - * Delete the node at specified path. - * - * @param path Node path - */ - void delete(YangInstanceIdentifier path); - - /** - * Merge the specified data with the currently-present data - * at specified path. - * - * @param path Node path - * @param data Data to be merged - */ - void merge(YangInstanceIdentifier path, NormalizedNode data); - - /** - * Replace the data at specified path with supplied data. - * - * @param path Node path - * @param data New node data - */ - void write(YangInstanceIdentifier path, NormalizedNode data); - - /** - * Alters data tree using this modification. - * - * @throws DataValidationFailedException if modification data is not valid - * @throws TranslationException if failed while updating data tree state - */ - void commit() throws DataValidationFailedException, TranslationException; - - /** - * Validate and prepare modification before commit. Besides commit, no further operation is expected after validate - * and the behaviour is undefined. - * - * @throws DataValidationFailedException if modification data is not valid - */ - void validate() throws DataValidationFailedException; -} diff --git a/infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/ModifiableDataManager.java b/infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/ModifiableDataManager.java deleted file mode 100644 index 11cd513ea..000000000 --- a/infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/ModifiableDataManager.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data; - -import com.google.common.annotations.Beta; - -/** - * Facade over data tree that allows tree modification. - */ -@Beta -public interface ModifiableDataManager extends ReadableDataManager { - - /** - * Creates read-only snapshot of a ModifiableDataTree. - * - * @return modification - */ - DataModification newModification(); -} diff --git a/infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/ReadableDataManager.java b/infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/ReadableDataManager.java deleted file mode 100644 index 0e98c0903..000000000 --- a/infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/ReadableDataManager.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data; - -import com.google.common.annotations.Beta; -import com.google.common.base.Optional; -import com.google.common.util.concurrent.CheckedFuture; -import javax.annotation.Nonnull; -import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; - -/** - * Facade over data tree that allows reading tree nodes. - */ -@Beta -public interface ReadableDataManager { - - /** - * Reads a particular node from the data tree. - * - * @param path Path of the node - * @return a CheckFuture containing the result of the read. - */ - CheckedFuture>, ReadFailedException> read(@Nonnull final YangInstanceIdentifier path); -} diff --git a/infra/data-api/src/main/yang/data-api.yang b/infra/data-api/src/main/yang/data-api.yang index 693a73acd..d09d1d371 100644 --- a/infra/data-api/src/main/yang/data-api.yang +++ b/infra/data-api/src/main/yang/data-api.yang @@ -21,12 +21,12 @@ module data-api { identity honeycomb-readable-data-tree { base "config:service-type"; - config:java-class io.fd.honeycomb.v3po.data.ReadableDataManager; + config:java-class io.fd.honeycomb.data.ReadableDataManager; } identity honeycomb-modifiable-data-tree { base "config:service-type"; - config:java-class io.fd.honeycomb.v3po.data.ModifiableDataManager; + config:java-class io.fd.honeycomb.data.ModifiableDataManager; } typedef datatree-type { diff --git a/infra/data-impl/pom.xml b/infra/data-impl/pom.xml index b80c796a5..dc7f6b0d5 100644 --- a/infra/data-impl/pom.xml +++ b/infra/data-impl/pom.xml @@ -95,7 +95,7 @@ ${project.groupId}.${project.artifactId} - io.fd.honeycomb.v3po.data.impl.* + io.fd.honeycomb.data.impl.* diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/DataBroker.java b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/DataBroker.java new file mode 100644 index 000000000..eb19c9dd1 --- /dev/null +++ b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/DataBroker.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.honeycomb.data.DataModification; +import io.fd.honeycomb.data.ModifiableDataManager; +import io.fd.honeycomb.data.ReadableDataManager; +import java.io.Closeable; +import java.io.IOException; +import java.util.Collections; +import java.util.Map; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension; +import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Data Broker which provides data transaction functionality for YANG capable data provider using {@link NormalizedNode} + * data format. + */ +public class DataBroker implements DOMDataBroker, Closeable { + + private static final Logger LOG = LoggerFactory.getLogger(DataBroker.class); + private final TransactionFactory transactionFactory; + + /** + * Creates DataBroker instance. + * + * @param transactionFactory transaction producing factory + */ + public DataBroker(final TransactionFactory transactionFactory) { + this.transactionFactory = transactionFactory; + } + + @Override + public DOMDataReadOnlyTransaction newReadOnlyTransaction() { + LOG.trace("DataBroker({}).newReadOnlyTransaction()", this); + return transactionFactory.newReadOnlyTransaction(); + } + + @Override + public DOMDataReadWriteTransaction newReadWriteTransaction() { + LOG.trace("DataBroker({}).newReadWriteTransaction()", this); + return transactionFactory.newReadWriteTransaction(); + } + + @Override + public DOMDataWriteTransaction newWriteOnlyTransaction() { + LOG.trace("DataBroker({}).newWriteOnlyTransaction()", this); + return transactionFactory.newWriteOnlyTransaction(); + } + + @Override + public ListenerRegistration registerDataChangeListener(final LogicalDatastoreType store, + final YangInstanceIdentifier path, + final DOMDataChangeListener listener, + final DataChangeScope triggeringScope) { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public DOMTransactionChain createTransactionChain(final TransactionChainListener listener) { + throw new UnsupportedOperationException("Not supported"); + } + + @Nonnull + @Override + public Map, DOMDataBrokerExtension> getSupportedExtensions() { + return Collections.emptyMap(); + } + + /** + * Create DataBroker for a modifiable config DT, but only readable Operational + */ + @Nonnull + public static DataBroker create(@Nonnull final ModifiableDataManager configDataTree, + @Nonnull final ReadableDataManager operationalDataTree) { + checkNotNull(operationalDataTree, "operationalDataTree should not be null"); + checkNotNull(configDataTree, "configDataTree should not be null"); + return new DataBroker(new MainPipelineTxFactory(configDataTree, operationalDataTree)); + } + + /** + * Create DataBroker for modifiable operational DT, but no support for config + */ + @Nonnull + public static DataBroker create(@Nonnull final ModifiableDataManager operationalDataTree) { + checkNotNull(operationalDataTree, "operationalDataTree should not be null"); + return new DataBroker(new ContextPipelineTxFactory(operationalDataTree)); + } + + @Override + public void close() throws IOException { + // NOOP + } + + /** + * Transaction provider factory to be used by {@link DataBroker} + */ + public interface TransactionFactory { + + DOMDataReadOnlyTransaction newReadOnlyTransaction(); + + DOMDataReadWriteTransaction newReadWriteTransaction(); + + DOMDataWriteTransaction newWriteOnlyTransaction(); + } + + /** + * Transaction factory specific for Honeycomb's main pipeline (config: read+write, operational: read-only) + */ + private static class MainPipelineTxFactory implements TransactionFactory { + private final ReadableDataManager operationalDataTree; + private final ModifiableDataManager configDataTree; + + MainPipelineTxFactory(@Nonnull final ModifiableDataManager configDataTree, + @Nonnull final ReadableDataManager operationalDataTree) { + this.operationalDataTree = operationalDataTree; + this.configDataTree = configDataTree; + } + + @Override + public DOMDataReadOnlyTransaction newReadOnlyTransaction() { + return ReadOnlyTransaction.create(configDataTree.newModification(), operationalDataTree); + } + + @Override + public DOMDataReadWriteTransaction newReadWriteTransaction() { + final DataModification configModification = configDataTree.newModification(); + return new ReadWriteTransaction( + ReadOnlyTransaction.create(configModification, operationalDataTree), + WriteTransaction.createConfigOnly(configModification)); + } + + @Override + public DOMDataWriteTransaction newWriteOnlyTransaction() { + return WriteTransaction.createConfigOnly(configDataTree.newModification()); + } + } + + /** + * Transaction factory specific for Honeycomb's context pipeline (config: none, operational: read+write) + */ + private static class ContextPipelineTxFactory implements TransactionFactory { + private final ModifiableDataManager operationalDataTree; + + ContextPipelineTxFactory(@Nonnull final ModifiableDataManager operationalDataTree) { + this.operationalDataTree = operationalDataTree; + } + + @Override + public DOMDataReadOnlyTransaction newReadOnlyTransaction() { + return ReadOnlyTransaction.createOperationalOnly(operationalDataTree); + } + + @Override + public DOMDataReadWriteTransaction newReadWriteTransaction() { + final DataModification dataModification = operationalDataTree.newModification(); + return new ReadWriteTransaction( + ReadOnlyTransaction.createOperationalOnly(dataModification), + WriteTransaction.createOperationalOnly(dataModification)); + } + + @Override + public DOMDataWriteTransaction newWriteOnlyTransaction() { + return WriteTransaction.createOperationalOnly(operationalDataTree.newModification()); + } + } +} + + diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModifiableDataTreeDelegator.java b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModifiableDataTreeDelegator.java new file mode 100644 index 000000000..e9a616eea --- /dev/null +++ b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModifiableDataTreeDelegator.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.util.concurrent.Futures.immediateCheckedFuture; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.google.common.util.concurrent.CheckedFuture; +import io.fd.honeycomb.data.DataModification; +import io.fd.honeycomb.data.ReadableDataManager; +import io.fd.honeycomb.translate.TranslationException; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.honeycomb.translate.util.TransactionMappingContext; +import io.fd.honeycomb.translate.util.write.TransactionWriteContext; +import io.fd.honeycomb.translate.write.DataObjectUpdate; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.registry.WriterRegistry; +import java.util.Map; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Extension of {@link ModifiableDataTreeManager} that propagates data changes to underlying writer layer before they + * are fully committed in the backing data tree. Data changes are propagated in BA format. + */ +public final class ModifiableDataTreeDelegator extends ModifiableDataTreeManager { + + private static final Logger LOG = LoggerFactory.getLogger(ModifiableDataTreeDelegator.class); + private static final ReadableDataManager EMPTY_OPERATIONAL = p -> immediateCheckedFuture(Optional.absent()); + + private final WriterRegistry writerRegistry; + private final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker; + // TODO what to use instead of deprecated BindingNormalizedNodeSerializer ? + private final BindingNormalizedNodeSerializer serializer; + + /** + * Creates configuration data tree instance. + * @param serializer service for serialization between Java Binding Data representation and NormalizedNode + * representation. + * @param dataTree data tree for configuration data representation + * @param writerRegistry service for translation between Java Binding Data and data provider, capable of performing + * @param contextBroker BA broker providing full access to mapping context data + */ + public ModifiableDataTreeDelegator(@Nonnull final BindingNormalizedNodeSerializer serializer, + @Nonnull final DataTree dataTree, + @Nonnull final WriterRegistry writerRegistry, + @Nonnull final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker) { + super(dataTree); + this.contextBroker = checkNotNull(contextBroker, "contextBroker should not be null"); + this.serializer = checkNotNull(serializer, "serializer should not be null"); + this.writerRegistry = checkNotNull(writerRegistry, "writerRegistry should not be null"); + } + + @Override + public DataModification newModification() { + return new ConfigSnapshot(super.newModification()); + } + + private final class ConfigSnapshot extends ModifiableDataTreeManager.ConfigSnapshot { + + private final DataModification untouchedModification; + + /** + * @param untouchedModification DataModification captured while this modification/snapshot was created. + * To be used later while invoking writers to provide them with before state + * (state without current modifications). + * It must be captured as close as possible to when current modification started. + */ + ConfigSnapshot(final DataModification untouchedModification) { + this.untouchedModification = untouchedModification; + } + + /** + * Pass the changes to underlying writer layer. + * Transform from BI to BA. + * Revert(Write data before to subtrees that have been successfully modified before failure) in case of failure. + */ + @Override + protected void processCandidate(final DataTreeCandidate candidate) + throws TranslationException { + + final DataTreeCandidateNode rootNode = candidate.getRootNode(); + final YangInstanceIdentifier rootPath = candidate.getRootPath(); + LOG.trace("ConfigDataTree.modify() rootPath={}, rootNode={}, dataBefore={}, dataAfter={}", + rootPath, rootNode, rootNode.getDataBefore(), rootNode.getDataAfter()); + + final ModificationDiff modificationDiff = + ModificationDiff.recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, rootNode); + LOG.debug("ConfigDataTree.modify() diff: {}", modificationDiff); + + // Distinguish between updates (create + update) and deletes + final WriterRegistry.DataObjectUpdates baUpdates = toBindingAware(modificationDiff.getUpdates()); + LOG.debug("ConfigDataTree.modify() extracted updates={}", baUpdates); + + try (final WriteContext ctx = getTransactionWriteContext()) { + writerRegistry.update(baUpdates, ctx); + + final CheckedFuture contextUpdateResult = + ((TransactionMappingContext) ctx.getMappingContext()).submit(); + // Blocking on context data update + contextUpdateResult.checkedGet(); + + } catch (WriterRegistry.BulkUpdateException e) { + LOG.warn("Failed to apply all changes", e); + LOG.info("Trying to revert successful changes for current transaction"); + + try { + e.revertChanges(); + LOG.info("Changes successfully reverted"); + } catch (WriterRegistry.Reverter.RevertFailedException revertFailedException) { + // fail with failed revert + LOG.error("Failed to revert successful changes", revertFailedException); + throw revertFailedException; + } + + throw e; // fail with success revert + } catch (TransactionCommitFailedException e) { + // FIXME revert should probably occur when context is not written successfully + final String msg = "Error while updating mapping context data"; + LOG.error(msg, e); + throw new TranslationException(msg, e); + } catch (TranslationException e) { + LOG.error("Error while processing data change (updates={})", baUpdates, e); + throw e; + } + } + + private TransactionWriteContext getTransactionWriteContext() { + // Before Tx must use modification + final DOMDataReadOnlyTransaction beforeTx = ReadOnlyTransaction.create(untouchedModification, EMPTY_OPERATIONAL); + // After Tx must use current modification + final DOMDataReadOnlyTransaction afterTx = ReadOnlyTransaction.create(this, EMPTY_OPERATIONAL); + final TransactionMappingContext mappingContext = new TransactionMappingContext( + contextBroker.newReadWriteTransaction()); + return new TransactionWriteContext(serializer, beforeTx, afterTx, mappingContext); + } + + private WriterRegistry.DataObjectUpdates toBindingAware( + final Map biNodes) { + return ModifiableDataTreeDelegator.toBindingAware(biNodes, serializer); + } + } + + @VisibleForTesting + static WriterRegistry.DataObjectUpdates toBindingAware( + final Map biNodes, + final BindingNormalizedNodeSerializer serializer) { + + final Multimap, DataObjectUpdate> dataObjectUpdates = HashMultimap.create(); + final Multimap, DataObjectUpdate.DataObjectDelete> dataObjectDeletes = + HashMultimap.create(); + + for (Map.Entry biEntry : biNodes.entrySet()) { + final InstanceIdentifier unkeyedIid = + RWUtils.makeIidWildcarded(serializer.fromYangInstanceIdentifier(biEntry.getKey())); + + ModificationDiff.NormalizedNodeUpdate normalizedNodeUpdate = biEntry.getValue(); + final DataObjectUpdate dataObjectUpdate = toDataObjectUpdate(normalizedNodeUpdate, serializer); + if (dataObjectUpdate != null) { + if (dataObjectUpdate instanceof DataObjectUpdate.DataObjectDelete) { + dataObjectDeletes.put(unkeyedIid, ((DataObjectUpdate.DataObjectDelete) dataObjectUpdate)); + } else { + dataObjectUpdates.put(unkeyedIid, dataObjectUpdate); + } + } + } + return new WriterRegistry.DataObjectUpdates(dataObjectUpdates, dataObjectDeletes); + } + + @Nullable + private static DataObjectUpdate toDataObjectUpdate( + final ModificationDiff.NormalizedNodeUpdate normalizedNodeUpdate, + final BindingNormalizedNodeSerializer serializer) { + + InstanceIdentifier baId = serializer.fromYangInstanceIdentifier(normalizedNodeUpdate.getId()); + checkNotNull(baId, "Unable to transform instance identifier: %s into BA", normalizedNodeUpdate.getId()); + + DataObject dataObjectBefore = getDataObject(serializer, + normalizedNodeUpdate.getDataBefore(), normalizedNodeUpdate.getId()); + DataObject dataObjectAfter = + getDataObject(serializer, normalizedNodeUpdate.getDataAfter(), normalizedNodeUpdate.getId()); + + return dataObjectBefore == null && dataObjectAfter == null + ? null + : DataObjectUpdate.create(baId, dataObjectBefore, dataObjectAfter); + } + + @Nullable + private static DataObject getDataObject(@Nonnull final BindingNormalizedNodeSerializer serializer, + @Nullable final NormalizedNode data, + @Nonnull final YangInstanceIdentifier id) { + DataObject dataObject = null; + if (data != null) { + final Map.Entry, DataObject> dataObjectEntry = + serializer.fromNormalizedNode(id, data); + if (dataObjectEntry != null) { + dataObject = dataObjectEntry.getValue(); + } + } + return dataObject; + } + +} + + + diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModifiableDataTreeManager.java b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModifiableDataTreeManager.java new file mode 100644 index 000000000..61ccf185c --- /dev/null +++ b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModifiableDataTreeManager.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.util.concurrent.Futures.immediateCheckedFuture; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import io.fd.honeycomb.data.ModifiableDataManager; +import io.fd.honeycomb.data.DataModification; +import io.fd.honeycomb.translate.TranslationException; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * DataTree backed implementation for modifiable data manager. + */ +public class ModifiableDataTreeManager implements ModifiableDataManager { + + private static final Logger LOG = LoggerFactory.getLogger(ModifiableDataTreeManager.class); + + private final DataTree dataTree; + + public ModifiableDataTreeManager(@Nonnull final DataTree dataTree) { + this.dataTree = checkNotNull(dataTree, "dataTree should not be null"); + } + + @Override + public DataModification newModification() { + return new ConfigSnapshot(); + } + + @Override + public final CheckedFuture>, ReadFailedException> read(@Nonnull final YangInstanceIdentifier path) { + return newModification().read(path); + } + + protected class ConfigSnapshot implements DataModification { + private final DataTreeModification modification; + private boolean validated = false; + + ConfigSnapshot() { + this(dataTree.takeSnapshot().newModification()); + } + + protected ConfigSnapshot(final DataTreeModification modification) { + this.modification = modification; + } + + @Override + public CheckedFuture>, ReadFailedException> read( + @Nonnull final YangInstanceIdentifier path) { + final Optional> node = modification.readNode(path); + if (LOG.isTraceEnabled() && node.isPresent()) { + LOG.trace("ConfigSnapshot.read: {}", node.get()); + } + return immediateCheckedFuture(node); + } + + @Override + public final void delete(final YangInstanceIdentifier path) { + modification.delete(path); + } + + @Override + public final void merge(final YangInstanceIdentifier path, final NormalizedNode data) { + modification.merge(path, data); + } + + @Override + public final void write(final YangInstanceIdentifier path, final NormalizedNode data) { + modification.write(path, data); + } + + @Override + public final void commit() throws DataValidationFailedException, TranslationException { + if(!validated) { + validate(); + } + final DataTreeCandidate candidate = dataTree.prepare(modification); + processCandidate(candidate); + dataTree.commit(candidate); + } + + protected void processCandidate(final DataTreeCandidate candidate) throws TranslationException { + // NOOP + } + + @Override + public final void validate() throws DataValidationFailedException { + modification.ready(); + dataTree.validate(modification); + validated = true; + } + } +} + + + diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModificationDiff.java b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModificationDiff.java new file mode 100644 index 000000000..710f8a053 --- /dev/null +++ b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModificationDiff.java @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.collect.ImmutableMap; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; +import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.MixinNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType; + +/** + * Recursively collects and provides all unique and non-null modifications (modified normalized nodes). + */ +final class ModificationDiff { + + private static final ModificationDiff EMPTY_DIFF = new ModificationDiff(Collections.emptyMap()); + private static final EnumSet LEAF_MODIFICATIONS = EnumSet.of(ModificationType.WRITE, ModificationType.DELETE); + + private final Map updates; + + private ModificationDiff(@Nonnull Map updates) { + this.updates = updates; + } + + /** + * Get processed modifications. + * + * @return mapped modifications, where key is keyed {@link YangInstanceIdentifier}. + */ + Map getUpdates() { + return updates; + } + + private ModificationDiff merge(final ModificationDiff other) { + if (this == EMPTY_DIFF) { + return other; + } + + if (other == EMPTY_DIFF) { + return this; + } + + return new ModificationDiff(join(updates, other.updates)); + } + + private static Map join(Map first, + Map second) { + final Map merged = new HashMap<>(); + merged.putAll(first); + merged.putAll(second); + return merged; + } + + private static ModificationDiff create(YangInstanceIdentifier id, DataTreeCandidateNode candidate) { + return new ModificationDiff(ImmutableMap.of(id, NormalizedNodeUpdate.create(id, candidate))); + } + + /** + * Produce an aggregated diff from a candidate node recursively. MixinNodes are ignored as modifications and so + * are complex nodes which direct leaves were not modified. + */ + @Nonnull + static ModificationDiff recursivelyFromCandidate(@Nonnull final YangInstanceIdentifier yangIid, + @Nonnull final DataTreeCandidateNode currentCandidate) { + // recursively process child nodes for exact modifications + return recursivelyChildrenFromCandidate(yangIid, currentCandidate) + // also add modification on current level, if elligible + .merge(isModification(currentCandidate) + ? ModificationDiff.create(yangIid, currentCandidate) + : EMPTY_DIFF); + } + + /** + * Check whether current node was modified. {@link MixinNode}s are ignored + * and only nodes which direct leaves(or choices) are modified are considered a modification. + */ + private static Boolean isModification(@Nonnull final DataTreeCandidateNode currentCandidate) { + // Mixin nodes are not considered modifications + if (isMixin(currentCandidate) && !isAugment(currentCandidate)) { + return false; + } else { + return isCurrentModified(currentCandidate); + } + } + + private static Boolean isCurrentModified(final @Nonnull DataTreeCandidateNode currentCandidate) { + // Check if there are any modified leaves and if so, consider current node as modified + final Boolean directLeavesModified = currentCandidate.getChildNodes().stream() + .filter(ModificationDiff::isLeaf) + // For some reason, we get modifications on unmodified list keys TODO debug and report ODL bug + // and that messes up our modifications collection here, so we need to skip + .filter(ModificationDiff::isBeforeAndAfterDifferent) + .filter(child -> LEAF_MODIFICATIONS.contains(child.getModificationType())) + .findFirst() + .isPresent(); + + return directLeavesModified + // Also check choices (choices do not exist in BA world and if anything within a choice was modified, + // consider its parent as being modified) + || currentCandidate.getChildNodes().stream() + .filter(ModificationDiff::isChoice) + // Recursively check each choice if there was any change to it + .filter(ModificationDiff::isCurrentModified) + .findFirst() + .isPresent(); + } + + /** + * Process all non-leaf child nodes recursively, creating aggregated {@link ModificationDiff}. + */ + private static ModificationDiff recursivelyChildrenFromCandidate(final @Nonnull YangInstanceIdentifier yangIid, + final @Nonnull DataTreeCandidateNode currentCandidate) { + // recursively process child nodes for specific modifications + return currentCandidate.getChildNodes().stream() + // not interested in modifications to leaves + .filter(child -> !isLeaf(child)) + .map(candidate -> recursivelyFromCandidate(yangIid.node(candidate.getIdentifier()), candidate)) + .reduce(ModificationDiff::merge) + .orElse(EMPTY_DIFF); + } + + /** + * Check whether candidate.before and candidate.after is different. If not return false. + */ + private static boolean isBeforeAndAfterDifferent(@Nonnull final DataTreeCandidateNode candidateNode) { + if (candidateNode.getDataBefore().isPresent()) { + return !candidateNode.getDataBefore().get().equals(candidateNode.getDataAfter().orNull()); + } + + // considering not a modification if data after is also null + return candidateNode.getDataAfter().isPresent(); + } + + /** + * Check whether candidate node is for a leaf type node. + */ + private static boolean isLeaf(final DataTreeCandidateNode candidateNode) { + // orNull intentional, some candidate nodes have both data after and data before null + return candidateNode.getDataAfter().orNull() instanceof LeafNode + || candidateNode.getDataBefore().orNull() instanceof LeafNode; + } + + /** + * Check whether candidate node is for a Mixin type node. + */ + private static boolean isMixin(final DataTreeCandidateNode candidateNode) { + // orNull intentional, some candidate nodes have both data after and data before null + return candidateNode.getDataAfter().orNull() instanceof MixinNode + || candidateNode.getDataBefore().orNull() instanceof MixinNode; + } + + /** + * Check whether candidate node is for an Augmentation type node. + */ + private static boolean isAugment(final DataTreeCandidateNode candidateNode) { + // orNull intentional, some candidate nodes have both data after and data before null + return candidateNode.getDataAfter().orNull() instanceof AugmentationNode + || candidateNode.getDataBefore().orNull() instanceof AugmentationNode; + } + + /** + * Check whether candidate node is for a Choice type node. + */ + private static boolean isChoice(final DataTreeCandidateNode candidateNode) { + // orNull intentional, some candidate nodes have both data after and data before null + return candidateNode.getDataAfter().orNull() instanceof ChoiceNode + || candidateNode.getDataBefore().orNull() instanceof ChoiceNode; + } + + @Override + public String toString() { + return "ModificationDiff{updates=" + updates + '}'; + } + + /** + * Update to a normalized node identifiable by its {@link YangInstanceIdentifier}. + */ + static final class NormalizedNodeUpdate { + + @Nonnull + private final YangInstanceIdentifier id; + @Nullable + private final NormalizedNode dataBefore; + @Nullable + private final NormalizedNode dataAfter; + + private NormalizedNodeUpdate(@Nonnull final YangInstanceIdentifier id, + @Nullable final NormalizedNode dataBefore, + @Nullable final NormalizedNode dataAfter) { + this.id = checkNotNull(id); + this.dataAfter = dataAfter; + this.dataBefore = dataBefore; + } + + @Nullable + public NormalizedNode getDataBefore() { + return dataBefore; + } + + @Nullable + public NormalizedNode getDataAfter() { + return dataAfter; + } + + @Nonnull + public YangInstanceIdentifier getId() { + return id; + } + + static NormalizedNodeUpdate create(@Nonnull final YangInstanceIdentifier id, + @Nonnull final DataTreeCandidateNode candidate) { + return create(id, candidate.getDataBefore().orNull(), candidate.getDataAfter().orNull()); + } + + static NormalizedNodeUpdate create(@Nonnull final YangInstanceIdentifier id, + @Nullable final NormalizedNode dataBefore, + @Nullable final NormalizedNode dataAfter) { + checkArgument(!(dataBefore == null && dataAfter == null), "Both before and after data are null"); + return new NormalizedNodeUpdate(id, dataBefore, dataAfter); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + + final NormalizedNodeUpdate that = (NormalizedNodeUpdate) other; + + return id.equals(that.id); + + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public String toString() { + return "NormalizedNodeUpdate{" + "id=" + id + + ", dataBefore=" + dataBefore + + ", dataAfter=" + dataAfter + + '}'; + } + } + +} diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/PersistingDataTreeAdapter.java b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/PersistingDataTreeAdapter.java new file mode 100644 index 000000000..94c5e4446 --- /dev/null +++ b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/PersistingDataTreeAdapter.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Optional; +import io.fd.honeycomb.translate.util.JsonUtils; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import javax.annotation.Nonnull; +import org.opendaylight.controller.sal.core.api.model.SchemaService; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Adapter for a DataTree that stores current state of data in backing DataTree on each successful commit. + * Uses JSON format. + */ +public class PersistingDataTreeAdapter implements DataTree, AutoCloseable { + + private static final Logger LOG = LoggerFactory.getLogger(PersistingDataTreeAdapter.class); + + private final DataTree delegateDependency; + private final SchemaService schemaServiceDependency; + private final Path path; + + /** + * Create new Persisting DataTree adapter + * + * @param delegate backing data tree that actually handles all the operations + * @param persistPath path to a file (existing or not) to be used as storage for persistence. Full control over + * a file at peristPath is expected + * @param schemaService schemaContext provier + */ + public PersistingDataTreeAdapter(@Nonnull final DataTree delegate, + @Nonnull final SchemaService schemaService, + @Nonnull final Path persistPath) { + this.path = testPersistPath(checkNotNull(persistPath, "persistPath is null")); + this.delegateDependency = checkNotNull(delegate, "delegate is null"); + this.schemaServiceDependency = checkNotNull(schemaService, "schemaService is null"); + } + + /** + * Test whether file at persistPath exists and is readable or create it along with its parent structure + */ + private Path testPersistPath(final Path persistPath) { + try { + checkArgument(!Files.isDirectory(persistPath), "Path %s points to a directory", persistPath); + if(Files.exists(persistPath)) { + checkArgument(Files.isReadable(persistPath), + "Provided path %s points to existing, but non-readable file", persistPath); + return persistPath; + } + Files.createDirectories(persistPath.getParent()); + Files.write(persistPath, new byte[]{}, StandardOpenOption.CREATE); + } catch (IOException | UnsupportedOperationException e) { + LOG.warn("Provided path for persistence: {} is not usable", persistPath, e); + throw new IllegalArgumentException("Path " + persistPath + " cannot be used as "); + } + + return persistPath; + } + + @Override + public DataTreeSnapshot takeSnapshot() { + return delegateDependency.takeSnapshot(); + } + + @Override + public void setSchemaContext(final SchemaContext schemaContext) { + delegateDependency.setSchemaContext(schemaContext); + } + + @Override + public void commit(final DataTreeCandidate dataTreeCandidate) { + LOG.trace("Commit detected"); + delegateDependency.commit(dataTreeCandidate); + LOG.debug("Delegate commit successful. Persisting data"); + + // FIXME doing full read and full write might not be the fastest way of persisting data here + final DataTreeSnapshot dataTreeSnapshot = delegateDependency.takeSnapshot(); + + // TODO this can be handled in background by a dedicated thread + a limited blocking queue + // TODO enable configurable granularity for persists. Maybe doing write on every modification is too much + // and we could do bulk persist + persistCurrentData(dataTreeSnapshot.readNode(YangInstanceIdentifier.EMPTY)); + } + + private void persistCurrentData(final Optional> currentRoot) { + if(currentRoot.isPresent()) { + try { + LOG.trace("Persisting current data: {} into: {}", currentRoot.get(), path); + JsonUtils.writeJsonRoot(currentRoot.get(), schemaServiceDependency.getGlobalContext(), + Files.newOutputStream(path, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)); + LOG.trace("Data persisted successfully in {}", path); + } catch (IOException e) { + throw new IllegalStateException("Unable to persist current data", e); + } + } else { + LOG.debug("Skipping persistence, since there's no data to persist"); + } + } + + @Override + public YangInstanceIdentifier getRootPath() { + return delegateDependency.getRootPath(); + } + + @Override + public void validate(final DataTreeModification dataTreeModification) throws DataValidationFailedException { + delegateDependency.validate(dataTreeModification); + } + + @Override + public DataTreeCandidate prepare( + final DataTreeModification dataTreeModification) { + return delegateDependency.prepare(dataTreeModification); + } + + @Override + public void close() throws Exception { + LOG.trace("Closing {} for {}", getClass().getSimpleName(), path); + // NOOP + } +} diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ReadOnlyTransaction.java b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ReadOnlyTransaction.java new file mode 100644 index 000000000..00d105e26 --- /dev/null +++ b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ReadOnlyTransaction.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; + +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import io.fd.honeycomb.data.ReadableDataManager; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +final class ReadOnlyTransaction implements DOMDataReadOnlyTransaction { + + private static final Logger LOG = LoggerFactory.getLogger(ReadOnlyTransaction.class); + + @Nullable + private ReadableDataManager operationalData; + @Nullable + private ReadableDataManager configSnapshot; + + private boolean closed = false; + + /** + * @param configData config data tree manager. Null if config reads are not to be supported + * @param operationalData operational data tree manager. Null if operational reads are not to be supported + */ + private ReadOnlyTransaction(@Nullable final ReadableDataManager configData, + @Nullable final ReadableDataManager operationalData) { + this.configSnapshot = configData; + this.operationalData = operationalData; + } + + @Override + public synchronized void close() { + closed = true; + configSnapshot = null; + operationalData = null; + } + + @Override + public synchronized CheckedFuture>, ReadFailedException> read( + final LogicalDatastoreType store, + final YangInstanceIdentifier path) { + LOG.debug("ReadOnlyTransaction.read(), store={}, path={}", store, path); + checkState(!closed, "Transaction has been closed"); + + if (store == LogicalDatastoreType.OPERATIONAL) { + checkArgument(operationalData != null, "{} reads not supported", store); + return operationalData.read(path); + } else { + checkArgument(configSnapshot != null, "{} reads not supported", store); + return configSnapshot.read(path); + } + } + + @Override + public CheckedFuture exists(final LogicalDatastoreType store, + final YangInstanceIdentifier path) { + LOG.debug("ReadOnlyTransaction.exists() store={}, path={}", store, path); + + ListenableFuture listenableFuture = Futures.transform(read(store, path), IS_NODE_PRESENT); + return Futures.makeChecked(listenableFuture, ANY_EX_TO_READ_FAILED_EXCEPTION_MAPPER); + } + + @Nonnull + @Override + public Object getIdentifier() { + return this; + } + + @Nonnull + static ReadOnlyTransaction createOperationalOnly(@Nonnull final ReadableDataManager operationalData) { + return new ReadOnlyTransaction(null, requireNonNull(operationalData)); + } + + @Nonnull + static ReadOnlyTransaction createConfigOnly(@Nonnull final ReadableDataManager configData) { + return new ReadOnlyTransaction(requireNonNull(configData), null); + } + + @Nonnull + static ReadOnlyTransaction create(@Nonnull final ReadableDataManager configData, + @Nonnull final ReadableDataManager operationalData) { + return new ReadOnlyTransaction(requireNonNull(configData), requireNonNull(operationalData)); + } + + private static final Function>, ? extends Boolean> IS_NODE_PRESENT = + (Function>, Boolean>) input -> input == null ? Boolean.FALSE : input.isPresent(); + + private static final Function ANY_EX_TO_READ_FAILED_EXCEPTION_MAPPER = + (Function) e -> new ReadFailedException("Exists failed", e); +} \ No newline at end of file diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ReadWriteTransaction.java b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ReadWriteTransaction.java new file mode 100644 index 000000000..30035d99e --- /dev/null +++ b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ReadWriteTransaction.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.ListenableFuture; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.common.api.TransactionStatus; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +/** + * Composite DOM transaction that delegates reads to a {@link DOMDataReadTransaction} delegate and writes to a {@link + * DOMDataWriteTransaction} delegate. + */ +final class ReadWriteTransaction implements DOMDataReadWriteTransaction { + + private final DOMDataReadOnlyTransaction delegateReadTx; + private final DOMDataWriteTransaction delegateWriteTx; + + ReadWriteTransaction(@Nonnull final DOMDataReadOnlyTransaction delegateReadTx, + @Nonnull final DOMDataWriteTransaction delegateWriteTx) { + this.delegateReadTx = Preconditions.checkNotNull(delegateReadTx, "delegateReadTx should not be null"); + this.delegateWriteTx = Preconditions.checkNotNull(delegateWriteTx, "delegateWriteTx should not be null"); + } + + @Override + public boolean cancel() { + delegateReadTx.close(); + return delegateWriteTx.cancel(); + } + + @Override + public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, + final NormalizedNode data) { + delegateWriteTx.put(store, path, data); + } + + @Override + public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, + final NormalizedNode data) { + delegateWriteTx.merge(store, path, data); + } + + @Override + public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) { + delegateWriteTx.delete(store, path); + } + + @Override + public CheckedFuture submit() { + return delegateWriteTx.submit(); + } + + @Override + public ListenableFuture> commit() { + return delegateWriteTx.commit(); + } + + @Override + public CheckedFuture>, ReadFailedException> read(final LogicalDatastoreType store, + final YangInstanceIdentifier path) { + return delegateReadTx.read(store, path); + } + + @Override + public CheckedFuture exists(final LogicalDatastoreType store, + final YangInstanceIdentifier path) { + return delegateReadTx.exists(store, path); + } + + @Override + public Object getIdentifier() { + return this; + } +} + diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ReadableDataTreeDelegator.java b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ReadableDataTreeDelegator.java new file mode 100644 index 000000000..775f6326c --- /dev/null +++ b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ReadableDataTreeDelegator.java @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.getOnlyElement; + +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Collections2; +import com.google.common.collect.Multimap; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import io.fd.honeycomb.data.ReadableDataManager; +import io.fd.honeycomb.translate.MappingContext; +import io.fd.honeycomb.translate.ModificationCache; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.read.registry.ReaderRegistry; +import io.fd.honeycomb.translate.util.TransactionMappingContext; +import java.util.Collection; +import java.util.Map; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * ReadableDataTree implementation for operational data. + */ +public final class ReadableDataTreeDelegator implements ReadableDataManager { + private static final Logger LOG = LoggerFactory.getLogger(ReadableDataTreeDelegator.class); + + private final BindingNormalizedNodeSerializer serializer; + private final ReaderRegistry readerRegistry; + private final SchemaContext globalContext; + private final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker; + + /** + * Creates operational data tree instance. + * @param serializer service for serialization between Java Binding Data representation and NormalizedNode + * representation. + * @param globalContext service for obtaining top level context data from all yang modules. + * @param readerRegistry service responsible for translation between DataObjects and data provider. + * @param contextBroker BA broker for context data + */ + public ReadableDataTreeDelegator(@Nonnull BindingNormalizedNodeSerializer serializer, + @Nonnull final SchemaContext globalContext, + @Nonnull final ReaderRegistry readerRegistry, + @Nonnull final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker) { + this.contextBroker = checkNotNull(contextBroker, "contextBroker should not be null"); + this.globalContext = checkNotNull(globalContext, "globalContext should not be null"); + this.serializer = checkNotNull(serializer, "serializer should not be null"); + this.readerRegistry = checkNotNull(readerRegistry, "reader should not be null"); + } + + @Override + public CheckedFuture>, + org.opendaylight.controller.md.sal.common.api.data.ReadFailedException> read( + @Nonnull final YangInstanceIdentifier yangInstanceIdentifier) { + + try(TransactionMappingContext mappingContext = new TransactionMappingContext(contextBroker.newReadWriteTransaction()); + ReadContext ctx = new ReadContextImpl(mappingContext)) { + + final Optional> value; + if (checkNotNull(yangInstanceIdentifier).equals(YangInstanceIdentifier.EMPTY)) { + value = readRoot(ctx); + } else { + value = readNode(yangInstanceIdentifier, ctx); + } + + // Submit context mapping updates + final CheckedFuture contextUpdateResult = + ((TransactionMappingContext) ctx.getMappingContext()).submit(); + // Blocking on context data update + contextUpdateResult.checkedGet(); + + return Futures.immediateCheckedFuture(value); + + } catch (ReadFailedException e) { + return Futures.immediateFailedCheckedFuture( + new org.opendaylight.controller.md.sal.common.api.data.ReadFailedException("Failed to read data", e)); + } catch (TransactionCommitFailedException e) { + // FIXME revert should probably occur when context is not written successfully + final String msg = "Error while updating mapping context data"; + LOG.error(msg, e); + return Futures.immediateFailedCheckedFuture( + new org.opendaylight.controller.md.sal.common.api.data.ReadFailedException(msg, e) + ); + } + } + + private Optional> readNode(final YangInstanceIdentifier yangInstanceIdentifier, + final ReadContext ctx) throws ReadFailedException { + LOG.debug("OperationalDataTree.readNode(), yangInstanceIdentifier={}", yangInstanceIdentifier); + final InstanceIdentifier path = serializer.fromYangInstanceIdentifier(yangInstanceIdentifier); + checkNotNull(path, "Invalid instance identifier %s. Cannot create BA equivalent.", yangInstanceIdentifier); + LOG.debug("OperationalDataTree.readNode(), path={}", path); + + final Optional dataObject; + + dataObject = readerRegistry.read(path, ctx); + if (dataObject.isPresent()) { + final NormalizedNode value = toNormalizedNodeFunction(path).apply(dataObject.get()); + return Optional.>fromNullable(value); + } else { + return Optional.absent(); + } + } + + private Optional> readRoot(final ReadContext ctx) throws ReadFailedException { + LOG.debug("OperationalDataTree.readRoot()"); + + final DataContainerNodeAttrBuilder dataNodeBuilder = + Builders.containerBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(SchemaContext.NAME)); + + final Multimap, ? extends DataObject> dataObjects = + readerRegistry.readAll(ctx); + + for (final InstanceIdentifier instanceIdentifier : dataObjects.keySet()) { + final YangInstanceIdentifier rootElementId = serializer.toYangInstanceIdentifier(instanceIdentifier); + final NormalizedNode node = + wrapDataObjects(rootElementId, instanceIdentifier, dataObjects.get(instanceIdentifier)); + dataNodeBuilder.withChild((DataContainerChild) node); + } + return Optional.>of(dataNodeBuilder.build()); + } + + private NormalizedNode wrapDataObjects(final YangInstanceIdentifier yangInstanceIdentifier, + final InstanceIdentifier instanceIdentifier, + final Collection dataObjects) { + final Collection> normalizedRootElements = Collections2 + .transform(dataObjects, toNormalizedNodeFunction(instanceIdentifier)); + + final DataSchemaNode schemaNode = + globalContext.getDataChildByName(yangInstanceIdentifier.getLastPathArgument().getNodeType()); + if (schemaNode instanceof ListSchemaNode) { + // In case of a list, wrap all the values in a Mixin parent node + final ListSchemaNode listSchema = (ListSchemaNode) schemaNode; + return wrapListIntoMixinNode(normalizedRootElements, listSchema); + } else { + Preconditions.checkState(dataObjects.size() == 1, "Singleton list was expected"); + return getOnlyElement(normalizedRootElements); + } + } + + private static DataContainerChild wrapListIntoMixinNode( + final Collection> normalizedRootElements, final ListSchemaNode listSchema) { + if (listSchema.getKeyDefinition().isEmpty()) { + final CollectionNodeBuilder listBuilder = + Builders.unkeyedListBuilder(); + for (NormalizedNode normalizedRootElement : normalizedRootElements) { + listBuilder.withChild((UnkeyedListEntryNode) normalizedRootElement); + } + return listBuilder.build(); + } else { + final CollectionNodeBuilder listBuilder = + listSchema.isUserOrdered() + ? Builders.orderedMapBuilder() + : Builders.mapBuilder(); + + for (NormalizedNode normalizedRootElement : normalizedRootElements) { + listBuilder.withChild((MapEntryNode) normalizedRootElement); + } + return listBuilder.build(); + } + } + + @SuppressWarnings("unchecked") + private Function> toNormalizedNodeFunction(final InstanceIdentifier path) { + return dataObject -> { + LOG.trace("OperationalDataTree.toNormalizedNode(), path={}, dataObject={}", path, dataObject); + final Map.Entry> entry = + serializer.toNormalizedNode(path, dataObject); + + LOG.trace("OperationalDataTree.toNormalizedNode(), normalizedNodeEntry={}", entry); + return entry.getValue(); + }; + } + + private static final class ReadContextImpl implements ReadContext { + + private final ModificationCache ctx = new ModificationCache(); + private final MappingContext mappingContext; + + private ReadContextImpl(final MappingContext mappingContext) { + this.mappingContext = mappingContext; + } + + @Nonnull + @Override + public ModificationCache getModificationCache() { + return ctx; + } + + @Nonnull + @Override + public MappingContext getMappingContext() { + return mappingContext; + } + + @Override + public void close() { + // Make sure to clear the storage in case some customizer stored a reference to it to prevent memory leaks + ctx.close(); + } + } +} diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/WriteTransaction.java b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/WriteTransaction.java new file mode 100644 index 000000000..6bc6b1b4b --- /dev/null +++ b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/WriteTransaction.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; +import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.CANCELED; +import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.COMMITED; +import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.FAILED; +import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.NEW; +import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.SUBMITED; + +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import io.fd.honeycomb.data.DataModification; +import io.fd.honeycomb.translate.TranslationException; +import java.util.function.Consumer; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.NotThreadSafe; +import org.opendaylight.controller.md.sal.common.api.TransactionStatus; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@NotThreadSafe +final class WriteTransaction implements DOMDataWriteTransaction { + + private static final Logger LOG = LoggerFactory.getLogger(WriteTransaction.class); + + @Nullable + private DataModification operationalModification; + @Nullable + private DataModification configModification; + private TransactionStatus status = NEW; + + private WriteTransaction(@Nullable final DataModification configModification, + @Nullable final DataModification operationalModification) { + this.operationalModification = operationalModification; + this.configModification = configModification; + } + + private void checkIsNew() { + Preconditions.checkState(status == NEW, "Transaction was submitted or canceled"); + } + + @Override + public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, + final NormalizedNode data) { + LOG.debug("WriteTransaction.put() store={}, path={}, data={}", store, path, data); + checkIsNew(); + handleOperation(store, (modification) -> modification.write(path, data)); + } + + private void handleOperation(final LogicalDatastoreType store, + final Consumer r) { + switch (store) { + case CONFIGURATION: + checkArgument(configModification != null, "Modification of %s is not supported", store); + r.accept(configModification); + break; + case OPERATIONAL: + checkArgument(operationalModification != null, "Modification of %s is not supported", store); + r.accept(operationalModification); + break; + } + } + + @Override + public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, + final NormalizedNode data) { + LOG.debug("WriteTransaction.merge() store={}, path={}, data={}", store, path, data); + checkIsNew(); + handleOperation(store, (modification) -> modification.merge(path, data)); + } + + @Override + public boolean cancel() { + if (status != NEW) { + // only NEW transactions can be cancelled + return false; + } else { + status = CANCELED; + return true; + } + } + + @Override + public void delete(LogicalDatastoreType store, final YangInstanceIdentifier path) { + LOG.debug("WriteTransaction.delete() store={}, path={}", store, path); + checkIsNew(); + handleOperation(store, (modification) -> modification.delete(path)); + } + + @Override + public CheckedFuture submit() { + LOG.trace("WriteTransaction.submit()"); + checkIsNew(); + + try { + status = SUBMITED; + + // Validate first to catch any issues before attempting commit + if (configModification != null) { + configModification.validate(); + } + if (operationalModification != null) { + operationalModification.validate(); + } + + if(configModification != null) { + configModification.commit(); + } + if(operationalModification != null) { + operationalModification.commit(); + } + + status = COMMITED; + } catch (DataValidationFailedException | TranslationException e) { + status = FAILED; + LOG.error("Failed modify data tree", e); + return Futures.immediateFailedCheckedFuture( + new TransactionCommitFailedException("Failed to validate DataTreeModification", e)); + } + return Futures.immediateCheckedFuture(null); + } + + @Override + @Deprecated + public ListenableFuture> commit() { + throw new UnsupportedOperationException("deprecated"); + } + + @Override + public Object getIdentifier() { + return this; + } + + + @Nonnull + static WriteTransaction createOperationalOnly(@Nonnull final DataModification operationalData) { + return new WriteTransaction(null, requireNonNull(operationalData)); + } + + @Nonnull + static WriteTransaction createConfigOnly(@Nonnull final DataModification configData) { + return new WriteTransaction(requireNonNull(configData), null); + } + + @Nonnull + static WriteTransaction create(@Nonnull final DataModification configData, + @Nonnull final DataModification operationalData) { + return new WriteTransaction(requireNonNull(configData), requireNonNull(operationalData)); + } +} diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataBroker.java b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataBroker.java deleted file mode 100644 index c418ed332..000000000 --- a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataBroker.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import static com.google.common.base.Preconditions.checkNotNull; - -import io.fd.honeycomb.v3po.data.DataModification; -import io.fd.honeycomb.v3po.data.ModifiableDataManager; -import io.fd.honeycomb.v3po.data.ReadableDataManager; -import java.io.Closeable; -import java.io.IOException; -import java.util.Collections; -import java.util.Map; -import javax.annotation.Nonnull; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener; -import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; -import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension; -import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener; -import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; -import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction; -import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; -import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain; -import org.opendaylight.yangtools.concepts.ListenerRegistration; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Data Broker which provides data transaction functionality for YANG capable data provider using {@link NormalizedNode} - * data format. - */ -public class DataBroker implements DOMDataBroker, Closeable { - - private static final Logger LOG = LoggerFactory.getLogger(DataBroker.class); - private final TransactionFactory transactionFactory; - - /** - * Creates DataBroker instance. - * - * @param transactionFactory transaction producing factory - */ - public DataBroker(final TransactionFactory transactionFactory) { - this.transactionFactory = transactionFactory; - } - - @Override - public DOMDataReadOnlyTransaction newReadOnlyTransaction() { - LOG.trace("DataBroker({}).newReadOnlyTransaction()", this); - return transactionFactory.newReadOnlyTransaction(); - } - - @Override - public DOMDataReadWriteTransaction newReadWriteTransaction() { - LOG.trace("DataBroker({}).newReadWriteTransaction()", this); - return transactionFactory.newReadWriteTransaction(); - } - - @Override - public DOMDataWriteTransaction newWriteOnlyTransaction() { - LOG.trace("DataBroker({}).newWriteOnlyTransaction()", this); - return transactionFactory.newWriteOnlyTransaction(); - } - - @Override - public ListenerRegistration registerDataChangeListener(final LogicalDatastoreType store, - final YangInstanceIdentifier path, - final DOMDataChangeListener listener, - final DataChangeScope triggeringScope) { - throw new UnsupportedOperationException("Not supported"); - } - - @Override - public DOMTransactionChain createTransactionChain(final TransactionChainListener listener) { - throw new UnsupportedOperationException("Not supported"); - } - - @Nonnull - @Override - public Map, DOMDataBrokerExtension> getSupportedExtensions() { - return Collections.emptyMap(); - } - - /** - * Create DataBroker for a modifiable config DT, but only readable Operational - */ - @Nonnull - public static DataBroker create(@Nonnull final ModifiableDataManager configDataTree, - @Nonnull final ReadableDataManager operationalDataTree) { - checkNotNull(operationalDataTree, "operationalDataTree should not be null"); - checkNotNull(configDataTree, "configDataTree should not be null"); - return new DataBroker(new MainPipelineTxFactory(configDataTree, operationalDataTree)); - } - - /** - * Create DataBroker for modifiable operational DT, but no support for config - */ - @Nonnull - public static DataBroker create(@Nonnull final ModifiableDataManager operationalDataTree) { - checkNotNull(operationalDataTree, "operationalDataTree should not be null"); - return new DataBroker(new ContextPipelineTxFactory(operationalDataTree)); - } - - @Override - public void close() throws IOException { - // NOOP - } - - /** - * Transaction provider factory to be used by {@link DataBroker} - */ - public interface TransactionFactory { - - DOMDataReadOnlyTransaction newReadOnlyTransaction(); - - DOMDataReadWriteTransaction newReadWriteTransaction(); - - DOMDataWriteTransaction newWriteOnlyTransaction(); - } - - /** - * Transaction factory specific for Honeycomb's main pipeline (config: read+write, operational: read-only) - */ - private static class MainPipelineTxFactory implements TransactionFactory { - private final ReadableDataManager operationalDataTree; - private final ModifiableDataManager configDataTree; - - MainPipelineTxFactory(@Nonnull final ModifiableDataManager configDataTree, - @Nonnull final ReadableDataManager operationalDataTree) { - this.operationalDataTree = operationalDataTree; - this.configDataTree = configDataTree; - } - - @Override - public DOMDataReadOnlyTransaction newReadOnlyTransaction() { - return ReadOnlyTransaction.create(configDataTree.newModification(), operationalDataTree); - } - - @Override - public DOMDataReadWriteTransaction newReadWriteTransaction() { - final DataModification configModification = configDataTree.newModification(); - return new ReadWriteTransaction( - ReadOnlyTransaction.create(configModification, operationalDataTree), - WriteTransaction.createConfigOnly(configModification)); - } - - @Override - public DOMDataWriteTransaction newWriteOnlyTransaction() { - return WriteTransaction.createConfigOnly(configDataTree.newModification()); - } - } - - /** - * Transaction factory specific for Honeycomb's context pipeline (config: none, operational: read+write) - */ - private static class ContextPipelineTxFactory implements TransactionFactory { - private final ModifiableDataManager operationalDataTree; - - ContextPipelineTxFactory(@Nonnull final ModifiableDataManager operationalDataTree) { - this.operationalDataTree = operationalDataTree; - } - - @Override - public DOMDataReadOnlyTransaction newReadOnlyTransaction() { - return ReadOnlyTransaction.createOperationalOnly(operationalDataTree); - } - - @Override - public DOMDataReadWriteTransaction newReadWriteTransaction() { - final DataModification dataModification = operationalDataTree.newModification(); - return new ReadWriteTransaction( - ReadOnlyTransaction.createOperationalOnly(dataModification), - WriteTransaction.createOperationalOnly(dataModification)); - } - - @Override - public DOMDataWriteTransaction newWriteOnlyTransaction() { - return WriteTransaction.createOperationalOnly(operationalDataTree.newModification()); - } - } -} - - diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java deleted file mode 100644 index 2c2581ec0..000000000 --- a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.util.concurrent.Futures.immediateCheckedFuture; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Optional; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; -import com.google.common.util.concurrent.CheckedFuture; -import io.fd.honeycomb.v3po.data.DataModification; -import io.fd.honeycomb.v3po.data.ReadableDataManager; -import io.fd.honeycomb.v3po.translate.TranslationException; -import io.fd.honeycomb.v3po.translate.util.RWUtils; -import io.fd.honeycomb.v3po.translate.util.TransactionMappingContext; -import io.fd.honeycomb.v3po.translate.util.write.TransactionWriteContext; -import io.fd.honeycomb.v3po.translate.write.DataObjectUpdate; -import io.fd.honeycomb.v3po.translate.write.WriteContext; -import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistry; -import java.util.Map; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; -import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; -import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Extension of {@link ModifiableDataTreeManager} that propagates data changes to underlying writer layer before they - * are fully committed in the backing data tree. Data changes are propagated in BA format. - */ -public final class ModifiableDataTreeDelegator extends ModifiableDataTreeManager { - - private static final Logger LOG = LoggerFactory.getLogger(ModifiableDataTreeDelegator.class); - private static final ReadableDataManager EMPTY_OPERATIONAL = p -> immediateCheckedFuture(Optional.absent()); - - private final WriterRegistry writerRegistry; - private final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker; - // TODO what to use instead of deprecated BindingNormalizedNodeSerializer ? - private final BindingNormalizedNodeSerializer serializer; - - /** - * Creates configuration data tree instance. - * @param serializer service for serialization between Java Binding Data representation and NormalizedNode - * representation. - * @param dataTree data tree for configuration data representation - * @param writerRegistry service for translation between Java Binding Data and data provider, capable of performing - * @param contextBroker BA broker providing full access to mapping context data - */ - public ModifiableDataTreeDelegator(@Nonnull final BindingNormalizedNodeSerializer serializer, - @Nonnull final DataTree dataTree, - @Nonnull final WriterRegistry writerRegistry, - @Nonnull final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker) { - super(dataTree); - this.contextBroker = checkNotNull(contextBroker, "contextBroker should not be null"); - this.serializer = checkNotNull(serializer, "serializer should not be null"); - this.writerRegistry = checkNotNull(writerRegistry, "writerRegistry should not be null"); - } - - @Override - public DataModification newModification() { - return new ConfigSnapshot(super.newModification()); - } - - private final class ConfigSnapshot extends ModifiableDataTreeManager.ConfigSnapshot { - - private final DataModification untouchedModification; - - /** - * @param untouchedModification DataModification captured while this modification/snapshot was created. - * To be used later while invoking writers to provide them with before state - * (state without current modifications). - * It must be captured as close as possible to when current modification started. - */ - ConfigSnapshot(final DataModification untouchedModification) { - this.untouchedModification = untouchedModification; - } - - /** - * Pass the changes to underlying writer layer. - * Transform from BI to BA. - * Revert(Write data before to subtrees that have been successfully modified before failure) in case of failure. - */ - @Override - protected void processCandidate(final DataTreeCandidate candidate) - throws TranslationException { - - final DataTreeCandidateNode rootNode = candidate.getRootNode(); - final YangInstanceIdentifier rootPath = candidate.getRootPath(); - LOG.trace("ConfigDataTree.modify() rootPath={}, rootNode={}, dataBefore={}, dataAfter={}", - rootPath, rootNode, rootNode.getDataBefore(), rootNode.getDataAfter()); - - final ModificationDiff modificationDiff = - ModificationDiff.recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, rootNode); - LOG.debug("ConfigDataTree.modify() diff: {}", modificationDiff); - - // Distinguish between updates (create + update) and deletes - final WriterRegistry.DataObjectUpdates baUpdates = toBindingAware(modificationDiff.getUpdates()); - LOG.debug("ConfigDataTree.modify() extracted updates={}", baUpdates); - - try (final WriteContext ctx = getTransactionWriteContext()) { - writerRegistry.update(baUpdates, ctx); - - final CheckedFuture contextUpdateResult = - ((TransactionMappingContext) ctx.getMappingContext()).submit(); - // Blocking on context data update - contextUpdateResult.checkedGet(); - - } catch (WriterRegistry.BulkUpdateException e) { - LOG.warn("Failed to apply all changes", e); - LOG.info("Trying to revert successful changes for current transaction"); - - try { - e.revertChanges(); - LOG.info("Changes successfully reverted"); - } catch (WriterRegistry.Reverter.RevertFailedException revertFailedException) { - // fail with failed revert - LOG.error("Failed to revert successful changes", revertFailedException); - throw revertFailedException; - } - - throw e; // fail with success revert - } catch (TransactionCommitFailedException e) { - // FIXME revert should probably occur when context is not written successfully - final String msg = "Error while updating mapping context data"; - LOG.error(msg, e); - throw new TranslationException(msg, e); - } catch (TranslationException e) { - LOG.error("Error while processing data change (updates={})", baUpdates, e); - throw e; - } - } - - private TransactionWriteContext getTransactionWriteContext() { - // Before Tx must use modification - final DOMDataReadOnlyTransaction beforeTx = ReadOnlyTransaction.create(untouchedModification, EMPTY_OPERATIONAL); - // After Tx must use current modification - final DOMDataReadOnlyTransaction afterTx = ReadOnlyTransaction.create(this, EMPTY_OPERATIONAL); - final TransactionMappingContext mappingContext = new TransactionMappingContext( - contextBroker.newReadWriteTransaction()); - return new TransactionWriteContext(serializer, beforeTx, afterTx, mappingContext); - } - - private WriterRegistry.DataObjectUpdates toBindingAware( - final Map biNodes) { - return ModifiableDataTreeDelegator.toBindingAware(biNodes, serializer); - } - } - - @VisibleForTesting - static WriterRegistry.DataObjectUpdates toBindingAware( - final Map biNodes, - final BindingNormalizedNodeSerializer serializer) { - - final Multimap, DataObjectUpdate> dataObjectUpdates = HashMultimap.create(); - final Multimap, DataObjectUpdate.DataObjectDelete> dataObjectDeletes = - HashMultimap.create(); - - for (Map.Entry biEntry : biNodes.entrySet()) { - final InstanceIdentifier unkeyedIid = - RWUtils.makeIidWildcarded(serializer.fromYangInstanceIdentifier(biEntry.getKey())); - - ModificationDiff.NormalizedNodeUpdate normalizedNodeUpdate = biEntry.getValue(); - final DataObjectUpdate dataObjectUpdate = toDataObjectUpdate(normalizedNodeUpdate, serializer); - if (dataObjectUpdate != null) { - if (dataObjectUpdate instanceof DataObjectUpdate.DataObjectDelete) { - dataObjectDeletes.put(unkeyedIid, ((DataObjectUpdate.DataObjectDelete) dataObjectUpdate)); - } else { - dataObjectUpdates.put(unkeyedIid, dataObjectUpdate); - } - } - } - return new WriterRegistry.DataObjectUpdates(dataObjectUpdates, dataObjectDeletes); - } - - @Nullable - private static DataObjectUpdate toDataObjectUpdate( - final ModificationDiff.NormalizedNodeUpdate normalizedNodeUpdate, - final BindingNormalizedNodeSerializer serializer) { - - InstanceIdentifier baId = serializer.fromYangInstanceIdentifier(normalizedNodeUpdate.getId()); - checkNotNull(baId, "Unable to transform instance identifier: %s into BA", normalizedNodeUpdate.getId()); - - DataObject dataObjectBefore = getDataObject(serializer, - normalizedNodeUpdate.getDataBefore(), normalizedNodeUpdate.getId()); - DataObject dataObjectAfter = - getDataObject(serializer, normalizedNodeUpdate.getDataAfter(), normalizedNodeUpdate.getId()); - - return dataObjectBefore == null && dataObjectAfter == null - ? null - : DataObjectUpdate.create(baId, dataObjectBefore, dataObjectAfter); - } - - @Nullable - private static DataObject getDataObject(@Nonnull final BindingNormalizedNodeSerializer serializer, - @Nullable final NormalizedNode data, - @Nonnull final YangInstanceIdentifier id) { - DataObject dataObject = null; - if (data != null) { - final Map.Entry, DataObject> dataObjectEntry = - serializer.fromNormalizedNode(id, data); - if (dataObjectEntry != null) { - dataObject = dataObjectEntry.getValue(); - } - } - return dataObject; - } - -} - - - diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeManager.java b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeManager.java deleted file mode 100644 index 1082c479b..000000000 --- a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeManager.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.util.concurrent.Futures.immediateCheckedFuture; - -import com.google.common.base.Optional; -import com.google.common.util.concurrent.CheckedFuture; -import io.fd.honeycomb.v3po.data.DataModification; -import io.fd.honeycomb.v3po.data.ModifiableDataManager; -import io.fd.honeycomb.v3po.translate.TranslationException; -import javax.annotation.Nonnull; -import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * DataTree backed implementation for modifiable data manager. - */ -public class ModifiableDataTreeManager implements ModifiableDataManager { - - private static final Logger LOG = LoggerFactory.getLogger(ModifiableDataTreeManager.class); - - private final DataTree dataTree; - - public ModifiableDataTreeManager(@Nonnull final DataTree dataTree) { - this.dataTree = checkNotNull(dataTree, "dataTree should not be null"); - } - - @Override - public DataModification newModification() { - return new ConfigSnapshot(); - } - - @Override - public final CheckedFuture>, ReadFailedException> read(@Nonnull final YangInstanceIdentifier path) { - return newModification().read(path); - } - - protected class ConfigSnapshot implements DataModification { - private final DataTreeModification modification; - private boolean validated = false; - - ConfigSnapshot() { - this(dataTree.takeSnapshot().newModification()); - } - - protected ConfigSnapshot(final DataTreeModification modification) { - this.modification = modification; - } - - @Override - public CheckedFuture>, ReadFailedException> read( - @Nonnull final YangInstanceIdentifier path) { - final Optional> node = modification.readNode(path); - if (LOG.isTraceEnabled() && node.isPresent()) { - LOG.trace("ConfigSnapshot.read: {}", node.get()); - } - return immediateCheckedFuture(node); - } - - @Override - public final void delete(final YangInstanceIdentifier path) { - modification.delete(path); - } - - @Override - public final void merge(final YangInstanceIdentifier path, final NormalizedNode data) { - modification.merge(path, data); - } - - @Override - public final void write(final YangInstanceIdentifier path, final NormalizedNode data) { - modification.write(path, data); - } - - @Override - public final void commit() throws DataValidationFailedException, TranslationException { - if(!validated) { - validate(); - } - final DataTreeCandidate candidate = dataTree.prepare(modification); - processCandidate(candidate); - dataTree.commit(candidate); - } - - protected void processCandidate(final DataTreeCandidate candidate) throws TranslationException { - // NOOP - } - - @Override - public final void validate() throws DataValidationFailedException { - modification.ready(); - dataTree.validate(modification); - validated = true; - } - } -} - - - diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModificationDiff.java b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModificationDiff.java deleted file mode 100644 index abc0062de..000000000 --- a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModificationDiff.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.collect.ImmutableMap; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; -import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode; -import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; -import org.opendaylight.yangtools.yang.data.api.schema.MixinNode; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType; - -/** - * Recursively collects and provides all unique and non-null modifications (modified normalized nodes). - */ -final class ModificationDiff { - - private static final ModificationDiff EMPTY_DIFF = new ModificationDiff(Collections.emptyMap()); - private static final EnumSet LEAF_MODIFICATIONS = EnumSet.of(ModificationType.WRITE, ModificationType.DELETE); - - private final Map updates; - - private ModificationDiff(@Nonnull Map updates) { - this.updates = updates; - } - - /** - * Get processed modifications. - * - * @return mapped modifications, where key is keyed {@link YangInstanceIdentifier}. - */ - Map getUpdates() { - return updates; - } - - private ModificationDiff merge(final ModificationDiff other) { - if (this == EMPTY_DIFF) { - return other; - } - - if (other == EMPTY_DIFF) { - return this; - } - - return new ModificationDiff(join(updates, other.updates)); - } - - private static Map join(Map first, - Map second) { - final Map merged = new HashMap<>(); - merged.putAll(first); - merged.putAll(second); - return merged; - } - - private static ModificationDiff create(YangInstanceIdentifier id, DataTreeCandidateNode candidate) { - return new ModificationDiff(ImmutableMap.of(id, NormalizedNodeUpdate.create(id, candidate))); - } - - /** - * Produce an aggregated diff from a candidate node recursively. MixinNodes are ignored as modifications and so - * are complex nodes which direct leaves were not modified. - */ - @Nonnull - static ModificationDiff recursivelyFromCandidate(@Nonnull final YangInstanceIdentifier yangIid, - @Nonnull final DataTreeCandidateNode currentCandidate) { - // recursively process child nodes for exact modifications - return recursivelyChildrenFromCandidate(yangIid, currentCandidate) - // also add modification on current level, if elligible - .merge(isModification(currentCandidate) - ? ModificationDiff.create(yangIid, currentCandidate) - : EMPTY_DIFF); - } - - /** - * Check whether current node was modified. {@link MixinNode}s are ignored - * and only nodes which direct leaves(or choices) are modified are considered a modification. - */ - private static Boolean isModification(@Nonnull final DataTreeCandidateNode currentCandidate) { - // Mixin nodes are not considered modifications - if (isMixin(currentCandidate) && !isAugment(currentCandidate)) { - return false; - } else { - return isCurrentModified(currentCandidate); - } - } - - private static Boolean isCurrentModified(final @Nonnull DataTreeCandidateNode currentCandidate) { - // Check if there are any modified leaves and if so, consider current node as modified - final Boolean directLeavesModified = currentCandidate.getChildNodes().stream() - .filter(ModificationDiff::isLeaf) - // For some reason, we get modifications on unmodified list keys TODO debug and report ODL bug - // and that messes up our modifications collection here, so we need to skip - .filter(ModificationDiff::isBeforeAndAfterDifferent) - .filter(child -> LEAF_MODIFICATIONS.contains(child.getModificationType())) - .findFirst() - .isPresent(); - - return directLeavesModified - // Also check choices (choices do not exist in BA world and if anything within a choice was modified, - // consider its parent as being modified) - || currentCandidate.getChildNodes().stream() - .filter(ModificationDiff::isChoice) - // Recursively check each choice if there was any change to it - .filter(ModificationDiff::isCurrentModified) - .findFirst() - .isPresent(); - } - - /** - * Process all non-leaf child nodes recursively, creating aggregated {@link ModificationDiff}. - */ - private static ModificationDiff recursivelyChildrenFromCandidate(final @Nonnull YangInstanceIdentifier yangIid, - final @Nonnull DataTreeCandidateNode currentCandidate) { - // recursively process child nodes for specific modifications - return currentCandidate.getChildNodes().stream() - // not interested in modifications to leaves - .filter(child -> !isLeaf(child)) - .map(candidate -> recursivelyFromCandidate(yangIid.node(candidate.getIdentifier()), candidate)) - .reduce(ModificationDiff::merge) - .orElse(EMPTY_DIFF); - } - - /** - * Check whether candidate.before and candidate.after is different. If not return false. - */ - private static boolean isBeforeAndAfterDifferent(@Nonnull final DataTreeCandidateNode candidateNode) { - if (candidateNode.getDataBefore().isPresent()) { - return !candidateNode.getDataBefore().get().equals(candidateNode.getDataAfter().orNull()); - } - - // considering not a modification if data after is also null - return candidateNode.getDataAfter().isPresent(); - } - - /** - * Check whether candidate node is for a leaf type node. - */ - private static boolean isLeaf(final DataTreeCandidateNode candidateNode) { - // orNull intentional, some candidate nodes have both data after and data before null - return candidateNode.getDataAfter().orNull() instanceof LeafNode - || candidateNode.getDataBefore().orNull() instanceof LeafNode; - } - - /** - * Check whether candidate node is for a Mixin type node. - */ - private static boolean isMixin(final DataTreeCandidateNode candidateNode) { - // orNull intentional, some candidate nodes have both data after and data before null - return candidateNode.getDataAfter().orNull() instanceof MixinNode - || candidateNode.getDataBefore().orNull() instanceof MixinNode; - } - - /** - * Check whether candidate node is for an Augmentation type node. - */ - private static boolean isAugment(final DataTreeCandidateNode candidateNode) { - // orNull intentional, some candidate nodes have both data after and data before null - return candidateNode.getDataAfter().orNull() instanceof AugmentationNode - || candidateNode.getDataBefore().orNull() instanceof AugmentationNode; - } - - /** - * Check whether candidate node is for a Choice type node. - */ - private static boolean isChoice(final DataTreeCandidateNode candidateNode) { - // orNull intentional, some candidate nodes have both data after and data before null - return candidateNode.getDataAfter().orNull() instanceof ChoiceNode - || candidateNode.getDataBefore().orNull() instanceof ChoiceNode; - } - - @Override - public String toString() { - return "ModificationDiff{updates=" + updates + '}'; - } - - /** - * Update to a normalized node identifiable by its {@link YangInstanceIdentifier}. - */ - static final class NormalizedNodeUpdate { - - @Nonnull - private final YangInstanceIdentifier id; - @Nullable - private final NormalizedNode dataBefore; - @Nullable - private final NormalizedNode dataAfter; - - private NormalizedNodeUpdate(@Nonnull final YangInstanceIdentifier id, - @Nullable final NormalizedNode dataBefore, - @Nullable final NormalizedNode dataAfter) { - this.id = checkNotNull(id); - this.dataAfter = dataAfter; - this.dataBefore = dataBefore; - } - - @Nullable - public NormalizedNode getDataBefore() { - return dataBefore; - } - - @Nullable - public NormalizedNode getDataAfter() { - return dataAfter; - } - - @Nonnull - public YangInstanceIdentifier getId() { - return id; - } - - static NormalizedNodeUpdate create(@Nonnull final YangInstanceIdentifier id, - @Nonnull final DataTreeCandidateNode candidate) { - return create(id, candidate.getDataBefore().orNull(), candidate.getDataAfter().orNull()); - } - - static NormalizedNodeUpdate create(@Nonnull final YangInstanceIdentifier id, - @Nullable final NormalizedNode dataBefore, - @Nullable final NormalizedNode dataAfter) { - checkArgument(!(dataBefore == null && dataAfter == null), "Both before and after data are null"); - return new NormalizedNodeUpdate(id, dataBefore, dataAfter); - } - - @Override - public boolean equals(final Object other) { - if (this == other) { - return true; - } - if (other == null || getClass() != other.getClass()) { - return false; - } - - final NormalizedNodeUpdate that = (NormalizedNodeUpdate) other; - - return id.equals(that.id); - - } - - @Override - public int hashCode() { - return id.hashCode(); - } - - @Override - public String toString() { - return "NormalizedNodeUpdate{" + "id=" + id - + ", dataBefore=" + dataBefore - + ", dataAfter=" + dataAfter - + '}'; - } - } - -} diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java deleted file mode 100644 index 9b71dfd62..000000000 --- a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.base.Optional; -import io.fd.honeycomb.v3po.translate.util.JsonUtils; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import javax.annotation.Nonnull; -import org.opendaylight.controller.sal.core.api.model.SchemaService; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Adapter for a DataTree that stores current state of data in backing DataTree on each successful commit. - * Uses JSON format. - */ -public class PersistingDataTreeAdapter implements DataTree, AutoCloseable { - - private static final Logger LOG = LoggerFactory.getLogger(PersistingDataTreeAdapter.class); - - private final DataTree delegateDependency; - private final SchemaService schemaServiceDependency; - private final Path path; - - /** - * Create new Persisting DataTree adapter - * - * @param delegate backing data tree that actually handles all the operations - * @param persistPath path to a file (existing or not) to be used as storage for persistence. Full control over - * a file at peristPath is expected - * @param schemaService schemaContext provier - */ - public PersistingDataTreeAdapter(@Nonnull final DataTree delegate, - @Nonnull final SchemaService schemaService, - @Nonnull final Path persistPath) { - this.path = testPersistPath(checkNotNull(persistPath, "persistPath is null")); - this.delegateDependency = checkNotNull(delegate, "delegate is null"); - this.schemaServiceDependency = checkNotNull(schemaService, "schemaService is null"); - } - - /** - * Test whether file at persistPath exists and is readable or create it along with its parent structure - */ - private Path testPersistPath(final Path persistPath) { - try { - checkArgument(!Files.isDirectory(persistPath), "Path %s points to a directory", persistPath); - if(Files.exists(persistPath)) { - checkArgument(Files.isReadable(persistPath), - "Provided path %s points to existing, but non-readable file", persistPath); - return persistPath; - } - Files.createDirectories(persistPath.getParent()); - Files.write(persistPath, new byte[]{}, StandardOpenOption.CREATE); - } catch (IOException | UnsupportedOperationException e) { - LOG.warn("Provided path for persistence: {} is not usable", persistPath, e); - throw new IllegalArgumentException("Path " + persistPath + " cannot be used as "); - } - - return persistPath; - } - - @Override - public DataTreeSnapshot takeSnapshot() { - return delegateDependency.takeSnapshot(); - } - - @Override - public void setSchemaContext(final SchemaContext schemaContext) { - delegateDependency.setSchemaContext(schemaContext); - } - - @Override - public void commit(final DataTreeCandidate dataTreeCandidate) { - LOG.trace("Commit detected"); - delegateDependency.commit(dataTreeCandidate); - LOG.debug("Delegate commit successful. Persisting data"); - - // FIXME doing full read and full write might not be the fastest way of persisting data here - final DataTreeSnapshot dataTreeSnapshot = delegateDependency.takeSnapshot(); - - // TODO this can be handled in background by a dedicated thread + a limited blocking queue - // TODO enable configurable granularity for persists. Maybe doing write on every modification is too much - // and we could do bulk persist - persistCurrentData(dataTreeSnapshot.readNode(YangInstanceIdentifier.EMPTY)); - } - - private void persistCurrentData(final Optional> currentRoot) { - if(currentRoot.isPresent()) { - try { - LOG.trace("Persisting current data: {} into: {}", currentRoot.get(), path); - JsonUtils.writeJsonRoot(currentRoot.get(), schemaServiceDependency.getGlobalContext(), - Files.newOutputStream(path, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)); - LOG.trace("Data persisted successfully in {}", path); - } catch (IOException e) { - throw new IllegalStateException("Unable to persist current data", e); - } - } else { - LOG.debug("Skipping persistence, since there's no data to persist"); - } - } - - @Override - public YangInstanceIdentifier getRootPath() { - return delegateDependency.getRootPath(); - } - - @Override - public void validate(final DataTreeModification dataTreeModification) throws DataValidationFailedException { - delegateDependency.validate(dataTreeModification); - } - - @Override - public DataTreeCandidate prepare( - final DataTreeModification dataTreeModification) { - return delegateDependency.prepare(dataTreeModification); - } - - @Override - public void close() throws Exception { - LOG.trace("Closing {} for {}", getClass().getSimpleName(), path); - // NOOP - } -} diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java deleted file mode 100644 index 2850a0d9a..000000000 --- a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; -import static java.util.Objects.requireNonNull; - -import com.google.common.base.Function; -import com.google.common.base.Optional; -import com.google.common.util.concurrent.CheckedFuture; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import io.fd.honeycomb.v3po.data.ReadableDataManager; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; -import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -final class ReadOnlyTransaction implements DOMDataReadOnlyTransaction { - - private static final Logger LOG = LoggerFactory.getLogger(ReadOnlyTransaction.class); - - @Nullable - private ReadableDataManager operationalData; - @Nullable - private ReadableDataManager configSnapshot; - - private boolean closed = false; - - /** - * @param configData config data tree manager. Null if config reads are not to be supported - * @param operationalData operational data tree manager. Null if operational reads are not to be supported - */ - private ReadOnlyTransaction(@Nullable final ReadableDataManager configData, - @Nullable final ReadableDataManager operationalData) { - this.configSnapshot = configData; - this.operationalData = operationalData; - } - - @Override - public synchronized void close() { - closed = true; - configSnapshot = null; - operationalData = null; - } - - @Override - public synchronized CheckedFuture>, ReadFailedException> read( - final LogicalDatastoreType store, - final YangInstanceIdentifier path) { - LOG.debug("ReadOnlyTransaction.read(), store={}, path={}", store, path); - checkState(!closed, "Transaction has been closed"); - - if (store == LogicalDatastoreType.OPERATIONAL) { - checkArgument(operationalData != null, "{} reads not supported", store); - return operationalData.read(path); - } else { - checkArgument(configSnapshot != null, "{} reads not supported", store); - return configSnapshot.read(path); - } - } - - @Override - public CheckedFuture exists(final LogicalDatastoreType store, - final YangInstanceIdentifier path) { - LOG.debug("ReadOnlyTransaction.exists() store={}, path={}", store, path); - - ListenableFuture listenableFuture = Futures.transform(read(store, path), IS_NODE_PRESENT); - return Futures.makeChecked(listenableFuture, ANY_EX_TO_READ_FAILED_EXCEPTION_MAPPER); - } - - @Nonnull - @Override - public Object getIdentifier() { - return this; - } - - @Nonnull - static ReadOnlyTransaction createOperationalOnly(@Nonnull final ReadableDataManager operationalData) { - return new ReadOnlyTransaction(null, requireNonNull(operationalData)); - } - - @Nonnull - static ReadOnlyTransaction createConfigOnly(@Nonnull final ReadableDataManager configData) { - return new ReadOnlyTransaction(requireNonNull(configData), null); - } - - @Nonnull - static ReadOnlyTransaction create(@Nonnull final ReadableDataManager configData, - @Nonnull final ReadableDataManager operationalData) { - return new ReadOnlyTransaction(requireNonNull(configData), requireNonNull(operationalData)); - } - - private static final Function>, ? extends Boolean> IS_NODE_PRESENT = - (Function>, Boolean>) input -> input == null ? Boolean.FALSE : input.isPresent(); - - private static final Function ANY_EX_TO_READ_FAILED_EXCEPTION_MAPPER = - (Function) e -> new ReadFailedException("Exists failed", e); -} \ No newline at end of file diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadWriteTransaction.java b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadWriteTransaction.java deleted file mode 100644 index 88b46437e..000000000 --- a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadWriteTransaction.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.CheckedFuture; -import com.google.common.util.concurrent.ListenableFuture; -import javax.annotation.Nonnull; -import org.opendaylight.controller.md.sal.common.api.TransactionStatus; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; -import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; -import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; -import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction; -import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction; -import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; -import org.opendaylight.yangtools.yang.common.RpcResult; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; - -/** - * Composite DOM transaction that delegates reads to a {@link DOMDataReadTransaction} delegate and writes to a {@link - * DOMDataWriteTransaction} delegate. - */ -final class ReadWriteTransaction implements DOMDataReadWriteTransaction { - - private final DOMDataReadOnlyTransaction delegateReadTx; - private final DOMDataWriteTransaction delegateWriteTx; - - ReadWriteTransaction(@Nonnull final DOMDataReadOnlyTransaction delegateReadTx, - @Nonnull final DOMDataWriteTransaction delegateWriteTx) { - this.delegateReadTx = Preconditions.checkNotNull(delegateReadTx, "delegateReadTx should not be null"); - this.delegateWriteTx = Preconditions.checkNotNull(delegateWriteTx, "delegateWriteTx should not be null"); - } - - @Override - public boolean cancel() { - delegateReadTx.close(); - return delegateWriteTx.cancel(); - } - - @Override - public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, - final NormalizedNode data) { - delegateWriteTx.put(store, path, data); - } - - @Override - public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, - final NormalizedNode data) { - delegateWriteTx.merge(store, path, data); - } - - @Override - public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) { - delegateWriteTx.delete(store, path); - } - - @Override - public CheckedFuture submit() { - return delegateWriteTx.submit(); - } - - @Override - public ListenableFuture> commit() { - return delegateWriteTx.commit(); - } - - @Override - public CheckedFuture>, ReadFailedException> read(final LogicalDatastoreType store, - final YangInstanceIdentifier path) { - return delegateReadTx.read(store, path); - } - - @Override - public CheckedFuture exists(final LogicalDatastoreType store, - final YangInstanceIdentifier path) { - return delegateReadTx.exists(store, path); - } - - @Override - public Object getIdentifier() { - return this; - } -} - diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegator.java b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegator.java deleted file mode 100644 index aff023ebc..000000000 --- a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegator.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.collect.Iterables.getOnlyElement; - -import com.google.common.base.Function; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.collect.Collections2; -import com.google.common.collect.Multimap; -import com.google.common.util.concurrent.CheckedFuture; -import com.google.common.util.concurrent.Futures; -import io.fd.honeycomb.v3po.data.ReadableDataManager; -import io.fd.honeycomb.v3po.translate.MappingContext; -import io.fd.honeycomb.v3po.translate.ModificationCache; -import io.fd.honeycomb.v3po.translate.read.ReadContext; -import io.fd.honeycomb.v3po.translate.read.ReadFailedException; -import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistry; -import io.fd.honeycomb.v3po.translate.util.TransactionMappingContext; -import java.util.Collection; -import java.util.Map; -import javax.annotation.Nonnull; -import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; -import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; -import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; -import org.opendaylight.yangtools.yang.data.api.schema.MapNode; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode; -import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode; -import org.opendaylight.yangtools.yang.data.impl.schema.Builders; -import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder; -import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * ReadableDataTree implementation for operational data. - */ -public final class ReadableDataTreeDelegator implements ReadableDataManager { - private static final Logger LOG = LoggerFactory.getLogger(ReadableDataTreeDelegator.class); - - private final BindingNormalizedNodeSerializer serializer; - private final ReaderRegistry readerRegistry; - private final SchemaContext globalContext; - private final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker; - - /** - * Creates operational data tree instance. - * @param serializer service for serialization between Java Binding Data representation and NormalizedNode - * representation. - * @param globalContext service for obtaining top level context data from all yang modules. - * @param readerRegistry service responsible for translation between DataObjects and data provider. - * @param contextBroker BA broker for context data - */ - public ReadableDataTreeDelegator(@Nonnull BindingNormalizedNodeSerializer serializer, - @Nonnull final SchemaContext globalContext, - @Nonnull final ReaderRegistry readerRegistry, - @Nonnull final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker) { - this.contextBroker = checkNotNull(contextBroker, "contextBroker should not be null"); - this.globalContext = checkNotNull(globalContext, "globalContext should not be null"); - this.serializer = checkNotNull(serializer, "serializer should not be null"); - this.readerRegistry = checkNotNull(readerRegistry, "reader should not be null"); - } - - @Override - public CheckedFuture>, - org.opendaylight.controller.md.sal.common.api.data.ReadFailedException> read( - @Nonnull final YangInstanceIdentifier yangInstanceIdentifier) { - - try(TransactionMappingContext mappingContext = new TransactionMappingContext(contextBroker.newReadWriteTransaction()); - ReadContext ctx = new ReadContextImpl(mappingContext)) { - - final Optional> value; - if (checkNotNull(yangInstanceIdentifier).equals(YangInstanceIdentifier.EMPTY)) { - value = readRoot(ctx); - } else { - value = readNode(yangInstanceIdentifier, ctx); - } - - // Submit context mapping updates - final CheckedFuture contextUpdateResult = - ((TransactionMappingContext) ctx.getMappingContext()).submit(); - // Blocking on context data update - contextUpdateResult.checkedGet(); - - return Futures.immediateCheckedFuture(value); - - } catch (ReadFailedException e) { - return Futures.immediateFailedCheckedFuture( - new org.opendaylight.controller.md.sal.common.api.data.ReadFailedException( - "Failed to read VPP data", e)); - } catch (TransactionCommitFailedException e) { - // FIXME revert should probably occur when context is not written successfully - final String msg = "Error while updating mapping context data"; - LOG.error(msg, e); - return Futures.immediateFailedCheckedFuture( - new org.opendaylight.controller.md.sal.common.api.data.ReadFailedException(msg, e) - ); - } - } - - private Optional> readNode(final YangInstanceIdentifier yangInstanceIdentifier, - final ReadContext ctx) throws ReadFailedException { - LOG.debug("OperationalDataTree.readNode(), yangInstanceIdentifier={}", yangInstanceIdentifier); - final InstanceIdentifier path = serializer.fromYangInstanceIdentifier(yangInstanceIdentifier); - checkNotNull(path, "Invalid instance identifier %s. Cannot create BA equivalent.", yangInstanceIdentifier); - LOG.debug("OperationalDataTree.readNode(), path={}", path); - - final Optional dataObject; - - dataObject = readerRegistry.read(path, ctx); - if (dataObject.isPresent()) { - final NormalizedNode value = toNormalizedNodeFunction(path).apply(dataObject.get()); - return Optional.>fromNullable(value); - } else { - return Optional.absent(); - } - } - - private Optional> readRoot(final ReadContext ctx) throws ReadFailedException { - LOG.debug("OperationalDataTree.readRoot()"); - - final DataContainerNodeAttrBuilder dataNodeBuilder = - Builders.containerBuilder() - .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(SchemaContext.NAME)); - - final Multimap, ? extends DataObject> dataObjects = - readerRegistry.readAll(ctx); - - for (final InstanceIdentifier instanceIdentifier : dataObjects.keySet()) { - final YangInstanceIdentifier rootElementId = serializer.toYangInstanceIdentifier(instanceIdentifier); - final NormalizedNode node = - wrapDataObjects(rootElementId, instanceIdentifier, dataObjects.get(instanceIdentifier)); - dataNodeBuilder.withChild((DataContainerChild) node); - } - return Optional.>of(dataNodeBuilder.build()); - } - - private NormalizedNode wrapDataObjects(final YangInstanceIdentifier yangInstanceIdentifier, - final InstanceIdentifier instanceIdentifier, - final Collection dataObjects) { - final Collection> normalizedRootElements = Collections2 - .transform(dataObjects, toNormalizedNodeFunction(instanceIdentifier)); - - final DataSchemaNode schemaNode = - globalContext.getDataChildByName(yangInstanceIdentifier.getLastPathArgument().getNodeType()); - if (schemaNode instanceof ListSchemaNode) { - // In case of a list, wrap all the values in a Mixin parent node - final ListSchemaNode listSchema = (ListSchemaNode) schemaNode; - return wrapListIntoMixinNode(normalizedRootElements, listSchema); - } else { - Preconditions.checkState(dataObjects.size() == 1, "Singleton list was expected"); - return getOnlyElement(normalizedRootElements); - } - } - - private static DataContainerChild wrapListIntoMixinNode( - final Collection> normalizedRootElements, final ListSchemaNode listSchema) { - if (listSchema.getKeyDefinition().isEmpty()) { - final CollectionNodeBuilder listBuilder = - Builders.unkeyedListBuilder(); - for (NormalizedNode normalizedRootElement : normalizedRootElements) { - listBuilder.withChild((UnkeyedListEntryNode) normalizedRootElement); - } - return listBuilder.build(); - } else { - final CollectionNodeBuilder listBuilder = - listSchema.isUserOrdered() - ? Builders.orderedMapBuilder() - : Builders.mapBuilder(); - - for (NormalizedNode normalizedRootElement : normalizedRootElements) { - listBuilder.withChild((MapEntryNode) normalizedRootElement); - } - return listBuilder.build(); - } - } - - @SuppressWarnings("unchecked") - private Function> toNormalizedNodeFunction(final InstanceIdentifier path) { - return dataObject -> { - LOG.trace("OperationalDataTree.toNormalizedNode(), path={}, dataObject={}", path, dataObject); - final Map.Entry> entry = - serializer.toNormalizedNode(path, dataObject); - - LOG.trace("OperationalDataTree.toNormalizedNode(), normalizedNodeEntry={}", entry); - return entry.getValue(); - }; - } - - private static final class ReadContextImpl implements ReadContext { - - private final ModificationCache ctx = new ModificationCache(); - private final MappingContext mappingContext; - - private ReadContextImpl(final MappingContext mappingContext) { - this.mappingContext = mappingContext; - } - - @Nonnull - @Override - public ModificationCache getModificationCache() { - return ctx; - } - - @Nonnull - @Override - public MappingContext getMappingContext() { - return mappingContext; - } - - @Override - public void close() { - // Make sure to clear the storage in case some customizer stored a reference to it to prevent memory leaks - ctx.close(); - } - } -} diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java deleted file mode 100644 index c8f9bd3db..000000000 --- a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import static com.google.common.base.Preconditions.checkArgument; -import static java.util.Objects.requireNonNull; -import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.CANCELED; -import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.COMMITED; -import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.FAILED; -import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.NEW; -import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.SUBMITED; - -import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.CheckedFuture; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import io.fd.honeycomb.v3po.data.DataModification; -import io.fd.honeycomb.v3po.translate.TranslationException; -import java.util.function.Consumer; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.concurrent.NotThreadSafe; -import org.opendaylight.controller.md.sal.common.api.TransactionStatus; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; -import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; -import org.opendaylight.yangtools.yang.common.RpcResult; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@NotThreadSafe -final class WriteTransaction implements DOMDataWriteTransaction { - - private static final Logger LOG = LoggerFactory.getLogger(WriteTransaction.class); - - @Nullable - private DataModification operationalModification; - @Nullable - private DataModification configModification; - private TransactionStatus status = NEW; - - private WriteTransaction(@Nullable final DataModification configModification, - @Nullable final DataModification operationalModification) { - this.operationalModification = operationalModification; - this.configModification = configModification; - } - - private void checkIsNew() { - Preconditions.checkState(status == NEW, "Transaction was submitted or canceled"); - } - - @Override - public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, - final NormalizedNode data) { - LOG.debug("WriteTransaction.put() store={}, path={}, data={}", store, path, data); - checkIsNew(); - handleOperation(store, (modification) -> modification.write(path, data)); - } - - private void handleOperation(final LogicalDatastoreType store, - final Consumer r) { - switch (store) { - case CONFIGURATION: - checkArgument(configModification != null, "Modification of %s is not supported", store); - r.accept(configModification); - break; - case OPERATIONAL: - checkArgument(operationalModification != null, "Modification of %s is not supported", store); - r.accept(operationalModification); - break; - } - } - - @Override - public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, - final NormalizedNode data) { - LOG.debug("WriteTransaction.merge() store={}, path={}, data={}", store, path, data); - checkIsNew(); - handleOperation(store, (modification) -> modification.merge(path, data)); - } - - @Override - public boolean cancel() { - if (status != NEW) { - // only NEW transactions can be cancelled - return false; - } else { - status = CANCELED; - return true; - } - } - - @Override - public void delete(LogicalDatastoreType store, final YangInstanceIdentifier path) { - LOG.debug("WriteTransaction.delete() store={}, path={}", store, path); - checkIsNew(); - handleOperation(store, (modification) -> modification.delete(path)); - } - - @Override - public CheckedFuture submit() { - LOG.trace("WriteTransaction.submit()"); - checkIsNew(); - - try { - status = SUBMITED; - - // Validate first to catch any issues before attempting commit - if (configModification != null) { - configModification.validate(); - } - if (operationalModification != null) { - operationalModification.validate(); - } - - if(configModification != null) { - configModification.commit(); - } - if(operationalModification != null) { - operationalModification.commit(); - } - - status = COMMITED; - } catch (DataValidationFailedException | TranslationException e) { - status = FAILED; - LOG.error("Failed modify data tree", e); - return Futures.immediateFailedCheckedFuture( - new TransactionCommitFailedException("Failed to validate DataTreeModification", e)); - } - return Futures.immediateCheckedFuture(null); - } - - @Override - @Deprecated - public ListenableFuture> commit() { - throw new UnsupportedOperationException("deprecated"); - } - - @Override - public Object getIdentifier() { - return this; - } - - - @Nonnull - static WriteTransaction createOperationalOnly(@Nonnull final DataModification operationalData) { - return new WriteTransaction(null, requireNonNull(operationalData)); - } - - @Nonnull - static WriteTransaction createConfigOnly(@Nonnull final DataModification configData) { - return new WriteTransaction(requireNonNull(configData), null); - } - - @Nonnull - static WriteTransaction create(@Nonnull final DataModification configData, - @Nonnull final DataModification operationalData) { - return new WriteTransaction(requireNonNull(configData), requireNonNull(operationalData)); - } -} diff --git a/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java index eabcdcbc8..8db0d5404 100644 --- a/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java +++ b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java @@ -2,9 +2,9 @@ package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl. import com.google.common.base.Optional; import com.google.common.util.concurrent.CheckedFuture; -import io.fd.honeycomb.v3po.data.DataModification; -import io.fd.honeycomb.v3po.data.ModifiableDataManager; -import io.fd.honeycomb.v3po.data.impl.ModifiableDataTreeDelegator; +import io.fd.honeycomb.data.DataModification; +import io.fd.honeycomb.data.ModifiableDataManager; +import io.fd.honeycomb.data.impl.ModifiableDataTreeDelegator; import javax.annotation.Nonnull; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; diff --git a/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModule.java b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModule.java index 1526fddca..dcb0053ca 100644 --- a/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModule.java +++ b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModule.java @@ -2,8 +2,8 @@ package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl. import com.google.common.base.Optional; import com.google.common.util.concurrent.CheckedFuture; -import io.fd.honeycomb.v3po.data.ReadableDataManager; -import io.fd.honeycomb.v3po.data.impl.ReadableDataTreeDelegator; +import io.fd.honeycomb.data.ReadableDataManager; +import io.fd.honeycomb.data.impl.ReadableDataTreeDelegator; import javax.annotation.Nonnull; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; diff --git a/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/PersistingDataTreeAdapterModule.java b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/PersistingDataTreeAdapterModule.java index 145fc4345..c15feace9 100644 --- a/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/PersistingDataTreeAdapterModule.java +++ b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/PersistingDataTreeAdapterModule.java @@ -29,7 +29,7 @@ public class PersistingDataTreeAdapterModule extends @Override public java.lang.AutoCloseable createInstance() { - return new io.fd.honeycomb.v3po.data.impl.PersistingDataTreeAdapter( + return new io.fd.honeycomb.data.impl.PersistingDataTreeAdapter( getDelegateDependency(), getSchemaServiceDependency(), Paths.get(getPersistFilePath())); diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/DataBrokerTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/DataBrokerTest.java new file mode 100644 index 000000000..08cb3320f --- /dev/null +++ b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/DataBrokerTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import io.fd.honeycomb.data.ReadableDataManager; +import io.fd.honeycomb.data.ModifiableDataManager; +import io.fd.honeycomb.data.DataModification; +import java.util.Map; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension; +import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; + +public class DataBrokerTest { + + @Mock + private ReadableDataManager operationalData; + @Mock + private ModifiableDataManager confiDataTree; + @Mock + private DataModification configSnapshot; + private DataBroker broker; + + @Before + public void setUp() { + initMocks(this); + when(confiDataTree.newModification()).thenReturn(configSnapshot); + broker = DataBroker.create(confiDataTree, operationalData); + } + + @Test + public void testNewReadWriteTransaction() { + final DOMDataReadWriteTransaction readWriteTx = broker.newReadWriteTransaction(); + final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class); + readWriteTx.read(LogicalDatastoreType.CONFIGURATION, path); + + // verify that read and write transactions use the same config snapshot + verify(configSnapshot).read(path); + verify(confiDataTree).newModification(); + } + + @Test + public void testNewWriteOnlyTransaction() { + final DOMDataWriteTransaction writeTx = broker.newWriteOnlyTransaction(); + + // verify that write transactions use config snapshot + verify(confiDataTree).newModification(); + } + + @Test + public void testNewReadOnlyTransaction() { + final DOMDataReadOnlyTransaction readTx = broker.newReadOnlyTransaction(); + + final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class); + readTx.read(LogicalDatastoreType.CONFIGURATION, path); + + // verify that read transactions use config snapshot + verify(configSnapshot).read(path); + } + + @Test(expected = UnsupportedOperationException.class) + public void testRegisterDataChangeListener() { + final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class); + final DOMDataChangeListener listener = mock(DOMDataChangeListener.class); + broker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, path, listener, + AsyncDataBroker.DataChangeScope.BASE); + } + + @Test(expected = UnsupportedOperationException.class) + public void testCreateTransactionChain() { + final TransactionChainListener listener = mock(TransactionChainListener.class); + broker.createTransactionChain(listener); + } + + @Test + public void testGetSupportedExtensions() { + final Map, DOMDataBrokerExtension> supportedExtensions = + broker.getSupportedExtensions(); + assertTrue(supportedExtensions.isEmpty()); + } + + +} \ No newline at end of file diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ModifiableDataTreeDelegatorTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ModifiableDataTreeDelegatorTest.java new file mode 100644 index 000000000..dc962662d --- /dev/null +++ b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ModifiableDataTreeDelegatorTest.java @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.hasItems; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import com.google.common.base.Optional; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Multimap; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import io.fd.honeycomb.data.DataModification; +import io.fd.honeycomb.translate.TranslationException; +import io.fd.honeycomb.translate.write.DataObjectUpdate; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.registry.WriterRegistry; +import java.util.AbstractMap; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; + +public class ModifiableDataTreeDelegatorTest { + + @Mock + private WriterRegistry writer; + @Mock + private BindingNormalizedNodeSerializer serializer; + private DataTree dataTree; + @Mock + private org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification modification; + @Mock + private DataBroker contextBroker; + @Mock + private org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction tx; + + private ModifiableDataTreeManager configDataTree; + + static final InstanceIdentifier DEFAULT_ID = InstanceIdentifier.create(DataObject.class); + static DataObject DEFAULT_DATA_OBJECT = mockDataObject("serialized", DataObject.class); + + @Before + public void setUp() throws Exception { + initMocks(this); + dataTree = ModificationDiffTest.getDataTree(); + when(contextBroker.newReadWriteTransaction()).thenReturn(tx); + when(tx.submit()).thenReturn(Futures.immediateCheckedFuture(null)); + + when(serializer.fromYangInstanceIdentifier(any(YangInstanceIdentifier.class))).thenReturn(((InstanceIdentifier) DEFAULT_ID)); + final Map.Entry, DataObject> parsed = new AbstractMap.SimpleEntry<>(DEFAULT_ID, DEFAULT_DATA_OBJECT); + when(serializer.fromNormalizedNode(any(YangInstanceIdentifier.class), any(NormalizedNode.class))).thenReturn(parsed); + + configDataTree = new ModifiableDataTreeDelegator(serializer, dataTree, writer, contextBroker); + } + + @Test + public void testRead() throws Exception { + final ContainerNode topContainer = ModificationDiffTest.getTopContainer("topContainer"); + ModificationDiffTest.addNodeToTree(dataTree, topContainer, ModificationDiffTest.TOP_CONTAINER_ID); + final CheckedFuture>, ReadFailedException> read = + configDataTree.read(ModificationDiffTest.TOP_CONTAINER_ID); + final CheckedFuture>, ReadFailedException> read2 = + configDataTree.newModification().read(ModificationDiffTest.TOP_CONTAINER_ID); + final Optional> normalizedNodeOptional = read.get(); + final Optional> normalizedNodeOptional2 = read2.get(); + + assertEquals(normalizedNodeOptional, normalizedNodeOptional2); + assertTrue(normalizedNodeOptional.isPresent()); + assertEquals(topContainer, normalizedNodeOptional.get()); + assertEquals(dataTree.takeSnapshot().readNode(ModificationDiffTest.TOP_CONTAINER_ID), normalizedNodeOptional); + } + + @Test + public void testCommitSuccessful() throws Exception { + final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue"); + + final DataModification dataModification = configDataTree.newModification(); + dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList); + dataModification.validate(); + dataModification.commit(); + + final Multimap, DataObjectUpdate> map = HashMultimap.create(); + map.put(DEFAULT_ID, DataObjectUpdate.create(DEFAULT_ID, DEFAULT_DATA_OBJECT, DEFAULT_DATA_OBJECT)); + verify(writer).update(eq(new WriterRegistry.DataObjectUpdates(map, ImmutableMultimap.of())), any(WriteContext.class)); + assertEquals(nestedList, dataTree.takeSnapshot().readNode(ModificationDiffTest.NESTED_LIST_ID).get()); + } + + private static DataObject mockDataObject(final String name, final Class classToMock) { + final DataObject dataBefore = mock(classToMock, name); + doReturn(classToMock).when(dataBefore).getImplementedInterface(); + return dataBefore; + } + + @Test + public void testCommitUndoSuccessful() throws Exception { + final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue"); + + // Fail on update: + final WriterRegistry.Reverter reverter = mock(WriterRegistry.Reverter.class); + final TranslationException failedOnUpdateException = new TranslationException("update failed"); + doThrow(new WriterRegistry.BulkUpdateException(Collections.singleton(DEFAULT_ID), reverter, failedOnUpdateException)) + .when(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); + + try { + // Run the test + final DataModification dataModification = configDataTree.newModification(); + dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList); + dataModification.validate(); + dataModification.commit(); + fail("WriterRegistry.BulkUpdateException was expected"); + } catch (WriterRegistry.BulkUpdateException e) { + verify(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); + assertThat(e.getFailedIds(), hasItem(DEFAULT_ID)); + verify(reverter).revert(); + assertEquals(failedOnUpdateException, e.getCause()); + } + } + + @Test + public void testCommitUndoFailed() throws Exception { + final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue"); + + // Fail on update: + final WriterRegistry.Reverter reverter = mock(WriterRegistry.Reverter.class); + final TranslationException failedOnUpdateException = new TranslationException("update failed"); + doThrow(new WriterRegistry.BulkUpdateException(Collections.singleton(DEFAULT_ID), reverter, failedOnUpdateException)) + .when(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); + + // Fail on revert: + final TranslationException failedOnRevertException = new TranslationException("revert failed"); + doThrow(new WriterRegistry.Reverter.RevertFailedException(Collections.emptySet(), failedOnRevertException)) + .when(reverter).revert(); + + try { + // Run the test + final DataModification dataModification = configDataTree.newModification(); + dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList); + dataModification.validate(); + dataModification.commit(); + fail("WriterRegistry.Reverter.RevertFailedException was expected"); + } catch (WriterRegistry.Reverter.RevertFailedException e) { + verify(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); + verify(reverter).revert(); + assertEquals(failedOnRevertException, e.getCause()); + } + } + + private abstract static class DataObject1 implements DataObject {} + private abstract static class DataObject2 implements DataObject {} + private abstract static class DataObject3 implements DataObject {} + + @Test + public void testToBindingAware() throws Exception { + when(serializer.fromNormalizedNode(any(YangInstanceIdentifier.class), eq(null))).thenReturn(null); + + final Map biNodes = new HashMap<>(); + // delete + final QName nn1 = QName.create("namespace", "nn1"); + final YangInstanceIdentifier yid1 = mockYid(nn1); + final InstanceIdentifier iid1 = mockIid(yid1, DataObject1.class); + final NormalizedNode nn1B = mockNormalizedNode(nn1); + final DataObject1 do1B = mockDataObject(yid1, iid1, nn1B, DataObject1.class); + biNodes.put(yid1, ModificationDiff.NormalizedNodeUpdate.create(yid1, nn1B, null)); + + // create + final QName nn2 = QName.create("namespace", "nn1"); + final YangInstanceIdentifier yid2 = mockYid(nn2); + final InstanceIdentifier iid2 = mockIid(yid2, DataObject2.class);; + final NormalizedNode nn2A = mockNormalizedNode(nn2); + final DataObject2 do2A = mockDataObject(yid2, iid2, nn2A, DataObject2.class); + biNodes.put(yid2, ModificationDiff.NormalizedNodeUpdate.create(yid2, null, nn2A)); + + // update + final QName nn3 = QName.create("namespace", "nn1"); + final YangInstanceIdentifier yid3 = mockYid(nn3); + final InstanceIdentifier iid3 = mockIid(yid3, DataObject3.class); + final NormalizedNode nn3B = mockNormalizedNode(nn3); + final DataObject3 do3B = mockDataObject(yid3, iid3, nn3B, DataObject3.class); + final NormalizedNode nn3A = mockNormalizedNode(nn3); + final DataObject3 do3A = mockDataObject(yid3, iid3, nn3A, DataObject3.class);; + biNodes.put(yid3, ModificationDiff.NormalizedNodeUpdate.create(yid3, nn3B, nn3A)); + + final WriterRegistry.DataObjectUpdates dataObjectUpdates = + ModifiableDataTreeDelegator.toBindingAware(biNodes, serializer); + + assertThat(dataObjectUpdates.getDeletes().size(), is(1)); + assertThat(dataObjectUpdates.getDeletes().keySet(), hasItem(((InstanceIdentifier) iid1))); + assertThat(dataObjectUpdates.getDeletes().values(), hasItem( + ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid1, do1B, null)))); + + assertThat(dataObjectUpdates.getUpdates().size(), is(2)); + assertThat(dataObjectUpdates.getUpdates().keySet(), hasItems((InstanceIdentifier) iid2, (InstanceIdentifier) iid3)); + assertThat(dataObjectUpdates.getUpdates().values(), hasItems( + DataObjectUpdate.create(iid2, null, do2A), + DataObjectUpdate.create(iid3, do3B, do3A))); + + assertThat(dataObjectUpdates.getTypeIntersection().size(), is(3)); + } + + private D mockDataObject(final YangInstanceIdentifier yid1, + final InstanceIdentifier iid1, + final NormalizedNode nn1B, + final Class type) { + final D do1B = mock(type); + when(serializer.fromNormalizedNode(yid1, nn1B)).thenReturn(new AbstractMap.SimpleEntry<>(iid1, do1B)); + return do1B; + } + + private NormalizedNode mockNormalizedNode(final QName nn1) { + final NormalizedNode nn1B = mock(NormalizedNode.class); + when(nn1B.getNodeType()).thenReturn(nn1); + return nn1B; + } + + private InstanceIdentifier mockIid(final YangInstanceIdentifier yid1, + final Class type) { + final InstanceIdentifier iid1 = InstanceIdentifier.create(type); + when(serializer.fromYangInstanceIdentifier(yid1)).thenReturn(iid1); + return iid1; + } + + private YangInstanceIdentifier mockYid(final QName nn1) { + final YangInstanceIdentifier yid1 = mock(YangInstanceIdentifier.class); + when(yid1.getLastPathArgument()).thenReturn(new YangInstanceIdentifier.NodeIdentifier(nn1)); + return yid1; + } +} diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ModificationDiffTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ModificationDiffTest.java new file mode 100644 index 000000000..2fa82043b --- /dev/null +++ b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ModificationDiffTest.java @@ -0,0 +1,427 @@ +package io.fd.honeycomb.data.impl; + +import static org.hamcrest.CoreMatchers.hasItems; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.util.Map; +import org.junit.Test; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateTip; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; +import org.opendaylight.yangtools.yang.data.api.schema.tree.TipProducingDataTree; +import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; +import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor; +import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline; +import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl; + +public class ModificationDiffTest { + + static final QName TOP_CONTAINER_QNAME = + QName.create("urn:opendaylight:params:xml:ns:yang:test:diff", "2015-01-05", "top-container"); + static final QName STRING_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "string"); + static final QName NAME_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "name"); + static final QName TEXT_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "text"); + static final QName NESTED_LIST_QNAME = QName.create(TOP_CONTAINER_QNAME, "nested-list"); + static final QName DEEP_LIST_QNAME = QName.create(TOP_CONTAINER_QNAME, "deep-list"); + + static final QName WITH_CHOICE_CONTAINER_QNAME = + QName.create("urn:opendaylight:params:xml:ns:yang:test:diff", "2015-01-05", "with-choice"); + static final QName CHOICE_QNAME = QName.create(WITH_CHOICE_CONTAINER_QNAME, "choice"); + static final QName IN_CASE1_LEAF_QNAME = QName.create(WITH_CHOICE_CONTAINER_QNAME, "in-case1"); + static final QName IN_CASE2_LEAF_QNAME = QName.create(WITH_CHOICE_CONTAINER_QNAME, "in-case2"); + + static final YangInstanceIdentifier TOP_CONTAINER_ID = YangInstanceIdentifier.of(TOP_CONTAINER_QNAME); + static final YangInstanceIdentifier NESTED_LIST_ID = TOP_CONTAINER_ID.node(new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); + + + @Test + public void testInitialWrite() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + final DataTreeModification dataTreeModification = getModification(dataTree); + final NormalizedNode topContainer = getTopContainer("string1"); + final YangInstanceIdentifier TOP_CONTAINER_ID = YangInstanceIdentifier.of(TOP_CONTAINER_QNAME); + dataTreeModification.write(TOP_CONTAINER_ID, topContainer); + final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification); + + final ModificationDiff modificationDiff = getModificationDiff(prepare); + + assertThat(modificationDiff.getUpdates().size(), is(1)); + assertThat(modificationDiff.getUpdates().values().size(), is(1)); + assertUpdate(modificationDiff.getUpdates().values().iterator().next(), TOP_CONTAINER_ID, null, topContainer); + } + + @Test + public void testInitialWriteForContainerWithChoice() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + final DataTreeModification dataTreeModification = getModification(dataTree); + final ContainerNode containerWithChoice = Builders.containerBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(WITH_CHOICE_CONTAINER_QNAME)) + .withChild(Builders.choiceBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CHOICE_QNAME)) + .withChild(ImmutableNodes.leafNode(IN_CASE1_LEAF_QNAME, "withinCase1")) + .build()) + .build(); + final YangInstanceIdentifier WITH_CHOICE_CONTAINER_ID = YangInstanceIdentifier.of(WITH_CHOICE_CONTAINER_QNAME); + dataTreeModification.write(WITH_CHOICE_CONTAINER_ID, containerWithChoice); + final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification); + + final Map updates = getModificationDiff(prepare).getUpdates(); + + assertThat(updates.size(), is(1)); + assertUpdate(getNormalizedNodeUpdateForAfterType(updates, ContainerNode.class), + WITH_CHOICE_CONTAINER_ID, null, containerWithChoice); + } + + private DataTreeModification getModification(final TipProducingDataTree dataTree) { + final DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); + return dataTreeSnapshot.newModification(); + } + + @Test + public void testWriteNonPresenceEmptyContainer() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + final DataTreeModification dataTreeModification = getModification(dataTree); + final NormalizedNode topContainer = ImmutableNodes.containerNode(TOP_CONTAINER_QNAME); + dataTreeModification.write(TOP_CONTAINER_ID, topContainer); + final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification); + + final ModificationDiff modificationDiff = getModificationDiff(prepare); + + assertThat(modificationDiff.getUpdates().size(), is(0)); + } + + private DataTreeCandidateTip prepareModification(final TipProducingDataTree dataTree, + final DataTreeModification dataTreeModification) + throws DataValidationFailedException { + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + return dataTree.prepare(dataTreeModification); + } + + @Test + public void testUpdateWrite() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + final ContainerNode topContainer = getTopContainer("string1"); + addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID); + + final DataTreeModification dataTreeModification = getModification(dataTree); + final NormalizedNode topContainerAfter = getTopContainer("string2"); + dataTreeModification.write(TOP_CONTAINER_ID, topContainerAfter); + final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification); + + final Map updates = getModificationDiff(prepare).getUpdates(); + + assertThat(updates.size(), is(1)); + assertThat(updates.values().size(), is(1)); + assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, topContainerAfter); + } + + private ModificationDiff getModificationDiff(final DataTreeCandidateTip prepare) { + return ModificationDiff.recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode()); + } + + @Test + public void testUpdateMerge() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + final ContainerNode topContainer = getTopContainer("string1"); + addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID); + + final DataTreeModification dataTreeModification = getModification(dataTree); + final NormalizedNode topContainerAfter = getTopContainer("string2"); + dataTreeModification.merge(TOP_CONTAINER_ID, topContainerAfter); + final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification); + + final Map updates = getModificationDiff(prepare).getUpdates(); + assertThat(updates.size(), is(1)); + assertThat(updates.values().size(), is(1)); + assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, topContainerAfter); + } + + @Test + public void testUpdateDelete() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + final ContainerNode topContainer = getTopContainer("string1"); + addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID); + + final DataTreeModification dataTreeModification = getModification(dataTree); + dataTreeModification.delete(TOP_CONTAINER_ID); + final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification); + + final Map updates = getModificationDiff(prepare).getUpdates(); + assertThat(updates.size(), is(1)); + assertThat(updates.values().size(), is(1)); + assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, null); + } + + @Test + public void testWriteAndUpdateInnerList() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + + DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); + DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); + final YangInstanceIdentifier listId = + YangInstanceIdentifier.create( + new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), + new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); + + final MapNode mapNode = getNestedList("name1", "text"); + final YangInstanceIdentifier listEntryId = listId.node(mapNode.getValue().iterator().next().getIdentifier()); + dataTreeModification.write(listId, mapNode); + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); + + Map updates = getModificationDiff(prepare).getUpdates(); + + assertThat(updates.size(), is(1)); + assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class), + listEntryId, null, mapNode.getValue().iterator().next()); + + // Commit so that update can be tested next + dataTree.commit(prepare); + + YangInstanceIdentifier listItemId = listId.node( + new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1")); + MapEntryNode mapEntryNode = + getNestedList("name1", "text-update").getValue().iterator().next(); + + dataTreeSnapshot = dataTree.takeSnapshot(); + dataTreeModification = dataTreeSnapshot.newModification(); + dataTreeModification.write(listItemId, mapEntryNode); + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + prepare = dataTree.prepare(dataTreeModification); + + updates = getModificationDiff(prepare).getUpdates(); + assertThat(updates.size(), is(1 /*Actual list entry*/)); + } +// + private void assertUpdate(final ModificationDiff.NormalizedNodeUpdate update, + final YangInstanceIdentifier idExpected, + final NormalizedNode beforeExpected, + final NormalizedNode afterExpected) { + assertThat(update.getId(), is(idExpected)); + assertThat(update.getDataBefore(), is(beforeExpected)); + assertThat(update.getDataAfter(), is(afterExpected)); + } + + @Test + public void testWriteTopContainerAndInnerList() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + + DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); + DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); + + final ContainerNode topContainer = getTopContainer("string1"); + dataTreeModification.write(TOP_CONTAINER_ID, topContainer); + + final YangInstanceIdentifier listId = + YangInstanceIdentifier.create( + new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), + new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); + + final MapNode mapNode = getNestedList("name1", "text"); + final YangInstanceIdentifier listEntryId = listId.node(mapNode.getValue().iterator().next().getIdentifier()); + + dataTreeModification.write(listId, mapNode); + + final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification); + + final Map updates = getModificationDiff(prepare).getUpdates(); + + assertThat(updates.size(), is(2)); + assertThat(updates.values().size(), is(2)); + assertUpdate(getNormalizedNodeUpdateForAfterType(updates, ContainerNode.class), TOP_CONTAINER_ID, null, + Builders.containerBuilder(topContainer).withChild(mapNode).build()); + assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class), listEntryId, null, mapNode.getValue().iterator().next()); + // Assert that keys of the updates map are not wildcarded YID + assertThat(updates.keySet(), hasItems( + TOP_CONTAINER_ID, + listEntryId)); + } + + private ModificationDiff.NormalizedNodeUpdate getNormalizedNodeUpdateForAfterType( + final Map updates, + final Class> containerNodeClass) { + return updates.values().stream() + .filter(update -> containerNodeClass.isAssignableFrom(update.getDataAfter().getClass())) + .findFirst().get(); + } + + private ModificationDiff.NormalizedNodeUpdate getNormalizedNodeUpdateForBeforeType( + final Map updates, + final Class> containerNodeClass) { + return updates.values().stream() + .filter(update -> containerNodeClass.isAssignableFrom(update.getDataBefore().getClass())) + .findFirst().get(); + } + + @Test + public void testWriteDeepList() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + + DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); + DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); + + YangInstanceIdentifier listId = + YangInstanceIdentifier.create( + new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), + new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); + + MapNode mapNode = getNestedList("name1", "text"); + dataTreeModification.write(listId, mapNode); + + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); + dataTree.commit(prepare); + + dataTreeSnapshot = dataTree.takeSnapshot(); + dataTreeModification = dataTreeSnapshot.newModification(); + + final YangInstanceIdentifier.NodeIdentifierWithPredicates nestedListNodeId = + new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1"); + listId = YangInstanceIdentifier.create( + new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), + new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME), + nestedListNodeId); + final YangInstanceIdentifier deepListId = + listId.node(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME)); + final YangInstanceIdentifier deepListEntryId = deepListId.node( + new YangInstanceIdentifier.NodeIdentifierWithPredicates(DEEP_LIST_QNAME, NAME_LEAF_QNAME, "name1")); + + final MapEntryNode deepListEntry = getDeepList("name1").getValue().iterator().next(); + // Merge parent list, just to see no modifications on it + dataTreeModification.merge( + listId, + Builders.mapEntryBuilder().withNodeIdentifier(nestedListNodeId) + .withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, "name1")).build()); + dataTreeModification.merge( + deepListId, + Builders.mapBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME)) + .build()); + dataTreeModification.merge( + deepListEntryId, + deepListEntry); + + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + prepare = dataTree.prepare(dataTreeModification); + dataTree.commit(prepare); + + final Map updates = getModificationDiff(prepare).getUpdates(); + assertThat(updates.size(), is(1)); + assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class), deepListEntryId, null, deepListEntry); + } + + @Test + public void testDeleteInnerListItem() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + + DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); + DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); + final YangInstanceIdentifier listId = + YangInstanceIdentifier.create( + new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), + new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); + + final MapNode mapNode = getNestedList("name1", "text"); + dataTreeModification.write(listId, mapNode); + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); + + // Commit so that update can be tested next + dataTree.commit(prepare); + + YangInstanceIdentifier listItemId = listId.node( + new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1")); + + dataTreeSnapshot = dataTree.takeSnapshot(); + dataTreeModification = dataTreeSnapshot.newModification(); + dataTreeModification.delete(listItemId); + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + prepare = dataTree.prepare(dataTreeModification); + + final Map updates = getModificationDiff(prepare).getUpdates(); + assertThat(updates.size(), is(1)); + assertUpdate(getNormalizedNodeUpdateForBeforeType(updates, MapEntryNode.class), listItemId, mapNode.getValue().iterator().next(), null); + } + + static void addNodeToTree(final DataTree dataTree, final NormalizedNode node, + final YangInstanceIdentifier id) + throws DataValidationFailedException { + DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); + DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); + dataTreeModification.write(id, node); + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + DataTreeCandidate prepare = dataTree.prepare(dataTreeModification); + dataTree.commit(prepare); + } + + static TipProducingDataTree getDataTree() throws ReactorException { + final TipProducingDataTree dataTree = InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION); + dataTree.setSchemaContext(getSchemaCtx()); + return dataTree; + } + + static ContainerNode getTopContainer(final String stringValue) { + return Builders.containerBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME)) + .withChild(ImmutableNodes.leafNode(STRING_LEAF_QNAME, stringValue)) + .build(); + } + + static MapNode getNestedList(final String listItemName, final String text) { + return Builders.mapBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)) + .withChild( + Builders.mapEntryBuilder() + .withNodeIdentifier( + new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, + NAME_LEAF_QNAME, listItemName)) + .withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, listItemName)) + .withChild(ImmutableNodes.leafNode(TEXT_LEAF_QNAME, text)) + .build() + ) + .build(); + } + + private MapNode getDeepList(final String listItemName) { + return Builders.mapBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME)) + .withChild( + Builders.mapEntryBuilder() + .withNodeIdentifier( + new YangInstanceIdentifier.NodeIdentifierWithPredicates(DEEP_LIST_QNAME, + NAME_LEAF_QNAME, listItemName)) + .withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, listItemName)) + .build() + ) + .build(); + } + + private static SchemaContext getSchemaCtx() throws ReactorException { + final CrossSourceStatementReactor.BuildAction buildAction = YangInferencePipeline.RFC6020_REACTOR.newBuild(); + buildAction.addSource(new YangStatementSourceImpl(ModificationDiffTest.class.getResourceAsStream("/test-diff.yang"))); + return buildAction.buildEffective(); + } +} \ No newline at end of file diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/PersistingDataTreeAdapterTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/PersistingDataTreeAdapterTest.java new file mode 100644 index 000000000..305166421 --- /dev/null +++ b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/PersistingDataTreeAdapterTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.sal.core.api.model.SchemaService; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; + +public class PersistingDataTreeAdapterTest { + + @Mock + private DataTree delegatingDataTree; + @Mock + private SchemaService schemaService; + @Mock + private DataTreeSnapshot snapshot; + + private Path tmpPersistFile; + + private PersistingDataTreeAdapter persistingDataTreeAdapter; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + tmpPersistFile = Files.createTempFile("testing-hc-persistence", "json"); + persistingDataTreeAdapter = new PersistingDataTreeAdapter(delegatingDataTree, schemaService, tmpPersistFile); + } + + @Test + public void testNoPersistOnFailure() throws Exception { + doThrow(new IllegalStateException("testing errors")).when(delegatingDataTree).commit(any(DataTreeCandidate.class)); + + try { + persistingDataTreeAdapter.commit(null); + fail("Exception expected"); + } catch (IllegalStateException e) { + verify(delegatingDataTree, times(0)).takeSnapshot(); + verify(delegatingDataTree).commit(any(DataTreeCandidate.class)); + } + } + +} \ No newline at end of file diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ReadOnlyTransactionTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ReadOnlyTransactionTest.java new file mode 100644 index 000000000..77470d17c --- /dev/null +++ b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ReadOnlyTransactionTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import io.fd.honeycomb.data.ReadableDataManager; +import io.fd.honeycomb.data.DataModification; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public class ReadOnlyTransactionTest { + + @Mock + private ReadableDataManager operationalData; + @Mock + private DataModification configSnapshot; + + private ReadOnlyTransaction readOnlyTx; + + @Before + public void setUp() { + initMocks(this); + readOnlyTx = ReadOnlyTransaction.create(configSnapshot, operationalData); + } + + @Test + public void testExists() { + final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class); + final CheckedFuture>, ReadFailedException> + future = mock(CheckedFuture.class); + when(operationalData.read(path)).thenReturn(future); + + readOnlyTx.exists(LogicalDatastoreType.OPERATIONAL, path); + + verify(operationalData).read(path); + } + + @Test + public void testGetIdentifier() { + assertNotNull(readOnlyTx.getIdentifier()); + } +} \ No newline at end of file diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ReadWriteTransactionTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ReadWriteTransactionTest.java new file mode 100644 index 000000000..820d499be --- /dev/null +++ b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ReadWriteTransactionTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.verify; +import static org.mockito.MockitoAnnotations.initMocks; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public class ReadWriteTransactionTest { + + @Mock + private DOMDataReadOnlyTransaction readTx; + + @Mock + private DOMDataWriteTransaction writeTx; + + private LogicalDatastoreType store; + + @Mock + private YangInstanceIdentifier path; + + @Mock + private NormalizedNode data; + + private ReadWriteTransaction readWriteTx; + + @Before + public void setUp() { + initMocks(this); + store = LogicalDatastoreType.CONFIGURATION; + readWriteTx = new ReadWriteTransaction(readTx, writeTx); + } + + @Test + public void testCancel() { + readWriteTx.cancel(); + verify(writeTx).cancel(); + } + + @Test + public void testPut() { + readWriteTx.put(store, path, data); + verify(writeTx).put(store, path, data); + } + + @Test + public void testMerge() { + readWriteTx.merge(store, path, data); + verify(writeTx).merge(store, path, data); + } + + @Test + public void testDelete() { + readWriteTx.delete(store, path); + verify(writeTx).delete(store, path); + } + + @Test + public void testSubmit() throws Exception { + readWriteTx.submit(); + verify(writeTx).submit(); + } + + + @SuppressWarnings("deprecation") + @Test + public void testCommit() throws Exception { + readWriteTx.commit(); + verify(writeTx).commit(); + } + + @Test + public void testRead() { + readWriteTx.read(store, path); + verify(readTx).read(store, path); + } + + @Test + public void testExists() { + readWriteTx.exists(store, path); + verify(readTx).exists(store, path); + } + + @Test + public void testGetIdentifier() throws Exception { + assertNotNull(readWriteTx.getIdentifier()); + } +} \ No newline at end of file diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ReadableDataTreeDelegatorTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ReadableDataTreeDelegatorTest.java new file mode 100644 index 000000000..2b4cc2a06 --- /dev/null +++ b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ReadableDataTreeDelegatorTest.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.same; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import com.google.common.base.Optional; +import com.google.common.collect.Iterables; +import com.google.common.collect.LinkedListMultimap; +import com.google.common.collect.Multimap; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.registry.ReaderRegistry; +import java.util.Collections; +import java.util.Map; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +public class ReadableDataTreeDelegatorTest { + + @Mock + private BindingNormalizedNodeSerializer serializer; + @Mock + private ReaderRegistry reader; + + private ReadableDataTreeDelegator operationalData; + + @Mock + private InstanceIdentifier id; + @Mock + private Map.Entry> entry; + @Mock + private SchemaContext globalContext; + @Mock + private DataSchemaNode schemaNode; + @Mock + private ReadContext readCtx; + @Mock + private DOMDataBroker netconfMonitoringBroker; + @Mock + private DOMDataReadOnlyTransaction domDataReadOnlyTransaction; + @Mock + private DataBroker contextBroker; + + @Before + public void setUp() { + initMocks(this); + operationalData = new ReadableDataTreeDelegator(serializer, globalContext, reader, contextBroker); + doReturn(schemaNode).when(globalContext).getDataChildByName(any(QName.class)); + + doReturn(domDataReadOnlyTransaction).when(netconfMonitoringBroker).newReadOnlyTransaction(); + doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(domDataReadOnlyTransaction) + .read(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class)); + + final org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction ctxTransaction = mock( + org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction.class); + doReturn(ctxTransaction).when(contextBroker).newReadWriteTransaction(); + doReturn(Futures.immediateCheckedFuture(null)).when(ctxTransaction).submit(); + } + + @Test + public void testReadNode() throws Exception { + final YangInstanceIdentifier yangId = mock(YangInstanceIdentifier.class); + final YangInstanceIdentifier.PathArgument pArg = mock(YangInstanceIdentifier.PathArgument.class); + doReturn(pArg).when(yangId).getLastPathArgument(); + doReturn(Collections.singletonList(pArg)).when(yangId).getPathArguments(); + + doReturn(QName.create("namespace", "2012-12-12", "local")).when(pArg).getNodeType(); + doReturn(id).when(serializer).fromYangInstanceIdentifier(yangId); + + final DataObject dataObject = mock(DataObject.class); + doReturn(Optional.of(dataObject)).when(reader).read(same(id), any(ReadContext.class)); + + when(serializer.toNormalizedNode(id, dataObject)).thenReturn(entry); + final DataContainerChild expectedValue = mock(DataContainerChild.class); + doReturn(expectedValue).when(entry).getValue(); + + final CheckedFuture>, ReadFailedException> future = operationalData.read(yangId); + + verify(serializer).fromYangInstanceIdentifier(yangId); + verify(reader).read(same(id), any(ReadContext.class)); + final Optional> result = future.get(); + assertTrue(result.isPresent()); + assertEquals(expectedValue, result.get()); + } + + @Test + public void testReadNonExistingNode() throws Exception { + final YangInstanceIdentifier yangId = mock(YangInstanceIdentifier.class); + doReturn(id).when(serializer).fromYangInstanceIdentifier(yangId); + doReturn(Optional.absent()).when(reader).read(same(id), any(ReadContext.class)); + + final CheckedFuture>, ReadFailedException> future = operationalData.read(yangId); + + verify(serializer).fromYangInstanceIdentifier(yangId); + verify(reader).read(same(id), any(ReadContext.class)); + final Optional> result = future.get(); + assertFalse(result.isPresent()); + } + + @Test + public void testReadFailed() throws Exception { + doThrow(io.fd.honeycomb.translate.read.ReadFailedException.class).when(reader).readAll(any(ReadContext.class)); + + final CheckedFuture>, ReadFailedException> future = + operationalData.read( YangInstanceIdentifier.EMPTY); + + try { + future.checkedGet(); + } catch (ReadFailedException e) { + assertTrue(e.getCause() instanceof io.fd.honeycomb.translate.read.ReadFailedException); + return; + } + fail("ReadFailedException was expected"); + } + + @Test + public void testReadRootWithOneNonListElement() throws Exception { + // Prepare data + final InstanceIdentifier vppStateII = InstanceIdentifier.create(DataObject.class); + final DataObject vppState = mock(DataObject.class); + Multimap, DataObject> dataObjects = LinkedListMultimap.create(); + dataObjects.put(vppStateII, vppState); + doReturn(dataObjects).when(reader).readAll(any(ReadContext.class)); + + // Init serializer + final YangInstanceIdentifier vppYangId = YangInstanceIdentifier.builder().node(QName.create("n", "d")).build(); + when(serializer.toYangInstanceIdentifier(vppStateII)).thenReturn(vppYangId); + when(serializer.toNormalizedNode(vppStateII, vppState)).thenReturn(entry); + final DataContainerChild vppStateContainer = mock(DataContainerChild.class); + doReturn(vppStateContainer).when(entry).getValue(); + doReturn(vppYangId.getLastPathArgument()).when(vppStateContainer).getIdentifier(); + + // Read root + final CheckedFuture>, ReadFailedException> future = + operationalData.read(YangInstanceIdentifier.EMPTY); + + verify(reader).readAll(any(ReadContext.class)); + verify(serializer).toYangInstanceIdentifier(vppStateII); + verify(serializer).toNormalizedNode(vppStateII, vppState); + + // Check the result is an ContainerNode with only one child + final Optional> result = future.get(); + assertTrue(result.isPresent()); + + final ContainerNode rootNode = (ContainerNode) result.get(); + assertEquals(SchemaContext.NAME, rootNode.getIdentifier().getNodeType()); + assertEquals(vppStateContainer, Iterables.getOnlyElement(rootNode.getValue())); + } +} \ No newline at end of file diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/WriteTransactionTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/WriteTransactionTest.java new file mode 100644 index 000000000..1dab5524d --- /dev/null +++ b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/WriteTransactionTest.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.MockitoAnnotations.initMocks; + +import com.google.common.util.concurrent.CheckedFuture; +import io.fd.honeycomb.data.DataModification; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; + +public class WriteTransactionTest { + + @Mock + private DataModification configSnapshot; + @Mock + private YangInstanceIdentifier path; + @Mock + private NormalizedNode data; + + private WriteTransaction writeTx; + + @Before + public void setUp() { + initMocks(this); + writeTx = WriteTransaction.createConfigOnly(configSnapshot); + } + + @Test + public void testPut() { + writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data); + verify(configSnapshot).write(path, data); + } + + @Test(expected = IllegalArgumentException.class) + public void testPutOperational() { + writeTx.put(LogicalDatastoreType.OPERATIONAL, path, data); + verify(configSnapshot).write(path, data); + } + + @Test(expected = IllegalStateException.class) + public void testOnFinishedTx() { + writeTx.submit(); + writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data); + verify(configSnapshot).write(path, data); + } + + @Test + public void testMerge() { + writeTx.merge(LogicalDatastoreType.CONFIGURATION, path, data); + verify(configSnapshot).merge(path, data); + } + + @Test + public void testCancel() { + assertTrue(writeTx.cancel()); + } + + @Test + public void testCancelFinished() { + writeTx.submit(); + assertFalse(writeTx.cancel()); + } + + @Test + public void testDelete() { + writeTx.delete(LogicalDatastoreType.CONFIGURATION, path); + verify(configSnapshot).delete(path); + } + + @Test + public void testSubmit() throws Exception { + writeTx.submit(); + verify(configSnapshot).validate(); + verify(configSnapshot).commit(); + } + + @Test + public void testSubmitFailed() throws Exception { + doThrow(mock(DataValidationFailedException.class)).when(configSnapshot).commit(); + final CheckedFuture future = writeTx.submit(); + try { + future.get(); + } catch (Exception e) { + assertTrue(e.getCause() instanceof TransactionCommitFailedException); + return; + } + fail("Expected exception to be thrown"); + + } + + @Test(expected = UnsupportedOperationException.class) + public void testCommit() { + writeTx.commit(); + } + + @Test + public void testGetIdentifier() { + assertNotNull(writeTx.getIdentifier()); + } +} \ No newline at end of file diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java deleted file mode 100644 index 55b92b50b..000000000 --- a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.initMocks; - -import io.fd.honeycomb.v3po.data.ReadableDataManager; -import io.fd.honeycomb.v3po.data.ModifiableDataManager; -import io.fd.honeycomb.v3po.data.DataModification; -import java.util.Map; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener; -import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension; -import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener; -import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; -import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction; -import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; - -public class DataBrokerTest { - - @Mock - private ReadableDataManager operationalData; - @Mock - private ModifiableDataManager confiDataTree; - @Mock - private DataModification configSnapshot; - private DataBroker broker; - - @Before - public void setUp() { - initMocks(this); - when(confiDataTree.newModification()).thenReturn(configSnapshot); - broker = DataBroker.create(confiDataTree, operationalData); - } - - @Test - public void testNewReadWriteTransaction() { - final DOMDataReadWriteTransaction readWriteTx = broker.newReadWriteTransaction(); - final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class); - readWriteTx.read(LogicalDatastoreType.CONFIGURATION, path); - - // verify that read and write transactions use the same config snapshot - verify(configSnapshot).read(path); - verify(confiDataTree).newModification(); - } - - @Test - public void testNewWriteOnlyTransaction() { - final DOMDataWriteTransaction writeTx = broker.newWriteOnlyTransaction(); - - // verify that write transactions use config snapshot - verify(confiDataTree).newModification(); - } - - @Test - public void testNewReadOnlyTransaction() { - final DOMDataReadOnlyTransaction readTx = broker.newReadOnlyTransaction(); - - final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class); - readTx.read(LogicalDatastoreType.CONFIGURATION, path); - - // verify that read transactions use config snapshot - verify(configSnapshot).read(path); - } - - @Test(expected = UnsupportedOperationException.class) - public void testRegisterDataChangeListener() { - final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class); - final DOMDataChangeListener listener = mock(DOMDataChangeListener.class); - broker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, path, listener, - AsyncDataBroker.DataChangeScope.BASE); - } - - @Test(expected = UnsupportedOperationException.class) - public void testCreateTransactionChain() { - final TransactionChainListener listener = mock(TransactionChainListener.class); - broker.createTransactionChain(listener); - } - - @Test - public void testGetSupportedExtensions() { - final Map, DOMDataBrokerExtension> supportedExtensions = - broker.getSupportedExtensions(); - assertTrue(supportedExtensions.isEmpty()); - } - - -} \ No newline at end of file diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java deleted file mode 100644 index 915d738e9..000000000 --- a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import static org.hamcrest.CoreMatchers.hasItem; -import static org.hamcrest.CoreMatchers.hasItems; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.initMocks; - -import com.google.common.base.Optional; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.Multimap; -import com.google.common.util.concurrent.CheckedFuture; -import com.google.common.util.concurrent.Futures; -import io.fd.honeycomb.v3po.data.DataModification; -import io.fd.honeycomb.v3po.translate.TranslationException; -import io.fd.honeycomb.v3po.translate.write.DataObjectUpdate; -import io.fd.honeycomb.v3po.translate.write.WriteContext; -import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistry; -import java.util.AbstractMap; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.opendaylight.controller.md.sal.binding.api.DataBroker; -import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; -import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.MapNode; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; - -public class ModifiableDataTreeDelegatorTest { - - @Mock - private WriterRegistry writer; - @Mock - private BindingNormalizedNodeSerializer serializer; - private DataTree dataTree; - @Mock - private org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification modification; - @Mock - private DataBroker contextBroker; - @Mock - private org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction tx; - - private ModifiableDataTreeManager configDataTree; - - static final InstanceIdentifier DEFAULT_ID = InstanceIdentifier.create(DataObject.class); - static DataObject DEFAULT_DATA_OBJECT = mockDataObject("serialized", DataObject.class); - - @Before - public void setUp() throws Exception { - initMocks(this); - dataTree = ModificationDiffTest.getDataTree(); - when(contextBroker.newReadWriteTransaction()).thenReturn(tx); - when(tx.submit()).thenReturn(Futures.immediateCheckedFuture(null)); - - when(serializer.fromYangInstanceIdentifier(any(YangInstanceIdentifier.class))).thenReturn(((InstanceIdentifier) DEFAULT_ID)); - final Map.Entry, DataObject> parsed = new AbstractMap.SimpleEntry<>(DEFAULT_ID, DEFAULT_DATA_OBJECT); - when(serializer.fromNormalizedNode(any(YangInstanceIdentifier.class), any(NormalizedNode.class))).thenReturn(parsed); - - configDataTree = new ModifiableDataTreeDelegator(serializer, dataTree, writer, contextBroker); - } - - @Test - public void testRead() throws Exception { - final ContainerNode topContainer = ModificationDiffTest.getTopContainer("topContainer"); - ModificationDiffTest.addNodeToTree(dataTree, topContainer, ModificationDiffTest.TOP_CONTAINER_ID); - final CheckedFuture>, ReadFailedException> read = - configDataTree.read(ModificationDiffTest.TOP_CONTAINER_ID); - final CheckedFuture>, ReadFailedException> read2 = - configDataTree.newModification().read(ModificationDiffTest.TOP_CONTAINER_ID); - final Optional> normalizedNodeOptional = read.get(); - final Optional> normalizedNodeOptional2 = read2.get(); - - assertEquals(normalizedNodeOptional, normalizedNodeOptional2); - assertTrue(normalizedNodeOptional.isPresent()); - assertEquals(topContainer, normalizedNodeOptional.get()); - assertEquals(dataTree.takeSnapshot().readNode(ModificationDiffTest.TOP_CONTAINER_ID), normalizedNodeOptional); - } - - @Test - public void testCommitSuccessful() throws Exception { - final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue"); - - final DataModification dataModification = configDataTree.newModification(); - dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList); - dataModification.validate(); - dataModification.commit(); - - final Multimap, DataObjectUpdate> map = HashMultimap.create(); - map.put(DEFAULT_ID, DataObjectUpdate.create(DEFAULT_ID, DEFAULT_DATA_OBJECT, DEFAULT_DATA_OBJECT)); - verify(writer).update(eq(new WriterRegistry.DataObjectUpdates(map, ImmutableMultimap.of())), any(WriteContext.class)); - assertEquals(nestedList, dataTree.takeSnapshot().readNode(ModificationDiffTest.NESTED_LIST_ID).get()); - } - - private static DataObject mockDataObject(final String name, final Class classToMock) { - final DataObject dataBefore = mock(classToMock, name); - doReturn(classToMock).when(dataBefore).getImplementedInterface(); - return dataBefore; - } - - @Test - public void testCommitUndoSuccessful() throws Exception { - final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue"); - - // Fail on update: - final WriterRegistry.Reverter reverter = mock(WriterRegistry.Reverter.class); - final TranslationException failedOnUpdateException = new TranslationException("update failed"); - doThrow(new WriterRegistry.BulkUpdateException(Collections.singleton(DEFAULT_ID), reverter, failedOnUpdateException)) - .when(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); - - try { - // Run the test - final DataModification dataModification = configDataTree.newModification(); - dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList); - dataModification.validate(); - dataModification.commit(); - fail("WriterRegistry.BulkUpdateException was expected"); - } catch (WriterRegistry.BulkUpdateException e) { - verify(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); - assertThat(e.getFailedIds(), hasItem(DEFAULT_ID)); - verify(reverter).revert(); - assertEquals(failedOnUpdateException, e.getCause()); - } - } - - @Test - public void testCommitUndoFailed() throws Exception { - final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue"); - - // Fail on update: - final WriterRegistry.Reverter reverter = mock(WriterRegistry.Reverter.class); - final TranslationException failedOnUpdateException = new TranslationException("update failed"); - doThrow(new WriterRegistry.BulkUpdateException(Collections.singleton(DEFAULT_ID), reverter, failedOnUpdateException)) - .when(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); - - // Fail on revert: - final TranslationException failedOnRevertException = new TranslationException("revert failed"); - doThrow(new WriterRegistry.Reverter.RevertFailedException(Collections.emptySet(), failedOnRevertException)) - .when(reverter).revert(); - - try { - // Run the test - final DataModification dataModification = configDataTree.newModification(); - dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList); - dataModification.validate(); - dataModification.commit(); - fail("WriterRegistry.Reverter.RevertFailedException was expected"); - } catch (WriterRegistry.Reverter.RevertFailedException e) { - verify(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); - verify(reverter).revert(); - assertEquals(failedOnRevertException, e.getCause()); - } - } - - private abstract static class DataObject1 implements DataObject {} - private abstract static class DataObject2 implements DataObject {} - private abstract static class DataObject3 implements DataObject {} - - @Test - public void testToBindingAware() throws Exception { - when(serializer.fromNormalizedNode(any(YangInstanceIdentifier.class), eq(null))).thenReturn(null); - - final Map biNodes = new HashMap<>(); - // delete - final QName nn1 = QName.create("namespace", "nn1"); - final YangInstanceIdentifier yid1 = mockYid(nn1); - final InstanceIdentifier iid1 = mockIid(yid1, DataObject1.class); - final NormalizedNode nn1B = mockNormalizedNode(nn1); - final DataObject1 do1B = mockDataObject(yid1, iid1, nn1B, DataObject1.class); - biNodes.put(yid1, ModificationDiff.NormalizedNodeUpdate.create(yid1, nn1B, null)); - - // create - final QName nn2 = QName.create("namespace", "nn1"); - final YangInstanceIdentifier yid2 = mockYid(nn2); - final InstanceIdentifier iid2 = mockIid(yid2, DataObject2.class);; - final NormalizedNode nn2A = mockNormalizedNode(nn2); - final DataObject2 do2A = mockDataObject(yid2, iid2, nn2A, DataObject2.class); - biNodes.put(yid2, ModificationDiff.NormalizedNodeUpdate.create(yid2, null, nn2A)); - - // update - final QName nn3 = QName.create("namespace", "nn1"); - final YangInstanceIdentifier yid3 = mockYid(nn3); - final InstanceIdentifier iid3 = mockIid(yid3, DataObject3.class); - final NormalizedNode nn3B = mockNormalizedNode(nn3); - final DataObject3 do3B = mockDataObject(yid3, iid3, nn3B, DataObject3.class); - final NormalizedNode nn3A = mockNormalizedNode(nn3); - final DataObject3 do3A = mockDataObject(yid3, iid3, nn3A, DataObject3.class);; - biNodes.put(yid3, ModificationDiff.NormalizedNodeUpdate.create(yid3, nn3B, nn3A)); - - final WriterRegistry.DataObjectUpdates dataObjectUpdates = - ModifiableDataTreeDelegator.toBindingAware(biNodes, serializer); - - assertThat(dataObjectUpdates.getDeletes().size(), is(1)); - assertThat(dataObjectUpdates.getDeletes().keySet(), hasItem(((InstanceIdentifier) iid1))); - assertThat(dataObjectUpdates.getDeletes().values(), hasItem( - ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid1, do1B, null)))); - - assertThat(dataObjectUpdates.getUpdates().size(), is(2)); - assertThat(dataObjectUpdates.getUpdates().keySet(), hasItems((InstanceIdentifier) iid2, (InstanceIdentifier) iid3)); - assertThat(dataObjectUpdates.getUpdates().values(), hasItems( - DataObjectUpdate.create(iid2, null, do2A), - DataObjectUpdate.create(iid3, do3B, do3A))); - - assertThat(dataObjectUpdates.getTypeIntersection().size(), is(3)); - } - - private D mockDataObject(final YangInstanceIdentifier yid1, - final InstanceIdentifier iid1, - final NormalizedNode nn1B, - final Class type) { - final D do1B = mock(type); - when(serializer.fromNormalizedNode(yid1, nn1B)).thenReturn(new AbstractMap.SimpleEntry<>(iid1, do1B)); - return do1B; - } - - private NormalizedNode mockNormalizedNode(final QName nn1) { - final NormalizedNode nn1B = mock(NormalizedNode.class); - when(nn1B.getNodeType()).thenReturn(nn1); - return nn1B; - } - - private InstanceIdentifier mockIid(final YangInstanceIdentifier yid1, - final Class type) { - final InstanceIdentifier iid1 = InstanceIdentifier.create(type); - when(serializer.fromYangInstanceIdentifier(yid1)).thenReturn(iid1); - return iid1; - } - - private YangInstanceIdentifier mockYid(final QName nn1) { - final YangInstanceIdentifier yid1 = mock(YangInstanceIdentifier.class); - when(yid1.getLastPathArgument()).thenReturn(new YangInstanceIdentifier.NodeIdentifier(nn1)); - return yid1; - } -} diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java deleted file mode 100644 index bc7582e93..000000000 --- a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java +++ /dev/null @@ -1,427 +0,0 @@ -package io.fd.honeycomb.v3po.data.impl; - -import static org.hamcrest.CoreMatchers.hasItems; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; - -import java.util.Map; -import org.junit.Test; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; -import org.opendaylight.yangtools.yang.data.api.schema.MapNode; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateTip; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; -import org.opendaylight.yangtools.yang.data.api.schema.tree.TipProducingDataTree; -import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType; -import org.opendaylight.yangtools.yang.data.impl.schema.Builders; -import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; -import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; -import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor; -import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline; -import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl; - -public class ModificationDiffTest { - - static final QName TOP_CONTAINER_QNAME = - QName.create("urn:opendaylight:params:xml:ns:yang:test:diff", "2015-01-05", "top-container"); - static final QName STRING_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "string"); - static final QName NAME_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "name"); - static final QName TEXT_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "text"); - static final QName NESTED_LIST_QNAME = QName.create(TOP_CONTAINER_QNAME, "nested-list"); - static final QName DEEP_LIST_QNAME = QName.create(TOP_CONTAINER_QNAME, "deep-list"); - - static final QName WITH_CHOICE_CONTAINER_QNAME = - QName.create("urn:opendaylight:params:xml:ns:yang:test:diff", "2015-01-05", "with-choice"); - static final QName CHOICE_QNAME = QName.create(WITH_CHOICE_CONTAINER_QNAME, "choice"); - static final QName IN_CASE1_LEAF_QNAME = QName.create(WITH_CHOICE_CONTAINER_QNAME, "in-case1"); - static final QName IN_CASE2_LEAF_QNAME = QName.create(WITH_CHOICE_CONTAINER_QNAME, "in-case2"); - - static final YangInstanceIdentifier TOP_CONTAINER_ID = YangInstanceIdentifier.of(TOP_CONTAINER_QNAME); - static final YangInstanceIdentifier NESTED_LIST_ID = TOP_CONTAINER_ID.node(new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); - - - @Test - public void testInitialWrite() throws Exception { - final TipProducingDataTree dataTree = getDataTree(); - final DataTreeModification dataTreeModification = getModification(dataTree); - final NormalizedNode topContainer = getTopContainer("string1"); - final YangInstanceIdentifier TOP_CONTAINER_ID = YangInstanceIdentifier.of(TOP_CONTAINER_QNAME); - dataTreeModification.write(TOP_CONTAINER_ID, topContainer); - final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification); - - final ModificationDiff modificationDiff = getModificationDiff(prepare); - - assertThat(modificationDiff.getUpdates().size(), is(1)); - assertThat(modificationDiff.getUpdates().values().size(), is(1)); - assertUpdate(modificationDiff.getUpdates().values().iterator().next(), TOP_CONTAINER_ID, null, topContainer); - } - - @Test - public void testInitialWriteForContainerWithChoice() throws Exception { - final TipProducingDataTree dataTree = getDataTree(); - final DataTreeModification dataTreeModification = getModification(dataTree); - final ContainerNode containerWithChoice = Builders.containerBuilder() - .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(WITH_CHOICE_CONTAINER_QNAME)) - .withChild(Builders.choiceBuilder() - .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CHOICE_QNAME)) - .withChild(ImmutableNodes.leafNode(IN_CASE1_LEAF_QNAME, "withinCase1")) - .build()) - .build(); - final YangInstanceIdentifier WITH_CHOICE_CONTAINER_ID = YangInstanceIdentifier.of(WITH_CHOICE_CONTAINER_QNAME); - dataTreeModification.write(WITH_CHOICE_CONTAINER_ID, containerWithChoice); - final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification); - - final Map updates = getModificationDiff(prepare).getUpdates(); - - assertThat(updates.size(), is(1)); - assertUpdate(getNormalizedNodeUpdateForAfterType(updates, ContainerNode.class), - WITH_CHOICE_CONTAINER_ID, null, containerWithChoice); - } - - private DataTreeModification getModification(final TipProducingDataTree dataTree) { - final DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); - return dataTreeSnapshot.newModification(); - } - - @Test - public void testWriteNonPresenceEmptyContainer() throws Exception { - final TipProducingDataTree dataTree = getDataTree(); - final DataTreeModification dataTreeModification = getModification(dataTree); - final NormalizedNode topContainer = ImmutableNodes.containerNode(TOP_CONTAINER_QNAME); - dataTreeModification.write(TOP_CONTAINER_ID, topContainer); - final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification); - - final ModificationDiff modificationDiff = getModificationDiff(prepare); - - assertThat(modificationDiff.getUpdates().size(), is(0)); - } - - private DataTreeCandidateTip prepareModification(final TipProducingDataTree dataTree, - final DataTreeModification dataTreeModification) - throws DataValidationFailedException { - dataTreeModification.ready(); - dataTree.validate(dataTreeModification); - return dataTree.prepare(dataTreeModification); - } - - @Test - public void testUpdateWrite() throws Exception { - final TipProducingDataTree dataTree = getDataTree(); - final ContainerNode topContainer = getTopContainer("string1"); - addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID); - - final DataTreeModification dataTreeModification = getModification(dataTree); - final NormalizedNode topContainerAfter = getTopContainer("string2"); - dataTreeModification.write(TOP_CONTAINER_ID, topContainerAfter); - final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification); - - final Map updates = getModificationDiff(prepare).getUpdates(); - - assertThat(updates.size(), is(1)); - assertThat(updates.values().size(), is(1)); - assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, topContainerAfter); - } - - private ModificationDiff getModificationDiff(final DataTreeCandidateTip prepare) { - return ModificationDiff.recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode()); - } - - @Test - public void testUpdateMerge() throws Exception { - final TipProducingDataTree dataTree = getDataTree(); - final ContainerNode topContainer = getTopContainer("string1"); - addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID); - - final DataTreeModification dataTreeModification = getModification(dataTree); - final NormalizedNode topContainerAfter = getTopContainer("string2"); - dataTreeModification.merge(TOP_CONTAINER_ID, topContainerAfter); - final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification); - - final Map updates = getModificationDiff(prepare).getUpdates(); - assertThat(updates.size(), is(1)); - assertThat(updates.values().size(), is(1)); - assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, topContainerAfter); - } - - @Test - public void testUpdateDelete() throws Exception { - final TipProducingDataTree dataTree = getDataTree(); - final ContainerNode topContainer = getTopContainer("string1"); - addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID); - - final DataTreeModification dataTreeModification = getModification(dataTree); - dataTreeModification.delete(TOP_CONTAINER_ID); - final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification); - - final Map updates = getModificationDiff(prepare).getUpdates(); - assertThat(updates.size(), is(1)); - assertThat(updates.values().size(), is(1)); - assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, null); - } - - @Test - public void testWriteAndUpdateInnerList() throws Exception { - final TipProducingDataTree dataTree = getDataTree(); - - DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); - DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); - final YangInstanceIdentifier listId = - YangInstanceIdentifier.create( - new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), - new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); - - final MapNode mapNode = getNestedList("name1", "text"); - final YangInstanceIdentifier listEntryId = listId.node(mapNode.getValue().iterator().next().getIdentifier()); - dataTreeModification.write(listId, mapNode); - dataTreeModification.ready(); - dataTree.validate(dataTreeModification); - DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); - - Map updates = getModificationDiff(prepare).getUpdates(); - - assertThat(updates.size(), is(1)); - assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class), - listEntryId, null, mapNode.getValue().iterator().next()); - - // Commit so that update can be tested next - dataTree.commit(prepare); - - YangInstanceIdentifier listItemId = listId.node( - new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1")); - MapEntryNode mapEntryNode = - getNestedList("name1", "text-update").getValue().iterator().next(); - - dataTreeSnapshot = dataTree.takeSnapshot(); - dataTreeModification = dataTreeSnapshot.newModification(); - dataTreeModification.write(listItemId, mapEntryNode); - dataTreeModification.ready(); - dataTree.validate(dataTreeModification); - prepare = dataTree.prepare(dataTreeModification); - - updates = getModificationDiff(prepare).getUpdates(); - assertThat(updates.size(), is(1 /*Actual list entry*/)); - } -// - private void assertUpdate(final ModificationDiff.NormalizedNodeUpdate update, - final YangInstanceIdentifier idExpected, - final NormalizedNode beforeExpected, - final NormalizedNode afterExpected) { - assertThat(update.getId(), is(idExpected)); - assertThat(update.getDataBefore(), is(beforeExpected)); - assertThat(update.getDataAfter(), is(afterExpected)); - } - - @Test - public void testWriteTopContainerAndInnerList() throws Exception { - final TipProducingDataTree dataTree = getDataTree(); - - DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); - DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); - - final ContainerNode topContainer = getTopContainer("string1"); - dataTreeModification.write(TOP_CONTAINER_ID, topContainer); - - final YangInstanceIdentifier listId = - YangInstanceIdentifier.create( - new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), - new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); - - final MapNode mapNode = getNestedList("name1", "text"); - final YangInstanceIdentifier listEntryId = listId.node(mapNode.getValue().iterator().next().getIdentifier()); - - dataTreeModification.write(listId, mapNode); - - final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification); - - final Map updates = getModificationDiff(prepare).getUpdates(); - - assertThat(updates.size(), is(2)); - assertThat(updates.values().size(), is(2)); - assertUpdate(getNormalizedNodeUpdateForAfterType(updates, ContainerNode.class), TOP_CONTAINER_ID, null, - Builders.containerBuilder(topContainer).withChild(mapNode).build()); - assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class), listEntryId, null, mapNode.getValue().iterator().next()); - // Assert that keys of the updates map are not wildcarded YID - assertThat(updates.keySet(), hasItems( - TOP_CONTAINER_ID, - listEntryId)); - } - - private ModificationDiff.NormalizedNodeUpdate getNormalizedNodeUpdateForAfterType( - final Map updates, - final Class> containerNodeClass) { - return updates.values().stream() - .filter(update -> containerNodeClass.isAssignableFrom(update.getDataAfter().getClass())) - .findFirst().get(); - } - - private ModificationDiff.NormalizedNodeUpdate getNormalizedNodeUpdateForBeforeType( - final Map updates, - final Class> containerNodeClass) { - return updates.values().stream() - .filter(update -> containerNodeClass.isAssignableFrom(update.getDataBefore().getClass())) - .findFirst().get(); - } - - @Test - public void testWriteDeepList() throws Exception { - final TipProducingDataTree dataTree = getDataTree(); - - DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); - DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); - - YangInstanceIdentifier listId = - YangInstanceIdentifier.create( - new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), - new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); - - MapNode mapNode = getNestedList("name1", "text"); - dataTreeModification.write(listId, mapNode); - - dataTreeModification.ready(); - dataTree.validate(dataTreeModification); - DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); - dataTree.commit(prepare); - - dataTreeSnapshot = dataTree.takeSnapshot(); - dataTreeModification = dataTreeSnapshot.newModification(); - - final YangInstanceIdentifier.NodeIdentifierWithPredicates nestedListNodeId = - new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1"); - listId = YangInstanceIdentifier.create( - new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), - new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME), - nestedListNodeId); - final YangInstanceIdentifier deepListId = - listId.node(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME)); - final YangInstanceIdentifier deepListEntryId = deepListId.node( - new YangInstanceIdentifier.NodeIdentifierWithPredicates(DEEP_LIST_QNAME, NAME_LEAF_QNAME, "name1")); - - final MapEntryNode deepListEntry = getDeepList("name1").getValue().iterator().next(); - // Merge parent list, just to see no modifications on it - dataTreeModification.merge( - listId, - Builders.mapEntryBuilder().withNodeIdentifier(nestedListNodeId) - .withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, "name1")).build()); - dataTreeModification.merge( - deepListId, - Builders.mapBuilder() - .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME)) - .build()); - dataTreeModification.merge( - deepListEntryId, - deepListEntry); - - dataTreeModification.ready(); - dataTree.validate(dataTreeModification); - prepare = dataTree.prepare(dataTreeModification); - dataTree.commit(prepare); - - final Map updates = getModificationDiff(prepare).getUpdates(); - assertThat(updates.size(), is(1)); - assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class), deepListEntryId, null, deepListEntry); - } - - @Test - public void testDeleteInnerListItem() throws Exception { - final TipProducingDataTree dataTree = getDataTree(); - - DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); - DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); - final YangInstanceIdentifier listId = - YangInstanceIdentifier.create( - new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), - new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); - - final MapNode mapNode = getNestedList("name1", "text"); - dataTreeModification.write(listId, mapNode); - dataTreeModification.ready(); - dataTree.validate(dataTreeModification); - DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); - - // Commit so that update can be tested next - dataTree.commit(prepare); - - YangInstanceIdentifier listItemId = listId.node( - new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1")); - - dataTreeSnapshot = dataTree.takeSnapshot(); - dataTreeModification = dataTreeSnapshot.newModification(); - dataTreeModification.delete(listItemId); - dataTreeModification.ready(); - dataTree.validate(dataTreeModification); - prepare = dataTree.prepare(dataTreeModification); - - final Map updates = getModificationDiff(prepare).getUpdates(); - assertThat(updates.size(), is(1)); - assertUpdate(getNormalizedNodeUpdateForBeforeType(updates, MapEntryNode.class), listItemId, mapNode.getValue().iterator().next(), null); - } - - static void addNodeToTree(final DataTree dataTree, final NormalizedNode node, - final YangInstanceIdentifier id) - throws DataValidationFailedException { - DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); - DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); - dataTreeModification.write(id, node); - dataTreeModification.ready(); - dataTree.validate(dataTreeModification); - DataTreeCandidate prepare = dataTree.prepare(dataTreeModification); - dataTree.commit(prepare); - } - - static TipProducingDataTree getDataTree() throws ReactorException { - final TipProducingDataTree dataTree = InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION); - dataTree.setSchemaContext(getSchemaCtx()); - return dataTree; - } - - static ContainerNode getTopContainer(final String stringValue) { - return Builders.containerBuilder() - .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME)) - .withChild(ImmutableNodes.leafNode(STRING_LEAF_QNAME, stringValue)) - .build(); - } - - static MapNode getNestedList(final String listItemName, final String text) { - return Builders.mapBuilder() - .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)) - .withChild( - Builders.mapEntryBuilder() - .withNodeIdentifier( - new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, - NAME_LEAF_QNAME, listItemName)) - .withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, listItemName)) - .withChild(ImmutableNodes.leafNode(TEXT_LEAF_QNAME, text)) - .build() - ) - .build(); - } - - private MapNode getDeepList(final String listItemName) { - return Builders.mapBuilder() - .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME)) - .withChild( - Builders.mapEntryBuilder() - .withNodeIdentifier( - new YangInstanceIdentifier.NodeIdentifierWithPredicates(DEEP_LIST_QNAME, - NAME_LEAF_QNAME, listItemName)) - .withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, listItemName)) - .build() - ) - .build(); - } - - private static SchemaContext getSchemaCtx() throws ReactorException { - final CrossSourceStatementReactor.BuildAction buildAction = YangInferencePipeline.RFC6020_REACTOR.newBuild(); - buildAction.addSource(new YangStatementSourceImpl(ModificationDiffTest.class.getResourceAsStream("/test-diff.yang"))); - return buildAction.buildEffective(); - } -} \ No newline at end of file diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapterTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapterTest.java deleted file mode 100644 index 523d9dd70..000000000 --- a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapterTest.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import java.nio.file.Files; -import java.nio.file.Path; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.opendaylight.controller.sal.core.api.model.SchemaService; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; - -public class PersistingDataTreeAdapterTest { - - @Mock - private DataTree delegatingDataTree; - @Mock - private SchemaService schemaService; - @Mock - private DataTreeSnapshot snapshot; - - private Path tmpPersistFile; - - private PersistingDataTreeAdapter persistingDataTreeAdapter; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - tmpPersistFile = Files.createTempFile("testing-hc-persistence", "json"); - persistingDataTreeAdapter = new PersistingDataTreeAdapter(delegatingDataTree, schemaService, tmpPersistFile); - } - - @Test - public void testNoPersistOnFailure() throws Exception { - doThrow(new IllegalStateException("testing errors")).when(delegatingDataTree).commit(any(DataTreeCandidate.class)); - - try { - persistingDataTreeAdapter.commit(null); - fail("Exception expected"); - } catch (IllegalStateException e) { - verify(delegatingDataTree, times(0)).takeSnapshot(); - verify(delegatingDataTree).commit(any(DataTreeCandidate.class)); - } - } - -} \ No newline at end of file diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java deleted file mode 100644 index a13621725..000000000 --- a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import static org.junit.Assert.assertNotNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.initMocks; - -import com.google.common.base.Optional; -import com.google.common.util.concurrent.CheckedFuture; -import io.fd.honeycomb.v3po.data.ReadableDataManager; -import io.fd.honeycomb.v3po.data.DataModification; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; - -public class ReadOnlyTransactionTest { - - @Mock - private ReadableDataManager operationalData; - @Mock - private DataModification configSnapshot; - - private ReadOnlyTransaction readOnlyTx; - - @Before - public void setUp() { - initMocks(this); - readOnlyTx = ReadOnlyTransaction.create(configSnapshot, operationalData); - } - - @Test - public void testExists() { - final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class); - final CheckedFuture>, ReadFailedException> - future = mock(CheckedFuture.class); - when(operationalData.read(path)).thenReturn(future); - - readOnlyTx.exists(LogicalDatastoreType.OPERATIONAL, path); - - verify(operationalData).read(path); - } - - @Test - public void testGetIdentifier() { - assertNotNull(readOnlyTx.getIdentifier()); - } -} \ No newline at end of file diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadWriteTransactionTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadWriteTransactionTest.java deleted file mode 100644 index 1b67cd967..000000000 --- a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadWriteTransactionTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import static org.junit.Assert.assertNotNull; -import static org.mockito.Mockito.verify; -import static org.mockito.MockitoAnnotations.initMocks; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; -import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; - -public class ReadWriteTransactionTest { - - @Mock - private DOMDataReadOnlyTransaction readTx; - - @Mock - private DOMDataWriteTransaction writeTx; - - private LogicalDatastoreType store; - - @Mock - private YangInstanceIdentifier path; - - @Mock - private NormalizedNode data; - - private ReadWriteTransaction readWriteTx; - - @Before - public void setUp() { - initMocks(this); - store = LogicalDatastoreType.CONFIGURATION; - readWriteTx = new ReadWriteTransaction(readTx, writeTx); - } - - @Test - public void testCancel() { - readWriteTx.cancel(); - verify(writeTx).cancel(); - } - - @Test - public void testPut() { - readWriteTx.put(store, path, data); - verify(writeTx).put(store, path, data); - } - - @Test - public void testMerge() { - readWriteTx.merge(store, path, data); - verify(writeTx).merge(store, path, data); - } - - @Test - public void testDelete() { - readWriteTx.delete(store, path); - verify(writeTx).delete(store, path); - } - - @Test - public void testSubmit() throws Exception { - readWriteTx.submit(); - verify(writeTx).submit(); - } - - - @SuppressWarnings("deprecation") - @Test - public void testCommit() throws Exception { - readWriteTx.commit(); - verify(writeTx).commit(); - } - - @Test - public void testRead() { - readWriteTx.read(store, path); - verify(readTx).read(store, path); - } - - @Test - public void testExists() { - readWriteTx.exists(store, path); - verify(readTx).exists(store, path); - } - - @Test - public void testGetIdentifier() throws Exception { - assertNotNull(readWriteTx.getIdentifier()); - } -} \ No newline at end of file diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegatorTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegatorTest.java deleted file mode 100644 index 455050ab0..000000000 --- a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegatorTest.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.same; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.initMocks; - -import com.google.common.base.Optional; -import com.google.common.collect.Iterables; -import com.google.common.collect.LinkedListMultimap; -import com.google.common.collect.Multimap; -import com.google.common.util.concurrent.CheckedFuture; -import com.google.common.util.concurrent.Futures; -import io.fd.honeycomb.v3po.translate.read.ReadContext; -import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistry; -import java.util.Collections; -import java.util.Map; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.opendaylight.controller.md.sal.binding.api.DataBroker; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; -import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; -import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; -import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; - -public class ReadableDataTreeDelegatorTest { - - @Mock - private BindingNormalizedNodeSerializer serializer; - @Mock - private ReaderRegistry reader; - - private ReadableDataTreeDelegator operationalData; - - @Mock - private InstanceIdentifier id; - @Mock - private Map.Entry> entry; - @Mock - private SchemaContext globalContext; - @Mock - private DataSchemaNode schemaNode; - @Mock - private ReadContext readCtx; - @Mock - private DOMDataBroker netconfMonitoringBroker; - @Mock - private DOMDataReadOnlyTransaction domDataReadOnlyTransaction; - @Mock - private DataBroker contextBroker; - - @Before - public void setUp() { - initMocks(this); - operationalData = new ReadableDataTreeDelegator(serializer, globalContext, reader, contextBroker); - doReturn(schemaNode).when(globalContext).getDataChildByName(any(QName.class)); - - doReturn(domDataReadOnlyTransaction).when(netconfMonitoringBroker).newReadOnlyTransaction(); - doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(domDataReadOnlyTransaction) - .read(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class)); - - final org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction ctxTransaction = mock( - org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction.class); - doReturn(ctxTransaction).when(contextBroker).newReadWriteTransaction(); - doReturn(Futures.immediateCheckedFuture(null)).when(ctxTransaction).submit(); - } - - @Test - public void testReadNode() throws Exception { - final YangInstanceIdentifier yangId = mock(YangInstanceIdentifier.class); - final YangInstanceIdentifier.PathArgument pArg = mock(YangInstanceIdentifier.PathArgument.class); - doReturn(pArg).when(yangId).getLastPathArgument(); - doReturn(Collections.singletonList(pArg)).when(yangId).getPathArguments(); - - doReturn(QName.create("namespace", "2012-12-12", "local")).when(pArg).getNodeType(); - doReturn(id).when(serializer).fromYangInstanceIdentifier(yangId); - - final DataObject dataObject = mock(DataObject.class); - doReturn(Optional.of(dataObject)).when(reader).read(same(id), any(ReadContext.class)); - - when(serializer.toNormalizedNode(id, dataObject)).thenReturn(entry); - final DataContainerChild expectedValue = mock(DataContainerChild.class); - doReturn(expectedValue).when(entry).getValue(); - - final CheckedFuture>, ReadFailedException> future = operationalData.read(yangId); - - verify(serializer).fromYangInstanceIdentifier(yangId); - verify(reader).read(same(id), any(ReadContext.class)); - final Optional> result = future.get(); - assertTrue(result.isPresent()); - assertEquals(expectedValue, result.get()); - } - - @Test - public void testReadNonExistingNode() throws Exception { - final YangInstanceIdentifier yangId = mock(YangInstanceIdentifier.class); - doReturn(id).when(serializer).fromYangInstanceIdentifier(yangId); - doReturn(Optional.absent()).when(reader).read(same(id), any(ReadContext.class)); - - final CheckedFuture>, ReadFailedException> future = operationalData.read(yangId); - - verify(serializer).fromYangInstanceIdentifier(yangId); - verify(reader).read(same(id), any(ReadContext.class)); - final Optional> result = future.get(); - assertFalse(result.isPresent()); - } - - @Test - public void testReadFailed() throws Exception { - doThrow(io.fd.honeycomb.v3po.translate.read.ReadFailedException.class).when(reader).readAll(any(ReadContext.class)); - - final CheckedFuture>, ReadFailedException> future = - operationalData.read( YangInstanceIdentifier.EMPTY); - - try { - future.checkedGet(); - } catch (ReadFailedException e) { - assertTrue(e.getCause() instanceof io.fd.honeycomb.v3po.translate.read.ReadFailedException); - return; - } - fail("ReadFailedException was expected"); - } - - @Test - public void testReadRootWithOneNonListElement() throws Exception { - // Prepare data - final InstanceIdentifier vppStateII = InstanceIdentifier.create(DataObject.class); - final DataObject vppState = mock(DataObject.class); - Multimap, DataObject> dataObjects = LinkedListMultimap.create(); - dataObjects.put(vppStateII, vppState); - doReturn(dataObjects).when(reader).readAll(any(ReadContext.class)); - - // Init serializer - final YangInstanceIdentifier vppYangId = YangInstanceIdentifier.builder().node(QName.create("n", "d")).build(); - when(serializer.toYangInstanceIdentifier(vppStateII)).thenReturn(vppYangId); - when(serializer.toNormalizedNode(vppStateII, vppState)).thenReturn(entry); - final DataContainerChild vppStateContainer = mock(DataContainerChild.class); - doReturn(vppStateContainer).when(entry).getValue(); - doReturn(vppYangId.getLastPathArgument()).when(vppStateContainer).getIdentifier(); - - // Read root - final CheckedFuture>, ReadFailedException> future = - operationalData.read(YangInstanceIdentifier.EMPTY); - - verify(reader).readAll(any(ReadContext.class)); - verify(serializer).toYangInstanceIdentifier(vppStateII); - verify(serializer).toNormalizedNode(vppStateII, vppState); - - // Check the result is an ContainerNode with only one child - final Optional> result = future.get(); - assertTrue(result.isPresent()); - - final ContainerNode rootNode = (ContainerNode) result.get(); - assertEquals(SchemaContext.NAME, rootNode.getIdentifier().getNodeType()); - assertEquals(vppStateContainer, Iterables.getOnlyElement(rootNode.getValue())); - } -} \ No newline at end of file diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java deleted file mode 100644 index 9cde27d2b..000000000 --- a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import static junit.framework.TestCase.assertTrue; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.MockitoAnnotations.initMocks; - -import com.google.common.util.concurrent.CheckedFuture; -import io.fd.honeycomb.v3po.data.DataModification; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; - -public class WriteTransactionTest { - - @Mock - private DataModification configSnapshot; - @Mock - private YangInstanceIdentifier path; - @Mock - private NormalizedNode data; - - private WriteTransaction writeTx; - - @Before - public void setUp() { - initMocks(this); - writeTx = WriteTransaction.createConfigOnly(configSnapshot); - } - - @Test - public void testPut() { - writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data); - verify(configSnapshot).write(path, data); - } - - @Test(expected = IllegalArgumentException.class) - public void testPutOperational() { - writeTx.put(LogicalDatastoreType.OPERATIONAL, path, data); - verify(configSnapshot).write(path, data); - } - - @Test(expected = IllegalStateException.class) - public void testOnFinishedTx() { - writeTx.submit(); - writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data); - verify(configSnapshot).write(path, data); - } - - @Test - public void testMerge() { - writeTx.merge(LogicalDatastoreType.CONFIGURATION, path, data); - verify(configSnapshot).merge(path, data); - } - - @Test - public void testCancel() { - assertTrue(writeTx.cancel()); - } - - @Test - public void testCancelFinished() { - writeTx.submit(); - assertFalse(writeTx.cancel()); - } - - @Test - public void testDelete() { - writeTx.delete(LogicalDatastoreType.CONFIGURATION, path); - verify(configSnapshot).delete(path); - } - - @Test - public void testSubmit() throws Exception { - writeTx.submit(); - verify(configSnapshot).validate(); - verify(configSnapshot).commit(); - } - - @Test - public void testSubmitFailed() throws Exception { - doThrow(mock(DataValidationFailedException.class)).when(configSnapshot).commit(); - final CheckedFuture future = writeTx.submit(); - try { - future.get(); - } catch (Exception e) { - assertTrue(e.getCause() instanceof TransactionCommitFailedException); - return; - } - fail("Expected exception to be thrown"); - - } - - @Test(expected = UnsupportedOperationException.class) - public void testCommit() { - writeTx.commit(); - } - - @Test - public void testGetIdentifier() { - assertNotNull(writeTx.getIdentifier()); - } -} \ No newline at end of file diff --git a/infra/impl/src/main/config/context-datatree-config.xml b/infra/impl/src/main/config/context-datatree-config.xml index 313f0ebe6..4d43a40cf 100644 --- a/infra/impl/src/main/config/context-datatree-config.xml +++ b/infra/impl/src/main/config/context-datatree-config.xml @@ -19,9 +19,9 @@ - urn:opendaylight:params:xml:ns:yang:v3po:impl?module=v3po-impl&revision=2014-12-10 + urn:opendaylight:params:xml:ns:yang:honeycomb:impl?module=honeycomb-impl&revision=2014-12-10 urn:honeycomb:params:xml:ns:yang:translate:utils?module=translate-utils&revision=2016-04-06 - urn:honeycomb:params:xml:ns:yang:vpp:data:init?module=vpp-cfg-init&revision=2016-04-07 + urn:honeycomb:params:xml:ns:yang:data:init?module=cfg-init&revision=2016-04-07 urn:honeycomb:params:xml:ns:yang:data:api?module=data-api&revision=2016-04-11 urn:honeycomb:params:xml:ns:yang:data:impl?module=data-impl&revision=2016-04-11 urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28 @@ -60,7 +60,7 @@ - prefix:honeycomb-context-dom-data-broker + prefix:honeycomb-context-dom-data-broker honeycomb-context-data-broker diff --git a/infra/impl/src/main/config/default-config.xml b/infra/impl/src/main/config/default-config.xml index c817a5db3..9e63917cd 100644 --- a/infra/impl/src/main/config/default-config.xml +++ b/infra/impl/src/main/config/default-config.xml @@ -16,7 +16,7 @@ --> - urn:opendaylight:params:xml:ns:yang:v3po:impl?module=v3po-impl&revision=2014-12-10 + urn:opendaylight:params:xml:ns:yang:honeycomb:impl?module=honeycomb-impl&revision=2014-12-10 urn:honeycomb:params:xml:ns:yang:translate:utils?module=translate-utils&revision=2016-04-06 urn:honeycomb:params:xml:ns:yang:data:api?module=data-api&revision=2016-04-11 urn:honeycomb:params:xml:ns:yang:data:impl?module=data-impl&revision=2016-04-11 @@ -116,7 +116,7 @@ - prefix:honeycomb-dom-data-broker + prefix:honeycomb-dom-data-broker honeycomb-dom-data-broker prefix:honeycomb-modifiable-data-tree @@ -150,8 +150,8 @@ - prefix:v3po - v3po-default + prefix:honeycomb + honeycomb-default prefix:dom-broker-osgi-registry dom-broker @@ -242,8 +242,8 @@ dom:dom-broker-osgi-registry - vpp-dom-broker - /modules/module[type='v3po'][name='v3po-default'] + honeycomb-dom-broker + /modules/module[type='honeycomb'][name='honeycomb-default'] diff --git a/infra/impl/src/main/config/initializer-config.xml b/infra/impl/src/main/config/initializer-config.xml index f46069340..0b87e42dc 100644 --- a/infra/impl/src/main/config/initializer-config.xml +++ b/infra/impl/src/main/config/initializer-config.xml @@ -19,9 +19,9 @@ - urn:opendaylight:params:xml:ns:yang:v3po:impl?module=v3po-impl&revision=2014-12-10 + urn:opendaylight:params:xml:ns:yang:honeycomb:impl?module=honeycomb-impl&revision=2014-12-10 urn:honeycomb:params:xml:ns:yang:translate:utils?module=translate-utils&revision=2016-04-06 - urn:honeycomb:params:xml:ns:yang:vpp:data:init?module=vpp-cfg-init&revision=2016-04-07 + urn:honeycomb:params:xml:ns:yang:data:init?module=cfg-init&revision=2016-04-07 urn:honeycomb:params:xml:ns:yang:data:api?module=data-api&revision=2016-04-11 urn:honeycomb:params:xml:ns:yang:data:impl?module=data-impl&revision=2016-04-11 urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28 @@ -60,7 +60,7 @@ - prefix:honeycomb-dom-data-broker + prefix:honeycomb-dom-data-broker cfg-init-dom-data-broker prefix:honeycomb-modifiable-data-tree @@ -92,7 +92,7 @@ - prefix:persisted-file-initializer + prefix:persisted-file-initializer persisted-context-initializer dom:dom-async-data-broker @@ -107,7 +107,7 @@ oper - prefix:persisted-file-initializer + prefix:persisted-file-initializer persisted-config-initializer prefix:dom-async-data-broker @@ -123,14 +123,14 @@ - prefix:cfg-initializer-registry + prefix:cfg-initializer-registry initializer-registry - prefix:cfg-initializer + prefix:cfg-initializer persisted-context-initializer - prefix:cfg-initializer + prefix:cfg-initializer persisted-config-initializer @@ -170,7 +170,7 @@ - prefix:cfg-initializer + prefix:cfg-initializer persisted-context-initializer /modules/module[type='persisted-file-initializer'][name='persisted-context-initializer'] diff --git a/infra/impl/src/main/config/netconf-north-config.xml b/infra/impl/src/main/config/netconf-north-config.xml index 35bd5ff04..8e6f17019 100644 --- a/infra/impl/src/main/config/netconf-north-config.xml +++ b/infra/impl/src/main/config/netconf-north-config.xml @@ -83,7 +83,7 @@ - prefix:binding-broker-netconf + prefix:binding-broker-netconf binding-broker-netconf binding:binding-async-data-broker @@ -96,7 +96,7 @@ prefix:netconf-mdsal-mapper - netconf-vpp-mapper + netconf-honeycomb-mapper dom:schema-service @@ -112,13 +112,13 @@ dom:dom-broker-osgi-registry - vpp-dom-broker + honeycomb-dom-broker prefix:netconf-mapper-registry - vpp-mapper-aggregator-registry + honeycomb-mapper-aggregator-registry @@ -126,19 +126,19 @@ prefix:netconf-server-dispatcher-impl - netconf-vpp-server-dispatcher + netconf-honeycomb-server-dispatcher dom:netconf-northbound-mapper - vpp-mapper-aggregator + honeycomb-mapper-aggregator prefix:netconf-server-monitoring - vpp-server-monitor + honeycomb-server-monitor @@ -165,12 +165,12 @@ prefix:netconf-mdsal-monitoring-mapper - netconf-vpp-monitoring-mapper + netconf-honeycomb-monitoring-mapper prefix:netconf-server-monitoring - vpp-server-monitor + honeycomb-server-monitor @@ -183,7 +183,7 @@ prefix:netconf-mapper-registry - vpp-mapper-aggregator-registry + honeycomb-mapper-aggregator-registry @@ -191,19 +191,19 @@ prefix:netconf-mapper-aggregator - vpp-mapper-aggregator + honeycomb-mapper-aggregator prefix:netconf-server-monitoring-impl - vpp-server-monitor + honeycomb-server-monitor dom:netconf-northbound-mapper - vpp-mapper-aggregator + honeycomb-mapper-aggregator @@ -218,7 +218,7 @@ prefix:netconf-northbound-ssh - netconf-vpp-ssh-server + netconf-honeycomb-ssh-server 2830 @@ -243,7 +243,7 @@ prefix:netconf-server-dispatcher - netconf-vpp-server-dispatcher + netconf-honeycomb-server-dispatcher @@ -257,14 +257,14 @@ prefix:netconf-notification-manager - vpp-netconf-notification-manager + honeycomb-netconf-notification-manager prefix:netconf-mdsal-notification-mapper - netconf-vpp-notification-mapper + netconf-honeycomb-notification-mapper @@ -284,20 +284,20 @@ prefix:netconf-mapper-registry - vpp-mapper-aggregator-registry + honeycomb-mapper-aggregator-registry prefix:netconf-notification-registry - vpp-netconf-notification-manager + honeycomb-netconf-notification-manager prefix:netconf-notification-collector - vpp-netconf-notification-manager + honeycomb-netconf-notification-manager @@ -307,12 +307,12 @@ prefix:netconf-northbound-tcp - netconf-vpp-tcp-server + netconf-honeycomb-tcp-server prefix:netconf-server-dispatcher - netconf-vpp-server-dispatcher + netconf-honeycomb-server-dispatcher 7777 @@ -321,7 +321,7 @@ However netconf is wired to our reader registry, so we need to delegate the reads of netconf-monitoring through our readers to the dedicated DS--> - prefix:netconf-monitoring-reader + prefix:netconf-monitoring-reader netconf-monitoring-reader binding:binding-async-data-broker @@ -394,8 +394,8 @@ prefix:netconf-server-monitoring - vpp-server-monitor - /modules/module[type='netconf-server-monitoring-impl'][name='vpp-server-monitor'] + honeycomb-server-monitor + /modules/module[type='netconf-server-monitoring-impl'][name='honeycomb-server-monitor'] @@ -404,8 +404,8 @@ prefix:netconf-northbound-mapper - netconf-vpp-mapper - /modules/module[type='netconf-mdsal-mapper'][name='netconf-vpp-mapper'] + netconf-honeycomb-mapper + /modules/module[type='netconf-mdsal-mapper'][name='netconf-honeycomb-mapper'] @@ -413,8 +413,8 @@ prefix:netconf-northbound-mapper - vpp-mapper-aggregator - /modules/module[type='netconf-mapper-aggregator'][name='vpp-mapper-aggregator'] + honeycomb-mapper-aggregator + /modules/module[type='netconf-mapper-aggregator'][name='honeycomb-mapper-aggregator'] @@ -422,8 +422,8 @@ prefix:netconf-mapper-registry - vpp-mapper-aggregator-registry - /modules/module[type='netconf-mapper-aggregator'][name='vpp-mapper-aggregator'] + honeycomb-mapper-aggregator-registry + /modules/module[type='netconf-mapper-aggregator'][name='honeycomb-mapper-aggregator'] @@ -431,9 +431,9 @@ prefix:netconf-server-dispatcher - netconf-vpp-server-dispatcher + netconf-honeycomb-server-dispatcher - /modules/module[type='netconf-server-dispatcher-impl'][name='netconf-vpp-server-dispatcher'] + /modules/module[type='netconf-server-dispatcher-impl'][name='netconf-honeycomb-server-dispatcher'] @@ -442,9 +442,9 @@ prefix:netconf-northbound-mapper - netconf-vpp-notification-mapper + netconf-honeycomb-notification-mapper - /modules/module[type='netconf-mdsal-notification-mapper'][name='netconf-vpp-notification-mapper'] + /modules/module[type='netconf-mdsal-notification-mapper'][name='netconf-honeycomb-notification-mapper'] @@ -453,9 +453,9 @@ prefix:netconf-notification-collector - vpp-netconf-notification-manager + honeycomb-netconf-notification-manager - /modules/module[type='netconf-notification-manager'][name='vpp-netconf-notification-manager'] + /modules/module[type='netconf-notification-manager'][name='honeycomb-netconf-notification-manager'] @@ -464,9 +464,9 @@ prefix:netconf-notification-registry - vpp-netconf-notification-manager + honeycomb-netconf-notification-manager - /modules/module[type='netconf-notification-manager'][name='vpp-netconf-notification-manager'] + /modules/module[type='netconf-notification-manager'][name='honeycomb-netconf-notification-manager'] @@ -475,7 +475,7 @@ - urn:opendaylight:params:xml:ns:yang:v3po:impl?module=v3po-impl&revision=2014-12-10 + urn:opendaylight:params:xml:ns:yang:honeycomb:impl?module=honeycomb-impl&revision=2014-12-10 urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28 urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:mapper?module=netconf-mdsal-mapper&revision=2015-01-14 diff --git a/infra/impl/src/main/config/restconf-north-config.xml b/infra/impl/src/main/config/restconf-north-config.xml index ff2cdac18..00599b62c 100644 --- a/infra/impl/src/main/config/restconf-north-config.xml +++ b/infra/impl/src/main/config/restconf-north-config.xml @@ -24,7 +24,7 @@ dom:dom-broker-osgi-registry - vpp-dom-broker + honeycomb-dom-broker diff --git a/infra/impl/src/main/java/io/fd/honeycomb/impl/NorthboundFacadeHoneycombDOMBroker.java b/infra/impl/src/main/java/io/fd/honeycomb/impl/NorthboundFacadeHoneycombDOMBroker.java new file mode 100644 index 000000000..7e5dfd873 --- /dev/null +++ b/infra/impl/src/main/java/io/fd/honeycomb/impl/NorthboundFacadeHoneycombDOMBroker.java @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.impl; + +import com.google.common.base.Optional; +import com.google.common.collect.Maps; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import java.util.Map; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.NotThreadSafe; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint; +import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService; +import org.opendaylight.controller.md.sal.dom.api.DOMNotificationPublishService; +import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcAvailabilityListener; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; +import org.opendaylight.controller.sal.core.api.Broker; +import org.opendaylight.controller.sal.core.api.BrokerService; +import org.opendaylight.controller.sal.core.api.Consumer; +import org.opendaylight.controller.sal.core.api.Provider; +import org.opendaylight.controller.sal.core.api.model.SchemaService; +import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.osgi.framework.BundleContext; + +/** + * Implementation of dom broker to facade VPP pipeline for northbound APIs + */ +public class NorthboundFacadeHoneycombDOMBroker implements AutoCloseable, Broker { + + private static final BrokerService EMPTY_DOM_RPC_SERVICE = new EmptyDomRpcService(); + private static final BrokerService EMPTY_DOM_MOUNT_SERVICE = new EmptyDomMountService(); + + private Map, BrokerService> services; + + public NorthboundFacadeHoneycombDOMBroker(@Nonnull final DOMDataBroker domDataBrokerDependency, + @Nonnull final SchemaService schemaBiService, + @Nonnull final DOMNotificationService domNotificatioNService) { + services = Maps.newHashMap(); + services.put(DOMDataBroker.class, domDataBrokerDependency); + // All services below are required to be present by Restconf northbound + services.put(SchemaService.class, schemaBiService); + services.put(DOMRpcService.class, EMPTY_DOM_RPC_SERVICE); + services.put(DOMMountPointService.class, EMPTY_DOM_MOUNT_SERVICE); + services.put(DOMNotificationService.class, domNotificatioNService); + // TODO do both notification service types have to be registered ? + services.put(DOMNotificationPublishService.class, domNotificatioNService); + } + + @Override + public void close() throws Exception { + // NOOP + } + + @Override + public ConsumerSession registerConsumer(final Consumer consumer) { + final SimpleConsumerSession session = new SimpleConsumerSession(services); + consumer.onSessionInitiated(session); + return session; + } + + @Deprecated + @Override + public ConsumerSession registerConsumer(final Consumer consumer, final BundleContext bundleContext) { + throw new UnsupportedOperationException(); + } + + @Override + public ProviderSession registerProvider(final Provider provider) { + final SimpleProviderSession session = new SimpleProviderSession(services); + provider.onSessionInitiated(session); + return session; + } + + @Override + public ProviderSession registerProvider(final Provider provider, final BundleContext bundleContext) { + throw new UnsupportedOperationException(); + } + + @NotThreadSafe + private static class SimpleConsumerSession implements ConsumerSession { + private boolean closed; + private final Map, BrokerService> services; + + private SimpleConsumerSession(final Map, BrokerService> services) { + this.services = services; + } + + @Override + public boolean isClosed() { + return closed; + } + + @Override + public T getService(final Class aClass) { + return (T)services.get(aClass); + } + + @Override + public void close() { + closed = true; + } + } + + @NotThreadSafe + private static class SimpleProviderSession implements ProviderSession { + private boolean closed; + private final Map, BrokerService> services; + + private SimpleProviderSession(final Map, BrokerService> services) { + this.services = services; + } + + @Override + public boolean isClosed() { + return closed; + } + + @Override + public T getService(final Class aClass) { + return (T)services.get(aClass); + } + + @Override + public void close() { + closed = true; + } + } + + private static class EmptyDomRpcService implements DOMRpcService { + @Nonnull + @Override + public CheckedFuture invokeRpc(@Nonnull final SchemaPath schemaPath, + @Nullable final NormalizedNode normalizedNode) { + return Futures.immediateFailedCheckedFuture( + new DOMRpcImplementationNotAvailableException("RPCs not supported")); + } + + @Nonnull + @Override + public ListenerRegistration registerRpcListener(@Nonnull final T t) { + return new ListenerRegistration() { + @Override + public void close() { + // Noop + } + + @Override + public T getInstance() { + return t; + } + }; + } + } + + private static class EmptyDomMountService implements DOMMountPointService { + @Override + public Optional getMountPoint(final YangInstanceIdentifier yangInstanceIdentifier) { + return Optional.absent(); + } + + @Override + public DOMMountPointBuilder createMountPoint(final YangInstanceIdentifier yangInstanceIdentifier) { + throw new UnsupportedOperationException("No mountpoint support"); + } + + @Override + public ListenerRegistration registerProvisionListener( + final MountProvisionListener mountProvisionListener) { + return new ListenerRegistration() { + @Override + public void close() { + // Noop + } + + @Override + public MountProvisionListener getInstance() { + return mountProvisionListener; + } + }; + } + } +} diff --git a/infra/impl/src/main/java/io/fd/honeycomb/v3po/impl/NorthboundFacadeHoneycombDOMBroker.java b/infra/impl/src/main/java/io/fd/honeycomb/v3po/impl/NorthboundFacadeHoneycombDOMBroker.java deleted file mode 100644 index c2d70c38c..000000000 --- a/infra/impl/src/main/java/io/fd/honeycomb/v3po/impl/NorthboundFacadeHoneycombDOMBroker.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (c) 2015 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.impl; - -import com.google.common.base.Optional; -import com.google.common.collect.Maps; -import com.google.common.util.concurrent.CheckedFuture; -import com.google.common.util.concurrent.Futures; -import java.util.Map; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.concurrent.NotThreadSafe; -import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; -import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint; -import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService; -import org.opendaylight.controller.md.sal.dom.api.DOMNotificationPublishService; -import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService; -import org.opendaylight.controller.md.sal.dom.api.DOMRpcAvailabilityListener; -import org.opendaylight.controller.md.sal.dom.api.DOMRpcException; -import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException; -import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; -import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; -import org.opendaylight.controller.sal.core.api.Broker; -import org.opendaylight.controller.sal.core.api.BrokerService; -import org.opendaylight.controller.sal.core.api.Consumer; -import org.opendaylight.controller.sal.core.api.Provider; -import org.opendaylight.controller.sal.core.api.model.SchemaService; -import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener; -import org.opendaylight.yangtools.concepts.ListenerRegistration; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.model.api.SchemaPath; -import org.osgi.framework.BundleContext; - -/** - * Implementation of dom broker to facade VPP pipeline for northbound APIs - */ -public class NorthboundFacadeHoneycombDOMBroker implements AutoCloseable, Broker { - - private static final BrokerService EMPTY_DOM_RPC_SERVICE = new EmptyDomRpcService(); - private static final BrokerService EMPTY_DOM_MOUNT_SERVICE = new EmptyDomMountService(); - - private Map, BrokerService> services; - - public NorthboundFacadeHoneycombDOMBroker(@Nonnull final DOMDataBroker domDataBrokerDependency, - @Nonnull final SchemaService schemaBiService, - @Nonnull final DOMNotificationService domNotificatioNService) { - services = Maps.newHashMap(); - services.put(DOMDataBroker.class, domDataBrokerDependency); - // All services below are required to be present by Restconf northbound - services.put(SchemaService.class, schemaBiService); - services.put(DOMRpcService.class, EMPTY_DOM_RPC_SERVICE); - services.put(DOMMountPointService.class, EMPTY_DOM_MOUNT_SERVICE); - services.put(DOMNotificationService.class, domNotificatioNService); - // TODO do both notification service types have to be registered ? - services.put(DOMNotificationPublishService.class, domNotificatioNService); - } - - @Override - public void close() throws Exception { - // NOOP - } - - @Override - public ConsumerSession registerConsumer(final Consumer consumer) { - final SimpleConsumerSession session = new SimpleConsumerSession(services); - consumer.onSessionInitiated(session); - return session; - } - - @Deprecated - @Override - public ConsumerSession registerConsumer(final Consumer consumer, final BundleContext bundleContext) { - throw new UnsupportedOperationException(); - } - - @Override - public ProviderSession registerProvider(final Provider provider) { - final SimpleProviderSession session = new SimpleProviderSession(services); - provider.onSessionInitiated(session); - return session; - } - - @Override - public ProviderSession registerProvider(final Provider provider, final BundleContext bundleContext) { - throw new UnsupportedOperationException(); - } - - @NotThreadSafe - private static class SimpleConsumerSession implements ConsumerSession { - private boolean closed; - private final Map, BrokerService> services; - - private SimpleConsumerSession(final Map, BrokerService> services) { - this.services = services; - } - - @Override - public boolean isClosed() { - return closed; - } - - @Override - public T getService(final Class aClass) { - return (T)services.get(aClass); - } - - @Override - public void close() { - closed = true; - } - } - - @NotThreadSafe - private static class SimpleProviderSession implements ProviderSession { - private boolean closed; - private final Map, BrokerService> services; - - private SimpleProviderSession(final Map, BrokerService> services) { - this.services = services; - } - - @Override - public boolean isClosed() { - return closed; - } - - @Override - public T getService(final Class aClass) { - return (T)services.get(aClass); - } - - @Override - public void close() { - closed = true; - } - } - - private static class EmptyDomRpcService implements DOMRpcService { - @Nonnull - @Override - public CheckedFuture invokeRpc(@Nonnull final SchemaPath schemaPath, - @Nullable final NormalizedNode normalizedNode) { - return Futures.immediateFailedCheckedFuture( - new DOMRpcImplementationNotAvailableException("RPCs not supported")); - } - - @Nonnull - @Override - public ListenerRegistration registerRpcListener(@Nonnull final T t) { - return new ListenerRegistration() { - @Override - public void close() { - // Noop - } - - @Override - public T getInstance() { - return t; - } - }; - } - } - - private static class EmptyDomMountService implements DOMMountPointService { - @Override - public Optional getMountPoint(final YangInstanceIdentifier yangInstanceIdentifier) { - return Optional.absent(); - } - - @Override - public DOMMountPointBuilder createMountPoint(final YangInstanceIdentifier yangInstanceIdentifier) { - throw new UnsupportedOperationException("No mountpoint support"); - } - - @Override - public ListenerRegistration registerProvisionListener( - final MountProvisionListener mountProvisionListener) { - return new ListenerRegistration() { - @Override - public void close() { - // Noop - } - - @Override - public MountProvisionListener getInstance() { - return mountProvisionListener; - } - }; - } - } -} diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/ContextDataBrokerModule.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/ContextDataBrokerModule.java new file mode 100644 index 000000000..c2f7d688a --- /dev/null +++ b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/ContextDataBrokerModule.java @@ -0,0 +1,26 @@ +package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.honeycomb.impl.rev141210; + +import io.fd.honeycomb.data.impl.DataBroker; +import io.fd.honeycomb.data.impl.ModifiableDataTreeManager; + +public class ContextDataBrokerModule extends AbstractContextDataBrokerModule { + + public ContextDataBrokerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public ContextDataBrokerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, ContextDataBrokerModule oldModule, java.lang.AutoCloseable oldInstance) { + super(identifier, dependencyResolver, oldModule, oldInstance); + } + + @Override + public void customValidation() { + // add custom validation form module attributes here. + } + + @Override + public java.lang.AutoCloseable createInstance() { + return DataBroker.create(new ModifiableDataTreeManager(getContextDataTreeDependency())); + } + +} diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/ContextDataBrokerModuleFactory.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/ContextDataBrokerModuleFactory.java new file mode 100644 index 000000000..e07214b2e --- /dev/null +++ b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/ContextDataBrokerModuleFactory.java @@ -0,0 +1,13 @@ +/* +* Generated file +* +* Generated from: yang module name: v3po-impl yang module local name: honeycomb-context-dom-data-broker +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Mon May 16 15:27:12 CEST 2016 +* +* Do not modify this file unless it is present under src/main directory +*/ +package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.honeycomb.impl.rev141210; +public class ContextDataBrokerModuleFactory extends AbstractContextDataBrokerModuleFactory { + +} diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/DataBrokerModule.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/DataBrokerModule.java new file mode 100644 index 000000000..d97573e12 --- /dev/null +++ b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/DataBrokerModule.java @@ -0,0 +1,34 @@ +package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.honeycomb.impl.rev141210; + +import io.fd.honeycomb.data.impl.DataBroker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DataBrokerModule extends + AbstractDataBrokerModule { + + private static final Logger LOG = LoggerFactory.getLogger(DataBrokerModule.class); + + public DataBrokerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, + org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public DataBrokerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, + org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, + DataBrokerModule oldModule, + java.lang.AutoCloseable oldInstance) { + super(identifier, dependencyResolver, oldModule, oldInstance); + } + + @Override + public void customValidation() { + // add custom validation form module attributes here. + } + + @Override + public java.lang.AutoCloseable createInstance() { + LOG.debug("DataBrokerModule.createInstance()"); + return DataBroker.create(getConfigDataTreeDependency(), getOperationalDataTreeDependency()); + } +} diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/DataBrokerModuleFactory.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/DataBrokerModuleFactory.java new file mode 100644 index 000000000..388f26c67 --- /dev/null +++ b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/DataBrokerModuleFactory.java @@ -0,0 +1,13 @@ +/* +* Generated file +* +* Generated from: yang module name: v3po-impl yang module local name: honeycomb-dom-data-broker +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Mon Apr 11 07:53:38 CEST 2016 +* +* Do not modify this file unless it is present under src/main directory +*/ +package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.honeycomb.impl.rev141210; +public class DataBrokerModuleFactory extends AbstractDataBrokerModuleFactory { + +} diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/HoneycombModule.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/HoneycombModule.java new file mode 100644 index 000000000..333219518 --- /dev/null +++ b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/HoneycombModule.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.honeycomb.impl.rev141210; + +import io.fd.honeycomb.impl.NorthboundFacadeHoneycombDOMBroker; +import org.opendaylight.controller.sal.core.api.AbstractProvider; +import org.opendaylight.controller.sal.core.api.Broker; +import org.opendaylight.controller.sal.core.api.model.SchemaService; + +public class HoneycombModule extends AbstractHoneycombModule { + + public HoneycombModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, + org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public HoneycombModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, + org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, + HoneycombModule oldModule, + java.lang.AutoCloseable oldInstance) { + super(identifier, dependencyResolver, oldModule, oldInstance); + } + + @Override + public void customValidation() { + // add custom validation form module attributes here + } + + @Override + public java.lang.AutoCloseable createInstance() { + + final Broker.ProviderSession providerSession = + getDomBrokerDependency().registerProvider(new AbstractProvider() { + @Override + public void onSessionInitiated(final Broker.ProviderSession providerSession) { + // NOOP + } + }); + final SchemaService schemaBiService = providerSession.getService(SchemaService.class); + + return new NorthboundFacadeHoneycombDOMBroker(getHoneycombDomDataBrokerDependency(), schemaBiService, + getHoneycombDomNotificationServiceDependency()); + } + +} diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/HoneycombModuleFactory.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/HoneycombModuleFactory.java new file mode 100644 index 000000000..2e7b0956f --- /dev/null +++ b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/HoneycombModuleFactory.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* +* Generated file +* +* Generated from: yang module name: v3po yang module local name: v3po +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Fri Jan 02 13:49:24 CST 2015 +* +* Do not modify this file unless it is present under src/main directory +*/ +package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.honeycomb.impl.rev141210; +public class HoneycombModuleFactory extends AbstractHoneycombModuleFactory { + +} diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/NetconfBindingBrokerModule.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/NetconfBindingBrokerModule.java new file mode 100644 index 000000000..12fdcfe35 --- /dev/null +++ b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/NetconfBindingBrokerModule.java @@ -0,0 +1,117 @@ +package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.honeycomb.impl.rev141210; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; +import org.opendaylight.controller.sal.binding.api.BindingAwareConsumer; +import org.opendaylight.controller.sal.binding.api.BindingAwareProvider; +import org.opendaylight.controller.sal.binding.api.BindingAwareService; +import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.RpcService; +import org.osgi.framework.BundleContext; + +public class NetconfBindingBrokerModule extends AbstractNetconfBindingBrokerModule { + public NetconfBindingBrokerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public NetconfBindingBrokerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, NetconfBindingBrokerModule oldModule, java.lang.AutoCloseable oldInstance) { + super(identifier, dependencyResolver, oldModule, oldInstance); + } + + @Override + public void customValidation() { + // add custom validation form module attributes here. + } + + @Override + public java.lang.AutoCloseable createInstance() { + return new FakeBindingAwareBroker(getNetconfBindingBrokerDependency()); + } + + private static class FakeBindingAwareBroker implements BindingAwareBroker, AutoCloseable { + + private DataBroker netconfBindingBrokerDependency; + + public FakeBindingAwareBroker(final DataBroker netconfBindingBrokerDependency) { + + this.netconfBindingBrokerDependency = netconfBindingBrokerDependency; + } + + @Deprecated + @Override + public ConsumerContext registerConsumer(final BindingAwareConsumer bindingAwareConsumer, + final BundleContext bundleContext) { + throw new UnsupportedOperationException("Unsupported"); + } + + @Override + public ConsumerContext registerConsumer(final BindingAwareConsumer bindingAwareConsumer) { + final ConsumerContext consumerContext = new ConsumerContext() { + @Override + public T getSALService(final Class aClass) { + return aClass.equals(DataBroker.class) + ? (T) netconfBindingBrokerDependency + : null; + } + + @Override + public T getRpcService(final Class aClass) { + return null; + } + }; + bindingAwareConsumer.onSessionInitialized(consumerContext); + return consumerContext; + } + + @Override + public ProviderContext registerProvider(final BindingAwareProvider bindingAwareProvider, + final BundleContext bundleContext) { + throw new UnsupportedOperationException("Unsupported"); + } + + @Override + public ProviderContext registerProvider(final BindingAwareProvider bindingAwareProvider) { + final ProviderContext context = new ProviderContext() { + @Override + public >> ListenerRegistration registerRouteChangeListener( + final L l) { + throw new UnsupportedOperationException("Unsupported"); + } + + @Override + public T getRpcService(final Class aClass) { + throw new UnsupportedOperationException("Unsupported"); + } + + @Override + public RpcRegistration addRpcImplementation(final Class aClass, final T t) + throws IllegalStateException { + throw new UnsupportedOperationException("Unsupported"); + } + + @Override + public RoutedRpcRegistration addRoutedRpcImplementation( + final Class aClass, final T t) throws IllegalStateException { + throw new UnsupportedOperationException("Unsupported"); + } + + @Override + public T getSALService(final Class aClass) { + return aClass.equals(DataBroker.class) + ? (T) netconfBindingBrokerDependency + : null; } + }; + bindingAwareProvider.onSessionInitiated(context); + return context; + } + + @Override + public void close() throws Exception { + netconfBindingBrokerDependency = null; + } + } +} + diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/NetconfBindingBrokerModuleFactory.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/NetconfBindingBrokerModuleFactory.java new file mode 100644 index 000000000..fe9bdcf48 --- /dev/null +++ b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/NetconfBindingBrokerModuleFactory.java @@ -0,0 +1,13 @@ +/* +* Generated file +* +* Generated from: yang module name: v3po-impl yang module local name: binding-broker-netconf +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Wed Mar 23 10:45:48 CET 2016 +* +* Do not modify this file unless it is present under src/main directory +*/ +package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.honeycomb.impl.rev141210; +public class NetconfBindingBrokerModuleFactory extends AbstractNetconfBindingBrokerModuleFactory { + +} diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/NetconfMonitoringReaderModule.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/NetconfMonitoringReaderModule.java new file mode 100644 index 000000000..5b9a8b819 --- /dev/null +++ b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/NetconfMonitoringReaderModule.java @@ -0,0 +1,46 @@ +package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.honeycomb.impl.rev141210; + +import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder; +import io.fd.honeycomb.translate.util.read.BindingBrokerReader; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfStateBuilder; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class NetconfMonitoringReaderModule extends AbstractNetconfMonitoringReaderModule { + public NetconfMonitoringReaderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public NetconfMonitoringReaderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, NetconfMonitoringReaderModule oldModule, java.lang.AutoCloseable oldInstance) { + super(identifier, dependencyResolver, oldModule, oldInstance); + } + + @Override + public void customValidation() { + // add custom validation form module attributes here. + } + + @Override + public java.lang.AutoCloseable createInstance() { + return new ReaderFactory(getNetconfMonitoringBindingBrokerDependency()); + } + + + private static final class ReaderFactory implements AutoCloseable, io.fd.honeycomb.translate.read.ReaderFactory { + + private final DataBroker netconfMonitoringBindingBrokerDependency; + + public ReaderFactory(final DataBroker netconfMonitoringBindingBrokerDependency) { + this.netconfMonitoringBindingBrokerDependency = netconfMonitoringBindingBrokerDependency; + } + + @Override + public void init(final ModifiableReaderRegistryBuilder registry) { + registry.add(new BindingBrokerReader<>(InstanceIdentifier.create(NetconfState.class), + netconfMonitoringBindingBrokerDependency, + LogicalDatastoreType.OPERATIONAL, NetconfStateBuilder.class)); + } + } +} diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/NetconfMonitoringReaderModuleFactory.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/NetconfMonitoringReaderModuleFactory.java new file mode 100644 index 000000000..d5130fd53 --- /dev/null +++ b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/honeycomb/impl/rev141210/NetconfMonitoringReaderModuleFactory.java @@ -0,0 +1,13 @@ +/* +* Generated file +* +* Generated from: yang module name: v3po-impl yang module local name: netconf-monitoring-reader +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Tue Apr 12 13:03:40 CEST 2016 +* +* Do not modify this file unless it is present under src/main directory +*/ +package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.honeycomb.impl.rev141210; +public class NetconfMonitoringReaderModuleFactory extends AbstractNetconfMonitoringReaderModuleFactory { + +} diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModule.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModule.java deleted file mode 100644 index 1d1fa5323..000000000 --- a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModule.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210; - -import io.fd.honeycomb.v3po.data.impl.DataBroker; -import io.fd.honeycomb.v3po.data.impl.ModifiableDataTreeManager; - -public class ContextDataBrokerModule extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractContextDataBrokerModule { - - public ContextDataBrokerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { - super(identifier, dependencyResolver); - } - - public ContextDataBrokerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.ContextDataBrokerModule oldModule, java.lang.AutoCloseable oldInstance) { - super(identifier, dependencyResolver, oldModule, oldInstance); - } - - @Override - public void customValidation() { - // add custom validation form module attributes here. - } - - @Override - public java.lang.AutoCloseable createInstance() { - return DataBroker.create(new ModifiableDataTreeManager(getContextDataTreeDependency())); - } - -} diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModuleFactory.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModuleFactory.java deleted file mode 100644 index 360f5f472..000000000 --- a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModuleFactory.java +++ /dev/null @@ -1,13 +0,0 @@ -/* -* Generated file -* -* Generated from: yang module name: v3po-impl yang module local name: honeycomb-context-dom-data-broker -* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator -* Generated at: Mon May 16 15:27:12 CEST 2016 -* -* Do not modify this file unless it is present under src/main directory -*/ -package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210; -public class ContextDataBrokerModuleFactory extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractContextDataBrokerModuleFactory { - -} diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/DataBrokerModule.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/DataBrokerModule.java deleted file mode 100644 index 8aa3d64d9..000000000 --- a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/DataBrokerModule.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210; - -import io.fd.honeycomb.v3po.data.impl.DataBroker; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class DataBrokerModule extends - org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractDataBrokerModule { - - private static final Logger LOG = LoggerFactory.getLogger(DataBrokerModule.class); - - public DataBrokerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, - org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { - super(identifier, dependencyResolver); - } - - public DataBrokerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, - org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, - org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.DataBrokerModule oldModule, - java.lang.AutoCloseable oldInstance) { - super(identifier, dependencyResolver, oldModule, oldInstance); - } - - @Override - public void customValidation() { - // add custom validation form module attributes here. - } - - @Override - public java.lang.AutoCloseable createInstance() { - LOG.debug("DataBrokerModule.createInstance()"); - return DataBroker.create(getConfigDataTreeDependency(), getOperationalDataTreeDependency()); - } -} diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/DataBrokerModuleFactory.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/DataBrokerModuleFactory.java deleted file mode 100644 index cc30beacf..000000000 --- a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/DataBrokerModuleFactory.java +++ /dev/null @@ -1,13 +0,0 @@ -/* -* Generated file -* -* Generated from: yang module name: v3po-impl yang module local name: honeycomb-dom-data-broker -* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator -* Generated at: Mon Apr 11 07:53:38 CEST 2016 -* -* Do not modify this file unless it is present under src/main directory -*/ -package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210; -public class DataBrokerModuleFactory extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractDataBrokerModuleFactory { - -} diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfBindingBrokerModule.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfBindingBrokerModule.java deleted file mode 100644 index 48d227d01..000000000 --- a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfBindingBrokerModule.java +++ /dev/null @@ -1,117 +0,0 @@ -package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210; - -import org.opendaylight.controller.md.sal.binding.api.DataBroker; -import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener; -import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; -import org.opendaylight.controller.sal.binding.api.BindingAwareConsumer; -import org.opendaylight.controller.sal.binding.api.BindingAwareProvider; -import org.opendaylight.controller.sal.binding.api.BindingAwareService; -import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier; -import org.opendaylight.yangtools.concepts.ListenerRegistration; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.binding.RpcService; -import org.osgi.framework.BundleContext; - -public class NetconfBindingBrokerModule extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractNetconfBindingBrokerModule { - public NetconfBindingBrokerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { - super(identifier, dependencyResolver); - } - - public NetconfBindingBrokerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.NetconfBindingBrokerModule oldModule, java.lang.AutoCloseable oldInstance) { - super(identifier, dependencyResolver, oldModule, oldInstance); - } - - @Override - public void customValidation() { - // add custom validation form module attributes here. - } - - @Override - public java.lang.AutoCloseable createInstance() { - return new FakeBindingAwareBroker(getNetconfBindingBrokerDependency()); - } - - private static class FakeBindingAwareBroker implements BindingAwareBroker, AutoCloseable { - - private DataBroker netconfBindingBrokerDependency; - - public FakeBindingAwareBroker(final DataBroker netconfBindingBrokerDependency) { - - this.netconfBindingBrokerDependency = netconfBindingBrokerDependency; - } - - @Deprecated - @Override - public ConsumerContext registerConsumer(final BindingAwareConsumer bindingAwareConsumer, - final BundleContext bundleContext) { - throw new UnsupportedOperationException("Unsupported"); - } - - @Override - public ConsumerContext registerConsumer(final BindingAwareConsumer bindingAwareConsumer) { - final ConsumerContext consumerContext = new ConsumerContext() { - @Override - public T getSALService(final Class aClass) { - return aClass.equals(DataBroker.class) - ? (T) netconfBindingBrokerDependency - : null; - } - - @Override - public T getRpcService(final Class aClass) { - return null; - } - }; - bindingAwareConsumer.onSessionInitialized(consumerContext); - return consumerContext; - } - - @Override - public ProviderContext registerProvider(final BindingAwareProvider bindingAwareProvider, - final BundleContext bundleContext) { - throw new UnsupportedOperationException("Unsupported"); - } - - @Override - public ProviderContext registerProvider(final BindingAwareProvider bindingAwareProvider) { - final ProviderContext context = new ProviderContext() { - @Override - public >> ListenerRegistration registerRouteChangeListener( - final L l) { - throw new UnsupportedOperationException("Unsupported"); - } - - @Override - public T getRpcService(final Class aClass) { - throw new UnsupportedOperationException("Unsupported"); - } - - @Override - public RpcRegistration addRpcImplementation(final Class aClass, final T t) - throws IllegalStateException { - throw new UnsupportedOperationException("Unsupported"); - } - - @Override - public RoutedRpcRegistration addRoutedRpcImplementation( - final Class aClass, final T t) throws IllegalStateException { - throw new UnsupportedOperationException("Unsupported"); - } - - @Override - public T getSALService(final Class aClass) { - return aClass.equals(DataBroker.class) - ? (T) netconfBindingBrokerDependency - : null; } - }; - bindingAwareProvider.onSessionInitiated(context); - return context; - } - - @Override - public void close() throws Exception { - netconfBindingBrokerDependency = null; - } - } -} - diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfBindingBrokerModuleFactory.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfBindingBrokerModuleFactory.java deleted file mode 100644 index b64b0b1e1..000000000 --- a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfBindingBrokerModuleFactory.java +++ /dev/null @@ -1,13 +0,0 @@ -/* -* Generated file -* -* Generated from: yang module name: v3po-impl yang module local name: binding-broker-netconf -* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator -* Generated at: Wed Mar 23 10:45:48 CET 2016 -* -* Do not modify this file unless it is present under src/main directory -*/ -package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210; -public class NetconfBindingBrokerModuleFactory extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractNetconfBindingBrokerModuleFactory { - -} diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfMonitoringReaderModule.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfMonitoringReaderModule.java deleted file mode 100644 index 8502fcbd2..000000000 --- a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfMonitoringReaderModule.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210; - -import io.fd.honeycomb.v3po.translate.read.registry.ModifiableReaderRegistryBuilder; -import io.fd.honeycomb.v3po.translate.util.read.BindingBrokerReader; -import org.opendaylight.controller.md.sal.binding.api.DataBroker; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfStateBuilder; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -public class NetconfMonitoringReaderModule extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractNetconfMonitoringReaderModule { - public NetconfMonitoringReaderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { - super(identifier, dependencyResolver); - } - - public NetconfMonitoringReaderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.NetconfMonitoringReaderModule oldModule, java.lang.AutoCloseable oldInstance) { - super(identifier, dependencyResolver, oldModule, oldInstance); - } - - @Override - public void customValidation() { - // add custom validation form module attributes here. - } - - @Override - public java.lang.AutoCloseable createInstance() { - return new ReaderFactory(getNetconfMonitoringBindingBrokerDependency()); - } - - - private static final class ReaderFactory implements AutoCloseable, io.fd.honeycomb.v3po.translate.read.ReaderFactory { - - private final DataBroker netconfMonitoringBindingBrokerDependency; - - public ReaderFactory(final DataBroker netconfMonitoringBindingBrokerDependency) { - this.netconfMonitoringBindingBrokerDependency = netconfMonitoringBindingBrokerDependency; - } - - @Override - public void init(final ModifiableReaderRegistryBuilder registry) { - registry.add(new BindingBrokerReader<>(InstanceIdentifier.create(NetconfState.class), - netconfMonitoringBindingBrokerDependency, - LogicalDatastoreType.OPERATIONAL, NetconfStateBuilder.class)); - } - } -} diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfMonitoringReaderModuleFactory.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfMonitoringReaderModuleFactory.java deleted file mode 100644 index b8b7bec63..000000000 --- a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfMonitoringReaderModuleFactory.java +++ /dev/null @@ -1,13 +0,0 @@ -/* -* Generated file -* -* Generated from: yang module name: v3po-impl yang module local name: netconf-monitoring-reader -* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator -* Generated at: Tue Apr 12 13:03:40 CEST 2016 -* -* Do not modify this file unless it is present under src/main directory -*/ -package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210; -public class NetconfMonitoringReaderModuleFactory extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractNetconfMonitoringReaderModuleFactory { - -} diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModule.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModule.java deleted file mode 100644 index caa792da4..000000000 --- a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModule.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2015 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210; - -import io.fd.honeycomb.v3po.impl.NorthboundFacadeHoneycombDOMBroker; -import org.opendaylight.controller.sal.core.api.AbstractProvider; -import org.opendaylight.controller.sal.core.api.Broker; -import org.opendaylight.controller.sal.core.api.model.SchemaService; - -public class V3poModule extends - org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractV3poModule { - - public V3poModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, - org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { - super(identifier, dependencyResolver); - } - - public V3poModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, - org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, - org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.V3poModule oldModule, - java.lang.AutoCloseable oldInstance) { - super(identifier, dependencyResolver, oldModule, oldInstance); - } - - @Override - public void customValidation() { - // add custom validation form module attributes here - } - - @Override - public java.lang.AutoCloseable createInstance() { - - final Broker.ProviderSession providerSession = - getDomBrokerDependency().registerProvider(new AbstractProvider() { - @Override - public void onSessionInitiated(final Broker.ProviderSession providerSession) { - // NOOP - } - }); - final SchemaService schemaBiService = providerSession.getService(SchemaService.class); - - return new NorthboundFacadeHoneycombDOMBroker(getHoneycombDomDataBrokerDependency(), schemaBiService, - getHoneycombDomNotificationServiceDependency()); - } - -} diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactory.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactory.java deleted file mode 100644 index 5c942861d..000000000 --- a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactory.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2015 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* -* Generated file -* -* Generated from: yang module name: v3po yang module local name: v3po -* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator -* Generated at: Fri Jan 02 13:49:24 CST 2015 -* -* Do not modify this file unless it is present under src/main directory -*/ -package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210; -public class V3poModuleFactory extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractV3poModuleFactory { - -} diff --git a/infra/impl/src/main/yang/honeycomb-impl.yang b/infra/impl/src/main/yang/honeycomb-impl.yang new file mode 100644 index 000000000..46f1af862 --- /dev/null +++ b/infra/impl/src/main/yang/honeycomb-impl.yang @@ -0,0 +1,155 @@ +module honeycomb-impl { + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:honeycomb:impl"; + prefix "hc-impl"; + + import config { prefix config; revision-date 2013-04-05; } + import opendaylight-md-sal-binding { prefix md-sal-binding; revision-date 2013-10-28;} + import opendaylight-md-sal-dom {prefix dom;} + import translate-api { prefix tapi; revision-date 2016-04-06; } + import data-api { prefix dapi; revision-date 2016-04-11; } + import notification-api { prefix hc-notif-a; revision-date 2016-06-01; } + + description + "Service definition for honeycomb project"; + + revision "2014-12-10" { + description + "Initial revision"; + } + + identity honeycomb { + base config:module-type; + config:provided-service dom:dom-broker-osgi-registry; + config:java-name-prefix Honeycomb; + } + + augment "/config:modules/config:module/config:configuration" { + case honeycomb { + when "/config:modules/config:module/config:type = 'honeycomb'"; + container dom-broker { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity dom:dom-broker-osgi-registry; + } + } + } + + container honeycomb-dom-data-broker { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity dom:dom-async-data-broker; + } + } + } + + container honeycomb-dom-notification-service { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity hc-notif-a:dom-notification-service; + } + } + } + + } + } + + identity binding-broker-netconf { + base config:module-type; + config:provided-service md-sal-binding:binding-broker-osgi-registry; + config:java-name-prefix NetconfBindingBroker; + } + + augment "/config:modules/config:module/config:configuration" { + case binding-broker-netconf { + when "/config:modules/config:module/config:type = 'binding-broker-netconf'"; + + container netconf-binding-broker { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity md-sal-binding:binding-async-data-broker; + } + } + } + + } + } + + identity honeycomb-dom-data-broker { + base config:module-type; + config:provided-service dom:dom-async-data-broker; + config:java-name-prefix DataBroker; + } + + augment "/config:modules/config:module/config:configuration" { + case honeycomb-dom-data-broker { + when "/config:modules/config:module/config:type = 'honeycomb-dom-data-broker'"; + + container config-data-tree { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity dapi:honeycomb-modifiable-data-tree; + } + } + } + + container operational-data-tree { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity dapi:honeycomb-readable-data-tree; + } + } + } + + } + } + + identity honeycomb-context-dom-data-broker { + base config:module-type; + config:provided-service dom:dom-async-data-broker; + config:java-name-prefix ContextDataBroker; + description "DomBroker on top of context data-tree"; + } + + augment "/config:modules/config:module/config:configuration" { + case honeycomb-context-dom-data-broker { + when "/config:modules/config:module/config:type = 'honeycomb-context-dom-data-broker'"; + + container context-data-tree { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity dapi:data-tree; + } + } + } + } + } + + identity netconf-monitoring-reader { + base config:module-type; + config:provided-service tapi:honeycomb-reader-factory; + } + + augment "/config:modules/config:module/config:configuration" { + case netconf-monitoring-reader { + when "/config:modules/config:module/config:type = 'netconf-monitoring-reader'"; + + container netconf-monitoring-binding-broker { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity md-sal-binding:binding-async-data-broker; + } + } + } + + } + } +} diff --git a/infra/impl/src/main/yang/v3po-impl.yang b/infra/impl/src/main/yang/v3po-impl.yang deleted file mode 100644 index fa9670678..000000000 --- a/infra/impl/src/main/yang/v3po-impl.yang +++ /dev/null @@ -1,155 +0,0 @@ -module v3po-impl { - yang-version 1; - namespace "urn:opendaylight:params:xml:ns:yang:v3po:impl"; - prefix "v3po-impl"; - - import config { prefix config; revision-date 2013-04-05; } - import opendaylight-md-sal-binding { prefix md-sal-binding; revision-date 2013-10-28;} - import opendaylight-md-sal-dom {prefix dom;} - import translate-api { prefix tapi; revision-date 2016-04-06; } - import data-api { prefix dapi; revision-date 2016-04-11; } - import notification-api { prefix hc-notif-a; revision-date 2016-06-01; } - - description - "Service definition for v3po project"; - - revision "2014-12-10" { - description - "Initial revision"; - } - - identity v3po { - base config:module-type; - config:provided-service dom:dom-broker-osgi-registry; - config:java-name-prefix V3po; - } - - augment "/config:modules/config:module/config:configuration" { - case v3po { - when "/config:modules/config:module/config:type = 'v3po'"; - container dom-broker { - uses config:service-ref { - refine type { - mandatory true; - config:required-identity dom:dom-broker-osgi-registry; - } - } - } - - container honeycomb-dom-data-broker { - uses config:service-ref { - refine type { - mandatory true; - config:required-identity dom:dom-async-data-broker; - } - } - } - - container honeycomb-dom-notification-service { - uses config:service-ref { - refine type { - mandatory true; - config:required-identity hc-notif-a:dom-notification-service; - } - } - } - - } - } - - identity binding-broker-netconf { - base config:module-type; - config:provided-service md-sal-binding:binding-broker-osgi-registry; - config:java-name-prefix NetconfBindingBroker; - } - - augment "/config:modules/config:module/config:configuration" { - case binding-broker-netconf { - when "/config:modules/config:module/config:type = 'binding-broker-netconf'"; - - container netconf-binding-broker { - uses config:service-ref { - refine type { - mandatory true; - config:required-identity md-sal-binding:binding-async-data-broker; - } - } - } - - } - } - - identity honeycomb-dom-data-broker { - base config:module-type; - config:provided-service dom:dom-async-data-broker; - config:java-name-prefix DataBroker; - } - - augment "/config:modules/config:module/config:configuration" { - case honeycomb-dom-data-broker { - when "/config:modules/config:module/config:type = 'honeycomb-dom-data-broker'"; - - container config-data-tree { - uses config:service-ref { - refine type { - mandatory true; - config:required-identity dapi:honeycomb-modifiable-data-tree; - } - } - } - - container operational-data-tree { - uses config:service-ref { - refine type { - mandatory true; - config:required-identity dapi:honeycomb-readable-data-tree; - } - } - } - - } - } - - identity honeycomb-context-dom-data-broker { - base config:module-type; - config:provided-service dom:dom-async-data-broker; - config:java-name-prefix ContextDataBroker; - description "DomBroker on top of context data-tree"; - } - - augment "/config:modules/config:module/config:configuration" { - case honeycomb-context-dom-data-broker { - when "/config:modules/config:module/config:type = 'honeycomb-context-dom-data-broker'"; - - container context-data-tree { - uses config:service-ref { - refine type { - mandatory true; - config:required-identity dapi:data-tree; - } - } - } - } - } - - identity netconf-monitoring-reader { - base config:module-type; - config:provided-service tapi:honeycomb-reader-factory; - } - - augment "/config:modules/config:module/config:configuration" { - case netconf-monitoring-reader { - when "/config:modules/config:module/config:type = 'netconf-monitoring-reader'"; - - container netconf-monitoring-binding-broker { - uses config:service-ref { - refine type { - mandatory true; - config:required-identity md-sal-binding:binding-async-data-broker; - } - } - } - - } - } -} diff --git a/infra/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactoryTest.java b/infra/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactoryTest.java deleted file mode 100644 index 5b9a67458..000000000 --- a/infra/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactoryTest.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2015 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210; - -import org.junit.Test; - -public class V3poModuleFactoryTest { - @Test - public void testFactoryConstructor() { - // ensure no exceptions on construction - new V3poModuleFactory(); - } -} diff --git a/infra/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleTest.java b/infra/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleTest.java deleted file mode 100644 index 3d8e79641..000000000 --- a/infra/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2015 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210; - -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import javax.management.ObjectName; -import org.junit.Test; -import org.opendaylight.controller.config.api.DependencyResolver; -import org.opendaylight.controller.config.api.JmxAttribute; -import org.opendaylight.controller.config.api.ModuleIdentifier; -import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; -import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; -import org.opendaylight.controller.sal.core.api.Broker; -import org.opendaylight.controller.sal.core.api.Provider; -import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer; - -public class V3poModuleTest { - @Test - public void testCustomValidation() { - V3poModule module = new V3poModule(mock(ModuleIdentifier.class), mock(DependencyResolver.class)); - - // ensure no exceptions on validation - // currently this method is empty - module.customValidation(); - } - - @Test - public void testCreateInstance() throws Exception { - // configure mocks - DependencyResolver dependencyResolver = mock(DependencyResolver.class); - BindingAwareBroker broker = mock(BindingAwareBroker.class); - when(dependencyResolver.resolveInstance(eq(BindingAwareBroker.class), any(ObjectName.class), any(JmxAttribute.class))) - .thenReturn(broker); - final org.opendaylight.controller.sal.core.api.Broker domBroker = mock(org.opendaylight.controller.sal.core.api.Broker.class); - when(dependencyResolver.resolveInstance(eq(org.opendaylight.controller.sal.core.api.Broker.class), any(ObjectName.class), any(JmxAttribute.class))) - .thenReturn(domBroker); - doReturn(mock(Broker.ProviderSession.class)).when(domBroker).registerProvider(any(Provider.class)); - when(dependencyResolver.resolveInstance(eq(BindingNormalizedNodeSerializer.class), any(ObjectName.class), any(JmxAttribute.class))) - .thenReturn(mock(BindingNormalizedNodeSerializer.class)); - when(dependencyResolver.resolveInstance(eq(DOMDataBroker.class), any(ObjectName.class), any(JmxAttribute.class))) - .thenReturn(mock(DOMDataBroker.class)); - - // create instance of module with injected mocks - V3poModule module = new V3poModule(mock(ModuleIdentifier.class), dependencyResolver); - - // getInstance calls resolveInstance to get the broker dependency and then calls createInstance - AutoCloseable closeable = module.getInstance(); - - // ensure no exceptions on close - closeable.close(); - } -} diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/AbstractInfraTest.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/AbstractInfraTest.java new file mode 100644 index 000000000..3c996f6a6 --- /dev/null +++ b/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/AbstractInfraTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static org.mockito.Mockito.when; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.google.common.util.concurrent.Futures; +import java.util.Collections; +import java.util.Map; +import javassist.ClassPool; +import org.junit.Before; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.$YangModuleInfoImpl; +import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.DataObjectSerializerGenerator; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator; +import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry; +import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; +import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext; +import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +/** + * Base IT test infrastructure. + */ +abstract class AbstractInfraTest { + + protected BindingNormalizedNodeSerializer serializer; + protected SchemaContext schemaContext; + + @Mock + protected org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker; + @Mock + private org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction ctxTx; + + static BindingToNormalizedNodeCodec getSerializer(final ModuleInfoBackedContext moduleInfoBackedContext, + final SchemaContext schemaContext) { + final DataObjectSerializerGenerator serializerGenerator = new StreamWriterGenerator(JavassistUtils.forClassPool( + ClassPool.getDefault())); + final BindingNormalizedNodeCodecRegistry codecRegistry = new BindingNormalizedNodeCodecRegistry(serializerGenerator); + final BindingRuntimeContext ctx = + BindingRuntimeContext.create(moduleInfoBackedContext, schemaContext); + codecRegistry.onBindingRuntimeContextUpdated(ctx); + return new BindingToNormalizedNodeCodec(moduleInfoBackedContext, codecRegistry); + } + + static ModuleInfoBackedContext getSchemaContext() { + final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create(); + moduleInfoBackedContext.addModuleInfos(Collections.singleton($YangModuleInfoImpl.getInstance())); + return moduleInfoBackedContext; + } + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(contextBroker.newReadWriteTransaction()).thenReturn(ctxTx); + when(ctxTx.submit()).thenReturn(Futures.immediateCheckedFuture(null)); + + initSerializer(); + postSetup(); + } + + abstract void postSetup(); + + private void initSerializer() { + final ModuleInfoBackedContext moduleInfoBackedContext = getSchemaContext(); + schemaContext = moduleInfoBackedContext.tryToCreateSchemaContext().get(); + serializer = getSerializer(moduleInfoBackedContext, schemaContext); + } + + protected Multimap, ? extends DataObject> toBinding( + final NormalizedNode read) { + Multimap, DataObject> baNodes = HashMultimap.create(); + + for (DataContainerChild o : ((DataContainerNode) read).getValue()) { + final YangInstanceIdentifier yid = YangInstanceIdentifier.of(o.getNodeType()); + final Map.Entry, DataObject> baEntry = serializer.fromNormalizedNode(yid, o); + baNodes.put(baEntry.getKey(), baEntry.getValue()); + } + return baNodes; + } +} diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/HoneycombReadInfraTest.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/HoneycombReadInfraTest.java new file mode 100644 index 000000000..5ec2b55ba --- /dev/null +++ b/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/HoneycombReadInfraTest.java @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyListOf; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import com.google.common.base.Optional; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.common.util.concurrent.CheckedFuture; +import io.fd.honeycomb.translate.impl.read.GenericListReader; +import io.fd.honeycomb.translate.read.ListReader; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.read.Reader; +import io.fd.honeycomb.translate.read.registry.ReaderRegistry; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.honeycomb.translate.util.read.ReflexiveListReaderCustomizer; +import io.fd.honeycomb.translate.util.read.ReflexiveReader; +import io.fd.honeycomb.translate.util.read.registry.CompositeReaderRegistryBuilder; +import java.lang.reflect.InvocationTargetException; +import java.util.List; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.exceptions.misusing.MockitoConfigurationException; +import org.mockito.invocation.InvocationOnMock; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugmentBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleAugmentBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainerBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedListKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainerBuilder; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.Identifiable; +import org.opendaylight.yangtools.yang.binding.Identifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public class HoneycombReadInfraTest extends AbstractInfraTest { + + @Mock + private ReadContext ctx; + private ReaderRegistry registry; + + // 1 + private Reader simpleContainerReader = + mockReader(Ids.SIMPLE_CONTAINER_ID, this::readSimpleContainer, SimpleContainerBuilder.class); + // 1.1 + private Reader simpleAugmentReader = + mockReader(Ids.SIMPLE_AUGMENT_ID, this::readSimpleAugment, SimpleAugmentBuilder.class); + // 1.2 + // Noop reader(no real attributes) + private Reader complexAugmentReader = + mockReader(Ids.COMPLEX_AUGMENT_ID, HoneycombReadInfraTest::noopRead, ComplexAugmentBuilder.class); + // 1.2.1 + private Reader complexAugmentContainerReader = + mockReader(Ids.COMPLEX_AUGMENT_CONTAINER_ID, this::readComplexAugmentContainer, ComplexAugmentContainerBuilder.class); + // 2 + // Noop reader(no real attributes) + private Reader containerWithListReader = + mockReader(Ids.CONTAINER_WITH_LIST_ID, HoneycombReadInfraTest::noopRead, ContainerWithListBuilder.class); + // 2.1 + private ListReader listInContainerReader = + mockListReader(Ids.LIST_IN_CONTAINER_ID, this::readListInContainer, this::getListInContainerIds, + ListInContainerBuilder.class); + // 2.1.1 + private Reader containerInListReader = + mockReader(Ids.CONTAINER_IN_LIST_ID, this::readContainerInList, ContainerInListBuilder.class); + // 2.1.1.1 + private ListReader nestedListReader = + mockListReader(Ids.NESTED_LIST_ID, this::readNestedList, this::getNestedListIds, + NestedListBuilder.class); + + @Override + void postSetup() { + initReaderRegistry(); + } + + private void initReaderRegistry() { + registry = new CompositeReaderRegistryBuilder() + .add(containerWithListReader) // 2 + .addBefore(simpleContainerReader, Ids.CONTAINER_WITH_LIST_ID) // 1 + .add(simpleAugmentReader) // 1.1 + .addAfter(complexAugmentReader, Ids.SIMPLE_AUGMENT_ID) // 1.2 + .add(complexAugmentContainerReader) // 1.2.1 + .add(listInContainerReader) // 2.1 + .add(containerInListReader) // 2.1.1 + .addBefore(nestedListReader, Ids.SIMPLE_AUGMENT_ID) // 2.1.1.1 - Before relationship should be ignored + .build(); + } + + private Reader[] getOrderedReaders() { + return new Reader[]{simpleContainerReader, // 1 + simpleAugmentReader, // 1.1 + complexAugmentReader, // 1.2 + complexAugmentContainerReader, // 1.2.1 + containerWithListReader, // 2 + listInContainerReader, // 2.1 + containerInListReader, // 2.1.1 + nestedListReader}; // 2.1.1.1 + } + + @Test + public void testReadAll() throws Exception { + final ReadableDataTreeDelegator readableDataTreeDelegator = + new ReadableDataTreeDelegator(serializer, schemaContext, registry, contextBroker); + final CheckedFuture>, org.opendaylight.controller.md.sal.common.api.data.ReadFailedException> + read = readableDataTreeDelegator.read(YangInstanceIdentifier.EMPTY); + + final Multimap, ? extends DataObject> readAll = + toBinding(read.checkedGet().get()); + assertThat(readAll.size(), is(2)); + assertTrue(readAll.containsKey(Ids.CONTAINER_WITH_LIST_ID)); + assertEquals(readContainerWithList(), readAll.get(Ids.CONTAINER_WITH_LIST_ID).stream().findFirst().get()); + assertTrue(readAll.containsKey(Ids.SIMPLE_CONTAINER_ID)); + assertEquals(readSimpleContainer(), readAll.get(Ids.SIMPLE_CONTAINER_ID).stream().findFirst().get()); + + final Reader[] ordered = getOrderedReaders(); + final InOrder inOrder = inOrder(ordered); + + final List listInContainerKeys = getListInContainerIds(Ids.LIST_IN_CONTAINER_ID, ctx); + + verifyRootReader(inOrder, simpleContainerReader, Ids.SIMPLE_CONTAINER_ID, SimpleContainerBuilder.class); + verifyLeafChildReader(inOrder, simpleAugmentReader, Ids.SIMPLE_AUGMENT_ID); + verifyCompositeChildReader(inOrder, complexAugmentReader, ComplexAugmentBuilder.class, Ids.COMPLEX_AUGMENT_ID); + verifyLeafChildReader(inOrder, complexAugmentContainerReader, Ids.COMPLEX_AUGMENT_CONTAINER_ID); + verifyRootReader(inOrder, containerWithListReader, Ids.CONTAINER_WITH_LIST_ID, ContainerWithListBuilder.class); + verifyCompositeListReader(inOrder, listInContainerReader, Ids.LIST_IN_CONTAINER_ID, + listInContainerKeys, ListInContainerBuilder.class); + + for (ListInContainerKey k : listInContainerKeys) { + final InstanceIdentifier id = + Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, k).child(ContainerInList.class); + verifyCompositeChildReader(inOrder, containerInListReader, ContainerInListBuilder.class, id); + final InstanceIdentifier nestedId = id.child(NestedList.class); + verifyLeafListReader(inOrder, nestedListReader, nestedId); + } + + for (Reader reader : ordered) { + verifyNoMoreReadInteractions(reader); + } + } + + private > void verifyNoMoreReadInteractions(final Reader reader) { + verify(reader, atLeastOnce()).getManagedDataObjectType(); + verifyNoMoreInteractions(reader); + } + + private > void verifyCompositeChildReader(final InOrder inOrder, + final Reader reader, + final Class builderCls, + final InstanceIdentifier id) + throws ReadFailedException { + verifyRootReader(inOrder, reader, id, builderCls); + verify(reader, atLeastOnce()).merge(any(Builder.class), any(id.getTargetType())); + } + + private , K extends Identifier, B extends Builder> void verifyCompositeListReader( + final InOrder inOrder, + final ListReader reader, + final InstanceIdentifier id, + final List keys, + final Class builderCls) + throws ReadFailedException { + + // Id has to be wildcarded, since it was created using InstanceIdentifier.child() method + inOrder.verify(reader).getAllIds(eq(RWUtils.makeIidLastWildcarded(id)), any(ReadContext.class)); + keys.stream() + .map(k -> RWUtils.replaceLastInId(id, RWUtils.getCurrentIdItem(id, k))) + .forEach(keyedId -> { + try { + verify(reader).getBuilder(keyedId); + verify(reader).readCurrentAttributes(eq(keyedId), any(builderCls), any(ReadContext.class)); + } catch (ReadFailedException e) { throw new RuntimeException(e); } + }); + verify(reader, atLeastOnce()).merge(any(Builder.class), anyListOf(id.getTargetType())); + } + + private , K extends Identifier, B extends Builder> void verifyLeafListReader( + final InOrder inOrder, + final ListReader reader, + final InstanceIdentifier id) + throws ReadFailedException { + + // Id has to be wildcarded, since it was created using InstanceIdentifier.child() method + inOrder.verify(reader).readList(eq(RWUtils.makeIidLastWildcarded(id)), any(ReadContext.class)); + verify(reader, atLeastOnce()).merge(any(Builder.class), anyListOf(id.getTargetType())); + } + + private > void verifyRootReader(final InOrder inOrder, + final Reader reader, + final InstanceIdentifier id, + final Class builderCls) + throws ReadFailedException { + inOrder.verify(reader).readCurrentAttributes(eq(id), any(builderCls), any(ReadContext.class)); + verify(reader).getBuilder(id); + } + + + private > void verifyLeafChildReader(final InOrder inOrder, + final Reader reader, + final InstanceIdentifier... id) + throws ReadFailedException { + for (InstanceIdentifier singleId : id) { + inOrder.verify(reader).read(eq(singleId), any(ReadContext.class)); + verify(reader, atLeastOnce()).merge(any(Builder.class), any(singleId.getTargetType())); + } + } + + static > Reader mockReader(InstanceIdentifier id, + CurrentAttributesReader currentAttributesReader, + Class builderClass) { + final ReflexiveReader reflex = new ReflexiveReader(id, builderClass) { + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier id, + @Nonnull final B builder, + @Nonnull final ReadContext ctx) + throws ReadFailedException { + currentAttributesReader.readCurrentAttributes(id, builder, ctx); + } + }; + + // Simple spy on top of this reflexive reader cannot be used, spy also checks protected methods for invocation + // but those cannot be verified by mockito + final Reader mock = mock(Reader.class); + try { + doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).read(any(InstanceIdentifier.class), any(ReadContext.class)); + doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock) + .readCurrentAttributes(any(InstanceIdentifier.class), any(builderClass), any(ReadContext.class)); + doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).merge(any(Builder.class), any(id.getTargetType())); + doReturn(id).when(mock).getManagedDataObjectType(); + doReturn(builderClass.newInstance()).when(mock).getBuilder(any(InstanceIdentifier.class)); + } catch (Exception e) { + throw new RuntimeException(e); + } + + return mock; + } + + static , K extends Identifier, B extends Builder> ListReader mockListReader( + InstanceIdentifier id, + CurrentAttributesReader currentAttributesReader, + ListKeysReader listKeysReader, + Class builderClass) { + + ListReader reflex = new GenericListReader<>(id, + new ReflexiveListReaderCustomizer(id.getTargetType(), builderClass) { + + @Nonnull + @Override + public List getAllIds(@Nonnull final InstanceIdentifier id, @Nonnull final ReadContext context) + throws ReadFailedException { + return listKeysReader.getAllIds(id, context); + } + + @Override + public void readCurrentAttributes(final InstanceIdentifier id, final B builder, + final ReadContext context) + throws ReadFailedException { + currentAttributesReader.readCurrentAttributes(id, builder, context); + } + }); + + final ListReader mock = mock(ListReader.class /*, withSettings().verboseLogging()*/); + try { + // not using eq(id) instead using any(InstanceIdentifier.class) due to InstanceIdentifier.equals weird behavior + // with wildcarded instance identifiers for lists + doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).read(any(InstanceIdentifier.class), any(ReadContext.class)); + doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock) + .readCurrentAttributes(any(InstanceIdentifier.class), any(builderClass), any(ReadContext.class)); + doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).merge(any(Builder.class), any(id.getTargetType())); + doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).merge(any(Builder.class), anyListOf(id.getTargetType())); + doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).getAllIds(any(InstanceIdentifier.class), any(ReadContext.class)); + doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).readList(any(InstanceIdentifier.class), any(ReadContext.class)); + doReturn(id).when(mock).getManagedDataObjectType(); + doReturn(builderClass.newInstance()).when(mock).getBuilder(any(InstanceIdentifier.class)); + } catch (Exception e) { + throw new RuntimeException(e); + } + + return mock; + } + + private static > Object reflexiveAnswer(final Reader instance, + final InvocationOnMock i) { + try { + return i.getMethod().invoke(instance, i.getArguments()); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new MockitoConfigurationException("Unable to invoke stubbed method invocation: " + i + " on " + instance); + } + } + + @FunctionalInterface + interface CurrentAttributesReader> { + void readCurrentAttributes(@Nonnull final InstanceIdentifier id, + @Nonnull final B builder, + @Nonnull final ReadContext ctx); + } + + @FunctionalInterface + interface ListKeysReader, K extends Identifier> { + List getAllIds(@Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext ctx); + } + + + private void readSimpleContainer(final InstanceIdentifier id, + final SimpleContainerBuilder b, + final ReadContext readContext) { + b.setSimpleContainerName("simple"); + } + + private SimpleContainer readSimpleContainer() { + final SimpleContainerBuilder b = new SimpleContainerBuilder(); + readSimpleContainer(Ids.SIMPLE_CONTAINER_ID, b, ctx); + b.addAugmentation(SimpleAugment.class, readSimpleAugment()); + b.addAugmentation(ComplexAugment.class, + new ComplexAugmentBuilder().setComplexAugmentContainer(readComplexAugmentContainer()).build()); + return b.build(); + } + + private void readSimpleAugment(final InstanceIdentifier id, + final SimpleAugmentBuilder b, + final ReadContext readContext) { + b.setSimpleAugmentLeaf("simple augment"); + } + + private SimpleAugment readSimpleAugment() { + final SimpleAugmentBuilder b = new SimpleAugmentBuilder(); + readSimpleAugment(Ids.SIMPLE_AUGMENT_ID, b, ctx); + return b.build(); + } + + private void readComplexAugmentContainer(final InstanceIdentifier id, + final ComplexAugmentContainerBuilder b, + final ReadContext readContext) { + b.setSomeLeaf("nested container in augment"); + } + + private ComplexAugmentContainer readComplexAugmentContainer() { + final ComplexAugmentContainerBuilder b = new ComplexAugmentContainerBuilder(); + readComplexAugmentContainer(Ids.COMPLEX_AUGMENT_CONTAINER_ID, b, ctx); + return b.build(); + } + + private ContainerWithList readContainerWithList() { + final ContainerWithListBuilder b = new ContainerWithListBuilder(); + b.setListInContainer(readListInContainer()); + return b.build(); + } + + private List getListInContainerIds(final InstanceIdentifier id, + final ReadContext readContext) { + return Lists.newArrayList(1L, 2L) + .stream() + .map(ListInContainerKey::new) + .collect(Collectors.toList()); + } + + private List getNestedListIds(final InstanceIdentifier id, + final ReadContext readContext) { + return Lists.newArrayList("one", "two") + .stream() + .map(NestedListKey::new) + .collect(Collectors.toList()); + } + + private void readListInContainer(final InstanceIdentifier id, + final ListInContainerBuilder b, + final ReadContext readContext) { + final ListInContainerKey key = id.firstKeyOf(ListInContainer.class); + b.setId(key.getId()); + final ContainerInListBuilder cilBuilder = new ContainerInListBuilder(); + readContainerInList(id.child(ContainerInList.class), cilBuilder, readContext); + b.setContainerInList(cilBuilder.build()); + } + + private void readNestedList(final InstanceIdentifier id, + final NestedListBuilder b, + final ReadContext readContext) { + final NestedListKey key = id.firstKeyOf(NestedList.class); + b.setNestedId(key.getNestedId()); + b.setNestedName(key.getNestedId() + "name"); + } + + private void readContainerInList(final InstanceIdentifier id, + final ContainerInListBuilder b, + final ReadContext readContext) { + b.setName(id.firstKeyOf(ListInContainer.class).getId().toString()); + b.setNestedList(getNestedListIds(Ids.NESTED_LIST_ID, ctx).stream() + .map(key -> id.child(NestedList.class, key)) + .map(nestedId -> { + final NestedListBuilder nestedB = new NestedListBuilder(); + readNestedList(nestedId, nestedB, readContext); + return nestedB.build(); + }) + .collect(Collectors.toList())); + } + + private List readListInContainer() { + return getListInContainerIds(Ids.LIST_IN_CONTAINER_ID, ctx).stream() + .map(key -> Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, key)) + .map(id -> { + final ListInContainerBuilder b = new ListInContainerBuilder(); + readListInContainer(id, b, ctx); + return b.build(); + }).collect(Collectors.toList()); + } + + static > void noopRead(final InstanceIdentifier id, final B b, + final ReadContext readContext) { + // NOOP + } + +} diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/HoneycombSubtreeReadInfraTest.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/HoneycombSubtreeReadInfraTest.java new file mode 100644 index 000000000..a43f70a84 --- /dev/null +++ b/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/HoneycombSubtreeReadInfraTest.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +import com.google.common.base.Optional; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.CheckedFuture; +import io.fd.honeycomb.translate.impl.read.GenericListReader; +import io.fd.honeycomb.translate.read.ListReader; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.read.Reader; +import io.fd.honeycomb.translate.read.registry.ReaderRegistry; +import io.fd.honeycomb.translate.util.read.ReflexiveListReaderCustomizer; +import io.fd.honeycomb.translate.util.read.registry.CompositeReaderRegistryBuilder; +import java.util.List; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.junit.Test; +import org.mockito.Mock; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInListBuilder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public class HoneycombSubtreeReadInfraTest extends AbstractInfraTest { + + @Mock + private ReadContext ctx; + private ReaderRegistry registry; + + private Reader containerWithListReader = + HoneycombReadInfraTest.mockReader(Ids.CONTAINER_WITH_LIST_ID, this::readSubtree, ContainerWithListBuilder.class); + + private ListReader listInContainerReader = + new GenericListReader<>(Ids.LIST_IN_CONTAINER_ID, + new ReflexiveListReaderCustomizer(Ids.LIST_IN_CONTAINER_ID.getTargetType(), ListInContainerBuilder.class) { + + @Nonnull + @Override + public List getAllIds(@Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext context) + throws ReadFailedException { + // FIXME this is the only way of extending subtree reader via its list child + // Reflexive list reader has to be used in place of the list(managed by subtree reader perent) + // to enable further children readers. However, it will not work out of the box, because + // reflexive list reader has no way to tell what are the IDs to correctly invoke its children. + // Only way is to override the getAllIds method in reflexive reader and return the same list + // as parent used (this can be done using cache or repeated dump call) + return Lists.newArrayList(new ListInContainerKey(1L), new ListInContainerKey(2L)); + } + + @Override + public void readCurrentAttributes(final InstanceIdentifier id, + final ListInContainerBuilder builder, + final ReadContext context) throws ReadFailedException { + super.readCurrentAttributes(id, builder, context); + builder.setKey(id.firstKeyOf(ListInContainer.class)); + } + }); + + private Reader containerInListReader = + HoneycombReadInfraTest.mockReader(Ids.CONTAINER_IN_LIST_ID, this::readContainerInList, ContainerInListBuilder.class); + + // TODO Test subtree readers especially composite structure where readers are under subtree reader + + @Override + void postSetup() { + initReaderRegistry(); + } + + private void initReaderRegistry() { + registry = new CompositeReaderRegistryBuilder() + // Subtree reader handling its child list + .subtreeAdd(Sets.newHashSet(Ids.LIST_IN_CONTAINER_ID), containerWithListReader) + // Reflexive + .add(listInContainerReader) + .add(containerInListReader) + .build(); + } + + @Test + public void testReadAll() throws Exception { + final ReadableDataTreeDelegator readableDataTreeDelegator = + new ReadableDataTreeDelegator(serializer, schemaContext, registry, contextBroker); + final CheckedFuture>, org.opendaylight.controller.md.sal.common.api.data.ReadFailedException> + read = readableDataTreeDelegator.read(YangInstanceIdentifier.EMPTY); + + final Multimap, ? extends DataObject> readAll = + toBinding(read.checkedGet().get()); + assertThat(readAll.size(), is(1)); + assertEquals(readEntireSubtree(), readAll.get(Ids.CONTAINER_WITH_LIST_ID).stream().findFirst().get()); + } + + private void readSubtree(final InstanceIdentifier id, + final ContainerWithListBuilder b, + final ReadContext readContext) { + b.setListInContainer(Lists.newArrayList(1L, 2L).stream() + .map(l -> new ListInContainerBuilder().setId(l).build()) + .collect(Collectors.toList())); + } + + private ContainerWithList readEntireSubtree() { + final ContainerWithListBuilder b = new ContainerWithListBuilder(); + b.setListInContainer(Lists.newArrayList(1L, 2L).stream() + .map(l -> { + final ContainerInListBuilder containerInListBuilder = new ContainerInListBuilder(); + readContainerInList( + Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey(l)).child(ContainerInList.class), + containerInListBuilder, + ctx); + return new ListInContainerBuilder().setId(l).setContainerInList(containerInListBuilder.build()).build(); + }) + .collect(Collectors.toList())); + return b.build(); + } + + private void readContainerInList(final InstanceIdentifier id, + final ContainerInListBuilder b, + final ReadContext readContext) { + b.setName(id.firstKeyOf(ListInContainer.class).getId().toString()); + } +} diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/HoneycombWriteInfraTest.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/HoneycombWriteInfraTest.java new file mode 100644 index 000000000..90a18dd7a --- /dev/null +++ b/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/HoneycombWriteInfraTest.java @@ -0,0 +1,522 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import io.fd.honeycomb.data.DataModification; +import io.fd.honeycomb.translate.util.write.registry.FlatWriterRegistryBuilder; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.Writer; +import io.fd.honeycomb.translate.write.registry.WriterRegistry; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.junit.Test; +import org.mockito.InOrder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugmentBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithChoice; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithChoiceBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleAugmentBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainerBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.Choice; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.choice.c3.C3; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.choice.c3.C3Builder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedListKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainerBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.some.attributes.ContainerFromGrouping; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.some.attributes.ContainerFromGroupingBuilder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.TipProducingDataTree; +import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType; +import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory; + +/** + * Testing honeycomb writes from data tree up to mocked writers. + */ +public class HoneycombWriteInfraTest extends AbstractInfraTest { + + private TipProducingDataTree dataTree; + private WriterRegistry writerRegistry; + + Writer simpleContainerWriter = mockWriter(Ids.SIMPLE_CONTAINER_ID); + Writer complexAugmentWriter = mockWriter(Ids.COMPLEX_AUGMENT_ID); + Writer complexAugmentContainerWriter = mockWriter(Ids.COMPLEX_AUGMENT_CONTAINER_ID); + Writer simpleAugmentWriter = mockWriter(Ids.SIMPLE_AUGMENT_ID); + + Writer containerWithListWriter = mockWriter(Ids.CONTAINER_WITH_LIST_ID); + Writer listInContainerWriter = mockWriter(Ids.LIST_IN_CONTAINER_ID); + Writer containerInListWriter = mockWriter(Ids.CONTAINER_IN_LIST_ID); + Writer nestedListWriter = mockWriter(Ids.NESTED_LIST_ID); + + Writer containerWithChoiceWriter = mockWriter(Ids.CONTAINER_WITH_CHOICE_ID); + Writer containerFromGroupingWriter = mockWriter(Ids.CONTAINER_FROM_GROUPING_ID); + Writer c3Writer = mockWriter(Ids.C3_ID); + + + private static Writer mockWriter(final InstanceIdentifier id) { + final Writer mock = (Writer) mock(Writer.class); + when(mock.getManagedDataObjectType()).thenReturn(id); + return mock; + } + + @Override + void postSetup() { + initDataTree(); + initWriterRegistry(); + } + + private void initDataTree() { + dataTree = InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION); + dataTree.setSchemaContext(schemaContext); + } + + private void initWriterRegistry() { + writerRegistry = new FlatWriterRegistryBuilder() + .add(complexAugmentWriter) // unordered + .add(nestedListWriter) // 6 + .addAfter(listInContainerWriter, Ids.NESTED_LIST_ID) // 7 + .addAfter(containerInListWriter, Ids.LIST_IN_CONTAINER_ID) // 8 + .addAfter(containerWithListWriter, Ids.CONTAINER_IN_LIST_ID) // 9 + .addBefore(containerFromGroupingWriter, Ids.NESTED_LIST_ID) // 5 + .addBefore(containerWithChoiceWriter, Ids.CONTAINER_FROM_GROUPING_ID) // 4 + .addBefore(simpleContainerWriter, Ids.CONTAINER_WITH_CHOICE_ID) // 3 + .addBefore(c3Writer, Ids.SIMPLE_CONTAINER_ID) // 2 + .addBefore(simpleAugmentWriter, Ids.SIMPLE_CONTAINER_ID) // 2 + .addBefore(complexAugmentContainerWriter, Sets.newHashSet(Ids.C3_ID, Ids.SIMPLE_AUGMENT_ID)) // 1 + .build(); + } + + @Test + public void testWriteEmptyNonPresenceContainer() throws Exception { + final ModifiableDataTreeDelegator modifiableDataTreeDelegator = + new ModifiableDataTreeDelegator(serializer, dataTree, writerRegistry, contextBroker); + + final DataModification dataModification = modifiableDataTreeDelegator.newModification(); + final SimpleContainer data = new SimpleContainerBuilder() + .build(); + + final Map.Entry> normalizedNode = + serializer.toNormalizedNode(Ids.SIMPLE_CONTAINER_ID, data); + dataModification.write(normalizedNode.getKey(), normalizedNode.getValue()); + + dataModification.commit(); + + verify(simpleContainerWriter).getManagedDataObjectType(); + verifyNoMoreInteractions(simpleContainerWriter); + } + + @Test + public void testWriteEverything() throws Exception { + final ModifiableDataTreeDelegator modifiableDataTreeDelegator = + new ModifiableDataTreeDelegator(serializer, dataTree, writerRegistry, contextBroker); + + final DataModification dataModification = modifiableDataTreeDelegator.newModification(); + // Now write everything we can + writeSimpleContainer(dataModification); + writeContainerWithChoice(dataModification); + writeContainerWithList(dataModification); + dataModification.commit(); + + final Writer[] orderedWriters = getOrderedWriters(); + final InOrder inOrder = inOrder(orderedWriters); + verifyOrderedWrites(orderedWriters, inOrder); + } + + private void verifyOrderedWrites(final Writer[] orderedWriters, final InOrder inOrder) + throws WriteFailedException { + // TODO Modifications are not produced for nodes that do not contain any actual leaves (except when choice is a child) do we need those triggers ? + // Unordered + // verify(complexAugmentWriter).update(eq(COMPLEX_AUGMENT_ID), eq(null), eq(getComplexAugment()), any(WriteContext.class)); + // 1 + inOrder.verify(complexAugmentContainerWriter) + .update(eq(Ids.COMPLEX_AUGMENT_CONTAINER_ID), eq(null), eq(getComplexAugmentContainer()), any(WriteContext.class)); + // 2 + inOrder.verify(c3Writer) + .update(eq(Ids.C3_ID), eq(null), eq(getC3()), any(WriteContext.class)); + // 2 + verify(simpleAugmentWriter) + .update(eq(Ids.SIMPLE_AUGMENT_ID), eq(null), eq(getSimpleAugment()), any(WriteContext.class)); + // 3 + inOrder.verify(simpleContainerWriter) + .update(eq(Ids.SIMPLE_CONTAINER_ID), eq(null), eq(getSimpleContainer()), any(WriteContext.class)); + // 4 + inOrder.verify(containerWithChoiceWriter) + .update(eq(Ids.CONTAINER_WITH_CHOICE_ID), eq(null), eq(getContainerWithChoiceWithComplexCase()), any(WriteContext.class)); + // 5 + inOrder.verify(containerFromGroupingWriter) + .update(eq(Ids.CONTAINER_FROM_GROUPING_ID), eq(null), eq(getContainerFromGrouping()), any(WriteContext.class)); + + final KeyedInstanceIdentifier keyedListInContainer1 = + Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey((long) 1)); + final KeyedInstanceIdentifier keyedNestedList1 = + keyedListInContainer1.child(ContainerInList.class).child(NestedList.class, new NestedListKey("1")); + final KeyedInstanceIdentifier keyedListInContainer2 = + Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey((long) 2)); + final KeyedInstanceIdentifier keyedNestedList2 = + keyedListInContainer2.child(ContainerInList.class).child(NestedList.class, new NestedListKey("2")); + + // 6 - two items + inOrder.verify(nestedListWriter) + .update(eq(keyedNestedList1), eq(null), eq(getSingleNestedList("1")), any(WriteContext.class)); + verify(nestedListWriter) + .update(eq(keyedNestedList2), eq(null), eq(getSingleNestedList("2")), any(WriteContext.class)); + + // 7 - two items + inOrder.verify(listInContainerWriter) + .update(eq(keyedListInContainer1), eq(null), eq(getSingleListInContainer((long)1)), any(WriteContext.class)); + verify(listInContainerWriter) + .update(eq(keyedListInContainer2), eq(null), eq(getSingleListInContainer((long)2)), any(WriteContext.class)); + + // 8 + inOrder.verify(containerInListWriter) + .update(eq(keyedListInContainer1.child(ContainerInList.class)), eq(null), eq(getContainerInList("1")), any(WriteContext.class)); + verify(containerInListWriter) + .update(eq(keyedListInContainer2.child(ContainerInList.class)), eq(null), eq(getContainerInList("2")), any(WriteContext.class)); + + // 9 - Ignored because the container has no leaves, only complex child nodes + // inOrder.verify(containerWithListWriter) + // .update(eq(CONTAINER_WITH_LIST_ID), eq(null), eq(getContainerWithList()), any(WriteContext.class)); + + for (Writer orderedWriter : orderedWriters) { + verify(orderedWriter).getManagedDataObjectType(); + verifyNoMoreInteractions(orderedWriter); + } + } + + private Writer[] getOrderedWriters() { + return new Writer[]{complexAugmentWriter, // Unordered + complexAugmentContainerWriter, // 1 + c3Writer, // 2 + simpleAugmentWriter, // 2 + simpleContainerWriter, // 3 + containerWithChoiceWriter, // 4 + containerFromGroupingWriter, // 5 + nestedListWriter, // 6 + listInContainerWriter, // 7 + containerInListWriter, // 8 + containerWithListWriter}; + } + + @Test + public void testDeletes() throws Exception { + final ModifiableDataTreeDelegator modifiableDataTreeDelegator = + new ModifiableDataTreeDelegator(serializer, dataTree, writerRegistry, contextBroker); + + DataModification dataModification = modifiableDataTreeDelegator.newModification(); + // Now write everything we can + writeSimpleContainer(dataModification); + writeContainerWithChoice(dataModification); + writeContainerWithList(dataModification); + dataModification.commit(); + // Verify writes to be able to verifyNoMore interactions at the end + verifyOrderedWrites(getOrderedWriters(), inOrder(getOrderedWriters())); + + dataModification = modifiableDataTreeDelegator.newModification(); + deleteSimpleContainer(dataModification); + deleteContainerWithChoice(dataModification); + deleteContainerWithList(dataModification); + dataModification.commit(); + + final Writer[] orderedWriters = getOrderedWriters(); + Collections.reverse(Arrays.asList(orderedWriters)); + final InOrder inOrder = inOrder(orderedWriters); + + final KeyedInstanceIdentifier keyedListInContainer1 = + Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey((long) 1)); + final KeyedInstanceIdentifier keyedNestedList1 = + keyedListInContainer1.child(ContainerInList.class).child(NestedList.class, new NestedListKey("1")); + final KeyedInstanceIdentifier keyedListInContainer2 = + Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey((long) 2)); + final KeyedInstanceIdentifier keyedNestedList2 = + keyedListInContainer2.child(ContainerInList.class).child(NestedList.class, new NestedListKey("2")); + + // Deletes are handled in reverse order + // 1 + inOrder.verify(containerInListWriter) + .update(eq(keyedListInContainer1.child(ContainerInList.class)), eq(getContainerInList("1")), eq(null), any(WriteContext.class)); + verify(containerInListWriter) + .update(eq(keyedListInContainer2.child(ContainerInList.class)), eq(getContainerInList("2")), eq(null), any(WriteContext.class)); + + // 2 + inOrder.verify(listInContainerWriter) + .update(eq(keyedListInContainer1), eq(getSingleListInContainer((long)1)), eq(null), any(WriteContext.class)); + verify(listInContainerWriter) + .update(eq(keyedListInContainer2), eq(getSingleListInContainer((long)2)), eq(null), any(WriteContext.class)); + + // 3 + inOrder.verify(nestedListWriter) + .update(eq(keyedNestedList1), eq(getSingleNestedList("1")), eq(null), any(WriteContext.class)); + verify(nestedListWriter) + .update(eq(keyedNestedList2), eq(getSingleNestedList("2")), eq(null), any(WriteContext.class)); + // 4 + inOrder.verify(containerFromGroupingWriter) + .update(eq(Ids.CONTAINER_FROM_GROUPING_ID), eq(getContainerFromGrouping()), eq(null), any(WriteContext.class)); + // 5 + inOrder.verify(containerWithChoiceWriter) + .update(eq(Ids.CONTAINER_WITH_CHOICE_ID), eq(getContainerWithChoiceWithComplexCase()), eq(null), any(WriteContext.class)); + // 6 + inOrder.verify(simpleContainerWriter) + .update(eq(Ids.SIMPLE_CONTAINER_ID), eq(getSimpleContainer()), eq(null), any(WriteContext.class)); + // 7 + verify(simpleAugmentWriter) + .update(eq(Ids.SIMPLE_AUGMENT_ID), eq(getSimpleAugment()), eq(null), any(WriteContext.class)); + // 8 + inOrder.verify(c3Writer) + .update(eq(Ids.C3_ID), eq(getC3()), eq(null), any(WriteContext.class)); + // 9 + inOrder.verify(complexAugmentContainerWriter) + .update(eq(Ids.COMPLEX_AUGMENT_CONTAINER_ID), eq(getComplexAugmentContainer()), eq(null), any(WriteContext.class)); + + for (Writer orderedWriter : orderedWriters) { + verify(orderedWriter).getManagedDataObjectType(); + verifyNoMoreInteractions(orderedWriter); + } + } + + private void writeContainerWithList(final DataModification dataModification) { + final Map.Entry> normalizedNode = + serializer.toNormalizedNode(Ids.CONTAINER_WITH_LIST_ID, getContainerWithList()); + dataModification.write(normalizedNode.getKey(), normalizedNode.getValue()); + } + + private void deleteContainerWithList(final DataModification dataModification) { + dataModification.delete(serializer.toYangInstanceIdentifier(Ids.CONTAINER_WITH_LIST_ID)); + } + + private ContainerWithList getContainerWithList() { + return new ContainerWithListBuilder() + .setListInContainer(getListInContainer((long)1, (long)2)) + .build(); + } + + private List getListInContainer(final Long... keys) { + final ArrayList objects = Lists.newArrayList(); + for (Long key : keys) { + objects.add(getSingleListInContainer(key)); + } + return objects; + } + + private ListInContainer getSingleListInContainer(final Long key) { + return new ListInContainerBuilder() + .setId(key) + .setContainerInList(getContainerInList(Long.toString(key))) + .build(); + } + + private ContainerInList getContainerInList(String... nestedKeys) { + return new ContainerInListBuilder() + .setName("inlist") + .setNestedList(getNestedList(nestedKeys)) + .build(); + } + + private List getNestedList(String... keys) { + final ArrayList nestedList = new ArrayList<>(); + for (String key : keys) { + nestedList.add(getSingleNestedList(key)); + } + return nestedList; + } + + private NestedList getSingleNestedList(final String key) { + return new NestedListBuilder() + .setNestedId(key) + .setNestedName(key) + .build(); + } + + private void writeContainerWithChoice(final DataModification dataModification) { + writeContainerWithChoice(dataModification, getContainerWithChoiceWithComplexCase()); + } + + + private void deleteContainerWithChoice(final DataModification dataModification) { + dataModification.delete(serializer.toYangInstanceIdentifier(Ids.CONTAINER_WITH_CHOICE_ID)); + } + + private void writeContainerWithChoice(final DataModification dataModification, + final ContainerWithChoice containerWithChoice) { + final Map.Entry> normalizedNode = + serializer.toNormalizedNode(Ids.CONTAINER_WITH_CHOICE_ID, containerWithChoice); + dataModification.write(normalizedNode.getKey(), normalizedNode.getValue()); + } + + private ContainerWithChoice getContainerWithChoiceWithComplexCase() { + return new ContainerWithChoiceBuilder() + .setLeafFromGrouping("fromG") + .setName("name") + .setContainerFromGrouping(getContainerFromGrouping()) + .setChoice(getComplexCase()) + .build(); + } + + private Choice getComplexCase() { + return new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.choice.C3Builder() + .setC3(getC3()) + .build(); + } + + private C3 getC3() { + return new C3Builder() + .setName("c3") + .build(); + } + + private void writeContainerFromGrouping(final DataModification dataModification) { + final Map.Entry> normalizedNode = + serializer.toNormalizedNode(Ids.CONTAINER_FROM_GROUPING_ID, getContainerFromGrouping()); + dataModification.write(normalizedNode.getKey(), normalizedNode.getValue()); + } + + private ContainerFromGrouping getContainerFromGrouping() { + return new ContainerFromGroupingBuilder() + .setLeafInContainerFromGrouping(111) + .build(); + } + + + private void writeSimpleContainer(final DataModification dataModification) { + final Map.Entry> normalizedNode = + serializer.toNormalizedNode(Ids.SIMPLE_CONTAINER_ID, getSimpleContainer()); + dataModification.write(normalizedNode.getKey(), normalizedNode.getValue()); + } + + private void deleteSimpleContainer(final DataModification dataModification) { + final YangInstanceIdentifier yangId = + serializer.toYangInstanceIdentifier(Ids.SIMPLE_CONTAINER_ID); + dataModification.delete(yangId); + } + + private SimpleContainer getSimpleContainer() { + return new SimpleContainerBuilder() + .setSimpleContainerName("n") + .addAugmentation(SimpleAugment.class, getSimpleAugment()) + .addAugmentation(ComplexAugment.class, getComplexAugment()) + .build(); + } + + private ComplexAugment getComplexAugment() { + return new ComplexAugmentBuilder() + .setComplexAugmentContainer(getComplexAugmentContainer()) + .build(); + } + + private ComplexAugmentContainer getComplexAugmentContainer() { + return new ComplexAugmentContainerBuilder().setSomeLeaf("s").build(); + } + + private SimpleAugment getSimpleAugment() { + return new SimpleAugmentBuilder().setSimpleAugmentLeaf("a").build(); + } + + @Test + public void testWriteAndDeleteInTx() throws Exception { + final ModifiableDataTreeDelegator modifiableDataTreeDelegator = + new ModifiableDataTreeDelegator(serializer, dataTree, writerRegistry, contextBroker); + + final DataModification dataModification = modifiableDataTreeDelegator.newModification(); + // Now write everything we can + writeSimpleContainer(dataModification); + deleteSimpleContainer(dataModification); + dataModification.commit(); + + verify(simpleContainerWriter).getManagedDataObjectType(); + // No modification + verifyNoMoreInteractions(simpleContainerWriter); + } + + @Test + public void testSubtreeWriter() throws Exception { + writerRegistry = new FlatWriterRegistryBuilder() + // Handles also container from grouping + .subtreeAdd(Sets.newHashSet(Ids.CONTAINER_FROM_GROUPING_ID), containerWithChoiceWriter) + .build(); + + final ModifiableDataTreeDelegator modifiableDataTreeDelegator = + new ModifiableDataTreeDelegator(serializer, dataTree, writerRegistry, contextBroker); + + final ContainerWithChoice containerWithChoice = + new ContainerWithChoiceBuilder().setContainerFromGrouping(getContainerFromGrouping()).build(); + + // Test write subtree node + DataModification dataModification = modifiableDataTreeDelegator.newModification(); + writeContainerFromGrouping(dataModification); + dataModification.commit(); + + verify(containerWithChoiceWriter, atLeastOnce()).getManagedDataObjectType(); + verify(containerWithChoiceWriter) + .update(eq(Ids.CONTAINER_WITH_CHOICE_ID), eq(null), eq(containerWithChoice), any(WriteContext.class)); + verifyNoMoreInteractions(containerWithChoiceWriter); + + // Test delete sub-node + dataModification = modifiableDataTreeDelegator.newModification(); + final ContainerWithChoice containerWithChoiceEmpty = new ContainerWithChoiceBuilder().build(); + deleteContainerFromGrouping(dataModification); + dataModification.commit(); + + verify(containerWithChoiceWriter, atLeastOnce()).getManagedDataObjectType(); + verify(containerWithChoiceWriter) + .update(eq(Ids.CONTAINER_WITH_CHOICE_ID), eq(containerWithChoice), eq(containerWithChoiceEmpty), any(WriteContext.class)); + verifyNoMoreInteractions(containerWithChoiceWriter); + + // Test write with subtree node that's not handled by subtree writer + dataModification = modifiableDataTreeDelegator.newModification(); + writeContainerWithChoice(dataModification); + try { + dataModification.commit(); + fail("Missing writer for C3 should occur"); + } catch (IllegalArgumentException e) { + return; + } + } + + private void deleteContainerFromGrouping(final DataModification dataModification) { + dataModification.delete(serializer.toYangInstanceIdentifier(Ids.CONTAINER_FROM_GROUPING_ID)); + } +} \ No newline at end of file diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/Ids.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/Ids.java new file mode 100644 index 000000000..1ef83a0ed --- /dev/null +++ b/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/Ids.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithChoice; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.choice.c3.C3; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.some.attributes.ContainerFromGrouping; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Instance identifiers referencing all complex nodes within IT test-model. + */ +interface Ids { + + // Simple container + // ORDER = 3 + InstanceIdentifier SIMPLE_CONTAINER_ID = InstanceIdentifier.create(SimpleContainer.class); + // 2 + InstanceIdentifier SIMPLE_AUGMENT_ID = SIMPLE_CONTAINER_ID.augmentation(SimpleAugment.class); + // UNORDERED + InstanceIdentifier COMPLEX_AUGMENT_ID = SIMPLE_CONTAINER_ID.augmentation(ComplexAugment.class); + // 1 + InstanceIdentifier COMPLEX_AUGMENT_CONTAINER_ID = COMPLEX_AUGMENT_ID.child(ComplexAugmentContainer.class); + // Container with list + // 9 + InstanceIdentifier CONTAINER_WITH_LIST_ID = InstanceIdentifier.create(ContainerWithList.class); + // 7 + InstanceIdentifier LIST_IN_CONTAINER_ID = CONTAINER_WITH_LIST_ID.child(ListInContainer.class); + // 8 + InstanceIdentifier CONTAINER_IN_LIST_ID = LIST_IN_CONTAINER_ID.child(ContainerInList.class); + // 6 + InstanceIdentifier NESTED_LIST_ID = CONTAINER_IN_LIST_ID.child(NestedList.class); + // Container with choice + // 4 + InstanceIdentifier CONTAINER_WITH_CHOICE_ID = InstanceIdentifier.create(ContainerWithChoice.class); + // 2 + InstanceIdentifier C3_ID = CONTAINER_WITH_CHOICE_ID.child(C3.class); + // 5 + InstanceIdentifier CONTAINER_FROM_GROUPING_ID = CONTAINER_WITH_CHOICE_ID.child(ContainerFromGrouping.class); +} diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/AbstractInfraTest.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/AbstractInfraTest.java deleted file mode 100644 index 47644cc70..000000000 --- a/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/AbstractInfraTest.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import static org.mockito.Mockito.when; - -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; -import com.google.common.util.concurrent.Futures; -import java.util.Collections; -import java.util.Map; -import javassist.ClassPool; -import org.junit.Before; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.$YangModuleInfoImpl; -import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer; -import org.opendaylight.yangtools.binding.data.codec.gen.impl.DataObjectSerializerGenerator; -import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator; -import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry; -import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; -import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext; -import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; -import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; - -/** - * Base IT test infrastructure. - */ -abstract class AbstractInfraTest { - - protected BindingNormalizedNodeSerializer serializer; - protected SchemaContext schemaContext; - - @Mock - protected org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker; - @Mock - private org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction ctxTx; - - static BindingToNormalizedNodeCodec getSerializer(final ModuleInfoBackedContext moduleInfoBackedContext, - final SchemaContext schemaContext) { - final DataObjectSerializerGenerator serializerGenerator = new StreamWriterGenerator(JavassistUtils.forClassPool( - ClassPool.getDefault())); - final BindingNormalizedNodeCodecRegistry codecRegistry = new BindingNormalizedNodeCodecRegistry(serializerGenerator); - final BindingRuntimeContext ctx = - BindingRuntimeContext.create(moduleInfoBackedContext, schemaContext); - codecRegistry.onBindingRuntimeContextUpdated(ctx); - return new BindingToNormalizedNodeCodec(moduleInfoBackedContext, codecRegistry); - } - - static ModuleInfoBackedContext getSchemaContext() { - final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create(); - moduleInfoBackedContext.addModuleInfos(Collections.singleton($YangModuleInfoImpl.getInstance())); - return moduleInfoBackedContext; - } - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - when(contextBroker.newReadWriteTransaction()).thenReturn(ctxTx); - when(ctxTx.submit()).thenReturn(Futures.immediateCheckedFuture(null)); - - initSerializer(); - postSetup(); - } - - abstract void postSetup(); - - private void initSerializer() { - final ModuleInfoBackedContext moduleInfoBackedContext = getSchemaContext(); - schemaContext = moduleInfoBackedContext.tryToCreateSchemaContext().get(); - serializer = getSerializer(moduleInfoBackedContext, schemaContext); - } - - protected Multimap, ? extends DataObject> toBinding( - final NormalizedNode read) { - Multimap, DataObject> baNodes = HashMultimap.create(); - - for (DataContainerChild o : ((DataContainerNode) read).getValue()) { - final YangInstanceIdentifier yid = YangInstanceIdentifier.of(o.getNodeType()); - final Map.Entry, DataObject> baEntry = serializer.fromNormalizedNode(yid, o); - baNodes.put(baEntry.getKey(), baEntry.getValue()); - } - return baNodes; - } -} diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombReadInfraTest.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombReadInfraTest.java deleted file mode 100644 index b1f3a196a..000000000 --- a/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombReadInfraTest.java +++ /dev/null @@ -1,460 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyListOf; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -import com.google.common.base.Optional; -import com.google.common.collect.Lists; -import com.google.common.collect.Multimap; -import com.google.common.util.concurrent.CheckedFuture; -import io.fd.honeycomb.v3po.translate.impl.read.GenericListReader; -import io.fd.honeycomb.v3po.translate.read.ListReader; -import io.fd.honeycomb.v3po.translate.read.ReadContext; -import io.fd.honeycomb.v3po.translate.read.ReadFailedException; -import io.fd.honeycomb.v3po.translate.read.Reader; -import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistry; -import io.fd.honeycomb.v3po.translate.util.RWUtils; -import io.fd.honeycomb.v3po.translate.util.read.ReflexiveListReaderCustomizer; -import io.fd.honeycomb.v3po.translate.util.read.ReflexiveReader; -import io.fd.honeycomb.v3po.translate.util.read.registry.CompositeReaderRegistryBuilder; -import java.lang.reflect.InvocationTargetException; -import java.util.List; -import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import org.junit.Test; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.exceptions.misusing.MockitoConfigurationException; -import org.mockito.invocation.InvocationOnMock; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugment; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugmentBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithListBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleAugment; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleAugmentBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainer; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainerBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerKey; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInList; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInListBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedList; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedListBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedListKey; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainer; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainerBuilder; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.Identifiable; -import org.opendaylight.yangtools.yang.binding.Identifier; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; - -public class HoneycombReadInfraTest extends AbstractInfraTest { - - @Mock - private ReadContext ctx; - private ReaderRegistry registry; - - // 1 - private Reader simpleContainerReader = - mockReader(Ids.SIMPLE_CONTAINER_ID, this::readSimpleContainer, SimpleContainerBuilder.class); - // 1.1 - private Reader simpleAugmentReader = - mockReader(Ids.SIMPLE_AUGMENT_ID, this::readSimpleAugment, SimpleAugmentBuilder.class); - // 1.2 - // Noop reader(no real attributes) - private Reader complexAugmentReader = - mockReader(Ids.COMPLEX_AUGMENT_ID, HoneycombReadInfraTest::noopRead, ComplexAugmentBuilder.class); - // 1.2.1 - private Reader complexAugmentContainerReader = - mockReader(Ids.COMPLEX_AUGMENT_CONTAINER_ID, this::readComplexAugmentContainer, ComplexAugmentContainerBuilder.class); - // 2 - // Noop reader(no real attributes) - private Reader containerWithListReader = - mockReader(Ids.CONTAINER_WITH_LIST_ID, HoneycombReadInfraTest::noopRead, ContainerWithListBuilder.class); - // 2.1 - private ListReader listInContainerReader = - mockListReader(Ids.LIST_IN_CONTAINER_ID, this::readListInContainer, this::getListInContainerIds, - ListInContainerBuilder.class); - // 2.1.1 - private Reader containerInListReader = - mockReader(Ids.CONTAINER_IN_LIST_ID, this::readContainerInList, ContainerInListBuilder.class); - // 2.1.1.1 - private ListReader nestedListReader = - mockListReader(Ids.NESTED_LIST_ID, this::readNestedList, this::getNestedListIds, - NestedListBuilder.class); - - @Override - void postSetup() { - initReaderRegistry(); - } - - private void initReaderRegistry() { - registry = new CompositeReaderRegistryBuilder() - .add(containerWithListReader) // 2 - .addBefore(simpleContainerReader, Ids.CONTAINER_WITH_LIST_ID) // 1 - .add(simpleAugmentReader) // 1.1 - .addAfter(complexAugmentReader, Ids.SIMPLE_AUGMENT_ID) // 1.2 - .add(complexAugmentContainerReader) // 1.2.1 - .add(listInContainerReader) // 2.1 - .add(containerInListReader) // 2.1.1 - .addBefore(nestedListReader, Ids.SIMPLE_AUGMENT_ID) // 2.1.1.1 - Before relationship should be ignored - .build(); - } - - private Reader[] getOrderedReaders() { - return new Reader[]{simpleContainerReader, // 1 - simpleAugmentReader, // 1.1 - complexAugmentReader, // 1.2 - complexAugmentContainerReader, // 1.2.1 - containerWithListReader, // 2 - listInContainerReader, // 2.1 - containerInListReader, // 2.1.1 - nestedListReader}; // 2.1.1.1 - } - - @Test - public void testReadAll() throws Exception { - final ReadableDataTreeDelegator readableDataTreeDelegator = - new ReadableDataTreeDelegator(serializer, schemaContext, registry, contextBroker); - final CheckedFuture>, org.opendaylight.controller.md.sal.common.api.data.ReadFailedException> - read = readableDataTreeDelegator.read(YangInstanceIdentifier.EMPTY); - - final Multimap, ? extends DataObject> readAll = - toBinding(read.checkedGet().get()); - assertThat(readAll.size(), is(2)); - assertTrue(readAll.containsKey(Ids.CONTAINER_WITH_LIST_ID)); - assertEquals(readContainerWithList(), readAll.get(Ids.CONTAINER_WITH_LIST_ID).stream().findFirst().get()); - assertTrue(readAll.containsKey(Ids.SIMPLE_CONTAINER_ID)); - assertEquals(readSimpleContainer(), readAll.get(Ids.SIMPLE_CONTAINER_ID).stream().findFirst().get()); - - final Reader[] ordered = getOrderedReaders(); - final InOrder inOrder = inOrder(ordered); - - final List listInContainerKeys = getListInContainerIds(Ids.LIST_IN_CONTAINER_ID, ctx); - - verifyRootReader(inOrder, simpleContainerReader, Ids.SIMPLE_CONTAINER_ID, SimpleContainerBuilder.class); - verifyLeafChildReader(inOrder, simpleAugmentReader, Ids.SIMPLE_AUGMENT_ID); - verifyCompositeChildReader(inOrder, complexAugmentReader, ComplexAugmentBuilder.class, Ids.COMPLEX_AUGMENT_ID); - verifyLeafChildReader(inOrder, complexAugmentContainerReader, Ids.COMPLEX_AUGMENT_CONTAINER_ID); - verifyRootReader(inOrder, containerWithListReader, Ids.CONTAINER_WITH_LIST_ID, ContainerWithListBuilder.class); - verifyCompositeListReader(inOrder, listInContainerReader, Ids.LIST_IN_CONTAINER_ID, - listInContainerKeys, ListInContainerBuilder.class); - - for (ListInContainerKey k : listInContainerKeys) { - final InstanceIdentifier id = - Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, k).child(ContainerInList.class); - verifyCompositeChildReader(inOrder, containerInListReader, ContainerInListBuilder.class, id); - final InstanceIdentifier nestedId = id.child(NestedList.class); - verifyLeafListReader(inOrder, nestedListReader, nestedId); - } - - for (Reader reader : ordered) { - verifyNoMoreReadInteractions(reader); - } - } - - private > void verifyNoMoreReadInteractions(final Reader reader) { - verify(reader, atLeastOnce()).getManagedDataObjectType(); - verifyNoMoreInteractions(reader); - } - - private > void verifyCompositeChildReader(final InOrder inOrder, - final Reader reader, - final Class builderCls, - final InstanceIdentifier id) - throws ReadFailedException { - verifyRootReader(inOrder, reader, id, builderCls); - verify(reader, atLeastOnce()).merge(any(Builder.class), any(id.getTargetType())); - } - - private , K extends Identifier, B extends Builder> void verifyCompositeListReader( - final InOrder inOrder, - final ListReader reader, - final InstanceIdentifier id, - final List keys, - final Class builderCls) - throws ReadFailedException { - - // Id has to be wildcarded, since it was created using InstanceIdentifier.child() method - inOrder.verify(reader).getAllIds(eq(RWUtils.makeIidLastWildcarded(id)), any(ReadContext.class)); - keys.stream() - .map(k -> RWUtils.replaceLastInId(id, RWUtils.getCurrentIdItem(id, k))) - .forEach(keyedId -> { - try { - verify(reader).getBuilder(keyedId); - verify(reader).readCurrentAttributes(eq(keyedId), any(builderCls), any(ReadContext.class)); - } catch (ReadFailedException e) { throw new RuntimeException(e); } - }); - verify(reader, atLeastOnce()).merge(any(Builder.class), anyListOf(id.getTargetType())); - } - - private , K extends Identifier, B extends Builder> void verifyLeafListReader( - final InOrder inOrder, - final ListReader reader, - final InstanceIdentifier id) - throws ReadFailedException { - - // Id has to be wildcarded, since it was created using InstanceIdentifier.child() method - inOrder.verify(reader).readList(eq(RWUtils.makeIidLastWildcarded(id)), any(ReadContext.class)); - verify(reader, atLeastOnce()).merge(any(Builder.class), anyListOf(id.getTargetType())); - } - - private > void verifyRootReader(final InOrder inOrder, - final Reader reader, - final InstanceIdentifier id, - final Class builderCls) - throws ReadFailedException { - inOrder.verify(reader).readCurrentAttributes(eq(id), any(builderCls), any(ReadContext.class)); - verify(reader).getBuilder(id); - } - - - private > void verifyLeafChildReader(final InOrder inOrder, - final Reader reader, - final InstanceIdentifier... id) - throws ReadFailedException { - for (InstanceIdentifier singleId : id) { - inOrder.verify(reader).read(eq(singleId), any(ReadContext.class)); - verify(reader, atLeastOnce()).merge(any(Builder.class), any(singleId.getTargetType())); - } - } - - static > Reader mockReader(InstanceIdentifier id, - CurrentAttributesReader currentAttributesReader, - Class builderClass) { - final ReflexiveReader reflex = new ReflexiveReader(id, builderClass) { - - @Override - public void readCurrentAttributes(@Nonnull final InstanceIdentifier id, - @Nonnull final B builder, - @Nonnull final ReadContext ctx) - throws ReadFailedException { - currentAttributesReader.readCurrentAttributes(id, builder, ctx); - } - }; - - // Simple spy on top of this reflexive reader cannot be used, spy also checks protected methods for invocation - // but those cannot be verified by mockito - final Reader mock = mock(Reader.class); - try { - doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).read(any(InstanceIdentifier.class), any(ReadContext.class)); - doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock) - .readCurrentAttributes(any(InstanceIdentifier.class), any(builderClass), any(ReadContext.class)); - doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).merge(any(Builder.class), any(id.getTargetType())); - doReturn(id).when(mock).getManagedDataObjectType(); - doReturn(builderClass.newInstance()).when(mock).getBuilder(any(InstanceIdentifier.class)); - } catch (Exception e) { - throw new RuntimeException(e); - } - - return mock; - } - - static , K extends Identifier, B extends Builder> ListReader mockListReader( - InstanceIdentifier id, - CurrentAttributesReader currentAttributesReader, - ListKeysReader listKeysReader, - Class builderClass) { - - ListReader reflex = new GenericListReader<>(id, - new ReflexiveListReaderCustomizer(id.getTargetType(), builderClass) { - - @Nonnull - @Override - public List getAllIds(@Nonnull final InstanceIdentifier id, @Nonnull final ReadContext context) - throws ReadFailedException { - return listKeysReader.getAllIds(id, context); - } - - @Override - public void readCurrentAttributes(final InstanceIdentifier id, final B builder, - final ReadContext context) - throws ReadFailedException { - currentAttributesReader.readCurrentAttributes(id, builder, context); - } - }); - - final ListReader mock = mock(ListReader.class /*, withSettings().verboseLogging()*/); - try { - // not using eq(id) instead using any(InstanceIdentifier.class) due to InstanceIdentifier.equals weird behavior - // with wildcarded instance identifiers for lists - doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).read(any(InstanceIdentifier.class), any(ReadContext.class)); - doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock) - .readCurrentAttributes(any(InstanceIdentifier.class), any(builderClass), any(ReadContext.class)); - doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).merge(any(Builder.class), any(id.getTargetType())); - doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).merge(any(Builder.class), anyListOf(id.getTargetType())); - doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).getAllIds(any(InstanceIdentifier.class), any(ReadContext.class)); - doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).readList(any(InstanceIdentifier.class), any(ReadContext.class)); - doReturn(id).when(mock).getManagedDataObjectType(); - doReturn(builderClass.newInstance()).when(mock).getBuilder(any(InstanceIdentifier.class)); - } catch (Exception e) { - throw new RuntimeException(e); - } - - return mock; - } - - private static > Object reflexiveAnswer(final Reader instance, - final InvocationOnMock i) { - try { - return i.getMethod().invoke(instance, i.getArguments()); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new MockitoConfigurationException("Unable to invoke stubbed method invocation: " + i + " on " + instance); - } - } - - @FunctionalInterface - interface CurrentAttributesReader> { - void readCurrentAttributes(@Nonnull final InstanceIdentifier id, - @Nonnull final B builder, - @Nonnull final ReadContext ctx); - } - - @FunctionalInterface - interface ListKeysReader, K extends Identifier> { - List getAllIds(@Nonnull final InstanceIdentifier id, - @Nonnull final ReadContext ctx); - } - - - private void readSimpleContainer(final InstanceIdentifier id, - final SimpleContainerBuilder b, - final ReadContext readContext) { - b.setSimpleContainerName("simple"); - } - - private SimpleContainer readSimpleContainer() { - final SimpleContainerBuilder b = new SimpleContainerBuilder(); - readSimpleContainer(Ids.SIMPLE_CONTAINER_ID, b, ctx); - b.addAugmentation(SimpleAugment.class, readSimpleAugment()); - b.addAugmentation(ComplexAugment.class, - new ComplexAugmentBuilder().setComplexAugmentContainer(readComplexAugmentContainer()).build()); - return b.build(); - } - - private void readSimpleAugment(final InstanceIdentifier id, - final SimpleAugmentBuilder b, - final ReadContext readContext) { - b.setSimpleAugmentLeaf("simple augment"); - } - - private SimpleAugment readSimpleAugment() { - final SimpleAugmentBuilder b = new SimpleAugmentBuilder(); - readSimpleAugment(Ids.SIMPLE_AUGMENT_ID, b, ctx); - return b.build(); - } - - private void readComplexAugmentContainer(final InstanceIdentifier id, - final ComplexAugmentContainerBuilder b, - final ReadContext readContext) { - b.setSomeLeaf("nested container in augment"); - } - - private ComplexAugmentContainer readComplexAugmentContainer() { - final ComplexAugmentContainerBuilder b = new ComplexAugmentContainerBuilder(); - readComplexAugmentContainer(Ids.COMPLEX_AUGMENT_CONTAINER_ID, b, ctx); - return b.build(); - } - - private ContainerWithList readContainerWithList() { - final ContainerWithListBuilder b = new ContainerWithListBuilder(); - b.setListInContainer(readListInContainer()); - return b.build(); - } - - private List getListInContainerIds(final InstanceIdentifier id, - final ReadContext readContext) { - return Lists.newArrayList(1L, 2L) - .stream() - .map(ListInContainerKey::new) - .collect(Collectors.toList()); - } - - private List getNestedListIds(final InstanceIdentifier id, - final ReadContext readContext) { - return Lists.newArrayList("one", "two") - .stream() - .map(NestedListKey::new) - .collect(Collectors.toList()); - } - - private void readListInContainer(final InstanceIdentifier id, - final ListInContainerBuilder b, - final ReadContext readContext) { - final ListInContainerKey key = id.firstKeyOf(ListInContainer.class); - b.setId(key.getId()); - final ContainerInListBuilder cilBuilder = new ContainerInListBuilder(); - readContainerInList(id.child(ContainerInList.class), cilBuilder, readContext); - b.setContainerInList(cilBuilder.build()); - } - - private void readNestedList(final InstanceIdentifier id, - final NestedListBuilder b, - final ReadContext readContext) { - final NestedListKey key = id.firstKeyOf(NestedList.class); - b.setNestedId(key.getNestedId()); - b.setNestedName(key.getNestedId() + "name"); - } - - private void readContainerInList(final InstanceIdentifier id, - final ContainerInListBuilder b, - final ReadContext readContext) { - b.setName(id.firstKeyOf(ListInContainer.class).getId().toString()); - b.setNestedList(getNestedListIds(Ids.NESTED_LIST_ID, ctx).stream() - .map(key -> id.child(NestedList.class, key)) - .map(nestedId -> { - final NestedListBuilder nestedB = new NestedListBuilder(); - readNestedList(nestedId, nestedB, readContext); - return nestedB.build(); - }) - .collect(Collectors.toList())); - } - - private List readListInContainer() { - return getListInContainerIds(Ids.LIST_IN_CONTAINER_ID, ctx).stream() - .map(key -> Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, key)) - .map(id -> { - final ListInContainerBuilder b = new ListInContainerBuilder(); - readListInContainer(id, b, ctx); - return b.build(); - }).collect(Collectors.toList()); - } - - static > void noopRead(final InstanceIdentifier id, final B b, - final ReadContext readContext) { - // NOOP - } - -} diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombSubtreeReadInfraTest.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombSubtreeReadInfraTest.java deleted file mode 100644 index 8faeb84b6..000000000 --- a/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombSubtreeReadInfraTest.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; - -import com.google.common.base.Optional; -import com.google.common.collect.Lists; -import com.google.common.collect.Multimap; -import com.google.common.collect.Sets; -import com.google.common.util.concurrent.CheckedFuture; -import io.fd.honeycomb.v3po.translate.impl.read.GenericListReader; -import io.fd.honeycomb.v3po.translate.read.ListReader; -import io.fd.honeycomb.v3po.translate.read.ReadContext; -import io.fd.honeycomb.v3po.translate.read.ReadFailedException; -import io.fd.honeycomb.v3po.translate.read.Reader; -import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistry; -import io.fd.honeycomb.v3po.translate.util.read.ReflexiveListReaderCustomizer; -import io.fd.honeycomb.v3po.translate.util.read.registry.CompositeReaderRegistryBuilder; -import java.util.List; -import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import org.junit.Test; -import org.mockito.Mock; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithListBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerKey; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInList; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInListBuilder; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; - -public class HoneycombSubtreeReadInfraTest extends AbstractInfraTest { - - @Mock - private ReadContext ctx; - private ReaderRegistry registry; - - private Reader containerWithListReader = - HoneycombReadInfraTest.mockReader(Ids.CONTAINER_WITH_LIST_ID, this::readSubtree, ContainerWithListBuilder.class); - - private ListReader listInContainerReader = - new GenericListReader<>(Ids.LIST_IN_CONTAINER_ID, - new ReflexiveListReaderCustomizer(Ids.LIST_IN_CONTAINER_ID.getTargetType(), ListInContainerBuilder.class) { - - @Nonnull - @Override - public List getAllIds(@Nonnull final InstanceIdentifier id, - @Nonnull final ReadContext context) - throws ReadFailedException { - // FIXME this is the only way of extending subtree reader via its list child - // Reflexive list reader has to be used in place of the list(managed by subtree reader perent) - // to enable further children readers. However, it will not work out of the box, because - // reflexive list reader has no way to tell what are the IDs to correctly invoke its children. - // Only way is to override the getAllIds method in reflexive reader and return the same list - // as parent used (this can be done using cache or repeated dump call) - return Lists.newArrayList(new ListInContainerKey(1L), new ListInContainerKey(2L)); - } - - @Override - public void readCurrentAttributes(final InstanceIdentifier id, - final ListInContainerBuilder builder, - final ReadContext context) throws ReadFailedException { - super.readCurrentAttributes(id, builder, context); - builder.setKey(id.firstKeyOf(ListInContainer.class)); - } - }); - - private Reader containerInListReader = - HoneycombReadInfraTest.mockReader(Ids.CONTAINER_IN_LIST_ID, this::readContainerInList, ContainerInListBuilder.class); - - // TODO Test subtree readers especially composite structure where readers are under subtree reader - - @Override - void postSetup() { - initReaderRegistry(); - } - - private void initReaderRegistry() { - registry = new CompositeReaderRegistryBuilder() - // Subtree reader handling its child list - .subtreeAdd(Sets.newHashSet(Ids.LIST_IN_CONTAINER_ID), containerWithListReader) - // Reflexive - .add(listInContainerReader) - .add(containerInListReader) - .build(); - } - - @Test - public void testReadAll() throws Exception { - final ReadableDataTreeDelegator readableDataTreeDelegator = - new ReadableDataTreeDelegator(serializer, schemaContext, registry, contextBroker); - final CheckedFuture>, org.opendaylight.controller.md.sal.common.api.data.ReadFailedException> - read = readableDataTreeDelegator.read(YangInstanceIdentifier.EMPTY); - - final Multimap, ? extends DataObject> readAll = - toBinding(read.checkedGet().get()); - assertThat(readAll.size(), is(1)); - assertEquals(readEntireSubtree(), readAll.get(Ids.CONTAINER_WITH_LIST_ID).stream().findFirst().get()); - } - - private void readSubtree(final InstanceIdentifier id, - final ContainerWithListBuilder b, - final ReadContext readContext) { - b.setListInContainer(Lists.newArrayList(1L, 2L).stream() - .map(l -> new ListInContainerBuilder().setId(l).build()) - .collect(Collectors.toList())); - } - - private ContainerWithList readEntireSubtree() { - final ContainerWithListBuilder b = new ContainerWithListBuilder(); - b.setListInContainer(Lists.newArrayList(1L, 2L).stream() - .map(l -> { - final ContainerInListBuilder containerInListBuilder = new ContainerInListBuilder(); - readContainerInList( - Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey(l)).child(ContainerInList.class), - containerInListBuilder, - ctx); - return new ListInContainerBuilder().setId(l).setContainerInList(containerInListBuilder.build()).build(); - }) - .collect(Collectors.toList())); - return b.build(); - } - - private void readContainerInList(final InstanceIdentifier id, - final ContainerInListBuilder b, - final ReadContext readContext) { - b.setName(id.firstKeyOf(ListInContainer.class).getId().toString()); - } -} diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombWriteInfraTest.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombWriteInfraTest.java deleted file mode 100644 index b3a06ec46..000000000 --- a/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombWriteInfraTest.java +++ /dev/null @@ -1,521 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; -import io.fd.honeycomb.v3po.data.DataModification; -import io.fd.honeycomb.v3po.translate.util.write.registry.FlatWriterRegistryBuilder; -import io.fd.honeycomb.v3po.translate.write.WriteContext; -import io.fd.honeycomb.v3po.translate.write.Writer; -import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistry; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import org.junit.Test; -import org.mockito.InOrder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugment; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugmentBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithChoice; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithChoiceBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithListBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleAugment; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleAugmentBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainer; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainerBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.Choice; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.choice.c3.C3; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.choice.c3.C3Builder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerKey; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInList; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInListBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedList; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedListBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedListKey; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainer; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainerBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.some.attributes.ContainerFromGrouping; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.some.attributes.ContainerFromGroupingBuilder; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.TipProducingDataTree; -import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType; -import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory; - -/** - * Testing honeycomb writes from data tree up to mocked writers. - */ -public class HoneycombWriteInfraTest extends AbstractInfraTest { - - private TipProducingDataTree dataTree; - private WriterRegistry writerRegistry; - - Writer simpleContainerWriter = mockWriter(Ids.SIMPLE_CONTAINER_ID); - Writer complexAugmentWriter = mockWriter(Ids.COMPLEX_AUGMENT_ID); - Writer complexAugmentContainerWriter = mockWriter(Ids.COMPLEX_AUGMENT_CONTAINER_ID); - Writer simpleAugmentWriter = mockWriter(Ids.SIMPLE_AUGMENT_ID); - - Writer containerWithListWriter = mockWriter(Ids.CONTAINER_WITH_LIST_ID); - Writer listInContainerWriter = mockWriter(Ids.LIST_IN_CONTAINER_ID); - Writer containerInListWriter = mockWriter(Ids.CONTAINER_IN_LIST_ID); - Writer nestedListWriter = mockWriter(Ids.NESTED_LIST_ID); - - Writer containerWithChoiceWriter = mockWriter(Ids.CONTAINER_WITH_CHOICE_ID); - Writer containerFromGroupingWriter = mockWriter(Ids.CONTAINER_FROM_GROUPING_ID); - Writer c3Writer = mockWriter(Ids.C3_ID); - - - private static Writer mockWriter(final InstanceIdentifier id) { - final Writer mock = (Writer) mock(Writer.class); - when(mock.getManagedDataObjectType()).thenReturn(id); - return mock; - } - - @Override - void postSetup() { - initDataTree(); - initWriterRegistry(); - } - - private void initDataTree() { - dataTree = InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION); - dataTree.setSchemaContext(schemaContext); - } - - private void initWriterRegistry() { - writerRegistry = new FlatWriterRegistryBuilder() - .add(complexAugmentWriter) // unordered - .add(nestedListWriter) // 6 - .addAfter(listInContainerWriter, Ids.NESTED_LIST_ID) // 7 - .addAfter(containerInListWriter, Ids.LIST_IN_CONTAINER_ID) // 8 - .addAfter(containerWithListWriter, Ids.CONTAINER_IN_LIST_ID) // 9 - .addBefore(containerFromGroupingWriter, Ids.NESTED_LIST_ID) // 5 - .addBefore(containerWithChoiceWriter, Ids.CONTAINER_FROM_GROUPING_ID) // 4 - .addBefore(simpleContainerWriter, Ids.CONTAINER_WITH_CHOICE_ID) // 3 - .addBefore(c3Writer, Ids.SIMPLE_CONTAINER_ID) // 2 - .addBefore(simpleAugmentWriter, Ids.SIMPLE_CONTAINER_ID) // 2 - .addBefore(complexAugmentContainerWriter, Sets.newHashSet(Ids.C3_ID, Ids.SIMPLE_AUGMENT_ID)) // 1 - .build(); - } - - @Test - public void testWriteEmptyNonPresenceContainer() throws Exception { - final ModifiableDataTreeDelegator modifiableDataTreeDelegator = - new ModifiableDataTreeDelegator(serializer, dataTree, writerRegistry, contextBroker); - - final DataModification dataModification = modifiableDataTreeDelegator.newModification(); - final SimpleContainer data = new SimpleContainerBuilder() - .build(); - - final Map.Entry> normalizedNode = - serializer.toNormalizedNode(Ids.SIMPLE_CONTAINER_ID, data); - dataModification.write(normalizedNode.getKey(), normalizedNode.getValue()); - - dataModification.commit(); - - verify(simpleContainerWriter).getManagedDataObjectType(); - verifyNoMoreInteractions(simpleContainerWriter); - } - - @Test - public void testWriteEverything() throws Exception { - final ModifiableDataTreeDelegator modifiableDataTreeDelegator = - new ModifiableDataTreeDelegator(serializer, dataTree, writerRegistry, contextBroker); - - final DataModification dataModification = modifiableDataTreeDelegator.newModification(); - // Now write everything we can - writeSimpleContainer(dataModification); - writeContainerWithChoice(dataModification); - writeContainerWithList(dataModification); - dataModification.commit(); - - final Writer[] orderedWriters = getOrderedWriters(); - final InOrder inOrder = inOrder(orderedWriters); - verifyOrderedWrites(orderedWriters, inOrder); - } - - private void verifyOrderedWrites(final Writer[] orderedWriters, final InOrder inOrder) - throws io.fd.honeycomb.v3po.translate.write.WriteFailedException { - // TODO Modifications are not produced for nodes that do not contain any actual leaves (except when choice is a child) do we need those triggers ? - // Unordered - // verify(complexAugmentWriter).update(eq(COMPLEX_AUGMENT_ID), eq(null), eq(getComplexAugment()), any(WriteContext.class)); - // 1 - inOrder.verify(complexAugmentContainerWriter) - .update(eq(Ids.COMPLEX_AUGMENT_CONTAINER_ID), eq(null), eq(getComplexAugmentContainer()), any(WriteContext.class)); - // 2 - inOrder.verify(c3Writer) - .update(eq(Ids.C3_ID), eq(null), eq(getC3()), any(WriteContext.class)); - // 2 - verify(simpleAugmentWriter) - .update(eq(Ids.SIMPLE_AUGMENT_ID), eq(null), eq(getSimpleAugment()), any(WriteContext.class)); - // 3 - inOrder.verify(simpleContainerWriter) - .update(eq(Ids.SIMPLE_CONTAINER_ID), eq(null), eq(getSimpleContainer()), any(WriteContext.class)); - // 4 - inOrder.verify(containerWithChoiceWriter) - .update(eq(Ids.CONTAINER_WITH_CHOICE_ID), eq(null), eq(getContainerWithChoiceWithComplexCase()), any(WriteContext.class)); - // 5 - inOrder.verify(containerFromGroupingWriter) - .update(eq(Ids.CONTAINER_FROM_GROUPING_ID), eq(null), eq(getContainerFromGrouping()), any(WriteContext.class)); - - final KeyedInstanceIdentifier keyedListInContainer1 = - Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey((long) 1)); - final KeyedInstanceIdentifier keyedNestedList1 = - keyedListInContainer1.child(ContainerInList.class).child(NestedList.class, new NestedListKey("1")); - final KeyedInstanceIdentifier keyedListInContainer2 = - Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey((long) 2)); - final KeyedInstanceIdentifier keyedNestedList2 = - keyedListInContainer2.child(ContainerInList.class).child(NestedList.class, new NestedListKey("2")); - - // 6 - two items - inOrder.verify(nestedListWriter) - .update(eq(keyedNestedList1), eq(null), eq(getSingleNestedList("1")), any(WriteContext.class)); - verify(nestedListWriter) - .update(eq(keyedNestedList2), eq(null), eq(getSingleNestedList("2")), any(WriteContext.class)); - - // 7 - two items - inOrder.verify(listInContainerWriter) - .update(eq(keyedListInContainer1), eq(null), eq(getSingleListInContainer((long)1)), any(WriteContext.class)); - verify(listInContainerWriter) - .update(eq(keyedListInContainer2), eq(null), eq(getSingleListInContainer((long)2)), any(WriteContext.class)); - - // 8 - inOrder.verify(containerInListWriter) - .update(eq(keyedListInContainer1.child(ContainerInList.class)), eq(null), eq(getContainerInList("1")), any(WriteContext.class)); - verify(containerInListWriter) - .update(eq(keyedListInContainer2.child(ContainerInList.class)), eq(null), eq(getContainerInList("2")), any(WriteContext.class)); - - // 9 - Ignored because the container has no leaves, only complex child nodes - // inOrder.verify(containerWithListWriter) - // .update(eq(CONTAINER_WITH_LIST_ID), eq(null), eq(getContainerWithList()), any(WriteContext.class)); - - for (Writer orderedWriter : orderedWriters) { - verify(orderedWriter).getManagedDataObjectType(); - verifyNoMoreInteractions(orderedWriter); - } - } - - private Writer[] getOrderedWriters() { - return new Writer[]{complexAugmentWriter, // Unordered - complexAugmentContainerWriter, // 1 - c3Writer, // 2 - simpleAugmentWriter, // 2 - simpleContainerWriter, // 3 - containerWithChoiceWriter, // 4 - containerFromGroupingWriter, // 5 - nestedListWriter, // 6 - listInContainerWriter, // 7 - containerInListWriter, // 8 - containerWithListWriter}; - } - - @Test - public void testDeletes() throws Exception { - final ModifiableDataTreeDelegator modifiableDataTreeDelegator = - new ModifiableDataTreeDelegator(serializer, dataTree, writerRegistry, contextBroker); - - DataModification dataModification = modifiableDataTreeDelegator.newModification(); - // Now write everything we can - writeSimpleContainer(dataModification); - writeContainerWithChoice(dataModification); - writeContainerWithList(dataModification); - dataModification.commit(); - // Verify writes to be able to verifyNoMore interactions at the end - verifyOrderedWrites(getOrderedWriters(), inOrder(getOrderedWriters())); - - dataModification = modifiableDataTreeDelegator.newModification(); - deleteSimpleContainer(dataModification); - deleteContainerWithChoice(dataModification); - deleteContainerWithList(dataModification); - dataModification.commit(); - - final Writer[] orderedWriters = getOrderedWriters(); - Collections.reverse(Arrays.asList(orderedWriters)); - final InOrder inOrder = inOrder(orderedWriters); - - final KeyedInstanceIdentifier keyedListInContainer1 = - Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey((long) 1)); - final KeyedInstanceIdentifier keyedNestedList1 = - keyedListInContainer1.child(ContainerInList.class).child(NestedList.class, new NestedListKey("1")); - final KeyedInstanceIdentifier keyedListInContainer2 = - Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey((long) 2)); - final KeyedInstanceIdentifier keyedNestedList2 = - keyedListInContainer2.child(ContainerInList.class).child(NestedList.class, new NestedListKey("2")); - - // Deletes are handled in reverse order - // 1 - inOrder.verify(containerInListWriter) - .update(eq(keyedListInContainer1.child(ContainerInList.class)), eq(getContainerInList("1")), eq(null), any(WriteContext.class)); - verify(containerInListWriter) - .update(eq(keyedListInContainer2.child(ContainerInList.class)), eq(getContainerInList("2")), eq(null), any(WriteContext.class)); - - // 2 - inOrder.verify(listInContainerWriter) - .update(eq(keyedListInContainer1), eq(getSingleListInContainer((long)1)), eq(null), any(WriteContext.class)); - verify(listInContainerWriter) - .update(eq(keyedListInContainer2), eq(getSingleListInContainer((long)2)), eq(null), any(WriteContext.class)); - - // 3 - inOrder.verify(nestedListWriter) - .update(eq(keyedNestedList1), eq(getSingleNestedList("1")), eq(null), any(WriteContext.class)); - verify(nestedListWriter) - .update(eq(keyedNestedList2), eq(getSingleNestedList("2")), eq(null), any(WriteContext.class)); - // 4 - inOrder.verify(containerFromGroupingWriter) - .update(eq(Ids.CONTAINER_FROM_GROUPING_ID), eq(getContainerFromGrouping()), eq(null), any(WriteContext.class)); - // 5 - inOrder.verify(containerWithChoiceWriter) - .update(eq(Ids.CONTAINER_WITH_CHOICE_ID), eq(getContainerWithChoiceWithComplexCase()), eq(null), any(WriteContext.class)); - // 6 - inOrder.verify(simpleContainerWriter) - .update(eq(Ids.SIMPLE_CONTAINER_ID), eq(getSimpleContainer()), eq(null), any(WriteContext.class)); - // 7 - verify(simpleAugmentWriter) - .update(eq(Ids.SIMPLE_AUGMENT_ID), eq(getSimpleAugment()), eq(null), any(WriteContext.class)); - // 8 - inOrder.verify(c3Writer) - .update(eq(Ids.C3_ID), eq(getC3()), eq(null), any(WriteContext.class)); - // 9 - inOrder.verify(complexAugmentContainerWriter) - .update(eq(Ids.COMPLEX_AUGMENT_CONTAINER_ID), eq(getComplexAugmentContainer()), eq(null), any(WriteContext.class)); - - for (Writer orderedWriter : orderedWriters) { - verify(orderedWriter).getManagedDataObjectType(); - verifyNoMoreInteractions(orderedWriter); - } - } - - private void writeContainerWithList(final DataModification dataModification) { - final Map.Entry> normalizedNode = - serializer.toNormalizedNode(Ids.CONTAINER_WITH_LIST_ID, getContainerWithList()); - dataModification.write(normalizedNode.getKey(), normalizedNode.getValue()); - } - - private void deleteContainerWithList(final DataModification dataModification) { - dataModification.delete(serializer.toYangInstanceIdentifier(Ids.CONTAINER_WITH_LIST_ID)); - } - - private ContainerWithList getContainerWithList() { - return new ContainerWithListBuilder() - .setListInContainer(getListInContainer((long)1, (long)2)) - .build(); - } - - private List getListInContainer(final Long... keys) { - final ArrayList objects = Lists.newArrayList(); - for (Long key : keys) { - objects.add(getSingleListInContainer(key)); - } - return objects; - } - - private ListInContainer getSingleListInContainer(final Long key) { - return new ListInContainerBuilder() - .setId(key) - .setContainerInList(getContainerInList(Long.toString(key))) - .build(); - } - - private ContainerInList getContainerInList(String... nestedKeys) { - return new ContainerInListBuilder() - .setName("inlist") - .setNestedList(getNestedList(nestedKeys)) - .build(); - } - - private List getNestedList(String... keys) { - final ArrayList nestedList = new ArrayList<>(); - for (String key : keys) { - nestedList.add(getSingleNestedList(key)); - } - return nestedList; - } - - private NestedList getSingleNestedList(final String key) { - return new NestedListBuilder() - .setNestedId(key) - .setNestedName(key) - .build(); - } - - private void writeContainerWithChoice(final DataModification dataModification) { - writeContainerWithChoice(dataModification, getContainerWithChoiceWithComplexCase()); - } - - - private void deleteContainerWithChoice(final DataModification dataModification) { - dataModification.delete(serializer.toYangInstanceIdentifier(Ids.CONTAINER_WITH_CHOICE_ID)); - } - - private void writeContainerWithChoice(final DataModification dataModification, - final ContainerWithChoice containerWithChoice) { - final Map.Entry> normalizedNode = - serializer.toNormalizedNode(Ids.CONTAINER_WITH_CHOICE_ID, containerWithChoice); - dataModification.write(normalizedNode.getKey(), normalizedNode.getValue()); - } - - private ContainerWithChoice getContainerWithChoiceWithComplexCase() { - return new ContainerWithChoiceBuilder() - .setLeafFromGrouping("fromG") - .setName("name") - .setContainerFromGrouping(getContainerFromGrouping()) - .setChoice(getComplexCase()) - .build(); - } - - private Choice getComplexCase() { - return new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.choice.C3Builder() - .setC3(getC3()) - .build(); - } - - private C3 getC3() { - return new C3Builder() - .setName("c3") - .build(); - } - - private void writeContainerFromGrouping(final DataModification dataModification) { - final Map.Entry> normalizedNode = - serializer.toNormalizedNode(Ids.CONTAINER_FROM_GROUPING_ID, getContainerFromGrouping()); - dataModification.write(normalizedNode.getKey(), normalizedNode.getValue()); - } - - private ContainerFromGrouping getContainerFromGrouping() { - return new ContainerFromGroupingBuilder() - .setLeafInContainerFromGrouping(111) - .build(); - } - - - private void writeSimpleContainer(final DataModification dataModification) { - final Map.Entry> normalizedNode = - serializer.toNormalizedNode(Ids.SIMPLE_CONTAINER_ID, getSimpleContainer()); - dataModification.write(normalizedNode.getKey(), normalizedNode.getValue()); - } - - private void deleteSimpleContainer(final DataModification dataModification) { - final YangInstanceIdentifier yangId = - serializer.toYangInstanceIdentifier(Ids.SIMPLE_CONTAINER_ID); - dataModification.delete(yangId); - } - - private SimpleContainer getSimpleContainer() { - return new SimpleContainerBuilder() - .setSimpleContainerName("n") - .addAugmentation(SimpleAugment.class, getSimpleAugment()) - .addAugmentation(ComplexAugment.class, getComplexAugment()) - .build(); - } - - private ComplexAugment getComplexAugment() { - return new ComplexAugmentBuilder() - .setComplexAugmentContainer(getComplexAugmentContainer()) - .build(); - } - - private ComplexAugmentContainer getComplexAugmentContainer() { - return new ComplexAugmentContainerBuilder().setSomeLeaf("s").build(); - } - - private SimpleAugment getSimpleAugment() { - return new SimpleAugmentBuilder().setSimpleAugmentLeaf("a").build(); - } - - @Test - public void testWriteAndDeleteInTx() throws Exception { - final ModifiableDataTreeDelegator modifiableDataTreeDelegator = - new ModifiableDataTreeDelegator(serializer, dataTree, writerRegistry, contextBroker); - - final DataModification dataModification = modifiableDataTreeDelegator.newModification(); - // Now write everything we can - writeSimpleContainer(dataModification); - deleteSimpleContainer(dataModification); - dataModification.commit(); - - verify(simpleContainerWriter).getManagedDataObjectType(); - // No modification - verifyNoMoreInteractions(simpleContainerWriter); - } - - @Test - public void testSubtreeWriter() throws Exception { - writerRegistry = new FlatWriterRegistryBuilder() - // Handles also container from grouping - .subtreeAdd(Sets.newHashSet(Ids.CONTAINER_FROM_GROUPING_ID), containerWithChoiceWriter) - .build(); - - final ModifiableDataTreeDelegator modifiableDataTreeDelegator = - new ModifiableDataTreeDelegator(serializer, dataTree, writerRegistry, contextBroker); - - final ContainerWithChoice containerWithChoice = - new ContainerWithChoiceBuilder().setContainerFromGrouping(getContainerFromGrouping()).build(); - - // Test write subtree node - DataModification dataModification = modifiableDataTreeDelegator.newModification(); - writeContainerFromGrouping(dataModification); - dataModification.commit(); - - verify(containerWithChoiceWriter, atLeastOnce()).getManagedDataObjectType(); - verify(containerWithChoiceWriter) - .update(eq(Ids.CONTAINER_WITH_CHOICE_ID), eq(null), eq(containerWithChoice), any(WriteContext.class)); - verifyNoMoreInteractions(containerWithChoiceWriter); - - // Test delete sub-node - dataModification = modifiableDataTreeDelegator.newModification(); - final ContainerWithChoice containerWithChoiceEmpty = new ContainerWithChoiceBuilder().build(); - deleteContainerFromGrouping(dataModification); - dataModification.commit(); - - verify(containerWithChoiceWriter, atLeastOnce()).getManagedDataObjectType(); - verify(containerWithChoiceWriter) - .update(eq(Ids.CONTAINER_WITH_CHOICE_ID), eq(containerWithChoice), eq(containerWithChoiceEmpty), any(WriteContext.class)); - verifyNoMoreInteractions(containerWithChoiceWriter); - - // Test write with subtree node that's not handled by subtree writer - dataModification = modifiableDataTreeDelegator.newModification(); - writeContainerWithChoice(dataModification); - try { - dataModification.commit(); - fail("Missing writer for C3 should occur"); - } catch (IllegalArgumentException e) { - return; - } - } - - private void deleteContainerFromGrouping(final DataModification dataModification) { - dataModification.delete(serializer.toYangInstanceIdentifier(Ids.CONTAINER_FROM_GROUPING_ID)); - } -} \ No newline at end of file diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/Ids.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/Ids.java deleted file mode 100644 index 200f50c32..000000000 --- a/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/Ids.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugment; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithChoice; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleAugment; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainer; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.choice.c3.C3; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInList; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedList; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainer; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.some.attributes.ContainerFromGrouping; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Instance identifiers referencing all complex nodes within IT test-model. - */ -interface Ids { - - // Simple container - // ORDER = 3 - InstanceIdentifier SIMPLE_CONTAINER_ID = InstanceIdentifier.create(SimpleContainer.class); - // 2 - InstanceIdentifier SIMPLE_AUGMENT_ID = SIMPLE_CONTAINER_ID.augmentation(SimpleAugment.class); - // UNORDERED - InstanceIdentifier COMPLEX_AUGMENT_ID = SIMPLE_CONTAINER_ID.augmentation(ComplexAugment.class); - // 1 - InstanceIdentifier COMPLEX_AUGMENT_CONTAINER_ID = COMPLEX_AUGMENT_ID.child(ComplexAugmentContainer.class); - // Container with list - // 9 - InstanceIdentifier CONTAINER_WITH_LIST_ID = InstanceIdentifier.create(ContainerWithList.class); - // 7 - InstanceIdentifier LIST_IN_CONTAINER_ID = CONTAINER_WITH_LIST_ID.child(ListInContainer.class); - // 8 - InstanceIdentifier CONTAINER_IN_LIST_ID = LIST_IN_CONTAINER_ID.child(ContainerInList.class); - // 6 - InstanceIdentifier NESTED_LIST_ID = CONTAINER_IN_LIST_ID.child(NestedList.class); - // Container with choice - // 4 - InstanceIdentifier CONTAINER_WITH_CHOICE_ID = InstanceIdentifier.create(ContainerWithChoice.class); - // 2 - InstanceIdentifier C3_ID = CONTAINER_WITH_CHOICE_ID.child(C3.class); - // 5 - InstanceIdentifier CONTAINER_FROM_GROUPING_ID = CONTAINER_WITH_CHOICE_ID.child(ContainerFromGrouping.class); -} diff --git a/infra/notification/api/src/main/java/io/fd/honeycomb/notification/ManagedNotificationProducer.java b/infra/notification/api/src/main/java/io/fd/honeycomb/notification/ManagedNotificationProducer.java new file mode 100644 index 000000000..433512164 --- /dev/null +++ b/infra/notification/api/src/main/java/io/fd/honeycomb/notification/ManagedNotificationProducer.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fd.honeycomb.notification; + +import com.google.common.annotations.Beta; +import javax.annotation.Nonnull; + +/** + * Special notification producer that is capable of starting and stopping the notification stream + */ +@Beta +public interface ManagedNotificationProducer extends NotificationProducer { + + /** + * Start notification stream managed by this producer. + * + * @param collector Notification collector expected to collect produced notifications + */ + void start(@Nonnull NotificationCollector collector); + + /** + * Stop notification stream managed by this producer. + */ + void stop(); +} diff --git a/infra/notification/api/src/main/java/io/fd/honeycomb/notification/NotificationCollector.java b/infra/notification/api/src/main/java/io/fd/honeycomb/notification/NotificationCollector.java new file mode 100644 index 000000000..e40b874d5 --- /dev/null +++ b/infra/notification/api/src/main/java/io/fd/honeycomb/notification/NotificationCollector.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fd.honeycomb.notification; + +import com.google.common.annotations.Beta; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.Notification; + +/** + * Notification collector. Collects all the notifications, which are further + * propagated to all wired northbound interfaces. + */ +@Beta +public interface NotificationCollector extends AutoCloseable, NotificationProducer { + + /** + * Publish a single notification. + * + * @param notification notification to be published + */ + void onNotification(@Nonnull Notification notification); +} diff --git a/infra/notification/api/src/main/java/io/fd/honeycomb/notification/NotificationProducer.java b/infra/notification/api/src/main/java/io/fd/honeycomb/notification/NotificationProducer.java new file mode 100644 index 000000000..665318d0e --- /dev/null +++ b/infra/notification/api/src/main/java/io/fd/honeycomb/notification/NotificationProducer.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fd.honeycomb.notification; + +import com.google.common.annotations.Beta; +import java.util.Collection; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.Notification; + +/** + * Produces notification of exposed notification types + */ +@Beta +public interface NotificationProducer extends AutoCloseable { + + /** + * Return collection of notification types that will be emitted by this producer. + * Other types of notifications should not be emitted, since they can be rejected. + * + * @return collection of all notification types emitted by this producer + */ + @Nonnull + Collection> getNotificationTypes(); +} diff --git a/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/ManagedNotificationProducer.java b/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/ManagedNotificationProducer.java deleted file mode 100644 index 0f5e28cde..000000000 --- a/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/ManagedNotificationProducer.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.fd.honeycomb.v3po.notification; - -import com.google.common.annotations.Beta; -import javax.annotation.Nonnull; - -/** - * Special notification producer that is capable of starting and stopping the notification stream - */ -@Beta -public interface ManagedNotificationProducer extends NotificationProducer { - - /** - * Start notification stream managed by this producer. - * - * @param collector Notification collector expected to collect produced notifications - */ - void start(@Nonnull NotificationCollector collector); - - /** - * Stop notification stream managed by this producer. - */ - void stop(); -} diff --git a/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationCollector.java b/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationCollector.java deleted file mode 100644 index 406ab03d2..000000000 --- a/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationCollector.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.fd.honeycomb.v3po.notification; - -import com.google.common.annotations.Beta; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.yang.binding.Notification; - -/** - * Notification collector. Collects all the notifications, which are further - * propagated to all wired northbound interfaces. - */ -@Beta -public interface NotificationCollector extends AutoCloseable, NotificationProducer { - - /** - * Publish a single notification. - * - * @param notification notification to be published - */ - void onNotification(@Nonnull Notification notification); -} diff --git a/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationProducer.java b/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationProducer.java deleted file mode 100644 index dab773c45..000000000 --- a/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationProducer.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.fd.honeycomb.v3po.notification; - -import com.google.common.annotations.Beta; -import java.util.Collection; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.yang.binding.Notification; - -/** - * Produces notification of exposed notification types - */ -@Beta -public interface NotificationProducer extends AutoCloseable { - - /** - * Return collection of notification types that will be emitted by this producer. - * Other types of notifications should not be emitted, since they can be rejected. - * - * @return collection of all notification types emitted by this producer - */ - @Nonnull - Collection> getNotificationTypes(); -} diff --git a/infra/notification/api/src/main/yang/notification-api.yang b/infra/notification/api/src/main/yang/notification-api.yang index 4e6eb98ae..f0331bc07 100644 --- a/infra/notification/api/src/main/yang/notification-api.yang +++ b/infra/notification/api/src/main/yang/notification-api.yang @@ -15,7 +15,7 @@ module notification-api { identity honeycomb-notification-collector { base "config:service-type"; - config:java-class io.fd.honeycomb.v3po.notification.NotificationCollector; + config:java-class io.fd.honeycomb.notification.NotificationCollector; } identity dom-notification-service { @@ -25,7 +25,7 @@ module notification-api { identity honeycomb-notification-producer { base "config:service-type"; - config:java-class io.fd.honeycomb.v3po.notification.ManagedNotificationProducer; + config:java-class io.fd.honeycomb.notification.ManagedNotificationProducer; } } diff --git a/infra/notification/impl/src/main/config/notification-to-netconf-config.xml b/infra/notification/impl/src/main/config/notification-to-netconf-config.xml index d2aac0932..d64de6413 100644 --- a/infra/notification/impl/src/main/config/notification-to-netconf-config.xml +++ b/infra/notification/impl/src/main/config/notification-to-netconf-config.xml @@ -29,13 +29,13 @@ prefix:netconf-notification-registry - vpp-netconf-notification-manager + honeycomb-netconf-notification-manager prefix:netconf-notification-collector - vpp-netconf-notification-manager + honeycomb-netconf-notification-manager dom:schema-service diff --git a/infra/notification/impl/src/main/java/io/fd/honeycomb/notification/impl/HoneycombNotificationCollector.java b/infra/notification/impl/src/main/java/io/fd/honeycomb/notification/impl/HoneycombNotificationCollector.java new file mode 100644 index 000000000..c37e6925c --- /dev/null +++ b/infra/notification/impl/src/main/java/io/fd/honeycomb/notification/impl/HoneycombNotificationCollector.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fd.honeycomb.notification.impl; + +import io.fd.honeycomb.notification.NotificationCollector; +import java.util.Collection; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService; +import org.opendaylight.yangtools.yang.binding.Notification; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Notification collector based on MD-SAL's {@link NotificationPublishService}. + */ +public final class HoneycombNotificationCollector implements NotificationCollector, AutoCloseable { + + private static final Logger LOG = LoggerFactory.getLogger(HoneycombNotificationCollector.class); + + private final NotificationPublishService bindingDOMNotificationPublishServiceAdapter; + private final NotificationProducerRegistry notificationProducerRegistry; + + public HoneycombNotificationCollector( + @Nonnull final NotificationPublishService bindingDOMNotificationPublishServiceAdapter, + @Nonnull final NotificationProducerRegistry notificationProducerRegistry) { + this.bindingDOMNotificationPublishServiceAdapter = bindingDOMNotificationPublishServiceAdapter; + this.notificationProducerRegistry = notificationProducerRegistry; + } + + @Override + public void close() throws Exception { + LOG.trace("Closing"); + } + + @Override + public void onNotification(@Nonnull final Notification notification) { + LOG.debug("Notification: {} pushed into collector", notification.getClass().getSimpleName()); + LOG.trace("Notification: {} pushed into collector", notification); + try { + bindingDOMNotificationPublishServiceAdapter.putNotification(notification); + } catch (InterruptedException e) { + LOG.warn("Interrupted", e); + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + } + + @Override + @Nonnull + public Collection> getNotificationTypes() { + return notificationProducerRegistry.getNotificationTypes(); + } +} diff --git a/infra/notification/impl/src/main/java/io/fd/honeycomb/notification/impl/NotificationProducerRegistry.java b/infra/notification/impl/src/main/java/io/fd/honeycomb/notification/impl/NotificationProducerRegistry.java new file mode 100644 index 000000000..7b46c60b7 --- /dev/null +++ b/infra/notification/impl/src/main/java/io/fd/honeycomb/notification/impl/NotificationProducerRegistry.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fd.honeycomb.notification.impl; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import io.fd.honeycomb.notification.ManagedNotificationProducer; +import io.fd.honeycomb.notification.NotificationProducer; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.concurrent.ThreadSafe; +import org.opendaylight.yangtools.yang.binding.Notification; +import org.opendaylight.yangtools.yang.common.QName; + +/** + * Holds the collection of registered notification producers. + * Provides additional information about the types of notifications produced per producer and overall. + */ +@ThreadSafe +public final class NotificationProducerRegistry { + + private final Set> notificationTypes; + private final Map notificationQNameToProducer; + private final Multimap notificationProducerQNames; + + public NotificationProducerRegistry(final List notificationProducersDependency) { + this.notificationTypes = toTypes(notificationProducersDependency); + this.notificationQNameToProducer = toQNameMap(notificationProducersDependency); + this.notificationProducerQNames = toQNameMapReversed(notificationProducersDependency); + } + + private static Multimap toQNameMapReversed(final List notificationProducers) { + final Multimap multimap = HashMultimap.create(); + + for (ManagedNotificationProducer producer : notificationProducers) { + for (Class aClass : producer.getNotificationTypes()) { + multimap.put(producer, getQName(aClass)); + } + } + return multimap; + } + + private static Set> toTypes(final List notificationProducersDependency) { + // Get all notification types registered from HC notification producers + return notificationProducersDependency + .stream() + .flatMap(producer -> producer.getNotificationTypes().stream()) + .collect(Collectors.toSet()); + } + + + private static Map toQNameMap(final List producerDependencies) { + // Only a single notification producer per notification type is allowed + final Map qNamesToProducers = Maps.newHashMap(); + for (ManagedNotificationProducer notificationProducer : producerDependencies) { + for (QName qName : typesToQNames(notificationProducer.getNotificationTypes())) { + final NotificationProducer previousProducer = qNamesToProducers.put(qName, notificationProducer); + checkArgument(previousProducer == null, "2 producers of the same notification type: %s. " + + "Producer 1: {} Producer 2: {}" , qName, previousProducer, notificationProducer); + } + } + return qNamesToProducers; + } + + + private static Set typesToQNames(final Collection> notificationTypes) { + return notificationTypes + .stream() + .map(NotificationProducerRegistry::getQName) + .collect(Collectors.toSet()); + } + + + public static QName getQName(final Class aClass) { + try { + return (QName) aClass.getField("QNAME").get(null); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new IllegalArgumentException("Unable to retrieve QName for notification of type: " + aClass, e); + } + } + + Set> getNotificationTypes() { + return notificationTypes; + } + + Map getNotificationQNameToProducer() { + return notificationQNameToProducer; + } + + Multimap getNotificationProducerQNames() { + return notificationProducerQNames; + } +} diff --git a/infra/notification/impl/src/main/java/io/fd/honeycomb/notification/impl/NotificationProducerTracker.java b/infra/notification/impl/src/main/java/io/fd/honeycomb/notification/impl/NotificationProducerTracker.java new file mode 100644 index 000000000..808512c21 --- /dev/null +++ b/infra/notification/impl/src/main/java/io/fd/honeycomb/notification/impl/NotificationProducerTracker.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fd.honeycomb.notification.impl; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.collect.Sets; +import io.fd.honeycomb.notification.ManagedNotificationProducer; +import io.fd.honeycomb.notification.NotificationCollector; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import javax.annotation.concurrent.ThreadSafe; +import org.opendaylight.controller.md.sal.dom.spi.DOMNotificationSubscriptionListener; +import org.opendaylight.controller.md.sal.dom.spi.DOMNotificationSubscriptionListenerRegistry; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Starts & stops notification producer dependencies on demand. + * Uses {@link DOMNotificationSubscriptionListenerRegistry} to receive subscription change notifications. + */ +@ThreadSafe +public final class NotificationProducerTracker + implements DOMNotificationSubscriptionListener, AutoCloseable { + + private static final Logger LOG = LoggerFactory.getLogger(NotificationProducerTracker.class); + + private final ListenerRegistration subscriptionListener; + private final NotificationProducerRegistry registry; + private final NotificationCollector collector; + + private final Set alreadyStartedProducers = new HashSet<>(); + + public NotificationProducerTracker(@Nonnull final NotificationProducerRegistry registry, + @Nonnull final NotificationCollector collector, + @Nonnull final DOMNotificationSubscriptionListenerRegistry notificationRouter) { + this.registry = registry; + this.collector = collector; + this.subscriptionListener = notificationRouter.registerSubscriptionListener(this); + } + + @Override + public synchronized void onSubscriptionChanged(final Set set) { + LOG.debug("Subscriptions changed. Current subscriptions: {}", set); + final Set currentSubscriptions = set.stream().map(SchemaPath::getLastComponent).collect(Collectors.toSet()); + final Set startedQNames = getStartedQNames(alreadyStartedProducers); + final Sets.SetView newSubscriptions = Sets.difference(currentSubscriptions, startedQNames); + LOG.debug("Subscriptions changed. New subscriptions: {}", newSubscriptions); + final Sets.SetView deletedSubscriptions = Sets.difference(startedQNames, currentSubscriptions); + LOG.debug("Subscriptions changed. Deleted subscriptions: {}", deletedSubscriptions); + + newSubscriptions.stream().forEach(newSub -> { + if(!registry.getNotificationQNameToProducer().containsKey(newSub)) { + return; + } + final ManagedNotificationProducer producer = registry.getNotificationQNameToProducer().get(newSub); + if(alreadyStartedProducers.contains(producer)) { + return; + } + LOG.debug("Starting notification producer: {}", producer); + producer.start(collector); + alreadyStartedProducers.add(producer); + }); + + deletedSubscriptions.stream().forEach(newSub -> { + checkState(registry.getNotificationQNameToProducer().containsKey(newSub)); + final ManagedNotificationProducer producer = registry.getNotificationQNameToProducer().get(newSub); + checkState(alreadyStartedProducers.contains(producer)); + LOG.debug("Stopping notification producer: {}", producer); + producer.stop(); + alreadyStartedProducers.remove(producer); + }); + + } + + private Set getStartedQNames(final Set alreadyStartedProducers) { + return alreadyStartedProducers.stream() + .flatMap(p -> registry.getNotificationProducerQNames().get(p).stream()) + .collect(Collectors.toSet()); + } + + @Override + public synchronized void close() throws Exception { + LOG.trace("Closing"); + subscriptionListener.close(); + // Stop all producers + LOG.debug("Stopping all producers: {}", alreadyStartedProducers); + alreadyStartedProducers.forEach(ManagedNotificationProducer::stop); + alreadyStartedProducers.clear(); + } +} diff --git a/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollector.java b/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollector.java deleted file mode 100644 index e7d54e318..000000000 --- a/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollector.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.fd.honeycomb.v3po.notification.impl; - -import io.fd.honeycomb.v3po.notification.NotificationCollector; -import java.util.Collection; -import javax.annotation.Nonnull; -import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService; -import org.opendaylight.yangtools.yang.binding.Notification; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Notification collector based on MD-SAL's {@link NotificationPublishService}. - */ -public final class HoneycombNotificationCollector implements NotificationCollector, AutoCloseable { - - private static final Logger LOG = LoggerFactory.getLogger(HoneycombNotificationCollector.class); - - private final NotificationPublishService bindingDOMNotificationPublishServiceAdapter; - private final NotificationProducerRegistry notificationProducerRegistry; - - public HoneycombNotificationCollector( - @Nonnull final NotificationPublishService bindingDOMNotificationPublishServiceAdapter, - @Nonnull final NotificationProducerRegistry notificationProducerRegistry) { - this.bindingDOMNotificationPublishServiceAdapter = bindingDOMNotificationPublishServiceAdapter; - this.notificationProducerRegistry = notificationProducerRegistry; - } - - @Override - public void close() throws Exception { - LOG.trace("Closing"); - } - - @Override - public void onNotification(@Nonnull final Notification notification) { - LOG.debug("Notification: {} pushed into collector", notification.getClass().getSimpleName()); - LOG.trace("Notification: {} pushed into collector", notification); - try { - bindingDOMNotificationPublishServiceAdapter.putNotification(notification); - } catch (InterruptedException e) { - LOG.warn("Interrupted", e); - Thread.currentThread().interrupt(); - throw new RuntimeException(e); - } - } - - @Override - @Nonnull - public Collection> getNotificationTypes() { - return notificationProducerRegistry.getNotificationTypes(); - } -} diff --git a/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistry.java b/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistry.java deleted file mode 100644 index 8fba700bd..000000000 --- a/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistry.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.fd.honeycomb.v3po.notification.impl; - -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; -import io.fd.honeycomb.v3po.notification.ManagedNotificationProducer; -import io.fd.honeycomb.v3po.notification.NotificationProducer; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import javax.annotation.concurrent.ThreadSafe; -import org.opendaylight.yangtools.yang.binding.Notification; -import org.opendaylight.yangtools.yang.common.QName; - -/** - * Holds the collection of registered notification producers. - * Provides additional information about the types of notifications produced per producer and overall. - */ -@ThreadSafe -public final class NotificationProducerRegistry { - - private final Set> notificationTypes; - private final Map notificationQNameToProducer; - private final Multimap notificationProducerQNames; - - public NotificationProducerRegistry(final List notificationProducersDependency) { - this.notificationTypes = toTypes(notificationProducersDependency); - this.notificationQNameToProducer = toQNameMap(notificationProducersDependency); - this.notificationProducerQNames = toQNameMapReversed(notificationProducersDependency); - } - - private static Multimap toQNameMapReversed(final List notificationProducers) { - final Multimap multimap = HashMultimap.create(); - - for (ManagedNotificationProducer producer : notificationProducers) { - for (Class aClass : producer.getNotificationTypes()) { - multimap.put(producer, getQName(aClass)); - } - } - return multimap; - } - - private static Set> toTypes(final List notificationProducersDependency) { - // Get all notification types registered from HC notification producers - return notificationProducersDependency - .stream() - .flatMap(producer -> producer.getNotificationTypes().stream()) - .collect(Collectors.toSet()); - } - - - private static Map toQNameMap(final List producerDependencies) { - // Only a single notification producer per notification type is allowed - final Map qNamesToProducers = Maps.newHashMap(); - for (ManagedNotificationProducer notificationProducer : producerDependencies) { - for (QName qName : typesToQNames(notificationProducer.getNotificationTypes())) { - final NotificationProducer previousProducer = qNamesToProducers.put(qName, notificationProducer); - checkArgument(previousProducer == null, "2 producers of the same notification type: %s. " + - "Producer 1: {} Producer 2: {}" , qName, previousProducer, notificationProducer); - } - } - return qNamesToProducers; - } - - - private static Set typesToQNames(final Collection> notificationTypes) { - return notificationTypes - .stream() - .map(NotificationProducerRegistry::getQName) - .collect(Collectors.toSet()); - } - - - public static QName getQName(final Class aClass) { - try { - return (QName) aClass.getField("QNAME").get(null); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new IllegalArgumentException("Unable to retrieve QName for notification of type: " + aClass, e); - } - } - - Set> getNotificationTypes() { - return notificationTypes; - } - - Map getNotificationQNameToProducer() { - return notificationQNameToProducer; - } - - Multimap getNotificationProducerQNames() { - return notificationProducerQNames; - } -} diff --git a/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTracker.java b/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTracker.java deleted file mode 100644 index cefb50ac9..000000000 --- a/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTracker.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.fd.honeycomb.v3po.notification.impl; - -import static com.google.common.base.Preconditions.checkState; - -import com.google.common.collect.Sets; -import io.fd.honeycomb.v3po.notification.ManagedNotificationProducer; -import io.fd.honeycomb.v3po.notification.NotificationCollector; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.concurrent.ThreadSafe; -import org.opendaylight.controller.md.sal.dom.spi.DOMNotificationSubscriptionListener; -import org.opendaylight.controller.md.sal.dom.spi.DOMNotificationSubscriptionListenerRegistry; -import org.opendaylight.yangtools.concepts.ListenerRegistration; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.model.api.SchemaPath; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Starts & stops notification producer dependencies on demand. - * Uses {@link DOMNotificationSubscriptionListenerRegistry} to receive subscription change notifications. - */ -@ThreadSafe -public final class NotificationProducerTracker - implements DOMNotificationSubscriptionListener, AutoCloseable { - - private static final Logger LOG = LoggerFactory.getLogger(NotificationProducerTracker.class); - - private final ListenerRegistration subscriptionListener; - private final NotificationProducerRegistry registry; - private final NotificationCollector collector; - - private final Set alreadyStartedProducers = new HashSet<>(); - - public NotificationProducerTracker(@Nonnull final NotificationProducerRegistry registry, - @Nonnull final NotificationCollector collector, - @Nonnull final DOMNotificationSubscriptionListenerRegistry notificationRouter) { - this.registry = registry; - this.collector = collector; - this.subscriptionListener = notificationRouter.registerSubscriptionListener(this); - } - - @Override - public synchronized void onSubscriptionChanged(final Set set) { - LOG.debug("Subscriptions changed. Current subscriptions: {}", set); - final Set currentSubscriptions = set.stream().map(SchemaPath::getLastComponent).collect(Collectors.toSet()); - final Set startedQNames = getStartedQNames(alreadyStartedProducers); - final Sets.SetView newSubscriptions = Sets.difference(currentSubscriptions, startedQNames); - LOG.debug("Subscriptions changed. New subscriptions: {}", newSubscriptions); - final Sets.SetView deletedSubscriptions = Sets.difference(startedQNames, currentSubscriptions); - LOG.debug("Subscriptions changed. Deleted subscriptions: {}", deletedSubscriptions); - - newSubscriptions.stream().forEach(newSub -> { - if(!registry.getNotificationQNameToProducer().containsKey(newSub)) { - return; - } - final ManagedNotificationProducer producer = registry.getNotificationQNameToProducer().get(newSub); - if(alreadyStartedProducers.contains(producer)) { - return; - } - LOG.debug("Starting notification producer: {}", producer); - producer.start(collector); - alreadyStartedProducers.add(producer); - }); - - deletedSubscriptions.stream().forEach(newSub -> { - checkState(registry.getNotificationQNameToProducer().containsKey(newSub)); - final ManagedNotificationProducer producer = registry.getNotificationQNameToProducer().get(newSub); - checkState(alreadyStartedProducers.contains(producer)); - LOG.debug("Stopping notification producer: {}", producer); - producer.stop(); - alreadyStartedProducers.remove(producer); - }); - - } - - private Set getStartedQNames(final Set alreadyStartedProducers) { - return alreadyStartedProducers.stream() - .flatMap(p -> registry.getNotificationProducerQNames().get(p).stream()) - .collect(Collectors.toSet()); - } - - @Override - public synchronized void close() throws Exception { - LOG.trace("Closing"); - subscriptionListener.close(); - // Stop all producers - LOG.debug("Stopping all producers: {}", alreadyStartedProducers); - alreadyStartedProducers.forEach(ManagedNotificationProducer::stop); - alreadyStartedProducers.clear(); - } -} diff --git a/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModule.java b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModule.java index 4a9440cbc..056b38798 100644 --- a/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModule.java +++ b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModule.java @@ -1,10 +1,10 @@ package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601; -import io.fd.honeycomb.v3po.notification.NotificationCollector; -import io.fd.honeycomb.v3po.notification.NotificationProducer; -import io.fd.honeycomb.v3po.notification.impl.HoneycombNotificationCollector; -import io.fd.honeycomb.v3po.notification.impl.NotificationProducerRegistry; -import io.fd.honeycomb.v3po.notification.impl.NotificationProducerTracker; +import io.fd.honeycomb.notification.NotificationCollector; +import io.fd.honeycomb.notification.NotificationProducer; +import io.fd.honeycomb.notification.impl.HoneycombNotificationCollector; +import io.fd.honeycomb.notification.impl.NotificationProducerRegistry; +import io.fd.honeycomb.notification.impl.NotificationProducerTracker; import java.util.Collection; import org.opendaylight.controller.md.sal.binding.impl.BindingDOMNotificationPublishServiceAdapter; import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; diff --git a/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModule.java b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModule.java index 4d85d64c5..734d340e8 100644 --- a/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModule.java +++ b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModule.java @@ -1,8 +1,8 @@ package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601; import com.google.common.annotations.VisibleForTesting; -import io.fd.honeycomb.v3po.notification.NotificationCollector; -import io.fd.honeycomb.v3po.notification.impl.NotificationProducerRegistry; +import io.fd.honeycomb.notification.NotificationCollector; +import io.fd.honeycomb.notification.impl.NotificationProducerRegistry; import java.io.IOException; import java.util.Set; import java.util.stream.Collectors; diff --git a/infra/notification/impl/src/test/java/io/fd/honeycomb/notification/impl/HoneycombNotificationCollectorTest.java b/infra/notification/impl/src/test/java/io/fd/honeycomb/notification/impl/HoneycombNotificationCollectorTest.java new file mode 100644 index 000000000..ef9f548b7 --- /dev/null +++ b/infra/notification/impl/src/test/java/io/fd/honeycomb/notification/impl/HoneycombNotificationCollectorTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fd.honeycomb.notification.impl; + +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.verify; + +import com.google.common.collect.Lists; +import io.fd.honeycomb.notification.ManagedNotificationProducer; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfSessionStart; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfSessionStartBuilder; + +public class HoneycombNotificationCollectorTest { + + private NotificationProducerRegistry notificationRegistry; + @Mock + private NotificationPublishService notificationService; + @Mock + private ManagedNotificationProducer producer; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + notificationRegistry = new NotificationProducerRegistry(Lists.newArrayList(producer)); + } + + @Test + public void testNotificationTypes() throws Exception { + final HoneycombNotificationCollector honeycombNotificationCollector = + new HoneycombNotificationCollector(notificationService, notificationRegistry); + + honeycombNotificationCollector.getNotificationTypes(); + verify(producer, atLeast(1)).getNotificationTypes(); + } + + @Test + public void testCollect() throws Exception { + final HoneycombNotificationCollector honeycombNotificationCollector = + new HoneycombNotificationCollector(notificationService, notificationRegistry); + + final NetconfSessionStart notif = new NetconfSessionStartBuilder().build(); + honeycombNotificationCollector.onNotification(notif); + verify(notificationService).putNotification(notif); + } +} \ No newline at end of file diff --git a/infra/notification/impl/src/test/java/io/fd/honeycomb/notification/impl/NotificationProducerRegistryTest.java b/infra/notification/impl/src/test/java/io/fd/honeycomb/notification/impl/NotificationProducerRegistryTest.java new file mode 100644 index 000000000..a09803f02 --- /dev/null +++ b/infra/notification/impl/src/test/java/io/fd/honeycomb/notification/impl/NotificationProducerRegistryTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fd.honeycomb.notification.impl; + +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.is; +import static org.mockito.Mockito.doReturn; + +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import io.fd.honeycomb.notification.ManagedNotificationProducer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfCapabilityChange; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfSessionEnd; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfSessionStart; +import org.opendaylight.yangtools.yang.binding.Notification; +import org.opendaylight.yangtools.yang.common.QName; + +public class NotificationProducerRegistryTest { + + @Mock + private ManagedNotificationProducer producer; + @Mock + private ManagedNotificationProducer producer2; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + doReturn(Collections.singleton(NetconfCapabilityChange.class)) + .when(producer).getNotificationTypes(); + final ArrayList producer2Notifications = Lists.newArrayList(); + producer2Notifications.add(NetconfSessionStart.class); + producer2Notifications.add(NetconfSessionEnd.class); + doReturn(producer2Notifications).when(producer2).getNotificationTypes(); + } + + @Test + public void testNotificationTypes() throws Exception { + final NotificationProducerRegistry notificationRegistry = + new NotificationProducerRegistry(Lists.newArrayList(producer, producer2)); + + final Set> notificationTypes = + notificationRegistry.getNotificationTypes(); + + Assert.assertThat(notificationTypes, hasItem(NetconfSessionEnd.class)); + Assert.assertThat(notificationTypes, hasItem(NetconfSessionStart.class)); + Assert.assertThat(notificationTypes, hasItem(NetconfCapabilityChange.class)); + } + + @Test + public void testNotificationTypesMapped() throws Exception { + final NotificationProducerRegistry notificationRegistry = + new NotificationProducerRegistry(Lists.newArrayList(producer, producer2)); + + final Multimap notificationTypes = + notificationRegistry.getNotificationProducerQNames(); + + Assert.assertThat(notificationTypes.keySet(), hasItem(producer)); + Assert.assertThat(notificationTypes.get(producer), hasItem(NetconfCapabilityChange.QNAME)); + Assert.assertThat(notificationTypes.keySet(), hasItem(producer2)); + Assert.assertThat(notificationTypes.get(producer2), hasItem(NetconfSessionStart.QNAME)); + Assert.assertThat(notificationTypes.get(producer2), hasItem(NetconfSessionEnd.QNAME)); + + final Map notificationQNameToProducer = + notificationRegistry.getNotificationQNameToProducer(); + + Assert.assertThat(notificationQNameToProducer.keySet(), hasItem(NetconfCapabilityChange.QNAME)); + Assert.assertThat(notificationQNameToProducer.get(NetconfCapabilityChange.QNAME), is(producer)); + + Assert.assertThat(notificationQNameToProducer.keySet(), hasItem(NetconfSessionStart.QNAME)); + Assert.assertThat(notificationQNameToProducer.keySet(), hasItem(NetconfSessionEnd.QNAME)); + Assert.assertThat(notificationQNameToProducer.get(NetconfSessionStart.QNAME), is(producer2)); + Assert.assertThat(notificationQNameToProducer.get(NetconfSessionEnd.QNAME), is(producer2)); + + + } +} \ No newline at end of file diff --git a/infra/notification/impl/src/test/java/io/fd/honeycomb/notification/impl/NotificationProducerTrackerTest.java b/infra/notification/impl/src/test/java/io/fd/honeycomb/notification/impl/NotificationProducerTrackerTest.java new file mode 100644 index 000000000..4de2e7773 --- /dev/null +++ b/infra/notification/impl/src/test/java/io/fd/honeycomb/notification/impl/NotificationProducerTrackerTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fd.honeycomb.notification.impl; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import io.fd.honeycomb.notification.ManagedNotificationProducer; +import io.fd.honeycomb.notification.NotificationCollector; +import java.util.Collections; +import java.util.Set; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.md.sal.dom.spi.DOMNotificationSubscriptionListenerRegistry; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfSessionStart; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; + +public class NotificationProducerTrackerTest { + + private NotificationProducerRegistry registry; + @Mock + private DOMNotificationSubscriptionListenerRegistry subscriptionRegistry; + @Mock + private NotificationCollector collector; + @Mock + private ManagedNotificationProducer producer; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + doReturn(Collections.singleton(NetconfSessionStart.class)).when(producer).getNotificationTypes(); + registry = new NotificationProducerRegistry(Lists.newArrayList(producer)); + } + + @Test + public void name() throws Exception { + final NotificationProducerTracker notificationProducerTracker = + new NotificationProducerTracker(registry, collector, subscriptionRegistry); + verify(subscriptionRegistry).registerSubscriptionListener(notificationProducerTracker); + + final Set subscriptions = Sets.newHashSet(); + subscriptions.add(SchemaPath.create(true, NetconfSessionStart.QNAME)); + notificationProducerTracker.onSubscriptionChanged(subscriptions); + + verify(producer).start(collector); + + notificationProducerTracker.onSubscriptionChanged(Sets.newHashSet()); + verify(producer).stop(); + } +} \ No newline at end of file diff --git a/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollectorTest.java b/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollectorTest.java deleted file mode 100644 index f55d3abdf..000000000 --- a/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollectorTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.fd.honeycomb.v3po.notification.impl; - -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.verify; - -import com.google.common.collect.Lists; -import io.fd.honeycomb.v3po.notification.ManagedNotificationProducer; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfSessionStart; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfSessionStartBuilder; - -public class HoneycombNotificationCollectorTest { - - private NotificationProducerRegistry notificationRegistry; - @Mock - private NotificationPublishService notificationService; - @Mock - private ManagedNotificationProducer producer; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - notificationRegistry = new NotificationProducerRegistry(Lists.newArrayList(producer)); - } - - @Test - public void testNotificationTypes() throws Exception { - final HoneycombNotificationCollector honeycombNotificationCollector = - new HoneycombNotificationCollector(notificationService, notificationRegistry); - - honeycombNotificationCollector.getNotificationTypes(); - verify(producer, atLeast(1)).getNotificationTypes(); - } - - @Test - public void testCollect() throws Exception { - final HoneycombNotificationCollector honeycombNotificationCollector = - new HoneycombNotificationCollector(notificationService, notificationRegistry); - - final NetconfSessionStart notif = new NetconfSessionStartBuilder().build(); - honeycombNotificationCollector.onNotification(notif); - verify(notificationService).putNotification(notif); - } -} \ No newline at end of file diff --git a/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistryTest.java b/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistryTest.java deleted file mode 100644 index 5fdf502b9..000000000 --- a/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistryTest.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.fd.honeycomb.v3po.notification.impl; - -import static org.hamcrest.CoreMatchers.hasItem; -import static org.hamcrest.CoreMatchers.is; -import static org.mockito.Mockito.doReturn; - -import com.google.common.collect.Lists; -import com.google.common.collect.Multimap; -import io.fd.honeycomb.v3po.notification.ManagedNotificationProducer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Map; -import java.util.Set; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfCapabilityChange; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfSessionEnd; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfSessionStart; -import org.opendaylight.yangtools.yang.binding.Notification; -import org.opendaylight.yangtools.yang.common.QName; - -public class NotificationProducerRegistryTest { - - @Mock - private ManagedNotificationProducer producer; - @Mock - private ManagedNotificationProducer producer2; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - doReturn(Collections.singleton(NetconfCapabilityChange.class)) - .when(producer).getNotificationTypes(); - final ArrayList producer2Notifications = Lists.newArrayList(); - producer2Notifications.add(NetconfSessionStart.class); - producer2Notifications.add(NetconfSessionEnd.class); - doReturn(producer2Notifications).when(producer2).getNotificationTypes(); - } - - @Test - public void testNotificationTypes() throws Exception { - final NotificationProducerRegistry notificationRegistry = - new NotificationProducerRegistry(Lists.newArrayList(producer, producer2)); - - final Set> notificationTypes = - notificationRegistry.getNotificationTypes(); - - Assert.assertThat(notificationTypes, hasItem(NetconfSessionEnd.class)); - Assert.assertThat(notificationTypes, hasItem(NetconfSessionStart.class)); - Assert.assertThat(notificationTypes, hasItem(NetconfCapabilityChange.class)); - } - - @Test - public void testNotificationTypesMapped() throws Exception { - final NotificationProducerRegistry notificationRegistry = - new NotificationProducerRegistry(Lists.newArrayList(producer, producer2)); - - final Multimap notificationTypes = - notificationRegistry.getNotificationProducerQNames(); - - Assert.assertThat(notificationTypes.keySet(), hasItem(producer)); - Assert.assertThat(notificationTypes.get(producer), hasItem(NetconfCapabilityChange.QNAME)); - Assert.assertThat(notificationTypes.keySet(), hasItem(producer2)); - Assert.assertThat(notificationTypes.get(producer2), hasItem(NetconfSessionStart.QNAME)); - Assert.assertThat(notificationTypes.get(producer2), hasItem(NetconfSessionEnd.QNAME)); - - final Map notificationQNameToProducer = - notificationRegistry.getNotificationQNameToProducer(); - - Assert.assertThat(notificationQNameToProducer.keySet(), hasItem(NetconfCapabilityChange.QNAME)); - Assert.assertThat(notificationQNameToProducer.get(NetconfCapabilityChange.QNAME), is(producer)); - - Assert.assertThat(notificationQNameToProducer.keySet(), hasItem(NetconfSessionStart.QNAME)); - Assert.assertThat(notificationQNameToProducer.keySet(), hasItem(NetconfSessionEnd.QNAME)); - Assert.assertThat(notificationQNameToProducer.get(NetconfSessionStart.QNAME), is(producer2)); - Assert.assertThat(notificationQNameToProducer.get(NetconfSessionEnd.QNAME), is(producer2)); - - - } -} \ No newline at end of file diff --git a/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTrackerTest.java b/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTrackerTest.java deleted file mode 100644 index b62bf0709..000000000 --- a/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTrackerTest.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.fd.honeycomb.v3po.notification.impl; - -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.verify; - -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; -import io.fd.honeycomb.v3po.notification.ManagedNotificationProducer; -import io.fd.honeycomb.v3po.notification.NotificationCollector; -import java.util.Collections; -import java.util.Set; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.opendaylight.controller.md.sal.dom.spi.DOMNotificationSubscriptionListenerRegistry; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfSessionStart; -import org.opendaylight.yangtools.yang.model.api.SchemaPath; - -public class NotificationProducerTrackerTest { - - private NotificationProducerRegistry registry; - @Mock - private DOMNotificationSubscriptionListenerRegistry subscriptionRegistry; - @Mock - private NotificationCollector collector; - @Mock - private ManagedNotificationProducer producer; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - doReturn(Collections.singleton(NetconfSessionStart.class)).when(producer).getNotificationTypes(); - registry = new NotificationProducerRegistry(Lists.newArrayList(producer)); - } - - @Test - public void name() throws Exception { - final NotificationProducerTracker notificationProducerTracker = - new NotificationProducerTracker(registry, collector, subscriptionRegistry); - verify(subscriptionRegistry).registerSubscriptionListener(notificationProducerTracker); - - final Set subscriptions = Sets.newHashSet(); - subscriptions.add(SchemaPath.create(true, NetconfSessionStart.QNAME)); - notificationProducerTracker.onSubscriptionChanged(subscriptions); - - verify(producer).start(collector); - - notificationProducerTracker.onSubscriptionChanged(Sets.newHashSet()); - verify(producer).stop(); - } -} \ No newline at end of file diff --git a/infra/notification/pom.xml b/infra/notification/pom.xml index 81b8aae40..3deb37797 100644 --- a/infra/notification/pom.xml +++ b/infra/notification/pom.xml @@ -22,6 +22,7 @@ + io.fd.honeycomb notification-aggregator 1.0.0-SNAPSHOT notification diff --git a/infra/postman_rest_collection.json b/infra/postman_rest_collection.json deleted file mode 100644 index 3adf19582..000000000 --- a/infra/postman_rest_collection.json +++ /dev/null @@ -1,1386 +0,0 @@ -{ - "id": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "name": "Honeycomb RESTCONF calls", - "description": "Common mgmt operations on VPP using Honeycomb REST interface", - "order": [ - "286f2d2d-88eb-ad68-7c6f-43caecc6073e", - "d27322b8-59d5-a2dc-a7a2-8f7197057164", - "5dc1d9f4-e7ae-bb21-e558-8a56ac825922", - "2f1d8e0d-4961-4b7f-50ad-0210e52e59aa", - "93022e97-24fa-5784-1e59-94674817215f", - "8b867c90-459f-68f6-4b2e-fa653098c28d", - "e611d23a-c205-192b-e8f9-dd99fa169274", - "3fccc4cd-f14b-0333-83d5-924afe3938e4" - ], - "folders": [ - { - "id": "7a914134-23ea-3154-1557-d29dc8d464e7", - "name": "IP", - "description": "", - "order": [ - "014dcd2b-1024-37a3-bc85-c18b0fdc0e8c", - "eb4852a6-ff3a-f9f4-4332-6677a93fcd2a", - "dc22d351-497a-4f01-52c1-6f01c76cf80b", - "69a05849-026d-f120-5c6a-f3e2b0884d88", - "b10169c2-ae0e-bb1c-3dea-ca06839c6c9c", - "8d49cf41-facc-c4ac-cee7-475d50e2a0be", - "70835949-7252-a3cc-491b-f8aa69286399", - "d5a90cfd-fd7e-63b6-e1f1-16fc0671b0e1", - "3f7ee49b-ae11-b032-915f-a14bf838246f", - "345ba26c-60cf-5fa1-37d0-f5af7a8c33bf" - ], - "owner": "45557" - }, - { - "id": "3d67940d-3c7a-8127-3965-6aee160e6b6f", - "name": "L2 FIB", - "description": "", - "order": [ - "76e9ee89-1594-ff38-ffff-ffc8de5d4054", - "dfbcc9e8-6f3d-b1c2-8bf6-989b1b1b3196", - "e2ff4db0-c013-cf89-d7b5-66de22bc7115", - "a937f1fc-10d5-bc19-ad5f-ac91fc007779", - "cf177689-9dd7-bcfe-d490-e9de6bde4dbb", - "0b63013d-3958-b427-2288-380019800468", - "99674f70-ec54-99f2-9300-545a031d5bab", - "f624bc96-502b-685c-1596-672ad5fcf6a9", - "0a1d6c0d-fe5e-1e1d-2471-96d7a4683e2f", - "d752fed9-127a-9289-71f3-90ba2b459d77", - "5c800144-cf89-7b77-1dd2-f488101ca441" - ], - "owner": "45557", - "collectionId": "e9ba4e80-fb4d-1eae-07e7-97b323164130" - }, - { - "id": "f91a6422-9724-e504-a94c-c5a953315b6f", - "name": "ACL", - "description": "", - "order": [ - "d7cb3d97-fc40-f471-33f3-07aad557777d", - "c067f721-6b1c-f205-799d-720b242f8592", - "5ab03c5f-03c5-8d42-bf65-024239049857", - "4f11c937-9562-3377-d03b-a9829b25f688", - "491ee2b7-88bf-3a71-282d-a16a62996026", - "fa63c194-ea7a-51ad-a6d8-d7485ed67f2e", - "b9931024-fa86-4e04-e2a4-4feeaa9e99e1", - "d39e4c39-90b6-072c-44b8-d909cac53a5b", - "2ce36b98-d7a9-fe37-80f6-f3a4abf25103", - "d0984799-d321-d1e0-a855-cc434519bef0", - "be223323-bf59-9c4f-f896-ff74c03b01a1", - "ee11f1c0-a1fe-51a6-a5e5-fcdcfc2b620a" - ], - "owner": "45557", - "collectionId": "e9ba4e80-fb4d-1eae-07e7-97b323164130" - }, - { - "id": "60596aab-a4f1-bb64-d701-816de9482201", - "name": "TAP", - "description": "", - "order": [ - "3f9588e4-885f-3792-bdf4-d0f10704ae4d", - "27106942-aff5-6ab5-07ae-5315135297a1", - "4db9b360-0d57-6ce6-7e3c-a6acfe17d512", - "5995dcf7-51c0-42ef-448b-a70bb68735d5" - ], - "owner": "45557" - }, - { - "id": "c97b9ad9-64e6-5de3-09b8-851c1189d767", - "name": "VHOST", - "description": "", - "order": [ - "7f312f15-a81e-b513-83b7-a5f7755eae67", - "ac161bec-e046-4bb7-d695-60bdfb0c6cea", - "55102dbd-1fc9-5f91-2082-40a2dc311f4f" - ], - "owner": "45557" - }, - { - "id": "37fd9569-06e3-1f08-fbcc-4b1462107a32", - "name": "VLAN", - "description": "", - "order": [ - "2ca639f4-f4a4-07c7-9419-79fd66061458", - "27eb70b8-a191-dd5c-f106-6693086ce872", - "f2e9a3cb-f3cc-f501-6015-8ebf7d8b2c3e", - "cbb77318-52e0-5647-cd1c-8c679ea7b830", - "8b339568-b60a-715f-d4fd-416bafe981a9", - "bd2897c6-5fc7-bec5-87be-4fd52bb8e471", - "44de3eff-ace5-90a9-dd16-af3f5f13daad", - "b08a0677-6b59-74ea-e5c6-7383194b19dd" - ], - "owner": "45557", - "collectionId": "e9ba4e80-fb4d-1eae-07e7-97b323164130" - }, - { - "id": "288569d0-e425-7877-4fd9-a82e8a340ade", - "name": "VXLAN-GPE-TUNNEL", - "description": "", - "order": [ - "0f223706-ca7f-fb8f-60ed-2b18e769e344", - "203ea32c-e43a-669c-4f28-232926df6928" - ], - "owner": "45557" - }, - { - "id": "3868e66d-0ee5-bd4d-6b35-075c4841b5c1", - "name": "VXLAN-TUNNEL", - "description": "", - "order": [ - "f014ab21-8e40-c344-a68b-0f1b86578bd8", - "4f15d0e9-8530-157e-0fe8-d24034bc31df" - ], - "owner": "45557" - } - ], - "timestamp": 1460119141419, - "owner": "45557", - "remoteLink": "", - "public": false, - "requests": [ - { - "id": "0f223706-ca7f-fb8f-60ed-2b18e769e344", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/vxlanGpeTun3", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1464768006369, - "name": "Add vxlan-gpe - cfg", - "description": "", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"vxlanGpeTun3\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"v3po:vxlan-gpe-tunnel\",\r\n \"enabled\": \"true\",\r\n \"link-up-down-trap-enable\": \"enabled\",\r\n \"routing\" : {\r\n \"vrf-id\" : \"0\"\r\n },\r\n \"vxlan-gpe\" : {\r\n \"local\" : \"192.168.50.76\",\r\n \"remote\" : \"192.168.50.71\",\r\n \"vni\" : \"9\",\r\n \"next-protocol\" : \"ethernet\",\r\n \"encap-vrf-id\" : \"0\",\r\n \"decap-vrf-id\" : \"0\"\r\n }\r\n }\r\n ]\r\n \r\n}", - "folder": "288569d0-e425-7877-4fd9-a82e8a340ade" - }, - { - "id": "203ea32c-e43a-669c-4f28-232926df6928", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/vxlanGpeTun3", - "preRequestScript": "", - "pathVariables": {}, - "method": "DELETE", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1464764638498, - "name": "Delete vxlan-gpe - cfg", - "description": "", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "", - "folder": "288569d0-e425-7877-4fd9-a82e8a340ade" - }, - { - "id": "27106942-aff5-6ab5-07ae-5315135297a1", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/tapp2", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1462796722186, - "name": "Add complex tap ifc - cfg", - "description": "", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"tapp2\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"v3po:tap\",\r\n \"tap\" :{\r\n \"tap-name\" : \"tapp2\",\r\n \"mac\" : \"00:ff:ff:ff:ff:ff\",\r\n \"device-instance\" : 55\r\n }\r\n }\r\n ]\r\n \r\n}", - "folder": "60596aab-a4f1-bb64-d701-816de9482201" - }, - { - "id": "27eb70b8-a191-dd5c-f106-6693086ce872", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F9%2F0", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1464804325716, - "name": "Enable GigabitEthernet0/9/0 interface - cfg", - "description": "Enables GigabitEthernet0/9/0 interface. Equivalent vppctl command:\n\nvppctl set in state GigabitEthernet0/9/0 up\n\nNeeded to enable sub interface.\n\nTo verify invoke:\n\nvppctl show int", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "37fd9569-06e3-1f08-fbcc-4b1462107a32", - "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"GigabitEthernet0/9/0\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"v3po:ethernet\": {\r\n \"mtu\": 9216\r\n },\r\n \"enabled\": true,\r\n \"vpp-vlan:sub-interfaces\": {\r\n \"sub-interface\": [\r\n {\r\n \"identifier\": 1,\r\n \"vlan-type\": \"802dot1q\",\r\n \"tags\": {\r\n \"tag\": [\r\n {\r\n \"index\": 1,\r\n \"dot1q-tag\": {\r\n \"tag-type\": \"dot1q-types:c-vlan\",\r\n \"vlan-id\": \"any\"\r\n }\r\n },\r\n {\r\n \"index\": 0,\r\n \"dot1q-tag\": {\r\n \"tag-type\": \"dot1q-types:s-vlan\",\r\n \"vlan-id\": \"100\"\r\n }\r\n }\r\n ]\r\n },\r\n \"match\": {\r\n \"vlan-tagged\": {\r\n \"match-exact-tags\": true\r\n }\r\n },\r\n \"enabled\": false\r\n }\r\n ]\r\n }\r\n }\r\n ]\r\n \r\n}" - }, - { - "id": "286f2d2d-88eb-ad68-7c6f-43caecc6073e", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/", - "preRequestScript": "", - "pathVariables": {}, - "method": "GET", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1460539962712, - "name": "List ifcs - cfg", - "description": "List ifcs - cfg", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"testInterface\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"enabled\": \"true\",\r\n \"link-up-down-trap-enable\": \"enabled\",\r\n \"ietf-ip:ipv4\": {\r\n \"enabled\": \"true\",\r\n \"mtu\": \"1500\",\r\n \"address\": [\r\n {\r\n \"ip\": \"1.2.3.0\",\r\n \"netmask\": \"255.255.255.0\"\r\n }\r\n ]\r\n }\r\n }\r\n ]\r\n \r\n}" - }, - { - "id": "014dcd2b-1024-37a3-bc85-c18b0fdc0e8c", - "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F8%2F0/ipv4/neighbor/172.16.0.2", - "preRequestScript": null, - "pathVariables": {}, - "method": "GET", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": null, - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1466498440466, - "name": "Ip Neighbour Get", - "description": "This call returns data stored in honeycomb,to verify actual data stored by vpp, call vppctl show ip fib|arp", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\n \n \"interface\":{\n \"name\":\"GigabitEthernet0/8/0\",\n \"type\": \"iana-if-type:ethernetCsmacd\",\n \"enabled\":\"true\",\n \"ipv4\":{\n \"neighbor\":{\n \"ip\":\"172.16.0.2\",\n \"link-layer-address\":\"00:01:11:00:02:02\"\n }\n }\n }\n}\n" - }, - { - "id": "dc22d351-497a-4f01-52c1-6f01c76cf80b", - "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F8%2F0/ipv4/neighbor/172.16.0.2", - "preRequestScript": null, - "pathVariables": {}, - "method": "DELETE", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": null, - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1466498502212, - "name": "Ip Neighbour Delete", - "description": "Verify this call by invoking vppctl show ip fib|arp", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\n \"neighbor\":{\n \"ip\":\"172.16.0.2\",\n \"link-layer-address\":\"00:01:11:00:02:02\"\n }\n}\n" - }, - { - "id": "eb4852a6-ff3a-f9f4-4332-6677a93fcd2a", - "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F8%2F0/ipv4/neighbor/172.16.0.2", - "preRequestScript": null, - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": null, - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1466498409820, - "name": "Ip Neighbour Add", - "description": "This call doesnt have respective dump call,because of performance issues(dump for this can contain millions of entries). Data inserted throught this call can be verified by calling vppctl show ip fib|arp", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\n \n \"neighbor\":{\n \"ip\":\"172.16.0.2\",\n \"link-layer-address\":\"00:01:11:00:02:02\"\n }\n \n}\n" - }, - { - "id": "2ca639f4-f4a4-07c7-9419-79fd66061458", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F9%2F0/vpp-vlan:sub-interfaces/sub-interface/1", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1464804304987, - "name": "Add sub interface - cfg", - "description": "Adds sub interface. Corresponsing vpp cli command:\n\nvppctl create sub GigabitEthernet0/9/0 1 dot1q 100 inner-dot1q any\n\nTo verify run:\n./build-root/install-vpp-native/vpp-api-test/bin/vpp_api_test json\nand invoke:\n\n#vat sw_interface_dump", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "37fd9569-06e3-1f08-fbcc-4b1462107a32", - "rawModeData": "{\r\n \"sub-interface\": [\r\n {\r\n \"identifier\": \"1\",\r\n \"vlan-type\": \"802dot1q\",\r\n \"tags\": {\r\n \"tag\": [\r\n {\r\n \"index\": \"0\",\r\n \"dot1q-tag\": {\r\n \"tag-type\": \"dot1q-types:s-vlan\",\r\n \"vlan-id\": \"100\"\r\n }\r\n },\r\n {\r\n \"index\": \"1\",\r\n \"dot1q-tag\": {\r\n \"tag-type\": \"dot1q-types:c-vlan\",\r\n \"vlan-id\": \"any\"\r\n }\r\n }\r\n ]\r\n },\r\n \"match\": {\r\n \"vlan-tagged\": {\r\n \"match-exact-tags\": \"true\"\r\n }\r\n },\r\n \"enabled\": \"false\"\r\n }\r\n ]\r\n}" - }, - { - "id": "2f1d8e0d-4961-4b7f-50ad-0210e52e59aa", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/v3po:vpp", - "preRequestScript": "", - "pathVariables": {}, - "method": "GET", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1460540006597, - "name": "Read vpp - cfg", - "description": "", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"testInterface\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"enabled\": \"true\",\r\n \"link-up-down-trap-enable\": \"enabled\",\r\n \"ietf-ip:ipv4\": {\r\n \"enabled\": \"true\",\r\n \"mtu\": \"1500\",\r\n \"address\": [\r\n {\r\n \"ip\": \"1.2.3.0\",\r\n \"netmask\": \"255.255.255.0\"\r\n }\r\n ]\r\n }\r\n }\r\n ]\r\n \r\n}" - }, - { - "id": "76e9ee89-1594-ff38-ffff-ffc8de5d4054", - "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n", - "url": "http://localhost:8181/restconf/config/v3po:vpp/bridge-domains/bridge-domain/testBD", - "pathVariables": {}, - "preRequestScript": "", - "method": "PUT", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "data": [], - "dataMode": "raw", - "name": "Create bridge domain - cfg", - "description": "Creates bridgfe domain. Corresponds to invoking:\n\nvat# bridge_domain_add_del bd_id [bd_id] learn 0 forward 0 uu-flood 0 flood 1 arp-term 0\n\nTo verify run:\n\nvppctl show bridge-domain [bd_id] detail", - "descriptionFormat": "html", - "time": 1465805138795, - "version": 2, - "responses": [], - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "folder": "3d67940d-3c7a-8127-3965-6aee160e6b6f", - "rawModeData": "{\r\n \"bridge-domain\": [\r\n {\r\n \"name\": \"testBD\",\r\n \"flood\": \"true\",\r\n \"forward\": \"false\",\r\n \"learn\": \"false\",\r\n \"unknown-unicast-flood\": \"false\",\r\n \"arp-termination\": \"false\"\r\n }\r\n ]\r\n}" - }, - { - "id": "dfbcc9e8-6f3d-b1c2-8bf6-989b1b1b3196", - "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n", - "url": "http://localhost:8181/restconf/config/v3po:vpp/bridge-domains/bridge-domain/testBD", - "preRequestScript": "", - "pathVariables": {}, - "method": "GET", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1465806733493, - "name": "Show bridge domain - cfg", - "description": "Shows testBD bridge domain configuration.", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\r\n \"bridge-domain\": [\r\n {\r\n \"name\": \"testBD\",\r\n \"flood\": \"true\",\r\n \"forward\": \"false\",\r\n \"learn\": \"false\",\r\n \"unknown-unicast-flood\": \"false\",\r\n \"arp-termination\": \"false\"\r\n }\r\n ]\r\n}" - }, - { - "id": "e2ff4db0-c013-cf89-d7b5-66de22bc7115", - "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n", - "url": "http://localhost:8181/restconf/operational/v3po:vpp-state/bridge-domains/bridge-domain/testBD", - "preRequestScript": "", - "pathVariables": {}, - "method": "GET", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1465806752970, - "name": "Show bridge domain - oper", - "description": "Shows testBD bridge domain operational state.\nCorresponds to invoking:\n\nvppctl show bridge-domain [bd_id] detail", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\r\n \"bridge-domain\": [\r\n {\r\n \"name\": \"testBD\",\r\n \"flood\": \"true\",\r\n \"forward\": \"false\",\r\n \"learn\": \"false\",\r\n \"unknown-unicast-flood\": \"false\",\r\n \"arp-termination\": \"false\"\r\n }\r\n ]\r\n}" - }, - { - "id": "a937f1fc-10d5-bc19-ad5f-ac91fc007779", - "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0/v3po:l2", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1465805545425, - "name": "Add local0 to bridge domain", - "description": "Adds l2 interconnection of bridge-based type to local0 interface.\nCorresponds to the following command:\n\nvppctl set interface l2 bridge local0 [bd_id]\n\nTo verify invoke:\n\nvppctl show bridge-domain [bd_id] detail", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\r\n \"l2\": {\r\n \"bridge-domain\": \"testBD\",\r\n \"split-horizon-group\": 1,\r\n \"bridged-virtual-interface\": \"false\"\r\n }\r\n}" - }, - { - "id": "cf177689-9dd7-bcfe-d490-e9de6bde4dbb", - "headers": "", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0/", - "preRequestScript": null, - "pathVariables": {}, - "method": "GET", - "data": null, - "dataMode": "params", - "tests": null, - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1465805728052, - "name": "Show local0 - cfg", - "description": "Shows local0 interface configuration", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [] - }, - { - "id": "0b63013d-3958-b427-2288-380019800468", - "headers": "", - "url": "http://localhost:8181/restconf/operational/ietf-interfaces:interfaces-state/interface/local0/", - "pathVariables": {}, - "preRequestScript": null, - "method": "GET", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "data": null, - "dataMode": "params", - "name": "Show local0 - oper", - "description": "Shows local0 interface operational state", - "descriptionFormat": "html", - "time": 1465805719840, - "version": 2, - "responses": [], - "tests": null, - "currentHelper": "normal", - "helperAttributes": {}, - "folder": "3d67940d-3c7a-8127-3965-6aee160e6b6f" - }, - { - "id": "99674f70-ec54-99f2-9300-545a031d5bab", - "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n", - "url": "http://localhost:8181/restconf/config/v3po:vpp/bridge-domains/bridge-domain/testBD/l2-fib-table/l2-fib-entry/aa:bb:cc:dd:ee:ff", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1465806456783, - "name": "Add L2 FIB entry (forward)", - "description": "Adds L2 FIB static entry. Corresponds to invoking:\n\nvppctl l2fib add aa:bb:cc:dd:ee:ff [bd_id] local0\n\nTo verify run:\n\nvppctl show l2fib verbose", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\n \"l2-fib-entry\": [\n {\n \"phys-address\": \"aa:bb:cc:dd:ee:ff\",\n \"outgoing-interface\": \"local0\",\n \"action\": \"l2-fib-forward\"\n }\n ]\n}" - }, - { - "id": "f624bc96-502b-685c-1596-672ad5fcf6a9", - "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n", - "url": "http://localhost:8181/restconf/config/v3po:vpp/bridge-domains/bridge-domain/testBD/l2-fib-table/l2-fib-entry/aa:bb:cc:dd:ee:ff", - "preRequestScript": "", - "pathVariables": {}, - "method": "DELETE", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1465806577485, - "name": "Delete L2 FIB entry", - "description": "Removes L2 FIB entry. Corresponds to invoking:\n\nvppctl l2fib del aa:bb:cc:dd:ee:ff [bd_id]\n\nTo verify run:\n\nvppctl show l2fib verbose", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "" - }, - { - "id": "0a1d6c0d-fe5e-1e1d-2471-96d7a4683e2f", - "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n", - "url": "http://localhost:8181/restconf/config/v3po:vpp/bridge-domains/bridge-domain/testBD/l2-fib-table/l2-fib-entry/11:22:33:44:55:66", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1465806064247, - "name": "Add L2 FIB entry (static, forward)", - "description": "Adds L2 FIB static entry. Corresponds to invoking:\n\nvppctl l2fib add 11:22:33:44:55:66 [bd_id] local0 static\n\nTo verify run:\n\nvppctl show l2fib verbose", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\n \"l2-fib-entry\": [\n {\n \"phys-address\": \"11:22:33:44:55:66\",\n \"outgoing-interface\": \"local0\",\n \"static-config\": \"true\",\n \"action\": \"l2-fib-forward\"\n }\n ]\n}" - }, - { - "id": "d752fed9-127a-9289-71f3-90ba2b459d77", - "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n", - "url": "http://localhost:8181/restconf/config/v3po:vpp/bridge-domains/bridge-domain/testBD/l2-fib-table/l2-fib-entry/00:01:02:03:04:05", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1465814845273, - "name": "Add L2 FIB entry (filter)", - "description": "Adds L2 FIB static entry. Corresponds to invoking:\n\nvppctl l2fib add 00:01:02:03:04:05 [bd_id] filter\n\nTo verify run:\n\nvppctl show l2fib verbose", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\n \"l2-fib-entry\": [\n {\n \"phys-address\": \"00:01:02:03:04:05\",\n \"outgoing-interface\": \"local0\",\n \"static-config\": \"true\",\n \"action\": \"l2-fib-filter\"\n }\n ]\n}" - }, - { - "id": "5c800144-cf89-7b77-1dd2-f488101ca441", - "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0/v3po:l2", - "preRequestScript": "", - "pathVariables": {}, - "method": "DELETE", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1465984375972, - "name": "Remove local0 from bridge domain", - "description": "Removes l2 interconnection of bridge-based type from local0 interface.\nCorresponds to the following VAT command:\n\nvat# sw_interface_set_l2_bridge sw_if_index 0 bd_id 1 disable\n\nVerification in VAT:\n\nvat# bridge_domain_dump", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "" - }, - { - "id": "d7cb3d97-fc40-f471-33f3-07aad557777d", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/vpp-classifier:vpp-classifier/", - "preRequestScript": "", - "pathVariables": {}, - "method": "GET", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1467377260818, - "name": "Get classify tables - cfg", - "description": "Shows classify table configuration.", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "f91a6422-9724-e504-a94c-c5a953315b6f", - "rawModeData": "" - }, - { - "id": "c067f721-6b1c-f205-799d-720b242f8592", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/operational/vpp-classifier:vpp-classifier-state/", - "preRequestScript": "", - "pathVariables": {}, - "method": "GET", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1467377066230, - "name": "Get classify tables - oper", - "description": "Shows classify tables configured in the VPP.\n\nCorresponds to:\n\nvppctl sh class table verbose", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "f91a6422-9724-e504-a94c-c5a953315b6f", - "rawModeData": "" - }, - { - "id": "5ab03c5f-03c5-8d42-bf65-024239049857", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/vpp-classifier:vpp-classifier/classify-table/table0", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1468493354688, - "name": "Add classify table0", - "description": "Adds classify table0. Corresponding vpp cli command:\n\nvppctl classify table mask l2 src\n\nTo verify invoke:\n\nvppctl sh class table verbose\n\nor:\n\nvat# classify_table_info table_id 0", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "f91a6422-9724-e504-a94c-c5a953315b6f", - "rawModeData": "{\n \"classify-table\": [\n {\n \"name\": \"table0\",\n \"nbuckets\": \"2\",\n \"memory_size\": \"1048576\",\n \"miss_next\": \"permit\",\n \"mask\": \"00:00:00:00:00:00:ff:ff:ff:ff:ff:ff:00:00:00:00\"\n }\n ]\n}" - }, - { - "id": "4f11c937-9562-3377-d03b-a9829b25f688", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/operational/vpp-classifier:vpp-classifier-state/classify-table/table0", - "preRequestScript": "", - "pathVariables": {}, - "method": "GET", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1467378420028, - "name": "Get classify table - oper", - "description": "Shows classify table 0 operational state.\n\nCorresponds to:\n\nvat# classify_table_info table_id 0", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "f91a6422-9724-e504-a94c-c5a953315b6f", - "rawModeData": "" - }, - { - "id": "491ee2b7-88bf-3a71-282d-a16a62996026", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/vpp-classifier:vpp-classifier/classify-table/table1", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1468493298160, - "name": "Add classify table1", - "description": "Adds classify table1 chained to classify table0.\n\nCorresponding vpp cli command:\n\nvppctl classify table mask l2 dst next-table 0\n\nTo verify invoke:\n\nvppctl sh class table verbose\n\nor:\n\nvat# classify_table_info table_id 1", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "f91a6422-9724-e504-a94c-c5a953315b6f", - "rawModeData": "{\n \"classify-table\": [\n {\n \"name\": \"table1\",\n \"next_table\": \"table0\",\n \"nbuckets\": \"2\",\n \"memory_size\": \"1048576\",\n \"miss_next\": \"permit\",\n \"mask\": \"ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00\"\n }\n ]\n}" - }, - { - "id": "fa63c194-ea7a-51ad-a6d8-d7485ed67f2e", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/vpp-classifier:vpp-classifier/classify-table/table0/classify-session/00:00:00:00:00:00:01:02:03:04:05:06:00:00:00:00", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1468493546163, - "name": "Add classify session", - "description": "Adds classify session to table0. Corresponding vpp cli command:\n\nvppctl classify session acl-hit-next deny opaque-index 0 table-index 0 match l2 src 01:02:03:04:05:06\n\nTo verify invoke:\n\nvppctl sh class table verbose", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "f91a6422-9724-e504-a94c-c5a953315b6f", - "rawModeData": "{\n \"classify-session\": [\n {\n \"hit_next\": \"deny\",\n \"match\": \"00:00:00:00:00:00:01:02:03:04:05:06:00:00:00:00\"\n }\n ]\n}" - }, - { - "id": "b9931024-fa86-4e04-e2a4-4feeaa9e99e1", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/vpp-classifier:vpp-classifier/classify-table/table0/classify-session/00:00:00:00:00:00:01:02:03:04:05:07:00:00:00:00", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1468493583927, - "name": "Add another classify session", - "description": "Adds second classify session to table0. Corresponding vpp cli command:\n\nvppctl classify session acl-hit-next deny opaque-index 0 table-index 0 match l2 src 01:02:03:04:05:07\n\nTo verify invoke:\n\nvppctl sh class table verbose", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "f91a6422-9724-e504-a94c-c5a953315b6f", - "rawModeData": "{\n \"classify-session\": [\n {\n \"hit_next\": \"deny\",\n \"match\": \"00:00:00:00:00:00:01:02:03:04:05:07:00:00:00:00\"\n }\n ]\n}" - }, - { - "id": "d39e4c39-90b6-072c-44b8-d909cac53a5b", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0/v3po:acl", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1468495690154, - "name": "Enable L2 and Ipv4 ACL on local0 interface", - "description": "Enables L2 and Ipv4 ACL on local0. Corresponding vpp cli commands:\n\nset int input acl intfc local0 l2-table 0\n\nset int input acl intfc local0 ip4-table 0\n\nTo verify invoke:\n\nvppctl show inacl type l2\n\nthen:\n\nvppctl show inacl type ip4", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "f91a6422-9724-e504-a94c-c5a953315b6f", - "rawModeData": "{\n \"v3po:acl\": {\n \"l2-acl\": {\n \"classify-table\": \"table0\"\n },\n \"ip4-acl\": {\n \"classify-table\": \"table0\"\n }\n }\n}" - }, - { - "id": "2ce36b98-d7a9-fe37-80f6-f3a4abf25103", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0/v3po:acl/", - "preRequestScript": "", - "pathVariables": {}, - "method": "DELETE", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1467380189765, - "name": "Disable ACL on local0 interface", - "description": "Disables ACL on local0. To verify invoke:\n\nvppctl show inacl type l2\n\nand:\n\nvppctl show inacl type ip4", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "f91a6422-9724-e504-a94c-c5a953315b6f", - "rawModeData": "" - }, - { - "id": "d0984799-d321-d1e0-a855-cc434519bef0", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/vpp-classifier:vpp-classifier/classify-table/table0/classify-session/00:00:00:00:00:00:01:02:03:04:05:06:00:00:00:00", - "preRequestScript": "", - "pathVariables": {}, - "method": "DELETE", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1468493934488, - "name": "Remove classify session", - "description": "Removes classify session. To verify invoke:\n\nvppctl sh class table verbose", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "f91a6422-9724-e504-a94c-c5a953315b6f", - "rawModeData": "" - }, - { - "id": "be223323-bf59-9c4f-f896-ff74c03b01a1", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/vpp-classifier:vpp-classifier/classify-table/table0", - "preRequestScript": "", - "pathVariables": {}, - "method": "DELETE", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1467377830897, - "name": "Remove classify table0", - "description": "Removes classify table0. Corresponds to the following command:\n\nvppctl classify table del table 0\n\nTo verify invoke:\n\nvppctl sh class table verbose", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "f91a6422-9724-e504-a94c-c5a953315b6f", - "rawModeData": "" - }, - { - "id": "ee11f1c0-a1fe-51a6-a5e5-fcdcfc2b620a", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/vpp-classifier:vpp-classifier/classify-table/table1", - "preRequestScript": "", - "pathVariables": {}, - "method": "DELETE", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1467377388428, - "name": "Remove classify table1", - "description": "Removes classify table1. Corresponds to the following command:\n\nvppctl classify table del table 1\n\nTo verify invoke:\n\nvppctl sh class table verbose", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "f91a6422-9724-e504-a94c-c5a953315b6f", - "rawModeData": "" - }, - { - "id": "3f9588e4-885f-3792-bdf4-d0f10704ae4d", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/tapp", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1462535811974, - "name": "Add simple tap ifc -cfg", - "description": "", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "60596aab-a4f1-bb64-d701-816de9482201", - "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"tapp\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"v3po:tap\",\r\n \"tap\" :{\r\n \"tap-name\" : \"tapp\"\r\n }\r\n }\r\n ]\r\n \r\n}" - }, - { - "id": "3fccc4cd-f14b-0333-83d5-924afe3938e4", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1460640004531, - "name": "Set vrf id for local0 - cfg", - "description": "", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"local0\",\r\n \"description\": \"for testing purposes only\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"enabled\": \"true\",\r\n \"routing\" : {\r\n \"vrf-id\" : \"7\"\r\n },\r\n \"v3po:ethernet\": {\r\n \"mtu\": 64\r\n }\r\n }\r\n ]\r\n \r\n}" - }, - { - "id": "44de3eff-ace5-90a9-dd16-af3f5f13daad", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F9%2F0/vpp-vlan:sub-interfaces/sub-interface/1/l2/rewrite", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1464805188780, - "name": "Enable tag-rewrite translate 1-2 - cfg", - "description": "Enables tag-rewrite translate 1-2 operation for GigabitEthernet0/9/0.1 sub-interface. Corresponsing vpp cli command:\n\nvppctl set interface l2 tag-rewrite GigabitEthernet0/9/0.1 translate 1-2 dot1q 111 222\n\nTo verify run:\n./build-root/install-vpp-native/vpp-api-test/bin/vpp_api_test json\nand invoke:\n\n#vat sw_interface_dump\n\nor if sub-interface was added to bridge domain:\n\nvppctl show bridge-domain [bd_id] detail", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "37fd9569-06e3-1f08-fbcc-4b1462107a32", - "rawModeData": "{\r\n \"rewrite\": {\r\n \"vlan-type\": \"vpp-vlan:802dot1q\",\r\n \"pop-tags\": \"1\",\r\n \"push-tags\": [\r\n {\r\n \"index\": 0,\r\n \"dot1q-tag\": {\r\n \"tag-type\": \"dot1q-types:s-vlan\",\r\n \"vlan-id\": 111\r\n }\r\n },\r\n {\r\n \"index\": 1,\r\n \"dot1q-tag\": {\r\n \"tag-type\": \"dot1q-types:c-vlan\",\r\n \"vlan-id\": 222\r\n }\r\n }\r\n ]\r\n }\r\n}" - }, - { - "id": "4db9b360-0d57-6ce6-7e3c-a6acfe17d512", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/tapp2", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1462796801468, - "name": "Modify complex tap ifc - cfg", - "description": "", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"tapp2\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"v3po:tap\",\r\n \"tap\" :{\r\n \"tap-name\" : \"tapp2\",\r\n \"mac\" : \"00:ff:ff:ff:ff:ae\",\r\n \"device-instance\" : 77\r\n }\r\n }\r\n ]\r\n \r\n}", - "folder": "60596aab-a4f1-bb64-d701-816de9482201" - }, - { - "id": "4f15d0e9-8530-157e-0fe8-d24034bc31df", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/vxlanTun1", - "preRequestScript": "", - "pathVariables": {}, - "method": "DELETE", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": "{}", - "time": 1462892071867, - "name": "Delete virtual ifc - cfg", - "description": "", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "3868e66d-0ee5-bd4d-6b35-075c4841b5c1", - "timestamp": null, - "rawModeData": "" - }, - { - "id": "55102dbd-1fc9-5f91-2082-40a2dc311f4f", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/vhost1", - "preRequestScript": "", - "pathVariables": {}, - "method": "DELETE", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": "{}", - "time": 1462891488523, - "name": "Delete vhost user ifc - cfg", - "description": "Deletes vhost user interface.\nCorresponding vpp CLI command:\n\nvppctl delete vhost-user sw_if_index [index]\n\nTo verify invoke:\n\nvppctl show vhost-user\n\nor\n\nvppctl show int", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "c97b9ad9-64e6-5de3-09b8-851c1189d767", - "rawModeData": "", - "descriptionFormat": null - }, - { - "id": "5995dcf7-51c0-42ef-448b-a70bb68735d5", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/tapp2", - "preRequestScript": "", - "pathVariables": {}, - "method": "DELETE", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1462796994090, - "name": "Delete complex tap ifc - cfg", - "description": "", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "", - "folder": "60596aab-a4f1-bb64-d701-816de9482201" - }, - { - "id": "5dc1d9f4-e7ae-bb21-e558-8a56ac825922", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/operational/v3po:vpp-state", - "preRequestScript": "", - "pathVariables": {}, - "method": "GET", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1460539994330, - "name": "Read vpp-state - oper", - "description": "", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"testInterface\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"enabled\": \"true\",\r\n \"link-up-down-trap-enable\": \"enabled\",\r\n \"ietf-ip:ipv4\": {\r\n \"enabled\": \"true\",\r\n \"mtu\": \"1500\",\r\n \"address\": [\r\n {\r\n \"ip\": \"1.2.3.0\",\r\n \"netmask\": \"255.255.255.0\"\r\n }\r\n ]\r\n }\r\n }\r\n ]\r\n \r\n}" - }, - { - "id": "7f312f15-a81e-b513-83b7-a5f7755eae67", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/vhost1", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1462891472552, - "name": "Add vhost user ifc - cfg", - "description": "Adds vhost-user interface.\nCorresponsing vpp cli command:\n\ncreate vhost-user socket /tmp/soc1 server\n\nTo verify invoke:\n\nvppctl show vhost-user\n\nor\n\nvppctl show int", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"vhost1\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"v3po:vhost-user\",\r\n \"enabled\": \"true\",\r\n \"vhost-user\" : {\r\n \"socket\": \"/tmp/soc1\",\r\n \"role\": \"server\"\r\n }\r\n }\r\n ]\r\n \r\n}", - "folder": "c97b9ad9-64e6-5de3-09b8-851c1189d767" - }, - { - "id": "8b339568-b60a-715f-d4fd-416bafe981a9", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F9%2F0/vpp-vlan:sub-interfaces/sub-interface/1/l2/rewrite", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": "{}", - "time": 1464803940412, - "name": "Enable tag-rewrite push - cfg", - "description": "Enables tag-rewrite push operation for GigabitEthernet0/9/0.1 sub-interface. Corresponsing vpp cli command:\n\nvppctl set interface l2 tag-rewrite GigabitEthernet0/9/0.1 push dot1q 123 456\n\nTo verify run:\n./build-root/install-vpp-native/vpp-api-test/bin/vpp_api_test json\nand invoke:\n\n#vat sw_interface_dump\n\nor if sub-interface was added to bridge domain:\n\nvppctl show bridge-domain [bd_id] detail", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "37fd9569-06e3-1f08-fbcc-4b1462107a32", - "timestamp": null, - "rawModeData": "{\r\n \"rewrite\": {\r\n \"vlan-type\": \"vpp-vlan:802dot1q\",\r\n \"push-tags\": [\r\n {\r\n \"index\": 0,\r\n \"dot1q-tag\": {\r\n \"tag-type\": \"dot1q-types:s-vlan\",\r\n \"vlan-id\": 123\r\n }\r\n },\r\n {\r\n \"index\": 1,\r\n \"dot1q-tag\": {\r\n \"tag-type\": \"dot1q-types:c-vlan\",\r\n \"vlan-id\": 456\r\n }\r\n }\r\n ]\r\n }\r\n}" - }, - { - "id": "8b867c90-459f-68f6-4b2e-fa653098c28d", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1460540047356, - "name": "Enable local0 interface - cfg", - "description": "", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"local0\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"enabled\": \"true\",\r\n \"v3po:ethernet\": {\r\n \"mtu\": 64\r\n }\r\n }\r\n ]\r\n \r\n}" - }, - { - "id": "93022e97-24fa-5784-1e59-94674817215f", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/operational/naming-context:contexts", - "pathVariables": {}, - "preRequestScript": "", - "method": "GET", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "data": [], - "dataMode": "raw", - "name": "List naming contexts - context", - "description": "List mapping context stored in context datastore", - "descriptionFormat": "html", - "time": 1463556756647, - "version": 2, - "responses": [], - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"testInterface\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"enabled\": \"true\",\r\n \"link-up-down-trap-enable\": \"enabled\",\r\n \"ietf-ip:ipv4\": {\r\n \"enabled\": \"true\",\r\n \"mtu\": \"1500\",\r\n \"address\": [\r\n {\r\n \"ip\": \"1.2.3.0\",\r\n \"netmask\": \"255.255.255.0\"\r\n }\r\n ]\r\n }\r\n }\r\n ]\r\n \r\n}" - }, - { - "id": "69a05849-026d-f120-5c6a-f3e2b0884d88", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/operational/ietf-interfaces:interfaces-state/interface/local0", - "preRequestScript": "", - "pathVariables": {}, - "method": "GET", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1465472429324, - "name": "Read local0 - oper", - "description": "", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "7a914134-23ea-3154-1557-d29dc8d464e7", - "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"testInterface\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"enabled\": \"true\",\r\n \"link-up-down-trap-enable\": \"enabled\",\r\n \"ietf-ip:ipv4\": {\r\n \"enabled\": \"true\",\r\n \"mtu\": \"1500\",\r\n \"address\": [\r\n {\r\n \"ip\": \"1.2.3.0\",\r\n \"netmask\": \"255.255.255.0\"\r\n }\r\n ]\r\n }\r\n }\r\n ]\r\n \r\n}" - }, - { - "id": "b10169c2-ae0e-bb1c-3dea-ca06839c6c9c", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0/ipv4/address/127.0.0.1", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1466496859574, - "name": "Set ipv4 local0 interface - cfg", - "description": "", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "7a914134-23ea-3154-1557-d29dc8d464e7", - "rawModeData": "{\r\n \"address\": [{\r\n \"ip\" : \"127.0.0.1\",\r\n \"prefix-length\" : \"24\"\r\n }]\r\n}" - }, - { - "id": "8d49cf41-facc-c4ac-cee7-475d50e2a0be", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0/ipv4/address/127.0.0.1", - "preRequestScript": "", - "pathVariables": {}, - "method": "DELETE", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1466497187475, - "name": "Remove ipv4 from local0 interface - cfg", - "description": "", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "7a914134-23ea-3154-1557-d29dc8d464e7", - "rawModeData": "" - }, - { - "id": "70835949-7252-a3cc-491b-f8aa69286399", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0/ipv4/address/127.0.0.1", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1466497176571, - "name": "Set ipv4 local0 interface - cfg netmask", - "description": "", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "7a914134-23ea-3154-1557-d29dc8d464e7", - "rawModeData": "{\r\n \"address\": [{\r\n \"ip\" : \"127.0.0.1\",\r\n \"netmask\": \"255.255.255.128\"\r\n }]\r\n}" - }, - { - "id": "d5a90cfd-fd7e-63b6-e1f1-16fc0671b0e1", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/operational/ietf-interfaces:interfaces-state/interface/local0/ipv4", - "preRequestScript": "", - "pathVariables": {}, - "method": "GET", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1465473594194, - "name": "Read local0/ipv4 - oper", - "description": "", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "7a914134-23ea-3154-1557-d29dc8d464e7", - "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"testInterface\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"enabled\": \"true\",\r\n \"link-up-down-trap-enable\": \"enabled\",\r\n \"ietf-ip:ipv4\": {\r\n \"enabled\": \"true\",\r\n \"mtu\": \"1500\",\r\n \"address\": [\r\n {\r\n \"ip\": \"1.2.3.0\",\r\n \"netmask\": \"255.255.255.0\"\r\n }\r\n ]\r\n }\r\n }\r\n ]\r\n \r\n}" - }, - { - "id": "3f7ee49b-ae11-b032-915f-a14bf838246f", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0/ipv4", - "preRequestScript": "", - "pathVariables": {}, - "method": "GET", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1466497212835, - "name": "Read local0/ipv4 - cfg", - "description": "", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "7a914134-23ea-3154-1557-d29dc8d464e7", - "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"testInterface\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"enabled\": \"true\",\r\n \"link-up-down-trap-enable\": \"enabled\",\r\n \"ietf-ip:ipv4\": {\r\n \"enabled\": \"true\",\r\n \"mtu\": \"1500\",\r\n \"address\": [\r\n {\r\n \"ip\": \"1.2.3.0\",\r\n \"netmask\": \"255.255.255.0\"\r\n }\r\n ]\r\n }\r\n }\r\n ]\r\n \r\n}" - }, - { - "id": "345ba26c-60cf-5fa1-37d0-f5af7a8c33bf", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F9%2F0/vpp-vlan:sub-interfaces/sub-interface/1/ipv4/address/1.2.3.4", - "pathVariables": {}, - "preRequestScript": "", - "method": "PUT", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "data": [], - "dataMode": "raw", - "name": "Set ipv4 for sub-interface - cfg", - "description": "Correponds to the following command:\n\nvppctl set interface ip address GigabitEthernet0/9/0.1 1.2.3.4/8\n\nTo verify invoke:\nvppctl show int", - "descriptionFormat": "html", - "time": 1467117222646, - "version": 2, - "responses": [], - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "folder": "7a914134-23ea-3154-1557-d29dc8d464e7", - "rawModeData": "{\r\n \"address\": [{\r\n \"ip\" : \"1.2.3.4\",\r\n \"prefix-length\" : \"8\"\r\n }]\r\n}" - }, - { - "id": "ac161bec-e046-4bb7-d695-60bdfb0c6cea", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/vhost1", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1462891483790, - "name": "Modify vhost user ifc - cfg", - "description": "Modifies vhost-user interface socket.\nTo verify invoke:\n\nvppctl show vhost-user", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"vhost1\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"v3po:vhost-user\",\r\n \"enabled\": \"true\",\r\n \"vhost-user\" : {\r\n \"socket\": \"/tmp/soc2\",\r\n \"role\": \"server\"\r\n }\r\n }\r\n ]\r\n \r\n}", - "folder": "c97b9ad9-64e6-5de3-09b8-851c1189d767" - }, - { - "id": "b08a0677-6b59-74ea-e5c6-7383194b19dd", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F9%2F0/vpp-vlan:sub-interfaces/sub-interface/1/l2/rewrite", - "preRequestScript": "", - "pathVariables": {}, - "method": "DELETE", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1464804511683, - "name": "Disable tag-rewrite - cfg", - "description": "Disables tag-rewrite operation for GigabitEthernet0/9/0.1 sub-interface. Corresponsing vpp cli command:\n\nvppctl set interface l2 tag-rewrite GigabitEthernet0/9/0.1 disable\n\nTo verify run:\n./build-root/install-vpp-native/vpp-api-test/bin/vpp_api_test json\nand invoke:\n\n#vat sw_interface_dump\n\nor if sub-interface was added to bridge domain:\n\nvppctl show bridge-domain [bd_id] detail", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "37fd9569-06e3-1f08-fbcc-4b1462107a32", - "rawModeData": "" - }, - { - "id": "bd2897c6-5fc7-bec5-87be-4fd52bb8e471", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F9%2F0/vpp-vlan:sub-interfaces/sub-interface/1/l2/rewrite", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1464805111410, - "name": "Enable tag-rewrite pop 1 - cfg", - "description": "Enables tag-rewrite pop 1 operation for GigabitEthernet0/9/0.1 sub-interface. Corresponsing vpp cli command:\n\nvppctl set interface l2 tag-rewrite GigabitEthernet0/9/0.1 pop 1\n\nTo verify run:\n./build-root/install-vpp-native/vpp-api-test/bin/vpp_api_test json\nand invoke:\n\n#vat sw_interface_dump\n\nor if sub-interface was added to bridge domain:\n\nvppctl show bridge-domain [bd_id] detail", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "37fd9569-06e3-1f08-fbcc-4b1462107a32", - "rawModeData": "{\r\n \"rewrite\": {\r\n \"pop-tags\": \"1\"\r\n }\r\n}" - }, - { - "id": "cbb77318-52e0-5647-cd1c-8c679ea7b830", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F9%2F0/vpp-vlan:sub-interfaces/sub-interface/1/l2", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1464813539029, - "name": "Add sub interface to bridge-domain", - "description": "Adds sub interface to bridge domain. Corresponsing vpp cli command:\n\nvppctl set interface l2 bridge GigabitEthernet0/9/0.1 1 1\n\nTo verify invoke:\nvppctl show bridge-domain 1 detail", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "37fd9569-06e3-1f08-fbcc-4b1462107a32", - "rawModeData": "{\r\n \"l2\": {\r\n \"bridge-domain\": \"testBD\",\r\n \"split-horizon-group\": 1,\r\n \"bridged-virtual-interface\": \"false\"\r\n }\r\n}" - }, - { - "id": "d27322b8-59d5-a2dc-a7a2-8f7197057164", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/operational/ietf-interfaces:interfaces-state/", - "preRequestScript": "", - "pathVariables": {}, - "method": "GET", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1460539972177, - "name": "List ifcs - oper", - "description": "", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"testInterface\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"enabled\": \"true\",\r\n \"link-up-down-trap-enable\": \"enabled\",\r\n \"ietf-ip:ipv4\": {\r\n \"enabled\": \"true\",\r\n \"mtu\": \"1500\",\r\n \"address\": [\r\n {\r\n \"ip\": \"1.2.3.0\",\r\n \"netmask\": \"255.255.255.0\"\r\n }\r\n ]\r\n }\r\n }\r\n ]\r\n \r\n}" - }, - { - "id": "e611d23a-c205-192b-e8f9-dd99fa169274", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1460636113690, - "name": "Set interfaces bulk edit - cfg", - "description": "", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\n \"interfaces\": {\n \"interface\": [\n {\n \"name\": \"pg/stream-2\",\n \"enabled\": true,\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n {\n \"name\": \"pg/stream-3\",\n \"enabled\": true,\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n {\n \"name\": \"pg/stream-0\",\n \"enabled\": true,\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n {\n \"name\": \"local0\",\n \"description\": \"for testing purposes\",\n \"type\": \"iana-if-type:ethernetCsmacd\",\n \"enabled\": true\n },\n {\n \"name\": \"pg/stream-1\",\n \"enabled\": true,\n \"type\": \"iana-if-type:ethernetCsmacd\"\n }\n ]\n }\n}" - }, - { - "id": "f014ab21-8e40-c344-a68b-0f1b86578bd8", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/vxlanTun1", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1464767860505, - "name": "Add virtual ifc - cfg", - "description": "", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"vxlanTun1\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"v3po:vxlan-tunnel\",\r\n \"enabled\": \"true\",\r\n \"link-up-down-trap-enable\": \"enabled\",\r\n \"routing\" : {\r\n \"vrf-id\" : \"0\"\r\n },\r\n \"vxlan\" : {\r\n \"src\" : \"192.168.1.6\",\r\n \"dst\" : \"192.168.1.9\",\r\n \"vni\" : \"88\",\r\n \"encap-vrf-id\" : \"0\"\r\n }\r\n }\r\n ]\r\n \r\n}", - "folder": "3868e66d-0ee5-bd4d-6b35-075c4841b5c1" - }, - { - "id": "f2e9a3cb-f3cc-f501-6015-8ebf7d8b2c3e", - "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", - "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F9%2F0/vpp-vlan:sub-interfaces/sub-interface/1", - "preRequestScript": "", - "pathVariables": {}, - "method": "PUT", - "data": [], - "dataMode": "raw", - "version": 2, - "tests": "", - "currentHelper": "normal", - "helperAttributes": {}, - "time": 1464804333865, - "name": "Enable GigabitEthernet0/9/0.1 interface - cfg", - "description": "Enables GigabitEthernet0/9/0.1 sub interface. Equivalent vppctl command:\n\nvppctl set in state GigabitEthernet0/9/0.1 up\n\nTo enable sub interface, super interface should be enabled first.\n\nTo verify invoke:\n\nvppctl show int", - "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7", - "responses": [], - "folder": "37fd9569-06e3-1f08-fbcc-4b1462107a32", - "rawModeData": "{\r\n \"sub-interface\": [\r\n {\r\n \"identifier\": \"1\",\r\n \"vlan-type\": \"802dot1q\",\r\n \"tags\": {\r\n \"tag\": [\r\n {\r\n \"index\": \"0\",\r\n \"dot1q-tag\": {\r\n \"tag-type\": \"dot1q-types:s-vlan\",\r\n \"vlan-id\": \"100\"\r\n }\r\n },\r\n {\r\n \"index\": \"1\",\r\n \"dot1q-tag\": {\r\n \"tag-type\": \"dot1q-types:c-vlan\",\r\n \"vlan-id\": \"any\"\r\n }\r\n }\r\n ]\r\n },\r\n \"match\": {\r\n \"vlan-tagged\": {\r\n \"match-exact-tags\": \"true\"\r\n }\r\n },\r\n \"enabled\": \"true\"\r\n }\r\n ]\r\n}" - } - ] -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/MappingContext.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/MappingContext.java new file mode 100644 index 000000000..5a5ba980c --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/MappingContext.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate; + +import com.google.common.base.Optional; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Mapping context is persisted storage where mapping matadata are stored. + * A snapshot is created for each transaction to provide consistent view over context data. + * After a transaction is successfully finished, objects added to this context are propagated to backing storage. + */ +public interface MappingContext extends AutoCloseable { + + /** + * Read any mapping context data + * + * @param currentId Id of an object to read + * + * @return Relevant mapping context data + */ + Optional read(@Nonnull final InstanceIdentifier currentId); + + /** + * Delete the node at specified path. + * + * @param path Node path + */ + void delete(InstanceIdentifier path); + + /** + * Merge the specified data with the currently-present data + * at specified path. + * + * @param path Node path + * @param data Data to be merged + */ + void merge(InstanceIdentifier path, T data); + + /** + * Replace the data at specified path with supplied data. + * + * @param path Node path + * @param data New node data + */ + void put(InstanceIdentifier path, T data); + + @Override + void close(); +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/ModifiableSubtreeManagerRegistryBuilder.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/ModifiableSubtreeManagerRegistryBuilder.java new file mode 100644 index 000000000..4e6b2d805 --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/ModifiableSubtreeManagerRegistryBuilder.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate; + +import java.util.Collection; +import java.util.Set; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Registry builder where {@link SubtreeManager}s can be added with or without relationships between them. + * The relationships express what the order of execution should be. + */ +public interface ModifiableSubtreeManagerRegistryBuilder> { + + /** + * Add a handler responsible for writing only a single complex node. + */ + ModifiableSubtreeManagerRegistryBuilder add(@Nonnull S handler); + + /** + * Add a handler responsible for writing multiple complex nodes within a subtree its responsible for. Identifiers for + * subtree nodes handled by a single handler have to be relative from {@link DataObject} that represents subtree + * root. + */ + ModifiableSubtreeManagerRegistryBuilder subtreeAdd(@Nonnull Set> handledChildren, + @Nonnull S handler); + + /** + * Add a handler and make sure it will be executed before handler identifier by relatedType is executed. + */ + ModifiableSubtreeManagerRegistryBuilder addBefore(@Nonnull S handler, + @Nonnull InstanceIdentifier relatedType); + + ModifiableSubtreeManagerRegistryBuilder addBefore(@Nonnull S handler, + @Nonnull Collection> relatedTypes); + + ModifiableSubtreeManagerRegistryBuilder subtreeAddBefore(@Nonnull Set> handledChildren, + @Nonnull S handler, + @Nonnull InstanceIdentifier relatedType); + + ModifiableSubtreeManagerRegistryBuilder subtreeAddBefore(@Nonnull Set> handledChildren, + @Nonnull S handler, + @Nonnull Collection> relatedTypes); + + /** + * Add a handler and make sure it will be executed after handler identifier by relatedType is executed. + */ + ModifiableSubtreeManagerRegistryBuilder addAfter(@Nonnull S handler, + @Nonnull InstanceIdentifier relatedType); + + ModifiableSubtreeManagerRegistryBuilder addAfter(@Nonnull S handler, + @Nonnull Collection> relatedTypes); + + ModifiableSubtreeManagerRegistryBuilder subtreeAddAfter(@Nonnull Set> handledChildren, + @Nonnull S handler, + @Nonnull InstanceIdentifier relatedType); + + ModifiableSubtreeManagerRegistryBuilder subtreeAddAfter(@Nonnull Set> handledChildren, + @Nonnull S handler, + @Nonnull Collection> relatedTypes); +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/ModificationCache.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/ModificationCache.java new file mode 100644 index 000000000..dcf97439b --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/ModificationCache.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate; + +import com.google.common.collect.Maps; +import java.util.HashMap; + +/** + * Simple context class that provides transient storage during one or more read/write operations + */ +public class ModificationCache implements AutoCloseable { + + protected final HashMap map; + + public ModificationCache() { + map = Maps.newHashMap(); + } + + public Object get(final Object o) { + return map.get(o); + } + + public boolean containsKey(final Object o) { + return map.containsKey(o); + } + + public Object put(final Object o, final Object o2) { + return map.put(o, o2); + } + + @Override + public void close() { + map.clear(); + } +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/ModificationContext.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/ModificationContext.java new file mode 100644 index 000000000..049c6c05e --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/ModificationContext.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate; + +import javax.annotation.Nonnull; + +/** + * Common context for both writes and reads + */ +public interface ModificationContext extends AutoCloseable { + + /** + * Get key value transient storage for customizers. Is cleared for each new transaction. + * + * @return Context for customizers + */ + @Nonnull + ModificationCache getModificationCache(); + + /** + * Get persistent storage for mapping context. This context survives a modification. + * + * @return Mapping context accessor + */ + @Nonnull + MappingContext getMappingContext(); + + @Override + void close(); +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/SubtreeManager.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/SubtreeManager.java new file mode 100644 index 000000000..1e98582df --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/SubtreeManager.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate; + +import com.google.common.annotations.Beta; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Base identifiable subtree manager(reader, writer etc.) + * + * @param Specific DataObject derived type, that is managed by this manager + */ +@Beta +public interface SubtreeManager { + + /** + * Gets the type of node managed by this reader. + * + * @return Absolute instance identifier for managed type + */ + @Nonnull + InstanceIdentifier getManagedDataObjectType(); +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/SubtreeManagerRegistryBuilder.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/SubtreeManagerRegistryBuilder.java new file mode 100644 index 000000000..7d4104916 --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/SubtreeManagerRegistryBuilder.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate; + +public interface SubtreeManagerRegistryBuilder { + + R build(); +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/TranslationException.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/TranslationException.java new file mode 100644 index 000000000..6037391e2 --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/TranslationException.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate; + +import com.google.common.annotations.Beta; + +/** + * Base exception for the translation layer + */ +@Beta +public class TranslationException extends Exception { + + public TranslationException(final String s) { + super(s); + } + + public TranslationException(final String s, final Throwable cause) { + super(s, cause); + } + + public TranslationException(final Throwable cause) { + super(cause); + } +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/ListReader.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/ListReader.java new file mode 100644 index 000000000..3791d8e2a --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/ListReader.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.read; + +import com.google.common.annotations.Beta; +import java.util.Collections; +import java.util.List; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.Identifiable; +import org.opendaylight.yangtools.yang.binding.Identifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * List reader, allowing read of all the elements. + * + * @param Specific DataObject derived type, that is handled by this reader + */ +@Beta +public interface ListReader + , K extends Identifier, B extends Builder> extends Reader { + + /** + * Read all elements in this list. + * + * @param id Wildcarded identifier of list managed by this reader + * @param ctx Read context + * + * @return List of all entries in this list + * @throws ReadFailedException if read was unsuccessful + */ + @Nonnull + List readList(@Nonnull final InstanceIdentifier id, @Nonnull final ReadContext ctx) + throws ReadFailedException; + + /** + * Get IDs for all entries in the list. + */ + List getAllIds(@Nonnull InstanceIdentifier id, @Nonnull ReadContext ctx) + throws ReadFailedException; + + /** + * Merge read data into provided parent builder. + */ + void merge(@Nonnull final Builder builder, @Nonnull final List readData); + + @Override + default void merge(@Nonnull final Builder parentBuilder, @Nonnull final D readValue) { + merge(parentBuilder, Collections.singletonList(readValue)); + } +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/ReadContext.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/ReadContext.java new file mode 100644 index 000000000..655a5164c --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/ReadContext.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.read; + +import io.fd.honeycomb.translate.ModificationContext; + +/** + * Context providing information about current state of DataTree to readers + */ +public interface ReadContext extends ModificationContext { + +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/ReadFailedException.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/ReadFailedException.java new file mode 100644 index 000000000..fc429b1d0 --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/ReadFailedException.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.read; + +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.honeycomb.translate.TranslationException; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Thrown when a reader or customizer is not able to read data for the given id. + */ +public class ReadFailedException extends TranslationException { + + private final InstanceIdentifier failedId; + + /** + * Constructs an ReadFailedException given data id and exception cause. + * + * @param failedId instance identifier of the data object that could not be read + * @param cause the cause of read failure + */ + public ReadFailedException(@Nonnull final InstanceIdentifier failedId, final Throwable cause) { + super("Failed to read " + failedId, cause); + this.failedId = checkNotNull(failedId, "failedId should not be null"); + } + + /** + * Constructs an ReadFailedException given data id. + * + * @param failedId instance identifier of the data object that could not be read + */ + public ReadFailedException(@Nonnull final InstanceIdentifier failedId) { + this(failedId, null); + } + + /** + * Returns id of the data object that could not be read. + * + * @return data object instance identifier + */ + @Nonnull + public InstanceIdentifier getFailedId() { + return failedId; + } +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/Reader.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/Reader.java new file mode 100644 index 000000000..177b52415 --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/Reader.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.read; + +import com.google.common.annotations.Beta; +import com.google.common.base.Optional; +import io.fd.honeycomb.translate.SubtreeManager; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Base reader, responsible for translation between DataObjects and any other side. + * + * @param Specific DataObject derived type, that is handled by this reader + */ +@Beta +public interface Reader> extends SubtreeManager { + + // TODO make async + + /** + * Reads data identified by id + * + * @param id unique identifier of subtree to be read. The subtree must contain managed data object type. For + * identifiers pointing below node managed by this reader, it's reader's responsibility to filter out the + * right node or to delegate the read to a child reader. + * @param ctx Read context + * + * @return List of DataObjects identified by id. If the ID points to a single node, it will be wrapped in a list + * @throws ReadFailedException if read was unsuccessful + */ + @Nonnull + Optional read(@Nonnull InstanceIdentifier id, + @Nonnull ReadContext ctx) throws ReadFailedException; + + /** + * Fill in current node's attributes + * + * @param id {@link InstanceIdentifier} pointing to current node. In case of keyed list, key must be present. + * @param builder Builder object for current node where the read attributes must be placed + * @param ctx Current read context + */ + void readCurrentAttributes(@Nonnull InstanceIdentifier id, + @Nonnull B builder, + @Nonnull ReadContext ctx) throws ReadFailedException; + + /** + * Return new instance of a builder object for current node + * + * @param id {@link InstanceIdentifier} pointing to current node. In case of keyed list, key must be present. + * @return Builder object for current node type + */ + @Nonnull + B getBuilder(InstanceIdentifier id); + + /** + * Merge read data into provided parent builder. + */ + void merge(@Nonnull final Builder parentBuilder, @Nonnull final D readValue); +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/ReaderFactory.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/ReaderFactory.java new file mode 100644 index 000000000..701a26dd5 --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/ReaderFactory.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.read; + +import com.google.common.annotations.Beta; +import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder; +import javax.annotation.Nonnull; + +/** + * Factory producing readers for {@link ModifiableReaderRegistryBuilder}. + */ +@Beta +public interface ReaderFactory extends AutoCloseable { + + /** + * Initialize 1 or more readers and add them to provided registry. + */ + void init(@Nonnull ModifiableReaderRegistryBuilder registry); + + @Override + default void close() { + // NOOP TODO allow unregister + } +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/registry/ModifiableReaderRegistryBuilder.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/registry/ModifiableReaderRegistryBuilder.java new file mode 100644 index 000000000..10dcd9280 --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/registry/ModifiableReaderRegistryBuilder.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.read.registry; + +import com.google.common.annotations.Beta; +import io.fd.honeycomb.translate.ModifiableSubtreeManagerRegistryBuilder; +import io.fd.honeycomb.translate.read.Reader; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Mutable registry that allows adding new readers. + */ +@Beta +public interface ModifiableReaderRegistryBuilder + extends ModifiableSubtreeManagerRegistryBuilder>> { + + // TODO we should be able to add structural/reflexive readers automatically in the registry builder, we just need builder class + // We would need generated class loading strategy instance and then load builder classes relying on naming + package conventions of Binding spec + /** + * Add a structural reader that performs no read operation on its own, just fills in the hierarchy. + */ + void addStructuralReader(@Nonnull InstanceIdentifier id, + @Nonnull Class> builderType); +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/registry/ReaderRegistry.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/registry/ReaderRegistry.java new file mode 100644 index 000000000..9a1c99508 --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/registry/ReaderRegistry.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.read.registry; + +import com.google.common.annotations.Beta; +import com.google.common.base.Optional; +import com.google.common.collect.Multimap; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Simple delegating reader suitable as a holder for all other root readers, providing readAll feature. + */ +@Beta +public interface ReaderRegistry { + + /** + * Performs read on all registered root readers and merges the results into a Multimap. Keys represent identifiers + * for root DataObjects from the data tree modeled by YANG. + * + * @param ctx Read context + * + * @return multimap that preserves deterministic iteration order across non-distinct key values + * @throws ReadFailedException if read was unsuccessful + */ + @Nonnull + Multimap, ? extends DataObject> readAll(@Nonnull final ReadContext ctx) + throws ReadFailedException; + + /** + * Reads data identified by id. + * + * @param id unique identifier of subtree to be read. The subtree must contain managed data object type. For + * identifiers pointing below node managed by this reader, it's reader's responsibility to filter out the + * right node or to delegate the read to a child reader. + * @param ctx Read context + * + * @return List of DataObjects identified by id. If the ID points to a single node, it will be wrapped in a list + * @throws ReadFailedException if read was unsuccessful + */ + @Nonnull + Optional read(@Nonnull InstanceIdentifier id, + @Nonnull ReadContext ctx) + throws ReadFailedException; +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/registry/ReaderRegistryBuilder.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/registry/ReaderRegistryBuilder.java new file mode 100644 index 000000000..3f775f274 --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/read/registry/ReaderRegistryBuilder.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.read.registry; + +import com.google.common.annotations.Beta; +import io.fd.honeycomb.translate.SubtreeManagerRegistryBuilder; + +/** + * Builder for reader registries. + */ +@Beta +public interface ReaderRegistryBuilder extends SubtreeManagerRegistryBuilder { +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/DataObjectUpdate.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/DataObjectUpdate.java new file mode 100644 index 000000000..e76d76fcd --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/DataObjectUpdate.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.write; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Simple wrapper for BA id + data before and after state. Does not allow both before and after to be null. + */ +public class DataObjectUpdate { + + @Nonnull + private final InstanceIdentifier id; + @Nullable + private final DataObject dataBefore; + @Nullable + private final DataObject dataAfter; + + private DataObjectUpdate(@Nonnull final InstanceIdentifier id, + @Nullable final DataObject dataBefore, + @Nullable final DataObject dataAfter) { + this.id = checkNotNull(id); + this.dataAfter = dataAfter; + this.dataBefore = dataBefore; + } + + public DataObject getDataBefore() { + return dataBefore; + } + + public DataObject getDataAfter() { + return dataAfter; + } + + public InstanceIdentifier getId() { + return id; + } + + public static DataObjectUpdate create(@Nonnull final InstanceIdentifier id, + @Nullable final DataObject dataBefore, + @Nullable final DataObject dataAfter) { + checkArgument(!(dataBefore == null && dataAfter == null), "Both before and after data are null"); + if (dataBefore != null) { + checkArgument(id.getTargetType().isAssignableFrom(dataBefore.getClass())); + } + if (dataAfter != null) { + checkArgument(id.getTargetType().isAssignableFrom(dataAfter.getClass())); + } + + return dataAfter == null + ? new DataObjectDelete(id, dataBefore) + : new DataObjectUpdate(id, dataBefore, dataAfter); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final DataObjectUpdate that = (DataObjectUpdate) o; + + return id.equals(that.id); + + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public String toString() { + return "DataObjectUpdate{" + "id=" + id + + ", dataBefore=" + dataBefore + + ", dataAfter=" + dataAfter + + '}'; + } + + public DataObjectUpdate reverse() { + return DataObjectUpdate.create(id, dataAfter, dataBefore); + } + + public static class DataObjectDelete extends DataObjectUpdate { + + private DataObjectDelete(@Nonnull final InstanceIdentifier id, + @Nullable final DataObject dataBefore) { + super(id, dataBefore, null); + } + } +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/ListWriter.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/ListWriter.java new file mode 100644 index 000000000..ea4ed9c93 --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/ListWriter.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.write; + +import com.google.common.annotations.Beta; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.Identifiable; +import org.opendaylight.yangtools.yang.binding.Identifier; + +/** + * List writer, responsible for translation between a list of DataObjects and any other side. + * Handling all update operations(create, update, delete) + * + * @param Specific DataObject derived type, that is handled by this writer + * @param Identifier/key for D + */ +@Beta +public interface ListWriter, K extends Identifier> extends Writer { +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/WriteContext.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/WriteContext.java new file mode 100644 index 000000000..17aea8d8a --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/WriteContext.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.write; + +import com.google.common.annotations.Beta; +import com.google.common.base.Optional; +import io.fd.honeycomb.translate.ModificationContext; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Context providing information about current state of DataTree to writers + */ +@Beta +public interface WriteContext extends ModificationContext { + + /** + * Read any config data object before current modification was applied + * + * @param currentId Id of an object to read + * + * @return Data before the modification was applied + */ + Optional readBefore(@Nonnull final InstanceIdentifier currentId); + + /** + * Read any config data object from current modification + * + * @param currentId Id of an object to read + * + * @return Data from the modification + */ + Optional readAfter(@Nonnull final InstanceIdentifier currentId); + +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/WriteFailedException.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/WriteFailedException.java new file mode 100644 index 000000000..8f89d3163 --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/WriteFailedException.java @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.write; + +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.honeycomb.translate.TranslationException; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Thrown when a writer or customizer is not able to write/update/delete data . + */ +public class WriteFailedException extends TranslationException { + + private final InstanceIdentifier failedId; + + /** + * Constructs an WriteFailedException given data id, exception detail message and exception cause. + * + * @param failedId instance identifier of the data object that could not be read + * @param cause the cause of read failure + * @param message the exception detail message + */ + public WriteFailedException(@Nonnull final InstanceIdentifier failedId, + @Nonnull final String message, + @Nonnull final Throwable cause) { + super(message, cause); + this.failedId = checkNotNull(failedId, "failedId should not be null"); + } + + /** + * Constructs an WriteFailedException given data id. + * + * @param failedId instance identifier of the data object that could not be written + */ + public WriteFailedException(@Nonnull final InstanceIdentifier failedId, + @Nonnull final String message) { + super(message); + this.failedId = checkNotNull(failedId, "failedId should not be null"); + } + + /** + * Constructs an WriteFailedException given data id and exception cause. + * + * @param failedId instance identifier of the data object that could not be read + * @param cause the cause of read failure + */ + public WriteFailedException(@Nonnull final InstanceIdentifier failedId, + @Nonnull final Throwable cause) { + super(cause); + this.failedId = checkNotNull(failedId, "failedId should not be null"); + } + + /** + * Returns id of the data object that could not be written. + * + * @return data object instance identifier + */ + @Nonnull + public InstanceIdentifier getFailedId() { + return failedId; + } + + + /** + * Delete specific write failed exception + */ + public static class DeleteFailedException extends WriteFailedException { + + public DeleteFailedException(@Nonnull final InstanceIdentifier failedId, @Nonnull final Throwable cause) { + super(failedId, getMsg(failedId), cause); + } + + private static String getMsg(@Nonnull final InstanceIdentifier failedId) { + return String.format("Failed to delete data at: %s", failedId); + } + + public DeleteFailedException(@Nonnull final InstanceIdentifier failedId) { + super(failedId, getMsg(failedId)); + } + } + + /** + * Create specific write failed exception + */ + public static class CreateFailedException extends WriteFailedException { + + private final DataObject data; + + public CreateFailedException(@Nonnull final InstanceIdentifier failedId, + @Nonnull final DataObject data, + @Nonnull final Throwable cause) { + super(failedId, getMsg(failedId, data), cause); + this.data = checkNotNull(data, "data"); + } + + private static String getMsg(final @Nonnull InstanceIdentifier failedId, final DataObject data) { + return String.format("Failed to create data: %s at: %s", data, failedId); + } + + public CreateFailedException(@Nonnull final InstanceIdentifier failedId, + @Nonnull final DataObject data) { + super(failedId, getMsg(failedId, data)); + this.data = checkNotNull(data, "data"); + } + + public DataObject getData() { + return data; + } + } + + /** + * Update specific write failed exception + */ + public static class UpdateFailedException extends WriteFailedException { + + private final DataObject dataBefore; + private final DataObject dataAfter; + + public UpdateFailedException(@Nonnull final InstanceIdentifier failedId, + @Nonnull final DataObject dataBefore, + @Nonnull final DataObject dataAfter, + @Nonnull final Throwable cause) { + super(failedId, getMsg(failedId, dataBefore, dataAfter), cause); + this.dataBefore = checkNotNull(dataBefore, "dataBefore"); + this.dataAfter = checkNotNull(dataAfter, "dataAfter"); + } + + private static String getMsg(final @Nonnull InstanceIdentifier failedId, final DataObject dataBefore, + final DataObject dataAfter) { + return String.format("Failed to update data from: %s to: %s, at: %s", dataBefore, dataAfter, failedId); + } + + public UpdateFailedException(@Nonnull final InstanceIdentifier failedId, + @Nonnull final DataObject dataBefore, + @Nonnull final DataObject dataAfter) { + super(failedId, getMsg(failedId, dataBefore, dataAfter)); + this.dataBefore = checkNotNull(dataBefore, "dataBefore"); + this.dataAfter = checkNotNull(dataAfter, "dataAfter"); + } + + public DataObject getDataBefore() { + return dataBefore; + } + + public DataObject getDataAfter() { + return dataAfter; + } + } +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/Writer.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/Writer.java new file mode 100644 index 000000000..e6538012b --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/Writer.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.write; + +import com.google.common.annotations.Beta; +import io.fd.honeycomb.translate.SubtreeManager; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Base writer, responsible for translation between DataObjects and any other side. Handling all update operations(create, + * update, delete) + * + * @param Specific DataObject derived type, that is handled by this writer + */ +@Beta +public interface Writer extends SubtreeManager { + + /** + * Handle update operation. U from CRUD. + * + * @param id Identifier of data being written + * @param dataBefore Old data + * @param dataAfter New, updated data + * @param ctx Write context enabling writer to get information about candidate data as well as current data + * @throws WriteFailedException if update failed + */ + void update(@Nonnull final InstanceIdentifier id, + @Nullable final DataObject dataBefore, + @Nullable final DataObject dataAfter, + @Nonnull final WriteContext ctx) throws WriteFailedException; +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/WriterFactory.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/WriterFactory.java new file mode 100644 index 000000000..70328c7f5 --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/WriterFactory.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.write; + +import com.google.common.annotations.Beta; +import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder; +import javax.annotation.Nonnull; + +/** + * Factory producing writers for {@link ModifiableWriterRegistryBuilder}. + */ +@Beta +public interface WriterFactory extends AutoCloseable { + + /** + * Initialize 1 or more writers and add them to provided registry. + */ + void init(@Nonnull ModifiableWriterRegistryBuilder registry); + + @Override + default void close() { + // NOOP TODO allow unregister + } +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/registry/ModifiableWriterRegistryBuilder.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/registry/ModifiableWriterRegistryBuilder.java new file mode 100644 index 000000000..27761e5b4 --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/registry/ModifiableWriterRegistryBuilder.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.write.registry; + +import com.google.common.annotations.Beta; +import io.fd.honeycomb.translate.ModifiableSubtreeManagerRegistryBuilder; +import io.fd.honeycomb.translate.write.Writer; +import org.opendaylight.yangtools.yang.binding.DataObject; + +/** + * Mutable registry that allows adding new writers. + */ +@Beta +public interface ModifiableWriterRegistryBuilder + extends ModifiableSubtreeManagerRegistryBuilder> { + +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/registry/WriterRegistry.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/registry/WriterRegistry.java new file mode 100644 index 000000000..2467972da --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/registry/WriterRegistry.java @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.write.registry; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.Beta; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import io.fd.honeycomb.translate.TranslationException; +import io.fd.honeycomb.translate.write.DataObjectUpdate; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.Writer; +import java.util.Set; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Special {@link Writer} capable of performing bulk updates. + */ +@Beta +public interface WriterRegistry { + + /** + * Performs bulk update. + * + * @throws BulkUpdateException in case bulk update fails + * @throws TranslationException in case some other error occurs while processing update request + */ + void update(@Nonnull DataObjectUpdates updates, + @Nonnull WriteContext ctx) throws TranslationException; + + /** + * Simple DTO containing updates for {@link WriterRegistry}. Currently only deletes and updates (create + update) + * are distinguished. + */ + @Beta + final class DataObjectUpdates { + + private final Multimap, DataObjectUpdate> updates; + private final Multimap, DataObjectUpdate.DataObjectDelete> deletes; + + /** + * Create new instance. + * + * @param updates All updates indexed by their unkeyed {@link InstanceIdentifier} + * @param deletes All deletes indexed by their unkeyed {@link InstanceIdentifier} + */ + public DataObjectUpdates(@Nonnull final Multimap, DataObjectUpdate> updates, + @Nonnull final Multimap, DataObjectUpdate.DataObjectDelete> deletes) { + this.deletes = deletes; + this.updates = updates; + } + + public Multimap, DataObjectUpdate> getUpdates() { + return updates; + } + + public Multimap, DataObjectUpdate.DataObjectDelete> getDeletes() { + return deletes; + } + + public boolean isEmpty() { + return updates.isEmpty() && deletes.isEmpty(); + } + + @Override + public String toString() { + return "DataObjectUpdates{" + "updates=" + updates + ", deletes=" + deletes + '}'; + } + + /** + * Get a {@link Set} containing all update types from both updates as well as deletes. + */ + public Set> getTypeIntersection() { + return Sets.union(deletes.keySet(), updates.keySet()); + } + + /** + * Check whether there is only a single type of data object to be updated. + * + * @return true if there is only a single type of updates (update + delete) + */ + public boolean containsOnlySingleType() { + return getTypeIntersection().size() == 1; + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + + final DataObjectUpdates that = (DataObjectUpdates) other; + + if (!updates.equals(that.updates)) { + return false; + } + return deletes.equals(that.deletes); + + } + + @Override + public int hashCode() { + int result = updates.hashCode(); + result = 31 * result + deletes.hashCode(); + return result; + } + + } + + /** + * Thrown when bulk update failed. + */ + @Beta + class BulkUpdateException extends TranslationException { + + private final Reverter reverter; + private final Set> failedIds; + + /** + * Constructs an BulkUpdateException. + * @param failedIds instance identifiers of the data objects that were not processed during bulk update. + * @param cause the cause of bulk update failure + */ + public BulkUpdateException(@Nonnull final Set> failedIds, + @Nonnull final Reverter reverter, + @Nonnull final Throwable cause) { + super("Bulk update failed at: " + failedIds, cause); + this.failedIds = failedIds; + this.reverter = checkNotNull(reverter, "reverter should not be null"); + } + + /** + * Reverts changes that were successfully applied during bulk update before failure occurred. + * + * @throws Reverter.RevertFailedException if revert fails + */ + public void revertChanges() throws Reverter.RevertFailedException { + reverter.revert(); + } + + public Set> getFailedIds() { + return failedIds; + } + } + + /** + * Abstraction over revert mechanism in case of a bulk update failure. + */ + @Beta + interface Reverter { + + /** + * Reverts changes that were successfully applied during bulk update before failure occurred. Changes are + * reverted in reverse order they were applied. + * + * @throws RevertFailedException if not all of applied changes were successfully reverted + */ + void revert() throws RevertFailedException; + + /** + * Thrown when some of the changes applied during bulk update were not reverted. + */ + @Beta + class RevertFailedException extends TranslationException { + + // TODO change to list of VppDataModifications to make debugging easier + private final Set> notRevertedChanges; + + /** + * Constructs a RevertFailedException with the list of changes that were not reverted. + * + * @param notRevertedChanges list of changes that were not reverted + * @param cause the cause of revert failure + */ + public RevertFailedException(@Nonnull final Set> notRevertedChanges, + final Throwable cause) { + super(cause); + checkNotNull(notRevertedChanges, "notRevertedChanges should not be null"); + this.notRevertedChanges = ImmutableSet.copyOf(notRevertedChanges); + } + + /** + * Returns the list of changes that were not reverted. + * + * @return list of changes that were not reverted + */ + @Nonnull + public Set> getNotRevertedChanges() { + return notRevertedChanges; + } + } + } +} \ No newline at end of file diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/registry/WriterRegistryBuilder.java b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/registry/WriterRegistryBuilder.java new file mode 100644 index 000000000..5e3481966 --- /dev/null +++ b/infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/registry/WriterRegistryBuilder.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.write.registry; + +import com.google.common.annotations.Beta; +import io.fd.honeycomb.translate.SubtreeManagerRegistryBuilder; + +/** + * Builder for writer registries. + */ +@Beta +public interface WriterRegistryBuilder extends SubtreeManagerRegistryBuilder { +} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/MappingContext.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/MappingContext.java deleted file mode 100644 index cff766e2a..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/MappingContext.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate; - -import com.google.common.base.Optional; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Mapping context is persisted storage where mapping matadata are stored. - * A snapshot is created for each transaction to provide consistent view over context data. - * After a transaction is successfully finished, objects added to this context are propagated to backing storage. - */ -public interface MappingContext extends AutoCloseable { - - /** - * Read any mapping context data - * - * @param currentId Id of an object to read - * - * @return Relevant mapping context data - */ - Optional read(@Nonnull final InstanceIdentifier currentId); - - /** - * Delete the node at specified path. - * - * @param path Node path - */ - void delete(InstanceIdentifier path); - - /** - * Merge the specified data with the currently-present data - * at specified path. - * - * @param path Node path - * @param data Data to be merged - */ - void merge(InstanceIdentifier path, T data); - - /** - * Replace the data at specified path with supplied data. - * - * @param path Node path - * @param data New node data - */ - void put(InstanceIdentifier path, T data); - - @Override - void close(); -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModifiableSubtreeManagerRegistryBuilder.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModifiableSubtreeManagerRegistryBuilder.java deleted file mode 100644 index 591a9e9bb..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModifiableSubtreeManagerRegistryBuilder.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate; - -import java.util.Collection; -import java.util.Set; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Registry builder where {@link SubtreeManager}s can be added with or without relationships between them. - * The relationships express what the order of execution should be. - */ -public interface ModifiableSubtreeManagerRegistryBuilder> { - - /** - * Add a handler responsible for writing only a single complex node. - */ - ModifiableSubtreeManagerRegistryBuilder add(@Nonnull S handler); - - /** - * Add a handler responsible for writing multiple complex nodes within a subtree its responsible for. Identifiers for - * subtree nodes handled by a single handler have to be relative from {@link DataObject} that represents subtree - * root. - */ - ModifiableSubtreeManagerRegistryBuilder subtreeAdd(@Nonnull Set> handledChildren, - @Nonnull S handler); - - /** - * Add a handler and make sure it will be executed before handler identifier by relatedType is executed. - */ - ModifiableSubtreeManagerRegistryBuilder addBefore(@Nonnull S handler, - @Nonnull InstanceIdentifier relatedType); - - ModifiableSubtreeManagerRegistryBuilder addBefore(@Nonnull S handler, - @Nonnull Collection> relatedTypes); - - ModifiableSubtreeManagerRegistryBuilder subtreeAddBefore(@Nonnull Set> handledChildren, - @Nonnull S handler, - @Nonnull InstanceIdentifier relatedType); - - ModifiableSubtreeManagerRegistryBuilder subtreeAddBefore(@Nonnull Set> handledChildren, - @Nonnull S handler, - @Nonnull Collection> relatedTypes); - - /** - * Add a handler and make sure it will be executed after handler identifier by relatedType is executed. - */ - ModifiableSubtreeManagerRegistryBuilder addAfter(@Nonnull S handler, - @Nonnull InstanceIdentifier relatedType); - - ModifiableSubtreeManagerRegistryBuilder addAfter(@Nonnull S handler, - @Nonnull Collection> relatedTypes); - - ModifiableSubtreeManagerRegistryBuilder subtreeAddAfter(@Nonnull Set> handledChildren, - @Nonnull S handler, - @Nonnull InstanceIdentifier relatedType); - - ModifiableSubtreeManagerRegistryBuilder subtreeAddAfter(@Nonnull Set> handledChildren, - @Nonnull S handler, - @Nonnull Collection> relatedTypes); -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModificationCache.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModificationCache.java deleted file mode 100644 index cb2d4fde0..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModificationCache.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate; - -import com.google.common.collect.Maps; -import java.util.HashMap; - -/** - * Simple context class that provides transient storage during one or more read/write operations - */ -public class ModificationCache implements AutoCloseable { - - protected final HashMap map; - - public ModificationCache() { - map = Maps.newHashMap(); - } - - public Object get(final Object o) { - return map.get(o); - } - - public boolean containsKey(final Object o) { - return map.containsKey(o); - } - - public Object put(final Object o, final Object o2) { - return map.put(o, o2); - } - - @Override - public void close() { - map.clear(); - } -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModificationContext.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModificationContext.java deleted file mode 100644 index 2c039aba0..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModificationContext.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate; - -import javax.annotation.Nonnull; - -/** - * Common context for both writes and reads - */ -public interface ModificationContext extends AutoCloseable { - - /** - * Get key value transient storage for customizers. Is cleared for each new transaction. - * - * @return Context for customizers - */ - @Nonnull - ModificationCache getModificationCache(); - - /** - * Get persistent storage for mapping context. This context survives a modification. - * - * @return Mapping context accessor - */ - @Nonnull - MappingContext getMappingContext(); - - @Override - void close(); -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/SubtreeManager.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/SubtreeManager.java deleted file mode 100644 index 40842763d..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/SubtreeManager.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate; - -import com.google.common.annotations.Beta; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Base identifiable subtree manager(reader, writer etc.) - * - * @param Specific DataObject derived type, that is managed by this manager - */ -@Beta -public interface SubtreeManager { - - /** - * Gets the type of node managed by this reader. - * - * @return Absolute instance identifier for managed type - */ - @Nonnull - InstanceIdentifier getManagedDataObjectType(); -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/SubtreeManagerRegistryBuilder.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/SubtreeManagerRegistryBuilder.java deleted file mode 100644 index 2434deddb..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/SubtreeManagerRegistryBuilder.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate; - -public interface SubtreeManagerRegistryBuilder { - - R build(); -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/TranslationException.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/TranslationException.java deleted file mode 100644 index ee0049977..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/TranslationException.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate; - -import com.google.common.annotations.Beta; - -/** - * Base exception for the translation layer - */ -@Beta -public class TranslationException extends Exception { - - public TranslationException(final String s) { - super(s); - } - - public TranslationException(final String s, final Throwable cause) { - super(s, cause); - } - - public TranslationException(final Throwable cause) { - super(cause); - } -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ListReader.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ListReader.java deleted file mode 100644 index 13a7a55da..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ListReader.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.read; - -import com.google.common.annotations.Beta; -import java.util.Collections; -import java.util.List; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.Identifiable; -import org.opendaylight.yangtools.yang.binding.Identifier; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * List reader, allowing read of all the elements. - * - * @param Specific DataObject derived type, that is handled by this reader - */ -@Beta -public interface ListReader - , K extends Identifier, B extends Builder> extends Reader { - - /** - * Read all elements in this list. - * - * @param id Wildcarded identifier of list managed by this reader - * @param ctx Read context - * - * @return List of all entries in this list - * @throws ReadFailedException if read was unsuccessful - */ - @Nonnull - List readList(@Nonnull final InstanceIdentifier id, @Nonnull final ReadContext ctx) - throws ReadFailedException; - - /** - * Get IDs for all entries in the list. - */ - List getAllIds(@Nonnull InstanceIdentifier id, @Nonnull ReadContext ctx) - throws ReadFailedException; - - /** - * Merge read data into provided parent builder. - */ - void merge(@Nonnull final Builder builder, @Nonnull final List readData); - - @Override - default void merge(@Nonnull final Builder parentBuilder, @Nonnull final D readValue) { - merge(parentBuilder, Collections.singletonList(readValue)); - } -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReadContext.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReadContext.java deleted file mode 100644 index e3ddd420c..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReadContext.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.read; - -import io.fd.honeycomb.v3po.translate.ModificationContext; - -/** - * Context providing information about current state of DataTree to readers - */ -public interface ReadContext extends ModificationContext { - -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReadFailedException.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReadFailedException.java deleted file mode 100644 index 51d4fcb5a..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReadFailedException.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.read; - -import static com.google.common.base.Preconditions.checkNotNull; - -import io.fd.honeycomb.v3po.translate.TranslationException; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Thrown when a reader or customizer is not able to read data for the given id. - */ -public class ReadFailedException extends TranslationException { - - private final InstanceIdentifier failedId; - - /** - * Constructs an ReadFailedException given data id and exception cause. - * - * @param failedId instance identifier of the data object that could not be read - * @param cause the cause of read failure - */ - public ReadFailedException(@Nonnull final InstanceIdentifier failedId, final Throwable cause) { - super("Failed to read " + failedId, cause); - this.failedId = checkNotNull(failedId, "failedId should not be null"); - } - - /** - * Constructs an ReadFailedException given data id. - * - * @param failedId instance identifier of the data object that could not be read - */ - public ReadFailedException(@Nonnull final InstanceIdentifier failedId) { - this(failedId, null); - } - - /** - * Returns id of the data object that could not be read. - * - * @return data object instance identifier - */ - @Nonnull - public InstanceIdentifier getFailedId() { - return failedId; - } -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/Reader.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/Reader.java deleted file mode 100644 index d0bf0dea1..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/Reader.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.read; - -import com.google.common.annotations.Beta; -import com.google.common.base.Optional; -import io.fd.honeycomb.v3po.translate.SubtreeManager; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Base reader, responsible for translation between DataObjects and any other side. - * - * @param Specific DataObject derived type, that is handled by this reader - */ -@Beta -public interface Reader> extends SubtreeManager { - - // TODO make async - - /** - * Reads data identified by id - * - * @param id unique identifier of subtree to be read. The subtree must contain managed data object type. For - * identifiers pointing below node managed by this reader, it's reader's responsibility to filter out the - * right node or to delegate the read to a child reader. - * @param ctx Read context - * - * @return List of DataObjects identified by id. If the ID points to a single node, it will be wrapped in a list - * @throws ReadFailedException if read was unsuccessful - */ - @Nonnull - Optional read(@Nonnull InstanceIdentifier id, - @Nonnull ReadContext ctx) throws ReadFailedException; - - /** - * Fill in current node's attributes - * - * @param id {@link InstanceIdentifier} pointing to current node. In case of keyed list, key must be present. - * @param builder Builder object for current node where the read attributes must be placed - * @param ctx Current read context - */ - void readCurrentAttributes(@Nonnull InstanceIdentifier id, - @Nonnull B builder, - @Nonnull ReadContext ctx) throws ReadFailedException; - - /** - * Return new instance of a builder object for current node - * - * @param id {@link InstanceIdentifier} pointing to current node. In case of keyed list, key must be present. - * @return Builder object for current node type - */ - @Nonnull - B getBuilder(InstanceIdentifier id); - - /** - * Merge read data into provided parent builder. - */ - void merge(@Nonnull final Builder parentBuilder, @Nonnull final D readValue); -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReaderFactory.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReaderFactory.java deleted file mode 100644 index 61472f8d2..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReaderFactory.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.read; - -import com.google.common.annotations.Beta; -import io.fd.honeycomb.v3po.translate.read.registry.ModifiableReaderRegistryBuilder; -import javax.annotation.Nonnull; - -/** - * Factory producing readers for {@link ModifiableReaderRegistryBuilder}. - */ -@Beta -public interface ReaderFactory extends AutoCloseable { - - /** - * Initialize 1 or more readers and add them to provided registry. - */ - void init(@Nonnull ModifiableReaderRegistryBuilder registry); - - @Override - default void close() { - // NOOP TODO allow unregister - } -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ModifiableReaderRegistryBuilder.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ModifiableReaderRegistryBuilder.java deleted file mode 100644 index c2eba4f72..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ModifiableReaderRegistryBuilder.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.read.registry; - -import com.google.common.annotations.Beta; -import io.fd.honeycomb.v3po.translate.ModifiableSubtreeManagerRegistryBuilder; -import io.fd.honeycomb.v3po.translate.read.Reader; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Mutable registry that allows adding new readers. - */ -@Beta -public interface ModifiableReaderRegistryBuilder - extends ModifiableSubtreeManagerRegistryBuilder>> { - - // TODO we should be able to add structural/reflexive readers automatically in the registry builder, we just need builder class - // We would need generated class loading strategy instance and then load builder classes relying on naming + package conventions of Binding spec - /** - * Add a structural reader that performs no read operation on its own, just fills in the hierarchy. - */ - void addStructuralReader(@Nonnull InstanceIdentifier id, - @Nonnull Class> builderType); -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ReaderRegistry.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ReaderRegistry.java deleted file mode 100644 index 308ec238d..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ReaderRegistry.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.read.registry; - -import com.google.common.annotations.Beta; -import com.google.common.base.Optional; -import com.google.common.collect.Multimap; -import io.fd.honeycomb.v3po.translate.read.ReadContext; -import io.fd.honeycomb.v3po.translate.read.ReadFailedException; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Simple delegating reader suitable as a holder for all other root readers, providing readAll feature. - */ -@Beta -public interface ReaderRegistry { - - /** - * Performs read on all registered root readers and merges the results into a Multimap. Keys represent identifiers - * for root DataObjects from the data tree modeled by YANG. - * - * @param ctx Read context - * - * @return multimap that preserves deterministic iteration order across non-distinct key values - * @throws ReadFailedException if read was unsuccessful - */ - @Nonnull - Multimap, ? extends DataObject> readAll(@Nonnull final ReadContext ctx) - throws ReadFailedException; - - /** - * Reads data identified by id. - * - * @param id unique identifier of subtree to be read. The subtree must contain managed data object type. For - * identifiers pointing below node managed by this reader, it's reader's responsibility to filter out the - * right node or to delegate the read to a child reader. - * @param ctx Read context - * - * @return List of DataObjects identified by id. If the ID points to a single node, it will be wrapped in a list - * @throws ReadFailedException if read was unsuccessful - */ - @Nonnull - Optional read(@Nonnull InstanceIdentifier id, - @Nonnull ReadContext ctx) - throws ReadFailedException; -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ReaderRegistryBuilder.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ReaderRegistryBuilder.java deleted file mode 100644 index ff95d6248..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ReaderRegistryBuilder.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.read.registry; - -import com.google.common.annotations.Beta; -import io.fd.honeycomb.v3po.translate.SubtreeManagerRegistryBuilder; - -/** - * Builder for reader registries. - */ -@Beta -public interface ReaderRegistryBuilder extends SubtreeManagerRegistryBuilder { -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/DataObjectUpdate.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/DataObjectUpdate.java deleted file mode 100644 index 0d891ecba..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/DataObjectUpdate.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.write; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Simple wrapper for BA id + data before and after state. Does not allow both before and after to be null. - */ -public class DataObjectUpdate { - - @Nonnull - private final InstanceIdentifier id; - @Nullable - private final DataObject dataBefore; - @Nullable - private final DataObject dataAfter; - - private DataObjectUpdate(@Nonnull final InstanceIdentifier id, - @Nullable final DataObject dataBefore, - @Nullable final DataObject dataAfter) { - this.id = checkNotNull(id); - this.dataAfter = dataAfter; - this.dataBefore = dataBefore; - } - - public DataObject getDataBefore() { - return dataBefore; - } - - public DataObject getDataAfter() { - return dataAfter; - } - - public InstanceIdentifier getId() { - return id; - } - - public static DataObjectUpdate create(@Nonnull final InstanceIdentifier id, - @Nullable final DataObject dataBefore, - @Nullable final DataObject dataAfter) { - checkArgument(!(dataBefore == null && dataAfter == null), "Both before and after data are null"); - if (dataBefore != null) { - checkArgument(id.getTargetType().isAssignableFrom(dataBefore.getClass())); - } - if (dataAfter != null) { - checkArgument(id.getTargetType().isAssignableFrom(dataAfter.getClass())); - } - - return dataAfter == null - ? new DataObjectDelete(id, dataBefore) - : new DataObjectUpdate(id, dataBefore, dataAfter); - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - final DataObjectUpdate that = (DataObjectUpdate) o; - - return id.equals(that.id); - - } - - @Override - public int hashCode() { - return id.hashCode(); - } - - @Override - public String toString() { - return "DataObjectUpdate{" + "id=" + id - + ", dataBefore=" + dataBefore - + ", dataAfter=" + dataAfter - + '}'; - } - - public DataObjectUpdate reverse() { - return DataObjectUpdate.create(id, dataAfter, dataBefore); - } - - public static class DataObjectDelete extends DataObjectUpdate { - - private DataObjectDelete(@Nonnull final InstanceIdentifier id, - @Nullable final DataObject dataBefore) { - super(id, dataBefore, null); - } - } -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ListWriter.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ListWriter.java deleted file mode 100644 index f29289d2d..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ListWriter.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.write; - -import com.google.common.annotations.Beta; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.Identifiable; -import org.opendaylight.yangtools.yang.binding.Identifier; - -/** - * List writer, responsible for translation between a list of DataObjects and any other side. - * Handling all update operations(create, update, delete) - * - * @param Specific DataObject derived type, that is handled by this writer - * @param Identifier/key for D - */ -@Beta -public interface ListWriter, K extends Identifier> extends Writer { -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriteContext.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriteContext.java deleted file mode 100644 index 3433b3f34..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriteContext.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.write; - -import com.google.common.annotations.Beta; -import com.google.common.base.Optional; -import io.fd.honeycomb.v3po.translate.ModificationContext; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Context providing information about current state of DataTree to writers - */ -@Beta -public interface WriteContext extends ModificationContext { - - /** - * Read any config data object before current modification was applied - * - * @param currentId Id of an object to read - * - * @return Data before the modification was applied - */ - Optional readBefore(@Nonnull final InstanceIdentifier currentId); - - /** - * Read any config data object from current modification - * - * @param currentId Id of an object to read - * - * @return Data from the modification - */ - Optional readAfter(@Nonnull final InstanceIdentifier currentId); - -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriteFailedException.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriteFailedException.java deleted file mode 100644 index 10a664fcf..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriteFailedException.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.write; - -import static com.google.common.base.Preconditions.checkNotNull; - -import io.fd.honeycomb.v3po.translate.TranslationException; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Thrown when a writer or customizer is not able to write/update/delete data . - */ -public class WriteFailedException extends TranslationException { - - private final InstanceIdentifier failedId; - - /** - * Constructs an WriteFailedException given data id, exception detail message and exception cause. - * - * @param failedId instance identifier of the data object that could not be read - * @param cause the cause of read failure - * @param message the exception detail message - */ - public WriteFailedException(@Nonnull final InstanceIdentifier failedId, - @Nonnull final String message, - @Nonnull final Throwable cause) { - super(message, cause); - this.failedId = checkNotNull(failedId, "failedId should not be null"); - } - - /** - * Constructs an WriteFailedException given data id. - * - * @param failedId instance identifier of the data object that could not be written - */ - public WriteFailedException(@Nonnull final InstanceIdentifier failedId, - @Nonnull final String message) { - super(message); - this.failedId = checkNotNull(failedId, "failedId should not be null"); - } - - /** - * Constructs an WriteFailedException given data id and exception cause. - * - * @param failedId instance identifier of the data object that could not be read - * @param cause the cause of read failure - */ - public WriteFailedException(@Nonnull final InstanceIdentifier failedId, - @Nonnull final Throwable cause) { - super(cause); - this.failedId = checkNotNull(failedId, "failedId should not be null"); - } - - /** - * Returns id of the data object that could not be written. - * - * @return data object instance identifier - */ - @Nonnull - public InstanceIdentifier getFailedId() { - return failedId; - } - - - /** - * Delete specific write failed exception - */ - public static class DeleteFailedException extends WriteFailedException { - - public DeleteFailedException(@Nonnull final InstanceIdentifier failedId, @Nonnull final Throwable cause) { - super(failedId, getMsg(failedId), cause); - } - - private static String getMsg(@Nonnull final InstanceIdentifier failedId) { - return String.format("Failed to delete data at: %s", failedId); - } - - public DeleteFailedException(@Nonnull final InstanceIdentifier failedId) { - super(failedId, getMsg(failedId)); - } - } - - /** - * Create specific write failed exception - */ - public static class CreateFailedException extends WriteFailedException { - - private final DataObject data; - - public CreateFailedException(@Nonnull final InstanceIdentifier failedId, - @Nonnull final DataObject data, - @Nonnull final Throwable cause) { - super(failedId, getMsg(failedId, data), cause); - this.data = checkNotNull(data, "data"); - } - - private static String getMsg(final @Nonnull InstanceIdentifier failedId, final DataObject data) { - return String.format("Failed to create data: %s at: %s", data, failedId); - } - - public CreateFailedException(@Nonnull final InstanceIdentifier failedId, - @Nonnull final DataObject data) { - super(failedId, getMsg(failedId, data)); - this.data = checkNotNull(data, "data"); - } - - public DataObject getData() { - return data; - } - } - - /** - * Update specific write failed exception - */ - public static class UpdateFailedException extends WriteFailedException { - - private final DataObject dataBefore; - private final DataObject dataAfter; - - public UpdateFailedException(@Nonnull final InstanceIdentifier failedId, - @Nonnull final DataObject dataBefore, - @Nonnull final DataObject dataAfter, - @Nonnull final Throwable cause) { - super(failedId, getMsg(failedId, dataBefore, dataAfter), cause); - this.dataBefore = checkNotNull(dataBefore, "dataBefore"); - this.dataAfter = checkNotNull(dataAfter, "dataAfter"); - } - - private static String getMsg(final @Nonnull InstanceIdentifier failedId, final DataObject dataBefore, - final DataObject dataAfter) { - return String.format("Failed to update data from: %s to: %s, at: %s", dataBefore, dataAfter, failedId); - } - - public UpdateFailedException(@Nonnull final InstanceIdentifier failedId, - @Nonnull final DataObject dataBefore, - @Nonnull final DataObject dataAfter) { - super(failedId, getMsg(failedId, dataBefore, dataAfter)); - this.dataBefore = checkNotNull(dataBefore, "dataBefore"); - this.dataAfter = checkNotNull(dataAfter, "dataAfter"); - } - - public DataObject getDataBefore() { - return dataBefore; - } - - public DataObject getDataAfter() { - return dataAfter; - } - } -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/Writer.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/Writer.java deleted file mode 100644 index 77abe341c..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/Writer.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.write; - -import com.google.common.annotations.Beta; -import io.fd.honeycomb.v3po.translate.SubtreeManager; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Base writer, responsible for translation between DataObjects and any other side. Handling all update operations(create, - * update, delete) - * - * @param Specific DataObject derived type, that is handled by this writer - */ -@Beta -public interface Writer extends SubtreeManager { - - /** - * Handle update operation. U from CRUD. - * - * @param id Identifier of data being written - * @param dataBefore Old data - * @param dataAfter New, updated data - * @param ctx Write context enabling writer to get information about candidate data as well as current data - * @throws WriteFailedException if update failed - */ - void update(@Nonnull final InstanceIdentifier id, - @Nullable final DataObject dataBefore, - @Nullable final DataObject dataAfter, - @Nonnull final WriteContext ctx) throws WriteFailedException; -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterFactory.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterFactory.java deleted file mode 100644 index ffc76a0e4..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterFactory.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.write; - -import com.google.common.annotations.Beta; -import io.fd.honeycomb.v3po.translate.write.registry.ModifiableWriterRegistryBuilder; -import javax.annotation.Nonnull; - -/** - * Factory producing writers for {@link ModifiableWriterRegistryBuilder}. - */ -@Beta -public interface WriterFactory extends AutoCloseable { - - /** - * Initialize 1 or more writers and add them to provided registry. - */ - void init(@Nonnull ModifiableWriterRegistryBuilder registry); - - @Override - default void close() { - // NOOP TODO allow unregister - } -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/ModifiableWriterRegistryBuilder.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/ModifiableWriterRegistryBuilder.java deleted file mode 100644 index 8670a5059..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/ModifiableWriterRegistryBuilder.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.write.registry; - -import com.google.common.annotations.Beta; -import io.fd.honeycomb.v3po.translate.ModifiableSubtreeManagerRegistryBuilder; -import io.fd.honeycomb.v3po.translate.write.Writer; -import org.opendaylight.yangtools.yang.binding.DataObject; - -/** - * Mutable registry that allows adding new writers. - */ -@Beta -public interface ModifiableWriterRegistryBuilder - extends ModifiableSubtreeManagerRegistryBuilder> { - -} diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/WriterRegistry.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/WriterRegistry.java deleted file mode 100644 index 439a85410..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/WriterRegistry.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.write.registry; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.annotations.Beta; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Multimap; -import com.google.common.collect.Sets; -import io.fd.honeycomb.v3po.translate.TranslationException; -import io.fd.honeycomb.v3po.translate.write.DataObjectUpdate; -import io.fd.honeycomb.v3po.translate.write.WriteContext; -import io.fd.honeycomb.v3po.translate.write.Writer; -import java.util.Set; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Special {@link Writer} capable of performing bulk updates. - */ -@Beta -public interface WriterRegistry { - - /** - * Performs bulk update. - * - * @throws BulkUpdateException in case bulk update fails - * @throws TranslationException in case some other error occurs while processing update request - */ - void update(@Nonnull DataObjectUpdates updates, - @Nonnull WriteContext ctx) throws TranslationException; - - /** - * Simple DTO containing updates for {@link WriterRegistry}. Currently only deletes and updates (create + update) - * are distinguished. - */ - @Beta - final class DataObjectUpdates { - - private final Multimap, DataObjectUpdate> updates; - private final Multimap, DataObjectUpdate.DataObjectDelete> deletes; - - /** - * Create new instance. - * - * @param updates All updates indexed by their unkeyed {@link InstanceIdentifier} - * @param deletes All deletes indexed by their unkeyed {@link InstanceIdentifier} - */ - public DataObjectUpdates(@Nonnull final Multimap, DataObjectUpdate> updates, - @Nonnull final Multimap, DataObjectUpdate.DataObjectDelete> deletes) { - this.deletes = deletes; - this.updates = updates; - } - - public Multimap, DataObjectUpdate> getUpdates() { - return updates; - } - - public Multimap, DataObjectUpdate.DataObjectDelete> getDeletes() { - return deletes; - } - - public boolean isEmpty() { - return updates.isEmpty() && deletes.isEmpty(); - } - - @Override - public String toString() { - return "DataObjectUpdates{" + "updates=" + updates + ", deletes=" + deletes + '}'; - } - - /** - * Get a {@link Set} containing all update types from both updates as well as deletes. - */ - public Set> getTypeIntersection() { - return Sets.union(deletes.keySet(), updates.keySet()); - } - - /** - * Check whether there is only a single type of data object to be updated. - * - * @return true if there is only a single type of updates (update + delete) - */ - public boolean containsOnlySingleType() { - return getTypeIntersection().size() == 1; - } - - @Override - public boolean equals(final Object other) { - if (this == other) { - return true; - } - if (other == null || getClass() != other.getClass()) { - return false; - } - - final DataObjectUpdates that = (DataObjectUpdates) other; - - if (!updates.equals(that.updates)) { - return false; - } - return deletes.equals(that.deletes); - - } - - @Override - public int hashCode() { - int result = updates.hashCode(); - result = 31 * result + deletes.hashCode(); - return result; - } - - } - - /** - * Thrown when bulk update failed. - */ - @Beta - class BulkUpdateException extends TranslationException { - - private final Reverter reverter; - private final Set> failedIds; - - /** - * Constructs an BulkUpdateException. - * @param failedIds instance identifiers of the data objects that were not processed during bulk update. - * @param cause the cause of bulk update failure - */ - public BulkUpdateException(@Nonnull final Set> failedIds, - @Nonnull final Reverter reverter, - @Nonnull final Throwable cause) { - super("Bulk update failed at: " + failedIds, cause); - this.failedIds = failedIds; - this.reverter = checkNotNull(reverter, "reverter should not be null"); - } - - /** - * Reverts changes that were successfully applied during bulk update before failure occurred. - * - * @throws Reverter.RevertFailedException if revert fails - */ - public void revertChanges() throws Reverter.RevertFailedException { - reverter.revert(); - } - - public Set> getFailedIds() { - return failedIds; - } - } - - /** - * Abstraction over revert mechanism in case of a bulk update failure. - */ - @Beta - interface Reverter { - - /** - * Reverts changes that were successfully applied during bulk update before failure occurred. Changes are - * reverted in reverse order they were applied. - * - * @throws RevertFailedException if not all of applied changes were successfully reverted - */ - void revert() throws RevertFailedException; - - /** - * Thrown when some of the changes applied during bulk update were not reverted. - */ - @Beta - class RevertFailedException extends TranslationException { - - // TODO change to list of VppDataModifications to make debugging easier - private final Set> notRevertedChanges; - - /** - * Constructs a RevertFailedException with the list of changes that were not reverted. - * - * @param notRevertedChanges list of changes that were not reverted - * @param cause the cause of revert failure - */ - public RevertFailedException(@Nonnull final Set> notRevertedChanges, - final Throwable cause) { - super(cause); - checkNotNull(notRevertedChanges, "notRevertedChanges should not be null"); - this.notRevertedChanges = ImmutableSet.copyOf(notRevertedChanges); - } - - /** - * Returns the list of changes that were not reverted. - * - * @return list of changes that were not reverted - */ - @Nonnull - public Set> getNotRevertedChanges() { - return notRevertedChanges; - } - } - } -} \ No newline at end of file diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/WriterRegistryBuilder.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/WriterRegistryBuilder.java deleted file mode 100644 index 3f0289ece..000000000 --- a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/WriterRegistryBuilder.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.write.registry; - -import com.google.common.annotations.Beta; -import io.fd.honeycomb.v3po.translate.SubtreeManagerRegistryBuilder; - -/** - * Builder for writer registries. - */ -@Beta -public interface WriterRegistryBuilder extends SubtreeManagerRegistryBuilder { -} diff --git a/infra/translate-api/src/main/yang/translate-api.yang b/infra/translate-api/src/main/yang/translate-api.yang index 796632dd2..133662289 100644 --- a/infra/translate-api/src/main/yang/translate-api.yang +++ b/infra/translate-api/src/main/yang/translate-api.yang @@ -16,37 +16,37 @@ module translate-api { identity honeycomb-reader-factory { base "config:service-type"; - config:java-class io.fd.honeycomb.v3po.translate.read.ReaderFactory; + config:java-class io.fd.honeycomb.translate.read.ReaderFactory; } identity honeycomb-reader-registry { base "config:service-type"; - config:java-class io.fd.honeycomb.v3po.translate.read.registry.ModifiableReaderRegistryBuilder; + config:java-class io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder; } identity honeycomb-reader-registry-builder { base "config:service-type"; - config:java-class io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistryBuilder; + config:java-class io.fd.honeycomb.translate.read.registry.ReaderRegistryBuilder; } identity honeycomb-writer-factory { base "config:service-type"; - config:java-class io.fd.honeycomb.v3po.translate.write.WriterFactory; + config:java-class io.fd.honeycomb.translate.write.WriterFactory; } identity honeycomb-writer-registry { base "config:service-type"; - config:java-class io.fd.honeycomb.v3po.translate.write.registry.ModifiableWriterRegistryBuilder; + config:java-class io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder; } identity honeycomb-writer-registry-builder { base "config:service-type"; - config:java-class io.fd.honeycomb.v3po.translate.write.registry.WriterRegistryBuilder; + config:java-class io.fd.honeycomb.translate.write.registry.WriterRegistryBuilder; } identity honeycomb-mapping-context { base "config:service-type"; - config:java-class io.fd.honeycomb.v3po.translate.MappingContext; + config:java-class io.fd.honeycomb.translate.MappingContext; } } \ No newline at end of file diff --git a/infra/translate-api/src/test/java/io/fd/honeycomb/translate/ReadFailedExceptionTest.java b/infra/translate-api/src/test/java/io/fd/honeycomb/translate/ReadFailedExceptionTest.java new file mode 100644 index 000000000..eb3d746c9 --- /dev/null +++ b/infra/translate-api/src/test/java/io/fd/honeycomb/translate/ReadFailedExceptionTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate; + +import io.fd.honeycomb.translate.read.ReadFailedException; +import org.junit.Assert; +import org.junit.Test; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class ReadFailedExceptionTest { + + @Test + public void testInstantiation() { + final InstanceIdentifier id = InstanceIdentifier.create(DataObject.class); + ReadFailedException e = new ReadFailedException(id); + Assert.assertEquals(id, e.getFailedId()); + Assert.assertNull(e.getCause()); + Assert.assertTrue(e.getMessage().contains(id.toString())); + } + + @Test + public void testInstantiationWithCause() { + final InstanceIdentifier id = InstanceIdentifier.create(DataObject.class); + final RuntimeException cause = new RuntimeException(); + ReadFailedException e = new ReadFailedException(id, cause); + Assert.assertEquals(id, e.getFailedId()); + Assert.assertEquals(cause, e.getCause()); + Assert.assertTrue(e.getMessage().contains(id.toString())); + } + + @Test(expected = NullPointerException.class) + public void testInstantiationFailed() { + new ReadFailedException(null); + } +} \ No newline at end of file diff --git a/infra/translate-api/src/test/java/io/fd/honeycomb/v3po/translate/ReadFailedExceptionTest.java b/infra/translate-api/src/test/java/io/fd/honeycomb/v3po/translate/ReadFailedExceptionTest.java deleted file mode 100644 index 71c0afbd3..000000000 --- a/infra/translate-api/src/test/java/io/fd/honeycomb/v3po/translate/ReadFailedExceptionTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate; - -import io.fd.honeycomb.v3po.translate.read.ReadFailedException; -import org.junit.Assert; -import org.junit.Test; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -public class ReadFailedExceptionTest { - - @Test - public void testInstantiation() { - final InstanceIdentifier id = InstanceIdentifier.create(DataObject.class); - ReadFailedException e = new ReadFailedException(id); - Assert.assertEquals(id, e.getFailedId()); - Assert.assertNull(e.getCause()); - Assert.assertTrue(e.getMessage().contains(id.toString())); - } - - @Test - public void testInstantiationWithCause() { - final InstanceIdentifier id = InstanceIdentifier.create(DataObject.class); - final RuntimeException cause = new RuntimeException(); - ReadFailedException e = new ReadFailedException(id, cause); - Assert.assertEquals(id, e.getFailedId()); - Assert.assertEquals(cause, e.getCause()); - Assert.assertTrue(e.getMessage().contains(id.toString())); - } - - @Test(expected = NullPointerException.class) - public void testInstantiationFailed() { - new ReadFailedException(null); - } -} \ No newline at end of file diff --git a/infra/translate-impl/pom.xml b/infra/translate-impl/pom.xml index 264f10956..f8906e7a3 100644 --- a/infra/translate-impl/pom.xml +++ b/infra/translate-impl/pom.xml @@ -71,7 +71,7 @@ ${project.groupId}.${project.artifactId} - io.fd.honeycomb.v3po.translate.impl.* + io.fd.honeycomb.translate.impl.* diff --git a/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/read/GenericListReader.java b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/read/GenericListReader.java new file mode 100644 index 000000000..c6e7f3cb5 --- /dev/null +++ b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/read/GenericListReader.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.impl.read; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.annotations.Beta; +import com.google.common.base.Optional; +import io.fd.honeycomb.translate.read.ListReader; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.ListReaderCustomizer; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.honeycomb.translate.util.read.AbstractGenericReader; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nonnull; +import javax.annotation.concurrent.ThreadSafe; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.Identifiable; +import org.opendaylight.yangtools.yang.binding.Identifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Composite implementation of {@link ListReader} able to place the read result into parent builder object intended + * for list node type. + *

+ * This reader checks if the IDs are wildcarded in which case it performs read of all list entries. In case the ID has a + * key, it reads only the specified value. + */ +@Beta +@ThreadSafe +public final class GenericListReader, K extends Identifier, B extends Builder> + extends AbstractGenericReader implements ListReader { + + private static final Logger LOG = LoggerFactory.getLogger(GenericListReader.class); + + private final ListReaderCustomizer customizer; + + /** + * Create new {@link GenericListReader} + * + * @param managedDataObjectType Class object for managed data type. Must come from a list node type. + * @param customizer Customizer instance to customize this generic reader + */ + public GenericListReader(@Nonnull final InstanceIdentifier managedDataObjectType, + @Nonnull final ListReaderCustomizer customizer) { + super(managedDataObjectType); + this.customizer = customizer; + } + + @Override + @Nonnull + public List readList(@Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext ctx) throws ReadFailedException { + LOG.trace("{}: Reading all list entries", this); + final List allIds = getAllIds(id, ctx); + LOG.debug("{}: Reading list entries for: {}", this, allIds); + + final ArrayList allEntries = new ArrayList<>(allIds.size()); + for (K key : allIds) { + final InstanceIdentifier.IdentifiableItem currentBdItem = RWUtils.getCurrentIdItem(id, key); + final InstanceIdentifier keyedId = RWUtils.replaceLastInId(id, currentBdItem); + final Optional read = readCurrent(keyedId, ctx); + if (read.isPresent()) { + final DataObject singleItem = read.get(); + checkArgument(getManagedDataObjectType().getTargetType().isAssignableFrom(singleItem.getClass())); + allEntries.add(getManagedDataObjectType().getTargetType().cast(singleItem)); + } + } + return allEntries; + } + + @Override + public List getAllIds(@Nonnull final InstanceIdentifier id, @Nonnull final ReadContext ctx) + throws ReadFailedException { + LOG.trace("{}: Getting all list ids", this); + final List allIds = customizer.getAllIds(id, ctx); + LOG.debug("{}: All list ids: {}", this, allIds); + return allIds; + } + + @Override + public void merge(@Nonnull final Builder builder, @Nonnull final List readData) { + customizer.merge(builder, readData); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final B builder, + @Nonnull final ReadContext ctx) + throws ReadFailedException { + customizer.readCurrentAttributes(id, builder, ctx); + } + + @Override + public B getBuilder(@Nonnull final InstanceIdentifier id) { + return customizer.getBuilder(id); + } + +} diff --git a/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/read/GenericReader.java b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/read/GenericReader.java new file mode 100644 index 000000000..1c638317a --- /dev/null +++ b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/read/GenericReader.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.impl.read; + +import com.google.common.annotations.Beta; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.Reader; +import io.fd.honeycomb.translate.spi.read.ReaderCustomizer; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.util.read.AbstractGenericReader; +import javax.annotation.Nonnull; +import javax.annotation.concurrent.ThreadSafe; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Composite implementation of {@link Reader}. + */ +@Beta +@ThreadSafe +public final class GenericReader> extends AbstractGenericReader + implements Reader { + + private final ReaderCustomizer customizer; + + /** + * Create a new {@link GenericReader}. + * + * @param id Instance identifier for managed data type + * @param customizer Customizer instance to customize this generic reader + */ + public GenericReader(@Nonnull final InstanceIdentifier id, + @Nonnull final ReaderCustomizer customizer) { + super(id); + this.customizer = customizer; + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier id, + @Nonnull final B builder, + @Nonnull final ReadContext ctx) throws ReadFailedException { + customizer.readCurrentAttributes(id, builder, ctx); + } + + @Override + public B getBuilder(@Nonnull final InstanceIdentifier id) { + return customizer.getBuilder(id); + } + + @Override + public void merge(@Nonnull final Builder parentBuilder, @Nonnull final C readValue) { + customizer.merge(parentBuilder, readValue); + } + +} 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 new file mode 100644 index 000000000..f61812931 --- /dev/null +++ b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/GenericListWriter.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.impl.write; + +import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer; +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.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; +import org.opendaylight.yangtools.yang.binding.Identifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Generic list node writer with customizable behavior thanks to injected customizer. + */ +public final class GenericListWriter, K extends Identifier> extends + AbstractGenericWriter implements ListWriter { + + private final WriterCustomizer customizer; + + public GenericListWriter(@Nonnull final InstanceIdentifier type, + @Nonnull final ListWriterCustomizer customizer) { + super(type); + this.customizer = customizer; + } + + @Override + protected void writeCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final D data, + @Nonnull final WriteContext ctx) throws WriteFailedException { + customizer.writeCurrentAttributes(id, data, ctx); + } + + @Override + protected void deleteCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final D dataBefore, + @Nonnull final WriteContext ctx) throws WriteFailedException { + customizer.deleteCurrentAttributes(id, dataBefore, ctx); + } + + @Override + protected void updateCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final D dataBefore, + @Nonnull final D dataAfter, @Nonnull final WriteContext ctx) + throws WriteFailedException { + customizer.updateCurrentAttributes(id, dataBefore, dataAfter, ctx); + } + + @Override + protected void writeCurrent(final InstanceIdentifier id, final D data, final WriteContext ctx) + throws WriteFailedException { + // Make sure the key is present + if (isWildcarded(id)) { + super.writeCurrent(getSpecificId(id, data), data, ctx); + } else { + super.writeCurrent(id, data, ctx); + } + } + + @Override + protected void updateCurrent(final InstanceIdentifier id, final D dataBefore, final D dataAfter, + final WriteContext ctx) throws WriteFailedException { + // Make sure the key is present + if (isWildcarded(id)) { + super.updateCurrent(getSpecificId(id, dataBefore), dataBefore, dataAfter, ctx); + } else { + super.updateCurrent(id, dataBefore, dataAfter, ctx); + } + } + + @Override + protected void deleteCurrent(final InstanceIdentifier id, final D dataBefore, final WriteContext ctx) + throws WriteFailedException { + // Make sure the key is present + if (isWildcarded(id)) { + super.deleteCurrent(getSpecificId(id, dataBefore), dataBefore, ctx); + } else { + super.deleteCurrent(id, dataBefore, ctx); + } + } + + private boolean isWildcarded(final InstanceIdentifier id) { + return id.firstIdentifierOf(getManagedDataObjectType().getTargetType()).isWildcarded(); + } + + private InstanceIdentifier getSpecificId(final InstanceIdentifier currentId, final D current) { + return RWUtils.replaceLastInId(currentId, + new InstanceIdentifier.IdentifiableItem<>(currentId.getTargetType(), current.getKey())); + } +} 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 new file mode 100644 index 000000000..486da9b4a --- /dev/null +++ b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/GenericWriter.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +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.WriteFailedException; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Generic writer with customizable behavior thanks to injected customizer. + */ +public final class GenericWriter extends AbstractGenericWriter { + + private final WriterCustomizer customizer; + + public GenericWriter(@Nonnull final InstanceIdentifier type, + @Nonnull final WriterCustomizer customizer) { + super(type); + this.customizer = customizer; + } + + @Override + protected void writeCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final D data, + @Nonnull final WriteContext ctx) throws WriteFailedException { + // TODO wrap all customizer invocations in try catch, and wrap runtime exceptions in ReadFailed + // TODO same for readers + customizer.writeCurrentAttributes(id, data, ctx); + } + + @Override + protected void deleteCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final D dataBefore, + @Nonnull final WriteContext ctx) throws WriteFailedException { + customizer.deleteCurrentAttributes(id, dataBefore, ctx); + } + + @Override + protected void updateCurrentAttributes(@Nonnull final InstanceIdentifier id, + @Nonnull final D dataBefore, + @Nonnull final D dataAfter, + @Nonnull final WriteContext ctx) throws WriteFailedException { + customizer.updateCurrentAttributes(id, dataBefore, dataAfter, ctx); + } +} diff --git a/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/GenericListReader.java b/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/GenericListReader.java deleted file mode 100644 index defb2e51b..000000000 --- a/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/GenericListReader.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.impl.read; - -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.common.annotations.Beta; -import com.google.common.base.Optional; -import io.fd.honeycomb.v3po.translate.read.ListReader; -import io.fd.honeycomb.v3po.translate.read.ReadContext; -import io.fd.honeycomb.v3po.translate.read.ReadFailedException; -import io.fd.honeycomb.v3po.translate.spi.read.ListReaderCustomizer; -import io.fd.honeycomb.v3po.translate.util.RWUtils; -import io.fd.honeycomb.v3po.translate.util.read.AbstractGenericReader; -import java.util.ArrayList; -import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.concurrent.ThreadSafe; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.Identifiable; -import org.opendaylight.yangtools.yang.binding.Identifier; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Composite implementation of {@link ListReader} able to place the read result into parent builder object intended - * for list node type. - *

- * This reader checks if the IDs are wildcarded in which case it performs read of all list entries. In case the ID has a - * key, it reads only the specified value. - */ -@Beta -@ThreadSafe -public final class GenericListReader, K extends Identifier, B extends Builder> - extends AbstractGenericReader implements ListReader { - - private static final Logger LOG = LoggerFactory.getLogger(GenericListReader.class); - - private final ListReaderCustomizer customizer; - - /** - * Create new {@link GenericListReader} - * - * @param managedDataObjectType Class object for managed data type. Must come from a list node type. - * @param customizer Customizer instance to customize this generic reader - */ - public GenericListReader(@Nonnull final InstanceIdentifier managedDataObjectType, - @Nonnull final ListReaderCustomizer customizer) { - super(managedDataObjectType); - this.customizer = customizer; - } - - @Override - @Nonnull - public List readList(@Nonnull final InstanceIdentifier id, - @Nonnull final ReadContext ctx) throws ReadFailedException { - LOG.trace("{}: Reading all list entries", this); - final List allIds = getAllIds(id, ctx); - LOG.debug("{}: Reading list entries for: {}", this, allIds); - - final ArrayList allEntries = new ArrayList<>(allIds.size()); - for (K key : allIds) { - final InstanceIdentifier.IdentifiableItem currentBdItem = RWUtils.getCurrentIdItem(id, key); - final InstanceIdentifier keyedId = RWUtils.replaceLastInId(id, currentBdItem); - final Optional read = readCurrent(keyedId, ctx); - if (read.isPresent()) { - final DataObject singleItem = read.get(); - checkArgument(getManagedDataObjectType().getTargetType().isAssignableFrom(singleItem.getClass())); - allEntries.add(getManagedDataObjectType().getTargetType().cast(singleItem)); - } - } - return allEntries; - } - - @Override - public List getAllIds(@Nonnull final InstanceIdentifier id, @Nonnull final ReadContext ctx) - throws ReadFailedException { - LOG.trace("{}: Getting all list ids", this); - final List allIds = customizer.getAllIds(id, ctx); - LOG.debug("{}: All list ids: {}", this, allIds); - return allIds; - } - - @Override - public void merge(@Nonnull final Builder builder, @Nonnull final List readData) { - customizer.merge(builder, readData); - } - - @Override - public void readCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final B builder, - @Nonnull final ReadContext ctx) - throws ReadFailedException { - customizer.readCurrentAttributes(id, builder, ctx); - } - - @Override - public B getBuilder(@Nonnull final InstanceIdentifier id) { - return customizer.getBuilder(id); - } - -} diff --git a/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/GenericReader.java b/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/GenericReader.java deleted file mode 100644 index eace7fa89..000000000 --- a/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/GenericReader.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.impl.read; - -import com.google.common.annotations.Beta; -import io.fd.honeycomb.v3po.translate.read.ReadContext; -import io.fd.honeycomb.v3po.translate.read.ReadFailedException; -import io.fd.honeycomb.v3po.translate.read.Reader; -import io.fd.honeycomb.v3po.translate.spi.read.ReaderCustomizer; -import io.fd.honeycomb.v3po.translate.util.read.AbstractGenericReader; -import javax.annotation.Nonnull; -import javax.annotation.concurrent.ThreadSafe; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Composite implementation of {@link Reader}. - */ -@Beta -@ThreadSafe -public final class GenericReader> extends AbstractGenericReader - implements Reader { - - private final ReaderCustomizer customizer; - - /** - * Create a new {@link GenericReader}. - * - * @param id Instance identifier for managed data type - * @param customizer Customizer instance to customize this generic reader - */ - public GenericReader(@Nonnull final InstanceIdentifier id, - @Nonnull final ReaderCustomizer customizer) { - super(id); - this.customizer = customizer; - } - - @Override - public void readCurrentAttributes(@Nonnull final InstanceIdentifier id, - @Nonnull final B builder, - @Nonnull final ReadContext ctx) throws ReadFailedException { - customizer.readCurrentAttributes(id, builder, ctx); - } - - @Override - public B getBuilder(@Nonnull final InstanceIdentifier id) { - return customizer.getBuilder(id); - } - - @Override - public void merge(@Nonnull final Builder parentBuilder, @Nonnull final C readValue) { - customizer.merge(parentBuilder, readValue); - } - -} diff --git a/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriter.java b/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriter.java deleted file mode 100644 index 32daf5975..000000000 --- a/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriter.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.impl.write; - -import io.fd.honeycomb.v3po.translate.spi.write.ListWriterCustomizer; -import io.fd.honeycomb.v3po.translate.spi.write.WriterCustomizer; -import io.fd.honeycomb.v3po.translate.util.RWUtils; -import io.fd.honeycomb.v3po.translate.util.write.AbstractGenericWriter; -import io.fd.honeycomb.v3po.translate.write.ListWriter; -import io.fd.honeycomb.v3po.translate.write.WriteContext; -import io.fd.honeycomb.v3po.translate.write.WriteFailedException; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.Identifiable; -import org.opendaylight.yangtools.yang.binding.Identifier; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Generic list node writer with customizable behavior thanks to injected customizer. - */ -public final class GenericListWriter, K extends Identifier> extends - AbstractGenericWriter implements ListWriter { - - private final WriterCustomizer customizer; - - public GenericListWriter(@Nonnull final InstanceIdentifier type, - @Nonnull final ListWriterCustomizer customizer) { - super(type); - this.customizer = customizer; - } - - @Override - protected void writeCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final D data, - @Nonnull final WriteContext ctx) throws WriteFailedException { - customizer.writeCurrentAttributes(id, data, ctx); - } - - @Override - protected void deleteCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final D dataBefore, - @Nonnull final WriteContext ctx) throws WriteFailedException { - customizer.deleteCurrentAttributes(id, dataBefore, ctx); - } - - @Override - protected void updateCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final D dataBefore, - @Nonnull final D dataAfter, @Nonnull final WriteContext ctx) - throws WriteFailedException { - customizer.updateCurrentAttributes(id, dataBefore, dataAfter, ctx); - } - - @Override - protected void writeCurrent(final InstanceIdentifier id, final D data, final WriteContext ctx) - throws WriteFailedException { - // Make sure the key is present - if (isWildcarded(id)) { - super.writeCurrent(getSpecificId(id, data), data, ctx); - } else { - super.writeCurrent(id, data, ctx); - } - } - - @Override - protected void updateCurrent(final InstanceIdentifier id, final D dataBefore, final D dataAfter, - final WriteContext ctx) throws WriteFailedException { - // Make sure the key is present - if (isWildcarded(id)) { - super.updateCurrent(getSpecificId(id, dataBefore), dataBefore, dataAfter, ctx); - } else { - super.updateCurrent(id, dataBefore, dataAfter, ctx); - } - } - - @Override - protected void deleteCurrent(final InstanceIdentifier id, final D dataBefore, final WriteContext ctx) - throws WriteFailedException { - // Make sure the key is present - if (isWildcarded(id)) { - super.deleteCurrent(getSpecificId(id, dataBefore), dataBefore, ctx); - } else { - super.deleteCurrent(id, dataBefore, ctx); - } - } - - private boolean isWildcarded(final InstanceIdentifier id) { - return id.firstIdentifierOf(getManagedDataObjectType().getTargetType()).isWildcarded(); - } - - private InstanceIdentifier getSpecificId(final InstanceIdentifier currentId, final D current) { - return RWUtils.replaceLastInId(currentId, - new InstanceIdentifier.IdentifiableItem<>(currentId.getTargetType(), current.getKey())); - } -} diff --git a/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriter.java b/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriter.java deleted file mode 100644 index 65c192ffa..000000000 --- a/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriter.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.impl.write; - -import io.fd.honeycomb.v3po.translate.spi.write.WriterCustomizer; -import io.fd.honeycomb.v3po.translate.util.write.AbstractGenericWriter; -import io.fd.honeycomb.v3po.translate.write.WriteContext; -import io.fd.honeycomb.v3po.translate.write.WriteFailedException; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Generic writer with customizable behavior thanks to injected customizer. - */ -public final class GenericWriter extends AbstractGenericWriter { - - private final WriterCustomizer customizer; - - public GenericWriter(@Nonnull final InstanceIdentifier type, - @Nonnull final WriterCustomizer customizer) { - super(type); - this.customizer = customizer; - } - - @Override - protected void writeCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final D data, - @Nonnull final WriteContext ctx) throws WriteFailedException { - // TODO wrap all customizer invocations in try catch, and wrap runtime exceptions in ReadFailed - // TODO same for readers - customizer.writeCurrentAttributes(id, data, ctx); - } - - @Override - protected void deleteCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final D dataBefore, - @Nonnull final WriteContext ctx) throws WriteFailedException { - customizer.deleteCurrentAttributes(id, dataBefore, ctx); - } - - @Override - protected void updateCurrentAttributes(@Nonnull final InstanceIdentifier id, - @Nonnull final D dataBefore, - @Nonnull final D dataAfter, - @Nonnull final WriteContext ctx) throws WriteFailedException { - customizer.updateCurrentAttributes(id, dataBefore, dataAfter, ctx); - } -} 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 new file mode 100644 index 000000000..d8bf96d86 --- /dev/null +++ b/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/GenericListWriterTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.impl.write; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer; +import java.util.Collections; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.Identifiable; +import org.opendaylight.yangtools.yang.binding.Identifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class GenericListWriterTest { + + private static final InstanceIdentifier + DATA_OBJECT_INSTANCE_IDENTIFIER = InstanceIdentifier.create(IdentifiableDataObject.class); + @Mock + private ListWriterCustomizer customizer; + @Mock + private WriteContext ctx; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testUpdate() throws Exception { + final GenericListWriter writer = + new GenericListWriter<>(DATA_OBJECT_INSTANCE_IDENTIFIER, customizer); + + final IdentifiableDataObject before = mock(IdentifiableDataObject.class); + final DataObjectIdentifier beforeKey = mock(DataObjectIdentifier.class); + when(before.getKey()).thenReturn(beforeKey); + final IdentifiableDataObject after = mock(IdentifiableDataObject.class); + final DataObjectIdentifier keyAfter = mock(DataObjectIdentifier.class); + when(after.getKey()).thenReturn(keyAfter); + + assertEquals(DATA_OBJECT_INSTANCE_IDENTIFIER, writer.getManagedDataObjectType()); + + final InstanceIdentifier keyedIdBefore = + (InstanceIdentifier) InstanceIdentifier.create(Collections + .singleton(new InstanceIdentifier.IdentifiableItem<>(IdentifiableDataObject.class, beforeKey))); + final InstanceIdentifier keyedIdAfter = + (InstanceIdentifier) InstanceIdentifier.create(Collections + .singleton(new InstanceIdentifier.IdentifiableItem<>(IdentifiableDataObject.class, keyAfter))); + + writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, before, after, ctx); + verify(customizer).updateCurrentAttributes(keyedIdBefore, before, after, ctx); + + writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, before, null, ctx); + verify(customizer).deleteCurrentAttributes(keyedIdBefore, before, ctx); + + writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, null, after, ctx); + verify(customizer).writeCurrentAttributes(keyedIdAfter, after, ctx); + } + + private abstract static class IdentifiableDataObject implements DataObject, Identifiable {} + private abstract static class DataObjectIdentifier implements Identifier {} +} \ 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 new file mode 100644 index 000000000..827481322 --- /dev/null +++ b/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/GenericWriterTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.impl.write; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; +import io.fd.honeycomb.translate.write.WriteContext; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class GenericWriterTest { + + private static final InstanceIdentifier + DATA_OBJECT_INSTANCE_IDENTIFIER = InstanceIdentifier.create(DataObject.class); + @Mock + private WriterCustomizer customizer; + @Mock + private WriteContext ctx; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testUpdate() throws Exception { + final GenericWriter writer = + new GenericWriter<>(DATA_OBJECT_INSTANCE_IDENTIFIER, customizer); + + final DataObject before = mock(DataObject.class); + final DataObject after = mock(DataObject.class); + + assertEquals(DATA_OBJECT_INSTANCE_IDENTIFIER, writer.getManagedDataObjectType()); + writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, before, after, ctx); + verify(customizer).updateCurrentAttributes(DATA_OBJECT_INSTANCE_IDENTIFIER, before, after, ctx); + + writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, before, null, ctx); + verify(customizer).deleteCurrentAttributes(DATA_OBJECT_INSTANCE_IDENTIFIER, before, ctx); + + writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, null, after, ctx); + verify(customizer).writeCurrentAttributes(DATA_OBJECT_INSTANCE_IDENTIFIER, after, ctx); + } +} \ No newline at end of file diff --git a/infra/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriterTest.java b/infra/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriterTest.java deleted file mode 100644 index 54a7466e1..000000000 --- a/infra/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriterTest.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.impl.write; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import io.fd.honeycomb.v3po.translate.spi.write.ListWriterCustomizer; -import io.fd.honeycomb.v3po.translate.write.WriteContext; -import java.util.Collections; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.Identifiable; -import org.opendaylight.yangtools.yang.binding.Identifier; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -public class GenericListWriterTest { - - private static final InstanceIdentifier - DATA_OBJECT_INSTANCE_IDENTIFIER = InstanceIdentifier.create(IdentifiableDataObject.class); - @Mock - private ListWriterCustomizer customizer; - @Mock - private WriteContext ctx; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testUpdate() throws Exception { - final GenericListWriter writer = - new GenericListWriter<>(DATA_OBJECT_INSTANCE_IDENTIFIER, customizer); - - final IdentifiableDataObject before = mock(IdentifiableDataObject.class); - final DataObjectIdentifier beforeKey = mock(DataObjectIdentifier.class); - when(before.getKey()).thenReturn(beforeKey); - final IdentifiableDataObject after = mock(IdentifiableDataObject.class); - final DataObjectIdentifier keyAfter = mock(DataObjectIdentifier.class); - when(after.getKey()).thenReturn(keyAfter); - - assertEquals(DATA_OBJECT_INSTANCE_IDENTIFIER, writer.getManagedDataObjectType()); - - final InstanceIdentifier keyedIdBefore = - (InstanceIdentifier) InstanceIdentifier.create(Collections - .singleton(new InstanceIdentifier.IdentifiableItem<>(IdentifiableDataObject.class, beforeKey))); - final InstanceIdentifier keyedIdAfter = - (InstanceIdentifier) InstanceIdentifier.create(Collections - .singleton(new InstanceIdentifier.IdentifiableItem<>(IdentifiableDataObject.class, keyAfter))); - - writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, before, after, ctx); - verify(customizer).updateCurrentAttributes(keyedIdBefore, before, after, ctx); - - writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, before, null, ctx); - verify(customizer).deleteCurrentAttributes(keyedIdBefore, before, ctx); - - writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, null, after, ctx); - verify(customizer).writeCurrentAttributes(keyedIdAfter, after, ctx); - } - - private abstract static class IdentifiableDataObject implements DataObject, Identifiable {} - private abstract static class DataObjectIdentifier implements Identifier {} -} \ No newline at end of file diff --git a/infra/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriterTest.java b/infra/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriterTest.java deleted file mode 100644 index 87f18965b..000000000 --- a/infra/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriterTest.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.impl.write; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import io.fd.honeycomb.v3po.translate.spi.write.WriterCustomizer; -import io.fd.honeycomb.v3po.translate.write.WriteContext; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -public class GenericWriterTest { - - private static final InstanceIdentifier - DATA_OBJECT_INSTANCE_IDENTIFIER = InstanceIdentifier.create(DataObject.class); - @Mock - private WriterCustomizer customizer; - @Mock - private WriteContext ctx; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testUpdate() throws Exception { - final GenericWriter writer = - new GenericWriter<>(DATA_OBJECT_INSTANCE_IDENTIFIER, customizer); - - final DataObject before = mock(DataObject.class); - final DataObject after = mock(DataObject.class); - - assertEquals(DATA_OBJECT_INSTANCE_IDENTIFIER, writer.getManagedDataObjectType()); - writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, before, after, ctx); - verify(customizer).updateCurrentAttributes(DATA_OBJECT_INSTANCE_IDENTIFIER, before, after, ctx); - - writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, before, null, ctx); - verify(customizer).deleteCurrentAttributes(DATA_OBJECT_INSTANCE_IDENTIFIER, before, ctx); - - writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, null, after, ctx); - verify(customizer).writeCurrentAttributes(DATA_OBJECT_INSTANCE_IDENTIFIER, after, ctx); - } -} \ No newline at end of file diff --git a/infra/translate-spi/src/main/java/io/fd/honeycomb/translate/spi/read/ListReaderCustomizer.java b/infra/translate-spi/src/main/java/io/fd/honeycomb/translate/spi/read/ListReaderCustomizer.java new file mode 100644 index 000000000..deb0a279d --- /dev/null +++ b/infra/translate-spi/src/main/java/io/fd/honeycomb/translate/spi/read/ListReaderCustomizer.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.spi.read; + +import com.google.common.annotations.Beta; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import java.util.Collections; +import java.util.List; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.Identifiable; +import org.opendaylight.yangtools.yang.binding.Identifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * CompositeListReader SPI to customize its behavior. + * + * @param Specific DataObject derived type (Identifiable), that is handled by this customizer + * @param Specific Identifier for handled type (C) + * @param Specific Builder for handled type (C) + */ +@Beta +public interface ListReaderCustomizer, K extends Identifier, B extends Builder> + extends ReaderCustomizer { + + /** + * Return list with IDs of all list nodes to be read. + * + * @param id Wildcarded ID pointing to list node managed by enclosing reader + * @param context Read context + * @throws ReadFailedException if the list of IDs could not be read + */ + @Nonnull + List getAllIds(@Nonnull final InstanceIdentifier id, @Nonnull final ReadContext context) throws + ReadFailedException; + // TODO does it make sense with vpp APIs ? Should we replace it with a simple readAll ? + + /** + * Merge read data into provided parent builder. + */ + void merge(@Nonnull final Builder builder, @Nonnull final List readData); + + @Override + default void merge(@Nonnull final Builder parentBuilder, @Nonnull final C readValue) { + merge(parentBuilder, Collections.singletonList(readValue)); + } +} diff --git a/infra/translate-spi/src/main/java/io/fd/honeycomb/translate/spi/read/ReaderCustomizer.java b/infra/translate-spi/src/main/java/io/fd/honeycomb/translate/spi/read/ReaderCustomizer.java new file mode 100644 index 000000000..610fe1514 --- /dev/null +++ b/infra/translate-spi/src/main/java/io/fd/honeycomb/translate/spi/read/ReaderCustomizer.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.spi.read; + +import com.google.common.annotations.Beta; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * CompositeChildReader SPI to customize its behavior. + * + * @param Specific DataObject derived type (Identifiable), that is handled by this customizer + * @param Specific Builder for handled type (C) + */ +@Beta +public interface ReaderCustomizer> { + + /** + * Creates new builder that will be used to build read value. + */ + @Nonnull + B getBuilder(@Nonnull final InstanceIdentifier id); + + /** + * Adds current data (identified by id) to the provided builder. + * + * @param id id of current data object + * @param builder builder for creating read value + * @param ctx + * @throws ReadFailedException if read was unsuccessful + */ + void readCurrentAttributes(@Nonnull final InstanceIdentifier id, + @Nonnull final B builder, + @Nonnull final ReadContext ctx) throws ReadFailedException; + + // FIXME need to capture parent builder type, but that's inconvenient at best, is it ok to leave it Builder and + // cast in specific customizers ? ... probably better than adding another type parameter + + /** + * Merge read data into provided parent builder. + */ + void merge(@Nonnull final Builder parentBuilder, @Nonnull final C readValue); + + +} diff --git a/infra/translate-spi/src/main/java/io/fd/honeycomb/translate/spi/write/ListWriterCustomizer.java b/infra/translate-spi/src/main/java/io/fd/honeycomb/translate/spi/write/ListWriterCustomizer.java new file mode 100644 index 000000000..cb99be6c7 --- /dev/null +++ b/infra/translate-spi/src/main/java/io/fd/honeycomb/translate/spi/write/ListWriterCustomizer.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.spi.write; + +import com.google.common.annotations.Beta; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.Identifiable; +import org.opendaylight.yangtools.yang.binding.Identifier; + +/** + * CompositeListWriter SPI to customize its behavior. + * + * @param Specific DataObject derived type (Identifiable), that is handled by this customizer + * @param Specific Identifier for handled type (C) + */ +@Beta +public interface ListWriterCustomizer, K extends Identifier> extends + WriterCustomizer { + +} \ No newline at end of file diff --git a/infra/translate-spi/src/main/java/io/fd/honeycomb/translate/spi/write/WriterCustomizer.java b/infra/translate-spi/src/main/java/io/fd/honeycomb/translate/spi/write/WriterCustomizer.java new file mode 100644 index 000000000..8148657da --- /dev/null +++ b/infra/translate-spi/src/main/java/io/fd/honeycomb/translate/spi/write/WriterCustomizer.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.spi.write; + +import com.google.common.annotations.Beta; +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; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * CompositeChildWriter SPI to customize its behavior. + * + * @param Specific DataObject derived type (Identifiable), that is handled by this customizer + */ +@Beta +public interface WriterCustomizer { + + /** + * Handle write operation. C from CRUD. + * + * @param id Identifier(from root) of data being written + * @param dataAfter New data to be written + * @param writeContext Write context can be used to store any useful information and then utilized by other customizers + * + * @throws WriteFailedException if write was unsuccessful + */ + void writeCurrentAttributes(@Nonnull final InstanceIdentifier id, + @Nonnull final D dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException; + + /** + * Handle update operation. U from CRUD. + * + * @param id Identifier(from root) of data being written + * @param dataBefore Old data + * @param dataAfter New, updated data + * @param writeContext Write context can be used to store any useful information and then utilized by other customizers + * + * @throws WriteFailedException if update was unsuccessful + */ + void updateCurrentAttributes(@Nonnull final InstanceIdentifier id, + @Nonnull final D dataBefore, + @Nonnull final D dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException; + + /** + * Handle delete operation. D from CRUD. + * + * @param id Identifier(from root) of data being written + * @param dataBefore Old data being deleted + * @param writeContext Write context can be used to store any useful information and then utilized by other customizers + * + * @throws WriteFailedException if delete was unsuccessful + */ + void deleteCurrentAttributes(@Nonnull final InstanceIdentifier id, + @Nonnull final D dataBefore, + @Nonnull final WriteContext writeContext) throws WriteFailedException; + +} diff --git a/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/ListReaderCustomizer.java b/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/ListReaderCustomizer.java deleted file mode 100644 index 59557554f..000000000 --- a/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/ListReaderCustomizer.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.spi.read; - -import com.google.common.annotations.Beta; -import io.fd.honeycomb.v3po.translate.read.ReadContext; -import io.fd.honeycomb.v3po.translate.read.ReadFailedException; -import java.util.Collections; -import java.util.List; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.Identifiable; -import org.opendaylight.yangtools.yang.binding.Identifier; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * CompositeListReader SPI to customize its behavior. - * - * @param Specific DataObject derived type (Identifiable), that is handled by this customizer - * @param Specific Identifier for handled type (C) - * @param Specific Builder for handled type (C) - */ -@Beta -public interface ListReaderCustomizer, K extends Identifier, B extends Builder> - extends ReaderCustomizer { - - /** - * Return list with IDs of all list nodes to be read. - * - * @param id Wildcarded ID pointing to list node managed by enclosing reader - * @param context Read context - * @throws ReadFailedException if the list of IDs could not be read - */ - @Nonnull - List getAllIds(@Nonnull final InstanceIdentifier id, @Nonnull final ReadContext context) throws - ReadFailedException; - // TODO does it make sense with vpp APIs ? Should we replace it with a simple readAll ? - - /** - * Merge read data into provided parent builder. - */ - void merge(@Nonnull final Builder builder, @Nonnull final List readData); - - @Override - default void merge(@Nonnull final Builder parentBuilder, @Nonnull final C readValue) { - merge(parentBuilder, Collections.singletonList(readValue)); - } -} diff --git a/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/ReaderCustomizer.java b/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/ReaderCustomizer.java deleted file mode 100644 index 270da8beb..000000000 --- a/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/ReaderCustomizer.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.spi.read; - -import com.google.common.annotations.Beta; -import io.fd.honeycomb.v3po.translate.read.ReadContext; -import io.fd.honeycomb.v3po.translate.read.ReadFailedException; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * CompositeChildReader SPI to customize its behavior. - * - * @param Specific DataObject derived type (Identifiable), that is handled by this customizer - * @param Specific Builder for handled type (C) - */ -@Beta -public interface ReaderCustomizer> { - - /** - * Creates new builder that will be used to build read value. - */ - @Nonnull - B getBuilder(@Nonnull final InstanceIdentifier id); - - /** - * Adds current data (identified by id) to the provided builder. - * - * @param id id of current data object - * @param builder builder for creating read value - * @param ctx - * @throws ReadFailedException if read was unsuccessful - */ - void readCurrentAttributes(@Nonnull final InstanceIdentifier id, - @Nonnull final B builder, - @Nonnull final ReadContext ctx) throws ReadFailedException; - - // FIXME need to capture parent builder type, but that's inconvenient at best, is it ok to leave it Builder and - // cast in specific customizers ? ... probably better than adding another type parameter - - /** - * Merge read data into provided parent builder. - */ - void merge(@Nonnull final Builder parentBuilder, @Nonnull final C readValue); - - -} diff --git a/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/write/ListWriterCustomizer.java b/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/write/ListWriterCustomizer.java deleted file mode 100644 index 41cdb94b6..000000000 --- a/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/write/ListWriterCustomizer.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.spi.write; - -import com.google.common.annotations.Beta; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.Identifiable; -import org.opendaylight.yangtools.yang.binding.Identifier; - -/** - * CompositeListWriter SPI to customize its behavior. - * - * @param Specific DataObject derived type (Identifiable), that is handled by this customizer - * @param Specific Identifier for handled type (C) - */ -@Beta -public interface ListWriterCustomizer, K extends Identifier> extends - WriterCustomizer { - -} \ No newline at end of file diff --git a/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/write/WriterCustomizer.java b/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/write/WriterCustomizer.java deleted file mode 100644 index 06ce3f814..000000000 --- a/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/write/WriterCustomizer.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.spi.write; - -import com.google.common.annotations.Beta; -import io.fd.honeycomb.v3po.translate.write.WriteContext; -import io.fd.honeycomb.v3po.translate.write.WriteFailedException; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * CompositeChildWriter SPI to customize its behavior. - * - * @param Specific DataObject derived type (Identifiable), that is handled by this customizer - */ -@Beta -public interface WriterCustomizer { - - /** - * Handle write operation. C from CRUD. - * - * @param id Identifier(from root) of data being written - * @param dataAfter New data to be written - * @param writeContext Write context can be used to store any useful information and then utilized by other customizers - * - * @throws WriteFailedException if write was unsuccessful - */ - void writeCurrentAttributes(@Nonnull final InstanceIdentifier id, - @Nonnull final D dataAfter, - @Nonnull final WriteContext writeContext) throws WriteFailedException; - - /** - * Handle update operation. U from CRUD. - * - * @param id Identifier(from root) of data being written - * @param dataBefore Old data - * @param dataAfter New, updated data - * @param writeContext Write context can be used to store any useful information and then utilized by other customizers - * - * @throws WriteFailedException if update was unsuccessful - */ - void updateCurrentAttributes(@Nonnull final InstanceIdentifier id, - @Nonnull final D dataBefore, - @Nonnull final D dataAfter, - @Nonnull final WriteContext writeContext) throws WriteFailedException; - - /** - * Handle delete operation. D from CRUD. - * - * @param id Identifier(from root) of data being written - * @param dataBefore Old data being deleted - * @param writeContext Write context can be used to store any useful information and then utilized by other customizers - * - * @throws WriteFailedException if delete was unsuccessful - */ - void deleteCurrentAttributes(@Nonnull final InstanceIdentifier id, - @Nonnull final D dataBefore, - @Nonnull final WriteContext writeContext) throws WriteFailedException; - -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/AbstractSubtreeManagerRegistryBuilderBuilder.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/AbstractSubtreeManagerRegistryBuilderBuilder.java new file mode 100644 index 000000000..bf1e89c12 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/AbstractSubtreeManagerRegistryBuilderBuilder.java @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; +import io.fd.honeycomb.translate.ModifiableSubtreeManagerRegistryBuilder; +import io.fd.honeycomb.translate.SubtreeManager; +import io.fd.honeycomb.translate.SubtreeManagerRegistryBuilder; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import javax.annotation.Nonnull; +import org.jgrapht.experimental.dag.DirectedAcyclicGraph; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public abstract class AbstractSubtreeManagerRegistryBuilderBuilder, R> + implements ModifiableSubtreeManagerRegistryBuilder, SubtreeManagerRegistryBuilder, AutoCloseable { + + // Using directed acyclic graph to represent the ordering relationships between writers + private final DirectedAcyclicGraph, Order> + handlersRelations = new DirectedAcyclicGraph<>((sourceVertex, targetVertex) -> new Order()); + private final Map, S> handlersMap = new HashMap<>(); + + /** + * Add handler without any special relationship to any other type. + */ + @Override + public AbstractSubtreeManagerRegistryBuilderBuilder add(@Nonnull final S handler) { + // Make IID wildcarded just in case + // + the way InstanceIdentifier.create + equals work for Identifiable items is unexpected, meaning updates would + // not be matched to writers in registry + final InstanceIdentifier targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType()); + checkWriterNotPresentYet(targetType); + handlersRelations.addVertex(targetType); + handlersMap.put(targetType, handler); + return this; + } + + /** + * Add handler without any special relationship to any other type. + */ + @Override + public AbstractSubtreeManagerRegistryBuilderBuilder subtreeAdd(@Nonnull final Set> handledChildren, + @Nonnull final S handler) { + add(getSubtreeHandler(handledChildren, handler)); + return this; + } + + private void checkWriterNotPresentYet(final InstanceIdentifier targetType) { + Preconditions.checkArgument(!handlersMap.containsKey(targetType), + "Writer for type: %s already present: %s", targetType, handlersMap.get(targetType)); + } + + /** + * Add handler with relationship: to be executed before handler handling relatedType. + */ + @Override + public AbstractSubtreeManagerRegistryBuilderBuilder addBefore(@Nonnull final S handler, + @Nonnull final InstanceIdentifier relatedType) { + final InstanceIdentifier targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType()); + final InstanceIdentifier wildcardedRelatedType = RWUtils.makeIidWildcarded(relatedType); + checkWriterNotPresentYet(targetType); + handlersRelations.addVertex(targetType); + handlersRelations.addVertex(wildcardedRelatedType); + addEdge(targetType, wildcardedRelatedType); + handlersMap.put(targetType, handler); + return this; + } + + @Override + public AbstractSubtreeManagerRegistryBuilderBuilder addBefore(@Nonnull final S handler, + @Nonnull final Collection> relatedTypes) { + final InstanceIdentifier targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType()); + checkWriterNotPresentYet(targetType); + handlersRelations.addVertex(targetType); + relatedTypes.stream() + .map(RWUtils::makeIidWildcarded) + .forEach(handlersRelations::addVertex); + relatedTypes.stream() + .map(RWUtils::makeIidWildcarded) + .forEach(type -> addEdge(targetType, type)); + handlersMap.put(targetType, handler); + return this; + } + + @Override + public AbstractSubtreeManagerRegistryBuilderBuilder subtreeAddBefore( + @Nonnull final Set> handledChildren, + @Nonnull final S handler, + @Nonnull final InstanceIdentifier relatedType) { + return addBefore(getSubtreeHandler(handledChildren, handler), relatedType); + } + + @Override + public AbstractSubtreeManagerRegistryBuilderBuilder subtreeAddBefore( + @Nonnull final Set> handledChildren, + @Nonnull final S handler, + @Nonnull final Collection> relatedTypes) { + return addBefore(getSubtreeHandler(handledChildren, handler), relatedTypes); + } + + protected abstract S getSubtreeHandler(@Nonnull final Set> handledChildren, + @Nonnull final S handler); + + /** + * Add handler with relationship: to be executed after handler handling relatedType. + */ + @Override + public AbstractSubtreeManagerRegistryBuilderBuilder addAfter(@Nonnull final S handler, + @Nonnull final InstanceIdentifier relatedType) { + final InstanceIdentifier targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType()); + final InstanceIdentifier wildcardedRelatedType = RWUtils.makeIidWildcarded(relatedType); + checkWriterNotPresentYet(targetType); + handlersRelations.addVertex(targetType); + handlersRelations.addVertex(wildcardedRelatedType); + // set edge to indicate before relationship, just reversed + addEdge(wildcardedRelatedType, targetType); + handlersMap.put(targetType, handler); + return this; + } + + @Override + public AbstractSubtreeManagerRegistryBuilderBuilder addAfter(@Nonnull final S handler, + @Nonnull final Collection> relatedTypes) { + final InstanceIdentifier targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType()); + checkWriterNotPresentYet(targetType); + handlersRelations.addVertex(targetType); + relatedTypes.stream() + .map(RWUtils::makeIidWildcarded) + .forEach(handlersRelations::addVertex); + // set edge to indicate before relationship, just reversed + relatedTypes.stream() + .map(RWUtils::makeIidWildcarded) + .forEach(type -> addEdge(type, targetType)); + handlersMap.put(targetType, handler); + return this; + } + + @Override + public AbstractSubtreeManagerRegistryBuilderBuilder subtreeAddAfter( + @Nonnull final Set> handledChildren, + @Nonnull final S handler, + @Nonnull final InstanceIdentifier relatedType) { + return addAfter(getSubtreeHandler(handledChildren, handler), relatedType); + } + + @Override + public AbstractSubtreeManagerRegistryBuilderBuilder subtreeAddAfter( + @Nonnull final Set> handledChildren, + @Nonnull final S handler, + @Nonnull final Collection> relatedTypes) { + return addAfter(getSubtreeHandler(handledChildren, handler), relatedTypes); + } + + + private void addEdge(final InstanceIdentifier targetType, + final InstanceIdentifier relatedType) { + try { + handlersRelations.addDagEdge(targetType, relatedType); + } catch (DirectedAcyclicGraph.CycleFoundException e) { + throw new IllegalArgumentException(String.format( + "Unable to add writer with relation: %s -> %s. Loop detected", targetType, relatedType), e); + } + } + + protected ImmutableMap, S> getMappedHandlers() { + final ImmutableMap.Builder, S> builder = ImmutableMap.builder(); + // Iterate writer types according to their relationships from graph + handlersRelations.iterator() + .forEachRemaining(writerType -> { + // There might be types stored just for relationship sake, no real writer, ignoring those + if (handlersMap.containsKey(writerType)) { + builder.put(writerType, handlersMap.get(writerType)); + } + }); + + // TODO we could optimize subtree handlers, if there is a dedicated handler for a node managed by a subtree + // handler, recreate the subtree handler with a subset of handled child nodes + // This way it is not necessary to change the configuration of subtree writer, just to add a dedicated child + // writer. This will be needed if we ever switch to annotations for reader/writer hierarchy initialization + + return builder.build(); + } + + @Override + public void close() throws Exception { + handlersMap.clear(); + // Wrap sets into another set to avoid concurrent modification ex in graph + handlersRelations.removeAllEdges(Sets.newHashSet(handlersRelations.edgeSet())); + handlersRelations.removeAllVertices(Sets.newHashSet(handlersRelations.vertexSet())); + } + + // Represents edges in graph + private class Order {} +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/JsonUtils.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/JsonUtils.java new file mode 100644 index 000000000..a26334454 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/JsonUtils.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util; + +import com.google.common.base.Charsets; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter; +import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactory; +import org.opendaylight.yangtools.yang.data.codec.gson.JSONNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream; +import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; + +public final class JsonUtils { + + private JsonUtils() {} + + /** + * Serialize normalized node root structure into provided output stream + * + * @throws IOException if serialized data cannot be written into provided output stream + */ + public static void writeJsonRoot(@Nonnull final NormalizedNode rootData, + @Nonnull final SchemaContext schemaContext, + @Nonnull final OutputStream outputStream) throws IOException { + final JsonWriter + jsonWriter = createJsonWriter(outputStream, true); + final NormalizedNodeStreamWriter streamWriter = JSONNormalizedNodeStreamWriter + .createNestedWriter(JSONCodecFactory.create(schemaContext), SchemaPath.ROOT, null, jsonWriter); + final NormalizedNodeWriter normalizedNodeWriter = + NormalizedNodeWriter.forStreamWriter(streamWriter, true); + jsonWriter.beginObject(); + writeChildren(normalizedNodeWriter,(ContainerNode) rootData); + jsonWriter.endObject(); + jsonWriter.flush(); + } + + /** + * Read json serialized normalized node root structure and parse them into normalized nodes + * + * @return artificial normalized node holding all the top level nodes from provided stream as children. In case + * the stream is empty, empty artificial normalized node is returned + * + * @throws IllegalArgumentException if content in the provided input stream is not restore-able + */ + public static ContainerNode readJsonRoot(@Nonnull final SchemaContext schemaContext, + @Nonnull final InputStream stream) { + final DataContainerNodeAttrBuilder builder = + Builders.containerBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(schemaContext.getQName())); + final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(builder); + + final JsonParserStream jsonParser = JsonParserStream.create(writer, schemaContext); + final JsonReader reader = new JsonReader(new InputStreamReader(stream, Charsets.UTF_8)); + jsonParser.parse(reader); + + return builder.build(); + } + + private static void writeChildren(final NormalizedNodeWriter nnWriter, final ContainerNode data) throws IOException { + for(final DataContainerChild child : data.getValue()) { + nnWriter.write(child); + } + } + + private static JsonWriter createJsonWriter(final OutputStream entityStream, boolean prettyPrint) { + if (prettyPrint) { + return JsonWriterFactory.createJsonWriter(new OutputStreamWriter(entityStream, Charsets.UTF_8), 2); + } else { + return JsonWriterFactory.createJsonWriter(new OutputStreamWriter(entityStream, Charsets.UTF_8)); + } + } +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/RWUtils.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/RWUtils.java new file mode 100644 index 000000000..695ce6806 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/RWUtils.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util; + +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; +import io.fd.honeycomb.translate.SubtreeManager; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collector; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.Augmentation; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.Identifiable; +import org.opendaylight.yangtools.yang.binding.Identifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public final class RWUtils { + + private RWUtils() {} + + /** + * Collector expecting only a single resulting item from a stream + */ + public static Collector singleItemCollector() { + return Collectors.collectingAndThen( + Collectors.toList(), + list -> { + if (list.size() != 1) { + throw new IllegalStateException("Unexpected size of list: " + list + ". Single item expected"); + } + return list.get(0); + } + ); + } + + /** + * Find next item in ID after provided type + */ + @Nonnull + public static InstanceIdentifier.PathArgument getNextId(@Nonnull final InstanceIdentifier id, + @Nonnull final InstanceIdentifier type) { + // TODO this is inefficient(maybe, depending on actual Iterable type) + final Iterable pathArguments = id.getPathArguments(); + final int i = Iterables.indexOf(pathArguments, new Predicate() { + @Override + public boolean apply(final InstanceIdentifier.PathArgument input) { + return input.getType().isAssignableFrom(type.getTargetType()); + } + }); + Preconditions.checkArgument(i >= 0, "Unable to find %s type in %s", type.getTargetType(), id); + return Iterables.get(pathArguments, i + 1); + } + + /** + * Replace last item in ID with a provided IdentifiableItem of the same type + */ + @SuppressWarnings("unchecked") + @Nonnull + public static , K extends Identifier> InstanceIdentifier replaceLastInId( + @Nonnull final InstanceIdentifier id, final InstanceIdentifier.IdentifiableItem currentBdItem) { + + final Iterable pathArguments = id.getPathArguments(); + final Iterable withoutCurrent = + Iterables.limit(pathArguments, Iterables.size(pathArguments) - 1); + final Iterable concat = + Iterables.concat(withoutCurrent, Collections.singleton(currentBdItem)); + return (InstanceIdentifier) InstanceIdentifier.create(concat); + } + + /** + * Create IdentifiableItem from target type of provided ID with provided key + */ + @Nonnull + public static , K extends Identifier> InstanceIdentifier.IdentifiableItem getCurrentIdItem( + @Nonnull final InstanceIdentifier id, final K key) { + return new InstanceIdentifier.IdentifiableItem<>(id.getTargetType(), key); + } + + /** + * Trim InstanceIdentifier at indexOf(type) + */ + @SuppressWarnings("unchecked") + @Nonnull + public static InstanceIdentifier cutId(@Nonnull final InstanceIdentifier id, + @Nonnull final InstanceIdentifier type) { + final Iterable pathArguments = id.getPathArguments(); + final int i = Iterables.indexOf(pathArguments, new Predicate() { + @Override + public boolean apply(final InstanceIdentifier.PathArgument input) { + return input.getType().equals(type.getTargetType()); + } + }); + Preconditions.checkArgument(i >= 0, "ID %s does not contain %s", id, type); + return (InstanceIdentifier) InstanceIdentifier.create(Iterables.limit(pathArguments, i + 1)); + } + + /** + * Create an ordered map from a collection, checking for duplicity in the process. + */ + @Nonnull + public static Map uniqueLinkedIndex(@Nonnull final Collection values, @Nonnull final Function keyFunction) { + final Map objectObjectLinkedHashMap = Maps.newLinkedHashMap(); + for (V value : values) { + final K key = keyFunction.apply(value); + Preconditions.checkArgument(objectObjectLinkedHashMap.put(key, value) == null, + "Duplicate key detected : %s", key); + } + return objectObjectLinkedHashMap; + } + + public static final Function, Class> + MANAGER_CLASS_FUNCTION = new Function, Class>() { + @Override + public Class apply(final SubtreeManager input) { + return input.getManagedDataObjectType().getTargetType(); + } + }; + + public static final Function>, Class> + MANAGER_CLASS_AUG_FUNCTION = new Function>, Class>() { + + @Override + @SuppressWarnings("unchecked") + public Class apply(final SubtreeManager> input) { + final Class> targetType = input.getManagedDataObjectType().getTargetType(); + Preconditions.checkArgument(DataObject.class.isAssignableFrom(targetType)); + return (Class) targetType; + } + }; + + /** + * Transform a keyed instance identifier into a wildcarded one. + *

+ * ! This has to be called also for wildcarded List instance identifiers + * due to weird behavior of equals in InstanceIdentifier ! + */ + @SuppressWarnings("unchecked") + public static InstanceIdentifier makeIidWildcarded(final InstanceIdentifier id) { + final List transformedPathArguments = + StreamSupport.stream(id.getPathArguments().spliterator(), false) + .map(RWUtils::cleanPathArgumentFromKeys) + .collect(Collectors.toList()); + return (InstanceIdentifier) InstanceIdentifier.create(transformedPathArguments); + } + + /** + * Transform a keyed instance identifier into a wildcarded one, keeping keys except the last item. + */ + @SuppressWarnings("unchecked") + public static InstanceIdentifier makeIidLastWildcarded(final InstanceIdentifier id) { + final InstanceIdentifier.Item wildcardedItem = new InstanceIdentifier.Item<>(id.getTargetType()); + final Iterable pathArguments = id.getPathArguments(); + return (InstanceIdentifier) InstanceIdentifier.create( + Iterables.concat( + Iterables.limit(pathArguments, Iterables.size(pathArguments) - 1), + Collections.singleton(wildcardedItem))); + } + + private static InstanceIdentifier.PathArgument cleanPathArgumentFromKeys(final InstanceIdentifier.PathArgument pathArgument) { + return pathArgument instanceof InstanceIdentifier.IdentifiableItem + ? new InstanceIdentifier.Item<>(pathArgument.getType()) + : pathArgument; + } +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/ReflectionUtils.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/ReflectionUtils.java new file mode 100644 index 000000000..74c7be97c --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/ReflectionUtils.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util; + +import com.google.common.base.Optional; +import java.lang.reflect.Method; +import java.util.List; +import javax.annotation.Nonnull; + +/** + * Reflection based utilities + */ +public final class ReflectionUtils { + + private ReflectionUtils() {} + + /** + * Find a specific method using reflection + * + * @param managedType Class object to find method in + * @param prefix Method name prefix used when finding the method. Case does not matter. + * @param paramTypes List of input argument types + * @param retType Return type + * + * @return Found method or Optional.absent() if there's no such method + */ + @Nonnull + public static Optional findMethodReflex(@Nonnull final Class managedType, + @Nonnull final String prefix, + @Nonnull final List> paramTypes, + @Nonnull final Class retType) { + for (Method method : managedType.getMethods()) { + if (isMethodMatch(prefix, paramTypes, retType, method)) { + return Optional.of(method); + } + } + + return Optional.absent(); + } + + private static boolean isMethodMatch(final @Nonnull String prefix, + final @Nonnull List> paramTypes, + final @Nonnull Class retType, final Method method) { + if (!method.getName().toLowerCase().startsWith(prefix.toLowerCase())) { + return false; + } + + final Class[] parameterTypes = method.getParameterTypes(); + if (parameterTypes.length != paramTypes.size()) { + return false; + } + + for (int i = 0; i < parameterTypes.length; i++) { + if (!parameterTypes[i].isAssignableFrom(paramTypes.get(i))) { + return false; + } + } + + if (!method.getReturnType().equals(retType)) { + return false; + } + + return true; + } +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/TransactionMappingContext.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/TransactionMappingContext.java new file mode 100644 index 000000000..1b6504c78 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/TransactionMappingContext.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import io.fd.honeycomb.translate.MappingContext; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Binding Transaction backed mapping context. + */ +public class TransactionMappingContext implements MappingContext { + + private final ReadWriteTransaction readWriteTransaction; + + // TODO make async + + public TransactionMappingContext(final ReadWriteTransaction readWriteTransaction) { + this.readWriteTransaction = readWriteTransaction; + } + + @Override + public Optional read(@Nonnull final InstanceIdentifier currentId) { + try { + return readWriteTransaction.read(LogicalDatastoreType.OPERATIONAL, currentId).checkedGet(); + } catch (ReadFailedException e) { + throw new IllegalStateException("Unable to perform read", e); + } + } + + @Override + public void delete(final InstanceIdentifier path) { + readWriteTransaction.delete(LogicalDatastoreType.OPERATIONAL, path); + } + + @Override + public void merge(final InstanceIdentifier path, T data) { + readWriteTransaction.merge(LogicalDatastoreType.OPERATIONAL, path, data, true); + } + + @Override + public void put(final InstanceIdentifier path, T data) { + readWriteTransaction.put(LogicalDatastoreType.OPERATIONAL, path, data, true); + } + + public CheckedFuture submit() { + return readWriteTransaction.submit(); + } + + @Override + public void close() { + readWriteTransaction.cancel(); + } +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/AbstractGenericReader.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/AbstractGenericReader.java new file mode 100644 index 000000000..75a2a673c --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/AbstractGenericReader.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.read; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.annotations.Beta; +import com.google.common.base.Optional; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.Reader; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.honeycomb.translate.read.ReadFailedException; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Beta +public abstract class AbstractGenericReader> implements Reader { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractGenericReader.class); + + private final InstanceIdentifier instanceIdentifier; + + protected AbstractGenericReader(final InstanceIdentifier managedDataObjectType) { + this.instanceIdentifier = RWUtils.makeIidWildcarded(managedDataObjectType); + } + + @Nonnull + @Override + public final InstanceIdentifier getManagedDataObjectType() { + return instanceIdentifier; + } + + /** + * @param id {@link InstanceIdentifier} pointing to current node. In case of keyed list, key must be present. + * + */ + protected Optional readCurrent(@Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext ctx) throws ReadFailedException { + LOG.debug("{}: Reading current: {}", this, id); + final B builder = getBuilder(id); + // Cache empty value to determine if anything has changed later TODO cache in a field + final D emptyValue = builder.build(); + + LOG.trace("{}: Reading current attributes", this); + readCurrentAttributes(id, builder, ctx); + + // Need to check whether anything was filled in to determine if data is present or not. + final D built = builder.build(); + final Optional read = built.equals(emptyValue) + ? Optional.absent() + : Optional.of(built); + + LOG.debug("{}: Current node read successfully. Result: {}", this, read); + return read; + } + + @Nonnull + @Override + @SuppressWarnings("unchecked") + public Optional read(@Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext ctx) + throws ReadFailedException { + LOG.trace("{}: Reading : {}", this, id); + checkArgument(id.getTargetType().equals(getManagedDataObjectType().getTargetType())); + return readCurrent((InstanceIdentifier) id, ctx); + } + + @Override + public String toString() { + return String.format("Reader[%s]", getManagedDataObjectType().getTargetType().getSimpleName()); + } +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/BindingBrokerReader.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/BindingBrokerReader.java new file mode 100644 index 000000000..03df15175 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/BindingBrokerReader.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.read; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.Reader; +import io.fd.honeycomb.translate.read.ReadFailedException; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Simple DataBroker backed reader allowing to delegate reads to different brokers. + */ +public final class BindingBrokerReader> + implements Reader, AutoCloseable { + + private final InstanceIdentifier instanceIdentifier; + private final DataBroker dataBroker; + private final LogicalDatastoreType datastoreType; + private final ReflexiveReaderCustomizer reflexiveReaderCustomizer; + + public BindingBrokerReader(final InstanceIdentifier instanceIdentifier, + final DataBroker dataBroker, + final LogicalDatastoreType datastoreType, + final Class builderClass) { + this.reflexiveReaderCustomizer = new ReflexiveReaderCustomizer<>(instanceIdentifier.getTargetType(), builderClass); + this.instanceIdentifier = instanceIdentifier; + this.dataBroker = dataBroker; + this.datastoreType = datastoreType; + } + + @Nonnull + @Override + public Optional read(@Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext ctx) throws ReadFailedException { + try (final ReadOnlyTransaction readOnlyTransaction = dataBroker.newReadOnlyTransaction()) { + final CheckedFuture, org.opendaylight.controller.md.sal.common.api.data.ReadFailedException> + read = readOnlyTransaction.read(datastoreType, id); + try { + return read.checkedGet(); + } catch (org.opendaylight.controller.md.sal.common.api.data.ReadFailedException e) { + throw new ReadFailedException(id, e); + } + } + } + + @Override + public void merge(@Nonnull final Builder parentBuilder, @Nonnull final D readValue) { + reflexiveReaderCustomizer.merge(parentBuilder, readValue); + } + + @Nonnull + @Override + public B getBuilder(final InstanceIdentifier id) { + return reflexiveReaderCustomizer.getBuilder(id); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier id, + @Nonnull final B builder, + @Nonnull final ReadContext ctx) throws ReadFailedException { + throw new UnsupportedOperationException("Not supported"); + } + + @Nonnull + @Override + public InstanceIdentifier getManagedDataObjectType() { + return instanceIdentifier; + } + + @Override + public void close() throws Exception { + // Noop + } +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/KeepaliveReaderWrapper.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/KeepaliveReaderWrapper.java new file mode 100644 index 000000000..f8d1458c0 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/KeepaliveReaderWrapper.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.read; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.Reader; +import io.fd.honeycomb.translate.MappingContext; +import io.fd.honeycomb.translate.ModificationCache; +import io.fd.honeycomb.translate.read.ReadFailedException; +import java.io.Closeable; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Reader wrapper that periodically invokes a read to determine whether reads are still fully functional. + * In case a specific error occurs, Keep-alive failure listener gets notified. + */ +public final class KeepaliveReaderWrapper> implements Reader, Runnable, Closeable { + + private static final Logger LOG = LoggerFactory.getLogger(KeepaliveReaderWrapper.class); + + private static final NoopReadContext CTX = new NoopReadContext(); + + private final Reader delegate; + private final Class exceptionType; + private final KeepaliveFailureListener failureListener; + private final ScheduledFuture scheduledFuture; + + /** + * Create new Keepalive wrapper + * + * @param delegate underlying reader performing actual reads + * @param executor scheduled executor service to schedule keepalive calls + * @param exception type of exception used to differentiate keepalive exception from other exceptions + * @param delayInSeconds number of seconds to wait between keepalive calls + * @param failureListener listener to be called whenever a keepalive failure is detected + */ + public KeepaliveReaderWrapper(@Nonnull final Reader delegate, + @Nonnull final ScheduledExecutorService executor, + @Nonnull final Class exception, + @Nonnegative final int delayInSeconds, + @Nonnull final KeepaliveFailureListener failureListener) { + this.delegate = delegate; + this.exceptionType = exception; + this.failureListener = failureListener; + Preconditions.checkArgument(delayInSeconds > 0, "Delay cannot be < 0"); + LOG.debug("Starting keep-alive execution on top of: {} with delay of: {} seconds", delegate, delayInSeconds); + scheduledFuture = executor.scheduleWithFixedDelay(this, delayInSeconds, delayInSeconds, TimeUnit.SECONDS); + } + + @Nonnull + public Optional read(@Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext ctx) throws ReadFailedException { + return delegate.read(id, ctx); + } + + public void readCurrentAttributes(@Nonnull final InstanceIdentifier id, + @Nonnull final B builder, + @Nonnull final ReadContext ctx) throws ReadFailedException { + delegate.readCurrentAttributes(id, builder, ctx); + } + + @Nonnull + public B getBuilder(final InstanceIdentifier id) { + return delegate.getBuilder(id); + } + + public void merge(@Nonnull final Builder parentBuilder, + @Nonnull final D readValue) { + delegate.merge(parentBuilder, readValue); + } + + @Nonnull + @Override + public InstanceIdentifier getManagedDataObjectType() { + return delegate.getManagedDataObjectType(); + } + + @Override + public void run() { + LOG.trace("Invoking keepalive"); + try { + final Optional read = read(delegate.getManagedDataObjectType(), CTX); + LOG.debug("Keepalive executed successfully with data: {}", read); + } catch (Exception e) { + if (exceptionType.isAssignableFrom(e.getClass())) { + LOG.warn("Keepalive failed. Notifying listener", e); + failureListener.onKeepaliveFailure(); + } + LOG.warn("Keepalive failed unexpectedly", e); + throw new IllegalArgumentException("Unexpected failure during keep-alive execution", e); + } + } + + @Override + public void close() { + // Do not interrupt, it's not our executor + scheduledFuture.cancel(false); + } + + /** + * Listener that gets called whenever keepalive fails as expected + */ + public interface KeepaliveFailureListener { + + void onKeepaliveFailure(); + } + + private static final class NoopMappingContext implements MappingContext { + @Override + public Optional read(@Nonnull final InstanceIdentifier currentId) { + return Optional.absent(); + } + + @Override + public void delete(final InstanceIdentifier path) {} + + @Override + public void merge(final InstanceIdentifier path, final T data) {} + + @Override + public void put(final InstanceIdentifier path, final T data) {} + + @Override + public void close() {} + } + + private static class NoopReadContext implements ReadContext { + + private final ModificationCache modificationCache = new ModificationCache(); + + @Nonnull + @Override + public ModificationCache getModificationCache() { + return modificationCache; + } + + @Nonnull + @Override + public MappingContext getMappingContext() { + return new NoopMappingContext(); + } + + @Override + public void close() { + + } + } +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/NoopReaderCustomizer.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/NoopReaderCustomizer.java new file mode 100644 index 000000000..66bff9383 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/NoopReaderCustomizer.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.read; + +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.ReaderCustomizer; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public abstract class NoopReaderCustomizer> implements + ReaderCustomizer { + + @Override + public void readCurrentAttributes(InstanceIdentifier id, final B builder, final ReadContext context) throws + ReadFailedException { + // Noop + } +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/ReflexiveListReaderCustomizer.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/ReflexiveListReaderCustomizer.java new file mode 100644 index 000000000..bf2d109c7 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/ReflexiveListReaderCustomizer.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.read; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.base.Optional; +import io.fd.honeycomb.translate.spi.read.ListReaderCustomizer; +import io.fd.honeycomb.translate.util.ReflectionUtils; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.Identifiable; +import org.opendaylight.yangtools.yang.binding.Identifier; + +/** + * Might be slow ! + */ +public abstract class ReflexiveListReaderCustomizer, K extends Identifier, B extends Builder> + extends ReflexiveReaderCustomizer + implements ListReaderCustomizer { + + + public ReflexiveListReaderCustomizer(final Class typeClass, final Class builderClass) { + super(typeClass, builderClass); + } + + @Override + public void merge(@Nonnull final Builder parentBuilder, @Nonnull final C readValue) { + merge(parentBuilder, Collections.singletonList(readValue)); + } + + @Override + public void merge(@Nonnull final Builder parentBuilder, @Nonnull final List readData) { + final Optional method = + ReflectionUtils.findMethodReflex(parentBuilder.getClass(), "set" + getTypeClass().getSimpleName(), + Collections.singletonList(List.class), parentBuilder.getClass()); + + checkArgument(method.isPresent(), "Unable to set %s to %s", readData, parentBuilder); + + try { + method.get().invoke(parentBuilder, readData); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new IllegalArgumentException("Unable to set " + readData + " to " + parentBuilder, e); + } + } +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/ReflexiveReader.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/ReflexiveReader.java new file mode 100644 index 000000000..2aa35514e --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/ReflexiveReader.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.read; + +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Reader that performs no read operation on its own, just fills in the hierarchy. + *

+ * Might be slow due to reflection ! + */ +public class ReflexiveReader> extends AbstractGenericReader { + + private final ReflexiveReaderCustomizer customizer; + + public ReflexiveReader(final InstanceIdentifier identifier, final Class builderClass) { + super(identifier); + this.customizer = new ReflexiveReaderCustomizer<>(identifier.getTargetType(), builderClass); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final B builder, + @Nonnull final ReadContext ctx) + throws ReadFailedException { + customizer.readCurrentAttributes(id, builder, ctx); + } + + @Nonnull + @Override + public B getBuilder(final InstanceIdentifier id) { + return customizer.getBuilder(id); + } + + @Override + public void merge(@Nonnull final Builder parentBuilder, @Nonnull final C readValue) { + customizer.merge(parentBuilder, readValue); + } +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/ReflexiveReaderCustomizer.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/ReflexiveReaderCustomizer.java new file mode 100644 index 000000000..6ceb7a2b1 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/ReflexiveReaderCustomizer.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.read; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.base.Optional; +import com.google.common.collect.Lists; +import io.fd.honeycomb.translate.util.ReflectionUtils; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.Augmentation; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Might be slow ! + */ +class ReflexiveReaderCustomizer> extends NoopReaderCustomizer { + + private final Class typeClass; + private final Class builderClass; + + public ReflexiveReaderCustomizer(final Class typeClass, final Class builderClass) { + this.typeClass = typeClass; + this.builderClass = builderClass; + } + + protected Class getTypeClass() { + return typeClass; + } + + protected Class getBuilderClass() { + return builderClass; + } + + @Nonnull + @Override + public B getBuilder(@Nonnull InstanceIdentifier id) { + try { + return builderClass.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new IllegalStateException("Unable to instantiate " + builderClass, e); + } + } + + @Override + public void merge(@Nonnull final Builder parentBuilder, @Nonnull final C readValue) { + if (Augmentation.class.isAssignableFrom(typeClass)) { + mergeAugmentation(parentBuilder, (Class>) typeClass, readValue); + } else { + mergeRegular(parentBuilder, readValue); + } + } + + private static void mergeRegular(@Nonnull final Builder parentBuilder, + @Nonnull final DataObject readValue) { + final Optional method = + ReflectionUtils.findMethodReflex(parentBuilder.getClass(), "set", + Collections.singletonList(readValue.getClass()), parentBuilder.getClass()); + + checkArgument(method.isPresent(), "Unable to set %s to %s", readValue, parentBuilder); + + try { + method.get().invoke(parentBuilder, readValue); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new IllegalArgumentException("Unable to set " + readValue + " to " + parentBuilder, e); + } + } + + private static void mergeAugmentation(@Nonnull final Builder parentBuilder, + @Nonnull final Class> typeClass, + @Nonnull final DataObject readValue) { + final Optional method = + ReflectionUtils.findMethodReflex(parentBuilder.getClass(), "addAugmentation", + Lists.newArrayList(Class.class, Augmentation.class), parentBuilder.getClass()); + + checkArgument(method.isPresent(), "Not possible to add augmentations to builder: %s", parentBuilder); + try { + method.get().invoke(parentBuilder, typeClass, readValue); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new IllegalArgumentException("Unable to set " + readValue + " to " + parentBuilder, e); + } + } + +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReader.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReader.java new file mode 100644 index 000000000..91a195f44 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReader.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.read.registry; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import io.fd.honeycomb.translate.read.ListReader; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.Reader; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.honeycomb.translate.util.read.AbstractGenericReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.Identifiable; +import org.opendaylight.yangtools.yang.binding.Identifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class CompositeReader> extends AbstractGenericReader { + + private static final Logger LOG = LoggerFactory.getLogger(CompositeReader.class); + + private final Reader delegate; + private final ImmutableMap, Reader>> childReaders; + + private CompositeReader(final Reader reader, + final ImmutableMap, Reader>> childReaders) { + super(reader.getManagedDataObjectType()); + this.delegate = reader; + this.childReaders = childReaders; + } + + @VisibleForTesting + ImmutableMap, Reader>> getChildReaders() { + return childReaders; + } + + @SuppressWarnings("unchecked") + public static InstanceIdentifier appendTypeToId( + final InstanceIdentifier parentId, final InstanceIdentifier type) { + final InstanceIdentifier.PathArgument t = new InstanceIdentifier.Item<>(type.getTargetType()); + return (InstanceIdentifier) InstanceIdentifier.create(Iterables.concat( + parentId.getPathArguments(), Collections.singleton(t))); + } + + @Nonnull + @Override + public Optional read(@Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext ctx) throws ReadFailedException { + if (shouldReadCurrent(id)) { + LOG.trace("{}: Reading current: {}", this, id); + return readCurrent((InstanceIdentifier) id, ctx); + } else if (shouldDelegateToChild(id)) { + LOG.trace("{}: Reading child: {}", this, id); + return readSubtree(id, ctx); + } else { + // Fallback + LOG.trace("{}: Delegating read: {}", this, id); + return delegate.read(id, ctx); + } + } + + private boolean shouldReadCurrent(@Nonnull final InstanceIdentifier id) { + return id.getTargetType().equals(getManagedDataObjectType().getTargetType()); + } + + private boolean shouldDelegateToChild(@Nonnull final InstanceIdentifier id) { + return childReaders.containsKey(RWUtils.getNextId(id, getManagedDataObjectType()).getType()); + } + + private Optional readSubtree(final InstanceIdentifier id, + final ReadContext ctx) throws ReadFailedException { + final InstanceIdentifier.PathArgument nextId = RWUtils.getNextId(id, getManagedDataObjectType()); + final Reader> nextReader = childReaders.get(nextId.getType()); + checkArgument(nextReader != null, "Unable to read: %s. No delegate present, available readers at next level: %s", + id, childReaders.keySet()); + return nextReader.read(id, ctx); + } + + @SuppressWarnings("unchecked") + private void readChildren(final InstanceIdentifier id, @Nonnull final ReadContext ctx, final B builder) + throws ReadFailedException { + LOG.debug("{}: Reading children: {}", this, childReaders.keySet()); + for (Reader child : childReaders.values()) { + final InstanceIdentifier childId = appendTypeToId(id, child.getManagedDataObjectType()); + + LOG.debug("{}: Reading child from: {}", this, child); + if (child instanceof ListReader) { + final List list = ((ListReader) child).readList(childId, ctx); + ((ListReader) child).merge(builder, list); + } else { + final Optional read = child.read(childId, ctx); + if (read.isPresent()) { + child.merge(builder, read.get()); + } + } + } + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final B builder, + @Nonnull final ReadContext ctx) + throws ReadFailedException { + delegate.readCurrentAttributes(id, builder, ctx); + readChildren(id, ctx, builder); + } + + @Nonnull + @Override + public B getBuilder(final InstanceIdentifier id) { + return delegate.getBuilder(id); + } + + @Override + public void merge(@Nonnull final Builder parentBuilder, @Nonnull final D readValue) { + delegate.merge(parentBuilder, readValue); + } + + /** + * Wrap a Reader as a Composite Reader. + */ + static > Reader createForReader( + @Nonnull final Reader reader, + @Nonnull final ImmutableMap, Reader>> childReaders) { + + return (reader instanceof ListReader) + ? new CompositeListReader<>((ListReader) reader, childReaders) + : new CompositeReader<>(reader, childReaders); + } + + private static class CompositeListReader, B extends Builder, K extends Identifier> + extends CompositeReader + implements ListReader { + + private final ListReader delegate; + + private CompositeListReader(final ListReader reader, + final ImmutableMap, Reader>> childReaders) { + super(reader, childReaders); + this.delegate = reader; + } + + @Nonnull + @Override + public List readList(@Nonnull final InstanceIdentifier id, @Nonnull final ReadContext ctx) + throws ReadFailedException { + LOG.trace("{}: Reading all list entries", this); + final List allIds = delegate.getAllIds(id, ctx); + LOG.debug("{}: Reading list entries for: {}", this, allIds); + + // Override read list in order to perform readCurrent + readChildren here + final ArrayList allEntries = new ArrayList<>(allIds.size()); + for (K key : allIds) { + final InstanceIdentifier.IdentifiableItem currentBdItem = RWUtils.getCurrentIdItem(id, key); + final InstanceIdentifier keyedId = RWUtils.replaceLastInId(id, currentBdItem); + final Optional read = readCurrent(keyedId, ctx); + if (read.isPresent()) { + final DataObject singleItem = read.get(); + checkArgument(getManagedDataObjectType().getTargetType().isAssignableFrom(singleItem.getClass())); + allEntries.add(getManagedDataObjectType().getTargetType().cast(singleItem)); + } + } + return allEntries; + } + + @Override + public void merge(@Nonnull final Builder builder, @Nonnull final List readData) { + delegate.merge(builder, readData); + } + + @Override + public List getAllIds(@Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext ctx) throws ReadFailedException { + return delegate.getAllIds(id, ctx); + } + } + +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReaderRegistry.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReaderRegistry.java new file mode 100644 index 000000000..ec3ce0605 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReaderRegistry.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.read.registry; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; +import com.google.common.collect.Iterables; +import com.google.common.collect.LinkedListMultimap; +import com.google.common.collect.Multimap; +import io.fd.honeycomb.translate.read.ListReader; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.Reader; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.read.registry.ReaderRegistry; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Simple reader registry able to perform and aggregated read (ROOT read) on top of all provided readers. Also able to + * delegate a specific read to one of the delegate readers. + *

+ * This could serve as a utility to hold & hide all available readers in upper layers. + */ +public final class CompositeReaderRegistry implements ReaderRegistry { + + private static final Logger LOG = LoggerFactory.getLogger(CompositeReaderRegistry.class); + + private final Map, Reader>> rootReaders; + + /** + * Create new {@link CompositeReaderRegistry}. + * + * @param rootReaders List of delegate readers + */ + public CompositeReaderRegistry(@Nonnull final List>> rootReaders) { + this.rootReaders = RWUtils.uniqueLinkedIndex(checkNotNull(rootReaders), RWUtils.MANAGER_CLASS_FUNCTION); + } + + @VisibleForTesting + Map, Reader>> getRootReaders() { + return rootReaders; + } + + @Override + @Nonnull + public Multimap, ? extends DataObject> readAll( + @Nonnull final ReadContext ctx) throws ReadFailedException { + + LOG.debug("Reading from all delegates: {}", this); + LOG.trace("Reading from all delegates: {}", rootReaders.values()); + + final Multimap, DataObject> objects = LinkedListMultimap.create(); + for (Reader> rootReader : rootReaders.values()) { + LOG.debug("Reading from delegate: {}", rootReader); + + if (rootReader instanceof ListReader) { + final List listEntries = + ((ListReader) rootReader).readList(rootReader.getManagedDataObjectType(), ctx); + if (!listEntries.isEmpty()) { + objects.putAll(rootReader.getManagedDataObjectType(), listEntries); + } + } else { + final Optional read = rootReader.read(rootReader.getManagedDataObjectType(), ctx); + if (read.isPresent()) { + objects.putAll(rootReader.getManagedDataObjectType(), Collections.singletonList(read.get())); + } + } + } + + return objects; + } + + @Nonnull + @Override + public Optional read(@Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext ctx) + throws ReadFailedException { + final InstanceIdentifier.PathArgument first = checkNotNull( + Iterables.getFirst(id.getPathArguments(), null), "Empty id"); + final Reader> reader = rootReaders.get(first.getType()); + checkNotNull(reader, + "Unable to read %s. Missing reader. Current readers for: %s", id, rootReaders.keySet()); + LOG.debug("Reading from delegate: {}", reader); + return reader.read(id, ctx); + } + + @Override + public String toString() { + return getClass().getSimpleName() + + rootReaders.keySet().stream().map(Class::getSimpleName).collect(Collectors.toList()); + } +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReaderRegistryBuilder.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReaderRegistryBuilder.java new file mode 100644 index 000000000..3e21bfca3 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReaderRegistryBuilder.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.read.registry; + +import com.google.common.collect.ImmutableMap; +import io.fd.honeycomb.translate.read.Reader; +import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder; +import io.fd.honeycomb.translate.read.registry.ReaderRegistryBuilder; +import io.fd.honeycomb.translate.util.read.ReflexiveReader; +import io.fd.honeycomb.translate.read.registry.ReaderRegistry; +import io.fd.honeycomb.translate.util.AbstractSubtreeManagerRegistryBuilderBuilder; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import javax.annotation.concurrent.NotThreadSafe; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@NotThreadSafe +public final class CompositeReaderRegistryBuilder + extends AbstractSubtreeManagerRegistryBuilderBuilder>, ReaderRegistry> + implements ModifiableReaderRegistryBuilder, ReaderRegistryBuilder { + + private static final Logger LOG = LoggerFactory.getLogger(CompositeReaderRegistryBuilder.class); + + @Override + protected Reader> getSubtreeHandler(@Nonnull final Set> handledChildren, + @Nonnull final Reader> reader) { + return SubtreeReader.createForReader(handledChildren, reader); + } + + @Override + public void addStructuralReader(@Nonnull InstanceIdentifier id, + @Nonnull Class> builderType) { + add(new ReflexiveReader<>(id, builderType)); + } + + /** + * Create {@link CompositeReaderRegistry} with Readers ordered according to submitted relationships. + *

+ * Note: The ordering only applies between nodes on the same level, inter-level and inter-subtree relationships are + * ignored. + */ + @Override + public ReaderRegistry build() { + ImmutableMap, Reader>> mappedReaders = + getMappedHandlers(); + LOG.debug("Building Reader registry with Readers: {}", + mappedReaders.keySet().stream() + .map(InstanceIdentifier::getTargetType) + .map(Class::getSimpleName) + .collect(Collectors.joining(", "))); + + LOG.trace("Building Reader registry with Readers: {}", mappedReaders); + final List> readerOrder = new ArrayList<>(mappedReaders.keySet()); + + // Wrap readers into composite readers recursively, collect roots and create registry + final TypeHierarchy typeHierarchy = TypeHierarchy.create(mappedReaders.keySet()); + final List>> orderedRootReaders = + typeHierarchy.getRoots().stream() + .map(rootId -> toCompositeReader(rootId, mappedReaders, typeHierarchy)) + .collect(Collectors.toList()); + + // We are violating the ordering from mappedReaders, since we are forming a composite structure + // but at least order root writers + orderedRootReaders.sort((reader1, reader2) -> readerOrder.indexOf(reader1.getManagedDataObjectType()) + - readerOrder.indexOf(reader2.getManagedDataObjectType())); + + return new CompositeReaderRegistry(orderedRootReaders); + } + + private Reader> toCompositeReader( + final InstanceIdentifier instanceIdentifier, + final ImmutableMap, Reader>> mappedReaders, + final TypeHierarchy typeHierarchy) { + + // Order child readers according to the mappedReadersCollection + final ImmutableMap.Builder, Reader>> childReadersMapB = ImmutableMap.builder(); + for (InstanceIdentifier childId : mappedReaders.keySet()) { + if (typeHierarchy.getDirectChildren(instanceIdentifier).contains(childId)) { + childReadersMapB.put(childId.getTargetType(), toCompositeReader(childId, mappedReaders, typeHierarchy)); + } + } + + final ImmutableMap, Reader>> childReadersMap = childReadersMapB.build(); + return childReadersMap.isEmpty() + ? mappedReaders.get(instanceIdentifier) + : CompositeReader.createForReader(mappedReaders.get(instanceIdentifier), childReadersMap); + } +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/SubtreeReader.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/SubtreeReader.java new file mode 100644 index 000000000..720bd0b11 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/SubtreeReader.java @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.read.registry; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import io.fd.honeycomb.translate.read.ListReader; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.read.Reader; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.honeycomb.translate.util.ReflectionUtils; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.Identifiable; +import org.opendaylight.yangtools.yang.binding.Identifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Simple Reader delegate for subtree Readers (Readers handling also children nodes) providing a list of all the + * children nodes being handled. + */ +class SubtreeReader> implements Reader { + + private static final Logger LOG = LoggerFactory.getLogger(SubtreeReader.class); + + private final Reader delegate; + private final Set> handledChildTypes = new HashSet<>(); + + private SubtreeReader(final Reader delegate, Set> handledTypes) { + this.delegate = delegate; + for (InstanceIdentifier handledType : handledTypes) { + // Iid has to start with Reader's handled root type + checkArgument(delegate.getManagedDataObjectType().getTargetType().equals( + handledType.getPathArguments().iterator().next().getType()), + "Handled node from subtree has to be identified by an instance identifier starting from: %s." + + "Instance identifier was: %s", getManagedDataObjectType().getTargetType(), handledType); + checkArgument(Iterables.size(handledType.getPathArguments()) > 1, + "Handled node from subtree identifier too short: %s", handledType); + handledChildTypes.add(InstanceIdentifier.create(Iterables.concat( + getManagedDataObjectType().getPathArguments(), Iterables.skip(handledType.getPathArguments(), 1)))); + } + } + + /** + * Return set of types also handled by this Reader. All of the types are children of the type managed by this Reader + * excluding the type of this Reader. + */ + Set> getHandledChildTypes() { + return handledChildTypes; + } + + @Override + @Nonnull + public Optional read( + @Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext ctx) throws ReadFailedException { + final InstanceIdentifier wildcarded = RWUtils.makeIidWildcarded(id); + + // Reading entire subtree and filtering if is current reader responsible + if (getHandledChildTypes().contains(wildcarded)) { + LOG.debug("{}: Subtree node managed by this writer requested: {}. Reading current and filtering", this, id); + // If there's no dedicated reader, use read current + final InstanceIdentifier currentId = RWUtils.cutId(id, getManagedDataObjectType()); + final Optional current = delegate.read(currentId, ctx); + // then perform post-reading filtering (return only requested sub-node) + final Optional readSubtree = current.isPresent() + ? filterSubtree(current.get(), id, getManagedDataObjectType().getTargetType()) + : current; + + LOG.debug("{}: Subtree: {} read successfully. Result: {}", this, id, readSubtree); + return readSubtree; + + // Fallback solution, try delegate, maybe it can read the ID + } else { + return delegate.read(id, ctx); + } + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final B builder, + @Nonnull final ReadContext ctx) + throws ReadFailedException { + delegate.readCurrentAttributes(id, builder, ctx); + } + + @Nonnull + @Override + public B getBuilder(final InstanceIdentifier id) { + return delegate.getBuilder(id); + } + + @Override + public void merge(@Nonnull final Builder parentBuilder, @Nonnull final D readValue) { + delegate.merge(parentBuilder, readValue); + } + + @Nonnull + private static Optional filterSubtree(@Nonnull final DataObject parent, + @Nonnull final InstanceIdentifier absolutPath, + @Nonnull final Class managedType) { + final InstanceIdentifier.PathArgument nextId = + RWUtils.getNextId(absolutPath, InstanceIdentifier.create(parent.getClass())); + + final Optional nextParent = findNextParent(parent, nextId, managedType); + + if (Iterables.getLast(absolutPath.getPathArguments()).equals(nextId)) { + return nextParent; // we found the dataObject identified by absolutePath + } else if (nextParent.isPresent()) { + return filterSubtree(nextParent.get(), absolutPath, nextId.getType()); + } else { + return nextParent; // we can't go further, return Optional.absent() + } + } + + private static Optional findNextParent(@Nonnull final DataObject parent, + @Nonnull final InstanceIdentifier.PathArgument nextId, + @Nonnull final Class managedType) { + // TODO is there a better way than reflection ? e.g. convert into NN and filter out with a utility + Optional method = ReflectionUtils.findMethodReflex(managedType, "get", + Collections.emptyList(), nextId.getType()); + + if (method.isPresent()) { + return Optional.fromNullable(filterSingle(parent, nextId, method.get())); + } else { + // List child nodes + method = ReflectionUtils.findMethodReflex(managedType, + "get" + nextId.getType().getSimpleName(), Collections.emptyList(), List.class); + + if (method.isPresent()) { + return filterList(parent, nextId, method.get()); + } else { + throw new IllegalStateException( + "Unable to filter " + nextId + " from " + parent + " getters not found using reflexion"); + } + } + } + + @SuppressWarnings("unchecked") + private static Optional filterList(final DataObject parent, + final InstanceIdentifier.PathArgument nextId, + final Method method) { + final List invoke = (List) invoke(method, nextId, parent); + + checkArgument(nextId instanceof InstanceIdentifier.IdentifiableItem, + "Unable to perform wildcarded read for %s", nextId); + final Identifier key = ((InstanceIdentifier.IdentifiableItem) nextId).getKey(); + // TODO replace with stream().filter().findFirst() when we switch to using java's Optional instead of Guava's + // because now we would have to do awkward Optional transformation since findFirstReturns guava's optional + return Iterables.tryFind(invoke, new Predicate() { + + @Override + public boolean apply(@Nullable final DataObject input) { + final Optional keyGetter = ReflectionUtils.findMethodReflex(nextId.getType(), "get", + Collections.emptyList(), key.getClass()); + final Object actualKey; + actualKey = invoke(keyGetter.get(), nextId, input); + return key.equals(actualKey); + } + }); + } + + private static DataObject filterSingle(final DataObject parent, + final InstanceIdentifier.PathArgument nextId, final Method method) { + return nextId.getType().cast(invoke(method, nextId, parent)); + } + + private static Object invoke(final Method method, + final InstanceIdentifier.PathArgument nextId, final DataObject parent) { + try { + return method.invoke(parent); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new IllegalArgumentException("Unable to get " + nextId + " from " + parent, e); + } + } + + @Override + @Nonnull + public InstanceIdentifier getManagedDataObjectType() { + return delegate.getManagedDataObjectType(); + } + + /** + * Wrap a Reader as a subtree Reader. + */ + static > Reader createForReader(@Nonnull final Set> handledChildren, + @Nonnull final Reader reader) { + return (reader instanceof ListReader) + ? new SubtreeListReader<>((ListReader) reader, handledChildren) + : new SubtreeReader<>(reader, handledChildren); + } + + private static final class SubtreeListReader, B extends Builder, K extends Identifier> + extends SubtreeReader implements ListReader { + + private final ListReader delegate; + + private SubtreeListReader(final ListReader delegate, + final Set> handledTypes) { + super(delegate, handledTypes); + this.delegate = delegate; + } + + @Nonnull + @Override + public List readList(@Nonnull final InstanceIdentifier id, @Nonnull final ReadContext ctx) + throws ReadFailedException { + return delegate.readList(id, ctx); + } + + @Override + public void merge(@Nonnull final Builder builder, @Nonnull final List readData) { + delegate.merge(builder, readData); + } + + @Override + public List getAllIds(@Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext ctx) throws ReadFailedException { + return delegate.getAllIds(id, ctx); + } + } + +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/TypeHierarchy.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/TypeHierarchy.java new file mode 100644 index 000000000..a30663221 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/TypeHierarchy.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.read.registry; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.collect.Iterables; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.jgrapht.experimental.dag.DirectedAcyclicGraph; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +final class TypeHierarchy { + private final DirectedAcyclicGraph, Parent> hierarchy; + + private TypeHierarchy(@Nonnull final DirectedAcyclicGraph, Parent> hierarchy) { + this.hierarchy = hierarchy; + } + + Set> getAllChildren(InstanceIdentifier id) { + final HashSet> instanceIdentifiers = new HashSet<>(); + for (InstanceIdentifier childId : getDirectChildren(id)) { + instanceIdentifiers.add(childId); + instanceIdentifiers.addAll(getAllChildren(childId)); + } + return instanceIdentifiers; + } + + Set> getDirectChildren(InstanceIdentifier id) { + checkArgument(hierarchy.vertexSet().contains(id), + "Unknown reader: %s. Known readers: %s", id, hierarchy.vertexSet()); + + return hierarchy.outgoingEdgesOf(id).stream() + .map(hierarchy::getEdgeTarget) + .collect(Collectors.toSet()); + } + + Set> getRoots() { + return hierarchy.vertexSet().stream() + .filter(vertex -> hierarchy.incomingEdgesOf(vertex).size() == 0) + .collect(Collectors.toSet()); + } + + /** + * Create reader hierarchy from a flat set of instance identifiers. + * + * @param allIds Set of unkeyed instance identifiers + */ + static TypeHierarchy create(@Nonnull Set> allIds) { + final DirectedAcyclicGraph, Parent> + readersHierarchy = new DirectedAcyclicGraph<>((sourceVertex, targetVertex) -> new Parent()); + + for (InstanceIdentifier allId : allIds) { + checkArgument(!Iterables.isEmpty(allId.getPathArguments()), "Empty ID detected"); + + if (Iterables.size(allId.getPathArguments()) == 1) { + readersHierarchy.addVertex(allId); + } + + List pathArgs = new LinkedList<>(); + pathArgs.add(allId.getPathArguments().iterator().next()); + + for (InstanceIdentifier.PathArgument pathArgument : Iterables.skip(allId.getPathArguments(), 1)) { + final InstanceIdentifier previous = InstanceIdentifier.create(pathArgs); + pathArgs.add(pathArgument); + final InstanceIdentifier current = InstanceIdentifier.create(pathArgs); + + readersHierarchy.addVertex(previous); + readersHierarchy.addVertex(current); + + try { + readersHierarchy.addDagEdge(previous, current); + } catch (DirectedAcyclicGraph.CycleFoundException e) { + throw new IllegalArgumentException("Loop in hierarchy detected", e); + } + } + } + + return new TypeHierarchy(readersHierarchy); + } + + private static final class Parent{} +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/AbstractGenericWriter.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/AbstractGenericWriter.java new file mode 100644 index 000000000..ff2174ed2 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/AbstractGenericWriter.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.write; + +import static com.google.common.base.Preconditions.checkArgument; + +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.honeycomb.translate.write.Writer; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class AbstractGenericWriter implements Writer { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractGenericWriter.class); + + private final InstanceIdentifier instanceIdentifier; + + protected AbstractGenericWriter(final InstanceIdentifier type) { + this.instanceIdentifier = RWUtils.makeIidWildcarded(type); + } + + protected void writeCurrent(final InstanceIdentifier id, final D data, final WriteContext ctx) + throws WriteFailedException { + LOG.debug("{}: Writing current: {} data: {}", this, id, data); + writeCurrentAttributes(id, data, ctx); + LOG.debug("{}: Current node written successfully", this); + } + + protected void updateCurrent(final InstanceIdentifier id, final D dataBefore, final D dataAfter, + final WriteContext ctx) throws WriteFailedException { + LOG.debug("{}: Updating current: {} dataBefore: {}, datAfter: {}", this, id, dataBefore, dataAfter); + + if (dataBefore.equals(dataAfter)) { + LOG.debug("{}: Skipping current(no update): {}", this, id); + // No change, ignore + return; + } + updateCurrentAttributes(id, dataBefore, dataAfter, ctx); + LOG.debug("{}: Current node updated successfully", this); + } + + protected void deleteCurrent(final InstanceIdentifier id, final D dataBefore, final WriteContext ctx) + throws WriteFailedException { + LOG.debug("{}: Deleting current: {} dataBefore: {}", this, id, dataBefore); + deleteCurrentAttributes(id, dataBefore, ctx); + } + + @SuppressWarnings("unchecked") + @Override + public void update(@Nonnull final InstanceIdentifier id, + @Nullable final DataObject dataBefore, + @Nullable final DataObject dataAfter, + @Nonnull final WriteContext ctx) throws WriteFailedException { + LOG.debug("{}: Updating : {}", this, id); + LOG.trace("{}: Updating : {}, from: {} to: {}", this, id, dataBefore, dataAfter); + + checkArgument(idPointsToCurrent(id), "Cannot handle data: %s. Only: %s can be handled by writer: %s", + id, getManagedDataObjectType(), this); + + if (isWrite(dataBefore, dataAfter)) { + writeCurrent((InstanceIdentifier) id, castToManaged(dataAfter), ctx); + } else if (isDelete(dataBefore, dataAfter)) { + deleteCurrent((InstanceIdentifier) id, castToManaged(dataBefore), ctx); + } else { + checkArgument(dataBefore != null && dataAfter != null, "No data to process"); + updateCurrent((InstanceIdentifier) id, castToManaged(dataBefore), castToManaged(dataAfter), ctx); + } + } + + private void checkDataType(@Nonnull final DataObject dataAfter) { + checkArgument(getManagedDataObjectType().getTargetType().isAssignableFrom(dataAfter.getClass())); + } + + private D castToManaged(final DataObject data) { + checkDataType(data); + return getManagedDataObjectType().getTargetType().cast(data); + } + + private static boolean isWrite(final DataObject dataBefore, + final DataObject dataAfter) { + return dataBefore == null && dataAfter != null; + } + + private static boolean isDelete(final DataObject dataBefore, + final DataObject dataAfter) { + return dataAfter == null && dataBefore != null; + } + + private boolean idPointsToCurrent(final @Nonnull InstanceIdentifier id) { + return id.getTargetType().equals(getManagedDataObjectType().getTargetType()); + } + + protected abstract void writeCurrentAttributes(@Nonnull final InstanceIdentifier id, + @Nonnull final D data, + @Nonnull final WriteContext ctx) throws WriteFailedException; + + protected abstract void deleteCurrentAttributes(@Nonnull final InstanceIdentifier id, + @Nonnull final D dataBefore, + @Nonnull final WriteContext ctx) throws WriteFailedException; + + protected abstract void updateCurrentAttributes(@Nonnull final InstanceIdentifier id, + @Nonnull final D dataBefore, + @Nonnull final D dataAfter, + @Nonnull final WriteContext ctx) throws WriteFailedException; + + @Nonnull + @Override + public InstanceIdentifier getManagedDataObjectType() { + return instanceIdentifier; + } + + + @Override + public String toString() { + return String.format("Writer[%s]", getManagedDataObjectType().getTargetType().getSimpleName()); + } +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/NoopWriterRegistry.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/NoopWriterRegistry.java new file mode 100644 index 000000000..8f15c6f8d --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/NoopWriterRegistry.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.write; + +import io.fd.honeycomb.translate.TranslationException; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.registry.WriterRegistry; +import javax.annotation.Nonnull; + +/** + * Empty registry that does not perform any changes. Can be used in data layer, if we want to disable passing data to + * translation layer. + */ +public class NoopWriterRegistry implements WriterRegistry, AutoCloseable { + + @Override + public void update(@Nonnull final DataObjectUpdates updates, + @Nonnull final WriteContext ctx) throws TranslationException { + // NOOP + } + + @Override + public void close() throws Exception { + // NOOP + } +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/TransactionWriteContext.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/TransactionWriteContext.java new file mode 100644 index 000000000..51f999294 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/TransactionWriteContext.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.write; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import io.fd.honeycomb.translate.MappingContext; +import io.fd.honeycomb.translate.ModificationCache; +import io.fd.honeycomb.translate.write.WriteContext; +import java.util.Map; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +/** + * Transaction based WriteContext + */ +public final class TransactionWriteContext implements WriteContext { + + private final DOMDataReadOnlyTransaction beforeTx; + private final DOMDataReadOnlyTransaction afterTx; + private final ModificationCache ctx; + private final BindingNormalizedNodeSerializer serializer; + private final MappingContext mappingContext; + + public TransactionWriteContext(final BindingNormalizedNodeSerializer serializer, + final DOMDataReadOnlyTransaction beforeTx, + final DOMDataReadOnlyTransaction afterTx, + final MappingContext mappingContext) { + this.serializer = serializer; + // TODO do we have a BA transaction adapter ? If so, use it here and don't pass serializer + this.beforeTx = beforeTx; + this.afterTx = afterTx; + this.mappingContext = mappingContext; + this.ctx = new ModificationCache(); + } + + // TODO make this asynchronous + + @Override + public Optional readBefore(@Nonnull final InstanceIdentifier currentId) { + return read(currentId, beforeTx); + } + + @Override + public Optional readAfter(@Nonnull final InstanceIdentifier currentId) { + return read(currentId, afterTx); + } + + + private Optional read(final InstanceIdentifier currentId, + final DOMDataReadOnlyTransaction tx) { + final YangInstanceIdentifier path = serializer.toYangInstanceIdentifier(currentId); + + final CheckedFuture>, ReadFailedException> read = + tx.read(LogicalDatastoreType.CONFIGURATION, path); + + try { + // TODO once the APIs are asynchronous use just Futures.transform + final Optional> optional = read.checkedGet(); + + if (!optional.isPresent()) { + return Optional.absent(); + } + + final NormalizedNode data = optional.get(); + final Map.Entry, DataObject> entry = serializer.fromNormalizedNode(path, data); + + final Class targetType = currentId.getTargetType(); + checkState(targetType.isAssignableFrom(entry.getValue().getClass()), + "Unexpected data object type, should be: %s, but was: %s", targetType, entry.getValue().getClass()); + return Optional.of(targetType.cast(entry.getValue())); + } catch (ReadFailedException e) { + throw new IllegalStateException("Unable to perform read", e); + } + } + + @Nonnull + @Override + public ModificationCache getModificationCache() { + return ctx; + } + + @Nonnull + @Override + public MappingContext getMappingContext() { + return mappingContext; + } + + /** + * Does not close the transactions + */ + @Override + public void close() { + ctx.close(); + beforeTx.close(); + afterTx.close(); + } +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/registry/FlatWriterRegistry.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/registry/FlatWriterRegistry.java new file mode 100644 index 000000000..df8ec107b --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/registry/FlatWriterRegistry.java @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.write.registry; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.base.Optional; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import io.fd.honeycomb.translate.TranslationException; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.honeycomb.translate.write.DataObjectUpdate; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.honeycomb.translate.write.Writer; +import io.fd.honeycomb.translate.write.registry.WriterRegistry; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.ThreadSafe; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Flat writer registry, delegating updates to writers in the order writers were submitted. + */ +@ThreadSafe +final class FlatWriterRegistry implements WriterRegistry { + + private static final Logger LOG = LoggerFactory.getLogger(FlatWriterRegistry.class); + + // All types handled by writers directly or as children + private final ImmutableSet> handledTypes; + + private final Set> writersOrderReversed; + private final Set> writersOrder; + private final Map, Writer> writers; + + /** + * Create flat registry instance. + * + * @param writers immutable, ordered map of writers to use to process updates. Order of the writers has to be + * one in which create and update operations should be handled. Deletes will be handled in reversed + * order. All deletes are handled before handling all the updates. + */ + FlatWriterRegistry(@Nonnull final ImmutableMap, Writer> writers) { + this.writers = writers; + this.writersOrderReversed = Sets.newLinkedHashSet(Lists.reverse(Lists.newArrayList(writers.keySet()))); + this.writersOrder = writers.keySet(); + this.handledTypes = getAllHandledTypes(writers); + } + + private static ImmutableSet> getAllHandledTypes( + @Nonnull final ImmutableMap, Writer> writers) { + final ImmutableSet.Builder> handledTypesBuilder = ImmutableSet.builder(); + for (Map.Entry, Writer> writerEntry : writers.entrySet()) { + final InstanceIdentifier writerType = writerEntry.getKey(); + final Writer writer = writerEntry.getValue(); + handledTypesBuilder.add(writerType); + if (writer instanceof SubtreeWriter) { + handledTypesBuilder.addAll(((SubtreeWriter) writer).getHandledChildTypes()); + } + } + return handledTypesBuilder.build(); + } + + @Override + public void update(@Nonnull final DataObjectUpdates updates, + @Nonnull final WriteContext ctx) throws TranslationException { + if (updates.isEmpty()) { + return; + } + + // Optimization + if (updates.containsOnlySingleType()) { + // First process delete + singleUpdate(updates.getDeletes(), ctx); + // Next is update + singleUpdate(updates.getUpdates(), ctx); + } else { + // First process deletes + bulkUpdate(updates.getDeletes(), ctx, true, writersOrderReversed); + // Next are updates + bulkUpdate(updates.getUpdates(), ctx, true, writersOrder); + } + + LOG.debug("Update successful for types: {}", updates.getTypeIntersection()); + LOG.trace("Update successful for: {}", updates); + } + + private void singleUpdate(@Nonnull final Multimap, ? extends DataObjectUpdate> updates, + @Nonnull final WriteContext ctx) throws WriteFailedException { + if (updates.isEmpty()) { + return; + } + + final InstanceIdentifier singleType = updates.keySet().iterator().next(); + LOG.debug("Performing single type update for: {}", singleType); + Collection singleTypeUpdates = updates.get(singleType); + Writer writer = getWriter(singleType); + + if (writer == null) { + // This node must be handled by a subtree writer, find it and call it or else fail + checkArgument(handledTypes.contains(singleType), "Unable to process update. Missing writers for: %s", + singleType); + writer = getSubtreeWriterResponsible(singleType); + singleTypeUpdates = getParentDataObjectUpdate(ctx, updates, writer); + } + + LOG.trace("Performing single type update with writer: {}", writer); + for (DataObjectUpdate singleUpdate : singleTypeUpdates) { + writer.update(singleUpdate.getId(), singleUpdate.getDataBefore(), singleUpdate.getDataAfter(), ctx); + } + } + + private Writer getSubtreeWriterResponsible(final InstanceIdentifier singleType) { + final Writer writer;// This is slow ( minor TODO-perf ) + writer = writers.values().stream() + .filter(w -> w instanceof SubtreeWriter) + .filter(w -> ((SubtreeWriter) w).getHandledChildTypes().contains(singleType)) + .findFirst() + .get(); + return writer; + } + + private Collection getParentDataObjectUpdate(final WriteContext ctx, + final Multimap, ? extends DataObjectUpdate> updates, + final Writer writer) { + // Now read data for subtree reader root, but first keyed ID is needed and that ID can be cut from updates + InstanceIdentifier firstAffectedChildId = ((SubtreeWriter) writer).getHandledChildTypes().stream() + .filter(updates::containsKey) + .map(unkeyedId -> updates.get(unkeyedId)) + .flatMap(doUpdates -> doUpdates.stream()) + .map(DataObjectUpdate::getId) + .findFirst() + .get(); + + final InstanceIdentifier parentKeyedId = + RWUtils.cutId(firstAffectedChildId, writer.getManagedDataObjectType()); + + final Optional parentBefore = ctx.readBefore(parentKeyedId); + final Optional parentAfter = ctx.readAfter(parentKeyedId); + return Collections.singleton( + DataObjectUpdate.create(parentKeyedId, parentBefore.orNull(), parentAfter.orNull())); + } + + private void bulkUpdate(@Nonnull final Multimap, ? extends DataObjectUpdate> updates, + @Nonnull final WriteContext ctx, + final boolean attemptRevert, + @Nonnull final Set> writersOrder) throws BulkUpdateException { + if (updates.isEmpty()) { + return; + } + + LOG.debug("Performing bulk update with revert attempt: {} for: {}", attemptRevert, updates.keySet()); + + // Check that all updates can be handled + checkAllTypesCanBeHandled(updates); + + // Capture all changes successfully processed in case revert is needed + final Set> processedNodes = new HashSet<>(); + + // Iterate over all writers and call update if there are any related updates + for (InstanceIdentifier writerType : writersOrder) { + Collection 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) { + try { + writer.update(singleUpdate.getId(), singleUpdate.getDataBefore(), singleUpdate.getDataAfter(), ctx); + processedNodes.add(singleUpdate.getId()); + LOG.trace("Update successful for type: {}", writerType); + LOG.debug("Update successful for: {}", singleUpdate); + } catch (Exception e) { + LOG.error("Error while processing data change of: {} (updates={})", writerType, writersData, e); + + final Reverter reverter = attemptRevert + ? new ReverterImpl(processedNodes, updates, writersOrder, ctx) + : () -> {}; // NOOP reverter + + // Find out which changes left unprocessed + final Set> unprocessedChanges = updates.values().stream() + .map(DataObjectUpdate::getId) + .filter(id -> !processedNodes.contains(id)) + .collect(Collectors.toSet()); + throw new BulkUpdateException(unprocessedChanges, reverter, e); + } + } + } + } + + private void checkAllTypesCanBeHandled( + @Nonnull final Multimap, ? extends DataObjectUpdate> updates) { + if (!handledTypes.containsAll(updates.keySet())) { + final Sets.SetView> missingWriters = Sets.difference(updates.keySet(), handledTypes); + LOG.warn("Unable to process update. Missing writers for: {}", missingWriters); + throw new IllegalArgumentException("Unable to process update. Missing writers for: " + missingWriters); + } + } + + /** + * Check whether {@link SubtreeWriter} is affected by the updates. + * + * @return true if there are any updates to SubtreeWriter's child nodes (those marked by SubtreeWriter + * as being taken care of) + * */ + private static boolean isAffected(final SubtreeWriter writer, + final Multimap, ? extends DataObjectUpdate> updates) { + return !Sets.intersection(writer.getHandledChildTypes(), updates.keySet()).isEmpty(); + } + + @Nullable + private Writer getWriter(@Nonnull final InstanceIdentifier singleType) { + return writers.get(singleType); + } + + // FIXME unit test + private final class ReverterImpl implements Reverter { + + private final Collection> processedNodes; + private final Multimap, ? extends DataObjectUpdate> updates; + private final Set> revertDeleteOrder; + private final WriteContext ctx; + + ReverterImpl(final Collection> processedNodes, + final Multimap, ? extends DataObjectUpdate> updates, + final Set> writersOrderOriginal, + final WriteContext ctx) { + this.processedNodes = processedNodes; + this.updates = updates; + // Use opposite ordering when executing revert + this.revertDeleteOrder = writersOrderOriginal == FlatWriterRegistry.this.writersOrder + ? FlatWriterRegistry.this.writersOrderReversed + : FlatWriterRegistry.this.writersOrder; + this.ctx = ctx; + } + + @Override + public void revert() throws RevertFailedException { + Multimap, DataObjectUpdate> updatesToRevert = + filterAndRevertProcessed(updates, processedNodes); + + LOG.info("Attempting revert for changes: {}", updatesToRevert); + try { + // Perform reversed bulk update without revert attempt + bulkUpdate(updatesToRevert, ctx, true, revertDeleteOrder); + LOG.info("Revert successful"); + } catch (BulkUpdateException e) { + LOG.error("Revert failed", e); + throw new RevertFailedException(e.getFailedIds(), e); + } + } + + /** + * Create new updates map, but only keep already processed changes. Switching before and after data for each + * update. + */ + private Multimap, DataObjectUpdate> filterAndRevertProcessed( + final Multimap, ? extends DataObjectUpdate> updates, + final Collection> processedNodes) { + final Multimap, DataObjectUpdate> filtered = HashMultimap.create(); + for (InstanceIdentifier processedNode : processedNodes) { + final InstanceIdentifier wildcardedIid = RWUtils.makeIidWildcarded(processedNode); + if (updates.containsKey(wildcardedIid)) { + updates.get(wildcardedIid).stream() + .filter(dataObjectUpdate -> processedNode.contains(dataObjectUpdate.getId())) + .forEach(dataObjectUpdate -> filtered.put(processedNode, dataObjectUpdate.reverse())); + } + } + return filtered; + } + } + +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/registry/FlatWriterRegistryBuilder.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/registry/FlatWriterRegistryBuilder.java new file mode 100644 index 000000000..0f75de7e7 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/registry/FlatWriterRegistryBuilder.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.write.registry; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableMap; +import io.fd.honeycomb.translate.write.registry.WriterRegistryBuilder; +import io.fd.honeycomb.translate.util.AbstractSubtreeManagerRegistryBuilderBuilder; +import io.fd.honeycomb.translate.write.Writer; +import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder; +import io.fd.honeycomb.translate.write.registry.WriterRegistry; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import javax.annotation.concurrent.NotThreadSafe; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Builder for {@link FlatWriterRegistry} allowing users to specify inter-writer relationships. + */ +@NotThreadSafe +public final class FlatWriterRegistryBuilder + extends AbstractSubtreeManagerRegistryBuilderBuilder, WriterRegistry> + implements ModifiableWriterRegistryBuilder, WriterRegistryBuilder { + + private static final Logger LOG = LoggerFactory.getLogger(FlatWriterRegistryBuilder.class); + + @Override + protected Writer getSubtreeHandler(final @Nonnull Set> handledChildren, + final @Nonnull Writer writer) { + return SubtreeWriter.createForWriter(handledChildren, writer); + } + + /** + * Create FlatWriterRegistry with writers ordered according to submitted relationships. + */ + @Override + public WriterRegistry build() { + final ImmutableMap, Writer> mappedWriters = getMappedHandlers(); + LOG.debug("Building writer registry with writers: {}", + mappedWriters.keySet().stream() + .map(InstanceIdentifier::getTargetType) + .map(Class::getSimpleName) + .collect(Collectors.joining(", "))); + LOG.trace("Building writer registry with writers: {}", mappedWriters); + return new FlatWriterRegistry(mappedWriters); + } + + @VisibleForTesting + @Override + protected ImmutableMap, Writer> getMappedHandlers() { + return super.getMappedHandlers(); + } +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/registry/SubtreeWriter.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/registry/SubtreeWriter.java new file mode 100644 index 000000000..bab1da16f --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/registry/SubtreeWriter.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.write.registry; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.collect.Iterables; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.honeycomb.translate.write.Writer; +import java.util.HashSet; +import java.util.Set; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Simple writer delegate for subtree writers (writers handling also children nodes) providing a list of all the + * children nodes being handled. + */ +final class SubtreeWriter implements Writer { + + private final Writer delegate; + private final Set> handledChildTypes = new HashSet<>(); + + private SubtreeWriter(final Writer delegate, Set> handledTypes) { + this.delegate = delegate; + for (InstanceIdentifier handledType : handledTypes) { + // Iid has to start with writer's handled root type + checkArgument(delegate.getManagedDataObjectType().getTargetType().equals( + handledType.getPathArguments().iterator().next().getType()), + "Handled node from subtree has to be identified by an instance identifier starting from: %s." + + "Instance identifier was: %s", getManagedDataObjectType().getTargetType(), handledType); + checkArgument(Iterables.size(handledType.getPathArguments()) > 1, + "Handled node from subtree identifier too short: %s", handledType); + handledChildTypes.add(InstanceIdentifier.create(Iterables.concat( + getManagedDataObjectType().getPathArguments(), Iterables.skip(handledType.getPathArguments(), 1)))); + } + } + + /** + * Return set of types also handled by this writer. All of the types are children of the type managed by this + * writer excluding the type of this writer. + */ + Set> getHandledChildTypes() { + return handledChildTypes; + } + + @Override + public void update( + @Nonnull final InstanceIdentifier id, + @Nullable final DataObject dataBefore, + @Nullable final DataObject dataAfter, @Nonnull final WriteContext ctx) throws WriteFailedException { + delegate.update(id, dataBefore, dataAfter, ctx); + } + + @Override + @Nonnull + public InstanceIdentifier getManagedDataObjectType() { + return delegate.getManagedDataObjectType(); + } + + /** + * Wrap a writer as a subtree writer. + */ + static Writer createForWriter(@Nonnull final Set> handledChildren, + @Nonnull final Writer writer) { + return new SubtreeWriter<>(writer, handledChildren); + } +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/AbstractSubtreeManagerRegistryBuilderBuilder.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/AbstractSubtreeManagerRegistryBuilderBuilder.java deleted file mode 100644 index 23a66337f..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/AbstractSubtreeManagerRegistryBuilderBuilder.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util; - -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Sets; -import io.fd.honeycomb.v3po.translate.ModifiableSubtreeManagerRegistryBuilder; -import io.fd.honeycomb.v3po.translate.SubtreeManager; -import io.fd.honeycomb.v3po.translate.SubtreeManagerRegistryBuilder; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import javax.annotation.Nonnull; -import org.jgrapht.experimental.dag.DirectedAcyclicGraph; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -public abstract class AbstractSubtreeManagerRegistryBuilderBuilder, R> - implements ModifiableSubtreeManagerRegistryBuilder, SubtreeManagerRegistryBuilder, AutoCloseable { - - // Using directed acyclic graph to represent the ordering relationships between writers - private final DirectedAcyclicGraph, Order> - handlersRelations = new DirectedAcyclicGraph<>((sourceVertex, targetVertex) -> new Order()); - private final Map, S> handlersMap = new HashMap<>(); - - /** - * Add handler without any special relationship to any other type. - */ - @Override - public AbstractSubtreeManagerRegistryBuilderBuilder add(@Nonnull final S handler) { - // Make IID wildcarded just in case - // + the way InstanceIdentifier.create + equals work for Identifiable items is unexpected, meaning updates would - // not be matched to writers in registry - final InstanceIdentifier targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType()); - checkWriterNotPresentYet(targetType); - handlersRelations.addVertex(targetType); - handlersMap.put(targetType, handler); - return this; - } - - /** - * Add handler without any special relationship to any other type. - */ - @Override - public AbstractSubtreeManagerRegistryBuilderBuilder subtreeAdd(@Nonnull final Set> handledChildren, - @Nonnull final S handler) { - add(getSubtreeHandler(handledChildren, handler)); - return this; - } - - private void checkWriterNotPresentYet(final InstanceIdentifier targetType) { - Preconditions.checkArgument(!handlersMap.containsKey(targetType), - "Writer for type: %s already present: %s", targetType, handlersMap.get(targetType)); - } - - /** - * Add handler with relationship: to be executed before handler handling relatedType. - */ - @Override - public AbstractSubtreeManagerRegistryBuilderBuilder addBefore(@Nonnull final S handler, - @Nonnull final InstanceIdentifier relatedType) { - final InstanceIdentifier targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType()); - final InstanceIdentifier wildcardedRelatedType = RWUtils.makeIidWildcarded(relatedType); - checkWriterNotPresentYet(targetType); - handlersRelations.addVertex(targetType); - handlersRelations.addVertex(wildcardedRelatedType); - addEdge(targetType, wildcardedRelatedType); - handlersMap.put(targetType, handler); - return this; - } - - @Override - public AbstractSubtreeManagerRegistryBuilderBuilder addBefore(@Nonnull final S handler, - @Nonnull final Collection> relatedTypes) { - final InstanceIdentifier targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType()); - checkWriterNotPresentYet(targetType); - handlersRelations.addVertex(targetType); - relatedTypes.stream() - .map(RWUtils::makeIidWildcarded) - .forEach(handlersRelations::addVertex); - relatedTypes.stream() - .map(RWUtils::makeIidWildcarded) - .forEach(type -> addEdge(targetType, type)); - handlersMap.put(targetType, handler); - return this; - } - - @Override - public AbstractSubtreeManagerRegistryBuilderBuilder subtreeAddBefore( - @Nonnull final Set> handledChildren, - @Nonnull final S handler, - @Nonnull final InstanceIdentifier relatedType) { - return addBefore(getSubtreeHandler(handledChildren, handler), relatedType); - } - - @Override - public AbstractSubtreeManagerRegistryBuilderBuilder subtreeAddBefore( - @Nonnull final Set> handledChildren, - @Nonnull final S handler, - @Nonnull final Collection> relatedTypes) { - return addBefore(getSubtreeHandler(handledChildren, handler), relatedTypes); - } - - protected abstract S getSubtreeHandler(@Nonnull final Set> handledChildren, - @Nonnull final S handler); - - /** - * Add handler with relationship: to be executed after handler handling relatedType. - */ - @Override - public AbstractSubtreeManagerRegistryBuilderBuilder addAfter(@Nonnull final S handler, - @Nonnull final InstanceIdentifier relatedType) { - final InstanceIdentifier targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType()); - final InstanceIdentifier wildcardedRelatedType = RWUtils.makeIidWildcarded(relatedType); - checkWriterNotPresentYet(targetType); - handlersRelations.addVertex(targetType); - handlersRelations.addVertex(wildcardedRelatedType); - // set edge to indicate before relationship, just reversed - addEdge(wildcardedRelatedType, targetType); - handlersMap.put(targetType, handler); - return this; - } - - @Override - public AbstractSubtreeManagerRegistryBuilderBuilder addAfter(@Nonnull final S handler, - @Nonnull final Collection> relatedTypes) { - final InstanceIdentifier targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType()); - checkWriterNotPresentYet(targetType); - handlersRelations.addVertex(targetType); - relatedTypes.stream() - .map(RWUtils::makeIidWildcarded) - .forEach(handlersRelations::addVertex); - // set edge to indicate before relationship, just reversed - relatedTypes.stream() - .map(RWUtils::makeIidWildcarded) - .forEach(type -> addEdge(type, targetType)); - handlersMap.put(targetType, handler); - return this; - } - - @Override - public AbstractSubtreeManagerRegistryBuilderBuilder subtreeAddAfter( - @Nonnull final Set> handledChildren, - @Nonnull final S handler, - @Nonnull final InstanceIdentifier relatedType) { - return addAfter(getSubtreeHandler(handledChildren, handler), relatedType); - } - - @Override - public AbstractSubtreeManagerRegistryBuilderBuilder subtreeAddAfter( - @Nonnull final Set> handledChildren, - @Nonnull final S handler, - @Nonnull final Collection> relatedTypes) { - return addAfter(getSubtreeHandler(handledChildren, handler), relatedTypes); - } - - - private void addEdge(final InstanceIdentifier targetType, - final InstanceIdentifier relatedType) { - try { - handlersRelations.addDagEdge(targetType, relatedType); - } catch (DirectedAcyclicGraph.CycleFoundException e) { - throw new IllegalArgumentException(String.format( - "Unable to add writer with relation: %s -> %s. Loop detected", targetType, relatedType), e); - } - } - - protected ImmutableMap, S> getMappedHandlers() { - final ImmutableMap.Builder, S> builder = ImmutableMap.builder(); - // Iterate writer types according to their relationships from graph - handlersRelations.iterator() - .forEachRemaining(writerType -> { - // There might be types stored just for relationship sake, no real writer, ignoring those - if (handlersMap.containsKey(writerType)) { - builder.put(writerType, handlersMap.get(writerType)); - } - }); - - // TODO we could optimize subtree handlers, if there is a dedicated handler for a node managed by a subtree - // handler, recreate the subtree handler with a subset of handled child nodes - // This way it is not necessary to change the configuration of subtree writer, just to add a dedicated child - // writer. This will be needed if we ever switch to annotations for reader/writer hierarchy initialization - - return builder.build(); - } - - @Override - public void close() throws Exception { - handlersMap.clear(); - // Wrap sets into another set to avoid concurrent modification ex in graph - handlersRelations.removeAllEdges(Sets.newHashSet(handlersRelations.edgeSet())); - handlersRelations.removeAllVertices(Sets.newHashSet(handlersRelations.vertexSet())); - } - - // Represents edges in graph - private class Order {} -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/JsonUtils.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/JsonUtils.java deleted file mode 100644 index d9798d07d..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/JsonUtils.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util; - -import com.google.common.base.Charsets; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; -import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter; -import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactory; -import org.opendaylight.yangtools.yang.data.codec.gson.JSONNormalizedNodeStreamWriter; -import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream; -import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory; -import org.opendaylight.yangtools.yang.data.impl.schema.Builders; -import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter; -import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.yangtools.yang.model.api.SchemaPath; - -public final class JsonUtils { - - private JsonUtils() {} - - /** - * Serialize normalized node root structure into provided output stream - * - * @throws IOException if serialized data cannot be written into provided output stream - */ - public static void writeJsonRoot(@Nonnull final NormalizedNode rootData, - @Nonnull final SchemaContext schemaContext, - @Nonnull final OutputStream outputStream) throws IOException { - final JsonWriter - jsonWriter = createJsonWriter(outputStream, true); - final NormalizedNodeStreamWriter streamWriter = JSONNormalizedNodeStreamWriter - .createNestedWriter(JSONCodecFactory.create(schemaContext), SchemaPath.ROOT, null, jsonWriter); - final NormalizedNodeWriter normalizedNodeWriter = - NormalizedNodeWriter.forStreamWriter(streamWriter, true); - jsonWriter.beginObject(); - writeChildren(normalizedNodeWriter,(ContainerNode) rootData); - jsonWriter.endObject(); - jsonWriter.flush(); - } - - /** - * Read json serialized normalized node root structure and parse them into normalized nodes - * - * @return artificial normalized node holding all the top level nodes from provided stream as children. In case - * the stream is empty, empty artificial normalized node is returned - * - * @throws IllegalArgumentException if content in the provided input stream is not restore-able - */ - public static ContainerNode readJsonRoot(@Nonnull final SchemaContext schemaContext, - @Nonnull final InputStream stream) { - final DataContainerNodeAttrBuilder builder = - Builders.containerBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(schemaContext.getQName())); - final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(builder); - - final JsonParserStream jsonParser = JsonParserStream.create(writer, schemaContext); - final JsonReader reader = new JsonReader(new InputStreamReader(stream, Charsets.UTF_8)); - jsonParser.parse(reader); - - return builder.build(); - } - - private static void writeChildren(final NormalizedNodeWriter nnWriter, final ContainerNode data) throws IOException { - for(final DataContainerChild child : data.getValue()) { - nnWriter.write(child); - } - } - - private static JsonWriter createJsonWriter(final OutputStream entityStream, boolean prettyPrint) { - if (prettyPrint) { - return JsonWriterFactory.createJsonWriter(new OutputStreamWriter(entityStream, Charsets.UTF_8), 2); - } else { - return JsonWriterFactory.createJsonWriter(new OutputStreamWriter(entityStream, Charsets.UTF_8)); - } - } -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java deleted file mode 100644 index 2a565d9f2..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util; - -import com.google.common.base.Function; -import com.google.common.base.Preconditions; -import com.google.common.base.Predicate; -import com.google.common.collect.Iterables; -import com.google.common.collect.Maps; -import io.fd.honeycomb.v3po.translate.SubtreeManager; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.stream.Collector; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.yang.binding.Augmentation; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.Identifiable; -import org.opendaylight.yangtools.yang.binding.Identifier; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -public final class RWUtils { - - private RWUtils() {} - - /** - * Collector expecting only a single resulting item from a stream - */ - public static Collector singleItemCollector() { - return Collectors.collectingAndThen( - Collectors.toList(), - list -> { - if (list.size() != 1) { - throw new IllegalStateException("Unexpected size of list: " + list + ". Single item expected"); - } - return list.get(0); - } - ); - } - - /** - * Find next item in ID after provided type - */ - @Nonnull - public static InstanceIdentifier.PathArgument getNextId(@Nonnull final InstanceIdentifier id, - @Nonnull final InstanceIdentifier type) { - // TODO this is inefficient(maybe, depending on actual Iterable type) - final Iterable pathArguments = id.getPathArguments(); - final int i = Iterables.indexOf(pathArguments, new Predicate() { - @Override - public boolean apply(final InstanceIdentifier.PathArgument input) { - return input.getType().isAssignableFrom(type.getTargetType()); - } - }); - Preconditions.checkArgument(i >= 0, "Unable to find %s type in %s", type.getTargetType(), id); - return Iterables.get(pathArguments, i + 1); - } - - /** - * Replace last item in ID with a provided IdentifiableItem of the same type - */ - @SuppressWarnings("unchecked") - @Nonnull - public static , K extends Identifier> InstanceIdentifier replaceLastInId( - @Nonnull final InstanceIdentifier id, final InstanceIdentifier.IdentifiableItem currentBdItem) { - - final Iterable pathArguments = id.getPathArguments(); - final Iterable withoutCurrent = - Iterables.limit(pathArguments, Iterables.size(pathArguments) - 1); - final Iterable concat = - Iterables.concat(withoutCurrent, Collections.singleton(currentBdItem)); - return (InstanceIdentifier) InstanceIdentifier.create(concat); - } - - /** - * Create IdentifiableItem from target type of provided ID with provided key - */ - @Nonnull - public static , K extends Identifier> InstanceIdentifier.IdentifiableItem getCurrentIdItem( - @Nonnull final InstanceIdentifier id, final K key) { - return new InstanceIdentifier.IdentifiableItem<>(id.getTargetType(), key); - } - - /** - * Trim InstanceIdentifier at indexOf(type) - */ - @SuppressWarnings("unchecked") - @Nonnull - public static InstanceIdentifier cutId(@Nonnull final InstanceIdentifier id, - @Nonnull final InstanceIdentifier type) { - final Iterable pathArguments = id.getPathArguments(); - final int i = Iterables.indexOf(pathArguments, new Predicate() { - @Override - public boolean apply(final InstanceIdentifier.PathArgument input) { - return input.getType().equals(type.getTargetType()); - } - }); - Preconditions.checkArgument(i >= 0, "ID %s does not contain %s", id, type); - return (InstanceIdentifier) InstanceIdentifier.create(Iterables.limit(pathArguments, i + 1)); - } - - /** - * Create an ordered map from a collection, checking for duplicity in the process. - */ - @Nonnull - public static Map uniqueLinkedIndex(@Nonnull final Collection values, @Nonnull final Function keyFunction) { - final Map objectObjectLinkedHashMap = Maps.newLinkedHashMap(); - for (V value : values) { - final K key = keyFunction.apply(value); - Preconditions.checkArgument(objectObjectLinkedHashMap.put(key, value) == null, - "Duplicate key detected : %s", key); - } - return objectObjectLinkedHashMap; - } - - public static final Function, Class> - MANAGER_CLASS_FUNCTION = new Function, Class>() { - @Override - public Class apply(final SubtreeManager input) { - return input.getManagedDataObjectType().getTargetType(); - } - }; - - public static final Function>, Class> - MANAGER_CLASS_AUG_FUNCTION = new Function>, Class>() { - - @Override - @SuppressWarnings("unchecked") - public Class apply(final SubtreeManager> input) { - final Class> targetType = input.getManagedDataObjectType().getTargetType(); - Preconditions.checkArgument(DataObject.class.isAssignableFrom(targetType)); - return (Class) targetType; - } - }; - - /** - * Transform a keyed instance identifier into a wildcarded one. - *

- * ! This has to be called also for wildcarded List instance identifiers - * due to weird behavior of equals in InstanceIdentifier ! - */ - @SuppressWarnings("unchecked") - public static InstanceIdentifier makeIidWildcarded(final InstanceIdentifier id) { - final List transformedPathArguments = - StreamSupport.stream(id.getPathArguments().spliterator(), false) - .map(RWUtils::cleanPathArgumentFromKeys) - .collect(Collectors.toList()); - return (InstanceIdentifier) InstanceIdentifier.create(transformedPathArguments); - } - - /** - * Transform a keyed instance identifier into a wildcarded one, keeping keys except the last item. - */ - @SuppressWarnings("unchecked") - public static InstanceIdentifier makeIidLastWildcarded(final InstanceIdentifier id) { - final InstanceIdentifier.Item wildcardedItem = new InstanceIdentifier.Item<>(id.getTargetType()); - final Iterable pathArguments = id.getPathArguments(); - return (InstanceIdentifier) InstanceIdentifier.create( - Iterables.concat( - Iterables.limit(pathArguments, Iterables.size(pathArguments) - 1), - Collections.singleton(wildcardedItem))); - } - - private static InstanceIdentifier.PathArgument cleanPathArgumentFromKeys(final InstanceIdentifier.PathArgument pathArgument) { - return pathArgument instanceof InstanceIdentifier.IdentifiableItem - ? new InstanceIdentifier.Item<>(pathArgument.getType()) - : pathArgument; - } -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/ReflectionUtils.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/ReflectionUtils.java deleted file mode 100644 index 728c4f80d..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/ReflectionUtils.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util; - -import com.google.common.base.Optional; -import java.lang.reflect.Method; -import java.util.List; -import javax.annotation.Nonnull; - -/** - * Reflection based utilities - */ -public final class ReflectionUtils { - - private ReflectionUtils() {} - - /** - * Find a specific method using reflection - * - * @param managedType Class object to find method in - * @param prefix Method name prefix used when finding the method. Case does not matter. - * @param paramTypes List of input argument types - * @param retType Return type - * - * @return Found method or Optional.absent() if there's no such method - */ - @Nonnull - public static Optional findMethodReflex(@Nonnull final Class managedType, - @Nonnull final String prefix, - @Nonnull final List> paramTypes, - @Nonnull final Class retType) { - for (Method method : managedType.getMethods()) { - if (isMethodMatch(prefix, paramTypes, retType, method)) { - return Optional.of(method); - } - } - - return Optional.absent(); - } - - private static boolean isMethodMatch(final @Nonnull String prefix, - final @Nonnull List> paramTypes, - final @Nonnull Class retType, final Method method) { - if (!method.getName().toLowerCase().startsWith(prefix.toLowerCase())) { - return false; - } - - final Class[] parameterTypes = method.getParameterTypes(); - if (parameterTypes.length != paramTypes.size()) { - return false; - } - - for (int i = 0; i < parameterTypes.length; i++) { - if (!parameterTypes[i].isAssignableFrom(paramTypes.get(i))) { - return false; - } - } - - if (!method.getReturnType().equals(retType)) { - return false; - } - - return true; - } -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/TransactionMappingContext.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/TransactionMappingContext.java deleted file mode 100644 index 6abc3b1eb..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/TransactionMappingContext.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util; - -import com.google.common.base.Optional; -import com.google.common.util.concurrent.CheckedFuture; -import io.fd.honeycomb.v3po.translate.MappingContext; -import javax.annotation.Nonnull; -import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; -import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Binding Transaction backed mapping context. - */ -public class TransactionMappingContext implements MappingContext { - - private final ReadWriteTransaction readWriteTransaction; - - // TODO make async - - public TransactionMappingContext(final ReadWriteTransaction readWriteTransaction) { - this.readWriteTransaction = readWriteTransaction; - } - - @Override - public Optional read(@Nonnull final InstanceIdentifier currentId) { - try { - return readWriteTransaction.read(LogicalDatastoreType.OPERATIONAL, currentId).checkedGet(); - } catch (ReadFailedException e) { - throw new IllegalStateException("Unable to perform read", e); - } - } - - @Override - public void delete(final InstanceIdentifier path) { - readWriteTransaction.delete(LogicalDatastoreType.OPERATIONAL, path); - } - - @Override - public void merge(final InstanceIdentifier path, T data) { - readWriteTransaction.merge(LogicalDatastoreType.OPERATIONAL, path, data, true); - } - - @Override - public void put(final InstanceIdentifier path, T data) { - readWriteTransaction.put(LogicalDatastoreType.OPERATIONAL, path, data, true); - } - - public CheckedFuture submit() { - return readWriteTransaction.submit(); - } - - @Override - public void close() { - readWriteTransaction.cancel(); - } -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/AbstractGenericReader.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/AbstractGenericReader.java deleted file mode 100644 index 9bfbc2450..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/AbstractGenericReader.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.read; - -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.common.annotations.Beta; -import com.google.common.base.Optional; -import io.fd.honeycomb.v3po.translate.read.ReadContext; -import io.fd.honeycomb.v3po.translate.read.ReadFailedException; -import io.fd.honeycomb.v3po.translate.read.Reader; -import io.fd.honeycomb.v3po.translate.util.RWUtils; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@Beta -public abstract class AbstractGenericReader> implements Reader { - - private static final Logger LOG = LoggerFactory.getLogger(AbstractGenericReader.class); - - private final InstanceIdentifier instanceIdentifier; - - protected AbstractGenericReader(final InstanceIdentifier managedDataObjectType) { - this.instanceIdentifier = RWUtils.makeIidWildcarded(managedDataObjectType); - } - - @Nonnull - @Override - public final InstanceIdentifier getManagedDataObjectType() { - return instanceIdentifier; - } - - /** - * @param id {@link InstanceIdentifier} pointing to current node. In case of keyed list, key must be present. - * - */ - protected Optional readCurrent(@Nonnull final InstanceIdentifier id, - @Nonnull final ReadContext ctx) throws ReadFailedException { - LOG.debug("{}: Reading current: {}", this, id); - final B builder = getBuilder(id); - // Cache empty value to determine if anything has changed later TODO cache in a field - final D emptyValue = builder.build(); - - LOG.trace("{}: Reading current attributes", this); - readCurrentAttributes(id, builder, ctx); - - // Need to check whether anything was filled in to determine if data is present or not. - final D built = builder.build(); - final Optional read = built.equals(emptyValue) - ? Optional.absent() - : Optional.of(built); - - LOG.debug("{}: Current node read successfully. Result: {}", this, read); - return read; - } - - @Nonnull - @Override - @SuppressWarnings("unchecked") - public Optional read(@Nonnull final InstanceIdentifier id, - @Nonnull final ReadContext ctx) - throws ReadFailedException { - LOG.trace("{}: Reading : {}", this, id); - checkArgument(id.getTargetType().equals(getManagedDataObjectType().getTargetType())); - return readCurrent((InstanceIdentifier) id, ctx); - } - - @Override - public String toString() { - return String.format("Reader[%s]", getManagedDataObjectType().getTargetType().getSimpleName()); - } -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/BindingBrokerReader.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/BindingBrokerReader.java deleted file mode 100644 index 68aa3956e..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/BindingBrokerReader.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.read; - -import com.google.common.base.Optional; -import com.google.common.util.concurrent.CheckedFuture; -import io.fd.honeycomb.v3po.translate.read.ReadContext; -import io.fd.honeycomb.v3po.translate.read.ReadFailedException; -import io.fd.honeycomb.v3po.translate.read.Reader; -import javax.annotation.Nonnull; -import org.opendaylight.controller.md.sal.binding.api.DataBroker; -import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Simple DataBroker backed reader allowing to delegate reads to different brokers. - */ -public final class BindingBrokerReader> - implements Reader, AutoCloseable { - - private final InstanceIdentifier instanceIdentifier; - private final DataBroker dataBroker; - private final LogicalDatastoreType datastoreType; - private final ReflexiveReaderCustomizer reflexiveReaderCustomizer; - - public BindingBrokerReader(final InstanceIdentifier instanceIdentifier, - final DataBroker dataBroker, - final LogicalDatastoreType datastoreType, - final Class builderClass) { - this.reflexiveReaderCustomizer = new ReflexiveReaderCustomizer<>(instanceIdentifier.getTargetType(), builderClass); - this.instanceIdentifier = instanceIdentifier; - this.dataBroker = dataBroker; - this.datastoreType = datastoreType; - } - - @Nonnull - @Override - public Optional read(@Nonnull final InstanceIdentifier id, - @Nonnull final ReadContext ctx) throws ReadFailedException { - try (final ReadOnlyTransaction readOnlyTransaction = dataBroker.newReadOnlyTransaction()) { - final CheckedFuture, org.opendaylight.controller.md.sal.common.api.data.ReadFailedException> - read = readOnlyTransaction.read(datastoreType, id); - try { - return read.checkedGet(); - } catch (org.opendaylight.controller.md.sal.common.api.data.ReadFailedException e) { - throw new ReadFailedException(id, e); - } - } - } - - @Override - public void merge(@Nonnull final Builder parentBuilder, @Nonnull final D readValue) { - reflexiveReaderCustomizer.merge(parentBuilder, readValue); - } - - @Nonnull - @Override - public B getBuilder(final InstanceIdentifier id) { - return reflexiveReaderCustomizer.getBuilder(id); - } - - @Override - public void readCurrentAttributes(@Nonnull final InstanceIdentifier id, - @Nonnull final B builder, - @Nonnull final ReadContext ctx) throws ReadFailedException { - throw new UnsupportedOperationException("Not supported"); - } - - @Nonnull - @Override - public InstanceIdentifier getManagedDataObjectType() { - return instanceIdentifier; - } - - @Override - public void close() throws Exception { - // Noop - } -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/KeepaliveReaderWrapper.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/KeepaliveReaderWrapper.java deleted file mode 100644 index d782bcc7f..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/KeepaliveReaderWrapper.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.read; - -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import io.fd.honeycomb.v3po.translate.MappingContext; -import io.fd.honeycomb.v3po.translate.ModificationCache; -import io.fd.honeycomb.v3po.translate.read.ReadContext; -import io.fd.honeycomb.v3po.translate.read.ReadFailedException; -import io.fd.honeycomb.v3po.translate.read.Reader; -import java.io.Closeable; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import javax.annotation.Nonnegative; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Reader wrapper that periodically invokes a read to determine whether reads are still fully functional. - * In case a specific error occurs, Keep-alive failure listener gets notified. - */ -public final class KeepaliveReaderWrapper> implements Reader, Runnable, Closeable { - - private static final Logger LOG = LoggerFactory.getLogger(KeepaliveReaderWrapper.class); - - private static final NoopReadContext CTX = new NoopReadContext(); - - private final Reader delegate; - private final Class exceptionType; - private final KeepaliveFailureListener failureListener; - private final ScheduledFuture scheduledFuture; - - /** - * Create new Keepalive wrapper - * - * @param delegate underlying reader performing actual reads - * @param executor scheduled executor service to schedule keepalive calls - * @param exception type of exception used to differentiate keepalive exception from other exceptions - * @param delayInSeconds number of seconds to wait between keepalive calls - * @param failureListener listener to be called whenever a keepalive failure is detected - */ - public KeepaliveReaderWrapper(@Nonnull final Reader delegate, - @Nonnull final ScheduledExecutorService executor, - @Nonnull final Class exception, - @Nonnegative final int delayInSeconds, - @Nonnull final KeepaliveFailureListener failureListener) { - this.delegate = delegate; - this.exceptionType = exception; - this.failureListener = failureListener; - Preconditions.checkArgument(delayInSeconds > 0, "Delay cannot be < 0"); - LOG.debug("Starting keep-alive execution on top of: {} with delay of: {} seconds", delegate, delayInSeconds); - scheduledFuture = executor.scheduleWithFixedDelay(this, delayInSeconds, delayInSeconds, TimeUnit.SECONDS); - } - - @Nonnull - public Optional read(@Nonnull final InstanceIdentifier id, - @Nonnull final ReadContext ctx) throws ReadFailedException { - return delegate.read(id, ctx); - } - - public void readCurrentAttributes(@Nonnull final InstanceIdentifier id, - @Nonnull final B builder, - @Nonnull final ReadContext ctx) throws ReadFailedException { - delegate.readCurrentAttributes(id, builder, ctx); - } - - @Nonnull - public B getBuilder(final InstanceIdentifier id) { - return delegate.getBuilder(id); - } - - public void merge(@Nonnull final Builder parentBuilder, - @Nonnull final D readValue) { - delegate.merge(parentBuilder, readValue); - } - - @Nonnull - @Override - public InstanceIdentifier getManagedDataObjectType() { - return delegate.getManagedDataObjectType(); - } - - @Override - public void run() { - LOG.trace("Invoking keepalive"); - try { - final Optional read = read(delegate.getManagedDataObjectType(), CTX); - LOG.debug("Keepalive executed successfully with data: {}", read); - } catch (Exception e) { - if (exceptionType.isAssignableFrom(e.getClass())) { - LOG.warn("Keepalive failed. Notifying listener", e); - failureListener.onKeepaliveFailure(); - } - LOG.warn("Keepalive failed unexpectedly", e); - throw new IllegalArgumentException("Unexpected failure during keep-alive execution", e); - } - } - - @Override - public void close() { - // Do not interrupt, it's not our executor - scheduledFuture.cancel(false); - } - - /** - * Listener that gets called whenever keepalive fails as expected - */ - public interface KeepaliveFailureListener { - - void onKeepaliveFailure(); - } - - private static final class NoopMappingContext implements MappingContext { - @Override - public Optional read(@Nonnull final InstanceIdentifier currentId) { - return Optional.absent(); - } - - @Override - public void delete(final InstanceIdentifier path) {} - - @Override - public void merge(final InstanceIdentifier path, final T data) {} - - @Override - public void put(final InstanceIdentifier path, final T data) {} - - @Override - public void close() {} - } - - private static class NoopReadContext implements ReadContext { - - private final ModificationCache modificationCache = new ModificationCache(); - - @Nonnull - @Override - public ModificationCache getModificationCache() { - return modificationCache; - } - - @Nonnull - @Override - public MappingContext getMappingContext() { - return new NoopMappingContext(); - } - - @Override - public void close() { - - } - } -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/NoopReaderCustomizer.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/NoopReaderCustomizer.java deleted file mode 100644 index a4de9febb..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/NoopReaderCustomizer.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.read; - -import io.fd.honeycomb.v3po.translate.read.ReadContext; -import io.fd.honeycomb.v3po.translate.read.ReadFailedException; -import io.fd.honeycomb.v3po.translate.spi.read.ReaderCustomizer; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -public abstract class NoopReaderCustomizer> implements - ReaderCustomizer { - - @Override - public void readCurrentAttributes(InstanceIdentifier id, final B builder, final ReadContext context) throws - ReadFailedException { - // Noop - } -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveListReaderCustomizer.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveListReaderCustomizer.java deleted file mode 100644 index 8ad323cc3..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveListReaderCustomizer.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.read; - -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.common.base.Optional; -import io.fd.honeycomb.v3po.translate.spi.read.ListReaderCustomizer; -import io.fd.honeycomb.v3po.translate.util.ReflectionUtils; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Collections; -import java.util.List; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.Identifiable; -import org.opendaylight.yangtools.yang.binding.Identifier; - -/** - * Might be slow ! - */ -public abstract class ReflexiveListReaderCustomizer, K extends Identifier, B extends Builder> - extends ReflexiveReaderCustomizer - implements ListReaderCustomizer { - - - public ReflexiveListReaderCustomizer(final Class typeClass, final Class builderClass) { - super(typeClass, builderClass); - } - - @Override - public void merge(@Nonnull final Builder parentBuilder, @Nonnull final C readValue) { - merge(parentBuilder, Collections.singletonList(readValue)); - } - - @Override - public void merge(@Nonnull final Builder parentBuilder, @Nonnull final List readData) { - final Optional method = - ReflectionUtils.findMethodReflex(parentBuilder.getClass(), "set" + getTypeClass().getSimpleName(), - Collections.singletonList(List.class), parentBuilder.getClass()); - - checkArgument(method.isPresent(), "Unable to set %s to %s", readData, parentBuilder); - - try { - method.get().invoke(parentBuilder, readData); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new IllegalArgumentException("Unable to set " + readData + " to " + parentBuilder, e); - } - } -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReader.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReader.java deleted file mode 100644 index 2b2d9300b..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReader.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.read; - -import io.fd.honeycomb.v3po.translate.read.ReadContext; -import io.fd.honeycomb.v3po.translate.read.ReadFailedException; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Reader that performs no read operation on its own, just fills in the hierarchy. - *

- * Might be slow due to reflection ! - */ -public class ReflexiveReader> extends AbstractGenericReader { - - private final ReflexiveReaderCustomizer customizer; - - public ReflexiveReader(final InstanceIdentifier identifier, final Class builderClass) { - super(identifier); - this.customizer = new ReflexiveReaderCustomizer<>(identifier.getTargetType(), builderClass); - } - - @Override - public void readCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final B builder, - @Nonnull final ReadContext ctx) - throws ReadFailedException { - customizer.readCurrentAttributes(id, builder, ctx); - } - - @Nonnull - @Override - public B getBuilder(final InstanceIdentifier id) { - return customizer.getBuilder(id); - } - - @Override - public void merge(@Nonnull final Builder parentBuilder, @Nonnull final C readValue) { - customizer.merge(parentBuilder, readValue); - } -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReaderCustomizer.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReaderCustomizer.java deleted file mode 100644 index a6b9bf08e..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReaderCustomizer.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.read; - -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.common.base.Optional; -import com.google.common.collect.Lists; -import io.fd.honeycomb.v3po.translate.util.ReflectionUtils; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Collections; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.yang.binding.Augmentation; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Might be slow ! - */ -class ReflexiveReaderCustomizer> extends NoopReaderCustomizer { - - private final Class typeClass; - private final Class builderClass; - - public ReflexiveReaderCustomizer(final Class typeClass, final Class builderClass) { - this.typeClass = typeClass; - this.builderClass = builderClass; - } - - protected Class getTypeClass() { - return typeClass; - } - - protected Class getBuilderClass() { - return builderClass; - } - - @Nonnull - @Override - public B getBuilder(@Nonnull InstanceIdentifier id) { - try { - return builderClass.newInstance(); - } catch (InstantiationException | IllegalAccessException e) { - throw new IllegalStateException("Unable to instantiate " + builderClass, e); - } - } - - @Override - public void merge(@Nonnull final Builder parentBuilder, @Nonnull final C readValue) { - if (Augmentation.class.isAssignableFrom(typeClass)) { - mergeAugmentation(parentBuilder, (Class>) typeClass, readValue); - } else { - mergeRegular(parentBuilder, readValue); - } - } - - private static void mergeRegular(@Nonnull final Builder parentBuilder, - @Nonnull final DataObject readValue) { - final Optional method = - ReflectionUtils.findMethodReflex(parentBuilder.getClass(), "set", - Collections.singletonList(readValue.getClass()), parentBuilder.getClass()); - - checkArgument(method.isPresent(), "Unable to set %s to %s", readValue, parentBuilder); - - try { - method.get().invoke(parentBuilder, readValue); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new IllegalArgumentException("Unable to set " + readValue + " to " + parentBuilder, e); - } - } - - private static void mergeAugmentation(@Nonnull final Builder parentBuilder, - @Nonnull final Class> typeClass, - @Nonnull final DataObject readValue) { - final Optional method = - ReflectionUtils.findMethodReflex(parentBuilder.getClass(), "addAugmentation", - Lists.newArrayList(Class.class, Augmentation.class), parentBuilder.getClass()); - - checkArgument(method.isPresent(), "Not possible to add augmentations to builder: %s", parentBuilder); - try { - method.get().invoke(parentBuilder, typeClass, readValue); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new IllegalArgumentException("Unable to set " + readValue + " to " + parentBuilder, e); - } - } - -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReader.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReader.java deleted file mode 100644 index aa9b2dc92..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReader.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.read.registry; - -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Optional; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; -import io.fd.honeycomb.v3po.translate.read.ListReader; -import io.fd.honeycomb.v3po.translate.read.ReadContext; -import io.fd.honeycomb.v3po.translate.read.ReadFailedException; -import io.fd.honeycomb.v3po.translate.read.Reader; -import io.fd.honeycomb.v3po.translate.util.RWUtils; -import io.fd.honeycomb.v3po.translate.util.read.AbstractGenericReader; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.Identifiable; -import org.opendaylight.yangtools.yang.binding.Identifier; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class CompositeReader> extends AbstractGenericReader { - - private static final Logger LOG = LoggerFactory.getLogger(CompositeReader.class); - - private final Reader delegate; - private final ImmutableMap, Reader>> childReaders; - - private CompositeReader(final Reader reader, - final ImmutableMap, Reader>> childReaders) { - super(reader.getManagedDataObjectType()); - this.delegate = reader; - this.childReaders = childReaders; - } - - @VisibleForTesting - ImmutableMap, Reader>> getChildReaders() { - return childReaders; - } - - @SuppressWarnings("unchecked") - public static InstanceIdentifier appendTypeToId( - final InstanceIdentifier parentId, final InstanceIdentifier type) { - final InstanceIdentifier.PathArgument t = new InstanceIdentifier.Item<>(type.getTargetType()); - return (InstanceIdentifier) InstanceIdentifier.create(Iterables.concat( - parentId.getPathArguments(), Collections.singleton(t))); - } - - @Nonnull - @Override - public Optional read(@Nonnull final InstanceIdentifier id, - @Nonnull final ReadContext ctx) throws ReadFailedException { - if (shouldReadCurrent(id)) { - LOG.trace("{}: Reading current: {}", this, id); - return readCurrent((InstanceIdentifier) id, ctx); - } else if (shouldDelegateToChild(id)) { - LOG.trace("{}: Reading child: {}", this, id); - return readSubtree(id, ctx); - } else { - // Fallback - LOG.trace("{}: Delegating read: {}", this, id); - return delegate.read(id, ctx); - } - } - - private boolean shouldReadCurrent(@Nonnull final InstanceIdentifier id) { - return id.getTargetType().equals(getManagedDataObjectType().getTargetType()); - } - - private boolean shouldDelegateToChild(@Nonnull final InstanceIdentifier id) { - return childReaders.containsKey(RWUtils.getNextId(id, getManagedDataObjectType()).getType()); - } - - private Optional readSubtree(final InstanceIdentifier id, - final ReadContext ctx) throws ReadFailedException { - final InstanceIdentifier.PathArgument nextId = RWUtils.getNextId(id, getManagedDataObjectType()); - final Reader> nextReader = childReaders.get(nextId.getType()); - checkArgument(nextReader != null, "Unable to read: %s. No delegate present, available readers at next level: %s", - id, childReaders.keySet()); - return nextReader.read(id, ctx); - } - - @SuppressWarnings("unchecked") - private void readChildren(final InstanceIdentifier id, @Nonnull final ReadContext ctx, final B builder) - throws ReadFailedException { - LOG.debug("{}: Reading children: {}", this, childReaders.keySet()); - for (Reader child : childReaders.values()) { - final InstanceIdentifier childId = appendTypeToId(id, child.getManagedDataObjectType()); - - LOG.debug("{}: Reading child from: {}", this, child); - if (child instanceof ListReader) { - final List list = ((ListReader) child).readList(childId, ctx); - ((ListReader) child).merge(builder, list); - } else { - final Optional read = child.read(childId, ctx); - if (read.isPresent()) { - child.merge(builder, read.get()); - } - } - } - } - - @Override - public void readCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final B builder, - @Nonnull final ReadContext ctx) - throws ReadFailedException { - delegate.readCurrentAttributes(id, builder, ctx); - readChildren(id, ctx, builder); - } - - @Nonnull - @Override - public B getBuilder(final InstanceIdentifier id) { - return delegate.getBuilder(id); - } - - @Override - public void merge(@Nonnull final Builder parentBuilder, @Nonnull final D readValue) { - delegate.merge(parentBuilder, readValue); - } - - /** - * Wrap a Reader as a Composite Reader. - */ - static > Reader createForReader( - @Nonnull final Reader reader, - @Nonnull final ImmutableMap, Reader>> childReaders) { - - return (reader instanceof ListReader) - ? new CompositeListReader<>((ListReader) reader, childReaders) - : new CompositeReader<>(reader, childReaders); - } - - private static class CompositeListReader, B extends Builder, K extends Identifier> - extends CompositeReader - implements ListReader { - - private final ListReader delegate; - - private CompositeListReader(final ListReader reader, - final ImmutableMap, Reader>> childReaders) { - super(reader, childReaders); - this.delegate = reader; - } - - @Nonnull - @Override - public List readList(@Nonnull final InstanceIdentifier id, @Nonnull final ReadContext ctx) - throws ReadFailedException { - LOG.trace("{}: Reading all list entries", this); - final List allIds = delegate.getAllIds(id, ctx); - LOG.debug("{}: Reading list entries for: {}", this, allIds); - - // Override read list in order to perform readCurrent + readChildren here - final ArrayList allEntries = new ArrayList<>(allIds.size()); - for (K key : allIds) { - final InstanceIdentifier.IdentifiableItem currentBdItem = RWUtils.getCurrentIdItem(id, key); - final InstanceIdentifier keyedId = RWUtils.replaceLastInId(id, currentBdItem); - final Optional read = readCurrent(keyedId, ctx); - if (read.isPresent()) { - final DataObject singleItem = read.get(); - checkArgument(getManagedDataObjectType().getTargetType().isAssignableFrom(singleItem.getClass())); - allEntries.add(getManagedDataObjectType().getTargetType().cast(singleItem)); - } - } - return allEntries; - } - - @Override - public void merge(@Nonnull final Builder builder, @Nonnull final List readData) { - delegate.merge(builder, readData); - } - - @Override - public List getAllIds(@Nonnull final InstanceIdentifier id, - @Nonnull final ReadContext ctx) throws ReadFailedException { - return delegate.getAllIds(id, ctx); - } - } - -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistry.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistry.java deleted file mode 100644 index a9f606ae2..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistry.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.read.registry; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Optional; -import com.google.common.collect.Iterables; -import com.google.common.collect.LinkedListMultimap; -import com.google.common.collect.Multimap; -import io.fd.honeycomb.v3po.translate.read.ListReader; -import io.fd.honeycomb.v3po.translate.read.ReadContext; -import io.fd.honeycomb.v3po.translate.read.ReadFailedException; -import io.fd.honeycomb.v3po.translate.read.Reader; -import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistry; -import io.fd.honeycomb.v3po.translate.util.RWUtils; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Simple reader registry able to perform and aggregated read (ROOT read) on top of all provided readers. Also able to - * delegate a specific read to one of the delegate readers. - *

- * This could serve as a utility to hold & hide all available readers in upper layers. - */ -public final class CompositeReaderRegistry implements ReaderRegistry { - - private static final Logger LOG = LoggerFactory.getLogger(CompositeReaderRegistry.class); - - private final Map, Reader>> rootReaders; - - /** - * Create new {@link CompositeReaderRegistry}. - * - * @param rootReaders List of delegate readers - */ - public CompositeReaderRegistry(@Nonnull final List>> rootReaders) { - this.rootReaders = RWUtils.uniqueLinkedIndex(checkNotNull(rootReaders), RWUtils.MANAGER_CLASS_FUNCTION); - } - - @VisibleForTesting - Map, Reader>> getRootReaders() { - return rootReaders; - } - - @Override - @Nonnull - public Multimap, ? extends DataObject> readAll( - @Nonnull final ReadContext ctx) throws ReadFailedException { - - LOG.debug("Reading from all delegates: {}", this); - LOG.trace("Reading from all delegates: {}", rootReaders.values()); - - final Multimap, DataObject> objects = LinkedListMultimap.create(); - for (Reader> rootReader : rootReaders.values()) { - LOG.debug("Reading from delegate: {}", rootReader); - - if (rootReader instanceof ListReader) { - final List listEntries = - ((ListReader) rootReader).readList(rootReader.getManagedDataObjectType(), ctx); - if (!listEntries.isEmpty()) { - objects.putAll(rootReader.getManagedDataObjectType(), listEntries); - } - } else { - final Optional read = rootReader.read(rootReader.getManagedDataObjectType(), ctx); - if (read.isPresent()) { - objects.putAll(rootReader.getManagedDataObjectType(), Collections.singletonList(read.get())); - } - } - } - - return objects; - } - - @Nonnull - @Override - public Optional read(@Nonnull final InstanceIdentifier id, - @Nonnull final ReadContext ctx) - throws ReadFailedException { - final InstanceIdentifier.PathArgument first = checkNotNull( - Iterables.getFirst(id.getPathArguments(), null), "Empty id"); - final Reader> reader = rootReaders.get(first.getType()); - checkNotNull(reader, - "Unable to read %s. Missing reader. Current readers for: %s", id, rootReaders.keySet()); - LOG.debug("Reading from delegate: {}", reader); - return reader.read(id, ctx); - } - - @Override - public String toString() { - return getClass().getSimpleName() - + rootReaders.keySet().stream().map(Class::getSimpleName).collect(Collectors.toList()); - } -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistryBuilder.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistryBuilder.java deleted file mode 100644 index 3adda713d..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistryBuilder.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.read.registry; - -import com.google.common.collect.ImmutableMap; -import io.fd.honeycomb.v3po.translate.read.Reader; -import io.fd.honeycomb.v3po.translate.read.registry.ModifiableReaderRegistryBuilder; -import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistry; -import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistryBuilder; -import io.fd.honeycomb.v3po.translate.util.AbstractSubtreeManagerRegistryBuilderBuilder; -import io.fd.honeycomb.v3po.translate.util.read.ReflexiveReader; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.concurrent.NotThreadSafe; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@NotThreadSafe -public final class CompositeReaderRegistryBuilder - extends AbstractSubtreeManagerRegistryBuilderBuilder>, ReaderRegistry> - implements ModifiableReaderRegistryBuilder, ReaderRegistryBuilder { - - private static final Logger LOG = LoggerFactory.getLogger(CompositeReaderRegistryBuilder.class); - - @Override - protected Reader> getSubtreeHandler(@Nonnull final Set> handledChildren, - @Nonnull final Reader> reader) { - return SubtreeReader.createForReader(handledChildren, reader); - } - - @Override - public void addStructuralReader(@Nonnull InstanceIdentifier id, - @Nonnull Class> builderType) { - add(new ReflexiveReader<>(id, builderType)); - } - - /** - * Create {@link CompositeReaderRegistry} with Readers ordered according to submitted relationships. - *

- * Note: The ordering only applies between nodes on the same level, inter-level and inter-subtree relationships are - * ignored. - */ - @Override - public ReaderRegistry build() { - ImmutableMap, Reader>> mappedReaders = - getMappedHandlers(); - LOG.debug("Building Reader registry with Readers: {}", - mappedReaders.keySet().stream() - .map(InstanceIdentifier::getTargetType) - .map(Class::getSimpleName) - .collect(Collectors.joining(", "))); - - LOG.trace("Building Reader registry with Readers: {}", mappedReaders); - final List> readerOrder = new ArrayList<>(mappedReaders.keySet()); - - // Wrap readers into composite readers recursively, collect roots and create registry - final TypeHierarchy typeHierarchy = TypeHierarchy.create(mappedReaders.keySet()); - final List>> orderedRootReaders = - typeHierarchy.getRoots().stream() - .map(rootId -> toCompositeReader(rootId, mappedReaders, typeHierarchy)) - .collect(Collectors.toList()); - - // We are violating the ordering from mappedReaders, since we are forming a composite structure - // but at least order root writers - orderedRootReaders.sort((reader1, reader2) -> readerOrder.indexOf(reader1.getManagedDataObjectType()) - - readerOrder.indexOf(reader2.getManagedDataObjectType())); - - return new CompositeReaderRegistry(orderedRootReaders); - } - - private Reader> toCompositeReader( - final InstanceIdentifier instanceIdentifier, - final ImmutableMap, Reader>> mappedReaders, - final TypeHierarchy typeHierarchy) { - - // Order child readers according to the mappedReadersCollection - final ImmutableMap.Builder, Reader>> childReadersMapB = ImmutableMap.builder(); - for (InstanceIdentifier childId : mappedReaders.keySet()) { - if (typeHierarchy.getDirectChildren(instanceIdentifier).contains(childId)) { - childReadersMapB.put(childId.getTargetType(), toCompositeReader(childId, mappedReaders, typeHierarchy)); - } - } - - final ImmutableMap, Reader>> childReadersMap = childReadersMapB.build(); - return childReadersMap.isEmpty() - ? mappedReaders.get(instanceIdentifier) - : CompositeReader.createForReader(mappedReaders.get(instanceIdentifier), childReadersMap); - } -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/SubtreeReader.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/SubtreeReader.java deleted file mode 100644 index 50a20656e..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/SubtreeReader.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.read.registry; - -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.common.base.Optional; -import com.google.common.base.Predicate; -import com.google.common.collect.Iterables; -import io.fd.honeycomb.v3po.translate.read.ListReader; -import io.fd.honeycomb.v3po.translate.read.ReadContext; -import io.fd.honeycomb.v3po.translate.read.ReadFailedException; -import io.fd.honeycomb.v3po.translate.read.Reader; -import io.fd.honeycomb.v3po.translate.util.RWUtils; -import io.fd.honeycomb.v3po.translate.util.ReflectionUtils; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.Identifiable; -import org.opendaylight.yangtools.yang.binding.Identifier; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Simple Reader delegate for subtree Readers (Readers handling also children nodes) providing a list of all the - * children nodes being handled. - */ -class SubtreeReader> implements Reader { - - private static final Logger LOG = LoggerFactory.getLogger(SubtreeReader.class); - - private final Reader delegate; - private final Set> handledChildTypes = new HashSet<>(); - - private SubtreeReader(final Reader delegate, Set> handledTypes) { - this.delegate = delegate; - for (InstanceIdentifier handledType : handledTypes) { - // Iid has to start with Reader's handled root type - checkArgument(delegate.getManagedDataObjectType().getTargetType().equals( - handledType.getPathArguments().iterator().next().getType()), - "Handled node from subtree has to be identified by an instance identifier starting from: %s." - + "Instance identifier was: %s", getManagedDataObjectType().getTargetType(), handledType); - checkArgument(Iterables.size(handledType.getPathArguments()) > 1, - "Handled node from subtree identifier too short: %s", handledType); - handledChildTypes.add(InstanceIdentifier.create(Iterables.concat( - getManagedDataObjectType().getPathArguments(), Iterables.skip(handledType.getPathArguments(), 1)))); - } - } - - /** - * Return set of types also handled by this Reader. All of the types are children of the type managed by this Reader - * excluding the type of this Reader. - */ - Set> getHandledChildTypes() { - return handledChildTypes; - } - - @Override - @Nonnull - public Optional read( - @Nonnull final InstanceIdentifier id, - @Nonnull final ReadContext ctx) throws ReadFailedException { - final InstanceIdentifier wildcarded = RWUtils.makeIidWildcarded(id); - - // Reading entire subtree and filtering if is current reader responsible - if (getHandledChildTypes().contains(wildcarded)) { - LOG.debug("{}: Subtree node managed by this writer requested: {}. Reading current and filtering", this, id); - // If there's no dedicated reader, use read current - final InstanceIdentifier currentId = RWUtils.cutId(id, getManagedDataObjectType()); - final Optional current = delegate.read(currentId, ctx); - // then perform post-reading filtering (return only requested sub-node) - final Optional readSubtree = current.isPresent() - ? filterSubtree(current.get(), id, getManagedDataObjectType().getTargetType()) - : current; - - LOG.debug("{}: Subtree: {} read successfully. Result: {}", this, id, readSubtree); - return readSubtree; - - // Fallback solution, try delegate, maybe it can read the ID - } else { - return delegate.read(id, ctx); - } - } - - @Override - public void readCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final B builder, - @Nonnull final ReadContext ctx) - throws ReadFailedException { - delegate.readCurrentAttributes(id, builder, ctx); - } - - @Nonnull - @Override - public B getBuilder(final InstanceIdentifier id) { - return delegate.getBuilder(id); - } - - @Override - public void merge(@Nonnull final Builder parentBuilder, @Nonnull final D readValue) { - delegate.merge(parentBuilder, readValue); - } - - @Nonnull - private static Optional filterSubtree(@Nonnull final DataObject parent, - @Nonnull final InstanceIdentifier absolutPath, - @Nonnull final Class managedType) { - final InstanceIdentifier.PathArgument nextId = - RWUtils.getNextId(absolutPath, InstanceIdentifier.create(parent.getClass())); - - final Optional nextParent = findNextParent(parent, nextId, managedType); - - if (Iterables.getLast(absolutPath.getPathArguments()).equals(nextId)) { - return nextParent; // we found the dataObject identified by absolutePath - } else if (nextParent.isPresent()) { - return filterSubtree(nextParent.get(), absolutPath, nextId.getType()); - } else { - return nextParent; // we can't go further, return Optional.absent() - } - } - - private static Optional findNextParent(@Nonnull final DataObject parent, - @Nonnull final InstanceIdentifier.PathArgument nextId, - @Nonnull final Class managedType) { - // TODO is there a better way than reflection ? e.g. convert into NN and filter out with a utility - Optional method = ReflectionUtils.findMethodReflex(managedType, "get", - Collections.emptyList(), nextId.getType()); - - if (method.isPresent()) { - return Optional.fromNullable(filterSingle(parent, nextId, method.get())); - } else { - // List child nodes - method = ReflectionUtils.findMethodReflex(managedType, - "get" + nextId.getType().getSimpleName(), Collections.emptyList(), List.class); - - if (method.isPresent()) { - return filterList(parent, nextId, method.get()); - } else { - throw new IllegalStateException( - "Unable to filter " + nextId + " from " + parent + " getters not found using reflexion"); - } - } - } - - @SuppressWarnings("unchecked") - private static Optional filterList(final DataObject parent, - final InstanceIdentifier.PathArgument nextId, - final Method method) { - final List invoke = (List) invoke(method, nextId, parent); - - checkArgument(nextId instanceof InstanceIdentifier.IdentifiableItem, - "Unable to perform wildcarded read for %s", nextId); - final Identifier key = ((InstanceIdentifier.IdentifiableItem) nextId).getKey(); - // TODO replace with stream().filter().findFirst() when we switch to using java's Optional instead of Guava's - // because now we would have to do awkward Optional transformation since findFirstReturns guava's optional - return Iterables.tryFind(invoke, new Predicate() { - - @Override - public boolean apply(@Nullable final DataObject input) { - final Optional keyGetter = ReflectionUtils.findMethodReflex(nextId.getType(), "get", - Collections.emptyList(), key.getClass()); - final Object actualKey; - actualKey = invoke(keyGetter.get(), nextId, input); - return key.equals(actualKey); - } - }); - } - - private static DataObject filterSingle(final DataObject parent, - final InstanceIdentifier.PathArgument nextId, final Method method) { - return nextId.getType().cast(invoke(method, nextId, parent)); - } - - private static Object invoke(final Method method, - final InstanceIdentifier.PathArgument nextId, final DataObject parent) { - try { - return method.invoke(parent); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new IllegalArgumentException("Unable to get " + nextId + " from " + parent, e); - } - } - - @Override - @Nonnull - public InstanceIdentifier getManagedDataObjectType() { - return delegate.getManagedDataObjectType(); - } - - /** - * Wrap a Reader as a subtree Reader. - */ - static > Reader createForReader(@Nonnull final Set> handledChildren, - @Nonnull final Reader reader) { - return (reader instanceof ListReader) - ? new SubtreeListReader<>((ListReader) reader, handledChildren) - : new SubtreeReader<>(reader, handledChildren); - } - - private static final class SubtreeListReader, B extends Builder, K extends Identifier> - extends SubtreeReader implements ListReader { - - private final ListReader delegate; - - private SubtreeListReader(final ListReader delegate, - final Set> handledTypes) { - super(delegate, handledTypes); - this.delegate = delegate; - } - - @Nonnull - @Override - public List readList(@Nonnull final InstanceIdentifier id, @Nonnull final ReadContext ctx) - throws ReadFailedException { - return delegate.readList(id, ctx); - } - - @Override - public void merge(@Nonnull final Builder builder, @Nonnull final List readData) { - delegate.merge(builder, readData); - } - - @Override - public List getAllIds(@Nonnull final InstanceIdentifier id, - @Nonnull final ReadContext ctx) throws ReadFailedException { - return delegate.getAllIds(id, ctx); - } - } - -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/TypeHierarchy.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/TypeHierarchy.java deleted file mode 100644 index 005e3bc8d..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/TypeHierarchy.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.read.registry; - -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.common.collect.Iterables; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import org.jgrapht.experimental.dag.DirectedAcyclicGraph; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -final class TypeHierarchy { - private final DirectedAcyclicGraph, Parent> hierarchy; - - private TypeHierarchy(@Nonnull final DirectedAcyclicGraph, Parent> hierarchy) { - this.hierarchy = hierarchy; - } - - Set> getAllChildren(InstanceIdentifier id) { - final HashSet> instanceIdentifiers = new HashSet<>(); - for (InstanceIdentifier childId : getDirectChildren(id)) { - instanceIdentifiers.add(childId); - instanceIdentifiers.addAll(getAllChildren(childId)); - } - return instanceIdentifiers; - } - - Set> getDirectChildren(InstanceIdentifier id) { - checkArgument(hierarchy.vertexSet().contains(id), - "Unknown reader: %s. Known readers: %s", id, hierarchy.vertexSet()); - - return hierarchy.outgoingEdgesOf(id).stream() - .map(hierarchy::getEdgeTarget) - .collect(Collectors.toSet()); - } - - Set> getRoots() { - return hierarchy.vertexSet().stream() - .filter(vertex -> hierarchy.incomingEdgesOf(vertex).size() == 0) - .collect(Collectors.toSet()); - } - - /** - * Create reader hierarchy from a flat set of instance identifiers. - * - * @param allIds Set of unkeyed instance identifiers - */ - static TypeHierarchy create(@Nonnull Set> allIds) { - final DirectedAcyclicGraph, Parent> - readersHierarchy = new DirectedAcyclicGraph<>((sourceVertex, targetVertex) -> new Parent()); - - for (InstanceIdentifier allId : allIds) { - checkArgument(!Iterables.isEmpty(allId.getPathArguments()), "Empty ID detected"); - - if (Iterables.size(allId.getPathArguments()) == 1) { - readersHierarchy.addVertex(allId); - } - - List pathArgs = new LinkedList<>(); - pathArgs.add(allId.getPathArguments().iterator().next()); - - for (InstanceIdentifier.PathArgument pathArgument : Iterables.skip(allId.getPathArguments(), 1)) { - final InstanceIdentifier previous = InstanceIdentifier.create(pathArgs); - pathArgs.add(pathArgument); - final InstanceIdentifier current = InstanceIdentifier.create(pathArgs); - - readersHierarchy.addVertex(previous); - readersHierarchy.addVertex(current); - - try { - readersHierarchy.addDagEdge(previous, current); - } catch (DirectedAcyclicGraph.CycleFoundException e) { - throw new IllegalArgumentException("Loop in hierarchy detected", e); - } - } - } - - return new TypeHierarchy(readersHierarchy); - } - - private static final class Parent{} -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/AbstractGenericWriter.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/AbstractGenericWriter.java deleted file mode 100644 index 44b36edae..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/AbstractGenericWriter.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.write; - -import static com.google.common.base.Preconditions.checkArgument; - -import io.fd.honeycomb.v3po.translate.util.RWUtils; -import io.fd.honeycomb.v3po.translate.write.WriteContext; -import io.fd.honeycomb.v3po.translate.write.WriteFailedException; -import io.fd.honeycomb.v3po.translate.write.Writer; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public abstract class AbstractGenericWriter implements Writer { - - private static final Logger LOG = LoggerFactory.getLogger(AbstractGenericWriter.class); - - private final InstanceIdentifier instanceIdentifier; - - protected AbstractGenericWriter(final InstanceIdentifier type) { - this.instanceIdentifier = RWUtils.makeIidWildcarded(type); - } - - protected void writeCurrent(final InstanceIdentifier id, final D data, final WriteContext ctx) - throws WriteFailedException { - LOG.debug("{}: Writing current: {} data: {}", this, id, data); - writeCurrentAttributes(id, data, ctx); - LOG.debug("{}: Current node written successfully", this); - } - - protected void updateCurrent(final InstanceIdentifier id, final D dataBefore, final D dataAfter, - final WriteContext ctx) throws WriteFailedException { - LOG.debug("{}: Updating current: {} dataBefore: {}, datAfter: {}", this, id, dataBefore, dataAfter); - - if (dataBefore.equals(dataAfter)) { - LOG.debug("{}: Skipping current(no update): {}", this, id); - // No change, ignore - return; - } - updateCurrentAttributes(id, dataBefore, dataAfter, ctx); - LOG.debug("{}: Current node updated successfully", this); - } - - protected void deleteCurrent(final InstanceIdentifier id, final D dataBefore, final WriteContext ctx) - throws WriteFailedException { - LOG.debug("{}: Deleting current: {} dataBefore: {}", this, id, dataBefore); - deleteCurrentAttributes(id, dataBefore, ctx); - } - - @SuppressWarnings("unchecked") - @Override - public void update(@Nonnull final InstanceIdentifier id, - @Nullable final DataObject dataBefore, - @Nullable final DataObject dataAfter, - @Nonnull final WriteContext ctx) throws WriteFailedException { - LOG.debug("{}: Updating : {}", this, id); - LOG.trace("{}: Updating : {}, from: {} to: {}", this, id, dataBefore, dataAfter); - - checkArgument(idPointsToCurrent(id), "Cannot handle data: %s. Only: %s can be handled by writer: %s", - id, getManagedDataObjectType(), this); - - if (isWrite(dataBefore, dataAfter)) { - writeCurrent((InstanceIdentifier) id, castToManaged(dataAfter), ctx); - } else if (isDelete(dataBefore, dataAfter)) { - deleteCurrent((InstanceIdentifier) id, castToManaged(dataBefore), ctx); - } else { - checkArgument(dataBefore != null && dataAfter != null, "No data to process"); - updateCurrent((InstanceIdentifier) id, castToManaged(dataBefore), castToManaged(dataAfter), ctx); - } - } - - private void checkDataType(@Nonnull final DataObject dataAfter) { - checkArgument(getManagedDataObjectType().getTargetType().isAssignableFrom(dataAfter.getClass())); - } - - private D castToManaged(final DataObject data) { - checkDataType(data); - return getManagedDataObjectType().getTargetType().cast(data); - } - - private static boolean isWrite(final DataObject dataBefore, - final DataObject dataAfter) { - return dataBefore == null && dataAfter != null; - } - - private static boolean isDelete(final DataObject dataBefore, - final DataObject dataAfter) { - return dataAfter == null && dataBefore != null; - } - - private boolean idPointsToCurrent(final @Nonnull InstanceIdentifier id) { - return id.getTargetType().equals(getManagedDataObjectType().getTargetType()); - } - - protected abstract void writeCurrentAttributes(@Nonnull final InstanceIdentifier id, - @Nonnull final D data, - @Nonnull final WriteContext ctx) throws WriteFailedException; - - protected abstract void deleteCurrentAttributes(@Nonnull final InstanceIdentifier id, - @Nonnull final D dataBefore, - @Nonnull final WriteContext ctx) throws WriteFailedException; - - protected abstract void updateCurrentAttributes(@Nonnull final InstanceIdentifier id, - @Nonnull final D dataBefore, - @Nonnull final D dataAfter, - @Nonnull final WriteContext ctx) throws WriteFailedException; - - @Nonnull - @Override - public InstanceIdentifier getManagedDataObjectType() { - return instanceIdentifier; - } - - - @Override - public String toString() { - return String.format("Writer[%s]", getManagedDataObjectType().getTargetType().getSimpleName()); - } -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java deleted file mode 100644 index 7c45fcd82..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.write; - -import io.fd.honeycomb.v3po.translate.TranslationException; -import io.fd.honeycomb.v3po.translate.write.WriteContext; -import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistry; -import javax.annotation.Nonnull; - -/** - * Empty registry that does not perform any changes. Can be used in data layer, if we want to disable passing data to - * translation layer. - */ -public class NoopWriterRegistry implements WriterRegistry, AutoCloseable { - - @Override - public void update(@Nonnull final DataObjectUpdates updates, - @Nonnull final WriteContext ctx) throws TranslationException { - // NOOP - } - - @Override - public void close() throws Exception { - // NOOP - } -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionWriteContext.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionWriteContext.java deleted file mode 100644 index 47498f594..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionWriteContext.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.write; - -import static com.google.common.base.Preconditions.checkState; - -import com.google.common.base.Optional; -import com.google.common.util.concurrent.CheckedFuture; -import io.fd.honeycomb.v3po.translate.MappingContext; -import io.fd.honeycomb.v3po.translate.ModificationCache; -import io.fd.honeycomb.v3po.translate.write.WriteContext; -import java.util.Map; -import javax.annotation.Nonnull; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; -import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; -import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; - -/** - * Transaction based WriteContext - */ -public final class TransactionWriteContext implements WriteContext { - - private final DOMDataReadOnlyTransaction beforeTx; - private final DOMDataReadOnlyTransaction afterTx; - private final ModificationCache ctx; - private final BindingNormalizedNodeSerializer serializer; - private final MappingContext mappingContext; - - public TransactionWriteContext(final BindingNormalizedNodeSerializer serializer, - final DOMDataReadOnlyTransaction beforeTx, - final DOMDataReadOnlyTransaction afterTx, - final MappingContext mappingContext) { - this.serializer = serializer; - // TODO do we have a BA transaction adapter ? If so, use it here and don't pass serializer - this.beforeTx = beforeTx; - this.afterTx = afterTx; - this.mappingContext = mappingContext; - this.ctx = new ModificationCache(); - } - - // TODO make this asynchronous - - @Override - public Optional readBefore(@Nonnull final InstanceIdentifier currentId) { - return read(currentId, beforeTx); - } - - @Override - public Optional readAfter(@Nonnull final InstanceIdentifier currentId) { - return read(currentId, afterTx); - } - - - private Optional read(final InstanceIdentifier currentId, - final DOMDataReadOnlyTransaction tx) { - final YangInstanceIdentifier path = serializer.toYangInstanceIdentifier(currentId); - - final CheckedFuture>, ReadFailedException> read = - tx.read(LogicalDatastoreType.CONFIGURATION, path); - - try { - // TODO once the APIs are asynchronous use just Futures.transform - final Optional> optional = read.checkedGet(); - - if (!optional.isPresent()) { - return Optional.absent(); - } - - final NormalizedNode data = optional.get(); - final Map.Entry, DataObject> entry = serializer.fromNormalizedNode(path, data); - - final Class targetType = currentId.getTargetType(); - checkState(targetType.isAssignableFrom(entry.getValue().getClass()), - "Unexpected data object type, should be: %s, but was: %s", targetType, entry.getValue().getClass()); - return Optional.of(targetType.cast(entry.getValue())); - } catch (ReadFailedException e) { - throw new IllegalStateException("Unable to perform read", e); - } - } - - @Nonnull - @Override - public ModificationCache getModificationCache() { - return ctx; - } - - @Nonnull - @Override - public MappingContext getMappingContext() { - return mappingContext; - } - - /** - * Does not close the transactions - */ - @Override - public void close() { - ctx.close(); - beforeTx.close(); - afterTx.close(); - } -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java deleted file mode 100644 index 7433de813..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.write.registry; - -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.common.base.Optional; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Lists; -import com.google.common.collect.Multimap; -import com.google.common.collect.Sets; -import io.fd.honeycomb.v3po.translate.TranslationException; -import io.fd.honeycomb.v3po.translate.util.RWUtils; -import io.fd.honeycomb.v3po.translate.write.DataObjectUpdate; -import io.fd.honeycomb.v3po.translate.write.WriteContext; -import io.fd.honeycomb.v3po.translate.write.WriteFailedException; -import io.fd.honeycomb.v3po.translate.write.Writer; -import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistry; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.concurrent.ThreadSafe; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Flat writer registry, delegating updates to writers in the order writers were submitted. - */ -@ThreadSafe -final class FlatWriterRegistry implements WriterRegistry { - - private static final Logger LOG = LoggerFactory.getLogger(FlatWriterRegistry.class); - - // All types handled by writers directly or as children - private final ImmutableSet> handledTypes; - - private final Set> writersOrderReversed; - private final Set> writersOrder; - private final Map, Writer> writers; - - /** - * Create flat registry instance. - * - * @param writers immutable, ordered map of writers to use to process updates. Order of the writers has to be - * one in which create and update operations should be handled. Deletes will be handled in reversed - * order. All deletes are handled before handling all the updates. - */ - FlatWriterRegistry(@Nonnull final ImmutableMap, Writer> writers) { - this.writers = writers; - this.writersOrderReversed = Sets.newLinkedHashSet(Lists.reverse(Lists.newArrayList(writers.keySet()))); - this.writersOrder = writers.keySet(); - this.handledTypes = getAllHandledTypes(writers); - } - - private static ImmutableSet> getAllHandledTypes( - @Nonnull final ImmutableMap, Writer> writers) { - final ImmutableSet.Builder> handledTypesBuilder = ImmutableSet.builder(); - for (Map.Entry, Writer> writerEntry : writers.entrySet()) { - final InstanceIdentifier writerType = writerEntry.getKey(); - final Writer writer = writerEntry.getValue(); - handledTypesBuilder.add(writerType); - if (writer instanceof SubtreeWriter) { - handledTypesBuilder.addAll(((SubtreeWriter) writer).getHandledChildTypes()); - } - } - return handledTypesBuilder.build(); - } - - @Override - public void update(@Nonnull final DataObjectUpdates updates, - @Nonnull final WriteContext ctx) throws TranslationException { - if (updates.isEmpty()) { - return; - } - - // Optimization - if (updates.containsOnlySingleType()) { - // First process delete - singleUpdate(updates.getDeletes(), ctx); - // Next is update - singleUpdate(updates.getUpdates(), ctx); - } else { - // First process deletes - bulkUpdate(updates.getDeletes(), ctx, true, writersOrderReversed); - // Next are updates - bulkUpdate(updates.getUpdates(), ctx, true, writersOrder); - } - - LOG.debug("Update successful for types: {}", updates.getTypeIntersection()); - LOG.trace("Update successful for: {}", updates); - } - - private void singleUpdate(@Nonnull final Multimap, ? extends DataObjectUpdate> updates, - @Nonnull final WriteContext ctx) throws WriteFailedException { - if (updates.isEmpty()) { - return; - } - - final InstanceIdentifier singleType = updates.keySet().iterator().next(); - LOG.debug("Performing single type update for: {}", singleType); - Collection singleTypeUpdates = updates.get(singleType); - Writer writer = getWriter(singleType); - - if (writer == null) { - // This node must be handled by a subtree writer, find it and call it or else fail - checkArgument(handledTypes.contains(singleType), "Unable to process update. Missing writers for: %s", - singleType); - writer = getSubtreeWriterResponsible(singleType); - singleTypeUpdates = getParentDataObjectUpdate(ctx, updates, writer); - } - - LOG.trace("Performing single type update with writer: {}", writer); - for (DataObjectUpdate singleUpdate : singleTypeUpdates) { - writer.update(singleUpdate.getId(), singleUpdate.getDataBefore(), singleUpdate.getDataAfter(), ctx); - } - } - - private Writer getSubtreeWriterResponsible(final InstanceIdentifier singleType) { - final Writer writer;// This is slow ( minor TODO-perf ) - writer = writers.values().stream() - .filter(w -> w instanceof SubtreeWriter) - .filter(w -> ((SubtreeWriter) w).getHandledChildTypes().contains(singleType)) - .findFirst() - .get(); - return writer; - } - - private Collection getParentDataObjectUpdate(final WriteContext ctx, - final Multimap, ? extends DataObjectUpdate> updates, - final Writer writer) { - // Now read data for subtree reader root, but first keyed ID is needed and that ID can be cut from updates - InstanceIdentifier firstAffectedChildId = ((SubtreeWriter) writer).getHandledChildTypes().stream() - .filter(updates::containsKey) - .map(unkeyedId -> updates.get(unkeyedId)) - .flatMap(doUpdates -> doUpdates.stream()) - .map(DataObjectUpdate::getId) - .findFirst() - .get(); - - final InstanceIdentifier parentKeyedId = - RWUtils.cutId(firstAffectedChildId, writer.getManagedDataObjectType()); - - final Optional parentBefore = ctx.readBefore(parentKeyedId); - final Optional parentAfter = ctx.readAfter(parentKeyedId); - return Collections.singleton( - DataObjectUpdate.create(parentKeyedId, parentBefore.orNull(), parentAfter.orNull())); - } - - private void bulkUpdate(@Nonnull final Multimap, ? extends DataObjectUpdate> updates, - @Nonnull final WriteContext ctx, - final boolean attemptRevert, - @Nonnull final Set> writersOrder) throws BulkUpdateException { - if (updates.isEmpty()) { - return; - } - - LOG.debug("Performing bulk update with revert attempt: {} for: {}", attemptRevert, updates.keySet()); - - // Check that all updates can be handled - checkAllTypesCanBeHandled(updates); - - // Capture all changes successfully processed in case revert is needed - final Set> processedNodes = new HashSet<>(); - - // Iterate over all writers and call update if there are any related updates - for (InstanceIdentifier writerType : writersOrder) { - Collection 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) { - try { - writer.update(singleUpdate.getId(), singleUpdate.getDataBefore(), singleUpdate.getDataAfter(), ctx); - processedNodes.add(singleUpdate.getId()); - LOG.trace("Update successful for type: {}", writerType); - LOG.debug("Update successful for: {}", singleUpdate); - } catch (Exception e) { - LOG.error("Error while processing data change of: {} (updates={})", writerType, writersData, e); - - final Reverter reverter = attemptRevert - ? new ReverterImpl(processedNodes, updates, writersOrder, ctx) - : () -> {}; // NOOP reverter - - // Find out which changes left unprocessed - final Set> unprocessedChanges = updates.values().stream() - .map(DataObjectUpdate::getId) - .filter(id -> !processedNodes.contains(id)) - .collect(Collectors.toSet()); - throw new BulkUpdateException(unprocessedChanges, reverter, e); - } - } - } - } - - private void checkAllTypesCanBeHandled( - @Nonnull final Multimap, ? extends DataObjectUpdate> updates) { - if (!handledTypes.containsAll(updates.keySet())) { - final Sets.SetView> missingWriters = Sets.difference(updates.keySet(), handledTypes); - LOG.warn("Unable to process update. Missing writers for: {}", missingWriters); - throw new IllegalArgumentException("Unable to process update. Missing writers for: " + missingWriters); - } - } - - /** - * Check whether {@link SubtreeWriter} is affected by the updates. - * - * @return true if there are any updates to SubtreeWriter's child nodes (those marked by SubtreeWriter - * as being taken care of) - * */ - private static boolean isAffected(final SubtreeWriter writer, - final Multimap, ? extends DataObjectUpdate> updates) { - return !Sets.intersection(writer.getHandledChildTypes(), updates.keySet()).isEmpty(); - } - - @Nullable - private Writer getWriter(@Nonnull final InstanceIdentifier singleType) { - return writers.get(singleType); - } - - // FIXME unit test - private final class ReverterImpl implements Reverter { - - private final Collection> processedNodes; - private final Multimap, ? extends DataObjectUpdate> updates; - private final Set> revertDeleteOrder; - private final WriteContext ctx; - - ReverterImpl(final Collection> processedNodes, - final Multimap, ? extends DataObjectUpdate> updates, - final Set> writersOrderOriginal, - final WriteContext ctx) { - this.processedNodes = processedNodes; - this.updates = updates; - // Use opposite ordering when executing revert - this.revertDeleteOrder = writersOrderOriginal == FlatWriterRegistry.this.writersOrder - ? FlatWriterRegistry.this.writersOrderReversed - : FlatWriterRegistry.this.writersOrder; - this.ctx = ctx; - } - - @Override - public void revert() throws RevertFailedException { - Multimap, DataObjectUpdate> updatesToRevert = - filterAndRevertProcessed(updates, processedNodes); - - LOG.info("Attempting revert for changes: {}", updatesToRevert); - try { - // Perform reversed bulk update without revert attempt - bulkUpdate(updatesToRevert, ctx, true, revertDeleteOrder); - LOG.info("Revert successful"); - } catch (BulkUpdateException e) { - LOG.error("Revert failed", e); - throw new RevertFailedException(e.getFailedIds(), e); - } - } - - /** - * Create new updates map, but only keep already processed changes. Switching before and after data for each - * update. - */ - private Multimap, DataObjectUpdate> filterAndRevertProcessed( - final Multimap, ? extends DataObjectUpdate> updates, - final Collection> processedNodes) { - final Multimap, DataObjectUpdate> filtered = HashMultimap.create(); - for (InstanceIdentifier processedNode : processedNodes) { - final InstanceIdentifier wildcardedIid = RWUtils.makeIidWildcarded(processedNode); - if (updates.containsKey(wildcardedIid)) { - updates.get(wildcardedIid).stream() - .filter(dataObjectUpdate -> processedNode.contains(dataObjectUpdate.getId())) - .forEach(dataObjectUpdate -> filtered.put(processedNode, dataObjectUpdate.reverse())); - } - } - return filtered; - } - } - -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java deleted file mode 100644 index bfac2eedd..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.write.registry; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ImmutableMap; -import io.fd.honeycomb.v3po.translate.util.AbstractSubtreeManagerRegistryBuilderBuilder; -import io.fd.honeycomb.v3po.translate.write.Writer; -import io.fd.honeycomb.v3po.translate.write.registry.ModifiableWriterRegistryBuilder; -import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistry; -import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistryBuilder; -import java.util.Set; -import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.concurrent.NotThreadSafe; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Builder for {@link FlatWriterRegistry} allowing users to specify inter-writer relationships. - */ -@NotThreadSafe -public final class FlatWriterRegistryBuilder - extends AbstractSubtreeManagerRegistryBuilderBuilder, WriterRegistry> - implements ModifiableWriterRegistryBuilder, WriterRegistryBuilder { - - private static final Logger LOG = LoggerFactory.getLogger(FlatWriterRegistryBuilder.class); - - @Override - protected Writer getSubtreeHandler(final @Nonnull Set> handledChildren, - final @Nonnull Writer writer) { - return SubtreeWriter.createForWriter(handledChildren, writer); - } - - /** - * Create FlatWriterRegistry with writers ordered according to submitted relationships. - */ - @Override - public WriterRegistry build() { - final ImmutableMap, Writer> mappedWriters = getMappedHandlers(); - LOG.debug("Building writer registry with writers: {}", - mappedWriters.keySet().stream() - .map(InstanceIdentifier::getTargetType) - .map(Class::getSimpleName) - .collect(Collectors.joining(", "))); - LOG.trace("Building writer registry with writers: {}", mappedWriters); - return new FlatWriterRegistry(mappedWriters); - } - - @VisibleForTesting - @Override - protected ImmutableMap, Writer> getMappedHandlers() { - return super.getMappedHandlers(); - } -} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriter.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriter.java deleted file mode 100644 index e395b29da..000000000 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriter.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.write.registry; - -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.common.collect.Iterables; -import io.fd.honeycomb.v3po.translate.write.WriteContext; -import io.fd.honeycomb.v3po.translate.write.WriteFailedException; -import io.fd.honeycomb.v3po.translate.write.Writer; -import java.util.HashSet; -import java.util.Set; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Simple writer delegate for subtree writers (writers handling also children nodes) providing a list of all the - * children nodes being handled. - */ -final class SubtreeWriter implements Writer { - - private final Writer delegate; - private final Set> handledChildTypes = new HashSet<>(); - - private SubtreeWriter(final Writer delegate, Set> handledTypes) { - this.delegate = delegate; - for (InstanceIdentifier handledType : handledTypes) { - // Iid has to start with writer's handled root type - checkArgument(delegate.getManagedDataObjectType().getTargetType().equals( - handledType.getPathArguments().iterator().next().getType()), - "Handled node from subtree has to be identified by an instance identifier starting from: %s." - + "Instance identifier was: %s", getManagedDataObjectType().getTargetType(), handledType); - checkArgument(Iterables.size(handledType.getPathArguments()) > 1, - "Handled node from subtree identifier too short: %s", handledType); - handledChildTypes.add(InstanceIdentifier.create(Iterables.concat( - getManagedDataObjectType().getPathArguments(), Iterables.skip(handledType.getPathArguments(), 1)))); - } - } - - /** - * Return set of types also handled by this writer. All of the types are children of the type managed by this - * writer excluding the type of this writer. - */ - Set> getHandledChildTypes() { - return handledChildTypes; - } - - @Override - public void update( - @Nonnull final InstanceIdentifier id, - @Nullable final DataObject dataBefore, - @Nullable final DataObject dataAfter, @Nonnull final WriteContext ctx) throws WriteFailedException { - delegate.update(id, dataBefore, dataAfter, ctx); - } - - @Override - @Nonnull - public InstanceIdentifier getManagedDataObjectType() { - return delegate.getManagedDataObjectType(); - } - - /** - * Wrap a writer as a subtree writer. - */ - static Writer createForWriter(@Nonnull final Set> handledChildren, - @Nonnull final Writer writer) { - return new SubtreeWriter<>(writer, handledChildren); - } -} diff --git a/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModule.java b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModule.java index e1f04ebe2..99feb57f9 100644 --- a/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModule.java +++ b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModule.java @@ -1,6 +1,6 @@ package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406; -import io.fd.honeycomb.v3po.translate.util.read.registry.CompositeReaderRegistryBuilder; +import io.fd.honeycomb.translate.util.read.registry.CompositeReaderRegistryBuilder; public class DelegatingReaderRegistryModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.AbstractDelegatingReaderRegistryModule { public DelegatingReaderRegistryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { diff --git a/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingWriterRegistryModule.java b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingWriterRegistryModule.java index 7eadde80e..5ce89b355 100644 --- a/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingWriterRegistryModule.java +++ b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingWriterRegistryModule.java @@ -1,6 +1,6 @@ package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406; -import io.fd.honeycomb.v3po.translate.util.write.registry.FlatWriterRegistryBuilder; +import io.fd.honeycomb.translate.util.write.registry.FlatWriterRegistryBuilder; public class DelegatingWriterRegistryModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.AbstractDelegatingWriterRegistryModule { public DelegatingWriterRegistryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { diff --git a/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/NoopWriterRegistryModule.java b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/NoopWriterRegistryModule.java index 0f72df9da..be6bb9270 100644 --- a/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/NoopWriterRegistryModule.java +++ b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/NoopWriterRegistryModule.java @@ -1,8 +1,8 @@ package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406; -import io.fd.honeycomb.v3po.translate.util.write.NoopWriterRegistry; -import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistry; -import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistryBuilder; +import io.fd.honeycomb.translate.util.write.NoopWriterRegistry; +import io.fd.honeycomb.translate.write.registry.WriterRegistry; +import io.fd.honeycomb.translate.write.registry.WriterRegistryBuilder; public class NoopWriterRegistryModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.AbstractNoopWriterRegistryModule { public NoopWriterRegistryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { diff --git a/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/RealtimeMappingContextModule.java b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/RealtimeMappingContextModule.java index 99cf4b4e1..d245682d0 100644 --- a/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/RealtimeMappingContextModule.java +++ b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/RealtimeMappingContextModule.java @@ -1,7 +1,7 @@ package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406; import com.google.common.base.Optional; -import io.fd.honeycomb.v3po.translate.MappingContext; +import io.fd.honeycomb.translate.MappingContext; import javax.annotation.Nonnull; import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/impl/write/util/TransactionWriteContextTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/impl/write/util/TransactionWriteContextTest.java new file mode 100644 index 000000000..001e5567a --- /dev/null +++ b/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/impl/write/util/TransactionWriteContextTest.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.impl.write.util; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.Futures; +import io.fd.honeycomb.translate.ModificationCache; +import io.fd.honeycomb.translate.util.DataObjects; +import io.fd.honeycomb.translate.util.write.TransactionWriteContext; +import io.fd.honeycomb.translate.MappingContext; +import java.util.Map; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public class TransactionWriteContextTest { + + @Mock + private BindingNormalizedNodeSerializer serializer; + @Mock + private DOMDataReadOnlyTransaction beforeTx; + @Mock + private DOMDataReadOnlyTransaction afterTx; + @Mock + private Map.Entry entry; + @Mock + private MappingContext contextBroker; + + private TransactionWriteContext transactionWriteContext; + + @Before + public void setUp() { + initMocks(this); + transactionWriteContext = new TransactionWriteContext(serializer, beforeTx, afterTx, contextBroker); + } + + @Test + public void testReadBeforeNoData() throws Exception { + when(beforeTx.read(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class))).thenReturn( + Futures.immediateCheckedFuture(Optional.absent())); + + final InstanceIdentifier instanceId = + InstanceIdentifier.create(DataObjects.DataObject1.class); + + final Optional dataObjects = transactionWriteContext.readBefore(instanceId); + assertNotNull(dataObjects); + assertFalse(dataObjects.isPresent()); + + verify(serializer).toYangInstanceIdentifier(instanceId); + verify(serializer, never()).fromNormalizedNode(any(YangInstanceIdentifier.class), any(NormalizedNode.class)); + } + + @Test + public void testReadBefore() throws Exception { + when(beforeTx.read(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class))).thenReturn( + Futures.immediateCheckedFuture(Optional.of(mock(NormalizedNode.class)))); + + final InstanceIdentifier instanceId = + InstanceIdentifier.create(DataObjects.DataObject1.class); + final YangInstanceIdentifier yangId = YangInstanceIdentifier.builder().node(QName.create("n", "d")).build(); + when(serializer.toYangInstanceIdentifier(any(InstanceIdentifier.class))).thenReturn(yangId); + when(serializer.fromNormalizedNode(eq(yangId), any(NormalizedNode.class))).thenReturn(entry); + when(entry.getValue()).thenReturn(mock(DataObjects.DataObject1.class)); + + final Optional dataObjects = transactionWriteContext.readBefore(instanceId); + assertNotNull(dataObjects); + assertTrue(dataObjects.isPresent()); + + verify(serializer).toYangInstanceIdentifier(instanceId); + verify(serializer).fromNormalizedNode(eq(yangId), any(NormalizedNode.class)); + } + + @Test(expected = IllegalStateException.class) + public void testReadBeforeFailed() throws Exception { + when(beforeTx.read(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class))).thenReturn( + Futures.immediateFailedCheckedFuture(mock(ReadFailedException.class))); + transactionWriteContext.readBefore(mock(InstanceIdentifier.class)); + } + + @Test(expected = IllegalStateException.class) + public void testReadAfterFailed() throws Exception { + when(afterTx.read(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class))).thenReturn( + Futures.immediateFailedCheckedFuture(mock(ReadFailedException.class))); + transactionWriteContext.readAfter(mock(InstanceIdentifier.class)); + } + + @Test + public void testGetContext() throws Exception { + assertNotNull(transactionWriteContext.getModificationCache()); + } + + @Test + public void testClose() throws Exception { + final ModificationCache context = transactionWriteContext.getModificationCache(); + transactionWriteContext.close(); + // TODO verify context was closed + } +} \ No newline at end of file diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/DataObjects.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/DataObjects.java new file mode 100644 index 000000000..e602adbb6 --- /dev/null +++ b/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/DataObjects.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util; + +import org.opendaylight.yangtools.yang.binding.ChildOf; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class DataObjects { + public interface DataObject1 extends DataObject { + InstanceIdentifier IID = InstanceIdentifier.create(DataObject1.class); + } + + public interface DataObject2 extends DataObject { + InstanceIdentifier IID = InstanceIdentifier.create(DataObject2.class); + } + + public interface DataObject3 extends DataObject { + InstanceIdentifier IID = InstanceIdentifier.create(DataObject3.class); + interface DataObject31 extends DataObject, ChildOf { + InstanceIdentifier IID = DataObject3.IID.child(DataObject31.class); + } + } + + public interface DataObject4 extends DataObject { + InstanceIdentifier IID = InstanceIdentifier.create(DataObject4.class); + interface DataObject41 extends DataObject, ChildOf { + InstanceIdentifier IID = DataObject4.IID.child(DataObject41.class); + interface DataObject411 extends DataObject, ChildOf { + InstanceIdentifier IID = DataObject41.IID.child(DataObject411.class); + } + } + + interface DataObject42 extends DataObject, ChildOf { + InstanceIdentifier IID = DataObject4.IID.child(DataObject42.class); + } + } +} diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/JsonUtilsTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/JsonUtilsTest.java new file mode 100644 index 000000000..9c97ac503 --- /dev/null +++ b/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/JsonUtilsTest.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.google.common.io.ByteStreams; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import org.junit.Before; +import org.junit.Test; +import org.mockito.MockitoAnnotations; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor; +import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline; +import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl; +import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.EffectiveSchemaContext; + +public class JsonUtilsTest { + + public static final String NAMESPACE = "urn:opendaylight:params:xml:ns:yang:test:persistence"; + + private static final QName ROOT_QNAME = QName.create("urn:ietf:params:xml:ns:netconf:base:1.0", "data"); + private static final QName TOP_CONTAINER_NAME = QName.create(NAMESPACE, "2015-01-05", "top-container"); + private static final QName TOP_CONTAINER2_NAME = QName.create(NAMESPACE, "2015-01-05", "top-container2"); + private static final QName STRING_LEAF_QNAME = QName.create(TOP_CONTAINER_NAME, "string"); + + private Path tmpPersistFile; + private EffectiveSchemaContext effectiveSchemaContext; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + tmpPersistFile = Files.createTempFile("testing-hc-persistence", "json"); + + final CrossSourceStatementReactor.BuildAction buildAction = YangInferencePipeline.RFC6020_REACTOR.newBuild(); + buildAction.addSource(new YangStatementSourceImpl(getClass().getResourceAsStream("/test-persistence.yang"))); + effectiveSchemaContext = buildAction.buildEffective(); + } + + @Test + public void testPersist() throws Exception { + + NormalizedNode data = getData("testing"); + JsonUtils.writeJsonRoot(data, effectiveSchemaContext, Files.newOutputStream(tmpPersistFile, StandardOpenOption.CREATE)); + assertTrue(Files.exists(tmpPersistFile)); + + String persisted = new String(Files.readAllBytes(tmpPersistFile)); + String expected = + new String(ByteStreams.toByteArray(getClass().getResourceAsStream("/expected-persisted-output.txt"))); + + assertEquals(expected, persisted); + + data = getData("testing2"); + JsonUtils.writeJsonRoot(data, effectiveSchemaContext, Files.newOutputStream(tmpPersistFile, StandardOpenOption.CREATE)); + persisted = new String(Files.readAllBytes(tmpPersistFile)); + assertEquals(expected.replace("testing", "testing2"), persisted); + + // File has to stay even after close + assertTrue(Files.exists(tmpPersistFile)); + } + + @Test + public void testRestore() throws Exception { + final ContainerNode normalizedNodeOptional = JsonUtils + .readJsonRoot(effectiveSchemaContext, getClass().getResourceAsStream("/expected-persisted-output.txt")); + assertEquals(getData("testing"), normalizedNodeOptional); + } + + @Test(expected = IllegalArgumentException.class) + public void testRestoreInvalidFile() throws Exception { + JsonUtils.readJsonRoot(effectiveSchemaContext, getClass().getResourceAsStream("/test-persistence.yang")); + } + + private ContainerNode getData(final String stringValue) { + return Builders.containerBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(ROOT_QNAME)) + .withChild(Builders.containerBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_NAME)) + .withChild(ImmutableNodes.leafNode(STRING_LEAF_QNAME, stringValue)) + .build()) + .withChild(Builders.containerBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER2_NAME)) + .withChild(ImmutableNodes.leafNode(STRING_LEAF_QNAME, stringValue)) + .build()) + .build(); + } +} \ No newline at end of file diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/read/registry/CompositeReaderRegistryBuilderTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/read/registry/CompositeReaderRegistryBuilderTest.java new file mode 100644 index 000000000..d742575be --- /dev/null +++ b/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/read/registry/CompositeReaderRegistryBuilderTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.read.registry; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import io.fd.honeycomb.translate.read.Reader; +import io.fd.honeycomb.translate.read.registry.ReaderRegistry; +import io.fd.honeycomb.translate.util.DataObjects; +import java.util.List; +import java.util.Map; +import org.junit.Test; +import org.mockito.Mockito; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class CompositeReaderRegistryBuilderTest { + + private Reader> reader1 = + mock(DataObjects.DataObject1.class); + private Reader> reader2 = + mock(DataObjects.DataObject2.class); + private Reader> reader3 = + mock(DataObjects.DataObject3.class); + private Reader> reader31 = + mock(DataObjects.DataObject3.DataObject31.class); + + private Reader> reader4 = + mock(DataObjects.DataObject4.class); + private Reader> reader41 = + mock(DataObjects.DataObject4.DataObject41.class); + private Reader> reader411 = + mock(DataObjects.DataObject4.DataObject41.DataObject411.class); + private Reader> reader42 = + mock(DataObjects.DataObject4.DataObject42.class); + + @SuppressWarnings("unchecked") + private Reader> mock(final Class dataObjectType) { + final Reader> mock = Mockito.mock(Reader.class); + try { + when(mock.getManagedDataObjectType()) + .thenReturn(((InstanceIdentifier) dataObjectType.getDeclaredField("IID").get(null))); + } catch (IllegalAccessException | NoSuchFieldException e) { + throw new RuntimeException(e); + } + return mock; + } + + @Test + public void testCompositeStructure() throws Exception { + final CompositeReaderRegistryBuilder compositeReaderRegistryBuilder = new CompositeReaderRegistryBuilder(); + /* + Composite reader structure ordered left from right + + 1, 2, 3, 4 + 31 42, 41 + 411 + */ + compositeReaderRegistryBuilder.add(reader1); + compositeReaderRegistryBuilder.addAfter(reader2, reader1.getManagedDataObjectType()); + compositeReaderRegistryBuilder.addAfter(reader3, reader2.getManagedDataObjectType()); + compositeReaderRegistryBuilder.addAfter(reader31, reader1.getManagedDataObjectType()); + compositeReaderRegistryBuilder.addAfter(reader4, reader3.getManagedDataObjectType()); + compositeReaderRegistryBuilder.add(reader41); + compositeReaderRegistryBuilder.addBefore(reader42, reader41.getManagedDataObjectType()); + compositeReaderRegistryBuilder.add(reader411); + + final ReaderRegistry build = compositeReaderRegistryBuilder.build(); + + final Map, Reader>> rootReaders = + ((CompositeReaderRegistry) build).getRootReaders(); + final List> rootReaderOrder = Lists.newArrayList(rootReaders.keySet()); + + assertEquals(reader1.getManagedDataObjectType().getTargetType(), rootReaderOrder.get(0)); + assertEquals(reader2.getManagedDataObjectType().getTargetType(), rootReaderOrder.get(1)); + assertEquals(reader3.getManagedDataObjectType().getTargetType(), rootReaderOrder.get(2)); + assertEquals(reader4.getManagedDataObjectType().getTargetType(), rootReaderOrder.get(3)); + + assertFalse(rootReaders.get(DataObjects.DataObject1.class) instanceof CompositeReader); + assertFalse(rootReaders.get(DataObjects.DataObject2.class) instanceof CompositeReader); + assertTrue(rootReaders.get(DataObjects.DataObject3.class) instanceof CompositeReader); + assertTrue(rootReaders.get(DataObjects.DataObject4.class) instanceof CompositeReader); + + final ImmutableMap, Reader>> childReaders = + ((CompositeReader>) rootReaders + .get(DataObjects.DataObject4.class)).getChildReaders(); + final List> orderedChildReaders = Lists.newArrayList(childReaders.keySet()); + + assertEquals(reader42.getManagedDataObjectType().getTargetType(), orderedChildReaders.get(0)); + assertEquals(reader41.getManagedDataObjectType().getTargetType(), orderedChildReaders.get(1)); + assertTrue(childReaders.get(DataObjects.DataObject4.DataObject41.class) instanceof CompositeReader); + assertFalse(childReaders.get(DataObjects.DataObject4.DataObject42.class) instanceof CompositeReader); + } +} \ No newline at end of file diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/read/registry/SubtreeReaderTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/read/registry/SubtreeReaderTest.java new file mode 100644 index 000000000..799b9553f --- /dev/null +++ b/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/read/registry/SubtreeReaderTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.read.registry; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import com.google.common.base.Optional; +import com.google.common.collect.Sets; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.Reader; +import io.fd.honeycomb.translate.util.DataObjects; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.ChildOf; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class SubtreeReaderTest { + + @Mock + private Reader> delegate; + @Mock + private Reader> delegateLocal; + @Mock + private ReadContext ctx; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + doReturn(DataObjects.DataObject4.IID).when(delegate).getManagedDataObjectType(); + doReturn(DataObject1.IID).when(delegateLocal).getManagedDataObjectType(); + } + + @Test + public void testCreate() throws Exception { + final Reader> subtreeR = + SubtreeReader.createForReader(Sets.newHashSet(DataObjects.DataObject4.DataObject41.IID), delegate); + + subtreeR.getBuilder(DataObjects.DataObject4.IID); + verify(delegate).getBuilder(DataObjects.DataObject4.IID); + + subtreeR.getManagedDataObjectType(); + verify(delegate, atLeastOnce()).getManagedDataObjectType(); + } + + @Test(expected = IllegalArgumentException.class) + public void testCreateInvalid() throws Exception { + SubtreeReader.createForReader(Sets.newHashSet(DataObjects.DataObject1.IID), delegate); + } + + @Test(expected = IllegalStateException.class) + public void testReadOnlySubtreeCannotFilter() throws Exception { + final Reader> subtreeR = + SubtreeReader.createForReader(Sets.newHashSet(DataObjects.DataObject4.DataObject41.IID), delegate); + + doReturn(Optional.fromNullable(mock(DataObjects.DataObject4.class))).when(delegate).read(DataObjects.DataObject4.IID, ctx); + subtreeR.read(DataObjects.DataObject4.DataObject41.IID, ctx); + } + + @Test + public void testReadOnlySubtreeNotPresent() throws Exception { + final Reader> subtreeR = + SubtreeReader.createForReader(Sets.newHashSet(DataObjects.DataObject4.DataObject41.IID), delegate); + + doReturn(Optional.absent()).when(delegate).read(DataObjects.DataObject4.IID, ctx); + assertFalse(subtreeR.read(DataObjects.DataObject4.DataObject41.IID, ctx).isPresent()); + } + + @Test + public void testReadOnlySubtreeChild() throws Exception { + final Reader> subtreeR = + SubtreeReader.createForReader(Sets.newHashSet(DataObject1.DataObject11.IID), delegateLocal); + + final DataObject1 mock = mock(DataObject1.class); + final DataObject1.DataObject11 mock11 = mock(DataObject1.DataObject11.class); + doReturn(mock11).when(mock).getDataObject11(); + doReturn(Optional.fromNullable(mock)).when(delegateLocal).read(DataObject1.IID, ctx); + assertEquals(mock11, subtreeR.read(DataObject1.DataObject11.IID, ctx).get()); + } + + @Test + public void testReadEntireSubtree() throws Exception { + final Reader> subtreeR = + SubtreeReader.createForReader(Sets.newHashSet(DataObject1.DataObject11.IID), delegateLocal); + + final DataObject1 mock = mock(DataObject1.class); + final DataObject1.DataObject11 mock11 = mock(DataObject1.DataObject11.class); + doReturn(mock11).when(mock).getDataObject11(); + doReturn(Optional.fromNullable(mock)).when(delegateLocal).read(DataObject1.IID, ctx); + assertEquals(mock, subtreeR.read(DataObject1.IID, ctx).get()); + } + + public abstract static class DataObject1 implements DataObject { + public static InstanceIdentifier IID = InstanceIdentifier.create(DataObject1.class); + + public abstract DataObject11 getDataObject11(); + + public abstract static class DataObject11 implements DataObject, ChildOf { + public static InstanceIdentifier IID = DataObject1.IID.child(DataObject11.class); + } + } +} diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/read/registry/TypeHierarchyTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/read/registry/TypeHierarchyTest.java new file mode 100644 index 000000000..3b3ea3a35 --- /dev/null +++ b/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/read/registry/TypeHierarchyTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.read.registry; + +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.hasItems; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import com.google.common.collect.Sets; +import io.fd.honeycomb.translate.util.DataObjects; +import org.hamcrest.CoreMatchers; +import org.junit.Test; + +public class TypeHierarchyTest { + + @Test + public void testHierarchy() throws Exception { + final TypeHierarchy typeHierarchy = TypeHierarchy.create(Sets.newHashSet( + DataObjects.DataObject4.DataObject41.DataObject411.IID, + DataObjects.DataObject4.DataObject41.IID,/* Included in previous already */ + DataObjects.DataObject1.IID, + DataObjects.DataObject3.DataObject31.IID)); + + // Roots + assertThat(typeHierarchy.getRoots().size(), is(3)); + assertThat(typeHierarchy.getRoots(), CoreMatchers + .hasItems(DataObjects.DataObject1.IID, DataObjects.DataObject3.IID, DataObjects.DataObject4.IID)); + + // Leaves + assertThat(typeHierarchy.getDirectChildren(DataObjects.DataObject1.IID).size(), is(0)); + assertThat(typeHierarchy.getDirectChildren(DataObjects.DataObject3.DataObject31.IID).size(), is(0)); + assertThat(typeHierarchy.getDirectChildren(DataObjects.DataObject4.DataObject41.DataObject411.IID).size(), is(0)); + + // Intermediate leaves + assertThat(typeHierarchy.getDirectChildren(DataObjects.DataObject3.IID).size(), is(1)); + assertThat(typeHierarchy.getDirectChildren(DataObjects.DataObject3.IID), CoreMatchers + .hasItem(DataObjects.DataObject3.DataObject31.IID)); + assertEquals(typeHierarchy.getDirectChildren(DataObjects.DataObject3.IID), typeHierarchy.getAllChildren( + DataObjects.DataObject3.IID)); + + assertThat(typeHierarchy.getDirectChildren(DataObjects.DataObject4.DataObject41.IID).size(), is(1)); + assertThat(typeHierarchy.getDirectChildren(DataObjects.DataObject4.DataObject41.IID), CoreMatchers.hasItem( + DataObjects.DataObject4.DataObject41.DataObject411.IID)); + assertEquals(typeHierarchy.getDirectChildren(DataObjects.DataObject4.DataObject41.IID), typeHierarchy.getAllChildren( + DataObjects.DataObject4.DataObject41.IID)); + + assertThat(typeHierarchy.getDirectChildren(DataObjects.DataObject4.IID).size(), is(1)); + assertThat(typeHierarchy.getDirectChildren(DataObjects.DataObject4.IID), CoreMatchers + .hasItem(DataObjects.DataObject4.DataObject41.IID)); + assertThat(typeHierarchy.getAllChildren(DataObjects.DataObject4.IID).size(), is(2)); + assertTrue(typeHierarchy.getAllChildren(DataObjects.DataObject4.IID).contains(DataObjects.DataObject4.DataObject41.IID)); + assertTrue(typeHierarchy.getAllChildren(DataObjects.DataObject4.IID).contains(DataObjects.DataObject4.DataObject41.DataObject411.IID)); + } +} + diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/write/registry/FlatWriterRegistryBuilderTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/write/registry/FlatWriterRegistryBuilderTest.java new file mode 100644 index 000000000..79590a5a0 --- /dev/null +++ b/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/write/registry/FlatWriterRegistryBuilderTest.java @@ -0,0 +1,131 @@ +package io.fd.honeycomb.translate.util.write.registry; + +import static org.hamcrest.CoreMatchers.anyOf; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.hasItems; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import io.fd.honeycomb.translate.util.DataObjects; +import io.fd.honeycomb.translate.write.Writer; +import java.util.ArrayList; +import java.util.List; +import org.junit.Test; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class FlatWriterRegistryBuilderTest { + + + @Test + public void testRelationsBefore() throws Exception { + final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder(); + /* + 1 -> 2 -> 3 + -> 4 + */ + flatWriterRegistryBuilder.add(mockWriter(DataObjects.DataObject3.class)); + flatWriterRegistryBuilder.add(mockWriter(DataObjects.DataObject4.class)); + flatWriterRegistryBuilder.addBefore(mockWriter(DataObjects.DataObject2.class), + Lists.newArrayList(DataObjects.DataObject3.IID, DataObjects.DataObject4.IID)); + flatWriterRegistryBuilder.addBefore(mockWriter(DataObjects.DataObject1.class), DataObjects.DataObject2.IID); + final ImmutableMap, Writer> mappedWriters = + flatWriterRegistryBuilder.getMappedHandlers(); + + final ArrayList> typesInList = Lists.newArrayList(mappedWriters.keySet()); + assertEquals(DataObjects.DataObject1.IID, typesInList.get(0)); + assertEquals(DataObjects.DataObject2.IID, typesInList.get(1)); + assertThat(typesInList.get(2), anyOf(equalTo(DataObjects.DataObject3.IID), equalTo(DataObjects.DataObject4.IID))); + assertThat(typesInList.get(3), anyOf(equalTo(DataObjects.DataObject3.IID), equalTo(DataObjects.DataObject4.IID))); + } + + @Test + public void testRelationsAfter() throws Exception { + final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder(); + /* + 1 -> 2 -> 3 + -> 4 + */ + flatWriterRegistryBuilder.add(mockWriter(DataObjects.DataObject1.class)); + flatWriterRegistryBuilder.addAfter(mockWriter(DataObjects.DataObject2.class), DataObjects.DataObject1.IID); + flatWriterRegistryBuilder.addAfter(mockWriter(DataObjects.DataObject3.class), DataObjects.DataObject2.IID); + flatWriterRegistryBuilder.addAfter(mockWriter(DataObjects.DataObject4.class), DataObjects.DataObject2.IID); + final ImmutableMap, Writer> mappedWriters = + flatWriterRegistryBuilder.getMappedHandlers(); + + final List> typesInList = Lists.newArrayList(mappedWriters.keySet()); + assertEquals(DataObjects.DataObject1.IID, typesInList.get(0)); + assertEquals(DataObjects.DataObject2.IID, typesInList.get(1)); + assertThat(typesInList.get(2), anyOf(equalTo(DataObjects.DataObject3.IID), equalTo(DataObjects.DataObject4.IID))); + assertThat(typesInList.get(3), anyOf(equalTo(DataObjects.DataObject3.IID), equalTo(DataObjects.DataObject4.IID))); + } + + @Test(expected = IllegalArgumentException.class) + public void testRelationsLoop() throws Exception { + final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder(); + /* + 1 -> 2 -> 1 + */ + flatWriterRegistryBuilder.add(mockWriter(DataObjects.DataObject1.class)); + flatWriterRegistryBuilder.addAfter(mockWriter(DataObjects.DataObject2.class), DataObjects.DataObject1.IID); + flatWriterRegistryBuilder.addAfter(mockWriter(DataObjects.DataObject1.class), DataObjects.DataObject2.IID); + } + + @Test(expected = IllegalArgumentException.class) + public void testAddWriterTwice() throws Exception { + final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder(); + flatWriterRegistryBuilder.add(mockWriter(DataObjects.DataObject1.class)); + flatWriterRegistryBuilder.add(mockWriter(DataObjects.DataObject1.class)); + } + + @Test + public void testAddSubtreeWriter() throws Exception { + final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder(); + flatWriterRegistryBuilder.subtreeAdd( + Sets.newHashSet(DataObjects.DataObject4.DataObject41.IID, + DataObjects.DataObject4.DataObject41.IID), + mockWriter(DataObjects.DataObject4.class)); + final ImmutableMap, Writer> mappedWriters = + flatWriterRegistryBuilder.getMappedHandlers(); + final ArrayList> typesInList = Lists.newArrayList(mappedWriters.keySet()); + + assertEquals(DataObjects.DataObject4.IID, typesInList.get(0)); + assertEquals(1, typesInList.size()); + } + + @Test + public void testCreateSubtreeWriter() throws Exception { + final Writer forWriter = SubtreeWriter.createForWriter(Sets.newHashSet( + DataObjects.DataObject4.DataObject41.IID, + DataObjects.DataObject4.DataObject41.DataObject411.IID, + DataObjects.DataObject4.DataObject42.IID), + mockWriter(DataObjects.DataObject4.class)); + assertThat(forWriter, instanceOf(SubtreeWriter.class)); + assertThat(((SubtreeWriter) forWriter).getHandledChildTypes().size(), is(3)); + assertThat(((SubtreeWriter) forWriter).getHandledChildTypes(), hasItems(DataObjects.DataObject4.DataObject41.IID, + DataObjects.DataObject4.DataObject42.IID, DataObjects.DataObject4.DataObject41.DataObject411.IID)); + } + + @Test(expected = IllegalArgumentException.class) + public void testCreateInvalidSubtreeWriter() throws Exception { + SubtreeWriter.createForWriter(Sets.newHashSet( + InstanceIdentifier.create(DataObjects.DataObject3.class).child(DataObjects.DataObject3.DataObject31.class)), + mockWriter(DataObjects.DataObject4.class)); + } + + @SuppressWarnings("unchecked") + private Writer mockWriter(final Class doClass) + throws NoSuchFieldException, IllegalAccessException { + final Writer mock = mock(Writer.class); + when(mock.getManagedDataObjectType()).thenReturn((InstanceIdentifier) doClass.getDeclaredField("IID").get(null)); + return mock; + } + +} \ No newline at end of file diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/write/registry/FlatWriterRegistryTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/write/registry/FlatWriterRegistryTest.java new file mode 100644 index 000000000..6b75f923e --- /dev/null +++ b/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/write/registry/FlatWriterRegistryTest.java @@ -0,0 +1,265 @@ +package io.fd.honeycomb.translate.util.write.registry; + +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Multimap; +import io.fd.honeycomb.translate.util.DataObjects; +import io.fd.honeycomb.translate.write.DataObjectUpdate; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.Writer; +import io.fd.honeycomb.translate.util.DataObjects.DataObject1; +import io.fd.honeycomb.translate.write.registry.WriterRegistry; +import org.hamcrest.CoreMatchers; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class FlatWriterRegistryTest { + + @Mock + private Writer writer1; + @Mock + private Writer writer2; + @Mock + private Writer writer3; + @Mock + private WriteContext ctx; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(writer1.getManagedDataObjectType()).thenReturn(DataObjects.DataObject1.IID); + when(writer2.getManagedDataObjectType()).thenReturn(DataObjects.DataObject2.IID); + when(writer3.getManagedDataObjectType()).thenReturn(DataObjects.DataObject3.IID); + } + + @Test + public void testMultipleUpdatesForSingleWriter() throws Exception { + final FlatWriterRegistry flatWriterRegistry = + new FlatWriterRegistry(ImmutableMap.of(DataObjects.DataObject1.IID, writer1, DataObjects.DataObject2.IID, writer2)); + + final Multimap, DataObjectUpdate> updates = HashMultimap.create(); + final InstanceIdentifier iid = InstanceIdentifier.create(DataObjects.DataObject1.class); + final InstanceIdentifier iid2 = InstanceIdentifier.create(DataObjects.DataObject1.class); + final DataObjects.DataObject1 dataObject = mock(DataObjects.DataObject1.class); + updates.put(DataObjects.DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject)); + updates.put(DataObjects.DataObject1.IID, DataObjectUpdate.create(iid2, dataObject, dataObject)); + flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx); + + verify(writer1).update(iid, dataObject, dataObject, ctx); + verify(writer1).update(iid2, dataObject, dataObject, ctx); + // Invoked when registry is being created + verifyNoMoreInteractions(writer1); + verifyZeroInteractions(writer2); + } + + @Test + public void testMultipleUpdatesForMultipleWriters() throws Exception { + final FlatWriterRegistry flatWriterRegistry = + new FlatWriterRegistry(ImmutableMap.of(DataObjects.DataObject1.IID, writer1, DataObjects.DataObject2.IID, writer2)); + + final Multimap, DataObjectUpdate> updates = HashMultimap.create(); + final InstanceIdentifier iid = InstanceIdentifier.create(DataObjects.DataObject1.class); + final DataObjects.DataObject1 dataObject = mock(DataObjects.DataObject1.class); + updates.put(DataObjects.DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject)); + final InstanceIdentifier iid2 = InstanceIdentifier.create(DataObjects.DataObject2.class); + final DataObjects.DataObject2 dataObject2 = mock(DataObjects.DataObject2.class); + updates.put(DataObjects.DataObject2.IID, DataObjectUpdate.create(iid2, dataObject2, dataObject2)); + flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx); + + final InOrder inOrder = inOrder(writer1, writer2); + inOrder.verify(writer1).update(iid, dataObject, dataObject, ctx); + inOrder.verify(writer2).update(iid2, dataObject2, dataObject2, ctx); + + verifyNoMoreInteractions(writer1); + verifyNoMoreInteractions(writer2); + } + + @Test + public void testMultipleDeletesForMultipleWriters() throws Exception { + final FlatWriterRegistry flatWriterRegistry = + new FlatWriterRegistry(ImmutableMap.of(DataObjects.DataObject1.IID, writer1, DataObjects.DataObject2.IID, writer2)); + + final Multimap, DataObjectUpdate.DataObjectDelete> deletes = HashMultimap.create(); + final InstanceIdentifier iid = InstanceIdentifier.create(DataObjects.DataObject1.class); + final DataObjects.DataObject1 dataObject = mock(DataObjects.DataObject1.class); + deletes.put(DataObjects.DataObject1.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid, dataObject, null))); + final InstanceIdentifier iid2 = InstanceIdentifier.create(DataObjects.DataObject2.class); + final DataObjects.DataObject2 dataObject2 = mock(DataObjects.DataObject2.class); + deletes.put(DataObjects.DataObject2.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid2, dataObject2, null))); + flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(ImmutableMultimap.of(), deletes), ctx); + + final InOrder inOrder = inOrder(writer1, writer2); + // Reversed order of invocation, first writer2 and then writer1 + inOrder.verify(writer2).update(iid2, dataObject2, null, ctx); + inOrder.verify(writer1).update(iid, dataObject, null, ctx); + + verifyNoMoreInteractions(writer1); + verifyNoMoreInteractions(writer2); + } + + @Test + public void testMultipleUpdatesAndDeletesForMultipleWriters() throws Exception { + final FlatWriterRegistry flatWriterRegistry = + new FlatWriterRegistry(ImmutableMap.of(DataObjects.DataObject1.IID, writer1, DataObjects.DataObject2.IID, writer2)); + + final Multimap, DataObjectUpdate.DataObjectDelete> deletes = HashMultimap.create(); + final Multimap, DataObjectUpdate> updates = HashMultimap.create(); + final InstanceIdentifier iid = InstanceIdentifier.create(DataObjects.DataObject1.class); + final DataObjects.DataObject1 dataObject = mock(DataObjects.DataObject1.class); + // Writer 1 delete + deletes.put(DataObjects.DataObject1.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid, dataObject, null))); + // Writer 1 update + updates.put(DataObjects.DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject)); + final InstanceIdentifier iid2 = InstanceIdentifier.create(DataObjects.DataObject2.class); + final DataObjects.DataObject2 dataObject2 = mock(DataObjects.DataObject2.class); + // Writer 2 delete + deletes.put(DataObjects.DataObject2.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid2, dataObject2, null))); + // Writer 2 update + updates.put(DataObjects.DataObject2.IID, DataObjectUpdate.create(iid2, dataObject2, dataObject2)); + flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, deletes), ctx); + + final InOrder inOrder = inOrder(writer1, writer2); + // Reversed order of invocation, first writer2 and then writer1 for deletes + inOrder.verify(writer2).update(iid2, dataObject2, null, ctx); + inOrder.verify(writer1).update(iid, dataObject, null, ctx); + // Then also updates are processed + inOrder.verify(writer1).update(iid, dataObject, dataObject, ctx); + inOrder.verify(writer2).update(iid2, dataObject2, dataObject2, ctx); + + verifyNoMoreInteractions(writer1); + verifyNoMoreInteractions(writer2); + } + + @Test(expected = IllegalArgumentException.class) + public void testMultipleUpdatesOneMissing() throws Exception { + final FlatWriterRegistry flatWriterRegistry = + new FlatWriterRegistry(ImmutableMap.of(DataObjects.DataObject1.IID, writer1)); + + final Multimap, DataObjectUpdate> updates = HashMultimap.create(); + addUpdate(updates, DataObjects.DataObject1.class); + addUpdate(updates, DataObjects.DataObject2.class); + flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx); + } + + @Test + public void testMultipleUpdatesOneFailing() throws Exception { + final FlatWriterRegistry flatWriterRegistry = + new FlatWriterRegistry(ImmutableMap.of(DataObjects.DataObject1.IID, writer1, DataObjects.DataObject2.IID, writer2)); + + // Writer1 always fails + doThrow(new RuntimeException()).when(writer1) + .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class)); + + final Multimap, DataObjectUpdate> updates = HashMultimap.create(); + addUpdate(updates, DataObjects.DataObject1.class); + addUpdate(updates, DataObjects.DataObject2.class); + + try { + flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx); + fail("Bulk update should have failed on writer1"); + } catch (WriterRegistry.BulkUpdateException e) { + assertThat(e.getFailedIds().size(), is(2)); + assertThat(e.getFailedIds(), CoreMatchers.hasItem(InstanceIdentifier.create(DataObjects.DataObject2.class))); + assertThat(e.getFailedIds(), CoreMatchers.hasItem(InstanceIdentifier.create(DataObjects.DataObject1.class))); + } + } + + @Test + public void testMultipleUpdatesOneFailingThenRevertWithSuccess() throws Exception { + final FlatWriterRegistry flatWriterRegistry = + new FlatWriterRegistry( + ImmutableMap.of(DataObjects.DataObject1.IID, writer1, DataObjects.DataObject2.IID, writer2, DataObjects.DataObject3.IID, writer3)); + + // Writer1 always fails + doThrow(new RuntimeException()).when(writer3) + .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class)); + + final Multimap, DataObjectUpdate> updates = HashMultimap.create(); + addUpdate(updates, DataObjects.DataObject1.class); + addUpdate(updates, DataObjects.DataObject3.class); + final InstanceIdentifier iid2 = InstanceIdentifier.create(DataObjects.DataObject2.class); + final DataObjects.DataObject2 before2 = mock(DataObjects.DataObject2.class); + final DataObjects.DataObject2 after2 = mock(DataObjects.DataObject2.class); + updates.put(DataObjects.DataObject2.IID, DataObjectUpdate.create(iid2, before2, after2)); + + try { + flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx); + fail("Bulk update should have failed on writer1"); + } catch (WriterRegistry.BulkUpdateException e) { + assertThat(e.getFailedIds().size(), is(1)); + + final InOrder inOrder = inOrder(writer1, writer2, writer3); + inOrder.verify(writer1) + .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class)); + inOrder.verify(writer2) + .update(iid2, before2, after2, ctx); + inOrder.verify(writer3) + .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class)); + + e.revertChanges(); + // Revert changes. Successful updates are iterated in reverse + inOrder.verify(writer2) + .update(iid2, after2, before2, ctx); + inOrder.verify(writer1) + .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class)); + verifyNoMoreInteractions(writer3); + } + } + + @Test + public void testMultipleUpdatesOneFailingThenRevertWithFail() throws Exception { + final FlatWriterRegistry flatWriterRegistry = + new FlatWriterRegistry( + ImmutableMap.of(DataObjects.DataObject1.IID, writer1, DataObjects.DataObject2.IID, writer2, DataObjects.DataObject3.IID, writer3)); + + // Writer1 always fails + doThrow(new RuntimeException()).when(writer3) + .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class)); + + final Multimap, DataObjectUpdate> updates = HashMultimap.create(); + addUpdate(updates, DataObjects.DataObject1.class); + addUpdate(updates, DataObjects.DataObject2.class); + addUpdate(updates, DataObjects.DataObject3.class); + + try { + flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx); + fail("Bulk update should have failed on writer1"); + } catch (WriterRegistry.BulkUpdateException e) { + // Writer1 always fails from now + doThrow(new RuntimeException()).when(writer1) + .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class)); + try { + e.revertChanges(); + } catch (WriterRegistry.Reverter.RevertFailedException e1) { + assertThat(e1.getNotRevertedChanges().size(), is(1)); + assertThat(e1.getNotRevertedChanges(), CoreMatchers + .hasItem(InstanceIdentifier.create(DataObjects.DataObject1.class))); + } + } + } + + private void addUpdate(final Multimap, DataObjectUpdate> updates, + final Class type) throws Exception { + final InstanceIdentifier iid = (InstanceIdentifier) type.getDeclaredField("IID").get(null); + updates.put(iid, DataObjectUpdate.create(iid, mock(type), mock(type))); + } +} \ No newline at end of file diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/write/registry/SubtreeWriterTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/write/registry/SubtreeWriterTest.java new file mode 100644 index 000000000..313dd34a8 --- /dev/null +++ b/infra/translate-utils/src/test/java/io/fd/honeycomb/translate/util/write/registry/SubtreeWriterTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.write.registry; + +import static org.hamcrest.CoreMatchers.hasItem; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.when; + +import com.google.common.collect.Sets; +import io.fd.honeycomb.translate.util.DataObjects; +import io.fd.honeycomb.translate.write.Writer; +import java.util.Collections; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class SubtreeWriterTest { + + @Mock + Writer writer; + @Mock + Writer writer11; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(writer.getManagedDataObjectType()).thenReturn(DataObjects.DataObject4.IID); + when(writer11.getManagedDataObjectType()).thenReturn(DataObjects.DataObject4.DataObject41.IID); + } + + @Test(expected = IllegalArgumentException.class) + public void testSubtreeWriterCreationFail() throws Exception { + // The subtree node identified by IID.c(DataObject.class) is not a child of writer.getManagedDataObjectType + SubtreeWriter.createForWriter(Collections.singleton(InstanceIdentifier.create(DataObject.class)), writer); + } + + @Test(expected = IllegalArgumentException.class) + public void testSubtreeWriterCreationFailInvalidIid() throws Exception { + // The subtree node identified by IID.c(DataObject.class) is not a child of writer.getManagedDataObjectType + SubtreeWriter.createForWriter(Collections.singleton(DataObjects.DataObject4.IID), writer); + } + + @Test + public void testSubtreeWriterCreation() throws Exception { + final SubtreeWriter forWriter = (SubtreeWriter) SubtreeWriter.createForWriter(Sets.newHashSet( + DataObjects.DataObject4.DataObject41.IID, + DataObjects.DataObject4.DataObject41.DataObject411.IID, + DataObjects.DataObject4.DataObject42.IID), + writer); + + assertEquals(writer.getManagedDataObjectType(), forWriter.getManagedDataObjectType()); + assertEquals(3, forWriter.getHandledChildTypes().size()); + } + + @Test + public void testSubtreeWriterHandledTypes() throws Exception { + final SubtreeWriter forWriter = (SubtreeWriter) SubtreeWriter.createForWriter(Sets.newHashSet( + DataObjects.DataObject4.DataObject41.DataObject411.IID), + writer); + + assertEquals(writer.getManagedDataObjectType(), forWriter.getManagedDataObjectType()); + assertEquals(1, forWriter.getHandledChildTypes().size()); + assertThat(forWriter.getHandledChildTypes(), hasItem(DataObjects.DataObject4.DataObject41.DataObject411.IID)); + } + +} \ No newline at end of file diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/util/TransactionWriteContextTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/util/TransactionWriteContextTest.java deleted file mode 100644 index 7c93e992a..000000000 --- a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/util/TransactionWriteContextTest.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.impl.write.util; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.initMocks; - -import com.google.common.base.Optional; -import com.google.common.util.concurrent.Futures; -import io.fd.honeycomb.v3po.translate.MappingContext; -import io.fd.honeycomb.v3po.translate.ModificationCache; -import io.fd.honeycomb.v3po.translate.util.DataObjects; -import io.fd.honeycomb.v3po.translate.util.write.TransactionWriteContext; -import java.util.Map; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; -import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; -import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; - -public class TransactionWriteContextTest { - - @Mock - private BindingNormalizedNodeSerializer serializer; - @Mock - private DOMDataReadOnlyTransaction beforeTx; - @Mock - private DOMDataReadOnlyTransaction afterTx; - @Mock - private Map.Entry entry; - @Mock - private MappingContext contextBroker; - - private TransactionWriteContext transactionWriteContext; - - @Before - public void setUp() { - initMocks(this); - transactionWriteContext = new TransactionWriteContext(serializer, beforeTx, afterTx, contextBroker); - } - - @Test - public void testReadBeforeNoData() throws Exception { - when(beforeTx.read(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class))).thenReturn( - Futures.immediateCheckedFuture(Optional.absent())); - - final InstanceIdentifier instanceId = - InstanceIdentifier.create(DataObjects.DataObject1.class); - - final Optional dataObjects = transactionWriteContext.readBefore(instanceId); - assertNotNull(dataObjects); - assertFalse(dataObjects.isPresent()); - - verify(serializer).toYangInstanceIdentifier(instanceId); - verify(serializer, never()).fromNormalizedNode(any(YangInstanceIdentifier.class), any(NormalizedNode.class)); - } - - @Test - public void testReadBefore() throws Exception { - when(beforeTx.read(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class))).thenReturn( - Futures.immediateCheckedFuture(Optional.of(mock(NormalizedNode.class)))); - - final InstanceIdentifier instanceId = - InstanceIdentifier.create(DataObjects.DataObject1.class); - final YangInstanceIdentifier yangId = YangInstanceIdentifier.builder().node(QName.create("n", "d")).build(); - when(serializer.toYangInstanceIdentifier(any(InstanceIdentifier.class))).thenReturn(yangId); - when(serializer.fromNormalizedNode(eq(yangId), any(NormalizedNode.class))).thenReturn(entry); - when(entry.getValue()).thenReturn(mock(DataObjects.DataObject1.class)); - - final Optional dataObjects = transactionWriteContext.readBefore(instanceId); - assertNotNull(dataObjects); - assertTrue(dataObjects.isPresent()); - - verify(serializer).toYangInstanceIdentifier(instanceId); - verify(serializer).fromNormalizedNode(eq(yangId), any(NormalizedNode.class)); - } - - @Test(expected = IllegalStateException.class) - public void testReadBeforeFailed() throws Exception { - when(beforeTx.read(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class))).thenReturn( - Futures.immediateFailedCheckedFuture(mock(ReadFailedException.class))); - transactionWriteContext.readBefore(mock(InstanceIdentifier.class)); - } - - @Test(expected = IllegalStateException.class) - public void testReadAfterFailed() throws Exception { - when(afterTx.read(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class))).thenReturn( - Futures.immediateFailedCheckedFuture(mock(ReadFailedException.class))); - transactionWriteContext.readAfter(mock(InstanceIdentifier.class)); - } - - @Test - public void testGetContext() throws Exception { - assertNotNull(transactionWriteContext.getModificationCache()); - } - - @Test - public void testClose() throws Exception { - final ModificationCache context = transactionWriteContext.getModificationCache(); - transactionWriteContext.close(); - // TODO verify context was closed - } -} \ No newline at end of file diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/DataObjects.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/DataObjects.java deleted file mode 100644 index d823465bd..000000000 --- a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/DataObjects.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util; - -import org.opendaylight.yangtools.yang.binding.ChildOf; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -public class DataObjects { - public interface DataObject1 extends DataObject { - InstanceIdentifier IID = InstanceIdentifier.create(DataObject1.class); - } - - public interface DataObject2 extends DataObject { - InstanceIdentifier IID = InstanceIdentifier.create(DataObject2.class); - } - - public interface DataObject3 extends DataObject { - InstanceIdentifier IID = InstanceIdentifier.create(DataObject3.class); - interface DataObject31 extends DataObject, ChildOf { - InstanceIdentifier IID = DataObject3.IID.child(DataObject31.class); - } - } - - public interface DataObject4 extends DataObject { - InstanceIdentifier IID = InstanceIdentifier.create(DataObject4.class); - interface DataObject41 extends DataObject, ChildOf { - InstanceIdentifier IID = DataObject4.IID.child(DataObject41.class); - interface DataObject411 extends DataObject, ChildOf { - InstanceIdentifier IID = DataObject41.IID.child(DataObject411.class); - } - } - - interface DataObject42 extends DataObject, ChildOf { - InstanceIdentifier IID = DataObject4.IID.child(DataObject42.class); - } - } -} diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/JsonUtilsTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/JsonUtilsTest.java deleted file mode 100644 index bd48cb446..000000000 --- a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/JsonUtilsTest.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import com.google.common.io.ByteStreams; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import org.junit.Before; -import org.junit.Test; -import org.mockito.MockitoAnnotations; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.impl.schema.Builders; -import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; -import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor; -import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline; -import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl; -import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.EffectiveSchemaContext; - -public class JsonUtilsTest { - - public static final String NAMESPACE = "urn:opendaylight:params:xml:ns:yang:test:persistence"; - - private static final QName ROOT_QNAME = QName.create("urn:ietf:params:xml:ns:netconf:base:1.0", "data"); - private static final QName TOP_CONTAINER_NAME = QName.create(NAMESPACE, "2015-01-05", "top-container"); - private static final QName TOP_CONTAINER2_NAME = QName.create(NAMESPACE, "2015-01-05", "top-container2"); - private static final QName STRING_LEAF_QNAME = QName.create(TOP_CONTAINER_NAME, "string"); - - private Path tmpPersistFile; - private EffectiveSchemaContext effectiveSchemaContext; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - tmpPersistFile = Files.createTempFile("testing-hc-persistence", "json"); - - final CrossSourceStatementReactor.BuildAction buildAction = YangInferencePipeline.RFC6020_REACTOR.newBuild(); - buildAction.addSource(new YangStatementSourceImpl(getClass().getResourceAsStream("/test-persistence.yang"))); - effectiveSchemaContext = buildAction.buildEffective(); - } - - @Test - public void testPersist() throws Exception { - - NormalizedNode data = getData("testing"); - JsonUtils.writeJsonRoot(data, effectiveSchemaContext, Files.newOutputStream(tmpPersistFile, StandardOpenOption.CREATE)); - assertTrue(Files.exists(tmpPersistFile)); - - String persisted = new String(Files.readAllBytes(tmpPersistFile)); - String expected = - new String(ByteStreams.toByteArray(getClass().getResourceAsStream("/expected-persisted-output.txt"))); - - assertEquals(expected, persisted); - - data = getData("testing2"); - JsonUtils.writeJsonRoot(data, effectiveSchemaContext, Files.newOutputStream(tmpPersistFile, StandardOpenOption.CREATE)); - persisted = new String(Files.readAllBytes(tmpPersistFile)); - assertEquals(expected.replace("testing", "testing2"), persisted); - - // File has to stay even after close - assertTrue(Files.exists(tmpPersistFile)); - } - - @Test - public void testRestore() throws Exception { - final ContainerNode normalizedNodeOptional = JsonUtils - .readJsonRoot(effectiveSchemaContext, getClass().getResourceAsStream("/expected-persisted-output.txt")); - assertEquals(getData("testing"), normalizedNodeOptional); - } - - @Test(expected = IllegalArgumentException.class) - public void testRestoreInvalidFile() throws Exception { - JsonUtils.readJsonRoot(effectiveSchemaContext, getClass().getResourceAsStream("/test-persistence.yang")); - } - - private ContainerNode getData(final String stringValue) { - return Builders.containerBuilder() - .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(ROOT_QNAME)) - .withChild(Builders.containerBuilder() - .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_NAME)) - .withChild(ImmutableNodes.leafNode(STRING_LEAF_QNAME, stringValue)) - .build()) - .withChild(Builders.containerBuilder() - .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER2_NAME)) - .withChild(ImmutableNodes.leafNode(STRING_LEAF_QNAME, stringValue)) - .build()) - .build(); - } -} \ No newline at end of file diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistryBuilderTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistryBuilderTest.java deleted file mode 100644 index e57dcee43..000000000 --- a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistryBuilderTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.read.registry; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.when; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; -import io.fd.honeycomb.v3po.translate.read.Reader; -import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistry; -import io.fd.honeycomb.v3po.translate.util.DataObjects; -import java.util.List; -import java.util.Map; -import org.junit.Test; -import org.mockito.Mockito; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -public class CompositeReaderRegistryBuilderTest { - - private Reader> reader1 = - mock(DataObjects.DataObject1.class); - private Reader> reader2 = - mock(DataObjects.DataObject2.class); - private Reader> reader3 = - mock(DataObjects.DataObject3.class); - private Reader> reader31 = - mock(DataObjects.DataObject3.DataObject31.class); - - private Reader> reader4 = - mock(DataObjects.DataObject4.class); - private Reader> reader41 = - mock(DataObjects.DataObject4.DataObject41.class); - private Reader> reader411 = - mock(DataObjects.DataObject4.DataObject41.DataObject411.class); - private Reader> reader42 = - mock(DataObjects.DataObject4.DataObject42.class); - - @SuppressWarnings("unchecked") - private Reader> mock(final Class dataObjectType) { - final Reader> mock = Mockito.mock(Reader.class); - try { - when(mock.getManagedDataObjectType()) - .thenReturn(((InstanceIdentifier) dataObjectType.getDeclaredField("IID").get(null))); - } catch (IllegalAccessException | NoSuchFieldException e) { - throw new RuntimeException(e); - } - return mock; - } - - @Test - public void testCompositeStructure() throws Exception { - final CompositeReaderRegistryBuilder compositeReaderRegistryBuilder = new CompositeReaderRegistryBuilder(); - /* - Composite reader structure ordered left from right - - 1, 2, 3, 4 - 31 42, 41 - 411 - */ - compositeReaderRegistryBuilder.add(reader1); - compositeReaderRegistryBuilder.addAfter(reader2, reader1.getManagedDataObjectType()); - compositeReaderRegistryBuilder.addAfter(reader3, reader2.getManagedDataObjectType()); - compositeReaderRegistryBuilder.addAfter(reader31, reader1.getManagedDataObjectType()); - compositeReaderRegistryBuilder.addAfter(reader4, reader3.getManagedDataObjectType()); - compositeReaderRegistryBuilder.add(reader41); - compositeReaderRegistryBuilder.addBefore(reader42, reader41.getManagedDataObjectType()); - compositeReaderRegistryBuilder.add(reader411); - - final ReaderRegistry build = compositeReaderRegistryBuilder.build(); - - final Map, Reader>> rootReaders = - ((CompositeReaderRegistry) build).getRootReaders(); - final List> rootReaderOrder = Lists.newArrayList(rootReaders.keySet()); - - assertEquals(reader1.getManagedDataObjectType().getTargetType(), rootReaderOrder.get(0)); - assertEquals(reader2.getManagedDataObjectType().getTargetType(), rootReaderOrder.get(1)); - assertEquals(reader3.getManagedDataObjectType().getTargetType(), rootReaderOrder.get(2)); - assertEquals(reader4.getManagedDataObjectType().getTargetType(), rootReaderOrder.get(3)); - - assertFalse(rootReaders.get(DataObjects.DataObject1.class) instanceof CompositeReader); - assertFalse(rootReaders.get(DataObjects.DataObject2.class) instanceof CompositeReader); - assertTrue(rootReaders.get(DataObjects.DataObject3.class) instanceof CompositeReader); - assertTrue(rootReaders.get(DataObjects.DataObject4.class) instanceof CompositeReader); - - final ImmutableMap, Reader>> childReaders = - ((CompositeReader>) rootReaders - .get(DataObjects.DataObject4.class)).getChildReaders(); - final List> orderedChildReaders = Lists.newArrayList(childReaders.keySet()); - - assertEquals(reader42.getManagedDataObjectType().getTargetType(), orderedChildReaders.get(0)); - assertEquals(reader41.getManagedDataObjectType().getTargetType(), orderedChildReaders.get(1)); - assertTrue(childReaders.get(DataObjects.DataObject4.DataObject41.class) instanceof CompositeReader); - assertFalse(childReaders.get(DataObjects.DataObject4.DataObject42.class) instanceof CompositeReader); - } -} \ No newline at end of file diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/SubtreeReaderTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/SubtreeReaderTest.java deleted file mode 100644 index 324d71daa..000000000 --- a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/SubtreeReaderTest.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.read.registry; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import com.google.common.base.Optional; -import com.google.common.collect.Sets; -import io.fd.honeycomb.v3po.translate.read.ReadContext; -import io.fd.honeycomb.v3po.translate.read.Reader; -import io.fd.honeycomb.v3po.translate.util.DataObjects; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.yang.binding.ChildOf; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -public class SubtreeReaderTest { - - @Mock - private Reader> delegate; - @Mock - private Reader> delegateLocal; - @Mock - private ReadContext ctx; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - doReturn(DataObjects.DataObject4.IID).when(delegate).getManagedDataObjectType(); - doReturn(DataObject1.IID).when(delegateLocal).getManagedDataObjectType(); - } - - @Test - public void testCreate() throws Exception { - final Reader> subtreeR = - SubtreeReader.createForReader(Sets.newHashSet(DataObjects.DataObject4.DataObject41.IID), delegate); - - subtreeR.getBuilder(DataObjects.DataObject4.IID); - verify(delegate).getBuilder(DataObjects.DataObject4.IID); - - subtreeR.getManagedDataObjectType(); - verify(delegate, atLeastOnce()).getManagedDataObjectType(); - } - - @Test(expected = IllegalArgumentException.class) - public void testCreateInvalid() throws Exception { - SubtreeReader.createForReader(Sets.newHashSet(DataObjects.DataObject1.IID), delegate); - } - - @Test(expected = IllegalStateException.class) - public void testReadOnlySubtreeCannotFilter() throws Exception { - final Reader> subtreeR = - SubtreeReader.createForReader(Sets.newHashSet(DataObjects.DataObject4.DataObject41.IID), delegate); - - doReturn(Optional.fromNullable(mock(DataObjects.DataObject4.class))).when(delegate).read(DataObjects.DataObject4.IID, ctx); - subtreeR.read(DataObjects.DataObject4.DataObject41.IID, ctx); - } - - @Test - public void testReadOnlySubtreeNotPresent() throws Exception { - final Reader> subtreeR = - SubtreeReader.createForReader(Sets.newHashSet(DataObjects.DataObject4.DataObject41.IID), delegate); - - doReturn(Optional.absent()).when(delegate).read(DataObjects.DataObject4.IID, ctx); - assertFalse(subtreeR.read(DataObjects.DataObject4.DataObject41.IID, ctx).isPresent()); - } - - @Test - public void testReadOnlySubtreeChild() throws Exception { - final Reader> subtreeR = - SubtreeReader.createForReader(Sets.newHashSet(DataObject1.DataObject11.IID), delegateLocal); - - final DataObject1 mock = mock(DataObject1.class); - final DataObject1.DataObject11 mock11 = mock(DataObject1.DataObject11.class); - doReturn(mock11).when(mock).getDataObject11(); - doReturn(Optional.fromNullable(mock)).when(delegateLocal).read(DataObject1.IID, ctx); - assertEquals(mock11, subtreeR.read(DataObject1.DataObject11.IID, ctx).get()); - } - - @Test - public void testReadEntireSubtree() throws Exception { - final Reader> subtreeR = - SubtreeReader.createForReader(Sets.newHashSet(DataObject1.DataObject11.IID), delegateLocal); - - final DataObject1 mock = mock(DataObject1.class); - final DataObject1.DataObject11 mock11 = mock(DataObject1.DataObject11.class); - doReturn(mock11).when(mock).getDataObject11(); - doReturn(Optional.fromNullable(mock)).when(delegateLocal).read(DataObject1.IID, ctx); - assertEquals(mock, subtreeR.read(DataObject1.IID, ctx).get()); - } - - public abstract static class DataObject1 implements DataObject { - public static InstanceIdentifier IID = InstanceIdentifier.create(DataObject1.class); - - public abstract DataObject11 getDataObject11(); - - public abstract static class DataObject11 implements DataObject, ChildOf { - public static InstanceIdentifier IID = DataObject1.IID.child(DataObject11.class); - } - } -} diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/TypeHierarchyTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/TypeHierarchyTest.java deleted file mode 100644 index 7a664eef1..000000000 --- a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/TypeHierarchyTest.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.read.registry; - -import static org.hamcrest.CoreMatchers.hasItem; -import static org.hamcrest.CoreMatchers.hasItems; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -import com.google.common.collect.Sets; -import io.fd.honeycomb.v3po.translate.util.DataObjects.DataObject1; -import io.fd.honeycomb.v3po.translate.util.DataObjects.DataObject3; -import io.fd.honeycomb.v3po.translate.util.DataObjects.DataObject4; -import org.junit.Test; - -public class TypeHierarchyTest { - - @Test - public void testHierarchy() throws Exception { - final TypeHierarchy typeHierarchy = TypeHierarchy.create(Sets.newHashSet( - DataObject4.DataObject41.DataObject411.IID, - DataObject4.DataObject41.IID,/* Included in previous already */ - DataObject1.IID, - DataObject3.DataObject31.IID)); - - // Roots - assertThat(typeHierarchy.getRoots().size(), is(3)); - assertThat(typeHierarchy.getRoots(), hasItems(DataObject1.IID, DataObject3.IID, DataObject4.IID)); - - // Leaves - assertThat(typeHierarchy.getDirectChildren(DataObject1.IID).size(), is(0)); - assertThat(typeHierarchy.getDirectChildren(DataObject3.DataObject31.IID).size(), is(0)); - assertThat(typeHierarchy.getDirectChildren(DataObject4.DataObject41.DataObject411.IID).size(), is(0)); - - // Intermediate leaves - assertThat(typeHierarchy.getDirectChildren(DataObject3.IID).size(), is(1)); - assertThat(typeHierarchy.getDirectChildren(DataObject3.IID), hasItem(DataObject3.DataObject31.IID)); - assertEquals(typeHierarchy.getDirectChildren(DataObject3.IID), typeHierarchy.getAllChildren(DataObject3.IID)); - - assertThat(typeHierarchy.getDirectChildren(DataObject4.DataObject41.IID).size(), is(1)); - assertThat(typeHierarchy.getDirectChildren(DataObject4.DataObject41.IID), hasItem( - DataObject4.DataObject41.DataObject411.IID)); - assertEquals(typeHierarchy.getDirectChildren(DataObject4.DataObject41.IID), typeHierarchy.getAllChildren( - DataObject4.DataObject41.IID)); - - assertThat(typeHierarchy.getDirectChildren(DataObject4.IID).size(), is(1)); - assertThat(typeHierarchy.getDirectChildren(DataObject4.IID), hasItem(DataObject4.DataObject41.IID)); - assertThat(typeHierarchy.getAllChildren(DataObject4.IID).size(), is(2)); - assertTrue(typeHierarchy.getAllChildren(DataObject4.IID).contains(DataObject4.DataObject41.IID)); - assertTrue(typeHierarchy.getAllChildren(DataObject4.IID).contains(DataObject4.DataObject41.DataObject411.IID)); - } -} - diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilderTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilderTest.java deleted file mode 100644 index 743d84cbf..000000000 --- a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilderTest.java +++ /dev/null @@ -1,131 +0,0 @@ -package io.fd.honeycomb.v3po.translate.util.write.registry; - -import static org.hamcrest.CoreMatchers.anyOf; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.hasItems; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; -import io.fd.honeycomb.v3po.translate.util.DataObjects; -import io.fd.honeycomb.v3po.translate.write.Writer; -import java.util.ArrayList; -import java.util.List; -import org.junit.Test; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -public class FlatWriterRegistryBuilderTest { - - - @Test - public void testRelationsBefore() throws Exception { - final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder(); - /* - 1 -> 2 -> 3 - -> 4 - */ - flatWriterRegistryBuilder.add(mockWriter(DataObjects.DataObject3.class)); - flatWriterRegistryBuilder.add(mockWriter(DataObjects.DataObject4.class)); - flatWriterRegistryBuilder.addBefore(mockWriter(DataObjects.DataObject2.class), - Lists.newArrayList(DataObjects.DataObject3.IID, DataObjects.DataObject4.IID)); - flatWriterRegistryBuilder.addBefore(mockWriter(DataObjects.DataObject1.class), DataObjects.DataObject2.IID); - final ImmutableMap, Writer> mappedWriters = - flatWriterRegistryBuilder.getMappedHandlers(); - - final ArrayList> typesInList = Lists.newArrayList(mappedWriters.keySet()); - assertEquals(DataObjects.DataObject1.IID, typesInList.get(0)); - assertEquals(DataObjects.DataObject2.IID, typesInList.get(1)); - assertThat(typesInList.get(2), anyOf(equalTo(DataObjects.DataObject3.IID), equalTo(DataObjects.DataObject4.IID))); - assertThat(typesInList.get(3), anyOf(equalTo(DataObjects.DataObject3.IID), equalTo(DataObjects.DataObject4.IID))); - } - - @Test - public void testRelationsAfter() throws Exception { - final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder(); - /* - 1 -> 2 -> 3 - -> 4 - */ - flatWriterRegistryBuilder.add(mockWriter(DataObjects.DataObject1.class)); - flatWriterRegistryBuilder.addAfter(mockWriter(DataObjects.DataObject2.class), DataObjects.DataObject1.IID); - flatWriterRegistryBuilder.addAfter(mockWriter(DataObjects.DataObject3.class), DataObjects.DataObject2.IID); - flatWriterRegistryBuilder.addAfter(mockWriter(DataObjects.DataObject4.class), DataObjects.DataObject2.IID); - final ImmutableMap, Writer> mappedWriters = - flatWriterRegistryBuilder.getMappedHandlers(); - - final List> typesInList = Lists.newArrayList(mappedWriters.keySet()); - assertEquals(DataObjects.DataObject1.IID, typesInList.get(0)); - assertEquals(DataObjects.DataObject2.IID, typesInList.get(1)); - assertThat(typesInList.get(2), anyOf(equalTo(DataObjects.DataObject3.IID), equalTo(DataObjects.DataObject4.IID))); - assertThat(typesInList.get(3), anyOf(equalTo(DataObjects.DataObject3.IID), equalTo(DataObjects.DataObject4.IID))); - } - - @Test(expected = IllegalArgumentException.class) - public void testRelationsLoop() throws Exception { - final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder(); - /* - 1 -> 2 -> 1 - */ - flatWriterRegistryBuilder.add(mockWriter(DataObjects.DataObject1.class)); - flatWriterRegistryBuilder.addAfter(mockWriter(DataObjects.DataObject2.class), DataObjects.DataObject1.IID); - flatWriterRegistryBuilder.addAfter(mockWriter(DataObjects.DataObject1.class), DataObjects.DataObject2.IID); - } - - @Test(expected = IllegalArgumentException.class) - public void testAddWriterTwice() throws Exception { - final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder(); - flatWriterRegistryBuilder.add(mockWriter(DataObjects.DataObject1.class)); - flatWriterRegistryBuilder.add(mockWriter(DataObjects.DataObject1.class)); - } - - @Test - public void testAddSubtreeWriter() throws Exception { - final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder(); - flatWriterRegistryBuilder.subtreeAdd( - Sets.newHashSet(DataObjects.DataObject4.DataObject41.IID, - DataObjects.DataObject4.DataObject41.IID), - mockWriter(DataObjects.DataObject4.class)); - final ImmutableMap, Writer> mappedWriters = - flatWriterRegistryBuilder.getMappedHandlers(); - final ArrayList> typesInList = Lists.newArrayList(mappedWriters.keySet()); - - assertEquals(DataObjects.DataObject4.IID, typesInList.get(0)); - assertEquals(1, typesInList.size()); - } - - @Test - public void testCreateSubtreeWriter() throws Exception { - final Writer forWriter = SubtreeWriter.createForWriter(Sets.newHashSet( - DataObjects.DataObject4.DataObject41.IID, - DataObjects.DataObject4.DataObject41.DataObject411.IID, - DataObjects.DataObject4.DataObject42.IID), - mockWriter(DataObjects.DataObject4.class)); - assertThat(forWriter, instanceOf(SubtreeWriter.class)); - assertThat(((SubtreeWriter) forWriter).getHandledChildTypes().size(), is(3)); - assertThat(((SubtreeWriter) forWriter).getHandledChildTypes(), hasItems(DataObjects.DataObject4.DataObject41.IID, - DataObjects.DataObject4.DataObject42.IID, DataObjects.DataObject4.DataObject41.DataObject411.IID)); - } - - @Test(expected = IllegalArgumentException.class) - public void testCreateInvalidSubtreeWriter() throws Exception { - SubtreeWriter.createForWriter(Sets.newHashSet( - InstanceIdentifier.create(DataObjects.DataObject3.class).child(DataObjects.DataObject3.DataObject31.class)), - mockWriter(DataObjects.DataObject4.class)); - } - - @SuppressWarnings("unchecked") - private Writer mockWriter(final Class doClass) - throws NoSuchFieldException, IllegalAccessException { - final Writer mock = mock(Writer.class); - when(mock.getManagedDataObjectType()).thenReturn((InstanceIdentifier) doClass.getDeclaredField("IID").get(null)); - return mock; - } - -} \ No newline at end of file diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryTest.java deleted file mode 100644 index a72cb4fa7..000000000 --- a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryTest.java +++ /dev/null @@ -1,264 +0,0 @@ -package io.fd.honeycomb.v3po.translate.util.write.registry; - -import static org.hamcrest.CoreMatchers.hasItem; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; - -import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.Multimap; -import io.fd.honeycomb.v3po.translate.util.DataObjects.DataObject1; -import io.fd.honeycomb.v3po.translate.util.DataObjects.DataObject2; -import io.fd.honeycomb.v3po.translate.util.DataObjects.DataObject3; -import io.fd.honeycomb.v3po.translate.write.DataObjectUpdate; -import io.fd.honeycomb.v3po.translate.write.WriteContext; -import io.fd.honeycomb.v3po.translate.write.Writer; -import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistry; -import org.junit.Before; -import org.junit.Test; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -public class FlatWriterRegistryTest { - - @Mock - private Writer writer1; - @Mock - private Writer writer2; - @Mock - private Writer writer3; - @Mock - private WriteContext ctx; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - when(writer1.getManagedDataObjectType()).thenReturn(DataObject1.IID); - when(writer2.getManagedDataObjectType()).thenReturn(DataObject2.IID); - when(writer3.getManagedDataObjectType()).thenReturn(DataObject3.IID); - } - - @Test - public void testMultipleUpdatesForSingleWriter() throws Exception { - final FlatWriterRegistry flatWriterRegistry = - new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2)); - - final Multimap, DataObjectUpdate> updates = HashMultimap.create(); - final InstanceIdentifier iid = InstanceIdentifier.create(DataObject1.class); - final InstanceIdentifier 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.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx); - - verify(writer1).update(iid, dataObject, dataObject, ctx); - verify(writer1).update(iid2, dataObject, dataObject, ctx); - // Invoked when registry is being created - verifyNoMoreInteractions(writer1); - verifyZeroInteractions(writer2); - } - - @Test - public void testMultipleUpdatesForMultipleWriters() throws Exception { - final FlatWriterRegistry flatWriterRegistry = - new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2)); - - final Multimap, DataObjectUpdate> updates = HashMultimap.create(); - final InstanceIdentifier iid = InstanceIdentifier.create(DataObject1.class); - final DataObject1 dataObject = mock(DataObject1.class); - updates.put(DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject)); - final InstanceIdentifier iid2 = InstanceIdentifier.create(DataObject2.class); - final DataObject2 dataObject2 = mock(DataObject2.class); - updates.put(DataObject2.IID, DataObjectUpdate.create(iid2, dataObject2, dataObject2)); - flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx); - - final InOrder inOrder = inOrder(writer1, writer2); - inOrder.verify(writer1).update(iid, dataObject, dataObject, ctx); - inOrder.verify(writer2).update(iid2, dataObject2, dataObject2, ctx); - - verifyNoMoreInteractions(writer1); - verifyNoMoreInteractions(writer2); - } - - @Test - public void testMultipleDeletesForMultipleWriters() throws Exception { - final FlatWriterRegistry flatWriterRegistry = - new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2)); - - final Multimap, DataObjectUpdate.DataObjectDelete> deletes = HashMultimap.create(); - final InstanceIdentifier iid = InstanceIdentifier.create(DataObject1.class); - final DataObject1 dataObject = mock(DataObject1.class); - deletes.put(DataObject1.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid, dataObject, null))); - final InstanceIdentifier iid2 = InstanceIdentifier.create(DataObject2.class); - final DataObject2 dataObject2 = mock(DataObject2.class); - deletes.put(DataObject2.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid2, dataObject2, null))); - flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(ImmutableMultimap.of(), deletes), ctx); - - final InOrder inOrder = inOrder(writer1, writer2); - // Reversed order of invocation, first writer2 and then writer1 - inOrder.verify(writer2).update(iid2, dataObject2, null, ctx); - inOrder.verify(writer1).update(iid, dataObject, null, ctx); - - verifyNoMoreInteractions(writer1); - verifyNoMoreInteractions(writer2); - } - - @Test - public void testMultipleUpdatesAndDeletesForMultipleWriters() throws Exception { - final FlatWriterRegistry flatWriterRegistry = - new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2)); - - final Multimap, DataObjectUpdate.DataObjectDelete> deletes = HashMultimap.create(); - final Multimap, DataObjectUpdate> updates = HashMultimap.create(); - final InstanceIdentifier 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 update - updates.put(DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject)); - final InstanceIdentifier 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.update(new WriterRegistry.DataObjectUpdates(updates, deletes), ctx); - - final InOrder inOrder = inOrder(writer1, writer2); - // Reversed order of invocation, first writer2 and then writer1 for deletes - inOrder.verify(writer2).update(iid2, dataObject2, null, ctx); - inOrder.verify(writer1).update(iid, dataObject, null, ctx); - // Then also updates are processed - inOrder.verify(writer1).update(iid, dataObject, dataObject, ctx); - inOrder.verify(writer2).update(iid2, dataObject2, dataObject2, ctx); - - verifyNoMoreInteractions(writer1); - verifyNoMoreInteractions(writer2); - } - - @Test(expected = IllegalArgumentException.class) - public void testMultipleUpdatesOneMissing() throws Exception { - final FlatWriterRegistry flatWriterRegistry = - new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1)); - - final Multimap, DataObjectUpdate> updates = HashMultimap.create(); - addUpdate(updates, DataObject1.class); - addUpdate(updates, DataObject2.class); - flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx); - } - - @Test - public void testMultipleUpdatesOneFailing() throws Exception { - final FlatWriterRegistry flatWriterRegistry = - new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2)); - - // Writer1 always fails - doThrow(new RuntimeException()).when(writer1) - .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class)); - - final Multimap, DataObjectUpdate> updates = HashMultimap.create(); - addUpdate(updates, DataObject1.class); - addUpdate(updates, DataObject2.class); - - try { - flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx); - fail("Bulk update should have failed on writer1"); - } catch (WriterRegistry.BulkUpdateException e) { - assertThat(e.getFailedIds().size(), is(2)); - assertThat(e.getFailedIds(), hasItem(InstanceIdentifier.create(DataObject2.class))); - assertThat(e.getFailedIds(), hasItem(InstanceIdentifier.create(DataObject1.class))); - } - } - - @Test - public void testMultipleUpdatesOneFailingThenRevertWithSuccess() throws Exception { - final FlatWriterRegistry flatWriterRegistry = - new FlatWriterRegistry( - ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2, DataObject3.IID, writer3)); - - // Writer1 always fails - doThrow(new RuntimeException()).when(writer3) - .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class)); - - final Multimap, DataObjectUpdate> updates = HashMultimap.create(); - addUpdate(updates, DataObject1.class); - addUpdate(updates, DataObject3.class); - final InstanceIdentifier iid2 = InstanceIdentifier.create(DataObject2.class); - final DataObject2 before2 = mock(DataObject2.class); - final DataObject2 after2 = mock(DataObject2.class); - updates.put(DataObject2.IID, DataObjectUpdate.create(iid2, before2, after2)); - - try { - flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx); - fail("Bulk update should have failed on writer1"); - } catch (WriterRegistry.BulkUpdateException e) { - assertThat(e.getFailedIds().size(), is(1)); - - final InOrder inOrder = inOrder(writer1, writer2, writer3); - inOrder.verify(writer1) - .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class)); - inOrder.verify(writer2) - .update(iid2, before2, after2, ctx); - inOrder.verify(writer3) - .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class)); - - e.revertChanges(); - // Revert changes. Successful updates are iterated in reverse - inOrder.verify(writer2) - .update(iid2, after2, before2, ctx); - inOrder.verify(writer1) - .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class)); - verifyNoMoreInteractions(writer3); - } - } - - @Test - public void testMultipleUpdatesOneFailingThenRevertWithFail() throws Exception { - final FlatWriterRegistry flatWriterRegistry = - new FlatWriterRegistry( - ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2, DataObject3.IID, writer3)); - - // Writer1 always fails - doThrow(new RuntimeException()).when(writer3) - .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class)); - - final Multimap, DataObjectUpdate> updates = HashMultimap.create(); - addUpdate(updates, DataObject1.class); - addUpdate(updates, DataObject2.class); - addUpdate(updates, DataObject3.class); - - try { - flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx); - fail("Bulk update should have failed on writer1"); - } catch (WriterRegistry.BulkUpdateException e) { - // Writer1 always fails from now - doThrow(new RuntimeException()).when(writer1) - .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class)); - try { - e.revertChanges(); - } catch (WriterRegistry.Reverter.RevertFailedException e1) { - assertThat(e1.getNotRevertedChanges().size(), is(1)); - assertThat(e1.getNotRevertedChanges(), hasItem(InstanceIdentifier.create(DataObject1.class))); - } - } - } - - private void addUpdate(final Multimap, DataObjectUpdate> updates, - final Class type) throws Exception { - final InstanceIdentifier iid = (InstanceIdentifier) type.getDeclaredField("IID").get(null); - updates.put(iid, DataObjectUpdate.create(iid, mock(type), mock(type))); - } -} \ No newline at end of file diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriterTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriterTest.java deleted file mode 100644 index 627c69c92..000000000 --- a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriterTest.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.translate.util.write.registry; - -import static org.hamcrest.CoreMatchers.hasItem; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.when; - -import com.google.common.collect.Sets; -import io.fd.honeycomb.v3po.translate.util.DataObjects; -import io.fd.honeycomb.v3po.translate.write.Writer; -import java.util.Collections; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -public class SubtreeWriterTest { - - @Mock - Writer writer; - @Mock - Writer writer11; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - when(writer.getManagedDataObjectType()).thenReturn(DataObjects.DataObject4.IID); - when(writer11.getManagedDataObjectType()).thenReturn(DataObjects.DataObject4.DataObject41.IID); - } - - @Test(expected = IllegalArgumentException.class) - public void testSubtreeWriterCreationFail() throws Exception { - // The subtree node identified by IID.c(DataObject.class) is not a child of writer.getManagedDataObjectType - SubtreeWriter.createForWriter(Collections.singleton(InstanceIdentifier.create(DataObject.class)), writer); - } - - @Test(expected = IllegalArgumentException.class) - public void testSubtreeWriterCreationFailInvalidIid() throws Exception { - // The subtree node identified by IID.c(DataObject.class) is not a child of writer.getManagedDataObjectType - SubtreeWriter.createForWriter(Collections.singleton(DataObjects.DataObject4.IID), writer); - } - - @Test - public void testSubtreeWriterCreation() throws Exception { - final SubtreeWriter forWriter = (SubtreeWriter) SubtreeWriter.createForWriter(Sets.newHashSet( - DataObjects.DataObject4.DataObject41.IID, - DataObjects.DataObject4.DataObject41.DataObject411.IID, - DataObjects.DataObject4.DataObject42.IID), - writer); - - assertEquals(writer.getManagedDataObjectType(), forWriter.getManagedDataObjectType()); - assertEquals(3, forWriter.getHandledChildTypes().size()); - } - - @Test - public void testSubtreeWriterHandledTypes() throws Exception { - final SubtreeWriter forWriter = (SubtreeWriter) SubtreeWriter.createForWriter(Sets.newHashSet( - DataObjects.DataObject4.DataObject41.DataObject411.IID), - writer); - - assertEquals(writer.getManagedDataObjectType(), forWriter.getManagedDataObjectType()); - assertEquals(1, forWriter.getHandledChildTypes().size()); - assertThat(forWriter.getHandledChildTypes(), hasItem(DataObjects.DataObject4.DataObject41.DataObject411.IID)); - } - -} \ No newline at end of file -- cgit 1.2.3-korg