Merge branch 'split-mdsal' of ../controller
authorAndrew Grimberg <agrimberg@linuxfoundation.org>
Wed, 19 Aug 2015 20:35:44 +0000 (13:35 -0700)
committerAndrew Grimberg <agrimberg@linuxfoundation.org>
Wed, 19 Aug 2015 20:35:44 +0000 (13:35 -0700)
425 files changed:
opendaylight/md-sal/mdsal-artifacts/pom.xml [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/.gitignore [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/pom.xml [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingService.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingTransactionChain.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/ClusteredDataChangeListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataBroker.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataChangeListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataObjectModification.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeService.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeIdentifier.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeModification.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/MountPoint.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/MountPointService.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/NotificationPublishService.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/NotificationRejectedException.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/NotificationService.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/ReadOnlyTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/ReadTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/ReadWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/TransactionFactory.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/WriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/AbstractBindingAwareConsumer.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/AbstractBindingAwareProvider.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/AbstractBrokerAwareActivator.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/BindingAwareBroker.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/BindingAwareConsumer.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/BindingAwareProvider.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/BindingAwareService.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/NotificationListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/NotificationProviderService.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/NotificationService.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/RpcAvailabilityListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/RpcConsumerRegistry.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/RpcProviderRegistry.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/data/DataBrokerService.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/data/DataChangeListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/data/DataModificationTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/data/DataProviderService.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/data/SynchronizedTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/mount/MountInstance.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/mount/MountProviderInstance.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/mount/MountProviderService.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/mount/MountService.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/package-info.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/rpc/RpcContextIdentifier.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/rpc/RpcRouter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/rpc/RpcRoutingContext.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/rpc/RpcRoutingTable.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/pom.xml [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/.gitignore [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/AbstractNotificationListenerRegistration.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/AggregatedNotificationListenerRegistration.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/CompositeRoutedRpcRegistration.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/DelegatedRootRpcRegistration.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/FunctionalNotificationListenerAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HeliumNotificationProviderServiceAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HeliumNotificationProviderServiceWithInterestListeners.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HeliumNotificationServiceAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HeliumRpcProviderRegistry.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HydrogenDataBrokerAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HydrogenDataChangeEvent.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HydrogenMountInstanceAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HydrogenMountPointServiceAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HydrogenMountProvisionServiceAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HydrogenNotificationBrokerImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/ListenerMapGeneration.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/NotificationInvoker.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/NotificationListenerRegistration.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/NotifyTask.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractReadWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMAdapterBuilder.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMAdapterLoader.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataBrokerAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeListenerAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeServiceAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMMountPointListenerAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMMountPointServiceAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMNotificationListenerAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMNotificationPublishServiceAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMNotificationServiceAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMReadTransactionAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMReadWriteTransactionAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMRpcAdapterRegistration.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMRpcImplementationAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMRpcProviderServiceAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMRpcServiceAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMTransactionChainAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMWriteTransactionAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDataTreeChangeListenerRegistration.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingMountPointAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingRpcAdapterRegistration.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingStructuralType.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingToNormalizedNodeCodec.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ContextReferenceExtractor.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/DirectGetterRouteContextExtractor.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/FutureSchema.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/GetValueRouteContextExtractor.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/LazyDOMRpcResultFuture.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/LazyDataObjectModification.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/LazyDataTreeModification.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/LazySerializedContainerNode.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/LazySerializedDOMNotification.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/RpcServiceAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/spi/AdapterBuilder.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/spi/AdapterLoader.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolder.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RootBindingAwareBroker.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/ContextExtractorTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/BackwardsCompatibleNotificationBrokerTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/BindingDOMDataBrokerAdapterTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/BindingNormalizedCodecTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1125RegressionTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1333DataChangeListenerTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1418AugmentationTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/DataTreeChangeListenerTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/ForwardedBackwardsCompatibleDataBrokerTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/ForwardedNotificationAdapterTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/ListInsertionDataChangeListenerTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/WriteTransactionTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/AbstractDataBrokerTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/AbstractDataChangeListenerTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/AbstractNotificationBrokerTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/AbstractSchemaAwareTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/AssertCollections.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/DataBrokerTestCustomizer.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/AbstractDataServiceTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/AugmentationVerifier.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/BindingTestUtilities.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/BarListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/BarUpdate.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/CompositeListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/FlowDelete.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/FooListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/FooService.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/FooUpdate.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/Grouping.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/InheritedContextInput.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/ReferencableObject.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/ReferencableObjectKey.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/SimpleInput.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingBrokerTestFactory.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/MockSchemaService.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-util/pom.xml [new file with mode: 0644]
opendaylight/md-sal/sal-binding-util/src/main/java/org/opendaylight/controller/md/sal/binding/util/AbstractBindingSalConsumerInstance.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-util/src/main/java/org/opendaylight/controller/md/sal/binding/util/AbstractBindingSalProviderInstance.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-util/src/main/java/org/opendaylight/controller/md/sal/binding/util/BindingContextUtils.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-util/src/main/java/org/opendaylight/controller/md/sal/binding/util/TypeSafeDataReader.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/pom.xml [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/RegistrationListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/TransactionStatus.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncConfigurationCommitCohort.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncConfigurationCommitCoordinator.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncConfigurationCommitHandler.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataBroker.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataChangeEvent.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataChangeListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataTransactionFactory.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadOnlyTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataChange.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataChangeEvent.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataChangeListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataChangePublisher.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataCommitHandler.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataCommitHandlerRegistration.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataModification.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataModificationTransactionFactory.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataProvider.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataProvisionService.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataReader.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataStore.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataStoreUnavailableException.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataValidationFailedException.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/LogicalDatastoreType.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/OptimisticLockFailedException.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/ReadFailedException.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChain.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChainClosedException.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChainFactory.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChainListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionCommitDeadlockException.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionCommitFailedException.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/notify/NotificationPublishService.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/notify/NotificationSubscriptionService.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/MutableRoutingTable.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/Route.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RouteChange.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RouteChangeListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RouteChangePublisher.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RoutedRegistration.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/Router.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RoutingTable.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-impl/pom.xml [new file with mode: 0644]
opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/ListenerRegistry.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/routing/RoutingUtils.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/service/AbstractDataTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/AbstractLockableDelegator.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizationException.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizationOperation.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizer.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataSchemaContainerProxy.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-impl/src/test/resources/normalization-test.yang [new file with mode: 0644]
opendaylight/md-sal/sal-common-util/pom.xml [new file with mode: 0644]
opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/AbstractMXBean.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/QueuedNotificationManagerMXBean.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/QueuedNotificationManagerMXBeanImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/QueuedNotificationManagerStats.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStats.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStatsMXBean.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStatsMXBeanImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Arguments.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/CommitHandlerTransactions.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Futures.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/RpcErrors.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Rpcs.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/.gitignore [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/pom.xml [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/ClusteredDOMDataChangeListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataBroker.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataBrokerExtension.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataChangeListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadOnlyTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeChangeListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeChangeService.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeIdentifier.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeInaccessibleException.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeListeningException.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeLoopException.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducer.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerBusyException.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerException.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerFactory.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeService.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShard.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShardingConflictException.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShardingService.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMEvent.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMExtensibleService.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMMountPoint.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMMountPointService.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotification.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationPublishService.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationRejectedException.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationService.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcAvailabilityListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcException.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcIdentifier.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcImplementation.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcImplementationNotAvailableException.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcImplementationRegistration.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcProviderService.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcResult.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcService.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMService.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMServiceExtension.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMTransactionChain.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/AbstractConsumer.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/AbstractProvider.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/Broker.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/BrokerService.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/Consumer.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/Provider.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/RpcImplementationUnavailableException.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/RpcRoutingContext.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/model/SchemaService.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountProvisionListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/package-info.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/test/java/org/opendaylight/controller/md/sal/dom/api/AbstractDOMDataTreeServiceTestSuite.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/pom.xml [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMDataBroker.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedCompositeTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedTransactionFactory.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMRpcRoutingTableEntry.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/CommitCoordinationTask.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerTransactionChainImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadOnlyTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMNotificationRouter.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMNotificationRouterEvent.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMRpcRouter.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMRpcRoutingTable.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/GlobalDOMRpcRoutingTableEntry.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongDataBroker.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongFuture.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongTransactionChain.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/RoutedDOMRpcRoutingTableEntry.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/SerializedDOMDataBroker.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardRegistration.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataTree.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataTreeProducer.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardingTableEntry.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/TransactionCommitFailedExceptionMapper.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/UnknownDOMRpcRoutingTableEntry.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/jmx/CommitStatsMXBean.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/jmx/CommitStatsMXBeanImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/mount/DOMMountPointServiceImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/ConsumerContextImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/GlobalBundleScanningSchemaServiceImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/ProviderContextImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/SchemaContextProviders.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/AbstractBrokerServiceProxy.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/DOMDataBrokerProxy.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/DOMMountPointServiceProxy.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/ProxyFactory.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/SchemaServiceActivator.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/SchemaServiceProxy.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/package-info.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/util/ProxySchemaContext.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/util/YangSchemaUtils.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/BlockingTransactionChainListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerPerformanceTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataTreeListenerTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMTransactionChainTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/MountPointServiceTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/jmx/CommitStatsMXBeanImplTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestModel.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/test/resources/odl-datastore-test.yang [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/pom.xml [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/broker/spi/mount/SimpleDOMMountPoint.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/broker/spi/rpc/RpcRoutingStrategy.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/AbstractDOMDataTreeChangeListenerRegistration.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/AbstractDOMRpcImplementationRegistration.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/AbstractDOMRpcProviderService.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/AbstractRegistrationTree.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/DOMNotificationSubscriptionListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/DOMNotificationSubscriptionListenerRegistry.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/DefaultDOMRpcResult.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMDataBroker.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMDataReadOnlyTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMDataReadWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMDataWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMNotificationPublishService.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMNotificationService.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMRpcImplementation.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMRpcProviderService.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMRpcResult.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMRpcService.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMTransactionChain.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/RegistrationTreeNode.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/RegistrationTreeSnapshot.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/BrokerModule.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/ForwardingConsumerSession.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/ForwardingProviderSession.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/AbstractDOMStoreTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/AbstractDOMStoreTreeChangePublisher.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/AbstractSnapshotBackedTransactionChain.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStore.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreThreePhaseCommitCohort.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTransactionChain.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTransactionFactory.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTreeChangePublisher.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/ForwardingDOMStoreThreePhaseCommitCohort.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedReadTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedReadWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedTransactions.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/package-info.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/statistics/DOMStoreStatsTracker.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/package-info.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/pom.xml [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModule.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModuleFactory.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModule.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModuleFactory.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChainedTransactionCommitImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMImmutableDataChangeEvent.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMStoreTransactionChainImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DataChangeListenerRegistration.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStoreConfigProperties.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStoreFactory.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMStoreThreePhaseCommitCohort.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMStoreTreeChangePublisher.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeState.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/jmx/InMemoryDataStoreStats.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/DataChangeListenerRegistrationImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/ListenerNode.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/ListenerTree.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/ListenerWalker.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/main/yang/opendaylight-inmemory-datastore-provider.yang [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/AbstractDataChangeListenerTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DatastoreTestTask.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DefaultDataChangeListenerTestSuite.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/RootScopeSubtreeTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/SchemaUpdateForTransactionTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestDCLExecutorService.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestModel.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeBaseTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeOneTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeSubtreeTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/test/resources/odl-datastore-test.yang [new file with mode: 0644]
opendaylight/md-sal/sal-test-model/pom.xml [new file with mode: 0644]
opendaylight/md-sal/sal-test-model/src/main/java/org/opendaylight/controller/md/sal/test/model/util/ListsBindingUtils.java [new file with mode: 0644]
opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-mdsal-augment-test.yang [new file with mode: 0644]
opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-mdsal-list-test.yang [new file with mode: 0644]
opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-of-migration-test-model.yang [new file with mode: 0644]
opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-sal-test-store.yang [new file with mode: 0644]
opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-test-notification.yang [new file with mode: 0644]
opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-test-routed-rpc.yang [new file with mode: 0644]
opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-test-rpc-service.yang [new file with mode: 0644]

diff --git a/opendaylight/md-sal/mdsal-artifacts/pom.xml b/opendaylight/md-sal/mdsal-artifacts/pom.xml
new file mode 100644 (file)
index 0000000..3f64b77
--- /dev/null
@@ -0,0 +1,292 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+    Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v1.0 which accompanies this distribution,
+    and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>mdsal-artifacts</artifactId>
+    <version>1.3.0-SNAPSHOT</version>
+    <packaging>pom</packaging>
+
+    <dependencyManagement>
+        <dependencies>
+            <!-- Core API/implementation -->
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-common-api</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-common-impl</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-common-util</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-core-api</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-core-spi</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-broker-impl</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-binding-api</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-binding-broker-impl</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-binding-util</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-connector-api</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-inmemory-datastore</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-dom-config</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-dom-broker-config</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-binding-config</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>md-sal-config</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+
+            <!-- Test support -->
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-test-model</artifactId>
+                <version>${project.version}</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-binding-it</artifactId>
+                <version>${project.version}</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-binding-broker-impl</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+                <scope>test</scope>
+            </dependency>
+
+            <!-- Features -->
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>features-mdsal</artifactId>
+                <version>${project.version}</version>
+                <classifier>features</classifier>
+                <type>xml</type>
+                <scope>runtime</scope>
+            </dependency>
+            <dependency>
+              <groupId>${project.groupId}</groupId>
+              <artifactId>features-restconf</artifactId>
+              <version>${project.version}</version>
+              <classifier>features</classifier>
+              <type>xml</type>
+              <scope>runtime</scope>
+            </dependency>
+
+            <!-- Base model augmentations -->
+            <dependency>
+                <groupId>org.opendaylight.controller.model</groupId>
+                <artifactId>model-inventory</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.controller.model</groupId>
+                <artifactId>model-topology</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+
+            <!-- Clustered implementation -->
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-clustering-commons</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-clustering-config</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-distributed-datastore</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-remoterpc-connector</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.controller</groupId>
+                <artifactId>sal-akka-raft</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.controller</groupId>
+                <artifactId>sal-akka-raft</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.controller</groupId>
+                <artifactId>sal-akka-raft-example</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+
+            <!-- RESTCONF -->
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-remote</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.controller</groupId>
+                <artifactId>sal-rest-connector</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.controller</groupId>
+                <artifactId>sal-rest-connector-config</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.controller</groupId>
+                <artifactId>sal-restconf-broker</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.controller</groupId>
+                <artifactId>sal-rest-docgen</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.controller</groupId>
+                <artifactId>sal-rest-docgen-maven</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+
+            <!-- Toaster -->
+            <dependency>
+                <groupId>org.opendaylight.controller.samples</groupId>
+                <artifactId>sample-toaster</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.controller.samples</groupId>
+                <artifactId>sample-toaster-consumer</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.controller.samples</groupId>
+                <artifactId>sample-toaster-provider</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.controller.samples</groupId>
+                <artifactId>toaster-config</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.controller.samples</groupId>
+                <artifactId>features-toaster</artifactId>
+                <version>${project.version}</version>
+                <classifier>features</classifier>
+                <type>xml</type>
+                <scope>runtime</scope>
+            </dependency>
+
+            <!-- XSQL support -->
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-dom-xsql</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-karaf-xsql</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sal-dom-xsql-config</artifactId>
+                <version>${project.version}</version>
+                <classifier>config</classifier>
+                <type>xml</type>
+            </dependency>
+
+            <!-- MessageBus -->
+            <dependency>
+                <groupId>org.opendaylight.controller</groupId>
+                <artifactId>messagebus-api</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.controller</groupId>
+                <artifactId>messagebus-spi</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.controller</groupId>
+                <artifactId>messagebus-impl</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.controller</groupId>
+                <artifactId>messagebus-util</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+
+        </dependencies>
+    </dependencyManagement>
+</project>
diff --git a/opendaylight/md-sal/sal-binding-api/.gitignore b/opendaylight/md-sal/sal-binding-api/.gitignore
new file mode 100644 (file)
index 0000000..ea8c4bf
--- /dev/null
@@ -0,0 +1 @@
+/target
diff --git a/opendaylight/md-sal/sal-binding-api/pom.xml b/opendaylight/md-sal/sal-binding-api/pom.xml
new file mode 100644 (file)
index 0000000..c70115e
--- /dev/null
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>sal-parent</artifactId>
+    <version>1.3.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>sal-binding-api</artifactId>
+  <packaging>bundle</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-common-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>concepts</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-binding</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-common</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+  </dependencies>
+  <scm>
+    <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+    <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+    <tag>HEAD</tag>
+    <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+  </scm>
+</project>
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingService.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingService.java
new file mode 100644 (file)
index 0000000..ccce73c
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareService;
+
+/**
+ *
+ * Marker interface for MD-SAL services which are available for users of MD-SAL.
+ *
+ * BindingService is marker interface for infrastructure services provided by
+ * the SAL. These services may be session-specific, and wrapped by custom
+ * delegator patterns in order to introduce additional semantics / checks
+ * to the system.
+ *
+ * This interface extends {@link BindingAwareService}, order to be make
+ * new services available via
+ * {@link org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext}
+ * and via
+ * {@link org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext}
+ *
+ */
+public interface BindingService extends BindingAwareService {
+
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingTransactionChain.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingTransactionChain.java
new file mode 100644 (file)
index 0000000..c71aa04
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * A chain of transactions.
+ * <p>
+ * For more information about transaction chaining and transaction chains
+ * see {@link TransactionChain}.
+ *
+ * @see TransactionChain
+ *
+ */
+public interface BindingTransactionChain extends TransactionFactory, TransactionChain<InstanceIdentifier<?>, DataObject> {
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    ReadOnlyTransaction newReadOnlyTransaction();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    ReadWriteTransaction newReadWriteTransaction();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    WriteTransaction newWriteOnlyTransaction();
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/ClusteredDataChangeListener.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/ClusteredDataChangeListener.java
new file mode 100644 (file)
index 0000000..511910d
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+
+package org.opendaylight.controller.md.sal.binding.api;
+
+/**
+ * <p>
+ * ClusteredDataChangeListener is a marker interface to enable data change notifications on all instances in a cluster,
+ * where this listener is registered.
+ * </p>
+ *
+ * <p>Applications should implement ClusteredDataChangeListener instead of DataChangeListener, if they want to listen
+ * to data change notifications on any node of clustered datastore. DataChangeListener enables data change notifications
+ * only at leader of the datastore shard.</p>
+ *
+ */
+
+public interface ClusteredDataChangeListener extends DataChangeListener{
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataBroker.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataBroker.java
new file mode 100644 (file)
index 0000000..8b085ba
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+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.TransactionChainFactory;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Provides access to a conceptual data tree store and also provides the ability to
+ * subscribe for changes to data under a given branch of the tree.
+ * <p>
+ * For more information on usage, please see the documentation in {@link AsyncDataBroker}.
+ *
+ * @see AsyncDataBroker
+ * @see TransactionChainFactory
+ */
+public interface DataBroker extends  AsyncDataBroker<InstanceIdentifier<?>, DataObject, DataChangeListener>,
+    TransactionChainFactory<InstanceIdentifier<?>, DataObject>, TransactionFactory, BindingService, DataTreeChangeService {
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    ReadOnlyTransaction newReadOnlyTransaction();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    ReadWriteTransaction newReadWriteTransaction();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    WriteTransaction newWriteOnlyTransaction();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    ListenerRegistration<DataChangeListener> registerDataChangeListener(LogicalDatastoreType store,
+            InstanceIdentifier<?> path, DataChangeListener listener, DataChangeScope triggeringScope);
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    BindingTransactionChain createTransactionChain(TransactionChainListener listener);
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataChangeListener.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataChangeListener.java
new file mode 100644 (file)
index 0000000..2833f41
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/*
+ * DataChangeListener enables data change notifications only at leader of the datastore shard
+ */
+
+public interface DataChangeListener extends AsyncDataChangeListener<InstanceIdentifier<?>, DataObject> {
+    @Override
+    void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change);
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataObjectModification.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataObjectModification.java
new file mode 100644 (file)
index 0000000..afeb21c
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.api;
+
+import java.util.Collection;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+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.PathArgument;
+
+/**
+ * Modified Data Object.
+ *
+ * Represents modification of Data Object.
+ *
+ */
+public interface DataObjectModification<T extends DataObject> extends org.opendaylight.yangtools.concepts.Identifiable<PathArgument> {
+
+    enum ModificationType {
+        /**
+         *
+         * Child node (direct or indirect) was modified.
+         *
+         */
+        SUBTREE_MODIFIED,
+        /**
+         *
+         * Node was explicitly created / overwritten.
+         *
+         */
+        WRITE,
+        /**
+         *
+         * Node was deleted.
+         *
+         */
+        DELETE
+    }
+
+    @Override
+    PathArgument getIdentifier();
+
+    /**
+     * Returns type of modified object.
+     *
+     * @return type of modified object.
+     */
+    @Nonnull Class<T> getDataType();
+
+    /**
+     *
+     * Returns type of modification
+     *
+     * @return type Type of performed modification.
+     */
+    @Nonnull ModificationType getModificationType();
+
+    /**
+     * Returns before-state of top level container. Implementations are encouraged,
+     * but not required to provide this state.
+     *
+     * @param root Class representing data container
+     * @return State of object before modification. Null if subtree was not present,
+     *         or the implementation cannot provide the state.
+     */
+    @Nullable T getDataBefore();
+
+    /**
+     * Returns after-state of top level container.
+     *
+     * @param root Class representing data container
+     * @return State of object after modification. Null if subtree is not present.
+     */
+    @Nullable T getDataAfter();
+
+    /**
+     * Returns unmodifiable collection of modified direct children.
+     *
+     * @return unmodifiable collection of modified direct children.
+     */
+    @Nonnull Collection<DataObjectModification<? extends DataObject>> getModifiedChildren();
+
+    /**
+     * Returns container child modification if {@code child} was modified by this
+     * modification.
+     *
+     * For accessing all modified list items consider iterating over {@link #getModifiedChildren()}.
+     *
+     * @param child Type of child - must be only container
+     * @return Modification of {@code child} if {@code child} was modified, null otherwise.
+     * @throws IllegalArgumentException If supplied {@code child} class is not valid child according
+     *         to generated model.
+     */
+    @Nullable <C extends ChildOf<? super T>> DataObjectModification<C> getModifiedChildContainer(@Nonnull Class<C> child);
+
+    /**
+     * Returns augmentation child modification if {@code augmentation} was modified by this
+     * modification.
+     *
+     * For accessing all modified list items consider iterating over {@link #getModifiedChildren()}.
+     *
+     * @param augmentation Type of augmentation - must be only container
+     * @return Modification of {@code augmentation} if {@code augmentation} was modified, null otherwise.
+     * @throws IllegalArgumentException If supplied {@code augmentation} class is not valid augmentation
+     *         according to generated model.
+     */
+    @Nullable <C extends Augmentation<T> & DataObject> DataObjectModification<C> getModifiedAugmentation(@Nonnull Class<C> augmentation);
+
+
+    /**
+     * Returns child list item modification if {@code child} was modified by this modification.
+     *
+     * @param listItem Type of list item - must be list item with key
+     * @param listKey List item key
+     * @return Modification of {@code child} if {@code child} was modified, null otherwise.
+     * @throws IllegalArgumentException If supplied {@code listItem} class is not valid child according
+     *         to generated model.
+     */
+    <C extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<C>> DataObjectModification<C> getModifiedChildListItem(
+            @Nonnull Class<C> listItem,@Nonnull  K listKey);
+
+    /**
+     * Returns a child modification if a node identified by {@code childArgument} was modified by
+     * this modification.
+     *
+     * @param childArgument Path Argument of child node
+     * @return Modification of child identified by {@code childArgument} if {@code childArgument}
+     *         was modified, null otherwise.
+     * @throws IllegalArgumentException If supplied path argument is not valid child according to
+     *         generated model.
+     *
+     */
+    @Nullable DataObjectModification<? extends DataObject> getModifiedChild(PathArgument childArgument);
+
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeListener.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeListener.java
new file mode 100644 (file)
index 0000000..93ab968
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import java.util.Collection;
+import java.util.EventListener;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Interface implemented by classes interested in receiving notifications about
+ * data tree changes. This interface differs from {@link DataChangeListener}
+ * in that it provides a cursor-based view of the change, which has potentially
+ * lower overhead and allow more flexible consumption of change event.
+ */
+public interface DataTreeChangeListener<T extends DataObject> extends EventListener {
+    /**
+     * Invoked when there was data change for the supplied path, which was used
+     * to register this listener.
+     *
+     * <p>
+     * This method may be also invoked during registration of the listener if
+     * there is any pre-existing data in the conceptual data tree for supplied
+     * path. This initial event will contain all pre-existing data as created.
+     *
+     * <p>
+     * A data change event may be triggered spuriously, e.g. such that data before
+     * and after compare as equal. Implementations of this interface are expected
+     * to recover from such events. Event producers are expected to exert reasonable
+     * effort to suppress such events.
+     *
+     * In other words, it is completely acceptable to observe
+     * a {@link DataObjectModification}, while the state observed before and
+     * after- data items compare as equal.
+     *
+     * @param changes Collection of change events, may not be null or empty.
+     */
+    void onDataTreeChanged(@Nonnull Collection<DataTreeModification<T>> changes);
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeService.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeService.java
new file mode 100644 (file)
index 0000000..9d12e44
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * A {@link DOMService} which allows users to register for changes to a
+ * subtree.
+ */
+public interface DataTreeChangeService extends BindingService {
+    /**
+     * Registers a {@link DataTreeChangeListener} to receive
+     * notifications when data changes under a given path in the conceptual data
+     * tree.
+     * <p>
+     * You are able to register for notifications  for any node or subtree
+     * which can be represented using {@link DataTreeIdentifier}.
+     * <p>
+     *
+     * You are able to register for data change notifications for a subtree or leaf
+     * even if it does not exist. You will receive notification once that node is
+     * created.
+     * <p>
+     * If there is any pre-existing data in the data tree for the path for which you are
+     * registering, you will receive an initial data change event, which will
+     * contain all pre-existing data, marked as created.
+     *
+     * <p>
+     * This method returns a {@link ListenerRegistration} object. To
+     * "unregister" your listener for changes call the {@link ListenerRegistration#close()}
+     * method on the returned object.
+     * <p>
+     * You MUST explicitly unregister your listener when you no longer want to receive
+     * notifications. This is especially true in OSGi environments, where failure to
+     * do so during bundle shutdown can lead to stale listeners being still registered.
+     *
+     * @param treeId
+     *            Data tree identifier of the subtree which should be watched for
+     *            changes.
+     * @param listener
+     *            Listener instance which is being registered
+     * @return Listener registration object, which may be used to unregister
+     *         your listener using {@link ListenerRegistration#close()} to stop
+     *         delivery of change events.
+     */
+    @Nonnull <T extends DataObject,L extends DataTreeChangeListener<T>> ListenerRegistration<L> registerDataTreeChangeListener(@Nonnull DataTreeIdentifier<T> treeId, @Nonnull L listener);
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeIdentifier.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeIdentifier.java
new file mode 100644 (file)
index 0000000..b86d31b
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import com.google.common.base.Preconditions;
+import java.io.Serializable;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * A unique identifier for a particular subtree. It is composed of the logical
+ * data store type and the instance identifier of the root node.
+ */
+public final class DataTreeIdentifier<T extends DataObject> implements Immutable, Path<DataTreeIdentifier<?>>, Serializable {
+    private static final long serialVersionUID = 1L;
+    private final InstanceIdentifier<T> rootIdentifier;
+    private final LogicalDatastoreType datastoreType;
+
+    public DataTreeIdentifier(final LogicalDatastoreType datastoreType, final InstanceIdentifier<T> rootIdentifier) {
+        this.datastoreType = Preconditions.checkNotNull(datastoreType);
+        this.rootIdentifier = Preconditions.checkNotNull(rootIdentifier);
+    }
+
+    /**
+     * Return the logical data store type.
+     *
+     * @return Logical data store type. Guaranteed to be non-null.
+     */
+    public @Nonnull LogicalDatastoreType getDatastoreType() {
+        return datastoreType;
+    }
+
+    /**
+     * Return the {@link YangInstanceIdentifier} of the root node.
+     *
+     * @return Instance identifier corresponding to the root node.
+     */
+    public @Nonnull InstanceIdentifier<T> getRootIdentifier() {
+        return rootIdentifier;
+    }
+
+    @Override
+    public boolean contains(final DataTreeIdentifier<?> other) {
+        return datastoreType == other.datastoreType && rootIdentifier.contains(other.rootIdentifier);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + datastoreType.hashCode();
+        result = prime * result + rootIdentifier.hashCode();
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof DataTreeIdentifier)) {
+            return false;
+        }
+        final DataTreeIdentifier<?> other = (DataTreeIdentifier<?>) obj;
+        if (datastoreType != other.datastoreType) {
+            return false;
+        }
+        return rootIdentifier.equals(other.rootIdentifier);
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeModification.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeModification.java
new file mode 100644 (file)
index 0000000..374c185
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.api;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Represent root of modification.
+ *
+ * @author Tony Tkacik &lt;ttkacik@cisco.com&gt;
+ *
+ */
+public interface DataTreeModification<T extends DataObject> {
+
+    /**
+     * Get the modification root path. This is the path of the root node
+     * relative to the root of InstanceIdentifier namespace.
+     *
+     * @return absolute path of the root node
+     */
+    @Nonnull DataTreeIdentifier<T> getRootPath();
+
+    /**
+     * Get the modification root node.
+     *
+     * @return modification root node
+     */
+    @Nonnull DataObjectModification<T> getRootNode();
+
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/MountPoint.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/MountPoint.java
new file mode 100644 (file)
index 0000000..e8bd753
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import com.google.common.base.Optional;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface MountPoint extends Identifiable<InstanceIdentifier<?>>{
+
+    <T extends BindingService> Optional<T> getService(Class<T> service);
+
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/MountPointService.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/MountPointService.java
new file mode 100644 (file)
index 0000000..0558327
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import com.google.common.base.Optional;
+import java.util.EventListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface MountPointService extends BindingService {
+
+    Optional<MountPoint> getMountPoint(InstanceIdentifier<?> mountPoint);
+
+    <T extends MountPointListener> ListenerRegistration<T> registerListener(InstanceIdentifier<?> path, T listener);
+
+
+    public interface MountPointListener extends EventListener {
+
+        void onMountPointCreated(InstanceIdentifier<?> path);
+
+        void onMountPointRemoved(InstanceIdentifier<?> path);
+
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/NotificationPublishService.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/NotificationPublishService.java
new file mode 100644 (file)
index 0000000..5ec5126
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.TimeUnit;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+/**
+ * A {@link NotificationService} which also allows its users to
+ * submit YANG-modeled notifications for delivery. There are three
+ * methods of submission, following the patters from {@link java.util.concurrent.BlockingQueue}:
+ * - {@link #putNotification(Notification)}, which may block indefinitely
+ *   if the implementation cannot allocate resources to accept the notification,
+ * - {@link #offerNotification(Notification)}, which does not block if face
+ *   of resource starvation,
+ * - {@link #offerNotification(Notification, int, TimeUnit)}, which may block
+ *   for specified time if resources are thin.
+ *
+ * The actual delivery to listeners is asynchronous and implementation-specific.
+ * Users of this interface should not make any assumptions as to whether the
+ * notification has or has not been seen.
+ */
+public interface NotificationPublishService extends BindingService {
+
+    /**
+     * Well-known value indicating that the binding-aware implementation is currently not
+     * able to accept a notification.
+     */
+    ListenableFuture<Object> REJECTED = Futures.immediateFailedFuture(new NotificationRejectedException("Rejected due to resource constraints."));
+
+    /**
+     * Publishes a notification to subscribed listeners. This initiates
+     * the process of sending the notification, but delivery to the
+     * listeners can happen asynchronously, potentially after a call to
+     * this method returns.
+     *
+     * <b>Note:</b> This call will block when the notification queue is full.
+     *
+     * @param notification
+     *            the notification to publish.
+     * @throws InterruptedException if interrupted while waiting
+     * @throws NullPointerException if the notification is null
+     */
+    void putNotification(Notification notification) throws InterruptedException;
+
+    /**
+     * Publishes a notification to subscribed listeners. This initiates
+     * the process of sending the notification, but delivery to the
+     * listeners can happen asynchronously, potentially after a call to
+     * this method returns.
+     *
+     * Still guaranteed not to block. Returns Listenable Future which will complete once.
+     *
+     * @param notification
+     *            the notification to publish.
+     * @return A listenable future which will report completion when the service has finished
+     * propagating the notification to its immediate registrants, or {@value #REJECTED} if resource
+     * constraints prevent
+     * @throws NullPointerException if the notification is null
+     */
+    ListenableFuture<? extends Object> offerNotification(Notification notification);
+
+    /**
+     * Publishes a notification to subscribed listeners. This initiates
+     * the process of sending the notification, but delivery to the
+     * listeners can happen asynchronously, potentially after a call to
+     * this method returns. This method is guaranteed not to block more
+     * than the specified timeout.
+     *
+     * @param notification
+     *            the notification to publish.
+     * @param timeout how long to wait before giving up, in units of unit
+     * @param unit a TimeUnit determining how to interpret the
+     *             timeout parameter
+     * @return A listenable future which will report completion when the service has finished
+     * propagating the notification to its immediate registrants, or {@value #REJECTED} if resource
+     * constraints prevent
+     * @throws InterruptedException if interrupted while waiting
+     * @throws NullPointerException if the notification or unit is null
+     * @throws IllegalArgumentException if timeout is negative.
+     */
+    ListenableFuture<? extends Object> offerNotification(Notification notification, int timeout, TimeUnit unit)
+            throws InterruptedException;
+
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/NotificationRejectedException.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/NotificationRejectedException.java
new file mode 100644 (file)
index 0000000..41c3a40
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+/**
+ * <p>
+ * This exception indicates that given notification can not be processed by corresponding mechanism.
+ * More info can be provided in message.
+ * </p>
+ * <p>
+ * Expected use: {@link NotificationPublishService}
+ * </p>
+ */
+public class NotificationRejectedException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public NotificationRejectedException(final String message) {
+        super(message);
+    }
+
+    public NotificationRejectedException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/NotificationService.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/NotificationService.java
new file mode 100644 (file)
index 0000000..ba35235
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+
+/**
+ * Notification broker which allows clients to subscribe for and publish YANG-modeled notifications.
+ *
+ * Each YANG module which defines notifications results in a generated interface <code>{ModuleName}Listener</code>
+ * which handles all the notifications defined in the YANG model. Each notification type translates to
+ * a specific method of the form <code>on{NotificationType}</code> on the generated interface.
+ * The generated interface also extends the
+ * {@link org.opendaylight.yangtools.yang.binding.NotificationListener} interface and implementations
+ * are registered using {@link #registerNotificationListener(org.opendaylight.yangtools.yang.binding.NotificationListener)}
+ * method.
+ *
+ * <h5>Dispatch Listener Example</h5>
+ * <p>
+ * Lets assume we have following YANG model:
+ *
+ * <pre>
+ * module example {
+ *      ...
+ *
+ *      notification start {
+ *          ...
+ *      }
+ *
+ *      notification stop {
+ *           ...
+ *      }
+ * }
+ * </pre>
+ *
+ * The generated interface will be:
+ * <pre>
+ *  public interface ExampleListener extends NotificationListener {
+ *      void onStart(Start notification);
+ *      void onStop(Stop notification);
+ *  }
+ * </pre>
+ * The following defines an implementation of the generated interface:
+ * <pre>
+ *  public class MyExampleListener implements ExampleListener {
+ *      public void onStart(Start notification) {
+ *          // do something
+ *      }
+ *
+ *      public void onStop(Stop notification) {
+ *          // do something
+ *      }
+ *  }
+ * </pre>
+ * The implementation is registered as follows:
+ * <pre>
+ *  MyExampleListener listener = new MyExampleListener();
+ *  ListenerRegistration<NotificationListener> reg = service.registerNotificationListener( listener );
+ * </pre>
+ * The <code>onStart</code> method will be invoked when someone publishes a <code>Start</code> notification and
+ * the <code>onStop</code> method will be invoked when someone publishes a <code>Stop</code> notification.
+ */
+public interface NotificationService extends BindingService {
+    /**
+     * Registers a listener which implements a YANG-generated notification interface derived from
+     * {@link NotificationListener}. The listener is registered for all notifications present in
+     * the implemented interface.
+     *
+     * @param listener the listener implementation that will receive notifications.
+     * @return a {@link ListenerRegistration} instance that should be used to unregister the listener
+     *         by invoking the {@link ListenerRegistration#close()} method when no longer needed.
+     */
+    <T extends NotificationListener> ListenerRegistration<T> registerNotificationListener(T listener);
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/ReadOnlyTransaction.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/ReadOnlyTransaction.java
new file mode 100644 (file)
index 0000000..f3b42b9
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadOnlyTransaction;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * A transaction that provides a stateful read-only view of the data tree.
+ * <p>
+ * For more information on usage and examples, please see the documentation in
+ *  {@link org.opendaylight.controller.md.sal.common.api.data.AsyncReadTransaction}.
+ */
+public interface ReadOnlyTransaction extends ReadTransaction, AsyncReadOnlyTransaction<InstanceIdentifier<?>, DataObject> {
+
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/ReadTransaction.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/ReadTransaction.java
new file mode 100644 (file)
index 0000000..b0c9373
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadTransaction;
+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.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+
+/**
+ * A transaction that provides read access to a logical data store.
+ * <p>
+ * For more information on usage and examples, please see the documentation in {@link AsyncReadTransaction}.
+ */
+public interface ReadTransaction extends AsyncReadTransaction<InstanceIdentifier<?>, DataObject> {
+
+    /**
+     * Reads data from the provided logical data store located at the provided path.
+     *<p>
+     * If the target is a subtree, then the whole subtree is read (and will be
+     * accessible from the returned data object).
+     *
+     * @param store
+     *            Logical data store from which read should occur.
+     * @param path
+     *            Path which uniquely identifies subtree which client want to
+     *            read
+     * @return a CheckFuture containing the result of the read. The Future blocks until the
+     *         commit operation is complete. Once complete:
+     *         <ul>
+     *         <li>If the data at the supplied path exists, the Future returns an Optional object
+     *         containing the data.</li>
+     *         <li>If the data at the supplied path does not exist, the Future returns
+     *         Optional#absent().</li>
+     *         <li>If the read of the data fails, the Future will fail with a
+     *         {@link ReadFailedException} or an exception derived from ReadFailedException.</li>
+     *         </ul>
+     */
+    <T extends DataObject> CheckedFuture<Optional<T>,ReadFailedException> read(
+            LogicalDatastoreType store, InstanceIdentifier<T> path);
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/ReadWriteTransaction.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/ReadWriteTransaction.java
new file mode 100644 (file)
index 0000000..9fbbbe5
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadWriteTransaction;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * A transaction that enables combined read/write capabilities.
+ * <p>
+ * For more information on usage and examples, please see the documentation in {@link AsyncReadWriteTransaction}.
+ */
+public interface ReadWriteTransaction extends ReadTransaction, WriteTransaction, AsyncReadWriteTransaction<InstanceIdentifier<?>, DataObject> {
+
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/TransactionFactory.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/TransactionFactory.java
new file mode 100644 (file)
index 0000000..1f0a6fc
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataTransactionFactory;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface TransactionFactory extends AsyncDataTransactionFactory<InstanceIdentifier<?>, DataObject>{
+
+    @Override
+    ReadOnlyTransaction newReadOnlyTransaction();
+
+    @Override
+    ReadWriteTransaction newReadWriteTransaction();
+
+    @Override
+    WriteTransaction newWriteOnlyTransaction();
+
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/WriteTransaction.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/WriteTransaction.java
new file mode 100644 (file)
index 0000000..a6ba2e2
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * A transaction that provides mutation capabilities on a data tree.
+ * <p>
+ * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}.
+ */
+public interface WriteTransaction extends AsyncWriteTransaction<InstanceIdentifier<?>, DataObject> {
+
+    /**
+     * Stores a piece of data at the specified path. This acts as an add / replace
+     * operation, which is to say that whole subtree will be replaced by the specified data.
+     * * <p>
+     * This method does not automatically create missing parent nodes. It is equivalent to invoking
+     * {@link #put(LogicalDatastoreType, InstanceIdentifier, DataObject, boolean)}
+     * with <code>createMissingParents</code> set to false.
+     * <p>
+     * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}.
+     * <p>
+     * If you need to make sure that a parent object exists but you do not want modify
+     * its pre-existing state by using put, consider using {@link #merge} instead.
+     *
+     * @param store
+     *            the logical data store which should be modified
+     * @param path
+     *            the data object path
+     * @param data
+     *            the data object to be written to the specified path
+     * @throws IllegalStateException
+     *             if the transaction has already been submitted
+     */
+    <T extends DataObject> void put(LogicalDatastoreType store, InstanceIdentifier<T> path, T data);
+
+
+    /**
+     * Stores a piece of data at the specified path. This acts as an add /
+     * replace operation, which is to say that whole subtree will be replaced by
+     * the specified data.
+     * <p>
+     * For more information on usage and examples, please see the documentation
+     * in {@link AsyncWriteTransaction}.
+     * <p>
+     * If you need to make sure that a parent object exists but you do not want
+     * modify its pre-existing state by using put, consider using {@link #merge}
+     * instead.
+     *
+     * Note: Using <code>createMissingParents</code> with value true, may
+     * introduce garbage in data store, or recreate nodes, which were deleted by
+     * previous transaction.
+     *
+     * @param store
+     *            the logical data store which should be modified
+     * @param path
+     *            the data object path
+     * @param data
+     *            the data object to be written to the specified path
+     * @param createMissingParents
+     *            if true, any missing parent nodes will be automatically
+     *            created using a merge operation.
+     * @throws IllegalStateException
+     *             if the transaction has already been submitted
+     */
+    <T extends DataObject> void put(LogicalDatastoreType store, InstanceIdentifier<T> path, T data,
+            boolean createMissingParents);
+
+    /**
+     * Merges a piece of data with the existing data at a specified path. Any pre-existing data
+     * which is not explicitly overwritten will be preserved. This means that if you store a container,
+     * its child lists will be merged.
+     * <p>
+     * This method does not automatically create missing parent nodes. It is equivalent to invoking
+     * {@link #merge(LogicalDatastoreType, InstanceIdentifier, DataObject, boolean)}
+     * with <code>createMissingParents</code> set to false.
+     * <p>
+     * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}.
+     *<p>
+     * If you require an explicit replace operation, use {@link #put} instead.
+     * @param store
+     *            the logical data store which should be modified
+     * @param path
+     *            the data object path
+     * @param data
+     *            the data object to be merged to the specified path
+     * @throws IllegalStateException
+     *             if the transaction has already been submitted
+     */
+    <T extends DataObject> void merge(LogicalDatastoreType store, InstanceIdentifier<T> path, T data);
+
+    /**
+     * Merges a piece of data with the existing data at a specified path. Any
+     * pre-existing data which is not explicitly overwritten will be preserved.
+     * This means that if you store a container, its child lists will be merged.
+     * <p>
+     * For more information on usage and examples, please see the documentation
+     * in {@link AsyncWriteTransaction}.
+     * <p>
+     * If you require an explicit replace operation, use {@link #put} instead.
+     *
+     * @param store
+     *            the logical data store which should be modified
+     * @param path
+     *            the data object path
+     * @param data
+     *            the data object to be merged to the specified path
+     * @param createMissingParents
+     *            if true, any missing parent nodes will be automatically created
+     *            using a merge operation.
+     * @throws IllegalStateException
+     *             if the transaction has already been submitted
+     */
+    <T extends DataObject> void merge(LogicalDatastoreType store, InstanceIdentifier<T> path, T data,
+            boolean createMissingParents);
+
+    @Override
+    void delete(LogicalDatastoreType store, InstanceIdentifier<?> path);
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/AbstractBindingAwareConsumer.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/AbstractBindingAwareConsumer.java
new file mode 100644 (file)
index 0000000..fc9d9fa
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import org.osgi.framework.BundleContext;
+
+@Deprecated
+public abstract class AbstractBindingAwareConsumer extends AbstractBrokerAwareActivator implements BindingAwareConsumer {
+
+    @Override
+    protected final void onBrokerAvailable(BindingAwareBroker broker, BundleContext context) {
+        broker.registerConsumer(this, context);
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/AbstractBindingAwareProvider.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/AbstractBindingAwareProvider.java
new file mode 100644 (file)
index 0000000..0b9c604
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+import org.osgi.framework.BundleContext;
+
+@Deprecated
+public abstract class AbstractBindingAwareProvider extends AbstractBrokerAwareActivator implements BindingAwareProvider {
+
+    @Override
+    protected final void onBrokerAvailable(BindingAwareBroker broker, BundleContext context) {
+        ProviderContext ctx = broker.registerProvider(this, context);
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/AbstractBrokerAwareActivator.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/AbstractBrokerAwareActivator.java
new file mode 100644 (file)
index 0000000..bd78c58
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+public abstract class AbstractBrokerAwareActivator implements BundleActivator {
+
+    private static final ExecutorService mdActivationPool = Executors.newCachedThreadPool();
+    private BundleContext context;
+    private ServiceTracker<BindingAwareBroker, BindingAwareBroker> tracker;
+    private BindingAwareBroker broker;
+    private ServiceTrackerCustomizer<BindingAwareBroker, BindingAwareBroker> customizer = new ServiceTrackerCustomizer<BindingAwareBroker, BindingAwareBroker>() {
+
+        @Override
+        public BindingAwareBroker addingService(ServiceReference<BindingAwareBroker> reference) {
+            broker = context.getService(reference);
+            mdActivationPool.execute(new Runnable() {
+
+                @Override
+                public void run() {
+                    onBrokerAvailable(broker, context);
+                }
+            });
+            return broker;
+        }
+
+        @Override
+        public void modifiedService(ServiceReference<BindingAwareBroker> reference, BindingAwareBroker service) {
+            removedService(reference, service);
+            addingService(reference);
+        }
+
+        @Override
+        public void removedService(ServiceReference<BindingAwareBroker> reference, BindingAwareBroker service) {
+            broker = context.getService(reference);
+            mdActivationPool.execute(new Runnable() {
+
+                @Override
+                public void run() {
+                    onBrokerRemoved(broker, context);
+                }
+            });
+        }
+
+    };
+
+
+    @Override
+    public final void start(BundleContext context) throws Exception {
+        this.context = context;
+        startImpl(context);
+        tracker = new ServiceTracker<>(context, BindingAwareBroker.class, customizer);
+        tracker.open();
+
+    }
+
+
+
+    @Override
+    public final  void stop(BundleContext context) throws Exception {
+        tracker.close();
+        stopImpl(context);
+    }
+
+
+    /**
+     * Called when this bundle is started (before
+     * {@link #onSessionInitiated(ProviderContext)} so the Framework can perform
+     * the bundle-specific activities necessary to start this bundle. This
+     * method can be used to register services or to allocate any resources that
+     * this bundle needs.
+     *
+     * <p>
+     * This method must complete and return to its caller in a timely manner.
+     *
+     * @param context
+     *            The execution context of the bundle being started.
+     * @throws Exception
+     *             If this method throws an exception, this bundle is marked as
+     *             stopped and the Framework will remove this bundle's
+     *             listeners, unregister all services registered by this bundle,
+     *             and release all services used by this bundle.
+     */
+    protected void startImpl(BundleContext context) {
+        // NOOP
+    }
+
+    /**
+     * Called when this bundle is stopped so the Framework can perform the
+     * bundle-specific activities necessary to stop the bundle. In general, this
+     * method should undo the work that the {@code BundleActivator.start} method
+     * started. There should be no active threads that were started by this
+     * bundle when this bundle returns. A stopped bundle must not call any
+     * Framework objects.
+     *
+     * <p>
+     * This method must complete and return to its caller in a timely manner.
+     *
+     * @param context The execution context of the bundle being stopped.
+     * @throws Exception If this method throws an exception, the bundle is still
+     *         marked as stopped, and the Framework will remove the bundle's
+     *         listeners, unregister all services registered by the bundle, and
+     *         release all services used by the bundle.
+     */
+    protected void stopImpl(BundleContext context) {
+        // NOOP
+    }
+
+
+    protected abstract void onBrokerAvailable(BindingAwareBroker broker, BundleContext context);
+
+    protected void onBrokerRemoved(BindingAwareBroker broker, BundleContext context) {
+        stopImpl(context);
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/BindingAwareBroker.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/BindingAwareBroker.java
new file mode 100644 (file)
index 0000000..ab62c80
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.routing.RoutedRegistration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Binding-aware core of the SAL layer responsible for wiring the SAL consumers.
+ *
+ * The responsibility of the broker is to maintain registration of SAL
+ * functionality {@link Consumer}s and {@link Provider}s, store provider and
+ * consumer specific context and functionality registration via
+ * {@link ConsumerContext} and provide access to infrastructure services, which
+ * removes direct dependencies between providers and consumers.
+ *
+ * The Binding-aware broker is also responsible for translation from Java
+ * classes modeling the functionality and data to binding-independent form which
+ * is used in SAL Core.
+ *
+ *
+ * <h3>Infrastructure services</h3> Some examples of infrastructure services:
+ *
+ * <ul>
+ * <li>YANG Module service - see {@link ConsumerContext#getRpcService(Class)},
+ * {@link ProviderContext}
+ * <li>Notification Service - see {@link NotificationService} and
+ * {@link NotificationProviderService}
+ * <li>Functionality and Data model
+ * <li>Data Store access and modification - see {@link org.opendaylight.controller.sal.binding.api.data.DataBrokerService} and
+ * {@link org.opendaylight.controller.sal.binding.api.data.DataProviderService}
+ * </ul>
+ *
+ * The services are exposed via session.
+ *
+ * <h3>Session-based access</h3>
+ *
+ * The providers and consumers needs to register in order to use the
+ * binding-independent SAL layer and to expose functionality via SAL layer.
+ *
+ * For more information about session-based access see {@link ConsumerContext}
+ * and {@link ProviderContext}
+ */
+public interface BindingAwareBroker {
+    /*
+     * @deprecated Use registerConsumer(BindingAwareConsumer cons) instead (BundleContext is no longer used)
+     */
+    @Deprecated
+    ConsumerContext registerConsumer(BindingAwareConsumer consumer, BundleContext ctx);
+
+    /**
+     * Registers the {@link BindingAwareConsumer}, which will use the SAL layer.
+     *
+     * <p>
+     * Note that consumer could register additional functionality at later point
+     * by using service and functionality specific APIs.
+     *
+     * <p>
+     * The consumer is required to use returned session for all communication
+     * with broker or one of the broker services. The session is announced to
+     * the consumer by invoking
+     * {@link Consumer#onSessionInitiated(ConsumerContext)}.
+     *
+     * @param cons
+     *            Consumer to be registered.
+     * @return a session specific to consumer registration
+     * @throws IllegalArgumentException
+     *             If the consumer is <code>null</code>.
+     * @throws IllegalStateException
+     *             If the consumer is already registered.
+     */
+    ConsumerContext registerConsumer(BindingAwareConsumer consumer);
+
+    /*
+     * @deprecated Use registerProvider(BindingAwareProvider prov) instead (BundleContext is no longer used)
+     */
+    @Deprecated
+    ProviderContext registerProvider(BindingAwareProvider provider, BundleContext ctx);
+
+    /**
+     * Registers the {@link BindingAwareProvider}, which will use the SAL layer.
+     *
+     * <p>
+     * During the registration, the broker obtains the initial functionality
+     * from consumer, using the
+     * {@link BindingAwareProvider#getImplementations()}, and register that
+     * functionality into system and concrete infrastructure services.
+     *
+     * <p>
+     * Note that provider could register additional functionality at later point
+     * by using service and functionality specific APIs.
+     *
+     * <p>
+     * The consumer is <b>required to use</b> returned session for all
+     * communication with broker or one of the broker services. The session is
+     * announced to the consumer by invoking
+     * {@link BindingAwareProvider#onSessionInitiated(ProviderContext)}.
+     *
+     *
+     * @param prov
+     *            Provider to be registered.
+     * @return a session unique to the provider registration.
+     * @throws IllegalArgumentException
+     *             If the provider is <code>null</code>.
+     * @throws IllegalStateException
+     *             If the consumer is already registered.
+     */
+    ProviderContext registerProvider(BindingAwareProvider provider);
+
+    /**
+     * {@link BindingAwareConsumer} specific access to the SAL functionality.
+     *
+     * <p>
+     * ConsumerSession is {@link BindingAwareConsumer}-specific access to the
+     * SAL functionality and infrastructure services.
+     *
+     * <p>
+     * The session serves to store SAL context (e.g. registration of
+     * functionality) for the consumer and provides access to the SAL
+     * infrastructure services and other functionality provided by
+     * {@link Provider}s.
+     */
+    public interface ConsumerContext extends RpcConsumerRegistry {
+
+        /**
+         * Returns a session specific instance (implementation) of requested
+         * binding-aware infrastructural service
+         *
+         * @param service
+         *            Broker service
+         * @return Session specific implementation of service
+         */
+        <T extends BindingAwareService> T getSALService(Class<T> service);
+    }
+
+    /**
+     * {@link BindingAwareProvider} specific access to the SAL functionality.
+     *
+     * <p>
+     * ProviderSession is {@link BindingAwareProvider}-specific access to the
+     * SAL functionality and infrastructure services, which also allows for
+     * exposing the provider's functionality to the other
+     * {@link BindingAwareConsumer}s.
+     *
+     * <p>
+     * The session serves to store SAL context (e.g. registration of
+     * functionality) for the providers and exposes access to the SAL
+     * infrastructure services, dynamic functionality registration and any other
+     * functionality provided by other {@link BindingAwareConsumer}s.
+     *
+     */
+    public interface ProviderContext extends ConsumerContext, RpcProviderRegistry {
+
+    }
+
+    /**
+     * Represents an RPC implementation registration. Users should call the
+     * {@link ObjectRegistration#close close} method when the registration is no longer needed.
+     *
+     * @param <T> the implemented RPC service interface
+     */
+    public interface RpcRegistration<T extends RpcService> extends ObjectRegistration<T> {
+
+        /**
+         * Returns the implemented RPC service interface.
+         */
+        Class<T> getServiceType();
+
+        @Override
+        void close();
+    }
+
+    /**
+     * Represents a routed RPC implementation registration. Users should call the
+     * {@link RoutedRegistration#close close} method when the registration is no longer needed.
+     *
+     * @param <T> the implemented RPC service interface
+     */
+    public interface RoutedRpcRegistration<T extends RpcService> extends RpcRegistration<T>,
+            RoutedRegistration<Class<? extends BaseIdentity>, InstanceIdentifier<?>, T> {
+
+        /**
+         * Register particular instance identifier to be processed by this
+         * RpcService
+         *
+         * Deprecated in favor of {@link RoutedRegistration#registerPath(Object, Object)}.
+         *
+         * @param context
+         * @param instance
+         */
+        @Deprecated
+        void registerInstance(Class<? extends BaseIdentity> context, InstanceIdentifier<?> instance);
+
+        /**
+         * Unregister particular instance identifier to be processed by this
+         * RpcService
+         *
+         * Deprecated in favor of {@link RoutedRegistration#unregisterPath(Object, Object)}.
+         *
+         * @param context
+         * @param instance
+         */
+        @Deprecated
+        void unregisterInstance(Class<? extends BaseIdentity> context, InstanceIdentifier<?> instance);
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/BindingAwareConsumer.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/BindingAwareConsumer.java
new file mode 100644 (file)
index 0000000..bcbd687
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext;
+
+/**
+*
+* A developer implemented component that gets registered with the Broker.
+*
+* Semantically, a consumer may:
+*
+* <ol>
+*   <li>Subscribe for Notifications </li>
+*   <li>Invoke RPCs</li>
+*   <li>Read from either the operational or config data tree</li>
+*   <li>Write to the config data tree</li>
+* </ol>
+* If you need to:
+* <ol>
+*   <li> Emit Notifications</li>
+*   <li> Provide the implementation of RPCs </li>
+*   <li> Write to the operational data tree </li>
+* </ol>
+*
+* Consider using a BindingAwareProvider
+*
+* Examples:
+*
+* To get a NotificationService:
+*
+* {code
+* public void onSessionInitiated(ProviderContext session) {
+*      NotificationProviderService notificationService = session.getSALService(NotificationProviderService.class);
+*      notificationService.publish(notification)
+* }
+* where notification is an instance of a modeled Notification.
+* For more information on sending notifications via the NotificationProviderService
+* @see org.opendaylight.controller.sal.binding.api.NotificationProviderService
+*
+*
+* A consumer can *invoke* and RPC ( ie, call foo(fooArgs)) but it cannot register an RPC
+* implementation with the MD-SAL that others can invoke(call).
+* To get an invokable RPC:
+*
+* {code
+* public void onSessionInitiated(ProviderContext session) {
+*    MyService rpcFlowSalService = session.getRpcService(MyService.class);
+* }
+*
+* Where MyService.class is a Service interface generated from a yang model with RPCs modeled in it.  The returned
+* rpcFlowSalService can be used like any other object by invoking its methods.  Note, nothing special needs to be done
+* for RoutedRPCs.  They just work.
+*
+* To get a DataBroker to allow access to the data tree:
+*
+* {code
+* public void onSessionInitiated(final ProviderContext session) {
+*      DataBroker databroker = session.getSALService(BindingDataBroker.class);
+* }
+* }
+* @see org.opendaylight.controller.md.sal.common.api.data.BindingDataBroker
+* for more info on using the DataBroker.
+*
+*/
+public interface BindingAwareConsumer {
+
+    /**
+     * Callback signaling initialization of the consumer session to the SAL.
+     *
+     * The consumer MUST use the session for all communication with SAL or
+     * retrieving SAL infrastructure services.
+     *
+     * This method is invoked by
+     * {@link BindingAwareBroker#registerConsumer(BindingAwareConsumer)}
+     *
+     * @param session
+     *            Unique session between consumer and SAL.
+     */
+    void onSessionInitialized(ConsumerContext session);
+
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/BindingAwareProvider.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/BindingAwareProvider.java
new file mode 100644 (file)
index 0000000..35583c0
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+
+/**
+ *
+ * A developer implemented component that gets registered with the Broker.
+ *
+ * Semantically, a provider may:
+ *
+ * <ol>
+ *   <li> Emit Notifications</li>
+ *   <li> Provide the implementation of RPCs </li>
+ *   <li> Write to the operational data tree </li>
+ * </ol>
+ *
+ * If a class is not doing at least one of those three, consider using
+ * a BindingAwareConsumer instead:
+ * @see org.opendaylight.controller.sal.binding.api.BindingAwareConsumer
+ *
+ * <p>
+ *
+ *In addition, a BindingAwareProvider can in pursuit of its goals:
+ *
+ * <ol>
+ *   <li>Subscribe for Notifications </li>
+ *   <li>Invoke RPCs</li>
+ *   <li>Read from either the operational or config data tree</li>
+ *   <li>Write to the config data tree</li>
+ * </ol>
+ * (All of the above are things a Consumer can also do).
+ *
+ *<p>
+ *
+ * Examples:
+ *
+ *<p>
+ *
+ * To get a NotificationService:
+ *
+ * {code
+ * public void onSessionInitiated(ProviderContext session) {
+ *      NotificationProviderService notificationService = session.getSALService(NotificationProviderService.class);
+ * }
+ * For more information on sending notifications via the NotificationProviderService
+ * @see org.opendaylight.controller.sal.binding.api.NotificationProviderService
+ *
+ * To register an RPC implementation:
+ *
+ * {code
+ * public void onSessionInitiated(ProviderContext session) {
+ *    RpcRegistration<MyService> registration = session.addRpcImplementation(MyService.class, myImplementationInstance);
+ * }
+ *
+ * <p>
+ *
+ * Where MyService.class is a Service interface generated from a yang model with RPCs modeled in it and myImplementationInstance
+ * is an instance of a class that implements MyService.
+ *
+ * To register a Routed RPC Implementation:
+ * {code
+ * public void onSessionInitiated(ProviderContext session) {
+ *   RoutedRpcRegistration<SalFlowService> flowRegistration = session.addRoutedRpcImplementation(SalFlowService.class, salFlowServiceImplementationInstance);
+     flowRegistration.registerPath(NodeContext.class, nodeInstanceId);
+ * }
+ * }
+ *
+ * Where SalFlowService.class is a Service interface generated from a yang model with RPCs modeled in it and salFlowServiceImplementationInstance is an instance
+ * of a class that implements SalFlowService.
+ * <p>
+ * The line:
+ * {code
+ * flowRegistration.registerPath(NodeContext.class, nodeInstanceId);
+ * }
+ * Is indicating that the RPC implementation is registered to handle RPC invocations that have their NodeContext pointing to the node with instance id nodeInstanceId.
+ * This bears a bit of further explanation.  RoutedRPCs can be 'routed' to an implementation based upon 'context'.  'context' is a pointer (instanceId) to some place
+ * in the data tree.  In this example, the 'context' is a pointer to a Node.  In this way, a provider can register its ability to provide a service for a particular
+ * Node, but not *all* Nodes.  The Broker routes the RPC by 'context' to the correct implementation, without the caller having to do extra work.  Because of this when
+ * a RoutedRPC is registered, it needs to also be able to indicate for which 'contexts' it is providing an implementation.
+ *
+ * An example of a Routed RPC would be an updateFlow(node, flow) that would be routed based on node to the provider which had registered to provide
+ * it *for that node*.
+ *
+ *<p>
+ *
+ * To get a DataBroker to allow access to the data tree:
+ *
+ * {code
+ * public void onSessionInitiated(final ProviderContext session) {
+ *      DataBroker databroker = session.getSALService(BindingDataBroker.class);
+ * }
+ * }
+ * @see org.opendaylight.controller.md.sal.common.api.data.BindingDataBroker
+ * for more info on using the DataBroker.
+ *
+ */
+public interface BindingAwareProvider {
+
+    /**
+     * Callback signaling initialization of the consumer session to the SAL.
+     *
+     * The consumer MUST use the session for all communication with SAL or
+     * retrieving SAL infrastructure services.
+     *
+     * This method is invoked by
+     * {@link BindingAwareBroker#registerProvider(BindingAwareProvider)}
+     *
+     * @param session Unique session between consumer and SAL.
+     */
+    void onSessionInitiated(ProviderContext session);
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/BindingAwareService.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/BindingAwareService.java
new file mode 100644 (file)
index 0000000..adaa27f
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+/**
+ * Session-specific instance of the broker functionality.
+ *
+ * <p>
+ * BindingAwareService is marker interface for infrastructure services provided
+ * by the SAL. These services are session-specific, each
+ * {@link BindingAwareConsumer} and {@link BindingAwareProvider} usually has own
+ * instance of the service with it's own context.
+ *
+ * <p>
+ * The consumer's (or provider's) instance of specific service could be obtained
+ * by invoking {@link org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext#getSALService(Class)} method on session
+ * assigned to the consumer.
+ *
+ * <p>
+ * {@link org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext} and {@link BindingAwareProvider} may seem
+ * similar, but provider provides YANG model-based functionality and
+ * {@link BindingAwareProvider} exposes the necessary supporting functionality
+ * to implement specific functionality of YANG and to reuse it in the
+ * development of {@link BindingAwareConsumer}s and {@link BindingAwareProvider}s.
+ */
+public interface BindingAwareService {
+
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/NotificationListener.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/NotificationListener.java
new file mode 100644 (file)
index 0000000..e5a0a2b
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import java.util.EventListener;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+/**
+ * Interface for a generic listener that is interested in receiving YANG modeled notifications.
+ * This interface acts as a base interface for specific listeners which usually are a type
+ * capture of this interface.
+ *
+ * @param <T> the interested notification type
+ * @deprecated Deprecated unused API.
+ */
+@Deprecated
+public interface NotificationListener<T extends Notification> extends EventListener {
+    /**
+     * Invoked to deliver a notification.
+     * <p>
+     * Note that this method may be invoked from a shared thread pool, so implementations SHOULD NOT
+     * perform CPU-intensive operations and MUST NOT invoke any potentially blocking operations.
+     *
+     * @param notification the notification.
+     */
+    void onNotification(T notification);
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/NotificationProviderService.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/NotificationProviderService.java
new file mode 100644 (file)
index 0000000..4b06e77
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import java.util.EventListener;
+import java.util.concurrent.ExecutorService;
+import org.opendaylight.controller.md.sal.common.api.notify.NotificationPublishService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+/**
+ * Interface for a notification service that provides publish/subscribe capabilities for YANG
+ * modeled notifications. This interface is a combination of the {@link NotificationService} and
+ * {@link NotificationPublishService} interfaces.
+ *
+ * @deprecated Please use {@link org.opendaylight.controller.md.sal.binding.api.NotificationPublishService}.
+ */
+@Deprecated
+public interface NotificationProviderService extends NotificationService, NotificationPublishService<Notification> {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void publish(Notification notification);
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    void publish(Notification notification, ExecutorService executor);
+
+    /**
+     * Registers a listener to be notified about notification subscriptions. This
+     * enables a component to know when there is a notification listener subscribed
+     * for a particular notification type.
+     * <p>
+     * On registration of this listener, the
+     * {@link NotificationInterestListener#onNotificationSubscribtion(Class)} method
+     * will be invoked for every notification type that currently has a notification listener
+     * subscribed.
+     *
+     * @param interestListener the listener that will be notified when subscriptions
+     *                         for new notification types occur.
+     * @return a {@link ListenerRegistration} instance that should be used to unregister the listener
+     *         by invoking the {@link ListenerRegistration#close()} method when no longer needed.
+     */
+    ListenerRegistration<NotificationInterestListener> registerInterestListener(
+            NotificationInterestListener interestListener);
+
+    /**
+     * Interface for a listener interested in being notified about notification subscriptions.
+     */
+    public interface NotificationInterestListener extends EventListener {
+
+        /**
+         * Callback that is invoked when a notification listener subscribes for a
+         * particular notification type.
+         * <p>
+         * This method is only called for the first subscription that occurs for a
+         * particular notification type. Subsequent subscriptions for the same
+         * notification type do not trigger invocation of this method.
+         * <p>
+         * <b>Note:</b>This callback is delivered from thread not owned by this listener,
+         * all processing should be as fast as possible and implementations should
+         * not do any blocking calls or block this thread.
+         *
+         * @param notificationType the notification type for the subscription that occurred.
+         */
+        void onNotificationSubscribtion(Class<? extends Notification> notificationType);
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/NotificationService.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/NotificationService.java
new file mode 100644 (file)
index 0000000..dd66aa6
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+/**
+ * Notification broker which allows clients to subscribe for and publish YANG-modeled notifications.
+ *
+ *<p>
+ * Two styles of listeners are supported:
+ * <ul>
+ * <li>Generic listener</li>
+ * <li>Dispatch listener - listener, which implements <code>{ModelName}Listener</code> interface,
+ * which has dispatch methods for each defined notification. Methods are invoked based on notification type (class).
+ * </li>
+ *
+ * <h3>Generic Listener</h3>
+ * <p>
+ * A generic listener implements the {@link NotificationListener} interface which has one callback method
+ * <code>onNotification</code> that is invoked for any notification type the listener is subscribed to.
+ * <p>
+ * A generic listener is subscribed using the {@link #registerNotificationListener(Class, NotificationListener)}
+ * method by which you specify the type of notification to receive. A generic listener may be registered for
+ * multiple notification types via multiple subscriptions.
+ * <p>
+ * Generic listeners allow for a more flexible approach, allowing you to subscribe for just
+ * one type of notification from a YANG model. You could also have a general subscription
+ * for all notification in the system via
+ * <pre>
+ *   service.registerNotificationListener(Notification.class, listener);
+ * </pre>
+ *
+ * <h3>Dispatch Listener</h3>
+ * <p>
+ * A dispatch listener implements a YANG-generated module interface <code>{ModuleName}Listener</code>
+ * which handles all the notifications defined in the YANG model. Each notification type translates to
+ * a specific method of the form <code>on{NotificationType}</code> on the generated interface.
+ * The generated interface also extends the
+ * {@link org.opendaylight.yangtools.yang.binding.NotificationListener} interface and implementations
+ * are registered using {@link #registerNotificationListener(org.opendaylight.yangtools.yang.binding.NotificationListener)}
+ * method.
+ *
+ * <h5>Dispatch Listener Example</h5>
+ * <p>
+ * Lets assume we have following YANG model:
+ *
+ * <pre>
+ * module example {
+ *      ...
+ *
+ *      notification start {
+ *          ...
+ *      }
+ *
+ *      notification stop {
+ *           ...
+ *      }
+ * }
+ * </pre>
+ *
+ * The generated interface will be:
+ * <pre>
+ *  public interface ExampleListener extends NotificationListener {
+ *      void onStart(Start notification);
+ *      void onStop(Stop notification);
+ *  }
+ * </pre>
+ * The following defines an implementation of the generated interface:
+ * <pre>
+ *  public class MyExampleListener implements ExampleListener {
+ *      public void onStart(Start notification) {
+ *          // do something
+ *      }
+ *
+ *      public void onStop(Stop notification) {
+ *          // do something
+ *      }
+ *  }
+ * </pre>
+ * The implementation is registered as follows:
+ * <pre>
+ *  MyExampleListener listener = new MyExampleListener();
+ *  ListenerRegistration<NotificationListener> reg = service.registerNotificationListener( listener );
+ * </pre>
+ * The <code>onStart</code> method will be invoked when someone publishes a <code>Start</code> notification and
+ * the <code>onStop</code> method will be invoked when someone publishes a <code>Stop</code> notification.
+ *
+ * @deprecated Please use {@link org.opendaylight.controller.md.sal.binding.api.NotificationService} instead.
+ */
+@Deprecated
+public interface NotificationService extends BindingAwareService {
+    /**
+     * Registers a generic listener implementation for a specified notification type.
+     *
+     * @param notificationType the YANG-generated interface of the notification type.
+     * @param listener the listener implementation that will receive notifications.
+     * @return a {@link ListenerRegistration} instance that should be used to unregister the listener
+     *         by invoking the {@link ListenerRegistration#close()} method when no longer needed.
+     */
+    <T extends Notification> ListenerRegistration<NotificationListener<T>> registerNotificationListener(
+            Class<T> notificationType, NotificationListener<T> listener);
+
+    /**
+     * Registers a listener which implements a YANG-generated notification interface derived from
+     * {@link org.opendaylight.yangtools.yang.binding.NotificationListener}.
+     * The listener is registered for all notifications present in the implemented interface.
+     *
+     * @param listener the listener implementation that will receive notifications.
+     * @return a {@link ListenerRegistration} instance that should be used to unregister the listener
+     *         by invoking the {@link ListenerRegistration#close()} method when no longer needed.
+     */
+    ListenerRegistration<org.opendaylight.yangtools.yang.binding.NotificationListener> registerNotificationListener(
+            org.opendaylight.yangtools.yang.binding.NotificationListener listener);
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/RpcAvailabilityListener.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/RpcAvailabilityListener.java
new file mode 100644 (file)
index 0000000..3cf6e8d
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+public interface RpcAvailabilityListener {
+
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/RpcConsumerRegistry.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/RpcConsumerRegistry.java
new file mode 100644 (file)
index 0000000..0c5e764
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.binding.api.BindingService;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+/**
+ * Provides access to registered Remote Procedure Call (RPC) service implementations. The RPCs are
+ * defined in YANG models.
+ * <p>
+ * RPC implementations are registered using the {@link RpcProviderRegistry}.
+ *
+ */
+public interface RpcConsumerRegistry extends BindingAwareService, BindingService {
+    /**
+     * Returns an implementation of a requested RPC service.
+     *
+     * <p>
+     * The returned instance is not an actual implementation of the RPC service
+     * interface, but a proxy implementation of the interface that forwards to
+     * an actual implementation, if any.
+     * <p>
+     *
+     * The following describes the behavior of the proxy when invoking RPC methods:
+     * <ul>
+     * <li>If an actual implementation is registered with the MD-SAL, all invocations are
+     * forwarded to the registered implementation.</li>
+     * <li>If no actual implementation is registered, all invocations will fail by
+     * throwing {@link IllegalStateException}.</li>
+     * <li>Prior to invoking the actual implementation, the method arguments are are validated.
+     * If any are invalid, an {@link IllegalArgumentException} is thrown.
+     * </ul>
+     *
+     * The returned proxy is automatically updated with the most recent
+     * registered implementation.
+     * <p>
+     * The generated RPC method APIs require implementors to return a {@link java.util.concurrent.Future Future}
+     * instance that wraps the {@link org.opendaylight.yangtools.yang.common.RpcResult RpcResult}. Since
+     * RPC methods may be implemented asynchronously, callers should avoid blocking on the
+     * {@link java.util.concurrent.Future Future} result. Instead, it is recommended to use
+     * {@link com.google.common.util.concurrent.JdkFutureAdapters#listenInPoolThread(java.util.concurrent.Future)}
+     * or {@link com.google.common.util.concurrent.JdkFutureAdapters#listenInPoolThread(java.util.concurrent.Future, java.util.concurrent.Executor)}
+     * to listen for Rpc Result. This will asynchronously listen for future result in executor and
+     * will not block current thread.
+     *
+     * <pre>
+     *   final Future<RpcResult<SomeRpcOutput>> future = someRpcService.someRpc( ... );
+     *   Futures.addCallback(JdkFutureAdapters.listenInThreadPool(future), new FutureCallback<RpcResult<SomeRpcOutput>>() {
+     *
+     *       public void onSuccess(RpcResult<SomeRpcOutput> result) {
+     *          // process result ...
+     *       }
+     *
+     *       public void onFailure(Throwable t) {
+     *          // RPC failed
+     *       }
+     *   );
+     * </pre>
+     * @param serviceInterface the interface of the RPC Service. Typically this is an interface generated
+     *                         from a YANG model.
+     * @return the proxy for the requested RPC service. This method never returns null.
+     */
+    <T extends RpcService> T getRpcService(Class<T> serviceInterface);
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/RpcProviderRegistry.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/RpcProviderRegistry.java
new file mode 100644 (file)
index 0000000..22db985
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangePublisher;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
+import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+/**
+ * Provides a registry for Remote Procedure Call (RPC) service implementations. The RPCs are
+ * defined in YANG models.
+ * <p>
+ * There are 2 types of RPCs:
+ * <ul>
+ * <li>Global</li>
+ * <li>Routed</li>
+ * </ul>
+ *
+ * <h2>Global RPC</h2>
+ * <p>
+ * An RPC is global if there is intended to be only 1 registered implementation. A global RPC is not
+ * explicitly declared as such, essentially any RPC that is not defined to be routed is considered global.
+ * <p>
+ * Global RPCs are registered using the
+ * {@link #addRpcImplementation(Class, RpcService)} method.
+ *
+ * <h2>Routed RPC</h2>
+ * <p>
+ * MD-SAL supports routing of RPC between multiple implementations where the appropriate
+ * implementation is selected at run time based on the content of the RPC message as described in
+ * YANG model.
+ * <p>
+ * RPC routing is based on:
+ * <ul>
+ * <li><b>Route identifier</b> -
+ * An {@link org.opendaylight.yangtools.yang.binding.InstanceIdentifier InstanceIdentifier} value
+ * which is part of the RPC input. This value is used to select the correct
+ * implementation at run time.</li>
+ * <li><b>Context Type</b> - A YANG-defined construct which constrains the subset of
+ * valid route identifiers for a particular RPC.</li>
+ * </ul>
+ *
+ * <h3>Context type</h3>
+ * <p>
+ * A context type is modeled in YANG using a combination of a YANG <code>identity</code>
+ * and Opendaylight specific extensions from <code>yang-ext</code> module. These extensions are:
+ * <ul>
+ * <li><b>context-instance</b> - This is used in the data tree part of a YANG model to
+ * define a context type that associates nodes with a specified context <code>identity</code>.
+ * Instance identifiers that reference these nodes are valid route identifiers for RPCs that
+ * reference this context type.</li>
+ * <li><b>context-reference</b> - This is used in RPC input to mark a leaf of type
+ * <code>instance-identifier</code> as a reference to the particular context type defined by the
+ * specified context <code>identity</code>. The value of this
+ * leaf is used by the RPC broker at run time to route the RPC request to the correct implementation.
+ * Note that <code>context-reference</code> may only be used on leaf elements of type
+ * <code>instance-identifier</code> or a type derived from <code>instance-identifier</code>.</li>
+ * </ul>
+ *
+ *
+ * <h3>Routed RPC example</h3>
+ * <p>
+ * <h5>1. Defining a Context Type</h5>
+ * <p>
+ * The following snippet declares a simple YANG <code>identity</code> named <code>example-context</code>:
+ *
+ * <pre>
+ * module example {
+ *     ...
+ *     identity example-context {
+ *          description "Identity used to define an example-context type";
+ *     }
+ *     ...
+ * }
+ * </pre>
+ * <p>
+ * We then use the declared identity to define a context type by using it in combination
+ * with the <code>context-instance</code> YANG extension. We'll associate the context type
+ * with a list element in the data tree. This defines the set of nodes whose instance
+ * identifiers are valid for the <code>example-context</code> context type.
+ * <p>
+ * The following YANG snippet imports the <code>yang-ext</code> module and defines the list
+ * element named <code>item</code> inside a container named <code>foo</code>:
+ *
+ * <pre>
+ * module foo {
+ *     ...
+ *     import yang-ext {prefix ext;}
+ *     ...
+ *     container foo {
+ *          list item {
+ *              key "id";
+ *              leaf id {type string;}
+ *              ext:context-instance "example-context";
+ *          }
+ *     }
+ *     ...
+ * }
+ * </pre>
+ * <p>
+ * The statement <code>ext:context-instance "example-context";</code> inside the list element
+ * declares that any instance identifier referencing <code>item</code> in the data
+ * tree is valid for <code>example-context</code>. For example, the following instance
+ * identifier:
+ * <pre>
+ *     InstanceIdentifier.create(Foo.class).child(Item.class,new ItemKey("Foo"))
+ * </pre>
+ * is valid for <code>example-context</code>. However the following:
+ * <pre>
+ *     InstanceIdentifier.create(Example.class)
+ * </pre>
+ * is not valid.
+ * <p>
+ * So using an <code>identity</code> in combination with <code>context-instance</code> we
+ * have effectively defined a context type that can be referenced in a YANG RPC input.
+ *
+ * <h5>2. Defining an RPC to use the Context Type</h5>
+ * <p>
+ * To define an RPC to be routed based on the context type we need to add an input leaf element
+ * that references the context type which will hold an instance identifier value to be
+ * used to route the RPC.
+ * <p>
+ * The following snippet defines an RPC named <code>show-item</code> with 2 leaf elements
+ * as input: <code>item</code> of type <code>instance-identifier</code> and <code>description</code>:
+ *
+ * <pre>
+ * module foo {
+ *      ...
+ *      import yang-ext {prefix ext;}
+ *      ...
+ *      rpc show-item {
+ *          input {
+ *              leaf item {
+ *                  type instance-identifier;
+ *                  ext:context-reference example-context;
+ *              }
+ *              leaf description {
+ *                  type "string";
+ *              }
+ *          }
+ *      }
+ * }
+ * </pre>
+ * <p>
+ * We mark the <code>item</code> leaf with a <code>context-reference</code> statement that
+ * references the <code>example-context</code> context type. RPC calls will then be routed
+ * based on the instance identifier value contained in <code>item</code>. Only instance
+ * identifiers that point to a <code>foo/item</code> node are valid as input.
+ * <p>
+ * The generated RPC Service interface for the module is:
+ *
+ * <pre>
+ * interface FooService implements RpcService {
+ *      Future&lt;RpcResult&lt;Void&gt;&gt; showItem(ShowItemInput input);
+ * }
+ * </pre>
+ * <p>
+ * For constructing the RPC input, there are generated classes ShowItemInput and ShowItemInputBuilder.
+ *
+ * <h5>3. Registering a routed RPC implementation</h5>
+ * <p>
+ * To register a routed implementation for the <code>show-item</code> RPC, we must use the
+ * {@link #addRoutedRpcImplementation(Class, RpcService)} method. This
+ * will return a {@link RoutedRpcRegistration} instance which can then be used to register /
+ * unregister routed paths associated with the registered implementation.
+ * <p>
+ * The following snippet registers <code>myImpl</code> as the RPC implementation for an
+ * <code>item</code> with key <code>"foo"</code>:
+ * <pre>
+ * // Create the instance identifier path for item "foo"
+ * InstanceIdentifier path = InstanceIdentifier.create(Foo.class).child(Item.class, new ItemKey(&quot;foo&quot;));
+ *
+ * // Register myImpl as the implementation for the FooService RPC interface
+ * RoutedRpcRegistration reg = rpcRegistry.addRoutedRpcImplementation(FooService.class, myImpl);
+ *
+ * // Now register for the context type and specific path ID. The context type is specified by the
+ * // YANG-generated class for the example-context identity.
+ * reg.registerPath(ExampleContext.class, path);
+ * </pre>
+ * <p>
+ * It is also possible to register the same implementation for multiple paths:
+ *
+ * <pre>
+ * InstanceIdentifier one = InstanceIdentifier.create(Foo.class).child(Item.class, new ItemKey(&quot;One&quot;));
+ * InstanceIdentifier two = InstanceIdentifier.create(Foo.class).child(Item.class, new ItemKey(&quot;Two&quot;));
+ *
+ * RoutedRpcRegistration reg = rpcRegistry.addRoutedRpcImplementation(FooService.class, myImpl);
+ * reg.registerPath(ExampleContext.class, one);
+ * reg.registerPath(ExampleContext.class, two);
+ * </pre>
+ *
+ * <p>
+ * When another client invokes the <code>showItem(ShowItemInput)</code> method on the proxy instance
+ * retrieved via {@link RpcConsumerRegistry#getRpcService(Class)}, the proxy will inspect the
+ * arguments in ShowItemInput, extract the InstanceIdentifier value of the <code>item</code> leaf and select
+ * the implementation whose registered path matches the InstanceIdentifier value of the <code>item</code> leaf.
+ *
+ * <h2>Notes for RPC Implementations</h2>
+ *
+ * <h3>RpcResult</h3>
+ * <p>
+ * The generated interfaces require implementors to return
+ *  {@link java.util.concurrent.Future Future}&lt;{@link org.opendaylight.yangtools.yang.common.RpcResult RpcResult}&lt;{RpcName}Output&gt;&gt; instances.
+ *
+ * Implementations should do processing of RPC calls asynchronously and update the
+ * returned {@link java.util.concurrent.Future Future} instance when processing is complete.
+ * However using {@link com.google.common.util.concurrent.Futures#immediateFuture(Object) Futures.immediateFuture}
+ * is valid only if the result is immediately available and asynchronous processing is unnecessary and
+ * would only introduce additional complexity.
+ *
+ * <p>
+ * The {@link org.opendaylight.yangtools.yang.common.RpcResult RpcResult} is a generic
+ * wrapper for the RPC output payload, if any, and also allows for attaching error or
+ * warning information (possibly along with the payload) should the RPC processing partially
+ * or completely fail. This is intended to provide additional human readable information
+ * for users of the API and to transfer warning / error information across the system
+ * so it may be visible via other external APIs such as Restconf.
+ * <p>
+ * It is recommended to use the {@link org.opendaylight.yangtools.yang.common.RpcResult RpcResult}
+ * for conveying appropriate error information
+ * on failure rather than purposely throwing unchecked exceptions if at all possible.
+ * While unchecked exceptions will fail the returned {@link java.util.concurrent.Future Future},
+ * using the intended RpcResult to convey the error information is more user-friendly.
+ */
+public interface RpcProviderRegistry extends //
+        RpcConsumerRegistry, //
+        RouteChangePublisher<RpcContextIdentifier, InstanceIdentifier<?>> {
+    /**
+     * Registers a global implementation of the provided RPC service interface.
+     * All methods of the interface are required to be implemented.
+     *
+     * @param serviceInterface the YANG-generated interface of the RPC Service for which to register.
+     * @param implementation "the implementation of the RPC service interface.
+     * @return an RpcRegistration instance that should be used to unregister the RPC implementation
+     *         when no longer needed by calling {@link RpcRegistration#close()}.
+     *
+     * @throws IllegalStateException
+     *             if the supplied RPC interface is a routed RPC type.
+     */
+    <T extends RpcService> RpcRegistration<T> addRpcImplementation(Class<T> serviceInterface, T implementation)
+            throws IllegalStateException;
+
+    /**
+     * Registers an implementation of the given routed RPC service interface.
+     * <p>
+     * See the {@link RpcProviderRegistry class} documentation for information and example on
+     * how to use routed RPCs.
+     *
+     * @param serviceInterface the YANG-generated interface of the RPC Service for which to register.
+     * @param implementation the implementation instance to register.
+     * @return a RoutedRpcRegistration instance which can be used to register paths for the RPC
+     *         implementation via invoking {@link RoutedRpcRegistration#registerPath(....).
+     *         {@link RoutedRpcRegistration#close()} should be called to unregister the implementation
+     *         and all previously registered paths when no longer needed.
+     *
+     * @throws IllegalStateException
+     *            if the supplied RPC interface is not a routed RPC type.
+     */
+    <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(Class<T> serviceInterface,
+                                                                               T implementation)
+            throws IllegalStateException;
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/data/DataBrokerService.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/data/DataBrokerService.java
new file mode 100644 (file)
index 0000000..d30ca6b
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.data;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataChangePublisher;
+import org.opendaylight.controller.md.sal.common.api.data.DataModificationTransactionFactory;
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.controller.sal.binding.api.BindingAwareService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * DataBrokerService provides unified access to the data stores available in the
+ * system.
+ *
+ *
+ * @see DataProviderService
+ * @deprecated Replaced by newer better documented version {@link org.opendaylight.controller.md.sal.binding.api.DataBroker}
+ */
+@Deprecated
+public interface DataBrokerService extends //
+        BindingAwareService, //
+        DataModificationTransactionFactory<InstanceIdentifier<? extends DataObject>, DataObject>, //
+        DataReader<InstanceIdentifier<? extends DataObject>, DataObject>, //
+        DataChangePublisher<InstanceIdentifier<? extends DataObject>, DataObject, DataChangeListener> {
+    /**
+     * Creates a data modification transaction.
+     *
+     * @return new blank data modification transaction.
+     * @deprecated Replaced by more specific transaction types. Please use
+     *          {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadOnlyTransaction(),
+     *          {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadWriteTransaction()
+     *          or
+     *          {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newWriteOnlyTransaction().
+     */
+    @Deprecated
+    @Override
+    DataModificationTransaction beginTransaction();
+
+    /**
+     * Reads data subtree from configurational store.
+     * (Store which is populated by consumer, which is usually used to
+     * inject state into providers. E.g. Flow configuration)
+     *
+     *
+     * @deprecated Please use {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadOnlyTransaction()}
+     *
+     */
+    @Deprecated
+    @Override
+    public DataObject readConfigurationData(InstanceIdentifier<? extends DataObject> path);
+
+    /**
+     * Reads data subtree from operational store.
+     * (Store which is populated by providers, which is usually used to
+     * capture state of providers. E.g. Topology)
+     *
+     * @deprecated Please use {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadOnlyTransaction()}
+     */
+    @Deprecated
+    @Override
+    public DataObject readOperationalData(InstanceIdentifier<? extends DataObject> path);
+
+    /**
+     * Register a data change listener for particular subtree.
+     *
+     * Callback is invoked each time data in subtree changes.
+     *
+     * @deprecated Please use {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#registerDataChangeListener(org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType, InstanceIdentifier, org.opendaylight.controller.md.sal.binding.api.DataChangeListener, org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope)}
+     * which provides more fine-grained registration options.
+     */
+    @Deprecated
+    @Override
+    public ListenerRegistration<DataChangeListener> registerDataChangeListener(
+            InstanceIdentifier<? extends DataObject> path, DataChangeListener listener);
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/data/DataChangeListener.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/data/DataChangeListener.java
new file mode 100644 (file)
index 0000000..e26cbce
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.data;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ *
+ * @deprecated Replaced by {@link org.opendaylight.controller.md.sal.binding.api.DataChangeListener}
+ *
+ */
+@Deprecated
+public interface DataChangeListener extends
+        org.opendaylight.controller.md.sal.common.api.data.DataChangeListener<InstanceIdentifier<? extends DataObject>, DataObject> {
+
+    @Override
+    public void onDataChanged(DataChangeEvent<InstanceIdentifier<?>, DataObject> change);
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/data/DataModificationTransaction.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/data/DataModificationTransaction.java
new file mode 100644 (file)
index 0000000..0c250fd
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.data;
+
+import java.util.EventListener;
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.DataModification;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+/**
+ *
+ *
+ * @deprecated Replaced by more specific transaction types. Please use
+ *          {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadOnlyTransaction(),
+ *          {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadWriteTransaction()
+ *          or
+ *          {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newWriteOnlyTransaction().
+ *
+ *
+ */
+@Deprecated
+public interface DataModificationTransaction extends DataModification<InstanceIdentifier<? extends DataObject>, DataObject> {
+    /**
+     * Returns an unique identifier for transaction
+     *
+     */
+    @Override
+    Object getIdentifier();
+
+    /**
+     * Initiates a two-phase commit of candidate data.
+     *
+     * <p>
+     * The {@link Consumer} could initiate a commit of candidate data
+     *
+     * <p>
+     * The successful commit changes the state of the system and may affect
+     * several components.
+     *
+     * <p>
+     * The effects of successful commit of data are described in the
+     * specifications and YANG models describing the {@link Provider} components
+     * of controller. It is assumed that {@link Consumer} has an understanding
+     * of this changes.
+     *
+     *
+     * @see org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler for further information how two-phase commit is
+     *      processed.
+     * @param store
+     *            Identifier of the store, where commit should occur.
+     * @return Result of the commit, containing success information or list of
+     *         encountered errors, if commit was not successful.
+     */
+    @Override
+    Future<RpcResult<TransactionStatus>> commit();
+
+    /**
+     * Register a listener for transaction
+     *
+     * @param listener
+     * @return
+     */
+    ListenerRegistration<DataTransactionListener> registerListener(DataTransactionListener listener);
+
+    /**
+     * Listener for transaction state changes
+     */
+    public interface DataTransactionListener extends EventListener {
+        /**
+         * Callback is invoked after each transaction status change.
+         *
+         * @param transaction Transaction
+         * @param status New status
+         */
+        void onStatusUpdated(DataModificationTransaction transaction,TransactionStatus status);
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/data/DataProviderService.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/data/DataProviderService.java
new file mode 100644 (file)
index 0000000..7fa40b8
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.data;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataProvisionService;
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * DataProviderService is common access point for {@link org.opendaylight.controller.sal.binding.api.BindingAwareProvider} providers
+ * to access data trees described by the YANG model.
+ *
+ * @deprecated Replaced by {@link org.opendaylight.controller.md.sal.common.api.data.AsyncConfigurationCommitCoordinator} service.
+ */
+@Deprecated
+public interface DataProviderService extends DataBrokerService, DataProvisionService<InstanceIdentifier<? extends DataObject>, DataObject> {
+    /**
+     * Registers a data reader for particular subtree of overal YANG data tree.
+     *
+     * Registered data reader is called if anyone tries to read data from
+     * paths which are nested to provided path.
+     *
+     * @param path Subpath which is handled by registered data reader
+     * @param reader Instance of reader which
+     * @return Registration object for reader. Invoking {@link Registration#close()} will unregister reader.
+     * @deprecated Data Reader contract is removed from capabilities of MD-SAL and is replaced by  replaced by org.opendaylight.controller.sal.core.spi.data.DOMStore contract.
+     */
+    @Deprecated
+    Registration registerDataReader(InstanceIdentifier<? extends DataObject> path,DataReader<InstanceIdentifier<? extends DataObject>,DataObject> reader);
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/data/SynchronizedTransaction.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/data/SynchronizedTransaction.java
new file mode 100644 (file)
index 0000000..fc0d396
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.data;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Synchronized wrapper for DataModificationTransaction.
+ *
+ * To get instance of synchronized wrapper use {@link #from(DataModificationTransaction)}
+ *
+ * @deprecated Replaced by more specific transaction types. Please use
+ *          {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadOnlyTransaction(),
+ *          {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadWriteTransaction()
+ *          or
+ *          {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newWriteOnlyTransaction().
+ *
+ */
+@Deprecated
+public final class SynchronizedTransaction implements DataModificationTransaction,Delegator<DataModificationTransaction> {
+
+    private final DataModificationTransaction delegate;
+
+    private SynchronizedTransaction(final DataModificationTransaction delegate) {
+        this.delegate = delegate;
+    }
+
+    /**
+     * Returns synchronized wrapper on supplied transaction.
+     *
+     * @param transaction Transaction for which synchronized wrapper should be created.
+     * @return Synchronized wrapper over transaction.
+     */
+    public static final SynchronizedTransaction from(final DataModificationTransaction transaction) {
+        Preconditions.checkArgument(transaction != null, "Transaction must not be null.");
+        if (transaction instanceof SynchronizedTransaction) {
+            return (SynchronizedTransaction) transaction;
+        }
+        return new SynchronizedTransaction(transaction);
+    }
+
+    @Override
+    public synchronized Map<InstanceIdentifier<? extends DataObject>, DataObject> getCreatedOperationalData() {
+        return delegate.getCreatedOperationalData();
+    }
+
+    @Override
+    public synchronized Map<InstanceIdentifier<? extends DataObject>, DataObject> getCreatedConfigurationData() {
+        return delegate.getCreatedConfigurationData();
+    }
+
+    @Override
+    public synchronized DataObject readOperationalData(final InstanceIdentifier<? extends DataObject> path) {
+        return delegate.readOperationalData(path);
+    }
+
+    @Override
+    public synchronized TransactionStatus getStatus() {
+        return delegate.getStatus();
+    }
+
+    @Override
+    public synchronized Map<InstanceIdentifier<? extends DataObject>, DataObject> getUpdatedOperationalData() {
+        return delegate.getUpdatedOperationalData();
+    }
+
+    @Override
+    public synchronized Object getIdentifier() {
+        return delegate.getIdentifier();
+    }
+
+    @Override
+    public synchronized DataObject readConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
+        return delegate.readConfigurationData(path);
+    }
+
+    @Override
+    public synchronized Future<RpcResult<TransactionStatus>> commit() {
+        return delegate.commit();
+    }
+
+    @Override
+    public synchronized void putOperationalData(final InstanceIdentifier<? extends DataObject> path, final DataObject data) {
+        delegate.putOperationalData(path, data);
+    }
+
+    @Override
+    public synchronized void putConfigurationData(final InstanceIdentifier<? extends DataObject> path, final DataObject data) {
+        delegate.putConfigurationData(path, data);
+    }
+
+    @Override
+    public synchronized Map<InstanceIdentifier<? extends DataObject>, DataObject> getUpdatedConfigurationData() {
+        return delegate.getUpdatedConfigurationData();
+    }
+
+    @Override
+    public synchronized void removeOperationalData(final InstanceIdentifier<? extends DataObject> path) {
+        delegate.removeOperationalData(path);
+    }
+
+    @Override
+    public synchronized void removeConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
+        delegate.removeConfigurationData(path);
+    }
+
+    @Override
+    public synchronized Set<InstanceIdentifier<? extends DataObject>> getRemovedConfigurationData() {
+        return delegate.getRemovedConfigurationData();
+    }
+
+    @Override
+    public synchronized Set<InstanceIdentifier<? extends DataObject>> getRemovedOperationalData() {
+        return delegate.getRemovedOperationalData();
+    }
+
+    @Override
+    public synchronized Map<InstanceIdentifier<? extends DataObject>, DataObject> getOriginalConfigurationData() {
+        return delegate.getOriginalConfigurationData();
+    }
+
+    @Override
+    public synchronized ListenerRegistration<DataTransactionListener> registerListener(final DataTransactionListener listener) {
+        return delegate.registerListener(listener);
+    }
+
+    @Override
+    public synchronized Map<InstanceIdentifier<? extends DataObject>, DataObject> getOriginalOperationalData() {
+        return delegate.getOriginalOperationalData();
+    }
+
+    @Override
+    public synchronized DataModificationTransaction getDelegate() {
+        return delegate;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((delegate == null) ? 0 : delegate.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()){
+            return false;
+        }
+        SynchronizedTransaction other = (SynchronizedTransaction) obj;
+        if (delegate == null) {
+            if (other.delegate != null) {
+                return false;
+            }
+        } else if (!delegate.equals(other.delegate)) {
+            return false;
+        }
+        return true;
+    }
+}
+
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/mount/MountInstance.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/mount/MountInstance.java
new file mode 100644 (file)
index 0000000..b444160
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.mount;
+
+import org.opendaylight.controller.sal.binding.api.NotificationService;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface MountInstance //
+        extends //
+        RpcConsumerRegistry, //
+        Identifiable<InstanceIdentifier<?>>, //
+        NotificationService, //
+        DataBrokerService {
+
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/mount/MountProviderInstance.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/mount/MountProviderInstance.java
new file mode 100644 (file)
index 0000000..0d9a902
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.mount;
+
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+
+/**
+ * Provider's version of Mount Point, this version allows access to MD-SAL
+ * services specific for this mountpoint and registration / provision of
+ * interfaces for mount point.
+ *
+ * @author ttkacik
+ *
+ */
+public interface MountProviderInstance //
+        extends //
+        MountInstance, //
+        DataProviderService, //
+        RpcProviderRegistry, //
+        NotificationProviderService {
+
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/mount/MountProviderService.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/mount/MountProviderService.java
new file mode 100644 (file)
index 0000000..6af9093
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.mount;
+
+import java.util.EventListener;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Provider MountProviderService, this version allows access to MD-SAL services
+ * specific for this mountpoint and registration / provision of interfaces for
+ * mount point.
+ *
+ * @author ttkacik
+ *
+ */
+public interface MountProviderService extends MountService {
+
+    @Override
+    public MountProviderInstance getMountPoint(InstanceIdentifier<?> path);
+
+    MountProviderInstance createMountPoint(InstanceIdentifier<?> path);
+
+    MountProviderInstance createOrGetMountPoint(InstanceIdentifier<?> path);
+
+    ListenerRegistration<MountProvisionListener> registerProvisionListener(MountProvisionListener listener);
+
+    public interface MountProvisionListener extends EventListener {
+
+        void onMountPointCreated(InstanceIdentifier<?> path);
+
+        void onMountPointRemoved(InstanceIdentifier<?> path);
+
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/mount/MountService.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/mount/MountService.java
new file mode 100644 (file)
index 0000000..1644c40
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.mount;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareService;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface MountService extends BindingAwareService {
+
+    MountInstance getMountPoint(InstanceIdentifier<?> path);
+
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/package-info.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/package-info.java
new file mode 100644 (file)
index 0000000..31eec6b
--- /dev/null
@@ -0,0 +1,9 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/rpc/RpcContextIdentifier.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/rpc/RpcContextIdentifier.java
new file mode 100644 (file)
index 0000000..e3bedd3
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.rpc;
+
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+public final  class RpcContextIdentifier implements Immutable{
+
+    public final Class<? extends RpcService> rpcService;
+    public final Class<? extends BaseIdentity> routingContext;
+
+    private RpcContextIdentifier(Class<? extends RpcService> rpcService, Class<? extends BaseIdentity> routingContext) {
+        super();
+        this.rpcService = rpcService;
+        this.routingContext = routingContext;
+    }
+
+    public Class<? extends RpcService> getRpcService() {
+        return rpcService;
+    }
+
+    public Class<? extends BaseIdentity> getRoutingContext() {
+        return routingContext;
+    }
+
+    public static final RpcContextIdentifier contextForGlobalRpc(Class<? extends RpcService> serviceType) {
+        return new RpcContextIdentifier(serviceType, null);
+    }
+
+    public static final RpcContextIdentifier contextFor(Class<? extends RpcService> serviceType,Class<? extends BaseIdentity> routingContext) {
+        return new RpcContextIdentifier(serviceType, routingContext);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((routingContext == null) ? 0 : routingContext.hashCode());
+        result = prime * result + ((rpcService == null) ? 0 : rpcService.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        RpcContextIdentifier other = (RpcContextIdentifier) obj;
+        if (routingContext == null) {
+            if (other.routingContext != null)
+                return false;
+        } else if (!routingContext.equals(other.routingContext))
+            return false;
+        if (rpcService == null) {
+            if (other.rpcService != null)
+                return false;
+        } else if (!rpcService.equals(other.rpcService))
+            return false;
+        return true;
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/rpc/RpcRouter.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/rpc/RpcRouter.java
new file mode 100644 (file)
index 0000000..0042998
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.rpc;
+
+import java.util.Set;
+
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangePublisher;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+/**
+ * RpcRouter is responsible for selecting RpcService based on provided routing
+ * context identifier {@link RpcRoutingTable#getContextIdentifier()} and path in
+ * overall data tree (@link {@link InstanceIdentifier}.
+ *
+ *
+ * @author Tony Tkacik <ttkacik@cisco.com>
+ *
+ * @param <T>
+ *            Type of RpcService for which router provides routing information
+ *            and route selection.
+ */
+public interface RpcRouter<T extends RpcService> extends //
+        RouteChangePublisher<Class<? extends BaseIdentity>, InstanceIdentifier<?>> {
+
+    /**
+     * Returns a type of RpcService which is served by this instance of router.
+     *
+     * @return type of RpcService which is served by this instance of router.
+     */
+    Class<T> getServiceType();
+
+
+    /**
+     * Returns a instance of T which is associated with this router instance
+     * and routes messages based on routing tables.
+     *
+     * @return type of RpcService which is served by this instance of router.
+     */
+    T getInvocationProxy();
+
+    /**
+     * Returns a routing table for particular route context
+     *
+     * @param routeContext
+     * @return Routing Table for particular route context.
+     */
+    <C extends BaseIdentity> RpcRoutingTable<C, T> getRoutingTable(Class<C> routeContext);
+
+    /**
+     * Returns an instance of RpcService which is responsible for processing
+     * particular path.
+     *
+     * @param context
+     *            Rpc Routing Context
+     * @param path
+     *            Instance Identifier which is used as a selector of instance.
+     * @return instance of RpcService which is responsible for processing
+     *         particular path.
+     */
+    T getService(Class<? extends BaseIdentity> context, InstanceIdentifier<?> path);
+
+    /**
+     * Returns a default fallback instance of RpcService which is responsible
+     * for handling all unknown imports.
+     *
+     * @return default instance responsible for processing RPCs.
+     */
+    T getDefaultService();
+
+    Set<Class<? extends BaseIdentity>> getContexts();
+
+    RoutedRpcRegistration<T> addRoutedRpcImplementation(T service);
+
+    RpcRegistration<T> registerDefaultService(T service);
+
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/rpc/RpcRoutingContext.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/rpc/RpcRoutingContext.java
new file mode 100644 (file)
index 0000000..a886692
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.rpc;
+
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+public interface RpcRoutingContext<C extends BaseIdentity,S extends RpcService> {
+
+    Class<C> getContextType();
+    Class<S> getServiceType();
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/rpc/RpcRoutingTable.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/rpc/RpcRoutingTable.java
new file mode 100644 (file)
index 0000000..61c7cfc
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.rpc;
+
+import java.util.Map;
+
+import org.opendaylight.controller.md.sal.common.api.routing.MutableRoutingTable;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+public interface RpcRoutingTable<C extends BaseIdentity, S extends RpcService> extends
+        MutableRoutingTable<Class<? extends C>, InstanceIdentifier<?>, S> {
+
+    Class<C> getIdentifier();
+
+    /**
+     * Updates route for particular path to specified instance of
+     * {@link RpcService}.
+     *
+     * @param path
+     *            Path for which RpcService routing is to be updated
+     * @param service
+     *            Instance of RpcService which is responsible for processing Rpc
+     *            Requests.
+     */
+    void updateRoute(InstanceIdentifier<?> path, S service);
+
+    /**
+     * Deletes a route for particular path
+     *
+     * @param path
+     *            Path for which
+     */
+    void removeRoute(InstanceIdentifier<?> path);
+
+    /**
+     *
+     */
+    S getRoute(InstanceIdentifier<?> nodeInstance);
+
+    /**
+     *
+     * @return
+     */
+    Map<InstanceIdentifier<?>, S> getRoutes();
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/pom.xml b/opendaylight/md-sal/sal-binding-broker/pom.xml
new file mode 100644 (file)
index 0000000..7090781
--- /dev/null
@@ -0,0 +1,204 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>sal-parent</artifactId>
+    <version>1.3.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>sal-binding-broker-impl</artifactId>
+  <packaging>bundle</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.javassist</groupId>
+      <artifactId>javassist</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-binding-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-binding-util</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-broker-impl</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-common-impl</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-common-util</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-core-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>binding-generator-impl</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>binding-data-codec</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-data-impl</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-model-util</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools.model</groupId>
+      <artifactId>ietf-inet-types</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-all</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-parser-impl</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools.model</groupId>
+      <artifactId>ietf-topology-l3-unicast-igp</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-test-model</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-simple</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+            <Export-Package>
+                            org.opendaylight.controller.sal.binding.impl,
+                            org.opendaylight.controller.md.sal.binding.impl,
+                            org.opendaylight.controller.md.sal.binding.compat,
+                            org.opendaylight.controller.md.sal.binding.spi,
+                            org.opendaylight.controller.sal.binding.codegen.impl,
+            </Export-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+        <configuration>
+          <includes>
+            <include>org.opendaylight.controller.*</include>
+          </includes>
+        </configuration>
+        <executions>
+          <execution>
+            <id>pre-test</id>
+            <goals>
+              <goal>prepare-agent</goal>
+            </goals>
+          </execution>
+          <execution>
+            <id>post-test</id>
+            <goals>
+              <goal>report</goal>
+            </goals>
+            <phase>test</phase>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.opendaylight.yangtools</groupId>
+        <artifactId>yang-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>generate-sources</goal>
+            </goals>
+            <configuration>
+              <codeGenerators>
+                <generator>
+                  <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
+                  <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+                  <additionalConfiguration>
+                    <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
+                  </additionalConfiguration>
+                </generator>
+
+                <generator>
+                  <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+                  <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+                </generator>
+
+                <generator>
+                  <codeGeneratorClass>org.opendaylight.yangtools.yang.unified.doc.generator.maven.DocumentationGeneratorImpl</codeGeneratorClass>
+                  <outputBaseDir>target/site/models</outputBaseDir>
+                </generator>
+              </codeGenerators>
+              <inspectDependencies>true</inspectDependencies>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+  <scm>
+    <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+    <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+    <tag>HEAD</tag>
+    <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+  </scm>
+</project>
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/.gitignore b/opendaylight/md-sal/sal-binding-broker/src/main/.gitignore
new file mode 100644 (file)
index 0000000..04b73cb
--- /dev/null
@@ -0,0 +1 @@
+/xtend-gen
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/AbstractNotificationListenerRegistration.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/AbstractNotificationListenerRegistration.java
new file mode 100644 (file)
index 0000000..540c261
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Abstract implementation of {@link NotificationListenerRegistration}.
+ *
+ * @param <T> Notification type
+ */
+abstract class AbstractNotificationListenerRegistration<T extends Notification> extends AbstractListenerRegistration<NotificationListener<T>> implements NotificationListenerRegistration<T> {
+    private final Class<? extends Notification> type;
+
+    protected AbstractNotificationListenerRegistration(final Class<? extends Notification> type, final NotificationListener<T> listener) {
+        super(listener);
+        this.type = Preconditions.checkNotNull(type);
+    }
+
+    @Override
+    public Class<? extends Notification> getType() {
+        return type;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void notify(final Notification notification) {
+        if (!isClosed()) {
+            getInstance().onNotification((T)notification);
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/AggregatedNotificationListenerRegistration.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/AggregatedNotificationListenerRegistration.java
new file mode 100644 (file)
index 0000000..e93109d
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+/**
+ * An aggregated listener registration. This is a result of registering an invoker which can handle multiple
+ * interfaces at the same time. In order to support correct delivery, we need to maintain per-type registrations
+ * which get squashed if a notification which implements multiple interfaces is encountered.
+ *
+ * We take care of that by implementing alternate {@link #hashCode()}/{@link #equals(Object)}, which resolve
+ * to the backing aggregator.
+ *
+ * @param <N> Notification type
+ * @param <A> Aggregator type
+ */
+abstract class AggregatedNotificationListenerRegistration<N extends Notification, A> extends AbstractNotificationListenerRegistration<N> {
+    private final A aggregator;
+
+    protected AggregatedNotificationListenerRegistration(final Class<? extends Notification> type, final NotificationListener<N> listener, final A aggregator) {
+        super(type, listener);
+        this.aggregator = Preconditions.checkNotNull(aggregator);
+    }
+
+    protected A getAggregator() {
+        return aggregator;
+    }
+
+    @Override
+    public int hashCode() {
+        return aggregator.hashCode();
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!this.getClass().equals(obj.getClass())) {
+            return false;
+        }
+
+        return aggregator.equals(((AggregatedNotificationListenerRegistration<?, ?>)obj).aggregator);
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/CompositeRoutedRpcRegistration.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/CompositeRoutedRpcRegistration.java
new file mode 100644 (file)
index 0000000..bb6c2e7
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableSet;
+import java.util.HashMap;
+import java.util.Map;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMRpcProviderServiceAdapter;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+final class CompositeRoutedRpcRegistration<T extends RpcService> implements RoutedRpcRegistration<T> {
+
+    private final Class<T> type;
+    private final T instance;
+    private final BindingDOMRpcProviderServiceAdapter adapter;
+    private final Map<InstanceIdentifier<?>, ObjectRegistration<T>> registrations = new HashMap<>(2);
+
+    CompositeRoutedRpcRegistration(final Class<T> type, final T impl, final BindingDOMRpcProviderServiceAdapter providerAdapter) {
+        this.type = type;
+        this.instance = impl;
+        this.adapter = providerAdapter;
+    }
+
+    @Override
+    public Class<T> getServiceType() {
+        return type;
+    }
+
+    @Override
+    public T getInstance() {
+        return instance;
+    }
+
+    @Deprecated
+    @Override
+    public void registerInstance(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> path) {
+        registerPath(context, path);
+    }
+
+    @Override
+    public synchronized void registerPath(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> path) {
+        if(!registrations.containsKey(path)) {
+            registrations.put(path, adapter.registerRpcImplementation(type, instance, ImmutableSet.<InstanceIdentifier<?>>of(path)));
+        }
+    }
+
+
+    @Override
+    @Deprecated
+    public void unregisterInstance(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> path) {
+        unregisterPath(context, path);
+    }
+
+    @Override
+    public synchronized  void unregisterPath(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> path) {
+        final ObjectRegistration<T> reg = registrations.remove(path);
+        if(reg != null) {
+            try {
+                reg.close();
+            } catch (final Exception e) {
+                // FIXME: Once we have proper subclass of ObjectRegistrationo
+                throw Throwables.propagate(e);
+            }
+        }
+    }
+
+    @Override
+    public synchronized void close() {
+        try {
+            for(final ObjectRegistration<T> reg : registrations.values()) {
+                    reg.close();
+            }
+        } catch (final Exception e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/DelegatedRootRpcRegistration.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/DelegatedRootRpcRegistration.java
new file mode 100644 (file)
index 0000000..1c3e6d8
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import com.google.common.base.Throwables;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+final class DelegatedRootRpcRegistration<T extends RpcService> implements RpcRegistration<T> {
+
+    private final ObjectRegistration<T> delegate;
+    private final Class<T> type;
+
+    public DelegatedRootRpcRegistration(final Class<T> type,final ObjectRegistration<T> impl) {
+        this.delegate = impl;
+        this.type = type;
+    }
+
+
+    @Override
+    public void close() {
+        try {
+            // FIXME: Should use more specific registration object.
+            delegate.close();
+        } catch (final Exception e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+    @Override
+    public T getInstance() {
+        return delegate.getInstance();
+    }
+
+    @Override
+    public Class<T> getServiceType() {
+        return type;
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/FunctionalNotificationListenerAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/FunctionalNotificationListenerAdapter.java
new file mode 100644 (file)
index 0000000..94c2197
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.binding.impl.LazySerializedDOMNotification;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+final class FunctionalNotificationListenerAdapter<N extends Notification> implements DOMNotificationListener {
+
+    private final BindingNormalizedNodeSerializer codec;
+    private final NotificationListener<N> delegate;
+    private final Class<N> type;
+
+    public FunctionalNotificationListenerAdapter(final BindingNormalizedNodeSerializer codec, final Class<N> type, final NotificationListener<N> delegate) {
+        this.codec = codec;
+        this.type = type;
+        this.delegate = delegate;
+    }
+
+    @Override
+    public void onNotification(@Nonnull final DOMNotification notification) {
+        delegate.onNotification( type.cast(deserialize(notification)));
+    }
+
+    private Notification deserialize(final DOMNotification notification) {
+        if(notification instanceof LazySerializedDOMNotification) {
+            return ((LazySerializedDOMNotification) notification).getBindingData();
+        }
+        return codec.fromNormalizedNodeNotification(notification.getType(), notification.getBody());
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HeliumNotificationProviderServiceAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HeliumNotificationProviderServiceAdapter.java
new file mode 100644 (file)
index 0000000..24376d6
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import java.util.concurrent.ExecutorService;
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
+import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class HeliumNotificationProviderServiceAdapter extends HeliumNotificationServiceAdapter implements NotificationProviderService, AutoCloseable {
+    private static final Logger LOG = LoggerFactory.getLogger(HeliumNotificationProviderServiceAdapter.class);
+
+    private final NotificationPublishService notificationPublishService;
+
+    public HeliumNotificationProviderServiceAdapter(NotificationPublishService notificationPublishService,
+                                                 NotificationService notificationService) {
+        super(notificationService);
+        this.notificationPublishService = notificationPublishService;
+    }
+
+    @Override
+    public void publish(final Notification notification) {
+        try {
+            notificationPublishService.putNotification(notification);
+        } catch (InterruptedException e) {
+            LOG.error("Notification publication was interupted: "  + e);
+        }
+    }
+
+    @Override
+    public void publish(final Notification notification, final ExecutorService executor) {
+        try {
+            notificationPublishService.putNotification(notification);
+        } catch (InterruptedException e) {
+            LOG.error("Notification publication was interupted: "  + e);
+        }
+    }
+
+    @Override
+    public ListenerRegistration<NotificationInterestListener> registerInterestListener(
+            NotificationInterestListener interestListener) {
+        throw new UnsupportedOperationException("InterestListener is not supported.");
+    }
+
+    @Override
+    public void close() throws Exception {
+
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HeliumNotificationProviderServiceWithInterestListeners.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HeliumNotificationProviderServiceWithInterestListeners.java
new file mode 100644 (file)
index 0000000..1eb421d
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMNotificationPublishServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMNotificationServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
+import org.opendaylight.controller.md.sal.dom.spi.DOMNotificationSubscriptionListener;
+import org.opendaylight.controller.md.sal.dom.spi.DOMNotificationSubscriptionListenerRegistry;
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class HeliumNotificationProviderServiceWithInterestListeners extends HeliumNotificationProviderServiceAdapter {
+
+    private static final Logger LOG = LoggerFactory.getLogger(HeliumNotificationProviderServiceWithInterestListeners.class);
+
+    private final ListenerRegistry<NotificationInterestListener> interestListeners = ListenerRegistry.create();
+    private final ListenerRegistration<Listener> domListener;
+    private final DOMNotificationService domService;
+    private final BindingToNormalizedNodeCodec codec;
+
+    public HeliumNotificationProviderServiceWithInterestListeners(
+            final BindingDOMNotificationPublishServiceAdapter publishService, final BindingDOMNotificationServiceAdapter listenService, final DOMNotificationSubscriptionListenerRegistry registry) {
+        super(publishService, listenService);
+        this.codec = publishService.getCodecRegistry();
+        this.domListener = registry.registerSubscriptionListener(new Listener());
+        this.domService = listenService.getDomService();
+    }
+
+    @Override
+    public ListenerRegistration<NotificationInterestListener> registerInterestListener(
+            final NotificationInterestListener listener) {
+        notifyListener(listener, translate(domListener.getInstance().getAllObserved()));
+        return interestListeners.register(listener);
+    }
+
+    private Set<Class<? extends Notification>> translate(final Set<SchemaPath> added) {
+        return codec.getNotificationClasses(added);
+    }
+
+    private void notifyAllListeners(final Set<SchemaPath> added) {
+        final Iterator<ListenerRegistration<NotificationInterestListener>> listeners = interestListeners.iterator();
+        if(listeners.hasNext()) {
+            final Set<Class<? extends Notification>> baEvent = translate(added);
+            while(listeners.hasNext()) {
+                final NotificationInterestListener listenerRef = listeners.next().getInstance();
+                try {
+                    notifyListener(listenerRef,baEvent);
+                } catch (final Exception e) {
+                    LOG.warn("Unhandled exception during invoking listener {}",e, listenerRef);
+                }
+            }
+        }
+    }
+
+    @Override
+    public <T extends Notification> ListenerRegistration<NotificationListener<T>> registerNotificationListener(
+            final Class<T> type, final NotificationListener<T> listener) {
+
+        final FunctionalNotificationListenerAdapter<T> adapter = new FunctionalNotificationListenerAdapter<>(codec, type, listener);
+        final SchemaPath domType = SchemaPath.create(true, BindingReflections.findQName(type));
+        final ListenerRegistration<?> domReg = domService.registerNotificationListener(adapter, domType);
+        return new AbstractListenerRegistration<NotificationListener<T>>(listener) {
+            @Override
+            protected void removeRegistration() {
+                domReg.close();
+            }
+
+        };
+    }
+
+    private void notifyListener(final NotificationInterestListener listener, final Set<Class<? extends Notification>> baEvent) {
+        for(final Class<? extends Notification> event: baEvent) {
+            listener.onNotificationSubscribtion(event);
+        }
+    }
+
+    private final class Listener implements DOMNotificationSubscriptionListener {
+
+        private volatile Set<SchemaPath> allObserved = Collections.emptySet();
+
+        @Override
+        public void onSubscriptionChanged(final Set<SchemaPath> currentTypes) {
+            final Set<SchemaPath> added = Sets.difference(currentTypes, allObserved).immutableCopy();
+            notifyAllListeners(added);
+            allObserved = Sets.union(allObserved, added).immutableCopy();
+        }
+
+        Set<SchemaPath> getAllObserved() {
+            return allObserved;
+        }
+    }
+
+    @Override
+    public void close() throws Exception {
+        super.close();
+        domListener.close();
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HeliumNotificationServiceAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HeliumNotificationServiceAdapter.java
new file mode 100644 (file)
index 0000000..68daa45
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+
+public class HeliumNotificationServiceAdapter implements org.opendaylight.controller.sal.binding.api.NotificationService, AutoCloseable {
+
+    private final NotificationService notificationService;
+
+    public HeliumNotificationServiceAdapter(NotificationService notificationService) {
+        this.notificationService = notificationService;
+    }
+
+    @Override
+    public <T extends Notification> ListenerRegistration<org.opendaylight.controller.sal.binding.api.NotificationListener<T>> registerNotificationListener(
+            final Class<T> notificationType, final org.opendaylight.controller.sal.binding.api.NotificationListener<T> listener) {
+        throw new UnsupportedOperationException("Not supported type of listener.");
+    }
+
+    @Override
+    public ListenerRegistration<NotificationListener> registerNotificationListener(
+            final NotificationListener listener) {
+        return notificationService.registerNotificationListener(listener);
+    }
+
+    @Override
+    public void close() throws Exception {
+
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HeliumRpcProviderRegistry.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HeliumRpcProviderRegistry.java
new file mode 100644 (file)
index 0000000..555c96e
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMRpcProviderServiceAdapter;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+public class HeliumRpcProviderRegistry implements RpcProviderRegistry {
+
+    private final RpcConsumerRegistry consumerRegistry;
+    private final BindingDOMRpcProviderServiceAdapter providerAdapter;
+
+    public HeliumRpcProviderRegistry(final RpcConsumerRegistry consumerRegistry,
+            final BindingDOMRpcProviderServiceAdapter providerAdapter) {
+        this.consumerRegistry = consumerRegistry;
+        this.providerAdapter = providerAdapter;
+    }
+
+    @Override
+    public <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(final Class<T> type, final T impl)
+            throws IllegalStateException {
+        return new CompositeRoutedRpcRegistration<>(type,impl,providerAdapter);
+    }
+
+    @Override
+    public <T extends RpcService> RpcRegistration<T> addRpcImplementation(final Class<T> type, final T impl)
+            throws IllegalStateException {
+        final ObjectRegistration<T> reg = providerAdapter.registerRpcImplementation(type, impl);
+        return new DelegatedRootRpcRegistration<>(type,reg);
+    }
+
+    @Override
+    public <T extends RpcService> T getRpcService(final Class<T> type) {
+        return consumerRegistry.getRpcService(type);
+    }
+
+    @Override
+    public <L extends RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
+            final L arg0) {
+        // FIXME: Implement this only if necessary
+        return null;
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HydrogenDataBrokerAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HydrogenDataBrokerAdapter.java
new file mode 100644 (file)
index 0000000..da6859b
--- /dev/null
@@ -0,0 +1,468 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutionException;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandlerRegistration;
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.impl.service.AbstractDataTransaction;
+import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Deprecated
+public class HydrogenDataBrokerAdapter implements DataProviderService, AutoCloseable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(HydrogenDataBrokerAdapter.class);
+
+    private final ConcurrentMap<InstanceIdentifier<?>, CommitHandlerRegistrationImpl> commitHandlers =
+            new ConcurrentHashMap<>();
+    private final ListeningExecutorService executorService = SingletonHolder.getDefaultCommitExecutor();
+
+    private final DataBroker delegate;
+
+    public HydrogenDataBrokerAdapter(final DataBroker dataBroker) {
+        delegate = dataBroker;
+        LOG.info("ForwardedBackwardsCompatibleBroker started.");
+    }
+
+    @Override
+    public DataModificationTransaction beginTransaction() {
+        return new ForwardedBackwardsCompatibleTransacion(delegate.newReadWriteTransaction());
+    }
+
+    @Override
+    public DataObject readConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
+        final DataModificationTransaction tx = beginTransaction();
+        return tx.readConfigurationData(path);
+    }
+
+    @Override
+    public DataObject readOperationalData(final InstanceIdentifier<? extends DataObject> path) {
+        final DataModificationTransaction tx = beginTransaction();
+        return tx.readOperationalData(path);
+    }
+
+    @Override
+    public Registration registerCommitHandler(
+            final InstanceIdentifier<? extends DataObject> path,
+            final DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject> commitHandler) {
+        final CommitHandlerRegistrationImpl reg = new CommitHandlerRegistrationImpl(path, commitHandler);
+        commitHandlers.put(path, reg);
+        return reg;
+    }
+
+    @Override
+    @Deprecated
+    public ListenerRegistration<RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject>>> registerCommitHandlerListener(
+            final RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject>> commitHandlerListener) {
+        throw new UnsupportedOperationException("Not supported contract.");
+    }
+
+    @Override
+    public ListenerRegistration<DataChangeListener> registerDataChangeListener(
+            final InstanceIdentifier<? extends DataObject> path, final DataChangeListener listener) {
+
+
+        final org.opendaylight.controller.md.sal.binding.api.DataChangeListener asyncOperListener = new BackwardsCompatibleOperationalDataChangeInvoker(listener);
+        final org.opendaylight.controller.md.sal.binding.api.DataChangeListener asyncCfgListener = new BackwardsCompatibleConfigurationDataChangeInvoker(listener);
+
+        final ListenerRegistration<org.opendaylight.controller.md.sal.binding.api.DataChangeListener> cfgReg = delegate.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, path, asyncCfgListener, DataChangeScope.SUBTREE);
+        final ListenerRegistration<org.opendaylight.controller.md.sal.binding.api.DataChangeListener> operReg = delegate.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, path, asyncOperListener, DataChangeScope.SUBTREE);
+
+        return new LegacyListenerRegistration(listener,cfgReg,operReg);
+    }
+
+    @Override
+    public Registration registerDataReader(
+            final InstanceIdentifier<? extends DataObject> path,
+            final DataReader<InstanceIdentifier<? extends DataObject>, DataObject> reader) {
+        throw new UnsupportedOperationException("Data reader contract is not supported.");
+    }
+
+    public ListenableFuture<RpcResult<TransactionStatus>> commit(final ForwardedBackwardsCompatibleTransacion tx) {
+
+        final List<DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject>> subTrans = new ArrayList<>();
+        LOG.debug("Tx: {} Submitted.",tx.getIdentifier());
+        final ListenableFuture<Boolean> requestCommit = executorService.submit(new Callable<Boolean>() {
+
+            @Override
+            public Boolean call() throws Exception {
+                try {
+                    for (final CommitHandlerRegistrationImpl handler : commitHandlers.values()) {
+
+                        final DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> subTx = handler
+                                .getInstance().requestCommit(tx);
+                        subTrans.add(subTx);
+                    }
+                } catch (final Exception e) {
+                    LOG.error("Tx: {} Rollback.",tx.getIdentifier(),e);
+                    for (final DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> subTx : subTrans) {
+                        subTx.rollback();
+                    }
+                    return false;
+                }
+                LOG.debug("Tx: {} Can Commit True.",tx.getIdentifier());
+                return true;
+            }
+
+        });
+
+        final ListenableFuture<RpcResult<TransactionStatus>> dataStoreCommit = Futures.transform(requestCommit, new AsyncFunction<Boolean, RpcResult<TransactionStatus>>() {
+
+            @Override
+            public ListenableFuture<RpcResult<TransactionStatus>> apply(final Boolean requestCommitSuccess) throws Exception {
+                if(requestCommitSuccess) {
+                    return AbstractDataTransaction.convertToLegacyCommitFuture(tx.delegate.submit());
+                }
+                return Futures.immediateFuture(RpcResultBuilder.<TransactionStatus>failed().withResult(TransactionStatus.FAILED).build());
+            }
+        });
+
+        return Futures.transform(dataStoreCommit, new Function<RpcResult<TransactionStatus>,RpcResult<TransactionStatus>>() {
+            @Override
+            public RpcResult<TransactionStatus> apply(final RpcResult<TransactionStatus> input) {
+                if(input.isSuccessful()) {
+                    for(final DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> subTx : subTrans ) {
+                        subTx.finish();
+                    }
+                } else {
+                    LOG.error("Tx: {} Rollback - Datastore commit failed.",tx.getIdentifier());
+                    for(final DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> subTx : subTrans ) {
+                        subTx.rollback();
+                    }
+                }
+                return input;
+            }
+        });
+    }
+
+    @Deprecated
+    private class ForwardedBackwardsCompatibleTransacion implements DataModificationTransaction {
+
+        private final ListenerRegistry<DataTransactionListener> listeners = ListenerRegistry.create();
+        private final Map<InstanceIdentifier<? extends DataObject>, DataObject> updated = new HashMap<>();
+        private final Map<InstanceIdentifier<? extends DataObject>, DataObject> created = new HashMap<>();
+        private final Set<InstanceIdentifier<? extends DataObject>> removed = new HashSet<>();
+        private final Map<InstanceIdentifier<? extends DataObject>, DataObject> original = new HashMap<>();
+        private TransactionStatus status = TransactionStatus.NEW;
+
+        private final Set<InstanceIdentifier<? extends DataObject>> posponedRemovedOperational = new HashSet<>();
+        private final Set<InstanceIdentifier<? extends DataObject>> posponedRemovedConfiguration = new HashSet<>();
+
+        private final ReadWriteTransaction delegate;
+
+
+        @Override
+        public final TransactionStatus getStatus() {
+            return status;
+        }
+
+        protected ForwardedBackwardsCompatibleTransacion(final ReadWriteTransaction delegate) {
+            this.delegate = delegate;
+            LOG.debug("Tx {} allocated.",getIdentifier());
+        }
+
+        @Override
+        public void putOperationalData(final InstanceIdentifier<? extends DataObject> path, final DataObject data) {
+            final boolean previouslyRemoved = posponedRemovedOperational.remove(path);
+
+            @SuppressWarnings({ "rawtypes", "unchecked" })
+            final InstanceIdentifier<DataObject> castedPath = (InstanceIdentifier) path;
+            if(previouslyRemoved) {
+                delegate.put(LogicalDatastoreType.OPERATIONAL, castedPath, data,true);
+            } else {
+                delegate.merge(LogicalDatastoreType.OPERATIONAL, castedPath, data,true);
+            }
+        }
+
+        @Override
+        public void putConfigurationData(final InstanceIdentifier<? extends DataObject> path, final DataObject data) {
+            final boolean previouslyRemoved = posponedRemovedConfiguration.remove(path);
+            final DataObject originalObj = readConfigurationData(path);
+            if (originalObj != null) {
+                original.put(path, originalObj);
+
+            } else {
+                created.put(path, data);
+            }
+            updated.put(path, data);
+            @SuppressWarnings({"rawtypes","unchecked"})
+            final InstanceIdentifier<DataObject> castedPath = (InstanceIdentifier) path;
+            if(previouslyRemoved) {
+                delegate.put(LogicalDatastoreType.CONFIGURATION, castedPath, data,true);
+            } else {
+                delegate.merge(LogicalDatastoreType.CONFIGURATION, castedPath, data,true);
+            }
+        }
+
+        @Override
+        public void removeOperationalData(final InstanceIdentifier<? extends DataObject> path) {
+            posponedRemovedOperational.add(path);
+        }
+
+        @Override
+        public void removeConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
+            posponedRemovedConfiguration.add(path);
+        }
+
+        @Override
+        public Map<InstanceIdentifier<? extends DataObject>, DataObject> getCreatedOperationalData() {
+            return Collections.emptyMap();
+        }
+
+        @Override
+        public Map<InstanceIdentifier<? extends DataObject>, DataObject> getCreatedConfigurationData() {
+            return created;
+        }
+
+        @Override
+        public Map<InstanceIdentifier<? extends DataObject>, DataObject> getUpdatedOperationalData() {
+            return Collections.emptyMap();
+        }
+
+        @Override
+        public Map<InstanceIdentifier<? extends DataObject>, DataObject> getUpdatedConfigurationData() {
+            return updated;
+        }
+
+        @Override
+        public Set<InstanceIdentifier<? extends DataObject>> getRemovedConfigurationData() {
+            return removed;
+        }
+
+        @Override
+        public Set<InstanceIdentifier<? extends DataObject>> getRemovedOperationalData() {
+            return Collections.emptySet();
+        }
+
+        @Override
+        public Map<InstanceIdentifier<? extends DataObject>, DataObject> getOriginalConfigurationData() {
+            return original;
+        }
+
+        @Override
+        public Map<InstanceIdentifier<? extends DataObject>, DataObject> getOriginalOperationalData() {
+            return Collections.emptyMap();
+        }
+
+        @Override
+        public DataObject readOperationalData(final InstanceIdentifier<? extends DataObject> path) {
+            try {
+                return delegate.read(LogicalDatastoreType.OPERATIONAL, path).get().orNull();
+            } catch (InterruptedException | ExecutionException e) {
+                LOG.error("Read of {} failed.", path,e);
+                return null;
+            }
+        }
+
+        @Override
+        public DataObject readConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
+            try {
+                return delegate.read(LogicalDatastoreType.CONFIGURATION, path).get().orNull();
+            } catch (InterruptedException | ExecutionException e) {
+                LOG.error("Read of {} failed.", path,e);
+                return null;
+            }
+        }
+
+        private void changeStatus(final TransactionStatus status) {
+            LOG.trace("Transaction {} changed status to {}", getIdentifier(), status);
+            this.status = status;
+
+            for(final ListenerRegistration<DataTransactionListener> listener : listeners) {
+                try {
+                    listener.getInstance().onStatusUpdated(this, status);
+                } catch (final Exception e) {
+                    LOG.error("Error during invoking transaction listener {}",listener.getInstance(),e);
+                }
+            }
+        }
+
+        @Override
+        public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+
+            for(final InstanceIdentifier<? extends DataObject> path : posponedRemovedConfiguration) {
+                delegate.delete(LogicalDatastoreType.CONFIGURATION, path);
+            }
+
+            for(final InstanceIdentifier<? extends DataObject> path : posponedRemovedOperational) {
+                delegate.delete(LogicalDatastoreType.OPERATIONAL, path);
+            }
+
+            changeStatus(TransactionStatus.SUBMITED);
+
+            final ListenableFuture<RpcResult<TransactionStatus>> f = HydrogenDataBrokerAdapter.this.commit(this);
+
+            Futures.addCallback(f, new FutureCallback<RpcResult<TransactionStatus>>() {
+                @Override
+                public void onSuccess(final RpcResult<TransactionStatus> result) {
+                    changeStatus(result.getResult());
+                }
+
+                @Override
+                public void onFailure(final Throwable t) {
+                    LOG.error("Transaction {} failed to complete", getIdentifier(), t);
+                    changeStatus(TransactionStatus.FAILED);
+                }
+            });
+
+            return f;
+        }
+
+        @Override
+        public ListenerRegistration<DataTransactionListener> registerListener(final DataTransactionListener listener) {
+            return listeners.register(listener);
+        }
+
+        @Override
+        public Object getIdentifier() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+    }
+
+    private class CommitHandlerRegistrationImpl extends
+            AbstractObjectRegistration<DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject>> {
+
+        private final InstanceIdentifier<? extends DataObject> path;
+
+        public CommitHandlerRegistrationImpl(final InstanceIdentifier<? extends DataObject> path,
+                final DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject> commitHandler) {
+            super(commitHandler);
+            this.path = path;
+        }
+
+        @Override
+        protected void removeRegistration() {
+            commitHandlers.remove(path, this);
+        }
+
+    }
+
+
+    private static final class LegacyListenerRegistration implements ListenerRegistration<DataChangeListener> {
+
+        private final DataChangeListener instance;
+        private final ListenerRegistration<org.opendaylight.controller.md.sal.binding.api.DataChangeListener> cfgReg;
+        private final ListenerRegistration<org.opendaylight.controller.md.sal.binding.api.DataChangeListener> operReg;
+
+        public LegacyListenerRegistration(final DataChangeListener listener,
+                final ListenerRegistration<org.opendaylight.controller.md.sal.binding.api.DataChangeListener> cfgReg,
+                final ListenerRegistration<org.opendaylight.controller.md.sal.binding.api.DataChangeListener> operReg) {
+            this.instance = listener;
+            this.cfgReg = cfgReg;
+            this.operReg = operReg;
+        }
+
+        @Override
+        public DataChangeListener getInstance() {
+            return instance;
+        }
+
+        @Override
+        public void close() {
+            cfgReg.close();
+            operReg.close();
+        }
+
+    }
+
+    private static class BackwardsCompatibleOperationalDataChangeInvoker implements org.opendaylight.controller.md.sal.binding.api.DataChangeListener, Delegator<DataChangeListener> {
+
+        private final org.opendaylight.controller.md.sal.common.api.data.DataChangeListener<?,?> delegate;
+
+
+        public BackwardsCompatibleOperationalDataChangeInvoker(final DataChangeListener listener) {
+            this.delegate = listener;
+        }
+
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        @Override
+        public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+
+            final DataChangeEvent legacyChange = HydrogenDataChangeEvent.createOperational(change);
+            delegate.onDataChanged(legacyChange);
+
+        }
+
+        @Override
+        public DataChangeListener getDelegate() {
+            return (DataChangeListener) delegate;
+        }
+
+    }
+
+    private static class BackwardsCompatibleConfigurationDataChangeInvoker implements org.opendaylight.controller.md.sal.binding.api.DataChangeListener, Delegator<DataChangeListener> {
+        private final org.opendaylight.controller.md.sal.common.api.data.DataChangeListener<?,?> delegate;
+
+        public BackwardsCompatibleConfigurationDataChangeInvoker(final DataChangeListener listener) {
+            this.delegate = listener;
+        }
+
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        @Override
+        public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+
+            final DataChangeEvent legacyChange = HydrogenDataChangeEvent.createConfiguration(change);
+
+            delegate.onDataChanged(legacyChange);
+
+        }
+
+        @Override
+        public DataChangeListener getDelegate() {
+            return (DataChangeListener) delegate;
+        }
+
+    }
+
+    @Override
+    public void close() throws Exception {
+        // TODO Auto-generated method stub
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HydrogenDataChangeEvent.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HydrogenDataChangeEvent.java
new file mode 100644 (file)
index 0000000..8f50d0a
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+@Deprecated
+public abstract class HydrogenDataChangeEvent implements
+        DataChangeEvent<InstanceIdentifier<? extends DataObject>, DataObject> {
+
+    private HydrogenDataChangeEvent() {
+    }
+
+    public static final DataChangeEvent<InstanceIdentifier<?>, DataObject> createOperational(
+            final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+        return new OperationalChangeEvent(change);
+    }
+
+    public static final DataChangeEvent<InstanceIdentifier<?>, DataObject> createConfiguration(
+            final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+        return new ConfigurationChangeEvent(change);
+    }
+
+    @Override
+    public Map<InstanceIdentifier<?>, DataObject> getCreatedOperationalData() {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public Map<InstanceIdentifier<?>, DataObject> getCreatedConfigurationData() {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public Map<InstanceIdentifier<?>, DataObject> getUpdatedOperationalData() {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public Map<InstanceIdentifier<?>, DataObject> getUpdatedConfigurationData() {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public Set<InstanceIdentifier<?>> getRemovedConfigurationData() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public Set<InstanceIdentifier<?>> getRemovedOperationalData() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public Map<InstanceIdentifier<?>, DataObject> getOriginalConfigurationData() {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public Map<InstanceIdentifier<?>, DataObject> getOriginalOperationalData() {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public DataObject getOriginalConfigurationSubtree() {
+        return null;
+    }
+
+    @Override
+    public DataObject getOriginalOperationalSubtree() {
+        return null;
+    }
+
+    @Override
+    public DataObject getUpdatedConfigurationSubtree() {
+        return null;
+    }
+
+    @Override
+    public DataObject getUpdatedOperationalSubtree() {
+        return null;
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    private static final class OperationalChangeEvent extends HydrogenDataChangeEvent {
+
+        private final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> delegate;
+        private Map<InstanceIdentifier<?>, DataObject> updatedCache;
+
+        public OperationalChangeEvent(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+            this.delegate = change;
+        }
+
+        @Override
+        public Map<InstanceIdentifier<?>, DataObject> getCreatedOperationalData() {
+            return delegate.getCreatedData();
+        }
+
+        @Override
+        public Set<InstanceIdentifier<?>> getRemovedOperationalData() {
+            return delegate.getRemovedPaths();
+        }
+
+        @Override
+        public DataObject getOriginalOperationalSubtree() {
+            return delegate.getOriginalSubtree();
+        }
+
+        @Override
+        public DataObject getUpdatedOperationalSubtree() {
+            return delegate.getUpdatedSubtree();
+        }
+
+        @Override
+        public Map<InstanceIdentifier<?>, DataObject> getOriginalOperationalData() {
+            return (Map) delegate.getOriginalData();
+        }
+
+        @Override
+        public Map<InstanceIdentifier<?>, DataObject> getUpdatedOperationalData() {
+            if(updatedCache == null) {
+                final Map<InstanceIdentifier<?>, DataObject> created = delegate.getCreatedData();
+                final Map<InstanceIdentifier<?>, DataObject> updated = delegate.getUpdatedData();
+                final Map<InstanceIdentifier<?>, DataObject> updatedComposite =
+                        new HashMap<>(created.size() + updated.size());
+                updatedComposite.putAll(created);
+                updatedComposite.putAll(updated);
+                updatedCache = Collections.unmodifiableMap(updatedComposite);
+            }
+            return updatedCache;
+        }
+
+        @Override
+        public String toString() {
+            return "OperationalChangeEvent [delegate=" + delegate + "]";
+        }
+
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    private static final class ConfigurationChangeEvent extends HydrogenDataChangeEvent {
+
+        private final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> delegate;
+        private Map<InstanceIdentifier<?>, DataObject> updatedCache;
+
+        public ConfigurationChangeEvent(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+            this.delegate = change;
+        }
+
+        @Override
+        public Map<InstanceIdentifier<?>, DataObject> getCreatedConfigurationData() {
+            return delegate.getCreatedData();
+        }
+
+        @Override
+        public Set<InstanceIdentifier<?>> getRemovedConfigurationData() {
+            return delegate.getRemovedPaths();
+        }
+
+        @Override
+        public DataObject getOriginalConfigurationSubtree() {
+            return delegate.getOriginalSubtree();
+        }
+
+        @Override
+        public DataObject getUpdatedConfigurationSubtree() {
+            return delegate.getUpdatedSubtree();
+        }
+
+        @Override
+        public Map<InstanceIdentifier<?>, DataObject> getOriginalConfigurationData() {
+            return (Map) delegate.getOriginalData();
+        }
+
+        @Override
+        public Map<InstanceIdentifier<?>, DataObject> getUpdatedConfigurationData() {
+            if(updatedCache == null) {
+                final Map<InstanceIdentifier<?>, DataObject> created = delegate.getCreatedData();
+                final Map<InstanceIdentifier<?>, DataObject> updated = delegate.getUpdatedData();
+                final Map<InstanceIdentifier<?>, DataObject> updatedComposite =
+                        new HashMap<>(created.size() + updated.size());
+                updatedComposite.putAll(created);
+                updatedComposite.putAll(updated);
+                updatedCache = Collections.unmodifiableMap(updatedComposite);
+            }
+            return updatedCache;
+        }
+
+        @Override
+        public String toString() {
+            return "ConfigurationChangeEvent [delegate=" + delegate + "]";
+        }
+
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HydrogenMountInstanceAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HydrogenMountInstanceAdapter.java
new file mode 100644 (file)
index 0000000..e7aea8d
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableClassToInstanceMap;
+import java.util.concurrent.ExecutorService;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.MountPoint;
+import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandlerRegistration;
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
+import org.opendaylight.controller.sal.binding.api.BindingAwareService;
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.controller.sal.binding.api.NotificationService;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.binding.api.mount.MountProviderInstance;
+import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+@Deprecated
+public class HydrogenMountInstanceAdapter implements MountProviderInstance {
+
+    private final ClassToInstanceMap<BindingAwareService> services;
+    private final InstanceIdentifier<?> identifier;
+
+
+    public HydrogenMountInstanceAdapter(final MountPoint key) {
+        this.identifier = key.getIdentifier();
+        final ImmutableClassToInstanceMap.Builder<BindingAwareService> builder = ImmutableClassToInstanceMap.builder();
+
+        final Optional<DataBroker> dataBroker = key.getService(DataBroker.class);
+        if(dataBroker.isPresent()) {
+            builder.put(DataBrokerService.class, new HydrogenDataBrokerAdapter(dataBroker.get()));
+        }
+        final Optional<org.opendaylight.controller.md.sal.binding.api.NotificationService> notificationService = key.getService(org.opendaylight.controller.md.sal.binding.api.NotificationService.class);
+        if(notificationService.isPresent()) {
+            builder.put(NotificationService.class, new HeliumNotificationServiceAdapter(notificationService.get()));
+        }
+        final Optional<RpcConsumerRegistry> rpcRegistry = key.getService(RpcConsumerRegistry.class);
+        if(rpcRegistry.isPresent()) {
+            builder.put(RpcConsumerRegistry.class, rpcRegistry.get());
+        }
+        services = builder.build();
+    }
+
+
+    private <T extends BindingAwareService> T service(final Class<T> service) {
+        final T potential = services.getInstance(service);
+        Preconditions.checkState(potential != null, "Service %s is not supported by mount point %s",service,this.getIdentifier());
+        return potential;
+    }
+
+    @Override
+    public <T extends RpcService> T getRpcService(final Class<T> serviceInterface) {
+        return service(RpcConsumerRegistry.class).getRpcService(serviceInterface);
+    }
+
+    @Override
+    public InstanceIdentifier<?> getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public <T extends Notification> ListenerRegistration<NotificationListener<T>> registerNotificationListener(
+            final Class<T> notificationType, final NotificationListener<T> listener) {
+        return service(NotificationService.class).registerNotificationListener(notificationType, listener);
+    }
+
+    @Override
+    public ListenerRegistration<org.opendaylight.yangtools.yang.binding.NotificationListener> registerNotificationListener(
+            final org.opendaylight.yangtools.yang.binding.NotificationListener listener) {
+        return service(NotificationService.class).registerNotificationListener(listener);
+    }
+
+    @Override
+    public DataModificationTransaction beginTransaction() {
+        return service(DataBrokerService.class).beginTransaction();
+    }
+
+    @Override
+    public DataObject readConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
+        return service(DataBrokerService.class).readConfigurationData(path);
+    }
+
+    @Override
+    public DataObject readOperationalData(final InstanceIdentifier<? extends DataObject> path) {
+        return service(DataBrokerService.class).readOperationalData(path);
+    }
+
+    @Override
+    public ListenerRegistration<DataChangeListener> registerDataChangeListener(
+            final InstanceIdentifier<? extends DataObject> path, final DataChangeListener listener) {
+        return service(DataBrokerService.class).registerDataChangeListener(path,listener);
+    }
+
+    @Override
+    public <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(final Class<T> serviceInterface,
+            final T implementation) throws IllegalStateException {
+        return service(RpcProviderRegistry.class).addRoutedRpcImplementation(serviceInterface, implementation);
+    }
+
+    @Override
+    public <T extends RpcService> RpcRegistration<T> addRpcImplementation(final Class<T> serviceInterface, final T implementation)
+            throws IllegalStateException {
+        return service(RpcProviderRegistry.class).addRpcImplementation(serviceInterface, implementation);
+    }
+
+    @Override
+    public void publish(final Notification notification) {
+        service(NotificationProviderService.class).publish(notification);
+    }
+
+    @Override
+    public void publish(final Notification notification, final ExecutorService executor) {
+        service(NotificationProviderService.class).publish(notification);
+    }
+
+    @Override
+    public Registration registerCommitHandler(final InstanceIdentifier<? extends DataObject> arg0,
+            final DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject> arg1) {
+        return service(DataProviderService.class).registerCommitHandler(arg0, arg1);
+    }
+
+    @Override
+    public ListenerRegistration<RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject>>> registerCommitHandlerListener(
+            final RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject>> arg0) {
+        return service(DataProviderService.class).registerCommitHandlerListener(arg0);
+    }
+
+    @Override
+    public Registration registerDataReader(final InstanceIdentifier<? extends DataObject> path,
+            final DataReader<InstanceIdentifier<? extends DataObject>, DataObject> reader) {
+        return service(DataProviderService.class).registerDataReader(path, reader);
+    }
+
+    @Override
+    public ListenerRegistration<NotificationInterestListener> registerInterestListener(
+            final NotificationInterestListener interestListener) {
+        return service(NotificationProviderService.class).registerInterestListener(interestListener);
+    }
+
+    @Override
+    public <L extends RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
+            final L arg0) {
+        return service(RpcProviderRegistry.class).registerRouteChangeListener(arg0);
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HydrogenMountPointServiceAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HydrogenMountPointServiceAdapter.java
new file mode 100644 (file)
index 0000000..cecd461
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import com.google.common.base.Optional;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import org.opendaylight.controller.md.sal.binding.api.MountPoint;
+import org.opendaylight.controller.md.sal.binding.api.MountPointService;
+import org.opendaylight.controller.sal.binding.api.mount.MountService;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+@Deprecated
+public class HydrogenMountPointServiceAdapter implements MountService {
+
+    private final MountPointService delegate;
+
+    public HydrogenMountPointServiceAdapter(final MountPointService mountService) {
+        delegate = mountService;
+    }
+
+    private final LoadingCache<MountPoint, HydrogenMountInstanceAdapter> mountAdapters = CacheBuilder.newBuilder().weakKeys()
+            .build(new CacheLoader<MountPoint, HydrogenMountInstanceAdapter>() {
+
+                @Override
+                public HydrogenMountInstanceAdapter load(final MountPoint key) throws Exception {
+                    return new HydrogenMountInstanceAdapter(key);
+                }
+            });
+
+    @Override
+    public HydrogenMountInstanceAdapter getMountPoint(final InstanceIdentifier<?> path) {
+        final Optional<MountPoint> mount = delegate.getMountPoint(path);
+        if (mount.isPresent()) {
+            return mountAdapters.getUnchecked(mount.get());
+        }
+        return null;
+    }
+
+    MountPointService getDelegate() {
+        return delegate;
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HydrogenMountProvisionServiceAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HydrogenMountProvisionServiceAdapter.java
new file mode 100644 (file)
index 0000000..b2ee1a5
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import org.opendaylight.controller.md.sal.binding.api.MountPointService;
+import org.opendaylight.controller.sal.binding.api.mount.MountProviderInstance;
+import org.opendaylight.controller.sal.binding.api.mount.MountProviderService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+@Deprecated
+public class HydrogenMountProvisionServiceAdapter extends HydrogenMountPointServiceAdapter implements MountProviderService {
+
+    public HydrogenMountProvisionServiceAdapter(final MountPointService mountService) {
+        super(mountService);
+    }
+
+    @Override
+    public MountProviderInstance createMountPoint(final InstanceIdentifier<?> path) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public MountProviderInstance createOrGetMountPoint(final InstanceIdentifier<?> path) {
+        return getMountPoint(path);
+    }
+
+    @Override
+    public ListenerRegistration<MountProvisionListener> registerProvisionListener(final MountProvisionListener listener) {
+        return new ListenerRegistration<MountProvisionListener>() {
+
+            @Override
+            public MountProvisionListener getInstance() {
+                return listener;
+            }
+
+            @Override
+            public void close() {
+            }
+        };
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HydrogenNotificationBrokerImpl.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HydrogenNotificationBrokerImpl.java
new file mode 100644 (file)
index 0000000..90e9484
--- /dev/null
@@ -0,0 +1,155 @@
+/**
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.annotation.concurrent.GuardedBy;
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Deprecated
+public class HydrogenNotificationBrokerImpl implements NotificationProviderService, AutoCloseable {
+    private static final Logger LOG = LoggerFactory.getLogger(HydrogenNotificationBrokerImpl.class);
+
+    private final ListenerRegistry<NotificationInterestListener> interestListeners =
+            ListenerRegistry.create();
+    private final AtomicReference<ListenerMapGeneration> listeners = new AtomicReference<>(new ListenerMapGeneration());
+    private final ExecutorService executor;
+
+    public HydrogenNotificationBrokerImpl(final ExecutorService executor) {
+        this.executor = Preconditions.checkNotNull(executor);
+    }
+
+    @Override
+    public void publish(final Notification notification) {
+        publish(notification, executor);
+    }
+
+    @Override
+    public void publish(final Notification notification, final ExecutorService service) {
+        for (final NotificationListenerRegistration<?> r : listeners.get().listenersFor(notification)) {
+            service.submit(new NotifyTask(r, notification));
+        }
+    }
+
+    @GuardedBy("this")
+    private Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> mutableListeners() {
+        return HashMultimap.create(listeners.get().getListeners());
+    }
+
+    private void addRegistrations(final NotificationListenerRegistration<?>... registrations) {
+        synchronized (this) {
+            final Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> newListeners =
+                    mutableListeners();
+            for (final NotificationListenerRegistration<?> reg : registrations) {
+                newListeners.put(reg.getType(), reg);
+            }
+
+            listeners.set(new ListenerMapGeneration(newListeners));
+        }
+
+        // Notifications are dispatched out of lock...
+        for (final NotificationListenerRegistration<?> reg : registrations) {
+            announceNotificationSubscription(reg.getType());
+        }
+    }
+
+    private synchronized void removeRegistrations(final NotificationListenerRegistration<?>... registrations) {
+        final Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> newListeners =
+                mutableListeners();
+
+        for (final NotificationListenerRegistration<?> reg : registrations) {
+            newListeners.remove(reg.getType(), reg);
+        }
+
+        listeners.set(new ListenerMapGeneration(newListeners));
+    }
+
+    private void announceNotificationSubscription(final Class<? extends Notification> notification) {
+        for (final ListenerRegistration<NotificationInterestListener> listener : interestListeners) {
+            try {
+                listener.getInstance().onNotificationSubscribtion(notification);
+            } catch (final Exception e) {
+                LOG.warn("Listener {} reported unexpected error on notification {}",
+                        listener.getInstance(), notification, e);
+            }
+        }
+    }
+
+    @Override
+    public ListenerRegistration<NotificationInterestListener> registerInterestListener(final NotificationInterestListener interestListener) {
+        final ListenerRegistration<NotificationInterestListener> registration = this.interestListeners.register(interestListener);
+
+        for (final Class<? extends Notification> notification : listeners.get().getKnownTypes()) {
+            interestListener.onNotificationSubscribtion(notification);
+        }
+        return registration;
+    }
+
+    @Override
+    public <T extends Notification> NotificationListenerRegistration<T> registerNotificationListener(final Class<T> notificationType, final NotificationListener<T> listener) {
+        final NotificationListenerRegistration<T> reg = new AbstractNotificationListenerRegistration<T>(notificationType, listener) {
+            @Override
+            protected void removeRegistration() {
+                removeRegistrations(this);
+            }
+        };
+
+        addRegistrations(reg);
+        return reg;
+    }
+
+    @Override
+    public ListenerRegistration<org.opendaylight.yangtools.yang.binding.NotificationListener> registerNotificationListener(final org.opendaylight.yangtools.yang.binding.NotificationListener listener) {
+        final NotificationInvoker invoker = NotificationInvoker.invokerFor(listener);
+        final Set<Class<? extends Notification>> types = invoker.getSupportedNotifications();
+        final NotificationListenerRegistration<?>[] regs = new NotificationListenerRegistration<?>[types.size()];
+
+        // Populate the registrations...
+        int i = 0;
+        for (final Class<? extends Notification> type : types) {
+            regs[i] = new AggregatedNotificationListenerRegistration<Notification, Object>(type, invoker, regs) {
+                @Override
+                protected void removeRegistration() {
+                    // Nothing to do, will be cleaned up by parent (below)
+                }
+            };
+            ++i;
+        }
+
+        // ... now put them to use ...
+        addRegistrations(regs);
+
+        // ... finally return the parent registration
+        return new AbstractListenerRegistration<org.opendaylight.yangtools.yang.binding.NotificationListener>(listener) {
+            @Override
+            protected void removeRegistration() {
+                removeRegistrations(regs);
+                for (final ListenerRegistration<?> reg : regs) {
+                    reg.close();
+                }
+            }
+        };
+    }
+
+    @Override
+    public void close() {
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/ListenerMapGeneration.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/ListenerMapGeneration.java
new file mode 100644 (file)
index 0000000..f597838
--- /dev/null
@@ -0,0 +1,106 @@
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+import com.google.common.base.Predicate;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+
+/**
+ * An immutable view of the current generation of listeners.
+ */
+final class ListenerMapGeneration {
+    private static final int CACHE_MAX_ENTRIES = 1000;
+
+    /**
+     * Constant map of notification type to subscribed listeners.
+     */
+    private final Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> typeToListeners;
+
+    /**
+     * Dynamic cache of notification implementation to matching listeners. This cache loads entries based on
+     * the contents of the {@link #typeToListeners} map.
+     */
+    private final LoadingCache<Class<?>, Iterable<NotificationListenerRegistration<?>>> implementationToListeners =
+            CacheBuilder.newBuilder()
+            .weakKeys()
+            .maximumSize(CACHE_MAX_ENTRIES)
+            .build(new CacheLoader<Class<?>, Iterable<NotificationListenerRegistration<?>>>() {
+                @Override
+                public Iterable<NotificationListenerRegistration<?>> load(final Class<?> key) {
+                    final Set<NotificationListenerRegistration<?>> regs = new HashSet<>();
+
+                    for (final Class<?> type : getNotificationTypes(key)) {
+                        @SuppressWarnings("unchecked")
+                        final Collection<NotificationListenerRegistration<?>> l = typeToListeners.get((Class<? extends Notification>) type);
+                        if (l != null) {
+                            regs.addAll(l);
+                        }
+                    }
+
+                    return ImmutableSet.copyOf(regs);
+                }
+            });
+
+    ListenerMapGeneration() {
+        typeToListeners = ImmutableMultimap.of();
+    }
+
+    ListenerMapGeneration(final Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> listeners) {
+        this.typeToListeners = ImmutableMultimap.copyOf(listeners);
+    }
+
+    /**
+     * Current listeners. Exposed for creating the next generation.
+     *
+     * @return Current type-to-listener map.
+     */
+    Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> getListeners() {
+        return typeToListeners;
+    }
+
+    /**
+     * Look up the listeners which need to see this notification delivered.
+     *
+     * @param notification Notification object
+     * @return Iterable of listeners, guaranteed to be nonnull.
+     */
+    public Iterable<NotificationListenerRegistration<?>> listenersFor(final Notification notification) {
+        // Safe to use, as our loader does not throw checked exceptions
+        return implementationToListeners.getUnchecked(notification.getClass());
+    }
+
+    public Iterable<Class<? extends Notification>> getKnownTypes() {
+        return typeToListeners.keySet();
+    }
+
+    private static Iterable<Class<?>> getNotificationTypes(final Class<?> cls) {
+        final Class<?>[] ifaces = cls.getInterfaces();
+        return Iterables.filter(Arrays.asList(ifaces), new Predicate<Class<?>>() {
+            @Override
+            public boolean apply(final Class<?> input) {
+                if (Notification.class.equals(input)) {
+                    return false;
+                }
+                return Notification.class.isAssignableFrom(input);
+            }
+        });
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/NotificationInvoker.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/NotificationInvoker.java
new file mode 100644 (file)
index 0000000..f98c8b8
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.binding.util.NotificationListenerInvoker;
+import org.opendaylight.yangtools.yang.common.QName;
+
+final class NotificationInvoker implements org.opendaylight.controller.sal.binding.api.NotificationListener<Notification> {
+
+    private final NotificationListener delegate;
+    private final Map<Class<? extends Notification>,InvokerContext> invokers;
+
+
+    private NotificationInvoker(final NotificationListener listener) {
+        delegate = listener;
+        final Map<Class<? extends Notification>, InvokerContext> builder = new HashMap<>();
+        for(final TypeToken<?> ifaceToken : TypeToken.of(listener.getClass()).getTypes().interfaces()) {
+            final Class<?> iface = ifaceToken.getRawType();
+            if(NotificationListener.class.isAssignableFrom(iface) && BindingReflections.isBindingClass(iface)) {
+                @SuppressWarnings("unchecked")
+                final Class<? extends NotificationListener> listenerType = (Class<? extends NotificationListener>) iface;
+                final NotificationListenerInvoker invoker = NotificationListenerInvoker.from(listenerType);
+                for(final Class<? extends Notification> type : getNotificationTypes(listenerType)) {
+                    builder.put(type, new InvokerContext(BindingReflections.findQName(type) , invoker));
+                }
+            }
+        }
+        invokers = ImmutableMap.copyOf(builder);
+    }
+
+    public static NotificationInvoker invokerFor(final NotificationListener listener) {
+        return new NotificationInvoker(listener);
+    }
+
+    public Set<Class<? extends Notification>> getSupportedNotifications() {
+        return invokers.keySet();
+    }
+
+    @Override
+    public void onNotification(final Notification notification) {
+        getContext(notification.getImplementedInterface()).invoke(notification);
+    }
+
+    private InvokerContext getContext(final Class<?> type) {
+        return invokers.get(type);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Set<Class<? extends Notification>> getNotificationTypes(final Class<? extends org.opendaylight.yangtools.yang.binding.NotificationListener> type) {
+        // TODO: Investigate possibility and performance impact if we cache this or expose
+        // it from NotificationListenerInvoker
+        final Set<Class<? extends Notification>> ret = new HashSet<>();
+        for(final Method method : type.getMethods()) {
+            if(BindingReflections.isNotificationCallback(method)) {
+                final Class<? extends Notification> notification = (Class<? extends Notification>) method.getParameterTypes()[0];
+                ret.add(notification);
+            }
+        }
+        return ret;
+    }
+
+    private final class InvokerContext {
+
+        private final QName name;
+        private final NotificationListenerInvoker invoker;
+
+        private InvokerContext(final QName name, final NotificationListenerInvoker invoker) {
+            this.name = name;
+            this.invoker = invoker;
+        }
+
+        public void invoke(final Notification notification) {
+            invoker.invokeNotification(delegate, name, notification);
+        }
+
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/NotificationListenerRegistration.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/NotificationListenerRegistration.java
new file mode 100644 (file)
index 0000000..81183c9
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+/**
+ * A registration of a {@link NotificationListener}. Allows query of the type
+ * of the notification and dispatching the notification atomically with regard
+ * to unregistration.
+ *
+ * @param <T> Type of notification
+ */
+interface NotificationListenerRegistration<T extends Notification> extends ListenerRegistration<NotificationListener<T>> {
+    /**
+     * Return the interface class of the notification type.
+     *
+     * @return Notification type.
+     */
+    Class<? extends Notification> getType();
+
+    /**
+     * Dispatch a notification to the listener.
+     *
+     * @param notification Notification to be dispatched
+     */
+    void notify(Notification notification);
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/NotifyTask.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/NotifyTask.java
new file mode 100644 (file)
index 0000000..59be9af
--- /dev/null
@@ -0,0 +1,98 @@
+/**
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class NotifyTask implements Runnable {
+    private static final Logger LOG = LoggerFactory.getLogger(NotifyTask.class);
+
+    private final NotificationListenerRegistration<?> registration;
+    private final Notification notification;
+
+    public NotifyTask(final NotificationListenerRegistration<?> registration, final Notification notification) {
+        this.registration = Preconditions.checkNotNull(registration);
+        this.notification = Preconditions.checkNotNull(notification);
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T extends Notification> NotificationListenerRegistration<T> getRegistration() {
+        return (NotificationListenerRegistration<T>)registration;
+    }
+
+    @Override
+    public void run() {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Delivering notification {} to {}", notification, registration.getInstance());
+        } else {
+            LOG.trace("Delivering notification {} to {}", notification.getClass().getName(), registration.getInstance());
+        }
+
+        try {
+            getRegistration().notify(notification);
+        } catch (final Exception e) {
+            LOG.error("Unhandled exception thrown by listener: {}", registration.getInstance(), e);
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Notification delivered {} to {}", notification, registration.getInstance());
+        } else {
+            LOG.trace("Notification delivered {} to {}", notification.getClass().getName(), registration.getInstance());
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((registration== null) ? 0 : registration.hashCode());
+        result = prime * result + ((notification== null) ? 0 : notification.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final NotifyTask other = (NotifyTask) obj;
+        if (registration == null) {
+            if (other.registration != null) {
+                return false;
+            }
+        } else if (!registration.equals(other.registration)) {
+            return false;
+        }
+        if (notification == null) {
+            if (other.notification != null) {
+                return false;
+            }
+        } else if (!notification.equals(other.notification)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this)
+                .add("listener", registration)
+                .add("notification", notification.getClass())
+                .toString();
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java
new file mode 100644 (file)
index 0000000..ad52557
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Optional;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.opendaylight.controller.md.sal.binding.api.ClusteredDataChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.ClusteredDOMDataChangeListener;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+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.impl.codec.DeserializationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractForwardedDataBroker implements Delegator<DOMDataBroker>, AutoCloseable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractForwardedDataBroker.class);
+    // The Broker to whom we do all forwarding
+    private final DOMDataBroker domDataBroker;
+
+    private final BindingToNormalizedNodeCodec codec;
+
+    protected AbstractForwardedDataBroker(final DOMDataBroker domDataBroker, final BindingToNormalizedNodeCodec codec,
+            final SchemaService schemaService) {
+        this.domDataBroker = domDataBroker;
+        this.codec = codec;
+    }
+
+    protected AbstractForwardedDataBroker(final DOMDataBroker domDataBroker, final BindingToNormalizedNodeCodec codec) {
+        this.domDataBroker = domDataBroker;
+        this.codec = codec;
+    }
+
+    protected BindingToNormalizedNodeCodec getCodec() {
+        return codec;
+    }
+
+    @Override
+    public DOMDataBroker getDelegate() {
+        return domDataBroker;
+    }
+
+    public ListenerRegistration<DataChangeListener> registerDataChangeListener(final LogicalDatastoreType store,
+            final InstanceIdentifier<?> path, final DataChangeListener listener, final DataChangeScope triggeringScope) {
+        final DOMDataChangeListener domDataChangeListener;
+
+        if(listener instanceof ClusteredDataChangeListener) {
+            domDataChangeListener = new TranslatingClusteredDataChangeInvoker(store, path, listener, triggeringScope);
+        } else {
+            domDataChangeListener = new TranslatingDataChangeInvoker(store, path, listener,
+                triggeringScope);
+        }
+        final YangInstanceIdentifier domPath = codec.toYangInstanceIdentifierBlocking(path);
+        final ListenerRegistration<DOMDataChangeListener> domRegistration = domDataBroker.registerDataChangeListener(store,
+                domPath, domDataChangeListener, triggeringScope);
+        return new ListenerRegistrationImpl(listener, domRegistration);
+    }
+
+    protected Map<InstanceIdentifier<?>, DataObject> toBinding(final InstanceIdentifier<?> path,
+            final Map<YangInstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized) {
+        final Map<InstanceIdentifier<?>, DataObject> newMap = new HashMap<>();
+
+        for (final Map.Entry<YangInstanceIdentifier, ? extends NormalizedNode<?, ?>> entry : normalized.entrySet()) {
+            try {
+                final Optional<Entry<InstanceIdentifier<? extends DataObject>, DataObject>> potential = getCodec().toBinding(entry);
+                if (potential.isPresent()) {
+                    final Entry<InstanceIdentifier<? extends DataObject>, DataObject> binding = potential.get();
+                    newMap.put(binding.getKey(), binding.getValue());
+                }
+            } catch (final DeserializationException e) {
+                LOG.warn("Failed to transform {}, omitting it", entry, e);
+            }
+        }
+        return newMap;
+    }
+
+    protected Set<InstanceIdentifier<?>> toBinding(final InstanceIdentifier<?> path,
+            final Set<YangInstanceIdentifier> normalized) {
+        final Set<InstanceIdentifier<?>> hashSet = new HashSet<>();
+        for (final YangInstanceIdentifier normalizedPath : normalized) {
+            try {
+                final Optional<InstanceIdentifier<? extends DataObject>> potential = getCodec().toBinding(normalizedPath);
+                if (potential.isPresent()) {
+                    final InstanceIdentifier<? extends DataObject> binding = potential.get();
+                    hashSet.add(binding);
+                } else if (normalizedPath.getLastPathArgument() instanceof YangInstanceIdentifier.AugmentationIdentifier) {
+                    hashSet.add(path);
+                }
+            } catch (final DeserializationException e) {
+                LOG.warn("Failed to transform {}, omitting it", normalizedPath, e);
+            }
+        }
+        return hashSet;
+    }
+
+    protected Optional<DataObject> toBindingData(final InstanceIdentifier<?> path, final NormalizedNode<?, ?> data) {
+        if (path.isWildcarded()) {
+            return Optional.absent();
+        }
+        return (Optional<DataObject>) getCodec().deserializeFunction(path).apply(Optional.<NormalizedNode<?, ?>> of(data));
+    }
+
+    private class TranslatingDataChangeInvoker implements DOMDataChangeListener {
+        private final DataChangeListener bindingDataChangeListener;
+        private final LogicalDatastoreType store;
+        private final InstanceIdentifier<?> path;
+        private final DataChangeScope triggeringScope;
+
+        public TranslatingDataChangeInvoker(final LogicalDatastoreType store, final InstanceIdentifier<?> path,
+                final DataChangeListener bindingDataChangeListener, final DataChangeScope triggeringScope) {
+            this.store = store;
+            this.path = path;
+            this.bindingDataChangeListener = bindingDataChangeListener;
+            this.triggeringScope = triggeringScope;
+        }
+
+        @Override
+        public void onDataChanged(final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
+            bindingDataChangeListener.onDataChanged(new TranslatedDataChangeEvent(change, path));
+        }
+
+        @Override
+        public String toString() {
+            return bindingDataChangeListener.getClass().getName();
+        }
+    }
+
+    /**
+     * Translator for ClusteredDataChangeListener
+     */
+
+    private class TranslatingClusteredDataChangeInvoker extends TranslatingDataChangeInvoker implements
+        ClusteredDOMDataChangeListener {
+
+        public TranslatingClusteredDataChangeInvoker(LogicalDatastoreType store, InstanceIdentifier<?> path,
+                                                     DataChangeListener bindingDataChangeListener,
+                                                     DataChangeScope triggeringScope) {
+            super(store, path, bindingDataChangeListener, triggeringScope);
+        }
+    }
+
+    private class TranslatedDataChangeEvent implements AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> {
+        private final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> domEvent;
+        private final InstanceIdentifier<?> path;
+
+        private Map<InstanceIdentifier<?>, DataObject> createdCache;
+        private Map<InstanceIdentifier<?>, DataObject> updatedCache;
+        private Map<InstanceIdentifier<?>, DataObject> originalCache;
+        private Set<InstanceIdentifier<?>> removedCache;
+        private Optional<DataObject> originalDataCache;
+        private Optional<DataObject> updatedDataCache;
+
+        public TranslatedDataChangeEvent(
+                final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change,
+                final InstanceIdentifier<?> path) {
+            this.domEvent = change;
+            this.path = path;
+        }
+
+        @Override
+        public Map<InstanceIdentifier<?>, DataObject> getCreatedData() {
+            if (createdCache == null) {
+                createdCache = Collections.unmodifiableMap(toBinding(path, domEvent.getCreatedData()));
+            }
+            return createdCache;
+        }
+
+        @Override
+        public Map<InstanceIdentifier<?>, DataObject> getUpdatedData() {
+            if (updatedCache == null) {
+                updatedCache = Collections.unmodifiableMap(toBinding(path, domEvent.getUpdatedData()));
+            }
+            return updatedCache;
+
+        }
+
+        @Override
+        public Set<InstanceIdentifier<?>> getRemovedPaths() {
+            if (removedCache == null) {
+                removedCache = Collections.unmodifiableSet(toBinding(path, domEvent.getRemovedPaths()));
+            }
+            return removedCache;
+        }
+
+        @Override
+        public Map<InstanceIdentifier<?>, DataObject> getOriginalData() {
+            if (originalCache == null) {
+                originalCache = Collections.unmodifiableMap(toBinding(path, domEvent.getOriginalData()));
+            }
+            return originalCache;
+
+        }
+
+        @Override
+        public DataObject getOriginalSubtree() {
+            if (originalDataCache == null) {
+                if (domEvent.getOriginalSubtree() != null) {
+                    originalDataCache = toBindingData(path, domEvent.getOriginalSubtree());
+                } else {
+                    originalDataCache = Optional.absent();
+                }
+            }
+            return originalDataCache.orNull();
+        }
+
+        @Override
+        public DataObject getUpdatedSubtree() {
+            if (updatedDataCache == null) {
+                if (domEvent.getUpdatedSubtree() != null) {
+                    updatedDataCache = toBindingData(path, domEvent.getUpdatedSubtree());
+                } else {
+                    updatedDataCache = Optional.absent();
+                }
+            }
+            return updatedDataCache.orNull();
+        }
+
+        @Override
+        public String toString() {
+            return MoreObjects.toStringHelper(TranslatedDataChangeEvent.class) //
+                    .add("created", getCreatedData()) //
+                    .add("updated", getUpdatedData()) //
+                    .add("removed", getRemovedPaths()) //
+                    .add("dom", domEvent) //
+                    .toString();
+        }
+    }
+
+    private static class ListenerRegistrationImpl extends AbstractListenerRegistration<DataChangeListener> {
+        private final ListenerRegistration<DOMDataChangeListener> registration;
+
+        public ListenerRegistrationImpl(final DataChangeListener listener,
+                final ListenerRegistration<DOMDataChangeListener> registration) {
+            super(listener);
+            this.registration = registration;
+        }
+
+        @Override
+        protected void removeRegistration() {
+            registration.close();
+        }
+    }
+
+    @Override
+    public void close() {
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedTransaction.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedTransaction.java
new file mode 100644 (file)
index 0000000..3d337d8
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.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.Futures;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
+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.DOMDataReadTransaction;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.util.concurrent.MappingCheckedFuture;
+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;
+
+abstract class AbstractForwardedTransaction<T extends AsyncTransaction<YangInstanceIdentifier, NormalizedNode<?, ?>>>
+        implements Delegator<T>, Identifiable<Object> {
+
+    private final T delegate;
+    private final BindingToNormalizedNodeCodec codec;
+
+    public AbstractForwardedTransaction(final T delegateTx, final BindingToNormalizedNodeCodec codec) {
+        this.delegate = Preconditions.checkNotNull(delegateTx, "Delegate must not be null");
+        this.codec = Preconditions.checkNotNull(codec, "Codec must not be null");
+    }
+
+
+    @Override
+    public final  Object getIdentifier() {
+        return delegate.getIdentifier();
+    }
+
+    @Override
+    public final  T getDelegate() {
+        return delegate;
+    }
+
+    @SuppressWarnings("unchecked")
+    protected final <S extends AsyncTransaction<YangInstanceIdentifier, NormalizedNode<?, ?>>> S getDelegateChecked(final Class<S> txType) {
+        Preconditions.checkState(txType.isInstance(delegate));
+        return (S) delegate;
+    }
+
+    protected final BindingToNormalizedNodeCodec getCodec() {
+        return codec;
+    }
+
+    protected final <D extends DataObject> CheckedFuture<Optional<D>,ReadFailedException> doRead(
+            final DOMDataReadTransaction readTx, final LogicalDatastoreType store,
+            final InstanceIdentifier<D> path) {
+        Preconditions.checkArgument(!path.isWildcarded(), "Invalid read of wildcarded path %s", path);
+
+        return MappingCheckedFuture.create(
+                    Futures.transform(readTx.read(store, codec.toYangInstanceIdentifierBlocking(path)),
+                                      codec.deserializeFunction(path)),
+                    ReadFailedException.MAPPER);
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractReadWriteTransaction.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractReadWriteTransaction.java
new file mode 100644 (file)
index 0000000..8fbc118
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+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.impl.util.compat.DataNormalizationException;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class AbstractReadWriteTransaction extends AbstractWriteTransaction<DOMDataReadWriteTransaction> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractReadWriteTransaction.class);
+
+    public AbstractReadWriteTransaction(final DOMDataReadWriteTransaction delegate, final BindingToNormalizedNodeCodec codec) {
+        super(delegate, codec);
+    }
+
+    @Override
+    protected final void ensureParentsByMerge(final LogicalDatastoreType store,
+            final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalizedPath,
+            final InstanceIdentifier<?> path) {
+        List<PathArgument> currentArguments = new ArrayList<>();
+        DataNormalizationOperation<?> currentOp = getCodec().getDataNormalizer().getRootOperation();
+        Iterator<PathArgument> iterator = normalizedPath.getPathArguments().iterator();
+        while (iterator.hasNext()) {
+            PathArgument currentArg = iterator.next();
+            try {
+                currentOp = currentOp.getChild(currentArg);
+            } catch (DataNormalizationException e) {
+                throw new IllegalArgumentException(String.format("Invalid child encountered in path %s", path), e);
+            }
+            currentArguments.add(currentArg);
+            org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier currentPath = org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.create(
+                    currentArguments);
+
+            final Boolean exists;
+            try {
+                exists = getDelegate().exists(store, currentPath).checkedGet();
+            } catch (ReadFailedException e) {
+                LOG.error("Failed to read pre-existing data from store {} path {}", store, currentPath, e);
+                throw new IllegalStateException("Failed to read pre-existing data", e);
+            }
+
+            if (!exists && iterator.hasNext()) {
+                getDelegate().merge(store, currentPath, currentOp.createDefault(currentArg));
+            }
+        }
+    }
+
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractWriteTransaction.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractWriteTransaction.java
new file mode 100644 (file)
index 0000000..1e3e419
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.Map.Entry;
+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.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ *
+ * Abstract Base Transaction for transactions which are backed by
+ * {@link DOMDataWriteTransaction}
+ */
+public abstract class AbstractWriteTransaction<T extends DOMDataWriteTransaction> extends
+        AbstractForwardedTransaction<T> {
+
+    protected AbstractWriteTransaction(final T delegate, final BindingToNormalizedNodeCodec codec) {
+        super(delegate, codec);
+    }
+
+    public final <U extends DataObject> void put(final LogicalDatastoreType store,
+            final InstanceIdentifier<U> path, final U data, final boolean createParents) {
+        Preconditions.checkArgument(!path.isWildcarded(), "Cannot put data into wildcarded path %s", path);
+
+        final Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec().toNormalizedNode(path, data);
+        if (createParents) {
+            ensureParentsByMerge(store, normalized.getKey(), path);
+        } else {
+            ensureListParentIfNeeded(store,path,normalized);
+        }
+
+        getDelegate().put(store, normalized.getKey(), normalized.getValue());
+    }
+
+    public final <U extends DataObject> void merge(final LogicalDatastoreType store,
+            final InstanceIdentifier<U> path, final U data,final boolean createParents) {
+        Preconditions.checkArgument(!path.isWildcarded(), "Cannot merge data into wildcarded path %s", path);
+
+        final Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec().toNormalizedNode(path, data);
+        if (createParents) {
+            ensureParentsByMerge(store, normalized.getKey(), path);
+        } else {
+            ensureListParentIfNeeded(store,path,normalized);
+        }
+
+        getDelegate().merge(store, normalized.getKey(), normalized.getValue());
+    }
+
+    /**
+     *
+     * Ensures list parent if item is list, otherwise noop.
+     *
+     * <p>
+     * One of properties of binding specification is that it is imposible
+     * to represent list as a whole and thus it is impossible to write
+     * empty variation of MapNode without creating parent node, with
+     * empty list.
+     *
+     * <p>
+     * This actually makes writes such as
+     * <pre>
+     * put("Nodes", new NodesBuilder().build());
+     * put("Nodes/Node[key]", new NodeBuilder().setKey("key").build());
+     * </pre>
+     * To result in three DOM operations:
+     * <pre>
+     * put("/nodes",domNodes);
+     * merge("/nodes/node",domNodeList);
+     * put("/nodes/node/node[key]",domNode);
+     * </pre>
+     *
+     *
+     * In order to allow that to be inserted if necessary, if we know
+     * item is list item, we will try to merge empty MapNode or OrderedNodeMap
+     * to ensure list exists.
+     *
+     * @param store Data Store type
+     * @param path Path to data (Binding Aware)
+     * @param normalized Normalized version of data to be written
+     */
+    private void ensureListParentIfNeeded(final LogicalDatastoreType store, final InstanceIdentifier<?> path,
+            final Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalized) {
+        if (Identifiable.class.isAssignableFrom(path.getTargetType())) {
+            YangInstanceIdentifier parentMapPath = normalized.getKey().getParent();
+            Preconditions.checkArgument(parentMapPath != null, "Map path %s does not have a parent", path);
+
+            NormalizedNode<?, ?> emptyParent = getCodec().getDefaultNodeFor(parentMapPath);
+            getDelegate().merge(store, parentMapPath, emptyParent);
+        }
+    }
+
+    /**
+     * @deprecated Use {@link YangInstanceIdentifier#getParent()} instead.
+     */
+    @Deprecated
+    protected static Optional<YangInstanceIdentifier> getParent(final YangInstanceIdentifier child) {
+        return Optional.fromNullable(child.getParent());
+    }
+
+    /**
+     * Subclasses of this class are required to implement creation of parent
+     * nodes based on behaviour of their underlying transaction.
+     *
+     * @param store
+     * @param key
+     * @param path
+     */
+    protected abstract void ensureParentsByMerge(LogicalDatastoreType store,
+            YangInstanceIdentifier key, InstanceIdentifier<?> path);
+
+    protected final void doDelete(final LogicalDatastoreType store,
+            final InstanceIdentifier<?> path) {
+        Preconditions.checkArgument(!path.isWildcarded(), "Cannot delete wildcarded path %s", path);
+
+        final YangInstanceIdentifier normalized = getCodec().toYangInstanceIdentifierBlocking(path);
+        getDelegate().delete(store, normalized);
+    }
+
+    protected final CheckedFuture<Void,TransactionCommitFailedException> doSubmit() {
+        return getDelegate().submit();
+    }
+
+    protected final boolean doCancel() {
+        return getDelegate().cancel();
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMAdapterBuilder.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMAdapterBuilder.java
new file mode 100644 (file)
index 0000000..d460c48
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ClassToInstanceMap;
+import org.opendaylight.controller.md.sal.binding.api.BindingService;
+import org.opendaylight.controller.md.sal.binding.spi.AdapterBuilder;
+import org.opendaylight.controller.md.sal.dom.api.DOMService;
+
+abstract class BindingDOMAdapterBuilder<T extends BindingService> extends AdapterBuilder<T, DOMService> {
+
+    interface Factory<T extends BindingService> {
+
+        BindingDOMAdapterBuilder<T> newBuilder();
+
+    }
+
+    private BindingToNormalizedNodeCodec codec;
+
+    public void setCodec(final BindingToNormalizedNodeCodec codec) {
+        this.codec = codec;
+    }
+
+    @Override
+    protected final T createInstance(final ClassToInstanceMap<DOMService> delegates) {
+        Preconditions.checkState(codec != null);
+        return createInstance(codec,delegates);
+    }
+
+    protected abstract T createInstance(BindingToNormalizedNodeCodec codec2, ClassToInstanceMap<DOMService> delegates);
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMAdapterLoader.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMAdapterLoader.java
new file mode 100644 (file)
index 0000000..96108ee
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import org.opendaylight.controller.md.sal.binding.api.BindingService;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
+import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMAdapterBuilder.Factory;
+import org.opendaylight.controller.md.sal.binding.spi.AdapterBuilder;
+import org.opendaylight.controller.md.sal.binding.spi.AdapterLoader;
+import org.opendaylight.controller.md.sal.dom.api.DOMService;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
+
+public abstract class BindingDOMAdapterLoader extends AdapterLoader<BindingService, DOMService> {
+
+
+    private static final Map<Class<?>,BindingDOMAdapterBuilder.Factory<?>> FACTORIES = ImmutableMap.<Class<?>,BindingDOMAdapterBuilder.Factory<?>>builder()
+            .put(NotificationService.class,BindingDOMNotificationServiceAdapter.BUILDER_FACTORY)
+            .put(NotificationPublishService.class,BindingDOMNotificationPublishServiceAdapter.BUILDER_FACTORY)
+            .put(DataBroker.class,BindingDOMDataBrokerAdapter.BUILDER_FACTORY)
+            .put(RpcConsumerRegistry.class,BindingDOMRpcServiceAdapter.BUILDER_FACTORY)
+            .build();
+
+    private final BindingToNormalizedNodeCodec codec;
+
+    public BindingDOMAdapterLoader(final BindingToNormalizedNodeCodec codec) {
+        super();
+        this.codec = codec;
+    }
+
+    @Override
+    protected final AdapterBuilder<? extends BindingService, DOMService> createBuilder(final Class<? extends BindingService> key) {
+        final Factory<?> factory = FACTORIES.get(key);
+        Preconditions.checkArgument(factory != null, "Unsupported service type %s", key);
+        final BindingDOMAdapterBuilder<?> builder = factory.newBuilder();
+        builder.setCodec(codec);
+        return builder;
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataBrokerAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataBrokerAdapter.java
new file mode 100644 (file)
index 0000000..1c43f12
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeService;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMAdapterBuilder.Factory;
+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.DOMDataTreeChangeService;
+import org.opendaylight.controller.md.sal.dom.api.DOMService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * The DataBrokerImpl simply defers to the DOMDataBroker for all its operations.
+ * All transactions and listener registrations are wrapped by the DataBrokerImpl
+ * to allow binding aware components to use the DataBroker transparently.
+ *
+ * Besides this the DataBrokerImpl and it's collaborators also cache data that
+ * is already transformed from the binding independent to binding aware format
+ *
+
+ */
+public class BindingDOMDataBrokerAdapter extends AbstractForwardedDataBroker implements DataBroker, DataTreeChangeService {
+
+
+    static final Factory<DataBroker> BUILDER_FACTORY = new BindingDOMAdapterBuilder.Factory<DataBroker>() {
+
+        @Override
+        public BindingDOMAdapterBuilder<DataBroker> newBuilder() {
+            return new Builder();
+        }
+
+    };
+    private final DataTreeChangeService treeChangeService;
+
+    public BindingDOMDataBrokerAdapter(final DOMDataBroker domDataBroker, final BindingToNormalizedNodeCodec codec) {
+        super(domDataBroker, codec);
+        final DOMDataTreeChangeService domTreeChange = (DOMDataTreeChangeService) domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
+        if(domTreeChange != null) {
+            treeChangeService = BindingDOMDataTreeChangeServiceAdapter.create(codec, domTreeChange);
+        } else {
+            treeChangeService = null;
+        }
+    }
+
+    @Override
+
+    public ReadOnlyTransaction newReadOnlyTransaction() {
+        return new BindingDOMReadTransactionAdapter(getDelegate().newReadOnlyTransaction(),getCodec());
+    }
+
+    @Override
+    public ReadWriteTransaction newReadWriteTransaction() {
+        return new BindingDOMReadWriteTransactionAdapter(getDelegate().newReadWriteTransaction(),getCodec());
+    }
+
+    @Override
+    public WriteTransaction newWriteOnlyTransaction() {
+        return new BindingDOMWriteTransactionAdapter<>(getDelegate().newWriteOnlyTransaction(),getCodec());
+    }
+
+    @Override
+    public BindingTransactionChain createTransactionChain(final TransactionChainListener listener) {
+        return new BindingDOMTransactionChainAdapter(getDelegate(), getCodec(), listener);
+    }
+
+    private static class Builder extends BindingDOMAdapterBuilder<DataBroker> {
+
+        @Override
+        public Set<? extends Class<? extends DOMService>> getRequiredDelegates() {
+            return ImmutableSet.of(DOMDataBroker.class);
+        }
+
+        @Override
+        protected DataBroker createInstance(final BindingToNormalizedNodeCodec codec,
+                final ClassToInstanceMap<DOMService> delegates) {
+            final DOMDataBroker domDataBroker = delegates.getInstance(DOMDataBroker.class);
+            return new BindingDOMDataBrokerAdapter(domDataBroker, codec);
+        }
+
+    }
+
+    @Override
+    public <T extends DataObject, L extends DataTreeChangeListener<T>> ListenerRegistration<L> registerDataTreeChangeListener(
+            final DataTreeIdentifier<T> treeId, final L listener) {
+        if(treeChangeService == null) {
+            throw new UnsupportedOperationException("Underlying data broker does not expose DOMDataTreeChangeService.");
+        }
+        return treeChangeService.registerDataTreeChangeListener(treeId, listener);
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeListenerAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeListenerAdapter.java
new file mode 100644 (file)
index 0000000..ab1348f
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.Collection;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+
+/**
+ * Adapter wrapping Binding {@link DataTreeChangeListener} and exposing
+ * it as {@link DOMDataTreeChangeListener} and translated DOM events
+ * to their Binding equivalent.
+ *
+ */
+final class BindingDOMDataTreeChangeListenerAdapter<T extends DataObject> implements DOMDataTreeChangeListener {
+
+    private final BindingToNormalizedNodeCodec codec;
+    private final DataTreeChangeListener<T> listener;
+    private final LogicalDatastoreType store;
+
+    BindingDOMDataTreeChangeListenerAdapter(final BindingToNormalizedNodeCodec codec, final DataTreeChangeListener<T> listener,
+            final LogicalDatastoreType store) {
+        this.codec = Preconditions.checkNotNull(codec);
+        this.listener = Preconditions.checkNotNull(listener);
+        this.store = Preconditions.checkNotNull(store);
+    }
+
+    @Override
+    public void onDataTreeChanged(final Collection<DataTreeCandidate> domChanges) {
+        final Collection<DataTreeModification<T>> bindingChanges = LazyDataTreeModification.from(codec, domChanges, store);
+        listener.onDataTreeChanged(bindingChanges);
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeServiceAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeServiceAdapter.java
new file mode 100644 (file)
index 0000000..02daa45
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeService;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+
+/**
+ *
+ * Adapter exposing Binding {@link DataTreeChangeService} and wrapping
+ * {@link DOMDataTreeChangeService} and is responsible for translation
+ * and instantiation of {@link BindingDOMDataTreeChangeListenerAdapter}
+ * adapters.
+ *
+ * Each registered {@link DataTreeChangeListener} is wrapped using
+ * adapter and registered directly to DOM service.
+ */
+final class BindingDOMDataTreeChangeServiceAdapter implements DataTreeChangeService {
+
+    private final BindingToNormalizedNodeCodec codec;
+    private final DOMDataTreeChangeService dataTreeChangeService;
+
+    private BindingDOMDataTreeChangeServiceAdapter(final BindingToNormalizedNodeCodec codec,
+            final DOMDataTreeChangeService dataTreeChangeService) {
+        this.codec = Preconditions.checkNotNull(codec);
+        this.dataTreeChangeService = Preconditions.checkNotNull(dataTreeChangeService);
+    }
+
+    static DataTreeChangeService create(final BindingToNormalizedNodeCodec codec,
+            final DOMDataTreeChangeService dataTreeChangeService) {
+        return new BindingDOMDataTreeChangeServiceAdapter(codec, dataTreeChangeService);
+    }
+
+    @Override
+    public <T extends DataObject, L extends DataTreeChangeListener<T>> ListenerRegistration<L> registerDataTreeChangeListener(
+            final DataTreeIdentifier<T> treeId, final L listener) {
+        final DOMDataTreeIdentifier domIdentifier = toDomTreeIdentifier(treeId);
+        final BindingDOMDataTreeChangeListenerAdapter<T> domListener = new BindingDOMDataTreeChangeListenerAdapter<>(codec,listener, treeId.getDatastoreType());
+        final ListenerRegistration<BindingDOMDataTreeChangeListenerAdapter<T>> domReg = dataTreeChangeService.registerDataTreeChangeListener(domIdentifier, domListener);
+        return new BindingDataTreeChangeListenerRegistration<>(listener,domReg);
+    }
+
+    private DOMDataTreeIdentifier toDomTreeIdentifier(final DataTreeIdentifier<?> treeId) {
+        final YangInstanceIdentifier domPath = codec.toYangInstanceIdentifierBlocking(treeId.getRootIdentifier());
+        return new DOMDataTreeIdentifier(treeId.getDatastoreType(), domPath);
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMMountPointListenerAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMMountPointListenerAdapter.java
new file mode 100644 (file)
index 0000000..f331482
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.md.sal.binding.api.MountPointService.MountPointListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+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.impl.codec.DeserializationException;
+
+final class BindingDOMMountPointListenerAdapter<T extends MountPointListener> implements ListenerRegistration<T>, MountProvisionListener {
+
+    private final T listener;
+    private final ListenerRegistration<MountProvisionListener> registration;
+    private final BindingToNormalizedNodeCodec codec;
+
+    public BindingDOMMountPointListenerAdapter(final T listener, final BindingToNormalizedNodeCodec codec, final DOMMountPointService mountService) {
+        this.listener = listener;
+        this.codec = codec;
+        this.registration = mountService.registerProvisionListener(this);
+    }
+
+    @Override
+    public T getInstance() {
+        return listener;
+    }
+
+    @Override
+    public void close() {
+        registration.close();
+    }
+
+    @Override
+    public void onMountPointCreated(final YangInstanceIdentifier path) {
+        try {
+            final InstanceIdentifier<? extends DataObject> bindingPath = toBinding(path);
+            listener.onMountPointCreated(bindingPath);
+        } catch (final DeserializationException e) {
+            BindingDOMMountPointServiceAdapter.LOG.error("Unable to translate mountPoint path {}. Omitting event.",path,e);
+        }
+
+    }
+
+    private InstanceIdentifier<? extends DataObject> toBinding(final YangInstanceIdentifier path) throws DeserializationException {
+        final Optional<InstanceIdentifier<? extends DataObject>> instanceIdentifierOptional = codec.toBinding(path);
+        if(instanceIdentifierOptional.isPresent()) {
+            return instanceIdentifierOptional.get();
+        } else {
+            throw new DeserializationException("Deserialization unsuccessful, " + instanceIdentifierOptional);
+        }
+    }
+
+    @Override
+    public void onMountPointRemoved(final YangInstanceIdentifier path) {
+        try {
+            final InstanceIdentifier<? extends DataObject> bindingPath = toBinding(path);
+            listener.onMountPointRemoved(bindingPath);
+        } catch (final DeserializationException e) {
+            BindingDOMMountPointServiceAdapter.LOG.error("Unable to translate mountPoint path {}. Omitting event.",path,e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMMountPointServiceAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMMountPointServiceAdapter.java
new file mode 100644 (file)
index 0000000..759c470
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import org.opendaylight.controller.md.sal.binding.api.MountPoint;
+import org.opendaylight.controller.md.sal.binding.api.MountPointService;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class BindingDOMMountPointServiceAdapter implements MountPointService {
+
+    public static final Logger LOG = LoggerFactory.getLogger(BindingDOMMountPointServiceAdapter.class);
+
+    private final BindingToNormalizedNodeCodec codec;
+    private final DOMMountPointService mountService;
+    private final LoadingCache<DOMMountPoint, BindingMountPointAdapter> bindingMountpoints = CacheBuilder.newBuilder()
+            .weakKeys().build(new CacheLoader<DOMMountPoint, BindingMountPointAdapter>() {
+
+                @Override
+                public BindingMountPointAdapter load(DOMMountPoint key) throws Exception {
+                    return new BindingMountPointAdapter(codec,key);
+                }
+            });
+
+    public BindingDOMMountPointServiceAdapter(DOMMountPointService mountService,BindingToNormalizedNodeCodec codec) {
+        this.codec = codec;
+        this.mountService = mountService;
+    }
+
+    @Override
+    public Optional<MountPoint> getMountPoint(InstanceIdentifier<?> mountPoint) {
+
+        YangInstanceIdentifier domPath = codec.toYangInstanceIdentifierBlocking(mountPoint);
+        Optional<DOMMountPoint> domMount = mountService.getMountPoint(domPath);
+        if(domMount.isPresent()) {
+            return Optional.<MountPoint>fromNullable(bindingMountpoints.getUnchecked(domMount.get()));
+        }
+        return Optional.absent();
+    }
+
+    @Override
+    public <T extends MountPointListener> ListenerRegistration<T> registerListener(InstanceIdentifier<?> path,
+            T listener) {
+        return new BindingDOMMountPointListenerAdapter<T>(listener,codec,mountService);
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMNotificationListenerAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMNotificationListenerAdapter.java
new file mode 100644 (file)
index 0000000..cd3220e
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.binding.util.NotificationListenerInvoker;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+class BindingDOMNotificationListenerAdapter implements DOMNotificationListener {
+
+    private final BindingNormalizedNodeSerializer codec;
+    private final NotificationListener delegate;
+    private final Map<SchemaPath,NotificationListenerInvoker> invokers;
+
+    public BindingDOMNotificationListenerAdapter(final BindingNormalizedNodeSerializer codec, final NotificationListener delegate) {
+        this.codec = codec;
+        this.delegate = delegate;
+        this.invokers = createInvokerMapFor(delegate.getClass());
+    }
+
+    @Override
+    public void onNotification(@Nonnull final DOMNotification notification) {
+        final Notification baNotification = deserialize(notification);
+        final QName notificationQName = notification.getType().getLastComponent();
+        getInvoker(notification.getType()).invokeNotification(delegate, notificationQName, baNotification);
+    }
+
+    private Notification deserialize(final DOMNotification notification) {
+        if(notification instanceof LazySerializedDOMNotification) {
+            return ((LazySerializedDOMNotification) notification).getBindingData();
+        }
+        return codec.fromNormalizedNodeNotification(notification.getType(), notification.getBody());
+    }
+
+    private NotificationListenerInvoker getInvoker(final SchemaPath type) {
+        return invokers.get(type);
+    }
+
+    protected Set<SchemaPath> getSupportedNotifications() {
+        return invokers.keySet();
+    }
+
+    public static Map<SchemaPath, NotificationListenerInvoker> createInvokerMapFor(final Class<? extends NotificationListener> implClz) {
+        final Map<SchemaPath, NotificationListenerInvoker> builder = new HashMap<>();
+        for(final TypeToken<?> ifaceToken : TypeToken.of(implClz).getTypes().interfaces()) {
+            Class<?> iface = ifaceToken.getRawType();
+            if(NotificationListener.class.isAssignableFrom(iface) && BindingReflections.isBindingClass(iface)) {
+                @SuppressWarnings("unchecked")
+                final Class<? extends NotificationListener> listenerType = (Class<? extends NotificationListener>) iface;
+                final NotificationListenerInvoker invoker = NotificationListenerInvoker.from(listenerType);
+                for(final SchemaPath path : getNotificationTypes(listenerType)) {
+                    builder.put(path, invoker);
+                }
+            }
+        }
+        return ImmutableMap.copyOf(builder);
+    }
+
+    private static Set<SchemaPath> getNotificationTypes(final Class<? extends NotificationListener> type) {
+        // TODO: Investigate possibility and performance impact if we cache this or expose
+        // it from NotificationListenerInvoker
+        final Set<SchemaPath> ret = new HashSet<>();
+        for(final Method method : type.getMethods()) {
+            if(BindingReflections.isNotificationCallback(method)) {
+                final Class<?> notification = method.getParameterTypes()[0];
+                final QName name = BindingReflections.findQName(notification);
+                ret.add(SchemaPath.create(true, name));
+            }
+        }
+        return ret;
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMNotificationPublishServiceAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMNotificationPublishServiceAdapter.java
new file mode 100644 (file)
index 0000000..1fe81ea
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMAdapterBuilder.Factory;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationPublishService;
+import org.opendaylight.controller.md.sal.dom.api.DOMService;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+public class BindingDOMNotificationPublishServiceAdapter implements NotificationPublishService, AutoCloseable {
+
+    static final Factory<NotificationPublishService> BUILDER_FACTORY = new BindingDOMAdapterBuilder.Factory<NotificationPublishService>() {
+
+        @Override
+        public BindingDOMAdapterBuilder<NotificationPublishService> newBuilder() {
+            return new Builder();
+        }
+
+    };
+
+    private final BindingToNormalizedNodeCodec codecRegistry;
+    private final DOMNotificationPublishService domPublishService;
+
+    public BindingDOMNotificationPublishServiceAdapter(final BindingToNormalizedNodeCodec codec, final DOMNotificationPublishService domPublishService) {
+        this.codecRegistry = codec;
+        this.domPublishService = domPublishService;
+    }
+
+    public BindingToNormalizedNodeCodec getCodecRegistry() {
+        return codecRegistry;
+    }
+
+    public DOMNotificationPublishService getDomPublishService() {
+        return domPublishService;
+    }
+
+    @Override
+    public void putNotification(final Notification notification) throws InterruptedException {
+        domPublishService.putNotification(toDomNotification(notification));
+    }
+
+    @Override
+    public ListenableFuture<? extends Object> offerNotification(final Notification notification) {
+        ListenableFuture<?> offerResult = domPublishService.offerNotification(toDomNotification(notification));
+        return DOMNotificationPublishService.REJECTED.equals(offerResult)
+                ? NotificationPublishService.REJECTED
+                : offerResult;
+    }
+
+    @Override
+    public ListenableFuture<? extends Object> offerNotification(final Notification notification, final int timeout, final TimeUnit unit) throws InterruptedException {
+        ListenableFuture<?> offerResult = domPublishService.offerNotification(toDomNotification(notification), timeout, unit);
+        return DOMNotificationPublishService.REJECTED.equals(offerResult)
+                ? NotificationPublishService.REJECTED
+                : offerResult;
+    }
+
+    private DOMNotification toDomNotification(final Notification notification) {
+        return LazySerializedDOMNotification.create(codecRegistry, notification);
+    }
+
+    @Override
+    public void close() throws Exception {
+
+    }
+
+    protected static class Builder extends BindingDOMAdapterBuilder<NotificationPublishService> {
+
+        @Override
+        public Set<Class<? extends DOMService>> getRequiredDelegates() {
+            return ImmutableSet.<Class<? extends DOMService>>of(DOMNotificationPublishService.class);
+        }
+
+        @Override
+        protected NotificationPublishService createInstance(final BindingToNormalizedNodeCodec codec,
+                final ClassToInstanceMap<DOMService> delegates) {
+            final DOMNotificationPublishService domPublish = delegates.getInstance(DOMNotificationPublishService.class);
+            return new BindingDOMNotificationPublishServiceAdapter(codec, domPublish);
+        }
+
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMNotificationServiceAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMNotificationServiceAdapter.java
new file mode 100644 (file)
index 0000000..33eb9ee
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMAdapterBuilder.Factory;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
+import org.opendaylight.controller.md.sal.dom.api.DOMService;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+
+public class BindingDOMNotificationServiceAdapter implements NotificationService, AutoCloseable {
+
+    public static final Factory<NotificationService> BUILDER_FACTORY = new Factory<NotificationService>() {
+
+        @Override
+        public BindingDOMAdapterBuilder<NotificationService> newBuilder() {
+            return new Builder();
+        }
+
+    };
+    private final BindingNormalizedNodeSerializer codec;
+    private final DOMNotificationService domNotifService;
+
+    public BindingDOMNotificationServiceAdapter(final BindingNormalizedNodeSerializer codec, final DOMNotificationService domNotifService) {
+        this.codec = codec;
+        this.domNotifService = domNotifService;
+    }
+
+    @Override
+    public <T extends NotificationListener> ListenerRegistration<T> registerNotificationListener(final T listener) {
+        final BindingDOMNotificationListenerAdapter domListener = new BindingDOMNotificationListenerAdapter(codec, listener);
+        final ListenerRegistration<BindingDOMNotificationListenerAdapter> domRegistration =
+                domNotifService.registerNotificationListener(domListener, domListener.getSupportedNotifications());
+        return new ListenerRegistrationImpl<>(listener, domRegistration);
+    }
+
+    @Override
+    public void close() throws Exception {
+
+    }
+
+    private static class ListenerRegistrationImpl<T extends NotificationListener> extends AbstractListenerRegistration<T> {
+        private final ListenerRegistration<?> listenerRegistration;
+
+        public ListenerRegistrationImpl(final T listener, final ListenerRegistration<?> listenerRegistration) {
+            super(listener);
+            this.listenerRegistration = listenerRegistration;
+        }
+
+        @Override
+        protected void removeRegistration() {
+            listenerRegistration.close();
+        }
+    }
+
+    private static class Builder extends BindingDOMAdapterBuilder<NotificationService> {
+
+        @Override
+        protected NotificationService createInstance(final BindingToNormalizedNodeCodec codec,
+                final ClassToInstanceMap<DOMService> delegates) {
+            final DOMNotificationService domNotification = delegates.getInstance(DOMNotificationService.class);
+            return new BindingDOMNotificationServiceAdapter(codec.getCodecRegistry(), domNotification);
+        }
+
+        @Override
+        public Set<? extends Class<? extends DOMService>> getRequiredDelegates() {
+            return ImmutableSet.of(DOMNotificationService.class);
+        }
+    }
+
+    public DOMNotificationService getDomService() {
+        return domNotifService;
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMReadTransactionAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMReadTransactionAdapter.java
new file mode 100644 (file)
index 0000000..0e8a043
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+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.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+
+class BindingDOMReadTransactionAdapter extends AbstractForwardedTransaction<DOMDataReadOnlyTransaction> implements
+        ReadOnlyTransaction {
+
+    protected BindingDOMReadTransactionAdapter(final DOMDataReadOnlyTransaction delegate,
+            final BindingToNormalizedNodeCodec codec) {
+        super(delegate, codec);
+    }
+
+    @Override
+    public <T extends DataObject> CheckedFuture<Optional<T>,ReadFailedException> read(
+            final LogicalDatastoreType store, final InstanceIdentifier<T> path) {
+        return doRead(getDelegate(),store, path);
+    }
+
+    @Override
+    public void close() {
+        getDelegate().close();
+    }
+
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMReadWriteTransactionAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMReadWriteTransactionAdapter.java
new file mode 100644 (file)
index 0000000..e9c4efa
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+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.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+
+class BindingDOMReadWriteTransactionAdapter extends
+        BindingDOMWriteTransactionAdapter<DOMDataReadWriteTransaction> implements ReadWriteTransaction {
+
+    protected BindingDOMReadWriteTransactionAdapter(final DOMDataReadWriteTransaction delegate,
+            final BindingToNormalizedNodeCodec codec) {
+        super(delegate, codec);
+    }
+
+    @Override
+    public <T extends DataObject> CheckedFuture<Optional<T>,ReadFailedException> read(
+            final LogicalDatastoreType store, final InstanceIdentifier<T> path) {
+        return doRead(getDelegate(), store, path);
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMRpcAdapterRegistration.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMRpcAdapterRegistration.java
new file mode 100644 (file)
index 0000000..c971fc6
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationRegistration;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+class BindingDOMRpcAdapterRegistration<T extends RpcService> extends AbstractObjectRegistration<T>{
+
+    private final DOMRpcImplementationRegistration<?> reg;
+
+    public BindingDOMRpcAdapterRegistration(T instance, DOMRpcImplementationRegistration<?> reg) {
+        super(instance);
+        this.reg = reg;
+    }
+
+    @Override
+    protected void removeRegistration() {
+        reg.close();
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMRpcImplementationAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMRpcImplementationAdapter.java
new file mode 100644 (file)
index 0000000..175b0f3
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.JdkFutureAdapters;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.binding.util.RpcServiceInvoker;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+public class BindingDOMRpcImplementationAdapter implements DOMRpcImplementation {
+
+    private static final Cache<Class<?>, RpcServiceInvoker> SERVICE_INVOKERS = CacheBuilder.newBuilder().weakKeys().build();
+
+    private final BindingNormalizedNodeCodecRegistry codec;
+    private final RpcServiceInvoker invoker;
+    private final RpcService delegate;
+    private final QName inputQname;
+
+    public <T extends RpcService> BindingDOMRpcImplementationAdapter(final BindingNormalizedNodeCodecRegistry codec, final Class<T> type, final Map<SchemaPath, Method> localNameToMethod, final T delegate) {
+        try {
+            this.invoker = SERVICE_INVOKERS.get(type, new Callable<RpcServiceInvoker>() {
+                @Override
+                public RpcServiceInvoker call() {
+                    final Map<QName, Method> map = new HashMap<>();
+                    for (Entry<SchemaPath, Method> e : localNameToMethod.entrySet()) {
+                        map.put(e.getKey().getLastComponent(), e.getValue());
+                    }
+
+                    return RpcServiceInvoker.from(map);
+                }
+            });
+        } catch (ExecutionException e) {
+            throw new IllegalArgumentException("Failed to create invokers for type " + type, e);
+        }
+
+        this.codec = Preconditions.checkNotNull(codec);
+        this.delegate = Preconditions.checkNotNull(delegate);
+        inputQname = QName.cachedReference(QName.create(BindingReflections.getQNameModule(type), "input"));
+    }
+
+    @Override
+    public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final DOMRpcIdentifier rpc, final NormalizedNode<?, ?> input) {
+        final SchemaPath schemaPath = rpc.getType();
+        final DataObject bindingInput = input != null ? deserilialize(rpc.getType(),input) : null;
+        final ListenableFuture<RpcResult<?>> bindingResult = invoke(schemaPath,bindingInput);
+        return transformResult(schemaPath,bindingResult);
+    }
+
+    private DataObject deserilialize(final SchemaPath rpcPath, final NormalizedNode<?, ?> input) {
+        if (input instanceof LazySerializedContainerNode) {
+            return ((LazySerializedContainerNode) input).bindingData();
+        }
+        final SchemaPath inputSchemaPath = rpcPath.createChild(inputQname);
+        return codec.fromNormalizedNodeRpcData(inputSchemaPath, (ContainerNode) input);
+    }
+
+    private ListenableFuture<RpcResult<?>> invoke(final SchemaPath schemaPath, final DataObject input) {
+        return JdkFutureAdapters.listenInPoolThread(invoker.invokeRpc(delegate, schemaPath.getLastComponent(), input));
+    }
+
+    private CheckedFuture<DOMRpcResult, DOMRpcException> transformResult(final SchemaPath schemaPath,
+            final ListenableFuture<RpcResult<?>> bindingResult) {
+        return LazyDOMRpcResultFuture.create(codec, bindingResult);
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMRpcProviderServiceAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMRpcProviderServiceAdapter.java
new file mode 100644 (file)
index 0000000..f9cbe27
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.collect.ImmutableSet;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationRegistration;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcProviderService;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+public class BindingDOMRpcProviderServiceAdapter {
+
+    private static final Set<YangInstanceIdentifier> GLOBAL = ImmutableSet.of(YangInstanceIdentifier.builder().build());
+    private final BindingToNormalizedNodeCodec codec;
+    private final DOMRpcProviderService domRpcRegistry;
+
+    public BindingDOMRpcProviderServiceAdapter(final DOMRpcProviderService domRpcRegistry, final BindingToNormalizedNodeCodec codec) {
+        this.codec = codec;
+        this.domRpcRegistry = domRpcRegistry;
+    }
+
+    public <S extends RpcService, T extends S> ObjectRegistration<T> registerRpcImplementation(final Class<S> type,
+            final T implementation) {
+        return register(type, implementation, GLOBAL);
+    }
+
+    public <S extends RpcService, T extends S> ObjectRegistration<T> registerRpcImplementation(final Class<S> type,
+            final T implementation, final Set<InstanceIdentifier<?>> paths) {
+        return register(type, implementation, toYangInstanceIdentifiers(paths));
+    }
+
+    private <S extends RpcService, T extends S> ObjectRegistration<T> register(final Class<S> type, final T implementation, final Collection<YangInstanceIdentifier> rpcContextPaths) {
+        final Map<SchemaPath, Method> rpcs = codec.getRpcMethodToSchemaPath(type).inverse();
+
+        final BindingDOMRpcImplementationAdapter adapter = new BindingDOMRpcImplementationAdapter(codec.getCodecRegistry(), type, rpcs, implementation);
+        final Set<DOMRpcIdentifier> domRpcs = createDomRpcIdentifiers(rpcs.keySet(), rpcContextPaths);
+        final DOMRpcImplementationRegistration<?> domReg = domRpcRegistry.registerRpcImplementation(adapter, domRpcs);
+        return new BindingRpcAdapterRegistration<>(implementation, domReg);
+    }
+
+    private static Set<DOMRpcIdentifier> createDomRpcIdentifiers(final Set<SchemaPath> rpcs, final Collection<YangInstanceIdentifier> paths) {
+        final Set<DOMRpcIdentifier> ret = new HashSet<>();
+        for (final YangInstanceIdentifier path : paths) {
+            for (final SchemaPath rpc : rpcs) {
+                ret.add(DOMRpcIdentifier.create(rpc, path));
+            }
+        }
+        return ret;
+    }
+
+    private Collection<YangInstanceIdentifier> toYangInstanceIdentifiers(final Set<InstanceIdentifier<?>> identifiers) {
+        final Collection<YangInstanceIdentifier> ret = new ArrayList<>(identifiers.size());
+        for (final InstanceIdentifier<?> binding : identifiers) {
+            ret.add(codec.toYangInstanceIdentifierCached(binding));
+        }
+        return ret;
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMRpcServiceAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMRpcServiceAdapter.java
new file mode 100644 (file)
index 0000000..8ebc5e6
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMAdapterBuilder.Factory;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.md.sal.dom.api.DOMService;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+
+public class BindingDOMRpcServiceAdapter implements RpcConsumerRegistry {
+
+    protected static final Factory<RpcConsumerRegistry> BUILDER_FACTORY = new Factory<RpcConsumerRegistry>() {
+
+        @Override
+        public BindingDOMAdapterBuilder<RpcConsumerRegistry> newBuilder() {
+            return new Builder();
+        }
+
+    };
+
+    private final LoadingCache<Class<? extends RpcService>, RpcServiceAdapter> proxies = CacheBuilder.newBuilder()
+            .weakKeys()
+            .build(new CacheLoader<Class<? extends RpcService>, RpcServiceAdapter>() {
+
+                @Override
+                public RpcServiceAdapter load(final Class<? extends RpcService> key) throws Exception {
+                    return createProxy(key);
+                }
+
+            });
+
+    private final DOMRpcService domService;
+    private final BindingToNormalizedNodeCodec codec;
+
+    public BindingDOMRpcServiceAdapter(final DOMRpcService domService, final BindingToNormalizedNodeCodec codec) {
+        super();
+        this.domService = domService;
+        this.codec = codec;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T extends RpcService> T getRpcService(final Class<T> rpcService) {
+        Preconditions.checkArgument(rpcService != null, "Rpc Service needs to be specied.");
+        return (T) proxies.getUnchecked(rpcService).getProxy();
+    }
+
+    private RpcServiceAdapter createProxy(final Class<? extends RpcService> key) {
+        Preconditions.checkArgument(BindingReflections.isBindingClass(key));
+        Preconditions.checkArgument(key.isInterface(), "Supplied RPC service type must be interface.");
+        return new RpcServiceAdapter(key, codec, domService);
+    }
+
+    private static final class Builder extends BindingDOMAdapterBuilder<RpcConsumerRegistry> {
+
+        @Override
+        protected RpcConsumerRegistry createInstance(final BindingToNormalizedNodeCodec codec,
+                final ClassToInstanceMap<DOMService> delegates) {
+            final DOMRpcService domRpc  = delegates.getInstance(DOMRpcService.class);
+            return new BindingDOMRpcServiceAdapter(domRpc  , codec);
+        }
+
+        @Override
+        public Set<? extends Class<? extends DOMService>> getRequiredDelegates() {
+            return ImmutableSet.of(DOMRpcService.class);
+        }
+
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMTransactionChainAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMTransactionChainAdapter.java
new file mode 100644 (file)
index 0000000..4dc55d2
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+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.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.Delegator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class BindingDOMTransactionChainAdapter implements BindingTransactionChain, Delegator<DOMTransactionChain> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(BindingDOMTransactionChainAdapter.class);
+
+    private final DOMTransactionChain delegate;
+    private final BindingToNormalizedNodeCodec codec;
+    private final DelegateChainListener domListener;
+    private final TransactionChainListener bindingListener;
+
+    public BindingDOMTransactionChainAdapter(final DOMDataBroker chainFactory,
+            final BindingToNormalizedNodeCodec codec, final TransactionChainListener listener) {
+        Preconditions.checkNotNull(chainFactory, "DOM Transaction chain factory must not be null");
+        this.domListener = new DelegateChainListener();
+        this.bindingListener = listener;
+        this.delegate = chainFactory.createTransactionChain(domListener);
+        this.codec = codec;
+    }
+
+    @Override
+    public DOMTransactionChain getDelegate() {
+        return delegate;
+    }
+
+    @Override
+    public ReadOnlyTransaction newReadOnlyTransaction() {
+        final DOMDataReadOnlyTransaction delegateTx = delegate.newReadOnlyTransaction();
+        return new BindingDOMReadTransactionAdapter(delegateTx, codec);
+    }
+
+    @Override
+    public ReadWriteTransaction newReadWriteTransaction() {
+        final DOMDataReadWriteTransaction delegateTx = delegate.newReadWriteTransaction();
+        return new BindingDOMReadWriteTransactionAdapter(delegateTx, codec) {
+
+            @Override
+            public CheckedFuture<Void, TransactionCommitFailedException> submit() {
+                return listenForFailure(this,super.submit());
+            }
+
+        };
+    }
+
+    @Override
+    public WriteTransaction newWriteOnlyTransaction() {
+        final DOMDataWriteTransaction delegateTx = delegate.newWriteOnlyTransaction();
+        return new BindingDOMWriteTransactionAdapter<DOMDataWriteTransaction>(delegateTx, codec) {
+
+            @Override
+            public CheckedFuture<Void,TransactionCommitFailedException> submit() {
+                return listenForFailure(this,super.submit());
+            }
+
+        };
+    }
+
+    private CheckedFuture<Void, TransactionCommitFailedException> listenForFailure(
+            final WriteTransaction tx, final CheckedFuture<Void, TransactionCommitFailedException> future) {
+        Futures.addCallback(future, new FutureCallback<Void>() {
+            @Override
+            public void onFailure(final Throwable t) {
+                failTransactionChain(tx,t);
+            }
+
+            @Override
+            public void onSuccess(final Void result) {
+                // Intentionally NOOP
+            }
+        });
+
+        return future;
+    }
+
+    private void failTransactionChain(final WriteTransaction tx, final Throwable t) {
+        /*
+         *  We asume correct state change for underlaying transaction
+         *
+         * chain, so we are not changing any of our internal state
+         * to mark that we failed.
+         */
+        this.bindingListener.onTransactionChainFailed(this, tx, t);
+    }
+
+    @Override
+    public void close() {
+        delegate.close();
+    }
+
+    private final class DelegateChainListener implements TransactionChainListener {
+
+        @Override
+        public void onTransactionChainFailed(final TransactionChain<?, ?> chain,
+                final AsyncTransaction<?, ?> transaction, final Throwable cause) {
+            Preconditions.checkState(delegate.equals(chain),
+                    "Illegal state - listener for %s was invoked for incorrect chain %s.", delegate, chain);
+            /*
+             * Intentionally NOOP, callback for failure, since we
+             * are also listening on each transaction future for failure,
+             * in order to have reference to Binding Transaction (which was seen by client
+             * of this transaction chain), instead of DOM transaction
+             * which is known only to this chain, binding transaction implementation
+             * and underlying transaction chain.
+             *
+             */
+            LOG.debug("Transaction chain {} failed. Failed DOM Transaction {}",this,transaction,cause);
+        }
+
+        @Override
+        public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
+            Preconditions.checkState(delegate.equals(chain),
+                    "Illegal state - listener for %s was invoked for incorrect chain %s.", delegate, chain);
+            bindingListener.onTransactionChainSuccessful(BindingDOMTransactionChainAdapter.this);
+        }
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMWriteTransactionAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMWriteTransactionAdapter.java
new file mode 100644 (file)
index 0000000..88c8bf5
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+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.common.impl.service.AbstractDataTransaction;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+
+class BindingDOMWriteTransactionAdapter<T extends DOMDataWriteTransaction> extends
+        AbstractWriteTransaction<T> implements WriteTransaction {
+
+    protected BindingDOMWriteTransactionAdapter(final T delegateTx, final BindingToNormalizedNodeCodec codec) {
+        super(delegateTx, codec);
+    }
+
+    @Override
+    public <U extends DataObject> void put(final LogicalDatastoreType store, final InstanceIdentifier<U> path,
+                                           final U data) {
+        put(store, path, data,false);
+    }
+
+    @Override
+    public <D extends DataObject> void merge(final LogicalDatastoreType store, final InstanceIdentifier<D> path,
+                                             final D data) {
+        merge(store, path, data,false);
+    }
+
+
+    @Override
+    protected void ensureParentsByMerge(final LogicalDatastoreType store,
+            final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalizedPath, final InstanceIdentifier<?> path) {
+        List<PathArgument> currentArguments = new ArrayList<>();
+        DataNormalizationOperation<?> currentOp = getCodec().getDataNormalizer().getRootOperation();
+        Iterator<PathArgument> iterator = normalizedPath.getPathArguments().iterator();
+        while (iterator.hasNext()) {
+            PathArgument currentArg = iterator.next();
+            try {
+                currentOp = currentOp.getChild(currentArg);
+            } catch (DataNormalizationException e) {
+                throw new IllegalArgumentException(String.format("Invalid child encountered in path %s", path), e);
+            }
+            currentArguments.add(currentArg);
+            org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier currentPath = org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.create(
+                    currentArguments);
+
+            getDelegate().merge(store, currentPath, currentOp.createDefault(currentArg));
+        }
+    }
+
+    @Override
+    public void delete(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
+        doDelete( store, path);
+    }
+
+    @Override
+    public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+        return AbstractDataTransaction.convertToLegacyCommitFuture(submit());
+    }
+
+    @Override
+    public CheckedFuture<Void,TransactionCommitFailedException> submit() {
+        return doSubmit();
+    }
+
+    @Override
+    public boolean cancel() {
+        return doCancel();
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDataTreeChangeListenerRegistration.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDataTreeChangeListenerRegistration.java
new file mode 100644 (file)
index 0000000..8a92e5f
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+class BindingDataTreeChangeListenerRegistration<L extends DataTreeChangeListener<?>> extends AbstractListenerRegistration<L> {
+
+    private final ListenerRegistration<?> domReg;
+
+    BindingDataTreeChangeListenerRegistration(final L listener, final ListenerRegistration<?> domReg) {
+        super(listener);
+        this.domReg = Preconditions.checkNotNull(domReg);
+    }
+
+    @Override
+    protected void removeRegistration() {
+        domReg.close();
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingMountPointAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingMountPointAdapter.java
new file mode 100644 (file)
index 0000000..372771f
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.LoadingCache;
+import org.opendaylight.controller.md.sal.binding.api.BindingService;
+import org.opendaylight.controller.md.sal.binding.api.MountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMService;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class BindingMountPointAdapter implements MountPoint {
+
+    private final InstanceIdentifier<?> identifier;
+    private LoadingCache<Class<? extends BindingService>, Optional<BindingService>> services;
+
+    public BindingMountPointAdapter(final BindingToNormalizedNodeCodec codec, final DOMMountPoint domMountPoint) {
+        identifier = codec.getCodecRegistry().fromYangInstanceIdentifier(domMountPoint.getIdentifier());
+        services = CacheBuilder.newBuilder().build(new BindingDOMAdapterLoader(codec) {
+
+            @Override
+            protected DOMService getDelegate(Class<? extends DOMService> reqDeleg) {
+                return domMountPoint.getService(reqDeleg).orNull();
+            }
+        });
+    }
+
+    @Override
+    public InstanceIdentifier<?> getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public <T extends BindingService> Optional<T> getService(Class<T> service) {
+        Optional<BindingService> potential = services.getUnchecked(service);
+        if(potential.isPresent()) {
+            return Optional.of(service.cast(potential.get()));
+        }
+        return Optional.absent();
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingRpcAdapterRegistration.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingRpcAdapterRegistration.java
new file mode 100644 (file)
index 0000000..5fade2c
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationRegistration;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+class BindingRpcAdapterRegistration<T extends RpcService> extends AbstractObjectRegistration<T>{
+
+    private final DOMRpcImplementationRegistration<?> reg;
+
+    public BindingRpcAdapterRegistration(T instance, DOMRpcImplementationRegistration<?> reg) {
+        super(instance);
+        this.reg = reg;
+    }
+
+    @Override
+    protected void removeRegistration() {
+        reg.close();
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingStructuralType.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingStructuralType.java
new file mode 100644 (file)
index 0000000..7cd17dc
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+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.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+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.DataTreeCandidateNode;
+
+/**
+ *
+ * Defines structural mapping of Normalized Node to Binding data
+ * addressable by Instance Identifier.
+ *
+ * Not all binding data are addressable by instance identifier
+ * and there are some differences.
+ *
+ * See {@link #NOT_ADDRESSABLE},{@link #INVISIBLE_CONTAINER},{@link #VISIBLE_CONTAINER}
+ * for more details.
+ *
+ *
+ */
+enum BindingStructuralType {
+
+    /**
+     * DOM Item is not addressable in Binding Instance Identifier,
+     * data is not lost, but are available only via parent object.
+     *
+     * Such types of data are leaf-lists, leafs, list without keys
+     * or anyxml.
+     *
+     */
+    NOT_ADDRESSABLE,
+    /**
+     * Data container is addressable in NormalizedNode format,
+     * but in Binding it is not represented in Instance Identifier.
+     *
+     * This are choice / case nodes.
+     *
+     * This data is still accessible using parent object and their
+     * children are addressable.
+     *
+     */
+    INVISIBLE_CONTAINER,
+    /**
+     * Data container is addressable in NormalizedNode format,
+     * but in Binding it is not represented in Instance Identifier.
+     *
+     * This are list nodes.
+     *
+     * This data is still accessible using parent object and their
+     * children are addressable.
+     *
+     */
+    INVISIBLE_LIST,
+    /**
+     * Data container is addressable in Binding Instance Identifier format
+     * and also YangInstanceIdentifier format.
+     *
+     */
+    VISIBLE_CONTAINER,
+    /**
+     * Mapping algorithm was unable to detect type or was not updated after introduction
+     * of new NormalizedNode type.
+     */
+    UNKNOWN;
+
+    static BindingStructuralType from(final DataTreeCandidateNode domChildNode) {
+        final Optional<NormalizedNode<?, ?>> dataBased = domChildNode.getDataAfter().or(domChildNode.getDataBefore());
+        if(dataBased.isPresent()) {
+            return from(dataBased.get());
+        }
+        return from(domChildNode.getIdentifier());
+    }
+
+    private static BindingStructuralType from(final PathArgument identifier) {
+        if(identifier instanceof NodeIdentifierWithPredicates || identifier instanceof AugmentationIdentifier) {
+            return VISIBLE_CONTAINER;
+        }
+        if(identifier instanceof NodeWithValue) {
+            return NOT_ADDRESSABLE;
+        }
+        return UNKNOWN;
+    }
+
+    static BindingStructuralType from(final NormalizedNode<?, ?> data) {
+        if(isNotAddressable(data)) {
+            return NOT_ADDRESSABLE;
+        }
+        if(data instanceof MapNode) {
+            return INVISIBLE_LIST;
+        }
+        if(data instanceof ChoiceNode) {
+            return INVISIBLE_CONTAINER;
+        }
+        if(isVisibleContainer(data)) {
+            return VISIBLE_CONTAINER;
+        }
+        return UNKNOWN;
+    }
+
+    private static boolean isVisibleContainer(final NormalizedNode<?, ?> data) {
+        return data instanceof MapEntryNode || data instanceof ContainerNode || data instanceof AugmentationNode;
+    }
+
+    private static boolean isNotAddressable(final NormalizedNode<?, ?> d) {
+        return d instanceof LeafNode
+                || d instanceof AnyXmlNode
+                || d instanceof LeafSetNode
+                || d instanceof LeafSetEntryNode;
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingToNormalizedNodeCodec.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingToNormalizedNodeCodec.java
new file mode 100644 (file)
index 0000000..845f422
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableBiMap;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTree;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTreeFactory;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTreeNode;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.binding.data.codec.impl.MissingSchemaException;
+import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy;
+import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext;
+import org.opendaylight.yangtools.yang.binding.BindingMapping;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+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.codec.DeserializationException;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class BindingToNormalizedNodeCodec implements BindingCodecTreeFactory, BindingNormalizedNodeSerializer, SchemaContextListener, AutoCloseable {
+
+    private static final long WAIT_DURATION_SEC = 5;
+    private static final Logger LOG = LoggerFactory.getLogger(BindingToNormalizedNodeCodec.class);
+
+    private final BindingNormalizedNodeCodecRegistry codecRegistry;
+
+    private final GeneratedClassLoadingStrategy classLoadingStrategy;
+    private final FutureSchema futureSchema;
+    private final LoadingCache<InstanceIdentifier<?>, YangInstanceIdentifier> iiCache = CacheBuilder.newBuilder()
+            .softValues().build(new CacheLoader<InstanceIdentifier<?>, YangInstanceIdentifier>() {
+
+                @Override
+                public YangInstanceIdentifier load(final InstanceIdentifier<?> key) throws Exception {
+                    return toYangInstanceIdentifierBlocking(key);
+                }
+
+            });
+
+    private BindingRuntimeContext runtimeContext;
+    private DataNormalizer legacyToNormalized;
+
+    public BindingToNormalizedNodeCodec(final GeneratedClassLoadingStrategy classLoadingStrategy,
+            final BindingNormalizedNodeCodecRegistry codecRegistry) {
+        this(classLoadingStrategy,codecRegistry,false);
+
+    }
+
+    public BindingToNormalizedNodeCodec(final GeneratedClassLoadingStrategy classLoadingStrategy,
+            final BindingNormalizedNodeCodecRegistry codecRegistry,final boolean waitForSchema) {
+        this.classLoadingStrategy = Preconditions.checkNotNull(classLoadingStrategy,"classLoadingStrategy");
+        this.codecRegistry = Preconditions.checkNotNull(codecRegistry,"codecRegistry");
+        this.futureSchema = waitForSchema ? new FutureSchema(WAIT_DURATION_SEC, TimeUnit.SECONDS) : null;
+    }
+
+    YangInstanceIdentifier toYangInstanceIdentifierBlocking(final InstanceIdentifier<? extends DataObject> binding) {
+        try {
+            return codecRegistry.toYangInstanceIdentifier(binding);
+        } catch (final MissingSchemaException e) {
+            waitForSchema(decompose(binding),e);
+            return codecRegistry.toYangInstanceIdentifier(binding);
+        }
+    }
+
+    /**
+     * Translates supplied Binding Instance Identifier into NormalizedNode
+     * instance identifier.
+     *
+     * @param binding
+     *            Binding Instance Identifier
+     * @return DOM Instance Identifier
+     * @throws IllegalArgumentException
+     *             If supplied Instance Identifier is not valid.
+     */
+    public YangInstanceIdentifier toNormalized(final InstanceIdentifier<? extends DataObject> binding) {
+        return codecRegistry.toYangInstanceIdentifier(binding);
+    }
+
+    @Override
+    public YangInstanceIdentifier toYangInstanceIdentifier(final InstanceIdentifier<?> binding) {
+        return codecRegistry.toYangInstanceIdentifier(binding);
+    }
+
+
+    YangInstanceIdentifier toYangInstanceIdentifierCached(final InstanceIdentifier<?> binding) {
+        return iiCache .getUnchecked(binding);
+    }
+
+    @Override
+    public <T extends DataObject> Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
+            final InstanceIdentifier<T> path, final T data) {
+        return codecRegistry.toNormalizedNode(path, data);
+    }
+
+    /**
+     * Converts Binding Map.Entry to DOM Map.Entry
+     *
+     * Same as {@link #toNormalizedNode(InstanceIdentifier, DataObject)}.
+     *
+     * @param binding Map Entry with InstanceIdentifier as key and DataObject as value.
+     * @return DOM Map Entry with {@link YangInstanceIdentifier} as key and {@link NormalizedNode}
+     *         as value.
+     */
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    public Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
+            final Entry<InstanceIdentifier<? extends DataObject>, DataObject> binding) {
+        return toNormalizedNode((InstanceIdentifier) binding.getKey(),binding.getValue());
+    }
+
+    @Override
+    public Entry<InstanceIdentifier<?>, DataObject> fromNormalizedNode(final YangInstanceIdentifier path,
+            final NormalizedNode<?, ?> data) {
+        return codecRegistry.fromNormalizedNode(path, data);
+    }
+
+    @Override
+    public Notification fromNormalizedNodeNotification(final SchemaPath path, final ContainerNode data) {
+        return codecRegistry.fromNormalizedNodeNotification(path, data);
+    }
+
+    @Override
+    public DataObject fromNormalizedNodeRpcData(final SchemaPath path, final ContainerNode data) {
+        return codecRegistry.fromNormalizedNodeRpcData(path, data);
+    }
+
+    @Override
+    public InstanceIdentifier<?> fromYangInstanceIdentifier(final YangInstanceIdentifier dom) {
+        return codecRegistry.fromYangInstanceIdentifier(dom);
+    }
+
+    @Override
+    public ContainerNode toNormalizedNodeNotification(final Notification data) {
+        return codecRegistry.toNormalizedNodeNotification(data);
+    }
+
+    @Override
+    public ContainerNode toNormalizedNodeRpcData(final DataContainer data) {
+        return codecRegistry.toNormalizedNodeRpcData(data);
+    }
+
+    /**
+     *
+     * Returns a Binding-Aware instance identifier from normalized
+     * instance-identifier if it is possible to create representation.
+     *
+     * Returns Optional.absent for cases where target is mixin node except
+     * augmentation.
+     *
+     */
+    public Optional<InstanceIdentifier<? extends DataObject>> toBinding(final YangInstanceIdentifier normalized)
+                    throws DeserializationException {
+        try {
+            return Optional.<InstanceIdentifier<? extends DataObject>>fromNullable(codecRegistry.fromYangInstanceIdentifier(normalized));
+        } catch (final IllegalArgumentException e) {
+            return Optional.absent();
+        }
+    }
+
+    public DataNormalizer getDataNormalizer() {
+        return legacyToNormalized;
+    }
+
+    public Optional<Entry<InstanceIdentifier<? extends DataObject>, DataObject>> toBinding(
+            @Nonnull final Entry<YangInstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized)
+                    throws DeserializationException {
+        try {
+            /*
+             * This cast is required, due to generics behaviour in openjdk / oracle javac
+             *
+             * InstanceIdentifier has definition InstanceIdentifier<T extends DataObject>,
+             * this means '?' is always Â <? extends DataObject>. Eclipse compiler
+             * is able to determine this relationship and treats
+             * Entry<InstanceIdentifier<?>,DataObject> and Entry<InstanceIdentifier<? extends DataObject,DataObject>
+             * as assignable. However openjdk / oracle javac treats this two types
+             * as incompatible and issues a compile error.
+             *
+             * It is safe to  loose generic information and cast it to other generic signature.
+             *
+             */
+            @SuppressWarnings("unchecked")
+            final Entry<InstanceIdentifier<? extends DataObject>, DataObject> binding = Entry.class.cast(codecRegistry.fromNormalizedNode(normalized.getKey(), normalized.getValue()));
+            return Optional.fromNullable(binding);
+        } catch (final IllegalArgumentException e) {
+            return Optional.absent();
+        }
+    }
+
+    @Override
+    public void onGlobalContextUpdated(final SchemaContext arg0) {
+        legacyToNormalized = new DataNormalizer(arg0);
+        runtimeContext = BindingRuntimeContext.create(classLoadingStrategy, arg0);
+        codecRegistry.onBindingRuntimeContextUpdated(runtimeContext);
+        if(futureSchema != null) {
+            futureSchema.onRuntimeContextUpdated(runtimeContext);
+        }
+    }
+
+    public <T extends DataObject> Function<Optional<NormalizedNode<?, ?>>, Optional<T>>  deserializeFunction(final InstanceIdentifier<T> path) {
+        return codecRegistry.deserializeFunction(path);
+    }
+
+    /**
+     * Returns an default object according to YANG schema for supplied path.
+     *
+     * @param path DOM Path
+     * @return Node with defaults set on.
+     */
+    public NormalizedNode<?, ?> getDefaultNodeFor(final YangInstanceIdentifier path) {
+        final Iterator<PathArgument> iterator = path.getPathArguments().iterator();
+        DataNormalizationOperation<?> currentOp = legacyToNormalized.getRootOperation();
+        while (iterator.hasNext()) {
+            final PathArgument currentArg = iterator.next();
+            try {
+                currentOp = currentOp.getChild(currentArg);
+            } catch (final DataNormalizationException e) {
+                throw new IllegalArgumentException(String.format("Invalid child encountered in path %s", path), e);
+            }
+        }
+        return currentOp.createDefault(path.getLastPathArgument());
+    }
+
+    public BindingNormalizedNodeCodecRegistry getCodecRegistry() {
+        return codecRegistry;
+    }
+
+    @Override
+    public void close() {
+        // NOOP Intentionally
+    }
+
+    public BindingNormalizedNodeCodecRegistry getCodecFactory() {
+        return codecRegistry;
+    }
+
+    // FIXME: This should be probably part of Binding Runtime context
+    public ImmutableBiMap<Method, SchemaPath> getRpcMethodToSchemaPath(final Class<? extends RpcService> key) {
+        final Module module = getModuleBlocking(key);
+        final ImmutableBiMap.Builder<Method, SchemaPath> ret = ImmutableBiMap.<Method, SchemaPath>builder();
+        try {
+            for (final RpcDefinition rpcDef : module.getRpcs()) {
+                final Method method = findRpcMethod(key, rpcDef);
+                ret.put(method, rpcDef.getPath());
+            }
+        } catch (final NoSuchMethodException e) {
+            throw new IllegalStateException("Rpc defined in model does not have representation in generated class.", e);
+        }
+        return ret.build();
+    }
+
+    protected ImmutableBiMap<Method, RpcDefinition> getRpcMethodToSchema(final Class<? extends RpcService> key) {
+        final Module module = getModuleBlocking(key);
+        final ImmutableBiMap.Builder<Method, RpcDefinition> ret = ImmutableBiMap.builder();
+        try {
+            for (final RpcDefinition rpcDef : module.getRpcs()) {
+                final Method method = findRpcMethod(key, rpcDef);
+                ret.put(method, rpcDef);
+            }
+        } catch (final NoSuchMethodException e) {
+            throw new IllegalStateException("Rpc defined in model does not have representation in generated class.", e);
+        }
+        return ret.build();
+    }
+
+    private Module getModuleBlocking(final Class<?> modeledClass) {
+        final QNameModule moduleName = BindingReflections.getQNameModule(modeledClass);
+        final URI namespace = moduleName.getNamespace();
+        final Date revision = moduleName.getRevision();
+        Module module = runtimeContext.getSchemaContext().findModuleByNamespaceAndRevision(namespace, revision);
+        if(module == null && futureSchema != null && futureSchema.waitForSchema(namespace,revision)) {
+            module = runtimeContext.getSchemaContext().findModuleByNamespaceAndRevision(namespace, revision);
+        }
+        Preconditions.checkState(module != null, "Schema for %s is not available.", modeledClass);
+        return module;
+    }
+
+    private void waitForSchema(final Collection<Class<?>> binding, final MissingSchemaException e) {
+        if(futureSchema != null) {
+            LOG.warn("Blocking thread to wait for schema convergence updates for {} {}",futureSchema.getDuration(), futureSchema.getUnit());
+            if(!futureSchema.waitForSchema(binding)) {
+                return;
+            }
+        }
+        throw e;
+    }
+
+    private Method findRpcMethod(final Class<? extends RpcService> key, final RpcDefinition rpcDef) throws NoSuchMethodException {
+        final String methodName = BindingMapping.getMethodName(rpcDef.getQName());
+        if(rpcDef.getInput() != null) {
+            final Class<?> inputClz = runtimeContext.getClassForSchema(rpcDef.getInput());
+            return key.getMethod(methodName, inputClz);
+        }
+        return key.getMethod(methodName);
+    }
+
+    @Override
+    public BindingCodecTree create(final BindingRuntimeContext context) {
+        return codecRegistry.create(context);
+    }
+
+    @Override
+    public BindingCodecTree create(final SchemaContext context, final Class<?>... bindingClasses) {
+        return codecRegistry.create(context, bindingClasses);
+    }
+
+    @Nonnull
+    protected Map.Entry<InstanceIdentifier<?>, BindingCodecTreeNode<?>> getSubtreeCodec(
+            final YangInstanceIdentifier domIdentifier) {
+
+        final BindingCodecTree currentCodecTree = codecRegistry.getCodecContext();
+        final InstanceIdentifier<?> bindingPath = codecRegistry.fromYangInstanceIdentifier(domIdentifier);
+        Preconditions.checkArgument(bindingPath != null);
+        /**
+         * If we are able to deserialize YANG instance identifier, getSubtreeCodec must
+         * return non-null value.
+         */
+        final BindingCodecTreeNode<?> codecContext = currentCodecTree.getSubtreeCodec(bindingPath);
+        return new SimpleEntry<InstanceIdentifier<?>, BindingCodecTreeNode<?>>(bindingPath, codecContext);
+    }
+
+    @SuppressWarnings("unchecked")
+    public Set<Class<? extends Notification>> getNotificationClasses(final Set<SchemaPath> interested) {
+        final Set<Class<? extends Notification>> result = new HashSet<>();
+        final Set<NotificationDefinition> knownNotifications = runtimeContext.getSchemaContext().getNotifications();
+        for (final NotificationDefinition notification : knownNotifications) {
+            if (interested.contains(notification.getPath())) {
+                try {
+                    result.add((Class<? extends Notification>) runtimeContext.getClassForSchema(notification));
+                } catch (final IllegalStateException e) {
+                    // Ignore
+                    LOG.warn("Class for {} is currently not known.",notification.getPath(),e);
+                }
+            }
+        }
+        return result;
+    }
+
+    private static Collection<Class<?>> decompose(final InstanceIdentifier<?> path) {
+        final Set<Class<?>> clazzes = new HashSet<>();
+        for(final InstanceIdentifier.PathArgument arg : path.getPathArguments()) {
+            clazzes.add(arg.getType());
+        }
+        return clazzes;
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ContextReferenceExtractor.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ContextReferenceExtractor.java
new file mode 100644 (file)
index 0000000..84f1244
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import java.lang.reflect.Method;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+abstract class ContextReferenceExtractor {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ContextReferenceExtractor.class);
+    private static final ContextReferenceExtractor NULL_EXTRACTOR = new ContextReferenceExtractor() {
+
+        @Override
+        InstanceIdentifier<?> extract(final DataObject obj) {
+            return null;
+        }
+    };
+
+
+    private static final LoadingCache<Class<?>, ContextReferenceExtractor> EXTRACTORS = CacheBuilder.newBuilder()
+            .weakKeys().build(new CacheLoader<Class<?>, ContextReferenceExtractor>() {
+
+                @Override
+                public ContextReferenceExtractor load(final Class<?> key) throws Exception {
+                    return create(key);
+                }
+            });
+
+
+    private static final String GET_VALUE_NAME = "getValue";
+
+    static ContextReferenceExtractor from(final Class<?> obj) {
+        return EXTRACTORS.getUnchecked(obj);
+    }
+
+    /**
+     * Extract context-reference (Instance Identifier) from
+     * Binding DataObject.
+     *
+     * @param obj DataObject from which context reference
+     * should be extracted.
+     *
+     * @return Instance Identifier representing context reference
+     * or null, if data object does not contain context reference.
+     */
+    abstract @Nullable InstanceIdentifier<?> extract(DataObject obj);
+
+    @Nonnull
+    private static ContextReferenceExtractor create(final Class<?> key) {
+        final Method contextGetter = getContextGetter(key);
+        if (contextGetter == null) {
+            return NULL_EXTRACTOR;
+        }
+        final Class<?> returnType = contextGetter.getReturnType();
+        try {
+            if (InstanceIdentifier.class.isAssignableFrom(returnType)) {
+                return DirectGetterRouteContextExtractor.create(contextGetter);
+            }
+            final Method getValueMethod = findGetValueMethod(returnType,InstanceIdentifier.class);
+            if (getValueMethod != null) {
+                return GetValueRouteContextExtractor.create(contextGetter, getValueMethod);
+            } else {
+                LOG.warn("Class {} can not be used to determine context, falling back to NULL_EXTRACTOR.",returnType);
+            }
+        } catch (final IllegalAccessException e) {
+            LOG.warn("Class {} does not conform to Binding Specification v1. Falling back to NULL_EXTRACTOR", e);
+        }
+        return NULL_EXTRACTOR;
+    }
+
+    @Nullable
+    private static Method findGetValueMethod(final Class<?> type, final Class<?> returnType) {
+        try {
+            final Method method = type.getMethod(GET_VALUE_NAME);
+            if(returnType.equals(method.getReturnType())) {
+                return method;
+            }
+        } catch (final NoSuchMethodException e) {
+            LOG.warn("Value class {} does not comform to Binding Specification v1.", type, e);
+        }
+        return null;
+    }
+
+    private static Method getContextGetter(final Class<?> key) {
+        for (final Method method : key.getMethods()) {
+            if (method.getAnnotation(RoutingContext.class) != null) {
+                return method;
+            }
+        }
+        return null;
+    }
+
+
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/DirectGetterRouteContextExtractor.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/DirectGetterRouteContextExtractor.java
new file mode 100644 (file)
index 0000000..599dfcc
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Throwables;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+final class DirectGetterRouteContextExtractor extends ContextReferenceExtractor {
+
+    private final static Lookup PUBLIC_LOOKUP = MethodHandles.publicLookup();
+    private final MethodHandle handle;
+
+    private DirectGetterRouteContextExtractor(final MethodHandle rawHandle) {
+        handle = rawHandle.asType(MethodType.methodType(InstanceIdentifier.class, DataObject.class));
+    }
+
+    static final ContextReferenceExtractor create(final Method getterMethod) throws IllegalAccessException {
+        final MethodHandle getterHandle = PUBLIC_LOOKUP.unreflect(getterMethod);
+        return new DirectGetterRouteContextExtractor(getterHandle);
+    }
+
+    @Override
+    InstanceIdentifier<?> extract(final DataObject obj) {
+        try {
+            return (InstanceIdentifier<?>) handle.invokeExact(obj);
+        } catch (final Throwable e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/FutureSchema.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/FutureSchema.java
new file mode 100644 (file)
index 0000000..2898ba7
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Throwables;
+import com.google.common.util.concurrent.SettableFuture;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+
+class FutureSchema implements AutoCloseable {
+
+    private final List<FutureSchemaPredicate> postponedOperations = new CopyOnWriteArrayList<>();
+    private final long duration;
+    private final TimeUnit unit;
+
+    protected FutureSchema(final long time, final TimeUnit unit) {
+        this.duration = time;
+        this.unit = unit;
+    }
+
+    void onRuntimeContextUpdated(final BindingRuntimeContext context) {
+        for (final FutureSchemaPredicate op : postponedOperations) {
+            op.unlockIfPossible(context);
+        }
+    }
+
+    long getDuration() {
+        return duration;
+    }
+
+    TimeUnit getUnit() {
+        return unit;
+    }
+
+    @Override
+    public void close() {
+        for (final FutureSchemaPredicate op : postponedOperations) {
+            op.cancel();
+        }
+    }
+
+    private static boolean isSchemaAvailable(final Class<?> clz, final BindingRuntimeContext context) {
+        final Object schema;
+        if (Augmentation.class.isAssignableFrom(clz)) {
+            schema = context.getAugmentationDefinition(clz);
+        } else {
+            schema = context.getSchemaDefinition(clz);
+        }
+        return schema != null;
+    }
+
+    boolean waitForSchema(final URI namespace, final Date revision) {
+        final FutureSchemaPredicate postponedOp = new FutureSchemaPredicate() {
+
+            @Override
+            public boolean apply(final BindingRuntimeContext input) {
+                return input.getSchemaContext().findModuleByNamespaceAndRevision(namespace, revision) != null;
+            }
+        };
+        return postponedOp.waitForSchema();
+    }
+
+    boolean waitForSchema(final Collection<Class<?>> bindingClasses) {
+        final FutureSchemaPredicate postponedOp = new FutureSchemaPredicate() {
+
+            @Override
+            public boolean apply(final BindingRuntimeContext context) {
+                for (final Class<?> clz : bindingClasses) {
+                    if (!isSchemaAvailable(clz, context)) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+        };
+        return postponedOp.waitForSchema();
+    }
+
+    private abstract class FutureSchemaPredicate implements Predicate<BindingRuntimeContext> {
+
+        final boolean waitForSchema() {
+            try {
+                schemaPromise.get(duration, unit);
+                return true;
+            } catch (final InterruptedException | ExecutionException e) {
+                throw Throwables.propagate(e);
+            } catch (final TimeoutException e) {
+                return false;
+            } finally {
+                postponedOperations.remove(this);
+            }
+        }
+
+        final void unlockIfPossible(final BindingRuntimeContext context) {
+            if (!schemaPromise.isDone() && apply(context)) {
+                schemaPromise.set(null);
+            }
+        }
+
+        final void cancel() {
+            schemaPromise.cancel(true);
+        }
+
+        private final SettableFuture<?> schemaPromise = SettableFuture.create();
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/GetValueRouteContextExtractor.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/GetValueRouteContextExtractor.java
new file mode 100644 (file)
index 0000000..05725a1
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Throwables;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+final class GetValueRouteContextExtractor extends ContextReferenceExtractor {
+
+    private final static Lookup PUBLIC_LOOKUP = MethodHandles.publicLookup();
+    private final MethodHandle contextHandle;
+    private final MethodHandle valueHandle;
+
+    private GetValueRouteContextExtractor(final MethodHandle rawContextHandle, final MethodHandle rawValueHandle) {
+        contextHandle = rawContextHandle.asType(MethodType.methodType(Object.class, DataObject.class));
+        valueHandle = rawValueHandle.asType(MethodType.methodType(InstanceIdentifier.class, Object.class));
+    }
+
+    public static ContextReferenceExtractor create(final Method contextGetter, final Method getValueMethod)
+            throws IllegalAccessException {
+        final MethodHandle rawContextHandle = PUBLIC_LOOKUP.unreflect(contextGetter);
+        final MethodHandle rawValueHandle = PUBLIC_LOOKUP.unreflect(getValueMethod);
+        return new GetValueRouteContextExtractor(rawContextHandle, rawValueHandle);
+    }
+
+    @Override
+    InstanceIdentifier<?> extract(final DataObject obj) {
+        try {
+            final Object ctx = contextHandle.invokeExact(obj);
+            if (ctx != null) {
+                return (InstanceIdentifier<?>) valueHandle.invokeExact(ctx);
+            }
+            return null;
+        } catch (final Throwable e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/LazyDOMRpcResultFuture.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/LazyDOMRpcResultFuture.java
new file mode 100644 (file)
index 0000000..4c938a9
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+final class LazyDOMRpcResultFuture implements CheckedFuture<DOMRpcResult, DOMRpcException> {
+
+    private final ListenableFuture<RpcResult<?>> bindingFuture;
+    private final BindingNormalizedNodeCodecRegistry codec;
+    private volatile DOMRpcResult result;
+
+    private LazyDOMRpcResultFuture(final ListenableFuture<RpcResult<?>> delegate,
+            final BindingNormalizedNodeCodecRegistry codec) {
+        this.bindingFuture = Preconditions.checkNotNull(delegate, "delegate");
+        this.codec = Preconditions.checkNotNull(codec, "codec");
+    }
+
+    static CheckedFuture<DOMRpcResult, DOMRpcException> create(final BindingNormalizedNodeCodecRegistry codec,
+            final ListenableFuture<RpcResult<?>> bindingResult) {
+        return new LazyDOMRpcResultFuture(bindingResult, codec);
+    }
+
+    ListenableFuture<RpcResult<?>> getBindingFuture() {
+        return bindingFuture;
+    }
+
+    @Override
+    public boolean cancel(final boolean mayInterruptIfRunning) {
+        return bindingFuture.cancel(mayInterruptIfRunning);
+    }
+
+    @Override
+    public void addListener(final Runnable listener, final Executor executor) {
+        bindingFuture.addListener(listener, executor);
+    }
+
+    @Override
+    public DOMRpcResult get() throws InterruptedException, ExecutionException {
+        if (result != null) {
+            return result;
+        }
+        return transformIfNecessary(bindingFuture.get());
+    }
+
+    @Override
+    public DOMRpcResult get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException,
+            TimeoutException {
+        if (result != null) {
+            return result;
+        }
+        return transformIfNecessary(bindingFuture.get(timeout, unit));
+    }
+
+    @Override
+    public DOMRpcResult checkedGet() throws DOMRpcException {
+        try {
+            return get();
+        } catch (InterruptedException | ExecutionException e) {
+            // FIXME: Add exception mapping
+            throw Throwables.propagate(e);
+        }
+    }
+
+    @Override
+    public DOMRpcResult checkedGet(final long timeout, final TimeUnit unit) throws TimeoutException, DOMRpcException {
+        try {
+            return get(timeout, unit);
+        } catch (InterruptedException | ExecutionException e) {
+            // FIXME: Add exception mapping
+            throw Throwables.propagate(e);
+        }
+    }
+
+    @Override
+    public boolean isCancelled() {
+        return bindingFuture.isCancelled();
+    }
+
+    @Override
+    public boolean isDone() {
+        return bindingFuture.isDone();
+    }
+
+    private synchronized DOMRpcResult transformIfNecessary(final RpcResult<?> input) {
+        if (result == null) {
+            result = transform(input);
+        }
+        return result;
+    }
+
+    private DOMRpcResult transform(final RpcResult<?> input) {
+        if (input.isSuccessful()) {
+            final Object inputData = input.getResult();
+            if (inputData instanceof DataContainer) {
+                return new DefaultDOMRpcResult(codec.toNormalizedNodeRpcData((DataContainer) inputData));
+            } else {
+                return new DefaultDOMRpcResult((NormalizedNode<?, ?>) null);
+            }
+        }
+        return new DefaultDOMRpcResult(input.getErrors());
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/LazyDataObjectModification.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/LazyDataObjectModification.java
new file mode 100644 (file)
index 0000000..1170828
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTreeNode;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+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.binding.InstanceIdentifier.PathArgument;
+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.DataTreeCandidateNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Lazily translated {@link DataObjectModification} based on {@link DataTreeCandidateNode}.
+ *
+ * {@link LazyDataObjectModification} represents Data tree change event,
+ * but whole tree is not translated or resolved eagerly, but only child nodes
+ * which are directly accessed by user of data object modification.
+ *
+ * @param <T> Type of Binding Data Object
+ */
+final class LazyDataObjectModification<T extends DataObject> implements DataObjectModification<T> {
+
+    private final static Logger LOG = LoggerFactory.getLogger(LazyDataObjectModification.class);
+
+    private final BindingCodecTreeNode<T> codec;
+    private final DataTreeCandidateNode domData;
+    private final PathArgument identifier;
+    private Collection<DataObjectModification<? extends DataObject>> childNodesCache;
+
+    private LazyDataObjectModification(final BindingCodecTreeNode<T> codec, final DataTreeCandidateNode domData) {
+        this.codec = Preconditions.checkNotNull(codec);
+        this.domData = Preconditions.checkNotNull(domData);
+        this.identifier = codec.deserializePathArgument(domData.getIdentifier());
+    }
+
+    static <T extends DataObject> DataObjectModification<T> create(final BindingCodecTreeNode<T> codec,
+            final DataTreeCandidateNode domData) {
+        return new LazyDataObjectModification<>(codec,domData);
+    }
+
+    private static Collection<DataObjectModification<? extends DataObject>> from(final BindingCodecTreeNode<?> parentCodec,
+            final Collection<DataTreeCandidateNode> domChildNodes) {
+        final List<DataObjectModification<? extends DataObject>> result = new ArrayList<>(domChildNodes.size());
+        populateList(result, parentCodec, domChildNodes);
+        return result;
+    }
+
+    private static void populateList(final List<DataObjectModification<? extends DataObject>> result,
+            final BindingCodecTreeNode<?> parentCodec, final Collection<DataTreeCandidateNode> domChildNodes) {
+        for (final DataTreeCandidateNode domChildNode : domChildNodes) {
+            final BindingStructuralType type = BindingStructuralType.from(domChildNode);
+            if (type != BindingStructuralType.NOT_ADDRESSABLE) {
+                /*
+                 *  Even if type is UNKNOWN, from perspective of BindingStructuralType
+                 *  we try to load codec for it. We will use that type to further specify
+                 *  debug log.
+                 */
+                try {
+                    final BindingCodecTreeNode<?> childCodec =
+                            parentCodec.yangPathArgumentChild(domChildNode.getIdentifier());
+                    populateList(result,type, childCodec, domChildNode);
+                } catch (final IllegalArgumentException e) {
+                    if (type == BindingStructuralType.UNKNOWN) {
+                        LOG.debug("Unable to deserialize unknown DOM node {}",domChildNode,e);
+                    } else {
+                        LOG.debug("Binding representation for DOM node {} was not found",domChildNode,e);
+                    }
+                }
+            }
+        }
+    }
+
+    private static void populateList(final List<DataObjectModification<? extends DataObject>> result,
+            final BindingStructuralType type, final BindingCodecTreeNode<?> childCodec,
+            final DataTreeCandidateNode domChildNode) {
+        switch (type) {
+            case INVISIBLE_LIST:
+                // We use parent codec intentionally.
+                populateListWithSingleCodec(result, childCodec, domChildNode.getChildNodes());
+                break;
+            case INVISIBLE_CONTAINER:
+                populateList(result, childCodec, domChildNode.getChildNodes());
+                break;
+            case UNKNOWN:
+            case VISIBLE_CONTAINER:
+                result.add(create(childCodec, domChildNode));
+            default:
+                break;
+        }
+    }
+
+    private static void populateListWithSingleCodec(final List<DataObjectModification<? extends DataObject>> result,
+            final BindingCodecTreeNode<?> codec, final Collection<DataTreeCandidateNode> childNodes) {
+        for (final DataTreeCandidateNode child : childNodes) {
+            result.add(create(codec, child));
+        }
+    }
+
+    @Override
+    public T getDataBefore() {
+        return deserialize(domData.getDataBefore());
+    }
+
+    @Override
+    public T getDataAfter() {
+        return deserialize(domData.getDataAfter());
+    }
+
+    @Override
+    public Class<T> getDataType() {
+        return codec.getBindingClass();
+    }
+
+    @Override
+    public PathArgument getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public DataObjectModification.ModificationType getModificationType() {
+        switch(domData.getModificationType()) {
+            case WRITE:
+                return DataObjectModification.ModificationType.WRITE;
+            case SUBTREE_MODIFIED:
+                return DataObjectModification.ModificationType.SUBTREE_MODIFIED;
+            case DELETE:
+                return DataObjectModification.ModificationType.DELETE;
+
+            default:
+                // TODO: Should we lie about modification type instead of exception?
+                throw new IllegalStateException("Unsupported DOM Modification type " + domData.getModificationType());
+        }
+    }
+
+    @Override
+    public Collection<DataObjectModification<? extends DataObject>> getModifiedChildren() {
+        if (childNodesCache == null) {
+            childNodesCache = from(codec, domData.getChildNodes());
+        }
+        return childNodesCache;
+    }
+
+    @Override
+    public DataObjectModification<? extends DataObject> getModifiedChild(final PathArgument arg) {
+        final List<YangInstanceIdentifier.PathArgument> domArgumentList = new ArrayList<>();
+        final BindingCodecTreeNode<?> childCodec = codec.bindingPathArgumentChild(arg, domArgumentList);
+        final Iterator<YangInstanceIdentifier.PathArgument> toEnter = domArgumentList.iterator();
+        DataTreeCandidateNode current = domData;
+        while (toEnter.hasNext() && current != null) {
+            current = current.getModifiedChild(toEnter.next());
+        }
+        if (current != null) {
+            return create(childCodec, current);
+        }
+        return null;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <C extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<C>> DataObjectModification<C> getModifiedChildListItem(
+            final Class<C> listItem, final K listKey) {
+        return (DataObjectModification<C>) getModifiedChild(new InstanceIdentifier.IdentifiableItem<>(listItem, listKey));
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <C extends ChildOf<? super T>> DataObjectModification<C> getModifiedChildContainer(final Class<C> arg) {
+        return (DataObjectModification<C>) getModifiedChild(new InstanceIdentifier.Item<>(arg));
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <C extends Augmentation<T> & DataObject> DataObjectModification<C> getModifiedAugmentation(
+            final Class<C> augmentation) {
+        return (DataObjectModification<C>) getModifiedChild(new InstanceIdentifier.Item<>(augmentation));
+    }
+
+    private T deserialize(final Optional<NormalizedNode<?, ?>> dataAfter) {
+        if (dataAfter.isPresent()) {
+            return codec.deserialize(dataAfter.get());
+        }
+        return null;
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/LazyDataTreeModification.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/LazyDataTreeModification.java
new file mode 100644 (file)
index 0000000..2a90f96
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map.Entry;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTreeNode;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+
+/**
+ * Lazily translated {@link DataTreeModification} based on {@link DataTreeCandidate}.
+ *
+ * {@link DataTreeModification} represents Data tree change event,
+ * but whole tree is not translated or resolved eagerly, but only child nodes
+ * which are directly accessed by user of data object modification.
+ *
+ */
+class LazyDataTreeModification<T extends DataObject> implements DataTreeModification<T> {
+
+    private final DataTreeIdentifier<T> path;
+    private final DataObjectModification<T> rootNode;
+
+    LazyDataTreeModification(final LogicalDatastoreType datastoreType, final InstanceIdentifier<T> path, final BindingCodecTreeNode<T> codec, final DataTreeCandidate domChange) {
+        this.path = new DataTreeIdentifier<>(datastoreType, path);
+        this.rootNode = LazyDataObjectModification.create(codec, domChange.getRootNode());
+    }
+
+    @Override
+    public DataObjectModification<T> getRootNode() {
+        return rootNode;
+    }
+
+    @Override
+    public DataTreeIdentifier<T> getRootPath() {
+        return path;
+    }
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    static <T extends DataObject> DataTreeModification<T> create(final BindingToNormalizedNodeCodec codec, final DataTreeCandidate domChange,
+            final LogicalDatastoreType datastoreType) {
+        final Entry<InstanceIdentifier<?>, BindingCodecTreeNode<?>> codecCtx =
+                codec.getSubtreeCodec(domChange.getRootPath());
+        return (DataTreeModification<T>) new LazyDataTreeModification(datastoreType, codecCtx.getKey(), codecCtx.getValue(), domChange);
+    }
+
+    static <T extends DataObject> Collection<DataTreeModification<T>> from(final BindingToNormalizedNodeCodec codec,
+            final Collection<DataTreeCandidate> domChanges, final LogicalDatastoreType datastoreType) {
+        final List<DataTreeModification<T>> result = new ArrayList<>(domChanges.size());
+        for (final DataTreeCandidate domChange : domChanges) {
+            result.add(LazyDataTreeModification.<T>create(codec, domChange, datastoreType));
+        }
+        return result;
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/LazySerializedContainerNode.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/LazySerializedContainerNode.java
new file mode 100644 (file)
index 0000000..af50ee1
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Optional;
+import java.util.Collection;
+import java.util.Map;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+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.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ *
+ * FIXME: Should be this moved to binding-data-codec?
+ *
+ */
+class LazySerializedContainerNode implements ContainerNode {
+
+    private final NodeIdentifier identifier;
+    private final DataObject bindingData;
+
+    private BindingNormalizedNodeCodecRegistry registry;
+    private ContainerNode domData;
+
+    private LazySerializedContainerNode(final QName identifier, final DataObject binding,
+            final BindingNormalizedNodeCodecRegistry registry) {
+        this.identifier = new NodeIdentifier(identifier);
+        this.bindingData = binding;
+        this.registry = registry;
+        this.domData = null;
+    }
+
+    static NormalizedNode<?, ?> create(final SchemaPath rpcName, final DataObject data,
+            final BindingNormalizedNodeCodecRegistry codec) {
+        return new LazySerializedContainerNode(rpcName.getLastComponent(), data, codec);
+    }
+
+    static NormalizedNode<?, ?> withContextRef(final SchemaPath rpcName, final DataObject data,
+            final LeafNode<?> contextRef, final BindingNormalizedNodeCodecRegistry codec) {
+        return new WithContextRef(rpcName.getLastComponent(), data, contextRef, codec);
+    }
+
+    @Override
+    public Map<QName, String> getAttributes() {
+        return delegate().getAttributes();
+    }
+
+    private ContainerNode delegate() {
+        if (domData == null) {
+            domData = registry.toNormalizedNodeRpcData(bindingData);
+            registry = null;
+        }
+        return domData;
+    }
+
+    @Override
+    public final QName getNodeType() {
+        return identifier.getNodeType();
+    }
+
+    @Override
+    public final Collection<DataContainerChild<? extends PathArgument, ?>> getValue() {
+        return delegate().getValue();
+    }
+
+    @Override
+    public final NodeIdentifier getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public Optional<DataContainerChild<? extends PathArgument, ?>> getChild(final PathArgument child) {
+        return delegate().getChild(child);
+    }
+
+    @Override
+    public final Object getAttributeValue(final QName name) {
+        return delegate().getAttributeValue(name);
+    }
+
+    final DataObject bindingData() {
+        return bindingData;
+    }
+
+    /**
+     * Lazy Serialized Node with pre-cached serialized leaf holding routing information.
+     *
+     */
+    private static final class WithContextRef extends LazySerializedContainerNode {
+
+        private final LeafNode<?> contextRef;
+
+        protected WithContextRef(final QName identifier, final DataObject binding, final LeafNode<?> contextRef,
+                final BindingNormalizedNodeCodecRegistry registry) {
+            super(identifier, binding, registry);
+            this.contextRef = contextRef;
+        }
+
+        @Override
+        public Optional<DataContainerChild<? extends PathArgument, ?>> getChild(final PathArgument child) {
+            /*
+             * Use precached value of routing field and do not run full serialization if we are
+             * accessing it.
+             */
+            if (contextRef.getIdentifier().equals(child)) {
+                return Optional.<DataContainerChild<? extends PathArgument, ?>>of(contextRef);
+            }
+            return super.getChild(child);
+        }
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/LazySerializedDOMNotification.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/LazySerializedDOMNotification.java
new file mode 100644 (file)
index 0000000..07da99b
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * Lazy serialized implementation of DOM Notification.
+ *
+ * This implementation performs serialization of data, only if receiver
+ * of notification actually accessed data from notification.
+ *
+ */
+public final class LazySerializedDOMNotification implements DOMNotification {
+
+    private final BindingNormalizedNodeSerializer codec;
+    private final Notification data;
+    private final SchemaPath type;
+
+    private ContainerNode domBody;
+
+    private LazySerializedDOMNotification(final BindingNormalizedNodeSerializer codec, final Notification data, final SchemaPath type) {
+        super();
+        this.codec = codec;
+        this.data = data;
+        this.type = type;
+    }
+
+    static DOMNotification create(final BindingNormalizedNodeSerializer codec, final Notification data) {
+        final SchemaPath type = SchemaPath.create(true, BindingReflections.findQName(data.getImplementedInterface()));
+        return new LazySerializedDOMNotification(codec, data, type);
+    }
+
+    @Override
+    public SchemaPath getType() {
+        return type;
+    }
+
+    @Override
+    public ContainerNode getBody() {
+        if (domBody == null) {
+            domBody = codec.toNormalizedNodeNotification(data);
+        }
+        return domBody;
+    }
+
+    public Notification getBindingData() {
+        return data;
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/RpcServiceAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/RpcServiceAdapter.java
new file mode 100644 (file)
index 0000000..f9163ce
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Map.Entry;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.md.sal.dom.broker.spi.rpc.RpcRoutingStrategy;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+class RpcServiceAdapter implements InvocationHandler {
+
+    private final ImmutableMap<Method, RpcInvocationStrategy> rpcNames;
+    private final Class<? extends RpcService> type;
+    private final BindingToNormalizedNodeCodec codec;
+    private final DOMRpcService delegate;
+    private final RpcService proxy;
+
+    RpcServiceAdapter(final Class<? extends RpcService> type, final BindingToNormalizedNodeCodec codec,
+            final DOMRpcService domService) {
+        this.type = Preconditions.checkNotNull(type);
+        this.codec = Preconditions.checkNotNull(codec);
+        this.delegate = Preconditions.checkNotNull(domService);
+        final ImmutableMap.Builder<Method, RpcInvocationStrategy> rpcBuilder = ImmutableMap.builder();
+        for (final Entry<Method, RpcDefinition> rpc : codec.getRpcMethodToSchema(type).entrySet()) {
+            rpcBuilder.put(rpc.getKey(), createStrategy(rpc.getKey(), rpc.getValue()));
+        }
+        rpcNames = rpcBuilder.build();
+        proxy = (RpcService) Proxy.newProxyInstance(type.getClassLoader(), new Class[] {type}, this);
+    }
+
+    private ListenableFuture<RpcResult<?>> invoke0(final SchemaPath schemaPath, final NormalizedNode<?, ?> input) {
+        final CheckedFuture<DOMRpcResult, DOMRpcException> result = delegate.invokeRpc(schemaPath, input);
+        if(result instanceof LazyDOMRpcResultFuture) {
+            return ((LazyDOMRpcResultFuture) result).getBindingFuture();
+        }
+
+        return transformFuture(schemaPath, result, codec.getCodecFactory());
+    }
+
+    private RpcInvocationStrategy createStrategy(final Method method, final RpcDefinition schema) {
+        final RpcRoutingStrategy strategy = RpcRoutingStrategy.from(schema);
+        if (strategy.isContextBasedRouted()) {
+            return new RoutedStrategy(schema.getPath(), method, strategy.getLeaf());
+        }
+        return new NonRoutedStrategy(schema.getPath());
+    }
+
+    RpcService getProxy() {
+        return proxy;
+    }
+
+    @Override
+    public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
+
+        final RpcInvocationStrategy rpc = rpcNames.get(method);
+        if (rpc != null) {
+            if (method.getParameterTypes().length == 0) {
+                return rpc.invokeEmpty();
+            }
+            if (args.length != 1) {
+                throw new IllegalArgumentException("Input must be provided.");
+            }
+            return rpc.invoke((DataObject) args[0]);
+        }
+
+        if (isObjectMethod(method)) {
+            return callObjectMethod(proxy, method, args);
+        }
+        throw new UnsupportedOperationException("Method " + method.toString() + "is unsupported.");
+    }
+
+    private static boolean isObjectMethod(final Method m) {
+        switch (m.getName()) {
+            case "toString":
+                return (m.getReturnType().equals(String.class) && m.getParameterTypes().length == 0);
+            case "hashCode":
+                return (m.getReturnType().equals(int.class) && m.getParameterTypes().length == 0);
+            case "equals":
+                return (m.getReturnType().equals(boolean.class) && m.getParameterTypes().length == 1 && m
+                        .getParameterTypes()[0] == Object.class);
+            default:
+                return false;
+        }
+    }
+
+    private Object callObjectMethod(final Object self, final Method m, final Object[] args) {
+        switch (m.getName()) {
+            case "toString":
+                return type.getName() + "$Adapter{delegate=" + delegate.toString() + "}";
+            case "hashCode":
+                return System.identityHashCode(self);
+            case "equals":
+                return (self == args[0]);
+            default:
+                return null;
+        }
+    }
+
+    private static ListenableFuture<RpcResult<?>> transformFuture(final SchemaPath rpc,
+            final ListenableFuture<DOMRpcResult> domFuture, final BindingNormalizedNodeCodecRegistry codec) {
+        return Futures.transform(domFuture, new Function<DOMRpcResult, RpcResult<?>>() {
+            @Override
+            public RpcResult<?> apply(final DOMRpcResult input) {
+                final NormalizedNode<?, ?> domData = input.getResult();
+                final DataObject bindingResult;
+                if (domData != null) {
+                    final SchemaPath rpcOutput = rpc.createChild(QName.create(rpc.getLastComponent(), "output"));
+                    bindingResult = codec.fromNormalizedNodeRpcData(rpcOutput, (ContainerNode) domData);
+                } else {
+                    bindingResult = null;
+                }
+                return RpcResult.class.cast(RpcResultBuilder.success(bindingResult).build());
+            }
+        });
+    }
+
+    private abstract class RpcInvocationStrategy {
+
+        private final SchemaPath rpcName;
+
+        protected RpcInvocationStrategy(final SchemaPath path) {
+            rpcName = path;
+        }
+
+        final ListenableFuture<RpcResult<?>> invoke(final DataObject input) {
+            return invoke0(rpcName, serialize(input));
+        }
+
+        abstract NormalizedNode<?, ?> serialize(DataObject input);
+
+        final ListenableFuture<RpcResult<?>> invokeEmpty() {
+            return invoke0(rpcName, null);
+        }
+
+        final SchemaPath getRpcName() {
+            return rpcName;
+        }
+
+    }
+
+    private final class NonRoutedStrategy extends RpcInvocationStrategy {
+
+        protected NonRoutedStrategy(final SchemaPath path) {
+            super(path);
+        }
+
+        @Override
+        NormalizedNode<?, ?> serialize(final DataObject input) {
+            return LazySerializedContainerNode.create(getRpcName(), input, codec.getCodecRegistry());
+        }
+
+    }
+
+    private final class RoutedStrategy extends RpcInvocationStrategy {
+
+        private final ContextReferenceExtractor refExtractor;
+        private final NodeIdentifier contextName;
+
+        protected RoutedStrategy(final SchemaPath path, final Method rpcMethod, final QName leafName) {
+            super(path);
+            final Class<? extends DataContainer> inputType = BindingReflections.resolveRpcInputClass(rpcMethod).get();
+            refExtractor = ContextReferenceExtractor.from(inputType);
+            this.contextName = new NodeIdentifier(leafName);
+        }
+
+        @Override
+        NormalizedNode<?, ?> serialize(final DataObject input) {
+            final InstanceIdentifier<?> bindingII = refExtractor.extract(input);
+            if (bindingII != null) {
+                final YangInstanceIdentifier yangII = codec.toYangInstanceIdentifierCached(bindingII);
+                final LeafNode<?> contextRef = ImmutableNodes.leafNode(contextName, yangII);
+                return LazySerializedContainerNode.withContextRef(getRpcName(), input, contextRef,
+                        codec.getCodecRegistry());
+            }
+            return LazySerializedContainerNode.create(getRpcName(), input, codec.getCodecRegistry());
+        }
+
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/spi/AdapterBuilder.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/spi/AdapterBuilder.java
new file mode 100644 (file)
index 0000000..655a176
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.spi;
+
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableClassToInstanceMap;
+import com.google.common.collect.MutableClassToInstanceMap;
+import java.util.Set;
+import org.opendaylight.yangtools.concepts.Builder;
+
+public abstract class AdapterBuilder<T,D> implements Builder<T> {
+
+    private final ClassToInstanceMap<D> delegates = MutableClassToInstanceMap.create();
+
+    public abstract Set<? extends Class<? extends D>> getRequiredDelegates();
+
+    protected abstract T createInstance(ClassToInstanceMap<D> delegates);
+
+    private void checkAllRequiredServices() {
+        for(final Class<? extends D> type : getRequiredDelegates()) {
+            Preconditions.checkState(delegates.get(type) != null, "Requires service %s is not defined.",type);
+        }
+    }
+
+    public final <V extends D>void addDelegate(final Class<V> type,final D impl) {
+        delegates.put(type,impl);
+    }
+
+    @Override
+    public final  T build() {
+        checkAllRequiredServices();
+        return createInstance(ImmutableClassToInstanceMap.<D,D>copyOf(delegates));
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/spi/AdapterLoader.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/spi/AdapterLoader.java
new file mode 100644 (file)
index 0000000..7919a8f
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.spi;
+
+import com.google.common.base.Optional;
+import com.google.common.cache.CacheLoader;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+public abstract class AdapterLoader<T, D> extends CacheLoader<Class<? extends T>, Optional<T>> {
+
+    @Override
+    public Optional<T> load(final Class<? extends T> key) {
+
+        final AdapterBuilder<? extends T, D> builder = createBuilder(key);
+        for(final Class<? extends D> reqDeleg : builder.getRequiredDelegates()) {
+            final D deleg = getDelegate(reqDeleg);
+            if(deleg != null) {
+                builder.addDelegate(reqDeleg,deleg);
+            } else {
+                return Optional.absent();
+            }
+        }
+        return  Optional.<T>of(builder.build());
+    }
+
+    @Nullable
+    protected abstract D getDelegate(Class<? extends D> reqDeleg);
+
+    @Nonnull
+    protected abstract AdapterBuilder<? extends T, D> createBuilder(Class<? extends T> key);
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolder.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolder.java
new file mode 100644 (file)
index 0000000..6bcd74d
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.codegen.impl;
+
+import com.google.common.util.concurrent.ForwardingBlockingQueue;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import javassist.ClassPool;
+import org.apache.commons.lang3.StringUtils;
+import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SingletonHolder {
+    private static final Logger logger = LoggerFactory.getLogger(SingletonHolder.class);
+
+    public static final ClassPool CLASS_POOL = ClassPool.getDefault();
+    public static final JavassistUtils JAVASSIST = JavassistUtils.forClassPool(CLASS_POOL);
+
+    public static final int CORE_NOTIFICATION_THREADS = 4;
+    public static final int MAX_NOTIFICATION_THREADS = 32;
+    // block caller thread after MAX_NOTIFICATION_THREADS + MAX_NOTIFICATION_QUEUE_SIZE pending notifications
+    public static final int MAX_NOTIFICATION_QUEUE_SIZE = 1000;
+    public static final int NOTIFICATION_THREAD_LIFE = 15;
+    private static final String NOTIFICATION_QUEUE_SIZE_PROPERTY = "mdsal.notificationqueue.size";
+
+    private static ListeningExecutorService NOTIFICATION_EXECUTOR = null;
+    private static ListeningExecutorService COMMIT_EXECUTOR = null;
+    private static ListeningExecutorService CHANGE_EVENT_EXECUTOR = null;
+
+    /**
+     * @deprecated This method is only used from configuration modules and thus callers of it
+     *             should use service injection to make the executor configurable.
+     */
+    @Deprecated
+    public static synchronized ListeningExecutorService getDefaultNotificationExecutor() {
+
+        if (NOTIFICATION_EXECUTOR == null) {
+            int queueSize = MAX_NOTIFICATION_QUEUE_SIZE;
+            final String queueValue = System.getProperty(NOTIFICATION_QUEUE_SIZE_PROPERTY);
+            if (StringUtils.isNotBlank(queueValue)) {
+                try {
+                    queueSize = Integer.parseInt(queueValue);
+                    logger.trace("Queue size was set to {}", queueSize);
+                } catch (final NumberFormatException e) {
+                    logger.warn("Cannot parse {} as set by {}, using default {}", queueValue,
+                            NOTIFICATION_QUEUE_SIZE_PROPERTY, queueSize);
+                }
+            }
+
+            // Overriding the queue:
+            // ThreadPoolExecutor would not create new threads if the queue is not full, thus adding
+            // occurs in RejectedExecutionHandler.
+            // This impl saturates threadpool first, then queue. When both are full caller will get blocked.
+            final BlockingQueue<Runnable> delegate = new LinkedBlockingQueue<>(queueSize);
+            final BlockingQueue<Runnable> queue = new ForwardingBlockingQueue<Runnable>() {
+                @Override
+                protected BlockingQueue<Runnable> delegate() {
+                    return delegate;
+                }
+
+                @Override
+                public boolean offer(final Runnable r) {
+                    // ThreadPoolExecutor will spawn a new thread after core size is reached only
+                    // if the queue.offer returns false.
+                    return false;
+                }
+            };
+
+            final ThreadFactory factory = new ThreadFactoryBuilder()
+            .setDaemon(true)
+            .setNameFormat("md-sal-binding-notification-%d")
+            .build();
+
+            final ThreadPoolExecutor executor = new ThreadPoolExecutor(CORE_NOTIFICATION_THREADS, MAX_NOTIFICATION_THREADS,
+                    NOTIFICATION_THREAD_LIFE, TimeUnit.SECONDS, queue, factory,
+                    new RejectedExecutionHandler() {
+                // if the max threads are met, then it will raise a rejectedExecution. We then push to the queue.
+                @Override
+                public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) {
+                    try {
+                        executor.getQueue().put(r);
+                    } catch (final InterruptedException e) {
+                        throw new RejectedExecutionException("Interrupted while waiting on the queue", e);
+                    }
+                }
+            });
+
+            NOTIFICATION_EXECUTOR = MoreExecutors.listeningDecorator(executor);
+        }
+
+        return NOTIFICATION_EXECUTOR;
+    }
+
+    /**
+     * @deprecated This method is only used from configuration modules and thus callers of it
+     *             should use service injection to make the executor configurable.
+     */
+    @Deprecated
+    public static synchronized ListeningExecutorService getDefaultCommitExecutor() {
+        if (COMMIT_EXECUTOR == null) {
+            final ThreadFactory factory = new ThreadFactoryBuilder().setDaemon(true).setNameFormat("md-sal-binding-commit-%d").build();
+            /*
+             * FIXME: this used to be newCacheThreadPool(), but MD-SAL does not have transaction
+             *        ordering guarantees, which means that using a concurrent threadpool results
+             *        in application data being committed in random order, potentially resulting
+             *        in inconsistent data being present. Once proper primitives are introduced,
+             *        concurrency can be reintroduced.
+             */
+            final ExecutorService executor = Executors.newSingleThreadExecutor(factory);
+            COMMIT_EXECUTOR = MoreExecutors.listeningDecorator(executor);
+        }
+
+        return COMMIT_EXECUTOR;
+    }
+
+    public static ExecutorService getDefaultChangeEventExecutor() {
+        if (CHANGE_EVENT_EXECUTOR == null) {
+            final ThreadFactory factory = new ThreadFactoryBuilder().setDaemon(true).setNameFormat("md-sal-binding-change-%d").build();
+            /*
+             * FIXME: this used to be newCacheThreadPool(), but MD-SAL does not have transaction
+             *        ordering guarantees, which means that using a concurrent threadpool results
+             *        in application data being committed in random order, potentially resulting
+             *        in inconsistent data being present. Once proper primitives are introduced,
+             *        concurrency can be reintroduced.
+             */
+            final ExecutorService executor = Executors.newSingleThreadExecutor(factory);
+            CHANGE_EVENT_EXECUTOR  = MoreExecutors.listeningDecorator(executor);
+        }
+
+        return CHANGE_EVENT_EXECUTOR;
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RootBindingAwareBroker.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RootBindingAwareBroker.java
new file mode 100644 (file)
index 0000000..3fccb1d
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.impl;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.collect.ImmutableClassToInstanceMap;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.MountPointService;
+import org.opendaylight.controller.md.sal.binding.util.AbstractBindingSalProviderInstance;
+import org.opendaylight.controller.md.sal.binding.util.BindingContextUtils;
+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.NotificationProviderService;
+import org.opendaylight.controller.sal.binding.api.NotificationService;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.binding.api.mount.MountProviderService;
+import org.opendaylight.controller.sal.binding.api.mount.MountService;
+import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Mutable;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RootBindingAwareBroker implements Mutable, Identifiable<String>, BindingAwareBroker, AutoCloseable,
+        RpcProviderRegistry {
+
+    private final static Logger LOG = LoggerFactory.getLogger(RootBindingAwareBroker.class);
+
+    RootSalInstance controllerRoot;
+
+    private final String identifier;
+
+    private RpcProviderRegistry rpcBroker;
+
+    private NotificationProviderService notificationBroker;
+
+    @SuppressWarnings("deprecation")
+    private DataProviderService legacyDataBroker;
+
+    private DataBroker dataBroker;
+
+    private MountProviderService legacyMount;
+
+    private ImmutableClassToInstanceMap<BindingAwareService> supportedConsumerServices;
+
+    private ImmutableClassToInstanceMap<BindingAwareService> supportedProviderServices;
+
+    private MountPointService mountService;
+
+    public void setLegacyMountManager(final MountProviderService legacyMount) {
+        this.legacyMount = legacyMount;
+    }
+
+    public RootBindingAwareBroker(final String instanceName) {
+        this.identifier = instanceName;
+    }
+
+    @Override
+    public String getIdentifier() {
+        return identifier;
+    }
+
+    public RootSalInstance getRoot() {
+        return controllerRoot;
+    }
+
+    public DataProviderService getDataBroker() {
+        return this.legacyDataBroker;
+    }
+
+    public NotificationProviderService getNotificationBroker() {
+        return this.notificationBroker;
+    }
+
+    public RpcProviderRegistry getRpcProviderRegistry() {
+        return this.rpcBroker;
+    }
+
+    public RpcProviderRegistry getRpcBroker() {
+        return rpcBroker;
+    }
+
+    public MountPointService getMountService() {
+        return mountService;
+    }
+
+    public MountProviderService getLegacyMount() {
+        return legacyMount;
+    }
+
+    public void setDataBroker(final DataBroker asyncDataBroker) {
+        dataBroker = asyncDataBroker;
+    }
+
+    public void setMountService(final MountPointService mount) {
+        this.mountService = mount;
+    }
+
+    public void setRpcBroker(final RpcProviderRegistry rpcBroker) {
+        this.rpcBroker = rpcBroker;
+    }
+
+    public void setNotificationBroker(final NotificationProviderService notificationBroker) {
+        this.notificationBroker = notificationBroker;
+    }
+
+    public void setLegacyDataBroker(final DataProviderService dataBroker) {
+        this.legacyDataBroker = dataBroker;
+    }
+
+    public void start() {
+        checkState(controllerRoot == null, "Binding Aware Broker was already started.");
+        LOG.info("Starting Binding Aware Broker: {}", identifier);
+
+        controllerRoot = new RootSalInstance(getRpcProviderRegistry(), getNotificationBroker(), getDataBroker());
+
+        final ImmutableClassToInstanceMap.Builder<BindingAwareService> consBuilder = ImmutableClassToInstanceMap
+                .builder();
+
+        consBuilder.put(NotificationService.class, getRoot());
+        consBuilder.put(DataBrokerService.class, getRoot());
+        consBuilder.put(RpcConsumerRegistry.class, getRoot());
+        if (dataBroker != null) {
+            consBuilder.put(DataBroker.class, dataBroker);
+        }
+        consBuilder.put(MountPointService.class, mountService);
+        consBuilder.put(MountService.class, legacyMount).build();
+        supportedConsumerServices = consBuilder.build();
+        supportedProviderServices = ImmutableClassToInstanceMap.<BindingAwareService> builder()
+                .putAll(supportedConsumerServices).put(NotificationProviderService.class, getRoot())
+                .put(DataProviderService.class, getRoot()).put(RpcProviderRegistry.class, getRoot())
+                .put(MountProviderService.class, legacyMount).build();
+    }
+
+    @Override
+    public ConsumerContext registerConsumer(final BindingAwareConsumer consumer, final BundleContext ctx) {
+        return registerConsumer(consumer);
+    }
+
+    @Override
+    public ProviderContext registerProvider(final BindingAwareProvider provider, final BundleContext ctx) {
+        return registerProvider(provider);
+    }
+
+    @Override
+    public ConsumerContext registerConsumer(final BindingAwareConsumer consumer) {
+        checkState(supportedConsumerServices != null, "Broker is not initialized.");
+        return BindingContextUtils.createConsumerContextAndInitialize(consumer, supportedConsumerServices);
+    }
+
+    @Override
+    public ProviderContext registerProvider(final BindingAwareProvider provider) {
+        checkState(supportedProviderServices != null, "Broker is not initialized.");
+        return BindingContextUtils.createProviderContextAndInitialize(provider, supportedProviderServices);
+    }
+
+    @Override
+    public void close() throws Exception {
+        // FIXME: Close all sessions
+    }
+
+    @Override
+    public <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(final Class<T> type,
+            final T implementation) throws IllegalStateException {
+        return getRoot().addRoutedRpcImplementation(type, implementation);
+    }
+
+    @Override
+    public <T extends RpcService> RpcRegistration<T> addRpcImplementation(final Class<T> type, final T implementation)
+            throws IllegalStateException {
+        return getRoot().addRpcImplementation(type, implementation);
+    }
+
+    @Override
+    public <T extends RpcService> T getRpcService(final Class<T> module) {
+        return getRoot().getRpcService(module);
+    }
+
+    @Override
+    public <L extends RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
+            final L arg0) {
+        return getRoot().registerRouteChangeListener(arg0);
+    }
+
+    public class RootSalInstance extends
+            AbstractBindingSalProviderInstance<DataProviderService, NotificationProviderService, RpcProviderRegistry> {
+
+        public RootSalInstance(final RpcProviderRegistry rpcRegistry,
+                final NotificationProviderService notificationBroker, final DataProviderService dataBroker) {
+            super(rpcRegistry, notificationBroker, dataBroker);
+        }
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/ContextExtractorTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/ContextExtractorTest.java
new file mode 100644 (file)
index 0000000..a38f3f1
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.bi.ba.rpcservice.rev140701.RockTheHouseInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.bi.ba.rpcservice.rev140701.RockTheHouseInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.rpc.routing.rev140701.EncapsulatedRoute;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.rpc.routing.rev140701.EncapsulatedRouteInGrouping;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.rpc.routing.rev140701.RoutedSimpleRouteInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.rpc.routing.rev140701.RoutedSimpleRouteInputBuilder;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public final class ContextExtractorTest {
+
+    public interface Transitive extends EncapsulatedRouteInGrouping {
+
+    }
+
+    private static final InstanceIdentifier<?> TEST_ROUTE = InstanceIdentifier.create(Top.class);
+    private static final Transitive TEST_GROUPING = new Transitive() {
+
+        @Override
+        public Class<? extends DataContainer> getImplementedInterface() {
+            return Transitive.class;
+        }
+
+        @Override
+        public EncapsulatedRoute getRoute() {
+            return new EncapsulatedRoute(TEST_ROUTE);
+        }
+    };
+
+    @Test
+    public void testNonRoutedExtraction() {
+        final ContextReferenceExtractor extractor = ContextReferenceExtractor.from(RockTheHouseInput.class);
+        final RockTheHouseInput input = new RockTheHouseInputBuilder().build();
+        final InstanceIdentifier<?> extractedValue = extractor.extract(input);
+        assertNull(extractedValue);
+    }
+
+    @Test
+    public void testRoutedSimpleExtraction() {
+        final ContextReferenceExtractor extractor = ContextReferenceExtractor.from(RoutedSimpleRouteInput.class);
+        final RoutedSimpleRouteInput input = new RoutedSimpleRouteInputBuilder().setRoute(TEST_ROUTE).build();
+        final InstanceIdentifier<?> extractedValue = extractor.extract(input);
+        assertSame(TEST_ROUTE,extractedValue);
+    }
+
+    @Test
+    public void testRoutedEncapsulatedExtraction() {
+        final ContextReferenceExtractor extractor = ContextReferenceExtractor.from(EncapsulatedRouteInGrouping.class);
+        final InstanceIdentifier<?> extractedValue = extractor.extract(TEST_GROUPING);
+        assertSame(TEST_ROUTE,extractedValue);
+
+    }
+
+    @Test
+    public void testRoutedEncapsulatedTransitiveExtraction() {
+        final ContextReferenceExtractor extractor = ContextReferenceExtractor.from(Transitive.class);
+        final InstanceIdentifier<?> extractedValue = extractor.extract(TEST_GROUPING);
+        assertSame(TEST_ROUTE,extractedValue);
+    }
+ }
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/BackwardsCompatibleNotificationBrokerTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/BackwardsCompatibleNotificationBrokerTest.java
new file mode 100644 (file)
index 0000000..f43352d
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.concurrent.TimeUnit;
+import org.opendaylight.controller.md.sal.binding.compat.HeliumNotificationProviderServiceAdapter;
+
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
+import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.controller.md.sal.binding.test.AbstractNotificationBrokerTest;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.OpendaylightMdsalListTestListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.TwoLevelListChanged;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.TwoLevelListChangedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+
+public class BackwardsCompatibleNotificationBrokerTest extends AbstractNotificationBrokerTest {
+
+    private NotificationProviderService notificationProviderService;
+
+    @Before
+    public void initTest() {
+        final NotificationService notificationService = getNotificationService();
+        final NotificationPublishService notificationPublishService = getNotificationPublishService();
+        notificationProviderService = new HeliumNotificationProviderServiceAdapter(notificationPublishService, notificationService);
+    }
+
+    private TwoLevelListChanged createTestData() {
+        final TwoLevelListChangedBuilder tb = new TwoLevelListChangedBuilder();
+        tb.setTopLevelList(ImmutableList.of(new TopLevelListBuilder().setKey(new TopLevelListKey("test")).build()));
+        return tb.build();
+    }
+
+    @Test
+    public void testNotifSubscriptionForwarded() throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TwoLevelListChanged testData = createTestData();
+
+        final NotifTestListenerChild testNotifListener = new NotifTestListenerChild(latch);
+        final ListenerRegistration<NotificationListener> listenerRegistration =
+                notificationProviderService.registerNotificationListener(testNotifListener);
+        notificationProviderService.publish(testData);
+
+        latch.await(500L, TimeUnit.MILLISECONDS);
+        assertTrue(testNotifListener.getReceivedNotifications().size() == 1);
+        assertEquals(testData, testNotifListener.getReceivedNotifications().get(0));
+        listenerRegistration.close();
+    }
+
+    private static class NotifTestListenerChild extends  NotifTestListener {
+
+        public NotifTestListenerChild(final CountDownLatch latch) {
+            super(latch);
+        }
+    }
+    private static class NotifTestListener implements OpendaylightMdsalListTestListener {
+        private List<TwoLevelListChanged> receivedNotifications = new ArrayList<>();
+        private CountDownLatch latch;
+
+        public NotifTestListener(CountDownLatch latch) {
+            this.latch = latch;
+        }
+
+        @Override
+        public void onTwoLevelListChanged(TwoLevelListChanged notification) {
+            receivedNotifications.add(notification);
+            latch.countDown();
+        }
+
+        public List<TwoLevelListChanged> getReceivedNotifications() {
+            return receivedNotifications;
+        }
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/BindingDOMDataBrokerAdapterTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/BindingDOMDataBrokerAdapterTest.java
new file mode 100644 (file)
index 0000000..e2fceba
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl.test;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.binding.api.ClusteredDataChangeListener;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMDataBrokerAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.ClusteredDOMDataChangeListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+
+public class BindingDOMDataBrokerAdapterTest {
+
+    @Mock
+    DOMDataBroker dataBroker;
+
+    @Mock
+    GeneratedClassLoadingStrategy classLoadingStrategy;
+    @Mock
+    BindingNormalizedNodeCodecRegistry codecRegistry;
+
+    @Mock
+    YangInstanceIdentifier yangInstanceIdentifier;
+
+
+    private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier
+        .create(Top.class);
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testClusteredDataChangeListernerRegisteration() {
+
+        BindingToNormalizedNodeCodec codec = new BindingToNormalizedNodeCodec(classLoadingStrategy, codecRegistry);
+
+        BindingDOMDataBrokerAdapter bindingDOMDataBrokerAdapter = new BindingDOMDataBrokerAdapter(dataBroker, codec);
+        Mockito.when(codecRegistry.toYangInstanceIdentifier(TOP_PATH)).thenReturn(yangInstanceIdentifier);
+
+        ArgumentCaptor<ClusteredDOMDataChangeListener> clusteredDOMListener = ArgumentCaptor.
+            forClass(ClusteredDOMDataChangeListener.class);
+        ArgumentCaptor<LogicalDatastoreType> logicalDatastoreType = ArgumentCaptor.forClass(LogicalDatastoreType.class);
+        ArgumentCaptor<AsyncDataBroker.DataChangeScope> dataChangeScope = ArgumentCaptor.
+            forClass(AsyncDataBroker.DataChangeScope.class);
+        ArgumentCaptor<YangInstanceIdentifier> yangInstanceIdentifier = ArgumentCaptor.
+            forClass(YangInstanceIdentifier.class);
+
+        TestListener listener = new TestListener();
+
+        bindingDOMDataBrokerAdapter.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, TOP_PATH, listener,
+            AsyncDataBroker.DataChangeScope.BASE);
+        Mockito.verify(dataBroker).registerDataChangeListener(logicalDatastoreType.capture(), yangInstanceIdentifier.capture(),
+            clusteredDOMListener.capture(), dataChangeScope.capture());
+
+    }
+
+    private class TestListener implements ClusteredDataChangeListener {
+
+        @Override
+        public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/BindingNormalizedCodecTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/BindingNormalizedCodecTest.java
new file mode 100644 (file)
index 0000000..a427392
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl.test;
+
+import static org.junit.Assert.assertTrue;
+
+import javassist.ClassPool;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.controller.md.sal.binding.test.AbstractSchemaAwareTest;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeLeafOnlyAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
+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.GeneratedClassLoadingStrategy;
+import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class BindingNormalizedCodecTest extends AbstractSchemaAwareTest {
+
+    private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
+    private static final InstanceIdentifier<TopLevelList> BA_TOP_LEVEL_LIST = InstanceIdentifier
+            .builder(Top.class).child(TopLevelList.class, TOP_FOO_KEY).toInstance();
+    private static final InstanceIdentifier<TreeLeafOnlyAugment> BA_TREE_LEAF_ONLY = BA_TOP_LEVEL_LIST.augmentation(TreeLeafOnlyAugment.class);
+    private static final InstanceIdentifier<TreeComplexUsesAugment> BA_TREE_COMPLEX_USES = BA_TOP_LEVEL_LIST.augmentation(TreeComplexUsesAugment.class);
+    private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value");
+
+
+   private BindingToNormalizedNodeCodec codec;
+
+    @Override
+    protected void setupWithSchema(final SchemaContext context) {
+        final DataObjectSerializerGenerator streamWriter = StreamWriterGenerator.create(JavassistUtils.forClassPool(ClassPool.getDefault()));
+        final BindingNormalizedNodeCodecRegistry registry = new BindingNormalizedNodeCodecRegistry(streamWriter);
+        codec = new BindingToNormalizedNodeCodec(GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy(), registry);
+        codec.onGlobalContextUpdated(context);
+    };
+
+    @Test
+    public void testComplexAugmentationSerialization() {
+
+        final PathArgument lastArg = codec.toYangInstanceIdentifier(BA_TREE_COMPLEX_USES).getLastPathArgument();
+        assertTrue(lastArg instanceof AugmentationIdentifier);
+    }
+
+
+    @Test
+    public void testLeafOnlyAugmentationSerialization() {
+
+        final PathArgument leafOnlyLastArg = codec.toYangInstanceIdentifier(BA_TREE_LEAF_ONLY).getLastPathArgument();
+        assertTrue(leafOnlyLastArg instanceof AugmentationIdentifier);
+        assertTrue(((AugmentationIdentifier) leafOnlyLastArg).getPossibleChildNames().contains(SIMPLE_VALUE_QNAME));
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1125RegressionTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1125RegressionTest.java
new file mode 100644 (file)
index 0000000..7f5d36f
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl.test;
+
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertContains;
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertEmpty;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_FOO_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.path;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.topLevelList;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.binding.test.AbstractDataChangeListenerTest;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ContainerWithUsesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.TopBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Regression test suite for Bug 1125 - Can't detect switch disconnection
+ * https://bugs.opendaylight.org/show_bug.cgi?id=1125
+ */
+public class Bug1125RegressionTest extends AbstractDataChangeListenerTest {
+
+    private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier
+            .create(Top.class);
+    private static final InstanceIdentifier<TopLevelList> TOP_FOO_PATH = TOP_PATH
+            .child(TopLevelList.class, TOP_FOO_KEY);
+
+    private static final InstanceIdentifier<TreeComplexUsesAugment> FOO_AUGMENT_PATH = TOP_FOO_PATH
+            .augmentation(TreeComplexUsesAugment.class);
+
+    private static final InstanceIdentifier<TreeComplexUsesAugment> WILDCARDED_AUGMENT_PATH = TOP_PATH
+            .child(TopLevelList.class).augmentation(
+                    TreeComplexUsesAugment.class);
+
+    private void writeInitialState() {
+        WriteTransaction initialTx = getDataBroker().newWriteOnlyTransaction();
+        initialTx.put(LogicalDatastoreType.OPERATIONAL, TOP_PATH,
+                new TopBuilder().build());
+        TreeComplexUsesAugment fooAugment = new TreeComplexUsesAugmentBuilder()
+                .setContainerWithUses(
+                        new ContainerWithUsesBuilder().setLeafFromGrouping(
+                                "foo").build()).build();
+        initialTx.put(LogicalDatastoreType.OPERATIONAL, path(TOP_FOO_KEY),
+                topLevelList(TOP_FOO_KEY, fooAugment));
+        assertCommit(initialTx.submit());
+    }
+
+    private void delete(final InstanceIdentifier<?> path) {
+        WriteTransaction tx = getDataBroker().newWriteOnlyTransaction();
+        tx.delete(LogicalDatastoreType.OPERATIONAL, path);
+        assertCommit(tx.submit());
+    }
+
+    private void verifyRemoved(
+            final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event) {
+        assertEmpty(event.getCreatedData());
+        assertEmpty(event.getUpdatedData());
+        assertContains(event.getRemovedPaths(), FOO_AUGMENT_PATH);
+    }
+
+    private void deleteAndListenAugment(final DataChangeScope scope,
+            final InstanceIdentifier<?> path) {
+        writeInitialState();
+        TestListener listener = createListener(
+                LogicalDatastoreType.OPERATIONAL, WILDCARDED_AUGMENT_PATH,
+                scope);
+        delete(path);
+        verifyRemoved(listener.event());
+    }
+
+    @Test
+    public void deleteAndListenAugment() {
+
+        deleteAndListenAugment(DataChangeScope.ONE, TOP_PATH);
+
+        deleteAndListenAugment(DataChangeScope.BASE, TOP_PATH);
+
+        deleteAndListenAugment(DataChangeScope.SUBTREE, TOP_PATH);
+
+        deleteAndListenAugment(DataChangeScope.BASE, TOP_FOO_PATH);
+
+        deleteAndListenAugment(DataChangeScope.ONE, TOP_FOO_PATH);
+
+        deleteAndListenAugment(DataChangeScope.SUBTREE, TOP_FOO_PATH);
+
+        deleteAndListenAugment(DataChangeScope.BASE, FOO_AUGMENT_PATH);
+
+        deleteAndListenAugment(DataChangeScope.ONE, FOO_AUGMENT_PATH);
+
+        deleteAndListenAugment(DataChangeScope.SUBTREE, FOO_AUGMENT_PATH);
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1333DataChangeListenerTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1333DataChangeListenerTest.java
new file mode 100644 (file)
index 0000000..60d56db
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl.test;
+
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertContains;
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertEmpty;
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertNotContains;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_FOO_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.USES_ONE_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.USES_TWO_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.complexUsesAugment;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.path;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.top;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.topLevelList;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.binding.test.AbstractDataChangeListenerTest;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ *
+ * This testsuite tries to replicate bug 1333 and tests regresion of it
+ * using test-model with similar construction as one reported.
+ *
+ *
+ * See  https://bugs.opendaylight.org/show_bug.cgi?id=1333 for Bug Description
+ *
+ */
+public class Bug1333DataChangeListenerTest extends AbstractDataChangeListenerTest{
+
+    private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.create(Top.class);
+
+    private static final InstanceIdentifier<?> AUGMENT_WILDCARD = TOP_PATH.child(TopLevelList.class).augmentation(
+            TreeComplexUsesAugment.class);
+
+    public void writeTopWithListItem(final LogicalDatastoreType store) {
+        ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
+        Top topItem = top(topLevelList(TOP_FOO_KEY, complexUsesAugment(USES_ONE_KEY, USES_TWO_KEY)));
+        tx.put(store, TOP_PATH, topItem);
+        assertCommit(tx.submit());
+    }
+
+    public void deleteItem(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
+        ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
+        tx.delete(store, path);
+        assertCommit(tx.submit());
+    }
+
+    @Test
+    public void writeTopWithListItemAugmentedListenTopSubtree() {
+        TestListener listener = createListener(CONFIGURATION,TOP_PATH, DataChangeScope.SUBTREE);
+        listener.startCapture();
+
+        writeTopWithListItem(CONFIGURATION);
+
+        AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+
+        assertContains(event.getCreatedData(), TOP_PATH);
+        assertContains(event.getCreatedData(), path(TOP_FOO_KEY));
+        assertContains(event.getCreatedData(), path(TOP_FOO_KEY, TreeComplexUsesAugment.class));
+        assertContains(event.getCreatedData(), path(TOP_FOO_KEY, USES_ONE_KEY));
+        assertContains(event.getCreatedData(), path(TOP_FOO_KEY, USES_TWO_KEY));
+
+        assertEmpty(event.getUpdatedData());
+        assertEmpty(event.getRemovedPaths());
+    }
+
+    @Test
+    public void writeTopWithListItemAugmentedListenAugmentSubtreeWildcarded() {
+        TestListener listener = createListener(CONFIGURATION,AUGMENT_WILDCARD, DataChangeScope.SUBTREE);
+        listener.startCapture();
+        writeTopWithListItem(CONFIGURATION);
+
+        AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+
+        /*
+         * Event should not contain parent nodes
+         */
+        assertNotContains(event.getCreatedData(), TOP_PATH, path(TOP_FOO_KEY));
+
+        assertContains(event.getCreatedData(), path(TOP_FOO_KEY, TreeComplexUsesAugment.class));
+        assertContains(event.getCreatedData(), path(TOP_FOO_KEY, USES_ONE_KEY));
+        assertContains(event.getCreatedData(), path(TOP_FOO_KEY, USES_TWO_KEY));
+
+        assertEmpty(event.getUpdatedData());
+        assertEmpty(event.getRemovedPaths());
+    }
+
+    @Test
+    public void deleteAugmentChildListenTopSubtree() {
+        writeTopWithListItem(CONFIGURATION);
+        TestListener listener = createListener(CONFIGURATION, TOP_PATH, DataChangeScope.SUBTREE);
+        InstanceIdentifier<?> deletePath = path(TOP_FOO_KEY,USES_ONE_KEY);
+        deleteItem(CONFIGURATION,deletePath);
+
+        AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+
+
+        assertEmpty(event.getCreatedData());
+
+        assertContains(event.getRemovedPaths(), deletePath);
+
+        assertContains(event.getUpdatedData(), TOP_PATH);
+        assertContains(event.getUpdatedData(), path(TOP_FOO_KEY));
+        assertContains(event.getUpdatedData(), path(TOP_FOO_KEY, TreeComplexUsesAugment.class));
+
+        assertNotContains(event.getCreatedData(), path(TOP_FOO_KEY, USES_TWO_KEY));
+    }
+
+    @Test
+    public void deleteAugmentChildListenAugmentSubtreeWildcarded() {
+        writeTopWithListItem(CONFIGURATION);
+
+        TestListener listener = createListener(CONFIGURATION, AUGMENT_WILDCARD, DataChangeScope.SUBTREE);
+        InstanceIdentifier<?> deletePath = path(TOP_FOO_KEY,USES_ONE_KEY);
+        deleteItem(CONFIGURATION,deletePath);
+        AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+
+        assertEmpty(event.getCreatedData());
+
+        assertContains(event.getUpdatedData(), path(TOP_FOO_KEY, TreeComplexUsesAugment.class));
+
+        /*
+         * Event should not contain parent nodes
+         */
+        assertNotContains(event.getUpdatedData(), TOP_PATH, path(TOP_FOO_KEY));
+
+        assertContains(event.getRemovedPaths(), deletePath);
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1418AugmentationTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1418AugmentationTest.java
new file mode 100644 (file)
index 0000000..fe32fa9
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl.test;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.binding.test.AbstractDataChangeListenerTest;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeLeafOnlyUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ListViaUses;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ListViaUsesKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertContains;
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertEmpty;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.top;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_FOO_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.topLevelList;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.leafOnlyUsesAugment;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.complexUsesAugment;
+
+public class Bug1418AugmentationTest extends AbstractDataChangeListenerTest{
+    private static final InstanceIdentifier<Top> TOP = InstanceIdentifier.create(Top.class);
+    private static final InstanceIdentifier<TopLevelList> TOP_FOO = TOP.child(TopLevelList.class, TOP_FOO_KEY);
+    private static final InstanceIdentifier<TreeLeafOnlyUsesAugment> SIMPLE_AUGMENT =
+            TOP.child(TopLevelList.class, TOP_FOO_KEY).augmentation(TreeLeafOnlyUsesAugment.class);
+    private static final InstanceIdentifier<TreeComplexUsesAugment> COMPLEX_AUGMENT =
+            TOP.child(TopLevelList.class, TOP_FOO_KEY).augmentation(TreeComplexUsesAugment.class);
+    private static final ListViaUsesKey LIST_VIA_USES_KEY =
+            new ListViaUsesKey("list key");
+    private static final ListViaUsesKey LIST_VIA_USES_KEY_MOD =
+            new ListViaUsesKey("list key modified");
+
+    @Test
+    public void leafOnlyAugmentationCreatedTest() {
+        TestListener listener = createListener(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT,
+                AsyncDataBroker.DataChangeScope.SUBTREE);
+        WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP, top());
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP_FOO, topLevelList(new TopLevelListKey(TOP_FOO_KEY)));
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT, leafOnlyUsesAugment("test leaf"));
+        assertCommit(writeTx.submit());
+        assertTrue(listener.hasEvent());
+        AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+        assertContains(event.getCreatedData(), SIMPLE_AUGMENT);
+        assertEmpty(event.getUpdatedData());
+        assertEmpty(event.getOriginalData());
+        assertEmpty(event.getRemovedPaths());
+    }
+
+    @Test
+    public void leafOnlyAugmentationUpdatedTest() {
+        WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP, top());
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP_FOO, topLevelList(new TopLevelListKey(TOP_FOO_KEY)));
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT, leafOnlyUsesAugment("test leaf"));
+        assertCommit(writeTx.submit());
+        TestListener listener = createListener(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT,
+                AsyncDataBroker.DataChangeScope.SUBTREE);
+        writeTx = getDataBroker().newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT, leafOnlyUsesAugment("test leaf changed"));
+        assertCommit(writeTx.submit());
+        assertTrue(listener.hasEvent());
+        AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+        assertContains(event.getUpdatedData(), SIMPLE_AUGMENT);
+        assertContains(event.getOriginalData(), SIMPLE_AUGMENT);
+        assertEmpty(event.getCreatedData());
+        assertEmpty(event.getRemovedPaths());
+    }
+
+    @Test
+    public void leafOnlyAugmentationDeletedTest() {
+        WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP, top());
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP_FOO, topLevelList(new TopLevelListKey(TOP_FOO_KEY)));
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT, leafOnlyUsesAugment("test leaf"));
+        assertCommit(writeTx.submit());
+        TestListener listener = createListener(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT,
+                AsyncDataBroker.DataChangeScope.SUBTREE);
+        writeTx = getDataBroker().newWriteOnlyTransaction();
+        writeTx.delete(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT);
+        assertCommit(writeTx.submit());
+        assertTrue(listener.hasEvent());
+        AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+        assertContains(event.getRemovedPaths(), SIMPLE_AUGMENT);
+        assertContains(event.getOriginalData(), SIMPLE_AUGMENT);
+        assertEmpty(event.getCreatedData());
+        assertEmpty(event.getUpdatedData());
+    }
+
+    @Test
+    public void complexAugmentationCreatedTest() {
+        TestListener listener = createListener(LogicalDatastoreType.CONFIGURATION, COMPLEX_AUGMENT,
+                AsyncDataBroker.DataChangeScope.SUBTREE);
+        WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP, top());
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP_FOO, topLevelList(new TopLevelListKey(TOP_FOO_KEY)));
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, COMPLEX_AUGMENT, complexUsesAugment(LIST_VIA_USES_KEY));
+        assertCommit(writeTx.submit());
+        assertTrue(listener.hasEvent());
+        AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+        assertContains(event.getCreatedData(), COMPLEX_AUGMENT);
+        assertContains(event.getCreatedData(), COMPLEX_AUGMENT.child(ListViaUses.class, LIST_VIA_USES_KEY));
+        assertEmpty(event.getUpdatedData());
+        assertEmpty(event.getOriginalData());
+        assertEmpty(event.getRemovedPaths());
+    }
+
+    @Test
+    public void complexAugmentationUpdatedTest() {
+        WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP, top());
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP_FOO, topLevelList(new TopLevelListKey(TOP_FOO_KEY)));
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, COMPLEX_AUGMENT, complexUsesAugment(LIST_VIA_USES_KEY));
+        assertCommit(writeTx.submit());
+        TestListener listener = createListener(LogicalDatastoreType.CONFIGURATION, COMPLEX_AUGMENT,
+                AsyncDataBroker.DataChangeScope.SUBTREE);
+        writeTx = getDataBroker().newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, COMPLEX_AUGMENT, complexUsesAugment(LIST_VIA_USES_KEY_MOD));
+        assertCommit(writeTx.submit());
+        assertTrue(listener.hasEvent());
+        AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+        assertContains(event.getUpdatedData(), COMPLEX_AUGMENT);
+        assertContains(event.getCreatedData(), COMPLEX_AUGMENT.child(ListViaUses.class, LIST_VIA_USES_KEY_MOD));
+        assertContains(event.getRemovedPaths(), COMPLEX_AUGMENT.child(ListViaUses.class, LIST_VIA_USES_KEY));
+        assertContains(event.getOriginalData(), COMPLEX_AUGMENT);
+        assertContains(event.getOriginalData(), COMPLEX_AUGMENT.child(ListViaUses.class, LIST_VIA_USES_KEY));
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/DataTreeChangeListenerTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/DataTreeChangeListenerTest.java
new file mode 100644 (file)
index 0000000..4a4137c
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_BAR_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_FOO_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.USES_ONE_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.complexUsesAugment;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.path;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.top;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.topLevelList;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.SettableFuture;
+import java.util.Collection;
+import java.util.concurrent.TimeUnit;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMDataBrokerAdapter;
+import org.opendaylight.controller.md.sal.binding.test.AbstractDataBrokerTest;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.TwoLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+
+public class DataTreeChangeListenerTest extends AbstractDataBrokerTest {
+
+    private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.create(Top.class);
+    private static final PathArgument TOP_ARGUMENT= TOP_PATH.getPathArguments().iterator().next();
+    private static final InstanceIdentifier<TopLevelList> FOO_PATH = path(TOP_FOO_KEY);
+    private static final PathArgument FOO_ARGUMENT = Iterables.getLast(FOO_PATH.getPathArguments());
+    private static final TopLevelList FOO_DATA = topLevelList(TOP_FOO_KEY, complexUsesAugment(USES_ONE_KEY));
+    private static final InstanceIdentifier<TopLevelList> BAR_PATH = path(TOP_BAR_KEY);
+    private static final PathArgument BAR_ARGUMENT = Iterables.getLast(BAR_PATH.getPathArguments());
+    private static final TopLevelList BAR_DATA = topLevelList(TOP_BAR_KEY);
+private static final DataTreeIdentifier<Top> TOP_IDENTIFIER = new DataTreeIdentifier<Top>(LogicalDatastoreType.OPERATIONAL,
+            TOP_PATH);
+
+    private static final Top TOP_INITIAL_DATA = top(FOO_DATA);
+
+    private BindingDOMDataBrokerAdapter dataBrokerImpl;
+
+    private static final class EventCapturingListener<T extends DataObject> implements DataTreeChangeListener<T> {
+
+        private SettableFuture<Collection<DataTreeModification<T>>> changes = SettableFuture.create();
+
+        @Override
+        public void onDataTreeChanged(final Collection<DataTreeModification<T>> changes) {
+            this.changes.set(changes);
+
+        }
+
+        Collection<DataTreeModification<T>> nextEvent() throws Exception {
+            final Collection<DataTreeModification<T>> result = changes.get(200,TimeUnit.MILLISECONDS);
+            changes = SettableFuture.create();
+            return result;
+        }
+
+    }
+
+    @Override
+    protected Iterable<YangModuleInfo> getModuleInfos() throws Exception {
+        return ImmutableSet.of(
+                BindingReflections.getModuleInfo(TwoLevelList.class),
+                BindingReflections.getModuleInfo(TreeComplexUsesAugment.class)
+                );
+    }
+
+    @Override
+    protected void setupWithDataBroker(final DataBroker dataBroker) {
+        dataBrokerImpl = (BindingDOMDataBrokerAdapter) dataBroker;
+    }
+
+    @Test
+    public void testTopLevelListener() throws Exception {
+        final EventCapturingListener<Top> listener = new EventCapturingListener<>();
+        dataBrokerImpl.registerDataTreeChangeListener(TOP_IDENTIFIER, listener);
+
+        createAndVerifyTop(listener);
+
+        putTx(BAR_PATH, BAR_DATA).submit().checkedGet();
+        final DataObjectModification<Top> afterBarPutEvent = Iterables.getOnlyElement(listener.nextEvent()).getRootNode();
+        verifyModification(afterBarPutEvent, TOP_ARGUMENT, ModificationType.SUBTREE_MODIFIED);
+        final DataObjectModification<TopLevelList> barPutMod = afterBarPutEvent.getModifiedChildListItem(TopLevelList.class, TOP_BAR_KEY);
+        assertNotNull(barPutMod);
+        verifyModification(barPutMod, BAR_ARGUMENT, ModificationType.WRITE);
+
+        deleteTx(BAR_PATH).submit().checkedGet();
+        final DataObjectModification<Top> afterBarDeleteEvent = Iterables.getOnlyElement(listener.nextEvent()).getRootNode();
+        verifyModification(afterBarDeleteEvent, TOP_ARGUMENT, ModificationType.SUBTREE_MODIFIED);
+        final DataObjectModification<TopLevelList> barDeleteMod = afterBarDeleteEvent.getModifiedChildListItem(TopLevelList.class, TOP_BAR_KEY);
+        verifyModification(barDeleteMod, BAR_ARGUMENT, ModificationType.DELETE);
+    }
+
+    @Test
+    public void testWildcardedListListener() throws Exception {
+        final EventCapturingListener<TopLevelList> listener = new EventCapturingListener<>();
+        final DataTreeIdentifier<TopLevelList> wildcard = new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, TOP_PATH.child(TopLevelList.class));
+        dataBrokerImpl.registerDataTreeChangeListener(wildcard, listener);
+
+        putTx(TOP_PATH, TOP_INITIAL_DATA).submit().checkedGet();
+
+        final DataTreeModification<TopLevelList> fooWriteEvent = Iterables.getOnlyElement(listener.nextEvent());
+        assertEquals(FOO_PATH, fooWriteEvent.getRootPath().getRootIdentifier());
+        verifyModification(fooWriteEvent.getRootNode(), FOO_ARGUMENT, ModificationType.WRITE);
+
+        putTx(BAR_PATH, BAR_DATA).submit().checkedGet();
+        final DataTreeModification<TopLevelList> barWriteEvent = Iterables.getOnlyElement(listener.nextEvent());
+        assertEquals(BAR_PATH, barWriteEvent.getRootPath().getRootIdentifier());
+        verifyModification(barWriteEvent.getRootNode(), BAR_ARGUMENT, ModificationType.WRITE);
+
+        deleteTx(BAR_PATH).submit().checkedGet();
+        final DataTreeModification<TopLevelList> barDeleteEvent = Iterables.getOnlyElement(listener.nextEvent());
+        assertEquals(BAR_PATH, barDeleteEvent.getRootPath().getRootIdentifier());
+        verifyModification(barDeleteEvent.getRootNode(), BAR_ARGUMENT, ModificationType.DELETE);
+    }
+
+
+
+    private void createAndVerifyTop(final EventCapturingListener<Top> listener) throws Exception {
+        putTx(TOP_PATH,TOP_INITIAL_DATA).submit().checkedGet();
+        final Collection<DataTreeModification<Top>> events = listener.nextEvent();
+
+        assertFalse("Non empty collection should be received.",events.isEmpty());
+        final DataTreeModification<Top> initialWrite = Iterables.getOnlyElement(events);
+        final DataObjectModification<? extends DataObject> initialNode = initialWrite.getRootNode();
+        verifyModification(initialNode,TOP_PATH.getPathArguments().iterator().next(),ModificationType.WRITE);
+        assertEquals(TOP_INITIAL_DATA, initialNode.getDataAfter());
+    }
+
+    private void verifyModification(final DataObjectModification<? extends DataObject> barWrite, final PathArgument pathArg,
+            final ModificationType eventType) {
+        assertEquals(pathArg.getType(), barWrite.getDataType());
+        assertEquals(eventType,barWrite.getModificationType());
+        assertEquals(pathArg, barWrite.getIdentifier());
+    }
+
+    private <T extends DataObject> WriteTransaction putTx(final InstanceIdentifier<T> path,final T data) {
+        final WriteTransaction tx = dataBrokerImpl.newWriteOnlyTransaction();
+        tx.put(LogicalDatastoreType.OPERATIONAL, path, data);
+        return tx;
+    }
+
+    private WriteTransaction deleteTx(final InstanceIdentifier<?> path) {
+        final WriteTransaction tx = dataBrokerImpl.newWriteOnlyTransaction();
+        tx.delete(LogicalDatastoreType.OPERATIONAL, path);
+        return tx;
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/ForwardedBackwardsCompatibleDataBrokerTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/ForwardedBackwardsCompatibleDataBrokerTest.java
new file mode 100644 (file)
index 0000000..7598882
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl.test;
+
+import static junit.framework.TestCase.assertNotNull;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.compat.HydrogenDataBrokerAdapter;
+import org.opendaylight.controller.md.sal.binding.test.AbstractDataBrokerTest;
+import org.opendaylight.controller.md.sal.binding.test.DataBrokerTestCustomizer;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+
+@Deprecated
+public class ForwardedBackwardsCompatibleDataBrokerTest extends
+    AbstractDataBrokerTest {
+
+    private HydrogenDataBrokerAdapter dataBroker;
+    private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.create(Top.class);
+    private static final TopLevelListKey TOP_LIST_KEY = new TopLevelListKey("foo");
+    private static final InstanceIdentifier<TopLevelList> NODE_PATH = TOP_PATH.child(TopLevelList.class, TOP_LIST_KEY);
+    private static final TopLevelList NODE = new TopLevelListBuilder().setKey(TOP_LIST_KEY).build();
+
+    @Override
+    protected Iterable<YangModuleInfo> getModuleInfos() throws Exception {
+        return ImmutableSet.of(BindingReflections.getModuleInfo(TopLevelList.class));
+    }
+
+    @Override
+    protected DataBrokerTestCustomizer createDataBrokerTestCustomizer() {
+        return new DataBrokerTestCustomizer();
+    }
+
+    @Override
+    protected void setupWithDataBroker(final DataBroker dataBroker) {
+        super.setupWithDataBroker(dataBroker);
+        this.dataBroker = new HydrogenDataBrokerAdapter(dataBroker);
+    }
+
+
+
+
+    /**
+     * The purpose of this test is to exercise the backwards compatible broker
+     * <p>
+     * This test tries to execute the code which ensures that the parents
+     * for a given node get automatically created.
+     *
+     * @see org.opendaylight.controller.md.sal.binding.impl.AbstractReadWriteTransaction#ensureParentsByMerge(org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier, org.opendaylight.yangtools.yang.binding.InstanceIdentifier)
+     */
+    @Test
+    public void testEnsureParentsByMerge() throws InterruptedException, ExecutionException {
+        final DataModificationTransaction writeTx =
+            dataBroker.beginTransaction();
+
+        writeTx.putOperationalData(NODE_PATH, NODE);
+
+        writeTx.commit().get();
+
+        // TOP_PATH should exist as it is the parent of NODE_PATH
+        final DataObject object = dataBroker.readOperationalData(TOP_PATH);
+
+        assertNotNull(object);
+
+    }
+
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/ForwardedNotificationAdapterTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/ForwardedNotificationAdapterTest.java
new file mode 100644 (file)
index 0000000..cf7ad16
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.junit.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
+import org.opendaylight.controller.md.sal.binding.test.AbstractNotificationBrokerTest;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.OpendaylightMdsalListTestListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.TwoLevelListChanged;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.TwoLevelListChangedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ForwardedNotificationAdapterTest extends AbstractNotificationBrokerTest {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ForwardedNotificationAdapterTest.class);
+
+    @Override
+    protected Iterable<YangModuleInfo> getModuleInfos() throws Exception {
+        return ImmutableSet.of(BindingReflections.getModuleInfo(TwoLevelListChanged.class));
+
+    }
+
+    private TwoLevelListChanged createTestData() {
+        final TwoLevelListChangedBuilder tb = new TwoLevelListChangedBuilder();
+        tb.setTopLevelList(ImmutableList.of(new TopLevelListBuilder().setKey(new TopLevelListKey("test")).build()));
+        return tb.build();
+    }
+
+    @Test
+    public void testNotifSubscription() throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TwoLevelListChanged testData = createTestData();
+
+        final TestNotifListener testNotifListener = new TestNotifListener(latch);
+        final ListenerRegistration<TestNotifListener> listenerRegistration = getNotificationService()
+                .registerNotificationListener(testNotifListener);
+        getNotificationPublishService().putNotification(testData);
+
+        latch.await();
+        assertTrue(testNotifListener.getReceivedNotifications().size() == 1);
+        assertEquals(testData, testNotifListener.getReceivedNotifications().get(0));
+
+        listenerRegistration.close();
+    }
+
+    @Test
+    public void testNotifSubscription2() throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TwoLevelListChanged testData = createTestData();
+
+        final TestNotifListener testNotifListener = new TestNotifListener(latch);
+        final ListenerRegistration<TestNotifListener> listenerRegistration = getNotificationService()
+                .registerNotificationListener(testNotifListener);
+        try {
+            getNotificationPublishService().offerNotification(testData).get(1, TimeUnit.SECONDS);
+        } catch (ExecutionException | TimeoutException e) {
+            LOG.error("Notification delivery failed", e);
+            Assert.fail("notification should be delivered");
+        }
+
+        latch.await();
+        assertTrue(testNotifListener.getReceivedNotifications().size() == 1);
+        assertEquals(testData, testNotifListener.getReceivedNotifications().get(0));
+
+        listenerRegistration.close();
+    }
+
+    @Test
+    public void testNotifSubscription3() throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TwoLevelListChanged testData = createTestData();
+
+        final TestNotifListener testNotifListener = new TestNotifListener(latch);
+        final ListenerRegistration<TestNotifListener> listenerRegistration = getNotificationService()
+                .registerNotificationListener(testNotifListener);
+        assertNotSame(NotificationPublishService.REJECTED,
+                getNotificationPublishService().offerNotification(testData, 5, TimeUnit.SECONDS));
+
+        latch.await();
+        assertTrue(testNotifListener.getReceivedNotifications().size() == 1);
+        assertEquals(testData, testNotifListener.getReceivedNotifications().get(0));
+
+        listenerRegistration.close();
+    }
+
+    private static class TestNotifListener implements OpendaylightMdsalListTestListener {
+        private List<TwoLevelListChanged> receivedNotifications = new ArrayList<>();
+        private CountDownLatch latch;
+
+        public TestNotifListener(CountDownLatch latch) {
+            this.latch = latch;
+        }
+
+        @Override
+        public void onTwoLevelListChanged(TwoLevelListChanged notification) {
+            receivedNotifications.add(notification);
+            latch.countDown();
+        }
+
+        public List<TwoLevelListChanged> getReceivedNotifications() {
+            return receivedNotifications;
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/ListInsertionDataChangeListenerTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/ListInsertionDataChangeListenerTest.java
new file mode 100644 (file)
index 0000000..5449330
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl.test;
+import static org.junit.Assert.assertFalse;
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertContains;
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertEmpty;
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertNotContains;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_BAR_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_FOO_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.top;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.topLevelList;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.binding.test.AbstractDataChangeListenerTest;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ *
+ * This testsuite tests explanation for data change scope and data modifications
+ * which were described in
+ * https://lists.opendaylight.org/pipermail/controller-dev/2014-July/005541.html
+ *
+ *
+ */
+public class ListInsertionDataChangeListenerTest extends AbstractDataChangeListenerTest{
+
+    private static final InstanceIdentifier<Top> TOP = InstanceIdentifier.create(Top.class);
+    private static final InstanceIdentifier<TopLevelList> WILDCARDED = TOP.child(TopLevelList.class);
+    private static final InstanceIdentifier<TopLevelList> TOP_FOO = TOP.child(TopLevelList.class, TOP_FOO_KEY);
+    private static final InstanceIdentifier<TopLevelList> TOP_BAR = TOP.child(TopLevelList.class, TOP_BAR_KEY);
+
+
+    @Override
+    protected void setupWithDataBroker(final DataBroker dataBroker) {
+        WriteTransaction initialTx = dataBroker.newWriteOnlyTransaction();
+        initialTx.put(CONFIGURATION, TOP, top(topLevelList(TOP_FOO_KEY)));
+        assertCommit(initialTx.submit());
+    }
+
+    @Test
+    public void replaceTopNodeSubtreeListeners() {
+        TestListener topListener = createListener(CONFIGURATION, TOP, DataChangeScope.SUBTREE);
+        TestListener allListener = createListener(CONFIGURATION, WILDCARDED, DataChangeScope.SUBTREE);
+        TestListener fooListener = createListener(CONFIGURATION, TOP_FOO, DataChangeScope.SUBTREE);
+        TestListener barListener = createListener(CONFIGURATION, TOP_BAR, DataChangeScope.SUBTREE);
+
+        ReadWriteTransaction writeTx = getDataBroker().newReadWriteTransaction();
+        writeTx.put(CONFIGURATION, TOP, top(topLevelList(TOP_BAR_KEY)));
+        assertCommit(writeTx.submit());
+        AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> top = topListener.event();
+        AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> all = allListener.event();
+        AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> foo = fooListener.event();
+        AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> bar = barListener.event();
+
+        // Listener for TOP element
+        assertContains(top.getOriginalData(), TOP,TOP_FOO);
+        assertContains(top.getCreatedData(), TOP_BAR);
+        assertContains(top.getUpdatedData(), TOP);
+        assertContains(top.getRemovedPaths(), TOP_FOO);
+
+        /*
+         *  Listener for all list items
+         *
+         *  Updated should be empty, since no list item was
+         *  updated, items were only removed and added
+         */
+        assertContains(all.getOriginalData(), TOP_FOO);
+        assertContains(all.getCreatedData(), TOP_BAR);
+        assertEmpty(all.getUpdatedData());
+        assertContains(all.getRemovedPaths(), TOP_FOO);
+
+
+        /*
+         *  Listener for all Foo item
+         *
+         *  This one should see only Foo item removed
+         */
+        assertContains(foo.getOriginalData(), TOP_FOO);
+        assertEmpty(foo.getCreatedData());
+        assertEmpty(foo.getUpdatedData());
+        assertContains(foo.getRemovedPaths(), TOP_FOO);
+
+        /*
+         *  Listener for bar list items
+         *
+         *  Updated should be empty, since no list item was
+         *  updated, items were only removed and added
+         */
+        assertEmpty(bar.getOriginalData());
+        assertContains(bar.getCreatedData(), TOP_BAR);
+        assertEmpty(bar.getUpdatedData());
+        assertEmpty(bar.getRemovedPaths());
+    }
+
+    @Test
+    public void mergeTopNodeSubtreeListeners() {
+        TestListener topListener = createListener(CONFIGURATION, TOP, DataChangeScope.SUBTREE);
+        TestListener allListener = createListener(CONFIGURATION, WILDCARDED, DataChangeScope.SUBTREE);
+        TestListener fooListener = createListener(CONFIGURATION, TOP_FOO, DataChangeScope.SUBTREE);
+        TestListener barListener = createListener(CONFIGURATION, TOP_BAR, DataChangeScope.SUBTREE);
+
+        ReadWriteTransaction writeTx = getDataBroker().newReadWriteTransaction();
+        writeTx.merge(CONFIGURATION, TOP, top(topLevelList(TOP_BAR_KEY)));
+        assertCommit(writeTx.submit());
+
+        verifyBarOnlyAdded(topListener,allListener,fooListener,barListener);
+    }
+
+    @Test
+    public void putTopBarNodeSubtreeListeners() {
+        TestListener topListener = createListener(CONFIGURATION, TOP, DataChangeScope.SUBTREE);
+        TestListener allListener = createListener(CONFIGURATION, WILDCARDED, DataChangeScope.SUBTREE);
+        TestListener fooListener = createListener(CONFIGURATION, TOP_FOO, DataChangeScope.SUBTREE);
+        TestListener barListener = createListener(CONFIGURATION, TOP_BAR, DataChangeScope.SUBTREE);
+
+        ReadWriteTransaction writeTx = getDataBroker().newReadWriteTransaction();
+        writeTx.put(CONFIGURATION, TOP_BAR, topLevelList(TOP_BAR_KEY));
+        assertCommit(writeTx.submit());
+
+        verifyBarOnlyAdded(topListener,allListener,fooListener,barListener);
+    }
+
+    @Test
+    public void mergeTopBarNodeSubtreeListeners() {
+        TestListener topListener = createListener(CONFIGURATION, TOP, DataChangeScope.SUBTREE);
+        TestListener allListener = createListener(CONFIGURATION, WILDCARDED, DataChangeScope.SUBTREE);
+        TestListener fooListener = createListener(CONFIGURATION, TOP_FOO, DataChangeScope.SUBTREE);
+        TestListener barListener = createListener(CONFIGURATION, TOP_BAR, DataChangeScope.SUBTREE);
+
+        ReadWriteTransaction writeTx = getDataBroker().newReadWriteTransaction();
+        writeTx.merge(CONFIGURATION, TOP_BAR, topLevelList(TOP_BAR_KEY));
+        assertCommit(writeTx.submit());
+
+        verifyBarOnlyAdded(topListener,allListener,fooListener,barListener);
+    }
+
+    private void verifyBarOnlyAdded(final TestListener top, final TestListener all, final TestListener foo,
+            final TestListener bar) {
+
+        assertFalse(foo.hasEvent());
+
+        // Listener for TOP element
+        assertContains(top.event().getOriginalData(), TOP);
+        assertNotContains(top.event().getOriginalData(),TOP_FOO);
+        assertContains(top.event().getCreatedData(), TOP_BAR);
+        assertContains(top.event().getUpdatedData(), TOP);
+        assertEmpty(top.event().getRemovedPaths());
+
+        /*
+         *  Listener for all list items
+         *
+         *  Updated should be empty, since no list item was
+         *  updated, items were only removed and added
+         */
+        assertEmpty(all.event().getOriginalData());
+        assertContains(all.event().getCreatedData(), TOP_BAR);
+        assertEmpty(all.event().getUpdatedData());
+        assertEmpty(all.event().getRemovedPaths());
+
+        /*
+         *  Listener for all Foo item
+         *
+         *  Foo Listener should not have foo event
+         */
+        assertFalse(foo.hasEvent());
+
+        /*
+         *  Listener for bar list items
+         *
+         *  Updated should be empty, since no list item was
+         *  updated, items were only removed and added
+         */
+        assertEmpty(bar.event().getOriginalData());
+        assertContains(bar.event().getCreatedData(), TOP_BAR);
+        assertEmpty(bar.event().getUpdatedData());
+        assertEmpty(bar.event().getRemovedPaths());
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/WriteTransactionTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/WriteTransactionTest.java
new file mode 100644 (file)
index 0000000..b504837
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl.test;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.concurrent.ExecutionException;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.binding.test.AbstractDataBrokerTest;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.TopBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+
+
+public class WriteTransactionTest extends AbstractDataBrokerTest {
+
+    private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.create(Top.class);
+    private static final TopLevelListKey TOP_LIST_KEY = new TopLevelListKey("foo");
+    private static final InstanceIdentifier<TopLevelList> NODE_PATH = TOP_PATH.child(TopLevelList.class, TOP_LIST_KEY);
+    private static final TopLevelList NODE = new TopLevelListBuilder().setKey(TOP_LIST_KEY).build();
+    @Test
+    public void test() throws InterruptedException, ExecutionException {
+        WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.OPERATIONAL, TOP_PATH, new TopBuilder().build());
+        writeTx.put(LogicalDatastoreType.OPERATIONAL, NODE_PATH, NODE);
+        writeTx.submit().get();
+    }
+
+    @Test
+    public void testPutCreateParentsSuccess() throws TransactionCommitFailedException, InterruptedException, ExecutionException {
+
+        WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.OPERATIONAL, NODE_PATH, NODE,true);
+        writeTx.submit().checkedGet();
+
+        ReadOnlyTransaction readTx = getDataBroker().newReadOnlyTransaction();
+        Optional<Top> topNode = readTx.read(LogicalDatastoreType.OPERATIONAL, TOP_PATH).get();
+        assertTrue("Top node must exists after commit",topNode.isPresent());
+        Optional<TopLevelList> listNode = readTx.read(LogicalDatastoreType.OPERATIONAL, NODE_PATH).get();
+        assertTrue("List node must exists after commit",listNode.isPresent());
+    }
+
+    @Test
+    public void testMergeCreateParentsSuccess() throws TransactionCommitFailedException, InterruptedException, ExecutionException {
+
+        WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+        writeTx.merge(LogicalDatastoreType.OPERATIONAL, NODE_PATH, NODE,true);
+        writeTx.submit().checkedGet();
+
+        ReadOnlyTransaction readTx = getDataBroker().newReadOnlyTransaction();
+        Optional<Top> topNode = readTx.read(LogicalDatastoreType.OPERATIONAL, TOP_PATH).get();
+        assertTrue("Top node must exists after commit",topNode.isPresent());
+        Optional<TopLevelList> listNode = readTx.read(LogicalDatastoreType.OPERATIONAL, NODE_PATH).get();
+        assertTrue("List node must exists after commit",listNode.isPresent());
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/AbstractDataBrokerTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/AbstractDataBrokerTest.java
new file mode 100644 (file)
index 0000000..5789270
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.test;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+public class AbstractDataBrokerTest extends AbstractSchemaAwareTest {
+
+    private DataBrokerTestCustomizer testCustomizer;
+    private DataBroker dataBroker;
+    private DOMDataBroker domBroker;
+
+
+    @Override
+    protected void setupWithSchema(final SchemaContext context) {
+        testCustomizer = createDataBrokerTestCustomizer();
+        dataBroker = testCustomizer.createDataBroker();
+        domBroker = testCustomizer.createDOMDataBroker();
+        testCustomizer.updateSchema(context);
+        setupWithDataBroker(dataBroker);
+    }
+
+    protected void setupWithDataBroker(final DataBroker dataBroker) {
+        // Intentionally left No-op, subclasses may customize it
+    }
+
+   protected DataBrokerTestCustomizer createDataBrokerTestCustomizer() {
+        return new DataBrokerTestCustomizer();
+    }
+
+    public DataBroker getDataBroker() {
+        return dataBroker;
+    }
+
+    public DOMDataBroker getDomBroker() {
+        return domBroker;
+    }
+
+    protected static final void assertCommit(final ListenableFuture<Void> commit) {
+        try {
+            commit.get(500, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException | ExecutionException | TimeoutException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/AbstractDataChangeListenerTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/AbstractDataChangeListenerTest.java
new file mode 100644 (file)
index 0000000..7742f37
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.test;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.util.concurrent.SettableFuture;
+
+public abstract class AbstractDataChangeListenerTest extends AbstractDataBrokerTest {
+
+    protected static final class TestListener implements DataChangeListener {
+
+        private final SettableFuture<AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject>> event;
+        private boolean capture = false;
+
+        private TestListener() {
+            event = SettableFuture.create();
+        }
+
+        @Override
+        public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> arg) {
+            if (capture) {
+                event.set(arg);
+            }
+        }
+
+        public AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event() {
+            try {
+                return event.get(500, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException | TimeoutException | ExecutionException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+
+        public boolean hasEvent() {
+            return event.isDone();
+        }
+
+        public void startCapture() {
+            this.capture = true;
+        }
+    }
+
+    protected final TestListener createListener(final LogicalDatastoreType store, final InstanceIdentifier<?> path,
+            final DataChangeScope scope) {
+        TestListener listener = new TestListener();
+        getDataBroker().registerDataChangeListener(store, path, listener, scope);
+        listener.startCapture();
+        return listener;
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/AbstractNotificationBrokerTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/AbstractNotificationBrokerTest.java
new file mode 100644 (file)
index 0000000..6030983
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.test;
+
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
+import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.controller.md.sal.dom.broker.impl.DOMNotificationRouter;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class AbstractNotificationBrokerTest extends AbstractSchemaAwareTest{
+    private BindingToNormalizedNodeCodec bindingToNormalizedNodeCodec;
+    private DOMNotificationRouter domNotificationRouter;
+    private NotificationService notificationService;
+    private NotificationPublishService notificationPublishService;
+
+
+    @Override
+    protected void setupWithSchema(final SchemaContext context) {
+        final DataBrokerTestCustomizer testCustomizer = createDataBrokerTestCustomizer();
+        domNotificationRouter = testCustomizer.getDomNotificationRouter();
+        notificationService = testCustomizer.createNotificationService();
+        notificationPublishService = testCustomizer.createNotificationPublishService();
+        bindingToNormalizedNodeCodec = testCustomizer.getBindingToNormalized();
+        testCustomizer.updateSchema(context);
+    }
+
+    protected DataBrokerTestCustomizer createDataBrokerTestCustomizer() {
+        return new DataBrokerTestCustomizer();
+    }
+
+    public NotificationService getNotificationService() {
+        return notificationService;
+    }
+
+    public NotificationPublishService getNotificationPublishService() {
+        return notificationPublishService;
+    }
+
+    public DOMNotificationRouter getDomNotificationRouter() {
+        return domNotificationRouter;
+    }
+
+    public BindingToNormalizedNodeCodec getBindingToNormalizedNodeCodec() {
+        return bindingToNormalizedNodeCodec;
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/AbstractSchemaAwareTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/AbstractSchemaAwareTest.java
new file mode 100644 (file)
index 0000000..6d758aa
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.test;
+
+import org.junit.Before;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public abstract class AbstractSchemaAwareTest  {
+
+    private Iterable<YangModuleInfo> moduleInfos;
+    private SchemaContext schemaContext;
+
+
+    protected Iterable<YangModuleInfo> getModuleInfos() throws Exception {
+        return BindingReflections.loadModuleInfos();
+    }
+
+
+    @Before
+    public final void setup() throws Exception {
+        moduleInfos = getModuleInfos();
+        ModuleInfoBackedContext moduleContext = ModuleInfoBackedContext.create();
+        moduleContext.addModuleInfos(moduleInfos);
+        schemaContext = moduleContext.tryToCreateSchemaContext().get();
+        setupWithSchema(schemaContext);
+    }
+
+    /**
+     * Setups test with Schema context.
+     * This method is called before {@link #setupWithSchemaService(SchemaService)}
+     *
+     * @param context
+     */
+    protected abstract void setupWithSchema(SchemaContext context);
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/AssertCollections.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/AssertCollections.java
new file mode 100644 (file)
index 0000000..d96ebff
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.test;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.junit.Assert;
+
+public class AssertCollections {
+
+    public static void assertEmpty(final Collection<?> set) {
+        Assert.assertTrue(set.isEmpty());
+    }
+
+    public static void assertEmpty(final Map<?,?> set) {
+        Assert.assertTrue(set.isEmpty());
+    }
+
+    public static void assertContains(final Collection<?> set, final Object... values) {
+        for (Object key : values) {
+            Assert.assertTrue(set.contains(key));
+        }
+
+    }
+
+    public static void assertNotContains(final Collection<?> set, final Object... values) {
+        for (Object key : values) {
+            Assert.assertFalse(set.contains(key));
+        }
+    }
+
+    public static void assertContains(final Map<?,?> map, final Object... values) {
+        for (Object key : values) {
+            Assert.assertTrue(map.containsKey(key));
+        }
+    }
+
+    public static void assertNotContains(final Map<?,?> map, final Object... values) {
+        for (Object key : values) {
+            Assert.assertFalse(map.containsKey(key));
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/DataBrokerTestCustomizer.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/DataBrokerTestCustomizer.java
new file mode 100644 (file)
index 0000000..ab6d144
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import javassist.ClassPool;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
+import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMDataBrokerAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMNotificationPublishServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMNotificationServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.broker.impl.DOMNotificationRouter;
+import org.opendaylight.controller.md.sal.dom.broker.impl.SerializedDOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.sal.binding.test.util.MockSchemaService;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+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.GeneratedClassLoadingStrategy;
+import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class DataBrokerTestCustomizer {
+
+    private DOMDataBroker domDataBroker;
+    private final DOMNotificationRouter domNotificationRouter;
+    private final MockSchemaService schemaService;
+    private ImmutableMap<LogicalDatastoreType, DOMStore> datastores;
+    private final BindingToNormalizedNodeCodec bindingToNormalized;
+
+    public ImmutableMap<LogicalDatastoreType, DOMStore> createDatastores() {
+        return ImmutableMap.<LogicalDatastoreType, DOMStore>builder()
+                .put(LogicalDatastoreType.OPERATIONAL, createOperationalDatastore())
+                .put(LogicalDatastoreType.CONFIGURATION,createConfigurationDatastore())
+                .build();
+    }
+
+    public DataBrokerTestCustomizer() {
+        schemaService = new MockSchemaService();
+        final ClassPool pool = ClassPool.getDefault();
+        final DataObjectSerializerGenerator generator = StreamWriterGenerator.create(JavassistUtils.forClassPool(pool));
+        final BindingNormalizedNodeCodecRegistry codecRegistry = new BindingNormalizedNodeCodecRegistry(generator);
+        final GeneratedClassLoadingStrategy loading = GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy();
+        bindingToNormalized = new BindingToNormalizedNodeCodec(loading, codecRegistry);
+        schemaService.registerSchemaContextListener(bindingToNormalized);
+        domNotificationRouter = DOMNotificationRouter.create(16);
+    }
+
+    public DOMStore createConfigurationDatastore() {
+        final InMemoryDOMDataStore store = new InMemoryDOMDataStore("CFG", MoreExecutors.sameThreadExecutor());
+        schemaService.registerSchemaContextListener(store);
+        return store;
+    }
+
+    public DOMStore createOperationalDatastore() {
+        final InMemoryDOMDataStore store = new InMemoryDOMDataStore("OPER", MoreExecutors.sameThreadExecutor());
+        schemaService.registerSchemaContextListener(store);
+        return store;
+    }
+
+    public DOMDataBroker createDOMDataBroker() {
+        return new SerializedDOMDataBroker(getDatastores(), getCommitCoordinatorExecutor());
+    }
+
+    public NotificationService createNotificationService() {
+        return new BindingDOMNotificationServiceAdapter(bindingToNormalized.getCodecRegistry(), domNotificationRouter);
+    }
+
+    public NotificationPublishService createNotificationPublishService() {
+        return new BindingDOMNotificationPublishServiceAdapter(bindingToNormalized, domNotificationRouter);
+    }
+
+
+    public ListeningExecutorService getCommitCoordinatorExecutor() {
+        return MoreExecutors.sameThreadExecutor();
+    }
+
+    public DataBroker createDataBroker() {
+        return new BindingDOMDataBrokerAdapter(getDOMDataBroker(), bindingToNormalized);
+    }
+
+    public BindingToNormalizedNodeCodec getBindingToNormalized() {
+        return bindingToNormalized;
+    }
+
+    public SchemaService getSchemaService() {
+        return schemaService;
+    }
+
+    private DOMDataBroker getDOMDataBroker() {
+        if(domDataBroker == null) {
+            domDataBroker = createDOMDataBroker();
+        }
+        return domDataBroker;
+    }
+
+    private synchronized ImmutableMap<LogicalDatastoreType, DOMStore> getDatastores() {
+        if (datastores == null) {
+            datastores = createDatastores();
+        }
+        return datastores;
+    }
+
+    public void updateSchema(final SchemaContext ctx) {
+        schemaService.changeSchema(ctx);
+    }
+
+    public DOMNotificationRouter getDomNotificationRouter() {
+        return domNotificationRouter;
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/AbstractDataServiceTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/AbstractDataServiceTest.java
new file mode 100644 (file)
index 0000000..7acdafe
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test;
+
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.junit.Before;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.binding.test.util.BindingBrokerTestFactory;
+import org.opendaylight.controller.sal.binding.test.util.BindingTestContext;
+
+public abstract class AbstractDataServiceTest {
+
+    protected DataProviderService baDataService;
+
+    protected BindingTestContext testContext;
+
+    @Before
+    public void setUp() {
+        ListeningExecutorService executor = MoreExecutors.sameThreadExecutor();
+        BindingBrokerTestFactory factory = new BindingBrokerTestFactory();
+        factory.setExecutor(executor);
+        factory.setStartWithParsedSchema(getStartWithSchema());
+        testContext = factory.getTestContext();
+        testContext.start();
+
+        baDataService = testContext.getBindingDataBroker();
+    }
+
+    protected boolean getStartWithSchema() {
+        return true;
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/AugmentationVerifier.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/AugmentationVerifier.java
new file mode 100644 (file)
index 0000000..c8129e6
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test;
+
+import static org.junit.Assert.assertNotNull;
+import org.opendaylight.yangtools.yang.binding.Augmentable;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+
+public class AugmentationVerifier<T extends Augmentable<T>> {
+
+    private T object;
+
+    public AugmentationVerifier(T objectToVerify) {
+        this.object = objectToVerify;
+    }
+
+    public AugmentationVerifier<T> assertHasAugmentation(Class<? extends Augmentation<T>> augmentation) {
+        assertHasAugmentation(object, augmentation);
+        return (AugmentationVerifier<T>) this;
+    }
+
+    public static <T extends Augmentable<T>> void assertHasAugmentation(T object,
+            Class<? extends Augmentation<T>> augmentation) {
+        assertNotNull(object);
+        assertNotNull("Augmentation " + augmentation.getSimpleName() + " is not present.", object.getAugmentation(augmentation));
+    }
+
+    public static <T extends Augmentable<T>> AugmentationVerifier<T> from(T obj) {
+        return new AugmentationVerifier<T>(obj);
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/BindingTestUtilities.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/BindingTestUtilities.java
new file mode 100644 (file)
index 0000000..4dd3239
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test;
+
+public class BindingTestUtilities {
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/BarListener.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/BarListener.java
new file mode 100644 (file)
index 0000000..6c13372
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+
+public interface BarListener extends NotificationListener {
+
+    void onBarUpdate(BarUpdate notification);
+
+    void onFlowDelete(FlowDelete notification);
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/BarUpdate.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/BarUpdate.java
new file mode 100644 (file)
index 0000000..45f0313
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+public interface BarUpdate extends Grouping,Notification {
+
+
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/CompositeListener.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/CompositeListener.java
new file mode 100644 (file)
index 0000000..d9f752f
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+public interface CompositeListener extends FooListener,BarListener {
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/FlowDelete.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/FlowDelete.java
new file mode 100644 (file)
index 0000000..4a28aaa
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+public interface FlowDelete extends Notification{
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/FooListener.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/FooListener.java
new file mode 100644 (file)
index 0000000..cb8020e
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+
+public interface FooListener extends NotificationListener {
+
+    void onFooUpdate(FooUpdate notification);
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/FooService.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/FooService.java
new file mode 100644 (file)
index 0000000..2bd2119
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+public interface FooService extends RpcService {
+
+    Future<RpcResult<Void>> foo();
+
+    Future<RpcResult<Void>> simple(SimpleInput obj);
+
+    Future<RpcResult<Void>> inheritedContext(InheritedContextInput obj);
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/FooUpdate.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/FooUpdate.java
new file mode 100644 (file)
index 0000000..faf4576
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+public interface FooUpdate extends Notification {
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/Grouping.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/Grouping.java
new file mode 100644 (file)
index 0000000..5864845
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext;
+
+public interface Grouping {
+
+    @RoutingContext(BaseIdentity.class)
+    InstanceIdentifier<?> getInheritedIdentifier();
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/InheritedContextInput.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/InheritedContextInput.java
new file mode 100644 (file)
index 0000000..90375a0
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+public interface InheritedContextInput extends Grouping {
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/ReferencableObject.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/ReferencableObject.java
new file mode 100644 (file)
index 0000000..fa56507
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.DataRoot;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+
+public interface ReferencableObject extends DataObject,
+    Identifiable<ReferencableObjectKey>,ChildOf<DataRoot>{
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/ReferencableObjectKey.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/ReferencableObjectKey.java
new file mode 100644 (file)
index 0000000..68d7f6c
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.Identifier;
+
+public class ReferencableObjectKey implements Identifier<ReferencableObject> {
+    private static final long serialVersionUID = 1L;
+    final Integer value;
+
+    public ReferencableObjectKey(Integer _value) {
+        this.value = _value;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((value == null) ? 0 : value.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        ReferencableObjectKey other = (ReferencableObjectKey) obj;
+        if (value == null) {
+            if (other.value != null)
+                return false;
+        } else if (!value.equals(other.value))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "ReferencableObjectKey [value=" + value + "]";
+    }
+
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/SimpleInput.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/mock/SimpleInput.java
new file mode 100644 (file)
index 0000000..0a38c90
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.Augmentable;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext;
+
+public interface SimpleInput extends DataObject,Augmentable<SimpleInput> {
+
+    @RoutingContext(BaseIdentity.class)
+    InstanceIdentifier<?> getIdentifier();
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingBrokerTestFactory.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingBrokerTestFactory.java
new file mode 100644 (file)
index 0000000..c58e258
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.util;
+
+import java.util.concurrent.ExecutorService;
+
+import javassist.ClassPool;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+@Beta
+public class BindingBrokerTestFactory {
+
+    private static final ClassPool CLASS_POOL = ClassPool.getDefault();
+    private boolean startWithParsedSchema = true;
+    private ExecutorService executor;
+    private ClassPool classPool;
+
+
+    public boolean isStartWithParsedSchema() {
+        return startWithParsedSchema;
+    }
+
+    public void setStartWithParsedSchema(final boolean startWithParsedSchema) {
+        this.startWithParsedSchema = startWithParsedSchema;
+    }
+
+    public ExecutorService getExecutor() {
+        return executor;
+    }
+
+    public void setExecutor(final ExecutorService executor) {
+        this.executor = executor;
+    }
+
+
+    public BindingTestContext getTestContext() {
+        Preconditions.checkState(executor != null, "Executor is not set.");
+        ListeningExecutorService listenableExecutor = MoreExecutors.listeningDecorator(executor);
+        return new BindingTestContext(listenableExecutor, getClassPool(),startWithParsedSchema);
+    }
+
+    public ClassPool getClassPool() {
+        if(classPool == null) {
+            return CLASS_POOL;
+        }
+
+        return classPool;
+    }
+
+    public void setClassPool(final ClassPool classPool) {
+        this.classPool = classPool;
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java
new file mode 100644 (file)
index 0000000..5ca041a
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.util;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableClassToInstanceMap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.MutableClassToInstanceMap;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import javassist.ClassPool;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.MountPointService;
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
+import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.controller.md.sal.binding.compat.HeliumNotificationProviderServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.compat.HeliumRpcProviderRegistry;
+import org.opendaylight.controller.md.sal.binding.compat.HydrogenDataBrokerAdapter;
+import org.opendaylight.controller.md.sal.binding.compat.HydrogenMountProvisionServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMDataBrokerAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMMountPointServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMNotificationPublishServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMNotificationServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMRpcProviderServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMRpcServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+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.DOMRpcProviderService;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.md.sal.dom.broker.impl.DOMNotificationRouter;
+import org.opendaylight.controller.md.sal.dom.broker.impl.DOMRpcRouter;
+import org.opendaylight.controller.md.sal.dom.broker.impl.SerializedDOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.broker.impl.mount.DOMMountPointServiceImpl;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.binding.api.mount.MountProviderService;
+import org.opendaylight.controller.sal.binding.impl.RootBindingAwareBroker;
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.controller.sal.dom.broker.BrokerImpl;
+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.GeneratedClassLoadingStrategy;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+@Beta
+public class BindingTestContext implements AutoCloseable {
+
+
+    private BindingToNormalizedNodeCodec codec;
+
+    private RootBindingAwareBroker baBrokerImpl;
+
+    private HeliumNotificationProviderServiceAdapter baNotifyImpl;
+
+
+    private BrokerImpl biBrokerImpl;
+
+    private final ListeningExecutorService executor;
+    private final ClassPool classPool;
+
+    private final boolean startWithSchema;
+
+    private DOMMountPointService biMountImpl;
+
+    private ImmutableMap<LogicalDatastoreType, DOMStore> newDatastores;
+
+    @Deprecated
+    private DataProviderService baData;
+
+    private DOMDataBroker newDOMDataBroker;
+
+    private final MockSchemaService mockSchemaService = new MockSchemaService();
+
+    private DataBroker dataBroker;
+
+    private RpcConsumerRegistry baConsumerRpc;
+
+    private BindingDOMRpcProviderServiceAdapter baProviderRpc;
+    private DOMRpcRouter domRouter;
+
+    private NotificationPublishService publishService;
+
+    private NotificationService listenService;
+
+    private DOMNotificationPublishService domPublishService;
+
+    private DOMNotificationService domListenService;
+
+
+
+    public DOMDataBroker getDomAsyncDataBroker() {
+        return newDOMDataBroker;
+    }
+
+    public BindingToNormalizedNodeCodec getCodec() {
+        return codec;
+    }
+
+    protected BindingTestContext(final ListeningExecutorService executor, final ClassPool classPool, final boolean startWithSchema) {
+        this.executor = executor;
+        this.classPool = classPool;
+        this.startWithSchema = startWithSchema;
+    }
+
+    public void startDomDataBroker() {
+    }
+
+    public void startNewDataBroker() {
+        checkState(executor != null, "Executor needs to be set");
+        checkState(newDOMDataBroker != null, "DOM Data Broker must be set");
+        dataBroker = new BindingDOMDataBrokerAdapter(newDOMDataBroker, codec);
+    }
+
+    public void startNewDomDataBroker() {
+        checkState(executor != null, "Executor needs to be set");
+        final InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER", MoreExecutors.sameThreadExecutor());
+        final InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG", MoreExecutors.sameThreadExecutor());
+        newDatastores = ImmutableMap.<LogicalDatastoreType, DOMStore>builder()
+                .put(LogicalDatastoreType.OPERATIONAL, operStore)
+                .put(LogicalDatastoreType.CONFIGURATION, configStore)
+                .build();
+
+        newDOMDataBroker = new SerializedDOMDataBroker(newDatastores, executor);
+
+        mockSchemaService.registerSchemaContextListener(configStore);
+        mockSchemaService.registerSchemaContextListener(operStore);
+    }
+
+    public void startBindingDataBroker() {
+
+    }
+
+    public void startBindingBroker() {
+        checkState(executor != null, "Executor needs to be set");
+        checkState(baData != null, "Binding Data Broker must be started");
+        checkState(baNotifyImpl != null, "Notification Service must be started");
+
+        baConsumerRpc = new BindingDOMRpcServiceAdapter(getDomRpcInvoker(), codec);
+        baProviderRpc = new BindingDOMRpcProviderServiceAdapter(getDomRpcRegistry(), codec);
+
+        baBrokerImpl = new RootBindingAwareBroker("test");
+
+        final MountPointService mountService = new BindingDOMMountPointServiceAdapter(biMountImpl, codec);
+        baBrokerImpl.setMountService(mountService);
+        baBrokerImpl.setLegacyMountManager(new HydrogenMountProvisionServiceAdapter(mountService));
+        baBrokerImpl.setRpcBroker(new HeliumRpcProviderRegistry(baConsumerRpc, baProviderRpc));
+        baBrokerImpl.setLegacyDataBroker(baData);
+        baBrokerImpl.setNotificationBroker(baNotifyImpl);
+        baBrokerImpl.start();
+    }
+
+    public void startForwarding() {
+
+    }
+
+    private ProviderSession createMockContext() {
+
+        final ClassToInstanceMap<BrokerService> domBrokerServices = ImmutableClassToInstanceMap
+                .<BrokerService> builder()
+                //
+                .put(DOMRpcRouter.class, biBrokerImpl.getRouter()) //
+                .put(DOMMountPointService.class, biMountImpl)
+                .build();
+
+        return new ProviderSession() {
+
+            @Override
+            public <T extends BrokerService> T getService(final Class<T> service) {
+                return domBrokerServices.getInstance(service);
+            }
+
+            @Override
+            public boolean isClosed() {
+                return false;
+            }
+
+            @Override
+            public void close() {
+            }
+        };
+    }
+
+    public void startBindingToDomMappingService() {
+        checkState(classPool != null, "ClassPool needs to be present");
+
+        final DataObjectSerializerGenerator generator = StreamWriterGenerator.create(JavassistUtils.forClassPool(classPool));
+        final BindingNormalizedNodeCodecRegistry codecRegistry = new BindingNormalizedNodeCodecRegistry(generator);
+        final GeneratedClassLoadingStrategy loading = GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy();
+        codec = new BindingToNormalizedNodeCodec(loading,  codecRegistry);
+        mockSchemaService.registerSchemaContextListener(codec);
+    }
+
+    private void updateYangSchema(final ImmutableSet<YangModuleInfo> moduleInfos) {
+        mockSchemaService.changeSchema(getContext(moduleInfos));
+    }
+
+    private SchemaContext getContext(final ImmutableSet<YangModuleInfo> moduleInfos) {
+        final ModuleInfoBackedContext ctx = ModuleInfoBackedContext.create();
+        ctx.addModuleInfos(moduleInfos);
+        return ctx.tryToCreateSchemaContext().get();
+    }
+
+    public void start() {
+        startNewDomDataBroker();
+
+        startDomBroker();
+        startDomMountPoint();
+        startBindingToDomMappingService();
+        startNewDataBroker();
+        startNewBindingDataBroker();
+        startBindingNotificationBroker();
+        startBindingBroker();
+
+        startForwarding();
+        if (startWithSchema) {
+            loadYangSchemaFromClasspath();
+        }
+    }
+
+    public void startNewBindingDataBroker() {
+        final HydrogenDataBrokerAdapter forwarded = new HydrogenDataBrokerAdapter(dataBroker);
+        baData = forwarded;
+    }
+
+    private void startDomMountPoint() {
+        biMountImpl = new DOMMountPointServiceImpl();
+    }
+
+    private void startDomBroker() {
+        checkState(executor != null);
+
+        domRouter = new DOMRpcRouter();
+        mockSchemaService.registerSchemaContextListener(domRouter);
+
+        final ClassToInstanceMap<BrokerService> services = MutableClassToInstanceMap.create();
+        services.put(DOMRpcService.class, domRouter);
+
+        biBrokerImpl = new BrokerImpl(domRouter,services);
+
+    }
+
+    public void startBindingNotificationBroker() {
+        checkState(executor != null);
+        final DOMNotificationRouter router = DOMNotificationRouter.create(16);
+        domPublishService = router;
+        domListenService = router;
+        publishService = new BindingDOMNotificationPublishServiceAdapter(codec, domPublishService);
+        listenService = new BindingDOMNotificationServiceAdapter(codec, domListenService);
+        baNotifyImpl = new HeliumNotificationProviderServiceAdapter(publishService,listenService);
+
+    }
+
+    public void loadYangSchemaFromClasspath() {
+        final ImmutableSet<YangModuleInfo> moduleInfos = BindingReflections.loadModuleInfos();
+        updateYangSchema(moduleInfos);
+    }
+
+    @Deprecated
+    public DataProviderService getBindingDataBroker() {
+        return baData;
+    }
+
+    public RpcProviderRegistry getBindingRpcRegistry() {
+        return baBrokerImpl.getRoot();
+    }
+
+    public DOMRpcProviderService getDomRpcRegistry() {
+        return domRouter;
+    }
+
+    public DOMRpcService getDomRpcInvoker() {
+        return domRouter;
+    }
+
+    @Override
+    public void close() throws Exception {
+
+    }
+
+    public MountProviderService getBindingMountProviderService() {
+        return baBrokerImpl.getLegacyMount();
+    }
+
+    public DOMMountPointService getDomMountProviderService() {
+        return biMountImpl;
+    }
+
+    public DataBroker getDataBroker() {
+        return dataBroker;
+    }
+
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/MockSchemaService.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/MockSchemaService.java
new file mode 100644 (file)
index 0000000..8fc60e4
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.util;
+
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+
+public final class MockSchemaService implements SchemaService, SchemaContextProvider {
+
+    private SchemaContext schemaContext;
+
+    ListenerRegistry<SchemaContextListener> listeners = ListenerRegistry.create();
+
+    @Override
+    public void addModule(final Module module) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public synchronized SchemaContext getGlobalContext() {
+        return schemaContext;
+    }
+
+    @Override
+    public synchronized SchemaContext getSessionContext() {
+        return schemaContext;
+    }
+
+    @Override
+    public ListenerRegistration<SchemaContextListener> registerSchemaContextListener(
+            final SchemaContextListener listener) {
+        return listeners.register(listener);
+    }
+
+    @Override
+    public void removeModule(final Module module) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public synchronized SchemaContext getSchemaContext() {
+        return schemaContext;
+    }
+
+    public synchronized void changeSchema(final SchemaContext newContext) {
+        schemaContext = newContext;
+        for (ListenerRegistration<SchemaContextListener> listener : listeners) {
+            listener.getInstance().onGlobalContextUpdated(schemaContext);
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-util/pom.xml b/opendaylight/md-sal/sal-binding-util/pom.xml
new file mode 100644 (file)
index 0000000..bba26d3
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>sal-parent</artifactId>
+    <version>1.3.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>sal-binding-util</artifactId>
+  <packaging>bundle</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-binding-api</artifactId>
+    </dependency>
+  </dependencies>
+  <scm>
+    <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+    <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+    <tag>HEAD</tag>
+    <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+  </scm>
+</project>
diff --git a/opendaylight/md-sal/sal-binding-util/src/main/java/org/opendaylight/controller/md/sal/binding/util/AbstractBindingSalConsumerInstance.java b/opendaylight/md-sal/sal-binding-util/src/main/java/org/opendaylight/controller/md/sal/binding/util/AbstractBindingSalConsumerInstance.java
new file mode 100644 (file)
index 0000000..53abd39
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.util;
+
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.controller.sal.binding.api.NotificationService;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+import com.google.common.base.Preconditions;
+
+public abstract class AbstractBindingSalConsumerInstance<D extends DataBrokerService, N extends NotificationService, R extends RpcConsumerRegistry> //
+        implements //
+        RpcConsumerRegistry, //
+        NotificationService, //
+        DataBrokerService {
+
+    private final R rpcRegistry;
+    private final N notificationBroker;
+    private final D dataBroker;
+
+    protected final R getRpcRegistry() {
+        return rpcRegistry;
+    }
+
+    protected final N getNotificationBroker() {
+        return notificationBroker;
+    }
+
+    protected final D getDataBroker() {
+        return dataBroker;
+    }
+
+    protected final R getRpcRegistryChecked() {
+        Preconditions.checkState(rpcRegistry != null,"Rpc Registry is not available.");
+        return rpcRegistry;
+    }
+
+    protected final N getNotificationBrokerChecked() {
+        Preconditions.checkState(notificationBroker != null,"Notification Broker is not available.");
+        return notificationBroker;
+    }
+
+    protected final D getDataBrokerChecked() {
+        Preconditions.checkState(dataBroker != null, "Data Broker is not available");
+        return dataBroker;
+    }
+
+
+    protected AbstractBindingSalConsumerInstance(R rpcRegistry, N notificationBroker, D dataBroker) {
+        this.rpcRegistry = rpcRegistry;
+        this.notificationBroker = notificationBroker;
+        this.dataBroker = dataBroker;
+    }
+
+    @Override
+    public <T extends RpcService> T getRpcService(Class<T> module) {
+        return getRpcRegistryChecked().getRpcService(module);
+    }
+
+    @Override
+    public <T extends Notification> ListenerRegistration<NotificationListener<T>> registerNotificationListener(
+            Class<T> notificationType, NotificationListener<T> listener) {
+        return getNotificationBrokerChecked().registerNotificationListener(notificationType, listener);
+    }
+
+    @Override
+    public ListenerRegistration<org.opendaylight.yangtools.yang.binding.NotificationListener> registerNotificationListener(
+            org.opendaylight.yangtools.yang.binding.NotificationListener listener) {
+        return getNotificationBrokerChecked().registerNotificationListener(listener);
+    }
+
+    @Override
+    public DataModificationTransaction beginTransaction() {
+        return getDataBrokerChecked().beginTransaction();
+    }
+
+    @Override
+    @Deprecated
+    public DataObject readConfigurationData(InstanceIdentifier<? extends DataObject> path) {
+        return getDataBrokerChecked().readConfigurationData(path);
+    }
+
+    @Override
+    public DataObject readOperationalData(InstanceIdentifier<? extends DataObject> path) {
+        return getDataBrokerChecked().readOperationalData(path);
+    }
+
+    @Override
+    @Deprecated
+    public ListenerRegistration<DataChangeListener> registerDataChangeListener(
+            InstanceIdentifier<? extends DataObject> path, DataChangeListener listener) {
+        return getDataBrokerChecked().registerDataChangeListener(path, listener);
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-util/src/main/java/org/opendaylight/controller/md/sal/binding/util/AbstractBindingSalProviderInstance.java b/opendaylight/md-sal/sal-binding-util/src/main/java/org/opendaylight/controller/md/sal/binding/util/AbstractBindingSalProviderInstance.java
new file mode 100644 (file)
index 0000000..830942f
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.util;
+
+import java.util.concurrent.ExecutorService;
+
+import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandlerRegistration;
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+public abstract class AbstractBindingSalProviderInstance<D extends DataProviderService, N extends NotificationProviderService, R extends RpcProviderRegistry> //
+        extends AbstractBindingSalConsumerInstance<D, N, R> //
+        implements //
+        DataProviderService, //
+        RpcProviderRegistry, //
+        NotificationProviderService {
+
+    public AbstractBindingSalProviderInstance(R rpcRegistry, N notificationBroker,
+            D dataBroker) {
+        super(rpcRegistry, notificationBroker, dataBroker);
+    }
+
+    @Override
+    public Registration registerDataReader(
+            InstanceIdentifier<? extends DataObject> path,
+            DataReader<InstanceIdentifier<? extends DataObject>, DataObject> reader) {
+        return getDataBrokerChecked().registerDataReader(path, reader);
+    }
+
+    @Override
+    public Registration registerCommitHandler(
+            InstanceIdentifier<? extends DataObject> path,
+            DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject> commitHandler) {
+        return getDataBrokerChecked().registerCommitHandler(path, commitHandler);
+    }
+
+    @Override
+    public ListenerRegistration<RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject>>> registerCommitHandlerListener(
+            RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject>> commitHandlerListener) {
+        return getDataBrokerChecked().registerCommitHandlerListener(commitHandlerListener);
+    }
+
+    @Override
+    public <T extends RpcService> RpcRegistration<T> addRpcImplementation(Class<T> type, T implementation)
+            throws IllegalStateException {
+        return getRpcRegistryChecked().addRpcImplementation(type, implementation);
+    }
+
+    @Override
+    public <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(Class<T> type, T implementation)
+            throws IllegalStateException {
+        return getRpcRegistryChecked().addRoutedRpcImplementation(type, implementation);
+    }
+
+    @Override
+    public void publish(Notification notification) {
+        getNotificationBrokerChecked().publish(notification);
+    }
+
+    @Override
+    public void publish(Notification notification, ExecutorService service) {
+        getNotificationBrokerChecked().publish(notification, service);
+    }
+
+    @Override
+    public <L extends RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
+            L listener) {
+        return getRpcRegistryChecked().registerRouteChangeListener(listener);
+    }
+
+    @Override
+    public ListenerRegistration<NotificationInterestListener> registerInterestListener(
+            NotificationInterestListener interestListener) {
+        return getNotificationBrokerChecked().registerInterestListener(interestListener);
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-util/src/main/java/org/opendaylight/controller/md/sal/binding/util/BindingContextUtils.java b/opendaylight/md-sal/sal-binding-util/src/main/java/org/opendaylight/controller/md/sal/binding/util/BindingContextUtils.java
new file mode 100644 (file)
index 0000000..26aa27e
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.util;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
+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.RpcConsumerRegistry;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+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 com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.MutableClassToInstanceMap;
+
+public class BindingContextUtils {
+
+    public static ConsumerContext createConsumerContext(BindingAwareConsumer consumer,
+            ClassToInstanceMap<BindingAwareService> serviceProvider) {
+        checkNotNull(consumer,"Consumer should not be null");
+        checkNotNull(serviceProvider,"Service map should not be null");
+        return new SingleConsumerContextImpl(serviceProvider);
+    }
+
+    public static ProviderContext createProviderContext(BindingAwareProvider provider,
+            ClassToInstanceMap<BindingAwareService> serviceProvider) {
+        checkNotNull(provider,"Provider should not be null");
+        checkNotNull(serviceProvider,"Service map should not be null");
+        return new SingleProviderContextImpl(serviceProvider);
+    }
+
+    public static ConsumerContext createConsumerContextAndInitialize(BindingAwareConsumer consumer,
+            ClassToInstanceMap<BindingAwareService> serviceProvider) {
+        ConsumerContext context = createConsumerContext(consumer, serviceProvider);
+        consumer.onSessionInitialized(context);
+        return context;
+    }
+
+    public static ProviderContext createProviderContextAndInitialize(BindingAwareProvider provider,
+            ClassToInstanceMap<BindingAwareService> serviceProvider) {
+        ProviderContext context = createProviderContext(provider, serviceProvider);
+        provider.onSessionInitiated(context);
+        return context;
+    }
+
+    public static <T extends BindingAwareService> T createContextProxyOrReturnService(Class<T> service, T instance) {
+        // FIXME: Create Proxy
+        return instance;
+    }
+
+    private static class SingleConsumerContextImpl implements ConsumerContext, AutoCloseable {
+
+        private ClassToInstanceMap<BindingAwareService> alreadyRetrievedServices;
+        private ClassToInstanceMap<BindingAwareService> serviceProvider;
+
+        public SingleConsumerContextImpl(ClassToInstanceMap<BindingAwareService> serviceProvider) {
+            this.alreadyRetrievedServices = MutableClassToInstanceMap.create();
+            this.serviceProvider = serviceProvider;
+        }
+
+        @Override
+        public final <T extends RpcService> T getRpcService(Class<T> module) {
+            return getSALService(RpcConsumerRegistry.class).getRpcService(module);
+        }
+
+        @Override
+        public final <T extends BindingAwareService> T getSALService(Class<T> service) {
+            checkNotNull(service,"Service class should not be null.");
+            T potential = alreadyRetrievedServices.getInstance(service);
+            if(potential != null) {
+                return potential;
+            }
+            return tryToRetrieveSalService(service);
+        }
+
+        private synchronized <T extends BindingAwareService> T tryToRetrieveSalService(Class<T> service) {
+            final T potential = alreadyRetrievedServices.getInstance(service);
+            if(potential != null) {
+                return potential;
+            }
+            final T requested = serviceProvider.getInstance(service);
+            if(requested == null) {
+                throw new IllegalArgumentException("Requested service "+service.getName() +" is not available.");
+            }
+            final T retrieved = BindingContextUtils.createContextProxyOrReturnService(service,requested);
+            alreadyRetrievedServices.put(service, retrieved);
+            return retrieved;
+        }
+
+        @Override
+        public final void close() throws Exception {
+            alreadyRetrievedServices = null;
+            serviceProvider = null;
+        }
+    }
+
+    private static class SingleProviderContextImpl extends SingleConsumerContextImpl implements ProviderContext {
+
+        public SingleProviderContextImpl(ClassToInstanceMap<BindingAwareService> serviceProvider) {
+            super(serviceProvider);
+        }
+
+        @Override
+        public <L extends RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
+                L listener) {
+            return getSALService(RpcProviderRegistry.class).registerRouteChangeListener(listener);
+        }
+
+        @Override
+        public <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(Class<T> type,
+                T implementation) throws IllegalStateException {
+            return getSALService(RpcProviderRegistry.class).addRoutedRpcImplementation(type, implementation);
+        }
+
+        @Override
+        public <T extends RpcService> RpcRegistration<T> addRpcImplementation(Class<T> type, T implementation)
+                throws IllegalStateException {
+            return getSALService(RpcProviderRegistry.class).addRpcImplementation(type, implementation);
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-util/src/main/java/org/opendaylight/controller/md/sal/binding/util/TypeSafeDataReader.java b/opendaylight/md-sal/sal-binding-util/src/main/java/org/opendaylight/controller/md/sal/binding/util/TypeSafeDataReader.java
new file mode 100644 (file)
index 0000000..49ac59f
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.util;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ *
+ *
+ * @deprecated Use
+ *             {@link org.opendaylight.controller.md.sal.binding.api.ReadTransaction#read(org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType, InstanceIdentifier)}
+ *             instead.
+ */
+@Deprecated
+public final class TypeSafeDataReader {
+
+    private final DataReader<InstanceIdentifier<? extends DataObject>, DataObject> delegate;
+
+    public DataReader<InstanceIdentifier<?>, DataObject> getDelegate() {
+        return delegate;
+    }
+
+    public TypeSafeDataReader(
+            final DataReader<InstanceIdentifier<? extends DataObject>, DataObject> delegate) {
+        this.delegate = delegate;
+    }
+
+    @SuppressWarnings("unchecked")
+    public <D extends DataObject> D readConfigurationData(
+            final InstanceIdentifier<D> path) {
+        return (D) delegate.readConfigurationData(path);
+    }
+
+    @SuppressWarnings("unchecked")
+    public <D extends DataObject> D readOperationalData(
+            final InstanceIdentifier<D> path) {
+        return (D) delegate.readOperationalData(path);
+    }
+
+    public static TypeSafeDataReader forReader(
+            final DataReader<InstanceIdentifier<? extends DataObject>, DataObject> delegate) {
+        return new TypeSafeDataReader(delegate);
+    }
+}
diff --git a/opendaylight/md-sal/sal-common-api/pom.xml b/opendaylight/md-sal/sal-common-api/pom.xml
new file mode 100644 (file)
index 0000000..fe44128
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>sal-parent</artifactId>
+    <version>1.3.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>sal-common-api</artifactId>
+  <packaging>bundle</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>concepts</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>util</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-common</artifactId>
+    </dependency>
+  </dependencies>
+
+  <scm>
+    <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+    <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+    <tag>HEAD</tag>
+    <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+  </scm>
+</project>
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/RegistrationListener.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/RegistrationListener.java
new file mode 100644 (file)
index 0000000..3b3217d
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api;
+
+import java.util.EventListener;
+
+import org.opendaylight.yangtools.concepts.Registration;
+
+public interface RegistrationListener<T extends Registration> extends EventListener {
+
+    void onRegister(T registration);
+
+    void onUnregister(T registration);
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/TransactionStatus.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/TransactionStatus.java
new file mode 100644 (file)
index 0000000..a8989c4
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api;
+
+public enum TransactionStatus {
+    /**
+     * The transaction has been freshly allocated. The user is still accessing
+     * it and it has not been sealed.
+     */
+    NEW,
+    /**
+     * The transaction has been completed by the user and sealed. It is currently
+     * awaiting execution.
+     */
+    SUBMITED,
+    /**
+     * The transaction has been successfully committed to backing store.
+     */
+    COMMITED,
+    /**
+     * The transaction has failed to commit due to some underlying issue.
+     */
+    FAILED,
+    /**
+     * Currently unused.
+     */
+    CANCELED,
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncConfigurationCommitCohort.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncConfigurationCommitCohort.java
new file mode 100644 (file)
index 0000000..9fb350b
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ *
+ * Three phase Commit Cohort for subtree, which is
+ * uniquely associated with user submitted transcation.
+ *
+ * @param <P>
+ *            Type of path (subtree identifier), which represents location in
+ *            tree
+ * @param <D>
+ *            Type of data (payload), which represents data payload
+ */
+public interface AsyncConfigurationCommitCohort<P extends Path<P>, D> {
+
+    /**
+     * Initiates a pre-commit of associated request
+     *
+     * Implementation MUST NOT do any blocking calls during this callback, all
+     * pre-commit preparation SHOULD happen asynchronously and MUST result in
+     * completing returned future object.
+     *
+     * @param rebasedTransaction
+     *            Read-only view of transaction as if happened on top of actual
+     *            data store
+     * @return Future which is completed once pre-commit phase for this request
+     *         is finished.
+     */
+    ListenableFuture<Void> preCommit(AsyncReadTransaction<P, D> rebasedTransaction);
+
+    /**
+     *
+     * Initiates a commit phase of associated request
+     *
+     * Implementation MUST NOT do any blocking calls during this callback, all
+     * commit finalization SHOULD happen asynchronously and MUST result in
+     * completing returned future object.
+     *
+     * @return Future which is completed once commit phase for associated
+     *         request is finished.
+     */
+    ListenableFuture<Void> commit();
+
+    /**
+     *
+     * Initiates abort phase of associated request
+     *
+     * Implementation MUST NOT do any blocking calls during this callback, all
+     * commit finalization SHOULD happen asynchronously and MUST result in
+     * completing returned future object.
+     *
+     * @return Future which is completed once commit phase for associated
+     *         request is finished.
+     */
+    ListenableFuture<Void> abort();
+
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncConfigurationCommitCoordinator.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncConfigurationCommitCoordinator.java
new file mode 100644 (file)
index 0000000..6d669ab
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * Three Phase Commit Coordinator with support of user-supplied commit cohorts
+ * which participates in three-phase commit protocols
+ *
+ * @param <P>
+ *            Type of path (subtree identifier), which represents location in
+ *            tree
+ * @param <D>
+ *            Type of data (payload), which represents data payload
+ */
+public interface AsyncConfigurationCommitCoordinator<P extends Path<P>, D> {
+
+    /**
+     * Register configuration commit handler for particular subtree
+     *
+     * Configuration commit handler is invoked for all write transactions
+     * which modifies <code>subtree</code>
+     *
+     * @param subtree Subtree which configuration commit handler is interested it
+     * @param commitHandler Instance of user-provided commit handler
+     * @return Registration object representing this registration. Invoking {@link ObjectRegistration#close()}
+     *   will unregister configuration commit handler.
+     */
+    <C extends AsyncConfigurationCommitCohort<P, D>> ObjectRegistration<C> registerConfigurationCommitHandler(
+            P subtree, C commitHandler);
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncConfigurationCommitHandler.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncConfigurationCommitHandler.java
new file mode 100644 (file)
index 0000000..6025e13
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+import com.google.common.util.concurrent.CheckedFuture;
+
+/**
+ * User-supplied participant in three-phase commit of transaction for configuration data tree
+ *
+ * Client-supplied implementation of commit handler for subtree, which
+ * is responsible for processing CAN-COMMIT phase of three-phase commit protocol
+ * and return CommitCohort, which provides access to additional transitions
+ * such as PRE-COMMIT, COMMIT and ABORT.
+ *
+ * @param <P>
+ *            Type of path (subtree identifier), which represents location in
+ *            tree
+ * @param <D>
+ *            Type of data (payload), which represents data payload
+ */
+public interface AsyncConfigurationCommitHandler<P extends Path<P>, D> {
+
+    /**
+     *
+     * Requests a can commit phase
+     *
+     * Implementations SHOULD NOT do any blocking operation during
+     * processing this callback.
+     *
+     * <b>Implementation Notes</b>
+     * <ul>
+     * <li>Implementation are REQUIRED to use <code>request</code> object for any data related access</li>
+     * <li>Implementations SHOULD NOT use any other state stored outside configuration subtree for validation</li>
+     * <li>Validation should happen asynchronously, outside callback call by updating returned {@link CheckedFuture}
+     *     object.</li>
+     * <li>If validation (CAN_COMMIT) phase:
+     * <ul>
+     * <li><b>is successful</b> - invocation of {@link CheckedFuture#checkedGet()} on returned future MUST
+     *     return {@link AsyncConfigurationCommitCohort} associated with request.</li>
+     * <li><b>is unsuccessful</b> - invocation of {@link CheckedFuture#checkedGet()} must throw instance of {@link DataValidationFailedException}
+     * with human readable explanaition of error condition.
+     * </li>
+     * </ul>
+     * </li>
+     * @param request
+     *            Commit Request submitted by client, which contains
+     *            information about modifications and read-only view as
+     *            if transaction happened.
+     * @return CheckedFuture which contains client-supplied implementation of {@link AsyncConfigurationCommitCohort}
+     *         associated with submitted request, if can commit phase is
+     *         successful, if can commit was unsuccessful, future must fail with
+     *         {@link TransactionCommitFailedException} exception.
+     */
+    CheckedFuture<AsyncConfigurationCommitCohort<P, D>, DataValidationFailedException> canCommit(
+            ConfigurationCommitRequest<P, D> request);
+
+    /**
+     *
+     * Commit Request as was submitted by client code
+     *
+     * Commit Request contains list view of created / updated / removed
+     * path and read-only view of proposed client transaction,
+     * which may be used to retrieve modified or referenced data.
+     *
+     *
+     * @param <P>
+     *            Type of path (subtree identifier), which represents location
+     *            in tree
+     * @param <D>
+     *            Type of data (payload), which represents data payload
+     */
+    static interface ConfigurationCommitRequest<P extends Path<P>, D> {
+
+        /**
+         *
+         * Read-only transaction which provides access only to configuration
+         * data tree as if submitted transaction successfully happened and
+         * no other concurrent modifications happened between allocation
+         * of client transactions and write of client transactions.
+         *
+         * Implementations of Commit Handlers are REQUIRED to use this
+         * read-only view to access any data from configuration data tree,
+         * in order to capture them as preconditions for this transaction.
+         *
+         * @return Read-only transaction which provides access only to configuration
+         * data tree as if submitted transaction successfully happened
+         */
+        AsyncReadTransaction<P, D> getReadOnlyView();
+
+        /**
+         *
+         * Returns iteration of paths, to data which was introduced by this transaction.
+         *
+         * @return Iteration of paths, which was introduced by this transaction.
+         */
+        Iterable<P> getCreatedPaths();
+        /**
+         *
+         * Returns iteration of paths, to data which was updated by this transaction.
+         *
+         * @return Iteration of paths, which was updated by this transaction.
+         */
+        Iterable<P> getUpdatedPaths();
+
+        /**
+         *
+         * Returns iteration of paths, to data which was removed by this transaction.
+         *
+         * @return Iteration of paths, which was removed by this transaction.
+         */
+        Iterable<P> getRemovedPaths();
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataBroker.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataBroker.java
new file mode 100644 (file)
index 0000000..3a68092
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ *
+ * Base interface that provides access to a conceptual data tree store and also provides the ability to
+ * subscribe for changes to data under a given branch of the tree.
+ *
+ * <p>
+ * All operations on the data tree are performed via one of the transactions:
+ * <ul>
+ * <li>Read-Only - allocated using {@link #newReadOnlyTransaction()}
+ * <li>Write-Only - allocated using {@link #newWriteOnlyTransaction()}
+ * <li>Read-Write - allocated using {@link #newReadWriteTransaction()}
+ * </ul>
+ *
+ * <p>
+ * These transactions provide a stable isolated view of data tree, which is
+ * guaranteed to be not affected by other concurrent transactions, until
+ * transaction is committed.
+ *
+ * <p>
+ * For a detailed explanation of how transaction are isolated and how transaction-local
+ * changes are committed to global data tree, see
+ * {@link AsyncReadTransaction}, {@link AsyncWriteTransaction},
+ * {@link AsyncReadWriteTransaction} and {@link AsyncWriteTransaction#commit()}.
+ *
+ *
+ * <p>
+ * It is strongly recommended to use the type of transaction, which
+ * provides only the minimal capabilities you need. This allows for
+ * optimizations at the data broker / data store level. For example,
+ * implementations may optimize the transaction for reading if they know ahead
+ * of time that you only need to read data - such as not keeping additional meta-data,
+ * which may be required for write transactions.
+ *
+ * <p>
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * @param <P>
+ *            Type of path (subtree identifier), which represents location in
+ *            tree
+ * @param <D>
+ *            Type of data (payload), which represents data payload
+ */
+public interface AsyncDataBroker<P extends Path<P>, D, L extends AsyncDataChangeListener<P, D>> extends //
+        AsyncDataTransactionFactory<P, D> {
+
+    /**
+     *
+     * Scope of Data Change
+     *
+     * <p>
+     * Represents scope of data change (addition, replacement, deletion).
+     *
+     * The terminology for scope types is reused from LDAP.
+     *
+     * <h2>Examples</h2>
+     *
+     * Following is an example model with comments describing what notifications
+     * you would receive based on the scope you specify, when you are
+     * registering for changes on container a.
+     *
+     * <pre>
+     * container a              // scope BASE, ONE, SUBTREE
+     *    leaf "foo"            // scope ONE, SUBTREE
+     *    container             // scope ONE, SUBTREE
+     *       leaf  "bar"        // scope SUBTREE
+     *    list list             // scope ONE, SUBTREE
+     *      list [a]            // scope SUBTREE
+     *        id "a"            // scope SUBTREE
+     *      list [b]            // scope SUBTREE
+     *        id "b"            // scope SUBTREE
+     * </pre>
+     *
+     * Following is an example model with comments describing what notifications
+     * you would receive based on the scope you specify, when you are
+     * registering for changes on list list (without specifying concrete item in
+     * the list).
+     *
+     * <pre>
+     *  list list               // scope BASE, ONE, SUBTREE
+     *      list [a]            // scope ONE, SUBTREE
+     *        id "a"            // scope SUBTREE
+     *      list [b]            // scope ONE, SUBTREE
+     *        id "b"            // scope SUBTREE
+     * </pre>
+     *
+     *
+     * @see http://www.idevelopment.info/data/LDAP/LDAP_Resources/
+     *      SEARCH_Setting_the_SCOPE_Parameter.shtml
+     */
+    public enum DataChangeScope {
+
+        /**
+         * Represents only a direct change of the node, such as replacement of a
+         * node, addition or deletion.
+         *
+         */
+        BASE,
+        /**
+         * Represent a change (addition,replacement,deletion) of the node or one
+         * of its direct children.
+         *
+         * This scope is superset of {@link #BASE}.
+         *
+         */
+        ONE,
+        /**
+         * Represents a change of the node or any of or any of its child nodes,
+         * direct and nested.
+         *
+         * This scope is superset of {@link #ONE} and {@link #BASE}.
+         *
+         */
+        SUBTREE
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public AsyncReadOnlyTransaction<P, D> newReadOnlyTransaction();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public AsyncReadWriteTransaction<P, D> newReadWriteTransaction();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public AsyncWriteTransaction<P, D> newWriteOnlyTransaction();
+
+    /**
+     * Registers a {@link AsyncDataChangeListener} to receive
+     * notifications when data changes under a given path in the conceptual data
+     * tree.
+     * <p>
+     * You are able to register for notifications  for any node or subtree
+     * which can be reached via the supplied path.
+     * <p>
+     * If path type <code>P</code> allows it, you may specify paths up to the leaf nodes
+     * then it is possible to listen on leaf nodes.
+     * <p>
+     * You are able to register for data change notifications for a subtree even
+     * if it does not exist. You will receive notification once that node is
+     * created.
+     * <p>
+     * If there is any preexisting data in data tree on path for which you are
+     * registering, you will receive initial data change event, which will
+     * contain all preexisting data, marked as created.
+     *
+     * <p>
+     * You are also able to specify the scope of the changes you want to be
+     * notified.
+     * <p>
+     * Supported scopes are:
+     * <ul>
+     * <li>{@link DataChangeScope#BASE} - notification events will only be
+     * triggered when a node referenced by path is created, removed or replaced.
+     * <li>{@link DataChangeScope#ONE} - notifications events will only be
+     * triggered when a node referenced by path is created, removed or replaced,
+     * or any or any of its immediate children are created, updated or removed.
+     * <li>{@link DataChangeScope#SUBTREE} - notification events will be
+     * triggered when a node referenced by the path is created, removed
+     * or replaced or any of the children in its subtree are created, removed
+     * or replaced.
+     * </ul>
+     * See {@link DataChangeScope} for examples.
+     * <p>
+     * This method returns a {@link ListenerRegistration} object. To
+     * "unregister" your listener for changes call the "close" method on this
+     * returned object.
+     * <p>
+     * You MUST call close when you no longer need to receive notifications
+     * (such as during shutdown or for example if your bundle is shutting down).
+     *
+     * @param store
+     *            Logical Data Store - Logical Datastore you want to listen for
+     *            changes in. For example
+     *            {@link LogicalDatastoreType#OPERATIONAL} or
+     *            {@link LogicalDatastoreType#CONFIGURATION}
+     * @param path
+     *            Path (subtree identifier) on which client listener will be
+     *            invoked.
+     * @param listener
+     *            Instance of listener which should be invoked on
+     * @param triggeringScope
+     *            Scope of change which triggers callback.
+     * @return Listener registration object, which may be used to unregister
+     *         your listener using {@link ListenerRegistration#close()} to stop
+     *         delivery of change events.
+     */
+    ListenerRegistration<L> registerDataChangeListener(LogicalDatastoreType store, P path, L listener,
+            DataChangeScope triggeringScope);
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataChangeEvent.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataChangeEvent.java
new file mode 100644 (file)
index 0000000..ac4a0ff
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ *
+ * An event which contains a capture of changes in a data subtree
+ *
+ * <p>
+ * Represents a notification indicating that some data at or under a particular
+ * path has changed. The notification contains a capture of the changes in the data
+ * subtree. This event is triggered by successful application of modifications
+ * from a transaction on the global data tree. Use the
+ * {@link AsyncDataBroker#registerDataChangeListener(LogicalDatastoreType, Path, AsyncDataChangeListener, AsyncDataBroker.DataChangeScope)}
+ * method to register a listener for data change events.
+ *
+ * <p>
+ * A listener will only receive notifications for changes to data under the path
+ * they register for. See
+ * {@link AsyncDataBroker#registerDataChangeListener(LogicalDatastoreType, Path, AsyncDataChangeListener, AsyncDataBroker.DataChangeScope)}
+ * to learn more about registration scopes.
+ *
+ * <p>
+ * The entire subtree under the path will be provided via instance methods of Data
+ * Change Event even if just a leaf node changes.
+ *
+ * <p>
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * @param <P>
+ *            Type of path (subtree identifier), which represents location in
+ *            tree
+ * @param <D>
+ *            Type of data (payload), which represents data payload
+ */
+public interface AsyncDataChangeEvent<P extends Path<P>, D> extends Immutable {
+    /**
+     * Returns a map of paths and newly created objects, which were introduced by
+     * this change into conceptual data tree, if no new objects were introduced
+     * this map will be empty.
+     *<p>
+     * This map contains all data tree nodes (and paths to them) which were created
+     * and are in  the scope of listener registration. The data tree nodes
+     * contain their whole subtree with their current state.
+     *
+     * @return map of paths and newly created objects
+     */
+    Map<P, D> getCreatedData();
+
+    /**
+     * Returns a map of paths and objects which were updated by this change in the
+     * conceptual data tree if no existing objects were updated
+     * this map will be empty.
+     *<p>
+     * This map contains all data tree nodes (and paths to them) which were updated
+     * and are in the scope of listener registration. The data tree nodes
+     * contain their whole subtree with their current state.
+     *<p>
+     * A Node is considered updated if it contents were replaced or one of its
+     * children was created, removed or updated.
+     *<p>
+     * Original state of the updated data tree nodes is in
+     * {@link #getOriginalData()} stored with same path.
+     *
+     * @return map of paths and newly created objects
+     */
+    Map<P, D> getUpdatedData();
+
+    /**
+     * Returns an immutable set of removed paths.
+     *<p>
+     * This set contains the paths to the data tree nodes which are in the scope
+     * of the listener registration that have been removed.
+     *<p>
+     * Original state of the removed data tree nodes is in
+     * {@link #getOriginalData()} stored with same path.
+     *
+     * @return set of removed paths
+     */
+    Set<P> getRemovedPaths();
+
+    /**
+     * Returns an immutable map of updated or removed paths and their original
+     * states prior to this change.
+     *
+     *<p>
+     * This map contains the original version of the data tree nodes (and paths
+     * to them), which are in the scope of the listener registration.
+     *
+     * @return map of paths and original state of updated and removed objects.
+     */
+    Map<P, D> getOriginalData();
+
+    /**
+     * Returns an immutable stable view of data state, which captures the state of
+     * data store before the reported change.
+     *
+     *<p>
+     * The view is rooted at the point where the listener, to which the event is
+     * being delivered, was registered.
+     *<p>
+     * If listener used a wildcarded path (if supported by path type) during
+     * registration for change listeners this method returns null, and original
+     * state can be accessed only via {@link #getOriginalData()}
+     *
+     * @return Stable view of data before the change happened, rooted at the
+     *         listener registration path.
+     *
+     */
+    D getOriginalSubtree();
+
+    /**
+     * Returns an immutable stable view of data, which captures the state of data
+     * store after the reported change.
+     *<p>
+     * The view is rooted at the point where the listener, to which the event is
+     * being delivered, was registered.
+     *<p>
+     * If listener used a wildcarded path (if supported by path type) during
+     * registration for change listeners this method returns null, and state
+     * can be accessed only via {@link #getCreatedData()},
+     * {@link #getUpdatedData()}, {@link #getRemovedPaths()}
+     *
+     * @return Stable view of data after the change happened, rooted at the
+     *         listener registration path.
+     */
+    D getUpdatedSubtree();
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataChangeListener.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataChangeListener.java
new file mode 100644 (file)
index 0000000..0bdaf7b
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import java.util.EventListener;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * Listener of data change events on particular subtree.
+ *
+ * <p>
+ * User-supplied implementations of this listener interface MUST register via
+ * {@link AsyncDataBroker#registerDataChangeListener(LogicalDatastoreType, Path, AsyncDataChangeListener, AsyncDataBroker.DataChangeScope)}
+ * in order to start receiving data change events, which capture state changes
+ * in a subtree.
+ *
+ * <p>
+ * <b>Implementation Note:</b> This interface is intended to be implemented
+ * by users of MD-SAL.
+ *
+ * @param <P>
+ *            Type of path (subtree identifier), which represents location in
+ *            tree
+ * @param <D>
+ *            Type of data (payload), which represents data payload
+ */
+public interface AsyncDataChangeListener<P extends Path<P>, D> extends EventListener {
+    /**
+     *
+     * Invoked when there is data change for the particular path, which was used to
+     * register this listener.
+     * <p>
+     * This method may be also invoked during registration of the listener if
+     * there is any preexisting data in the conceptual data tree for supplied path.
+     * This initial event will contain all preexisting data as created.
+     *
+     * <p>
+     * <b>Note</b>: This method may be invoked from a shared thread pool.
+     * <li>Implementations <b>SHOULD NOT</b> perform CPU-intensive operations on the calling thread.
+     * <li>Implementations <b>MUST NOT block the calling thread</b> - to do so could lead to deadlock
+     * scenarios.
+     *
+     *<br>
+     *
+     * @param change
+     *            Data Change Event being delivered.
+     */
+    void onDataChanged(AsyncDataChangeEvent<P, D> change);
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataTransactionFactory.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataTransactionFactory.java
new file mode 100644 (file)
index 0000000..a558b96
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * A factory which allocates new transactions to operate on the data
+ * tree.
+ *
+ * <p>
+ * <b>Note:</b> This interface is not intended to be used directly, but rather
+ * via subinterfaces which introduces additional semantics to allocated
+ * transactions.
+ * <ul>
+ * <li> {@link AsyncDataBroker}
+ * <li> {@link TransactionChain}
+ * </ul>
+ *
+ * <p>
+ * All operations on the data tree are performed via one of the transactions:
+ * <ul>
+ * <li>Read-Only - allocated using {@link #newReadOnlyTransaction()}
+ * <li>Write-Only - allocated using {@link #newWriteOnlyTransaction()}
+ * <li>Read-Write - allocated using {@link #newReadWriteTransaction()}
+ * </ul>
+ *
+ * <p>
+ * These transactions provides a stable isolated view of the data tree, which is
+ * guaranteed to be not affected by other concurrent transactions, until
+ * transaction is committed.
+ *
+ * <p>
+ * For a detailed explanation of how transaction are isolated and how transaction-local
+ * changes are committed to global data tree, see
+ * {@link AsyncReadTransaction}, {@link AsyncWriteTransaction},
+ * {@link AsyncReadWriteTransaction} and {@link AsyncWriteTransaction#commit()}.
+ *
+ * <p>
+ * It is strongly recommended to use the type of transaction, which
+ * provides only the minimal capabilities you need. This allows for
+ * optimizations at the data broker / data store level. For example,
+ * implementations may optimize the transaction for reading if they know ahead
+ * of time that you only need to read data - such as not keeping additional meta-data,
+ * which may be required for write transactions.
+ *<p>
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * @see AsyncDataBroker
+ * @see TransactionChain
+ *
+ * @param <P>
+ *            Type of path (subtree identifier), which represents location in
+ *            tree
+ * @param <D>
+ *            Type of data (payload), which represents data payload
+ */
+public interface AsyncDataTransactionFactory<P extends Path<P>, D> {
+
+    /**
+     * Allocates a new read-only transaction which provides an immutable snapshot of
+     * the data tree.
+     *<p>
+     * The view of data tree is an immutable snapshot of current data tree state when
+     * transaction was allocated.
+     *
+     * @return new read-only transaction
+     */
+    AsyncReadOnlyTransaction<P, D> newReadOnlyTransaction();
+
+    /**
+     * Allocates new read-write transaction which provides a mutable view of the data
+     * tree.
+     *
+     * <p>
+     * Preconditions for mutation of data tree are captured from the snapshot of
+     * data tree state, when the transaction is allocated. If data was
+     * changed during transaction in an incompatible way then the commit of this transaction
+     * will fail. See {@link AsyncWriteTransaction#commit()} for more
+     * details about conflicting and not-conflicting changes and
+     * failure scenarios.
+     *
+     * @return new read-write transaction
+     */
+    AsyncReadWriteTransaction<P, D> newReadWriteTransaction();
+
+    /**
+     * Allocates new write-only transaction based on latest state of data
+     * tree.
+     *
+     * <p>
+     * Preconditions for mutation of data tree are captured from the snapshot of
+     * data tree state, when the transaction is allocated. If data was
+     * changed during transaction in an incompatible way then the commit of this transaction
+     * will fail. See {@link AsyncWriteTransaction#commit()} for more
+     * details about conflicting and not-conflicting changes and
+     * failure scenarios.
+     *
+     * <p>
+     * Since this transaction does not provide a view of the data it SHOULD BE
+     * used only by callers which are exclusive writers (exporters of data)
+     * to the subtree they modify. This prevents optimistic
+     * lock failures as described in {@link AsyncWriteTransaction#commit()}.
+     * <p>
+     * Exclusivity of writers to particular subtree SHOULD BE enforced by
+     * external locking mechanism.
+     *
+     * @return new write-only transaction
+     */
+    AsyncWriteTransaction<P, D> newWriteOnlyTransaction();
+
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadOnlyTransaction.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadOnlyTransaction.java
new file mode 100644 (file)
index 0000000..46c90b9
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * Marker interface for a read-only view of the data tree.
+ *
+ * @see AsyncReadTransaction
+ *
+ * @param <P>
+ *            Type of path (subtree identifier), which represents location in
+ *            tree
+ * @param <D>
+ *            Type of data (payload), which represents data payload
+ */
+public interface AsyncReadOnlyTransaction<P extends Path<P>, D> extends AsyncReadTransaction<P, D>, AutoCloseable {
+
+    /**
+     * Closes this transaction and releases all resources associated with it.
+     *
+     */
+    @Override
+    public void close();
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadTransaction.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadTransaction.java
new file mode 100644 (file)
index 0000000..afa8670
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ *
+ * Marker interface for stateful read view of the data tree.
+ *
+ * <p>
+ * View of the data tree is a stable point-in-time snapshot of the current data tree state when
+ * the transaction was created. It's state and underlying data tree
+ * is not affected by other concurrently running transactions.
+ *
+ * <p>
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * <h2>Transaction isolation example</h2>
+ * Lets assume initial state of data tree for <code>PATH</code> is <code>A</code>.
+ *
+ * <pre>
+ * txRead = broker.newReadOnlyTransaction();   // read Transaction is snapshot of data
+ * txWrite = broker.newReadWriteTransactoin(); // concurrent write transaction
+ *
+ * txRead.read(OPERATIONAL,PATH).get();        // will return Optional containing A
+ * txWrite = broker.put(OPERATIONAL,PATH,B);   // writes B to PATH
+ *
+ * txRead.read(OPERATIONAL,PATH).get();        // still returns Optional containing A
+ *
+ * txWrite.commit().get();                     // data tree is updated, PATH contains B
+ * txRead.read(OPERATIONAL,PATH).get();        // still returns Optional containing A
+ *
+ * txAfterCommit = broker.newReadOnlyTransaction(); // read Transaction is snapshot of new state
+ * txAfterCommit.read(OPERATIONAL,PATH).get(); // returns Optional containing B;
+ * </pre>
+ *
+ * <p>
+ * <b>Note:</b> example contains blocking calls on future only to illustrate
+ * that action happened after other asynchronous action. Use of blocking call
+ * {@link com.google.common.util.concurrent.ListenableFuture#get()} is discouraged for most
+ * uses and you should use
+ * {@link com.google.common.util.concurrent.Futures#addCallback(com.google.common.util.concurrent.ListenableFuture, com.google.common.util.concurrent.FutureCallback)}
+ * or other functions from {@link com.google.common.util.concurrent.Futures} to
+ * register more specific listeners.
+ *
+ * @param <P>
+ *            Type of path (subtree identifier), which represents location in
+ *            tree
+ * @param <D>
+ *            Type of data (payload), which represents data payload
+ *
+ * @see org.opendaylight.controller.md.sal.binding.api.ReadTransaction
+ * @see org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction
+ */
+public interface AsyncReadTransaction<P extends Path<P>, D> extends AsyncTransaction<P, D> {
+
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadWriteTransaction.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadWriteTransaction.java
new file mode 100644 (file)
index 0000000..16b4ed4
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * Transaction enabling a client to have a combined read/write capabilities.
+ *
+ * <p>
+ * The initial state of the write transaction is stable snapshot of current data tree
+ * state captured when transaction was created and it's state and underlying
+ * data tree are not affected by other concurrently running transactions.
+ *
+ * <p>
+ * Write transactions are isolated from other concurrent write transactions. All
+ * writes are local to the transaction and represents only a proposal of state
+ * change for data tree and it is not visible to any other concurrently running
+ * transactions.
+ *
+ * <p>
+ * Applications publish the changes proposed in the transaction by calling {@link #commit}
+ * on the transaction. This seals the transaction
+ * (preventing any further writes using this transaction) and submits it to be
+ * processed and applied to global conceptual data tree.
+ *
+ * <p>
+ * The transaction commit may fail due to a concurrent transaction modifying and committing data in
+ * an incompatible way. See {@link #commit()} for more concrete commit failure examples.
+ *
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * <h2>Examples</h2>
+ *
+ * <h3>Transaction local state</h3>
+ *
+ * Let assume initial state of data tree for <code>PATH</code> is <code>A</code>
+ * .
+ *
+ * <pre>
+ * txWrite = broker.newReadWriteTransaction(); // concurrent write transaction
+ *
+ * txWrite.read(OPERATIONAL,PATH).get()        // will return Optional containing A
+ * txWrite.put(OPERATIONAL,PATH,B);            // writes B to PATH
+ * txWrite.read(OPERATIONAL,PATH).get()        // will return Optional Containing B
+ *
+ * txWrite.commit().get();                     // data tree is updated, PATH contains B
+ *
+ * tx1afterCommit = broker.newReadOnlyTransaction(); // read Transaction is snapshot of new state
+ * tx1afterCommit.read(OPERATIONAL,PATH).get(); // returns Optional containing B
+ * </pre>
+ *
+ * As you could see read-write transaction provides capabilities as
+ * {@link AsyncWriteTransaction} but also allows for reading proposed changes as
+ * if they already happened.
+ *
+ * <h3>Transaction isolation (read transaction, read-write transaction)</h3> Let
+ * assume initial state of data tree for <code>PATH</code> is <code>A</code>.
+ *
+ * <pre>
+ * txRead = broker.newReadOnlyTransaction();   // read Transaction is snapshot of data
+ * txWrite = broker.newReadWriteTransaction(); // concurrent write transaction
+ *
+ * txRead.read(OPERATIONAL,PATH).get();        // will return Optional containing A
+ * txWrite.read(OPERATIONAL,PATH).get()        // will return Optional containing A
+ *
+ * txWrite.put(OPERATIONAL,PATH,B);            // writes B to PATH
+ * txWrite.read(OPERATIONAL,PATH).get()        // will return Optional Containing B
+ *
+ * txRead.read(OPERATIONAL,PATH).get();        // concurrent read transaction still returns
+ *                                             // Optional containing A
+ *
+ * txWrite.commit().get();                     // data tree is updated, PATH contains B
+ * txRead.read(OPERATIONAL,PATH).get();        // still returns Optional containing A
+ *
+ * tx1afterCommit = broker.newReadOnlyTransaction(); // read Transaction is snapshot of new state
+ * tx1afterCommit.read(OPERATIONAL,PATH).get(); // returns Optional containing B
+ * </pre>
+ *
+ * <h3>Transaction isolation (2 concurrent read-write transactions)</h3> Let
+ * assume initial state of data tree for <code>PATH</code> is <code>A</code>.
+ *
+ * <pre>
+ * tx1 = broker.newReadWriteTransaction(); // read Transaction is snapshot of data
+ * tx2 = broker.newReadWriteTransaction(); // concurrent write transaction
+ *
+ * tx1.read(OPERATIONAL,PATH).get();       // will return Optional containing A
+ * tx2.read(OPERATIONAL,PATH).get()        // will return Optional containing A
+ *
+ * tx2.put(OPERATIONAL,PATH,B);            // writes B to PATH
+ * tx2.read(OPERATIONAL,PATH).get()        // will return Optional Containing B
+ *
+ * tx1.read(OPERATIONAL,PATH).get();       // tx1 read-write transaction still sees Optional
+ *                                         // containing A since is isolated from tx2
+ * tx1.put(OPERATIONAL,PATH,C);            // writes C to PATH
+ * tx1.read(OPERATIONAL,PATH).get()        // will return Optional Containing C
+ *
+ * tx2.read(OPERATIONAL,PATH).get()        // tx2 read-write transaction still sees Optional
+ *                                         // containing B since is isolated from tx1
+ *
+ * tx2.commit().get();                     // data tree is updated, PATH contains B
+ * tx1.read(OPERATIONAL,PATH).get();       // still returns Optional containing C since is isolated from tx2
+ *
+ * tx1afterCommit = broker.newReadOnlyTransaction(); // read Transaction is snapshot of new state
+ * tx1afterCommit.read(OPERATIONAL,PATH).get(); // returns Optional containing B
+ *
+ * tx1.commit()                            // Will fail with OptimisticLockFailedException
+ *                                         // which means concurrent transaction changed the same PATH
+ *
+ * </pre>
+ *
+ * <p>
+ * <b>Note:</b> examples contains blocking calls on future only to illustrate
+ * that action happened after other asynchronous action. Use of blocking call
+ * {@link com.google.common.util.concurrent.ListenableFuture#get()} is discouraged for most uses and you should
+ * use
+ * {@link com.google.common.util.concurrent.Futures#addCallback(com.google.common.util.concurrent.ListenableFuture, com.google.common.util.concurrent.FutureCallback)}
+ * or other functions from {@link com.google.common.util.concurrent.Futures} to
+ * register more specific listeners.
+ *
+ * @see AsyncReadTransaction
+ * @see AsyncWriteTransaction
+ *
+ * @param <P>
+ *            Type of path (subtree identifier), which represents location in
+ *            tree
+ * @param <D>
+ *            Type of data (payload), which represents data payload
+ */
+public interface AsyncReadWriteTransaction<P extends Path<P>, D> extends AsyncReadTransaction<P, D>,
+        AsyncWriteTransaction<P, D> {
+
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncTransaction.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncTransaction.java
new file mode 100644 (file)
index 0000000..08ddfe6
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.concepts.Path;
+
+
+/**
+ *
+ * A common parent for all transactions which operate on a conceptual data tree.
+ *
+ * See derived transaction types for more concrete behavior:
+ * <ul>
+ * <li>{@link AsyncReadTransaction} - Read capabilities, user is able to read data from data tree</li>
+ * <li>{@link AsyncWriteTransaction} - Write capabilities, user is able to propose changes to data tree</li>
+ * <li>{@link AsyncReadWriteTransaction} - Read and Write capabilities, user is able to read state and to propose changes of state.</li>
+ * </ul>
+ *
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL.
+ *
+ * @param <P> Type of path (subtree identifier), which represents location in tree
+ * @param <D> Type of data (payload), which represents data payload
+ */
+public interface AsyncTransaction<P extends Path<P>,D> extends //
+    Identifiable<Object> {
+
+    @Override
+    public Object getIdentifier();
+
+
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncWriteTransaction.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncWriteTransaction.java
new file mode 100644 (file)
index 0000000..e47b54a
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Write transaction provides mutation capabilities for a data tree.
+ *
+ * <p>
+ * Initial state of write transaction is a stable snapshot of the current data tree.
+ * The state is captured when the transaction is created and its state and underlying
+ * data tree are not affected by other concurrently running transactions.
+ * <p>
+ * Write transactions are isolated from other concurrent write transactions. All
+ * writes are local to the transaction and represent only a proposal of state
+ * change for the data tree and it is not visible to any other concurrently running
+ * transaction.
+ * <p>
+ * Applications make changes to the local data tree in the transaction by via the
+ * <b>put</b>, <b>merge</b>, and <b>delete</b> operations.
+ *
+ * <h2>Put operation</h2>
+ * Stores a piece of data at a specified path. This acts as an add / replace
+ * operation, which is to say that whole subtree will be replaced by the
+ * specified data.
+ * <p>
+ * Performing the following put operations:
+ *
+ * <pre>
+ * 1) container { list [ a ] }
+ * 2) container { list [ b ] }
+ * </pre>
+ *
+ * will result in the following data being present:
+ *
+ * <pre>
+ * container { list [ b ] }
+ * </pre>
+ * <h2>Merge operation</h2>
+ * Merges a piece of data with the existing data at a specified path. Any pre-existing data
+ * which is not explicitly overwritten will be preserved. This means that if you store a container,
+ * its child lists will be merged.
+ * <p>
+ * Performing the following merge operations:
+ *
+ * <pre>
+ * 1) container { list [ a ] }
+ * 2) container { list [ b ] }
+ * </pre>
+ *
+ * will result in the following data being present:
+ *
+ * <pre>
+ * container { list [ a, b ] }
+ * </pre>
+ *
+ * This also means that storing the container will preserve any
+ * augmentations which have been attached to it.
+ *
+ * <h2>Delete operation</h2>
+ * Removes a piece of data from a specified path.
+ * <p>
+ * After applying changes to the local data tree, applications publish the changes proposed in the
+ * transaction by calling {@link #submit} on the transaction. This seals the transaction
+ * (preventing any further writes using this transaction) and submits it to be
+ * processed and applied to global conceptual data tree.
+ * <p>
+ * The transaction commit may fail due to a concurrent transaction modifying and committing data in
+ * an incompatible way. See {@link #submit} for more concrete commit failure examples.
+ * <p>
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * @param <P>
+ *            Type of path (subtree identifier), which represents location in
+ *            tree
+ * @param <D>
+ *            Type of data (payload), which represents data payload
+ */
+public interface AsyncWriteTransaction<P extends Path<P>, D> extends AsyncTransaction<P, D> {
+    /**
+     * Cancels the transaction.
+     *
+     * Transactions can only be cancelled if it's status is
+     * {@link TransactionStatus#NEW} or {@link TransactionStatus#SUBMITED}
+     *
+     * Invoking cancel() on {@link TransactionStatus#FAILED} or
+     * {@link TransactionStatus#CANCELED} will have no effect, and transaction
+     * is considered cancelled.
+     *
+     * Invoking cancel() on finished transaction  (future returned by {@link #submit()}
+     * already completed with {@link TransactionStatus#COMMITED}) will always
+     * fail (return false).
+     *
+     * @return <tt>false</tt> if the task could not be cancelled,
+     * typically because it has already completed normally;
+     * <tt>true</tt> otherwise
+     *
+     */
+    boolean cancel();
+
+    /**
+     * Removes a piece of data from specified path. This operation does not fail
+     * if the specified path does not exist.
+     *
+     * @param store
+     *            Logical data store which should be modified
+     * @param path
+     *            Data object path
+     * @throws IllegalStateException
+     *             if the transaction is no longer {@link TransactionStatus#NEW}
+     */
+    void delete(LogicalDatastoreType store, P path);
+
+    /**
+     * Submits this transaction to be asynchronously applied to update the logical data tree.
+     * The returned CheckedFuture conveys the result of applying the data changes.
+     * <p>
+     * <b>Note:</b> It is strongly recommended to process the CheckedFuture result in an asynchronous
+     * manner rather than using the blocking get() method. See example usage below.
+     * <p>
+     * This call logically seals the transaction, which prevents the client from
+     * further changing data tree using this transaction. Any subsequent calls to
+     * {@link #put(LogicalDatastoreType, Path, Object)},
+     * {@link #merge(LogicalDatastoreType, Path, Object)} or
+     * {@link #delete(LogicalDatastoreType, Path)} will fail with
+     * {@link IllegalStateException}.
+     *
+     * The transaction is marked as {@link TransactionStatus#SUBMITED} and
+     * enqueued into the data store back-end for processing.
+     *
+     * <p>
+     * Whether or not the commit is successful is determined by versioning
+     * of the data tree and validation of registered commit participants
+     * ({@link AsyncConfigurationCommitHandler})
+     * if the transaction changes the data tree.
+     * <p>
+     * The effects of a successful commit of data depends on data change listeners
+     * ({@link AsyncDataChangeListener}) and commit participants
+     * ({@link AsyncConfigurationCommitHandler}) that are registered with the data broker.
+     * <p>
+     * <h3>Example usage:</h3>
+     * <pre>
+     *  private void doWrite( final int tries ) {
+     *      WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
+     *
+     *      MyDataObject data = ...;
+     *      InstanceIdentifier<MyDataObject> path = ...;
+     *      writeTx.put( LogicalDatastoreType.OPERATIONAL, path, data );
+     *
+     *      Futures.addCallback( writeTx.submit(), new FutureCallback<Void>() {
+     *          public void onSuccess( Void result ) {
+     *              // succeeded
+     *          }
+     *
+     *          public void onFailure( Throwable t ) {
+     *              if( t instanceof OptimisticLockFailedException ) {
+     *                  if( ( tries - 1 ) > 0 ) {
+     *                      // do retry
+     *                      doWrite( tries - 1 );
+     *                  } else {
+     *                      // out of retries
+     *                  }
+     *              } else {
+     *                  // failed due to another type of TransactionCommitFailedException.
+     *              }
+     *          } );
+     * }
+     * ...
+     * doWrite( 2 );
+     * </pre>
+     * <h2>Failure scenarios</h2>
+     * <p>
+     * Transaction may fail because of multiple reasons, such as
+     * <ul>
+     * <li>Another transaction finished earlier and modified the same node in a
+     * non-compatible way (see below). In this case the returned future will fail with an
+     * {@link OptimisticLockFailedException}. It is the responsibility of the
+     * caller to create a new transaction and submit the same modification again in
+     * order to update data tree. <i><b>Warning</b>: In most cases, retrying after an
+     * OptimisticLockFailedException will result in a high probability of success.
+     * However, there are scenarios, albeit unusual, where any number of retries will
+     * not succeed. Therefore it is strongly recommended to limit the number of retries (2 or 3)
+     * to avoid an endless loop.</i>
+     * </li>
+     * <li>Data change introduced by this transaction did not pass validation by
+     * commit handlers or data was incorrectly structured. Returned future will
+     * fail with a {@link DataValidationFailedException}. User should not retry to
+     * create new transaction with same data, since it probably will fail again.
+     * </li>
+     * </ul>
+     *
+     * <h3>Change compatibility</h3>
+     *
+     * There are several sets of changes which could be considered incompatible
+     * between two transactions which are derived from same initial state.
+     * Rules for conflict detection applies recursively for each subtree
+     * level.
+     *
+     * <h4>Change compatibility of leafs, leaf-list items</h4>
+     *
+     * Following table shows  state changes and failures between two concurrent transactions,
+     * which are based on same initial state, Tx 1 completes successfully
+     * before Tx 2 is submitted.
+     *
+     * <table>
+     * <tr><th>Initial state</th><th>Tx 1</th><th>Tx 2</th><th>Result</th></tr>
+     * <tr><td>Empty</td><td>put(A,1)</td><td>put(A,2)</td><td>Tx 2 will fail, state is A=1</td></tr>
+     * <tr><td>Empty</td><td>put(A,1)</td><td>merge(A,2)</td><td>A=2</td></tr>
+     *
+     * <tr><td>Empty</td><td>merge(A,1)</td><td>put(A,2)</td><td>Tx 2 will fail, state is A=1</td></tr>
+     * <tr><td>Empty</td><td>merge(A,1)</td><td>merge(A,2)</td><td>A=2</td></tr>
+     *
+     *
+     * <tr><td>A=0</td><td>put(A,1)</td><td>put(A,2)</td><td>Tx 2 will fail, A=1</td></tr>
+     * <tr><td>A=0</td><td>put(A,1)</td><td>merge(A,2)</td><td>A=2</td></tr>
+     * <tr><td>A=0</td><td>merge(A,1)</td><td>put(A,2)</td><td>Tx 2 will fail, A=1</td></tr>
+     * <tr><td>A=0</td><td>merge(A,1)</td><td>merge(A,2)</td><td>A=2</td></tr>
+     *
+     * <tr><td>A=0</td><td>delete(A)</td><td>put(A,2)</td><td>Tx 2 will fail, A does not exists</td></tr>
+     * <tr><td>A=0</td><td>delete(A)</td><td>merge(A,2)</td><td>A=2</td></tr>
+     * </table>
+     *
+     * <h4>Change compatibility of subtrees</h4>
+     *
+     * Following table shows  state changes and failures between two concurrent transactions,
+     * which are based on same initial state, Tx 1 completes successfully
+     * before Tx 2 is submitted.
+     *
+     * <table>
+     * <tr><th>Initial state</th><th>Tx 1</th><th>Tx 2</th><th>Result</th></tr>
+     *
+     * <tr><td>Empty</td><td>put(TOP,[])</td><td>put(TOP,[])</td><td>Tx 2 will fail, state is TOP=[]</td></tr>
+     * <tr><td>Empty</td><td>put(TOP,[])</td><td>merge(TOP,[])</td><td>TOP=[]</td></tr>
+     *
+     * <tr><td>Empty</td><td>put(TOP,[FOO=1])</td><td>put(TOP,[BAR=1])</td><td>Tx 2 will fail, state is TOP=[FOO=1]</td></tr>
+     * <tr><td>Empty</td><td>put(TOP,[FOO=1])</td><td>merge(TOP,[BAR=1])</td><td>TOP=[FOO=1,BAR=1]</td></tr>
+     *
+     * <tr><td>Empty</td><td>merge(TOP,[FOO=1])</td><td>put(TOP,[BAR=1])</td><td>Tx 2 will fail, state is TOP=[FOO=1]</td></tr>
+     * <tr><td>Empty</td><td>merge(TOP,[FOO=1])</td><td>merge(TOP,[BAR=1])</td><td>TOP=[FOO=1,BAR=1]</td></tr>
+     *
+     * <tr><td>TOP=[]</td><td>put(TOP,[FOO=1])</td><td>put(TOP,[BAR=1])</td><td>Tx 2 will fail, state is TOP=[FOO=1]</td></tr>
+     * <tr><td>TOP=[]</td><td>put(TOP,[FOO=1])</td><td>merge(TOP,[BAR=1])</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+     * <tr><td>TOP=[]</td><td>merge(TOP,[FOO=1])</td><td>put(TOP,[BAR=1])</td><td>Tx 2 will fail, state is TOP=[FOO=1]</td></tr>
+     * <tr><td>TOP=[]</td><td>merge(TOP,[FOO=1])</td><td>merge(TOP,[BAR=1])</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+     * <tr><td>TOP=[]</td><td>delete(TOP)</td><td>put(TOP,[BAR=1])</td><td>Tx 2 will fail, state is empty store</td></tr>
+     * <tr><td>TOP=[]</td><td>delete(TOP)</td><td>merge(TOP,[BAR=1])</td><td>state is TOP=[BAR=1]</td></tr>
+     *
+     * <tr><td>TOP=[]</td><td>put(TOP/FOO,1)</td><td>put(TOP/BAR,1])</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+     * <tr><td>TOP=[]</td><td>put(TOP/FOO,1)</td><td>merge(TOP/BAR,1)</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+     * <tr><td>TOP=[]</td><td>merge(TOP/FOO,1)</td><td>put(TOP/BAR,1)</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+     * <tr><td>TOP=[]</td><td>merge(TOP/FOO,1)</td><td>merge(TOP/BAR,1)</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+     * <tr><td>TOP=[]</td><td>delete(TOP)</td><td>put(TOP/BAR,1)</td><td>Tx 2 will fail, state is empty store</td></tr>
+     * <tr><td>TOP=[]</td><td>delete(TOP)</td><td>merge(TOP/BAR,1]</td><td>Tx 2 will fail, state is empty store</td></tr>
+     *
+     * <tr><td>TOP=[FOO=1]</td><td>put(TOP/FOO,2)</td><td>put(TOP/BAR,1)</td><td>state is TOP=[FOO=2,BAR=1]</td></tr>
+     * <tr><td>TOP=[FOO=1]</td><td>put(TOP/FOO,2)</td><td>merge(TOP/BAR,1)</td><td>state is TOP=[FOO=2,BAR=1]</td></tr>
+     * <tr><td>TOP=[FOO=1]</td><td>merge(TOP/FOO,2)</td><td>put(TOP/BAR,1)</td><td>state is TOP=[FOO=2,BAR=1]</td></tr>
+     * <tr><td>TOP=[FOO=1]</td><td>merge(TOP/FOO,2)</td><td>merge(TOP/BAR,1)</td><td>state is TOP=[FOO=2,BAR=1]</td></tr>
+     * <tr><td>TOP=[FOO=1]</td><td>delete(TOP/FOO)</td><td>put(TOP/BAR,1)</td><td>state is TOP=[BAR=1]</td></tr>
+     * <tr><td>TOP=[FOO=1]</td><td>delete(TOP/FOO)</td><td>merge(TOP/BAR,1]</td><td>state is TOP=[BAR=1]</td></tr>
+     * </table>
+     *
+     *
+     * <h3>Examples of failure scenarios</h3>
+     *
+     * <h4>Conflict of two transactions</h4>
+     *
+     * This example illustrates two concurrent transactions, which derived from
+     * same initial state of data tree and proposes conflicting modifications.
+     *
+     * <pre>
+     * txA = broker.newWriteTransaction(); // allocates new transaction, data tree is empty
+     * txB = broker.newWriteTransaction(); // allocates new transaction, data tree is empty
+     *
+     * txA.put(CONFIGURATION, PATH, A);    // writes to PATH value A
+     * txB.put(CONFIGURATION, PATH, B)     // writes to PATH value B
+     *
+     * ListenableFuture futureA = txA.submit(); // transaction A is sealed and submitted
+     * ListenebleFuture futureB = txB.submit(); // transaction B is sealed and submitted
+     * </pre>
+     *
+     * Commit of transaction A will be processed asynchronously and data tree
+     * will be updated to contain value <code>A</code> for <code>PATH</code>.
+     * Returned {@link ListenableFuture} will successfully complete once
+     * state is applied to data tree.
+     *
+     * Commit of Transaction B will fail, because previous transaction also
+     * modified path in a concurrent way. The state introduced by transaction B
+     * will not be applied. Returned {@link ListenableFuture} object will fail
+     * with {@link OptimisticLockFailedException} exception, which indicates to
+     * client that concurrent transaction prevented the submitted transaction from being
+     * applied.
+     * <br>
+     * @return a CheckFuture containing the result of the commit. The Future blocks until the
+     *         commit operation is complete. A successful commit returns nothing. On failure,
+     *         the Future will fail with a {@link TransactionCommitFailedException} or an exception
+     *         derived from TransactionCommitFailedException.
+     *
+     * @throws IllegalStateException
+     *             if the transaction is not {@link TransactionStatus#NEW}
+     */
+    CheckedFuture<Void,TransactionCommitFailedException> submit();
+
+    /**
+     * @deprecated Use {@link #submit()} instead.
+     */
+    @Deprecated
+    ListenableFuture<RpcResult<TransactionStatus>> commit();
+
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataChange.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataChange.java
new file mode 100644 (file)
index 0000000..ebffbcc
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+public interface DataChange<P extends Path<P>, D> {
+
+    /**
+     * Returns a map of paths and newly created objects
+     *
+     * @return map of paths and newly created objects
+     */
+    Map<P, D> getCreatedOperationalData();
+
+    /**
+     * Returns a map of paths and newly created objects
+     *
+     * @return map of paths and newly created objects
+     */
+    Map<P, D> getCreatedConfigurationData();
+
+    /**
+     * Returns a map of paths and respective updated objects after update.
+     *
+     * Original state of the object is in
+     * {@link #getOriginalOperationalData()}
+     *
+     * @return map of paths and newly created objects
+     */
+    Map<P, D> getUpdatedOperationalData();
+
+    /**
+     * Returns a map of paths and respective updated objects after update.
+     *
+     * Original state of the object is in
+     * {@link #getOriginalConfigurationData()}
+     *
+     * @return map of paths and newly created objects
+     */
+    Map<P, D> getUpdatedConfigurationData();
+
+
+
+    /**
+     * Returns a set of paths of removed objects.
+     *
+     * Original state of the object is in
+     * {@link #getOriginalConfigurationData()}
+     *
+     * @return map of paths and newly created objects
+     */
+    Set<P> getRemovedConfigurationData();
+
+    /**
+     * Returns a set of paths of removed objects.
+     *
+     * Original state of the object is in
+     * {@link #getOriginalOperationalData()}
+     *
+     * @return map of paths and newly created objects
+     */
+    Set<P> getRemovedOperationalData();
+
+    /**
+     * Return a map of paths and original state of updated and removed objectd.
+     *
+     * @return map of paths and original state of updated and removed objectd.
+     */
+    Map<P, D> getOriginalConfigurationData();
+
+    /**
+     * Return a map of paths and original state of updated and removed objectd.
+     *
+     * @return map of paths and original state of updated and removed objectd.
+     */
+    Map<P, D> getOriginalOperationalData();
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataChangeEvent.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataChangeEvent.java
new file mode 100644 (file)
index 0000000..5e5ea0c
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ *
+ *
+ *
+ * @param <P>
+ * @param <D>
+ * @deprecated Replaced by {@link AsyncDataChangeEvent}
+ */
+@Deprecated
+public interface DataChangeEvent<P extends Path<P>,D> extends DataChange<P, D>, Immutable {
+
+    /**
+     * Returns a orignal subtree of data, which starts at the path
+     * where listener was registered.
+     *
+     */
+    D getOriginalConfigurationSubtree();
+
+    /**
+     * Returns a new subtree of data, which starts at the path
+     * where listener was registered.
+     *
+     */
+    D getOriginalOperationalSubtree();
+
+
+
+    /**
+     * Returns a updated subtree of data, which starts at the path
+     * where listener was registered.
+     *
+     */
+    D getUpdatedConfigurationSubtree();
+
+    /**
+     * Returns a udpated subtree of data, which starts at the path
+     * where listener was registered.
+     *
+     */
+    D getUpdatedOperationalSubtree();
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataChangeListener.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataChangeListener.java
new file mode 100644 (file)
index 0000000..2100617
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import java.util.EventListener;
+
+import org.opendaylight.yangtools.concepts.Path;
+/**
+ *
+ *
+ * @deprecated Replaced by {@link AsyncDataChangeEvent}
+ */
+@Deprecated
+public interface DataChangeListener<P extends Path<P>, D> extends EventListener {
+    /**
+     * Note that this method may be invoked from a shared thread pool, so
+     * implementations SHOULD NOT perform CPU-intensive operations and they
+     * definitely MUST NOT invoke any potentially blocking operations.
+     *
+     * @param change Data Change Event being delivered.
+     **/
+    void onDataChanged(DataChangeEvent<P, D> change);
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataChangePublisher.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataChangePublisher.java
new file mode 100644 (file)
index 0000000..222dba8
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ *
+ * @deprecated Replaced by {@link AsyncDataBroker}
+ */
+@Deprecated
+public interface DataChangePublisher<P extends Path<P>, D, L extends DataChangeListener<P,D>> {
+
+    ListenerRegistration<L> registerDataChangeListener(P path, L listener);
+
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataCommitHandler.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataCommitHandler.java
new file mode 100644 (file)
index 0000000..a9f02a8
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+/**
+ * Two phase commit handler (cohort) of the two-phase commit protocol of data.
+ *
+ * <p>
+ * The provider should expose the implementation of DataCommitHandler if it's
+ * functionality depends on any subset of data stored in data repositories, in
+ * order to participate in {@link DataBrokerService#commit(DataStoreIdentifier)
+ * operation.
+ *
+ * <p>
+ * Operations of two-phase commit handlers should not change data in data store,
+ * this is responsibility of the coordinator (broker or some component of the
+ * broker).
+ *
+ * Commit handlers are responsible for changing the internal state of the
+ * provider to reflect the committed changes in data.
+ *
+ * <h3>Two-phase commit</h3>
+ *
+ * <h4>Commit Request Phase</h4>
+ *
+ * <ol>
+ * <li> <code>Consumer</code> edits data by invocation of
+ * <code>DataBrokerService.editCandidateData(DataStoreIdentifier, DataRoot)</code>
+ * <li> <code>Consumer</code> starts a commit by invoking
+ * <code>DataBrokerService.commit(DataStoreIdentifier)</code>
+ * <li> <code>Broker</code> retrieves a list of all registered
+ * <code>DataCommitHandlers</code>
+ * <li>For each <code>DataCommitHandler</code>
+ * <ol>
+ * <li><code>Broker</code> invokes a
+ * <code>DataCommitHandler.requestCommit(DataStoreIdentifier)</code> operation.
+ * <li><code>DataCommitHandler</code> returns a <code>RpcResult</code> with
+ * <code>CommitTransaction</code>
+ * <li>If the result was successful, broker adds <code>CommitTransaction</code>
+ * to the list of opened transactions. If not, brokers stops a commit request
+ * phase and starts a rollback phase.
+ * </ol>
+ * <li><code>Broker</code> starts a commit finish phase
+ * </ol>
+ *
+ * <h4>Commit Finish Phase</h4>
+ *
+ * <ol>
+ * <li>For each <code>CommitTransaction</code> from Commit Request phase
+ * <ol>
+ * <li><code>Broker</code> broker invokes a
+ * <code>CommitTransaction.finish()</code>
+ * <li>The provider finishes a commit (applies the change) and returns an
+ * <code>RpcResult</code>.
+ * </ol>
+ * <li>
+ * <ul>
+ * <li>If all returned results means successful, the brokers end two-phase
+ * commit by returning a success commit result to the Consumer.
+ * <li>If error occured, the broker starts a commit rollback phase.
+ * </ul>
+ * </ol>
+ *
+ * <h4>Commit Rollback Phase</h4>
+ * <li>For each <code>DataCommitTransaction</code> from Commit Request phase
+ * <ol>
+ * <li><code>Broker</code>
+ * broker invokes a {@link DataCommitTransaction#finish()}
+ * <li>The provider rollbacks a commit and returns an {@link RpcResult} of
+ * rollback. </ol>
+ * <li>Broker returns a error result to the consumer.
+ *
+ * @param <P> Class representing a path
+ * @param <D> Superclass from which all data objects are derived from.
+ * @deprecated Replaced by {@link AsyncConfigurationCommitHandler}
+ */
+@Deprecated
+public interface DataCommitHandler<P extends Path<P>, D> {
+
+
+    DataCommitTransaction<P, D> requestCommit(DataModification<P,D> modification);
+
+    public interface DataCommitTransaction<P extends Path<P>, D> {
+
+        DataModification<P,D> getModification();
+
+        /**
+         *
+         * Finishes a commit.
+         *
+         * This callback is invoked by commit coordinator to finish commit action.
+         *
+         * The implementation is required to finish transaction or return unsuccessful
+         * rpc result if something went wrong.
+         *
+         * The provider (commit handler) should apply all changes to its state
+         * which are a result of data change-
+         *
+         * @return
+         */
+        RpcResult<Void> finish() throws IllegalStateException;
+
+        /**
+         * Rollbacks a commit.
+         *
+         * This callback is invoked by commit coordinator to finish commit action.
+         *
+         * The provider (commit handler) should rollback all changes to its state
+         * which were a result of previous request commit.
+         *
+         * @return
+         * @throws IllegalStateException
+         *             If the method is invoked after {@link #finish()}
+         */
+        RpcResult<Void> rollback() throws IllegalStateException;
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataCommitHandlerRegistration.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataCommitHandlerRegistration.java
new file mode 100644 (file)
index 0000000..dceb3de
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.concepts.Registration;
+
+/**
+ *
+ *
+ * @deprecated THis was intended as Registration object, normal use of {@link org.opendaylight.yangtools.concepts.ObjectRegistration}
+ * is suffiecient, since {@link #getPath()} was implementation leak.
+ *
+ * @param <P>
+ * @param <D>
+ */
+@Deprecated
+public interface DataCommitHandlerRegistration<P extends Path<P>,D> extends Registration {
+
+    P getPath();
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataModification.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataModification.java
new file mode 100644 (file)
index 0000000..630078d
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+/**
+ *
+ * @deprecated Replaced by {@link AsyncWriteTransaction}
+ */
+@Deprecated
+public interface DataModification<P extends Path<P>, D> extends DataChange<P, D>, DataReader<P, D> {
+    /**
+     * Returns transaction identifier
+     *
+     * @return Transaction identifier
+     */
+    Object getIdentifier();
+
+    TransactionStatus getStatus();
+
+    /**
+     * Store a piece of data at specified path. This acts as a merge operation,
+     * which is to say that any pre-existing data which is not explicitly
+     * overwritten will be preserved. This means that if you store a container,
+     * its child lists will be merged. Performing the following put operations:
+     *
+     * 1) container { list [ a ] }
+     * 2) container { list [ b ] }
+     *
+     * will result in the following data being present:
+     *
+     * container { list [ a, b ] }
+     *
+     * This also means that storing the container will preserve any augmentations
+     * which have been attached to it.
+     *
+     * If you require an explicit replace operation, perform
+     * {@link removeOperationalData} first.
+     */
+    void putOperationalData(P path, D data);
+
+    /**
+     * Store a piece of data at specified path. This acts as a merge operation,
+     * which is to say that any pre-existing data which is not explicitly
+     * overwritten will be preserved. This means that if you store a container,
+     * its child lists will be merged. Performing the following put operations:
+     *
+     * 1) container { list [ a ] }
+     * 2) container { list [ b ] }
+     *
+     * will result in the following data being present:
+     *
+     * container { list [ a, b ] }
+     *
+     * This also means that storing the container will preserve any augmentations
+     * which have been attached to it.
+     *
+     * If you require an explicit replace operation, perform
+     * {@link removeConfigurationData} first.
+     */
+    void putConfigurationData(P path, D data);
+
+    void removeOperationalData(P path);
+
+    void removeConfigurationData(P path);
+
+    /**
+     * Initiates a two-phase commit of modification.
+     *
+     * <p>
+     * The successful commit changes the state of the system and may affect
+     * several components.
+     *
+     * <p>
+     * The effects of successful commit of data are described in the
+     * specifications and YANG models describing the Provider components of
+     * controller. It is assumed that Consumer has an understanding of this
+     * changes.
+     *
+     *
+     * @see DataCommitHandler for further information how two-phase commit is
+     *      processed.
+     * @param store
+     *            Identifier of the store, where commit should occur.
+     * @return Result of the Commit, containing success information or list of
+     *         encountered errors, if commit was not successful. The Future
+     *         blocks until {@link TransactionStatus#COMMITED} or
+     *         {@link TransactionStatus#FAILED} is reached.
+     */
+    Future<RpcResult<TransactionStatus>> commit();
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataModificationTransactionFactory.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataModificationTransactionFactory.java
new file mode 100644 (file)
index 0000000..6a370b8
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+/**
+ *
+ * @deprecated Replaced by {@link AsyncDataTransactionFactory}
+ */
+@Deprecated
+public interface DataModificationTransactionFactory<P extends Path<P> ,D> {
+
+    DataModification<P, D> beginTransaction();
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataProvider.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataProvider.java
new file mode 100644 (file)
index 0000000..fdeda28
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ *
+ *
+ * @deprecated Replaced by org.opendaylight.controller.sal.core.spi.data.DOMStore contract.
+ */
+@Deprecated
+public interface DataProvider<P extends Path<P>, D> extends DataReader<P, D> {
+
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataProvisionService.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataProvisionService.java
new file mode 100644 (file)
index 0000000..6c7166e
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.concepts.Registration;
+
+/**
+ *
+ *
+ * @deprecated replaced by {@link AsyncDataBroker} and {@link AsyncConfigurationCommitCoordinator}
+ */
+@Deprecated
+public interface DataProvisionService<P extends Path<P> , D> {
+
+    public Registration registerCommitHandler(P path, DataCommitHandler<P, D> commitHandler);
+
+    public ListenerRegistration<RegistrationListener<DataCommitHandlerRegistration<P, D>>>
+        registerCommitHandlerListener(RegistrationListener<DataCommitHandlerRegistration<P, D>> commitHandlerListener);
+
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataReader.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataReader.java
new file mode 100644 (file)
index 0000000..2f657b8
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * Reader for reading YANG subtrees based on their path.
+ *
+ * Reader is requested to return object at specified path and all it's subnodes
+ * known to the reader or null if node is not found in this reader.
+ *
+ * @param <P> Path Type
+ * @param <D> Data Type
+ * @deprecated Replaced by org.opendaylight.controller.sal.core.spi.data.DOMStore contract.
+ */
+@Deprecated
+public interface DataReader<P extends Path<P> ,D> {
+
+    /**
+     * Reads data from Operational data store located at provided path
+     *
+     * @param path Path to data
+     * @return
+     */
+    D readOperationalData(P path);
+
+    D readConfigurationData(P path);
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataStore.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataStore.java
new file mode 100644 (file)
index 0000000..5688dd0
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ *
+ * @deprecated Replaced by org.opendaylight.controller.sal.core.spi.data.DOMStore Contract.
+ */
+@Deprecated
+public interface DataStore<P extends Path<P>, D> extends //
+        DataReader<P, D>, //
+        DataModificationTransactionFactory<P, D> {
+
+    @Override
+    public DataModification<P, D> beginTransaction();
+
+    @Override
+    public D readConfigurationData(P path);
+
+    @Override
+    public D readOperationalData(P path);
+
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataStoreUnavailableException.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataStoreUnavailableException.java
new file mode 100644 (file)
index 0000000..ebbd116
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+/**
+ * This exception occurs if the datastore is temporarily unavailable.
+ * A retry of the transaction may succeed after a period of time
+ */
+
+public class DataStoreUnavailableException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public DataStoreUnavailableException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataValidationFailedException.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataValidationFailedException.java
new file mode 100644 (file)
index 0000000..d48bfc7
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+import com.google.common.base.Preconditions;
+
+/**
+ *
+ * Failure of asynchronous transaction commit caused by invalid data.
+ *
+ * This exception is raised and returned when transaction commit
+ * failed, because other data submitted via transactions
+ *
+ *  Clients usually are not able recover from this error condition by
+ *  retrieving same transaction, since data introduced by this transaction
+ *  are invalid.
+ *
+ */
+public class DataValidationFailedException extends TransactionCommitFailedException {
+
+    private static final long serialVersionUID = 1L;
+
+    private Path<?> path;
+
+    private Class<? extends Path<?>> pathType;
+
+    public <P extends Path<P>> DataValidationFailedException(final Class<P> pathType,final P path,
+                                                             final String message, final Throwable cause) {
+        super(message, cause, RpcResultBuilder.newError(ErrorType.APPLICATION, "invalid-value", message, null,
+                                                        path != null ? path.toString() : null, cause));
+        this.pathType = Preconditions.checkNotNull(pathType, "path type must not be null");
+        this.path = Preconditions.checkNotNull(path,"path must not be null.");
+    }
+
+    public  <P extends Path<P>> DataValidationFailedException(final Class<P> pathType,final P path,
+                                                              final String message) {
+        this(pathType, path, message, null);
+    }
+
+    public final Path<?> getPath() {
+        return path;
+    }
+
+    public final Class<? extends Path<?>> getPathType() {
+        return pathType;
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/LogicalDatastoreType.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/LogicalDatastoreType.java
new file mode 100644 (file)
index 0000000..d2e41f1
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+public enum LogicalDatastoreType {
+
+    /**
+     * Logical atastore representing operational state of the system
+     * and it's components
+     *
+     * This datastore is used to describe operational state of
+     * the system and it's operation related data.
+     *
+     */
+    OPERATIONAL,
+    /**
+     * Logical Datastore representing configuration state of the system
+     * and it's components.
+     *
+     * This datastore is used to describe intended state of
+     * the system and intended operation mode.
+     *
+     */
+    CONFIGURATION
+
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/OptimisticLockFailedException.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/OptimisticLockFailedException.java
new file mode 100644 (file)
index 0000000..637721c
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+
+/**
+*
+* Failure of asynchronous transaction commit caused by failure
+* of optimistic locking.
+*
+* This exception is raised and returned when transaction commit
+* failed, because other transaction finished successfully
+* and modified same data as failed transaction.
+*
+*  Clients may recover from this error condition by
+*  retrieving current state and submitting new updated
+*  transaction.
+*
+*/
+public class OptimisticLockFailedException extends TransactionCommitFailedException {
+
+    private static final long serialVersionUID = 1L;
+
+    public OptimisticLockFailedException(final String message, final Throwable cause) {
+        super(message, cause, RpcResultBuilder.newError(ErrorType.APPLICATION, "resource-denied",
+                                                        message, null, null, cause));
+    }
+
+    public OptimisticLockFailedException(final String message) {
+        this(message, null);
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/ReadFailedException.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/ReadFailedException.java
new file mode 100644 (file)
index 0000000..b0a7807
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
+import org.opendaylight.yangtools.yang.common.OperationFailedException;
+import org.opendaylight.yangtools.yang.common.RpcError;
+
+/**
+ * An exception for a failed read.
+ */
+public class ReadFailedException extends OperationFailedException {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final ExceptionMapper<ReadFailedException> MAPPER =
+            new ExceptionMapper<ReadFailedException>("read", ReadFailedException.class) {
+                @Override
+                protected ReadFailedException newWithCause(String message, Throwable cause) {
+                    return new ReadFailedException(message, cause);
+                }
+    };
+
+    public ReadFailedException(String message, RpcError... errors) {
+        super(message, errors);
+    }
+
+    public ReadFailedException(String message, Throwable cause, RpcError... errors) {
+        super(message, cause, errors);
+    }
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChain.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChain.java
new file mode 100644 (file)
index 0000000..32e32f9
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * A chain of transactions. Transactions in a chain need to be committed in
+ * sequence and each transaction should see the effects of previous committed transactions
+ * as they occurred. A chain makes no guarantees of atomicity across the chained transactions -
+ * the transactions are committed as soon as possible in the order that they were submitted.
+ *
+ * This behaviour is different from the default AsyncDataBroker, where a
+ * transaction is always created from the current global state, not taking into
+ * account any transactions previously committed by the calling thread. Due to
+ * the asynchronous nature of transaction submission this can lead to surprising
+ * results. If a thread executes the following sequence sufficiently quickly:
+ *
+ * AsyncWriteTransaction t1 = broker.newWriteOnlyTransaction();
+ * t1.put(id, data);
+ * t1.submit();
+ *
+ * AsyncReadTransaction t2 = broker.newReadOnlyTransaction();
+ * Optional<?> maybeData = t2.read(id).get();
+ *
+ * it may happen, that it sees maybeData.isPresent() == false, simply because
+ * t1 has not completed the processes of being applied and t2 is actually
+ * allocated from the previous state. This is obviously bad for users who create
+ * incremental state in the datastore and actually read what they write in
+ * subsequent transactions.
+ *
+ * Using a TransactionChain instead of a broker solves this particular problem,
+ * and leads to expected behavior: t2 will always see the data written in t1
+ * present.
+ */
+public interface TransactionChain<P extends Path<P>, D> extends AutoCloseable,
+        AsyncDataTransactionFactory<P, D> {
+
+    /**
+     * Create a new read only transaction which will continue the chain.
+     *
+     * <p>
+     * The previous write transaction has to be either SUBMITTED
+     * ({@link AsyncWriteTransaction#submit submit} was invoked) or CANCELLED
+     * ({@link #close close} was invoked).
+     * <p>
+     * The returned read-only transaction presents an isolated view of the data if the previous
+     * write transaction was successful - in other words, this read-only transaction will see the
+     * state changes made by the previous write transaction in the chain. However, state which
+     * was introduced by other transactions outside this transaction chain after creation of
+     * the previous transaction is not visible.
+     *
+     * @return New transaction in the chain.
+     * @throws IllegalStateException
+     *             if the previous transaction was not SUBMITTED or CANCELLED.
+     * @throws TransactionChainClosedException
+     *             if the chain has been closed.
+     */
+    @Override
+    public AsyncReadOnlyTransaction<P, D> newReadOnlyTransaction();
+
+    /**
+     * Create a new read-write transaction which will continue the chain.
+     *
+     * <p>
+     * The previous write transaction has to be either SUBMITTED
+     * ({@link AsyncWriteTransaction#submit submit} was invoked) or CANCELLED
+     * ({@link #close close} was invoked).
+     * <p>
+     * The returned read-write transaction presents an isolated view of the data if the previous
+     * write transaction was successful - in other words, this read-write transaction will see the
+     * state changes made by the previous write transaction in the chain. However, state which
+     * was introduced by other transactions outside this transaction chain after creation of
+     * the previous transaction is not visible.
+     * <p>
+     * Committing this read-write transaction using {@link AsyncWriteTransaction#submit submit}
+     * will submit the state changes in this transaction to be visible to any subsequent
+     * transaction in this chain and also to any transaction outside this chain.
+     *
+     * @return New transaction in the chain.
+     * @throws IllegalStateException
+     *             if the previous transaction was not SUBMITTED or CANCELLED.
+     * @throws TransactionChainClosedException
+     *             if the chain has been closed.
+     */
+    @Override
+    public AsyncReadWriteTransaction<P, D> newReadWriteTransaction();
+
+    /**
+     * Create a new write-only transaction which will continue the chain.
+     *
+     * <p>
+     * The previous write transaction has to be either SUBMITTED
+     * ({@link AsyncWriteTransaction#submit submit} was invoked) or CANCELLED
+     * ({@link #close close} was invoked).
+     * <p>
+     * The returned write-only transaction presents an isolated view of the data if the previous
+     * write transaction was successful - in other words, this write-only transaction will see the
+     * state changes made by the previous write transaction in the chain. However, state which
+     * was introduced by other transactions outside this transaction chain after creation of
+     * the previous transaction is not visible.
+     * <p>
+     * Committing this write-only transaction using {@link AsyncWriteTransaction#submit submit}
+     * will submit the state changes in this transaction to be visible to any subsequent
+     * transaction in this chain and also to any transaction outside this chain.
+     *
+     * @return New transaction in the chain.
+     * @throws IllegalStateException
+     *             if the previous transaction was not SUBMITTED or CANCELLED.
+     * @throws TransactionChainClosedException
+     *             if the chain has been closed.
+     */
+    @Override
+    public AsyncWriteTransaction<P, D> newWriteOnlyTransaction();
+
+    @Override
+    void close();
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChainClosedException.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChainClosedException.java
new file mode 100644 (file)
index 0000000..5e1b35d
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+/**
+ * Exception thrown when an attempt is made to open a new transaction in a closed
+ * chain.
+ */
+public final class TransactionChainClosedException extends IllegalStateException {
+    private static final long serialVersionUID = 1L;
+
+    public TransactionChainClosedException(final String message) {
+        super(message);
+    }
+
+    public TransactionChainClosedException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChainFactory.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChainFactory.java
new file mode 100644 (file)
index 0000000..470e611
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * Interface for creating transaction chains.
+ */
+public interface TransactionChainFactory<P extends Path<P>, D> {
+
+    /**
+     * Create a new transaction chain. The chain will be initialized to read
+     * from its backing datastore, with no outstanding transaction. Listener
+     * will be registered to handle chain-level events.
+     *
+     * @param listener Transaction chain event listener
+     * @return A new transaction chain.
+     */
+    TransactionChain<P, D> createTransactionChain(TransactionChainListener listener);
+}
+
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChainListener.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChainListener.java
new file mode 100644 (file)
index 0000000..52b0812
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import java.util.EventListener;
+
+/**
+ * Listener for transaction chain events.
+ */
+public interface TransactionChainListener extends EventListener {
+    /**
+     * Invoked if when a transaction in the chain fails. All other transactions are automatically cancelled by the time
+     * this notification is invoked. Implementations should invoke chain.close() to close the chain.
+     *
+     * @param chain Transaction chain which failed
+     * @param transaction Transaction which caused the chain to fail
+     * @param cause The cause of transaction failure
+     */
+    void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction, Throwable cause);
+
+    /**
+     * Invoked when a transaction chain is completed. A transaction chain is considered completed when it has been
+     * closed and all its instructions have completed successfully.
+     *
+     * @param chain Transaction chain which completed
+     */
+    void onTransactionChainSuccessful(TransactionChain<?, ?> chain);
+}
+
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionCommitDeadlockException.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionCommitDeadlockException.java
new file mode 100644 (file)
index 0000000..50952ea
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import com.google.common.base.Supplier;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+/**
+ * A type of TransactionCommitFailedException that indicates a situation that would result in a
+ * threading deadlock. This can occur if a caller that submits a write transaction tries to perform
+ * a blocking call via one of the <code>get</code> methods on the returned ListenableFuture. Callers
+ * should process the commit result asynchronously (via Futures#addCallback) to ensure deadlock
+ * won't occur.
+ *
+ * @author Thomas Pantelis
+ */
+public class TransactionCommitDeadlockException extends TransactionCommitFailedException {
+    private static final long serialVersionUID = 1L;
+    private static final String DEADLOCK_MESSAGE =
+            "An attempt to block on a ListenableFuture via a get method from a write " +
+            "transaction submit was detected that would result in deadlock. The commit " +
+            "result must be obtained asynchronously, e.g. via Futures#addCallback, to avoid deadlock.";
+    private static final RpcError DEADLOCK_RPCERROR = RpcResultBuilder.newError(ErrorType.APPLICATION, "lock-denied", DEADLOCK_MESSAGE);
+
+    public static final Supplier<Exception> DEADLOCK_EXCEPTION_SUPPLIER = new Supplier<Exception>() {
+        @Override
+        public Exception get() {
+            return new TransactionCommitDeadlockException(DEADLOCK_MESSAGE, DEADLOCK_RPCERROR);
+        }
+    };
+
+    public TransactionCommitDeadlockException(final String message, final RpcError... errors) {
+        super(message, errors);
+    }
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionCommitFailedException.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionCommitFailedException.java
new file mode 100644 (file)
index 0000000..7ac76e4
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.yang.common.OperationFailedException;
+import org.opendaylight.yangtools.yang.common.RpcError;
+
+/**
+ *
+ * Failed commit of asynchronous transaction
+ *
+ * This exception is raised and returned when transaction commit
+ * failed.
+ *
+ */
+public class TransactionCommitFailedException extends OperationFailedException {
+
+    private static final long serialVersionUID = 1L;
+
+    public TransactionCommitFailedException(final String message, final RpcError... errors) {
+        this(message, null, errors);
+    }
+
+    public TransactionCommitFailedException(final String message, final Throwable cause,
+                                            final RpcError... errors) {
+        super(message, cause, errors);
+    }
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/notify/NotificationPublishService.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/notify/NotificationPublishService.java
new file mode 100644 (file)
index 0000000..0dd04d5
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.notify;
+
+import java.util.concurrent.ExecutorService;
+
+/**
+ * Interface for publishing YANG-modeled notifications.
+ * <p>
+ * Users of this interface can publish any YANG-modeled notification which will
+ * be delivered to all subscribed listeners.
+ * <p>
+ * Preferred way of publishing of notifications is done by invoking {@link #publish(Object)}.
+ *
+ * <p>You may consider using {@link #publish(Object, ExecutorService)} if and only if
+ * your use-case requires customized  execution policy or run-to-completion
+ * inside process.
+ *
+ * <p>
+ * The metadata required to deliver a notification to the correct listeners is
+ * extracted from the published notification.
+ *
+ *
+ * FIXME: Consider clarification of execution/delivery policy, how it will be
+ * affected by Actor model and cluster-wide notifications.
+ *
+ * @param <N>
+ *            the type of notifications
+ */
+public interface NotificationPublishService<N> {
+
+    /**
+     * Publishes a notification and notifies subscribed listeners. All listener
+     * notifications are done via a default executor.
+     * <p>
+     * <b>Note:</b> This call will block when the default executor is saturated
+     * and the notification queue for this executor is full.
+     *
+     * @param notification
+     *            the notification to publish.
+     */
+    void publish(N notification);
+
+    /**
+     * Publishes a notification and notifies subscribed listeners. All listener
+     * notifications are done via the provided executor.
+     * <p>
+     * <b>Note:</b> Use only if necessary. Consider using
+     * {@link #publish(Object)} for most use-cases.
+     *
+     * <p>
+     * By using this method you could customize execution policy of listeners present
+     * inside process (e.g. using  single-threaded executor or even same-thread executor
+     * delivery.
+     *
+     * <p>
+     * This executor is used only for inside-process notification deliveries.
+     *
+     * @param notification
+     *            the notification to publish.
+     * @param executor
+     *            the executor that will be used to deliver notifications to
+     *            subscribed listeners.
+     */
+    void publish(N notification, ExecutorService executor);
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/notify/NotificationSubscriptionService.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/notify/NotificationSubscriptionService.java
new file mode 100644 (file)
index 0000000..a83610f
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.notify;
+
+import java.util.EventListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+public interface NotificationSubscriptionService<T, N, L extends EventListener> {
+
+    ListenerRegistration<L> registerNotificationListener(T type, L listener);
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/MutableRoutingTable.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/MutableRoutingTable.java
new file mode 100644 (file)
index 0000000..0ecb2c5
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.routing;
+
+import org.opendaylight.yangtools.concepts.Mutable;
+import org.opendaylight.yangtools.concepts.Path;
+
+public interface MutableRoutingTable<C, P extends Path<P>, T> extends RoutingTable<C,P,T>, Mutable {
+
+    void setDefaultRoute(T target);
+    void updateRoute(P path,T target);
+    void removeRoute(P path);
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/Route.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/Route.java
new file mode 100644 (file)
index 0000000..4bfb30f
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.routing;
+
+import org.opendaylight.yangtools.concepts.Immutable;
+
+public interface Route<C,P> extends Immutable {
+
+    C getType();
+
+    P getPath();
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RouteChange.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RouteChange.java
new file mode 100644 (file)
index 0000000..0c04b93
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.routing;
+
+import java.util.Map;
+import java.util.Set;
+/**
+ * Event representing change in RPC routing table.
+ *
+ *
+ * @param <C> Type, which is used to represent Routing context.
+ * @param <P> Type of data tree path, which is used to identify route.
+ */
+public interface RouteChange<C,P> {
+
+    /**
+     *
+     * Returns a map of removed routes in associated routing contexts.
+     * <p>
+     * This map represents routes, which were withdrawn from broker local
+     * routing table and broker may need to forward RPC to other broker
+     * in order to process RPC request.
+     *
+     * @return Map of contexts and removed routes
+     */
+    Map<C,Set<P>> getRemovals();
+    /**
+    *
+    * Returns a map of announced routes in associated routing contexts.
+    *
+    * This map represents routes, which were announced by broker
+    * and are present in broker's local routing table. This routes
+    * are processed by implementations which are registered
+    * to originating broker.
+    *
+    * @return Map of contexts and announced routes
+    */
+    Map<C,Set<P>> getAnnouncements();
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RouteChangeListener.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RouteChangeListener.java
new file mode 100644 (file)
index 0000000..b3b6fe6
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.routing;
+
+import java.util.EventListener;
+/**
+ *
+ * Listener which is interested in receiving RouteChangeEvents
+ * for its local broker.
+ * <p>
+ * Listener is registerd via {@link RouteChangePublisher#registerRouteChangeListener(RouteChangeListener)}
+ *
+ *
+ * @param <C> Type, which is used to represent Routing context.
+ * @param <P> Type of data tree path, which is used to identify route.
+ */
+public interface RouteChangeListener<C,P> extends EventListener {
+
+    /**
+     * Callback which is invoked if there is an rpc routing table change.
+     *
+     * @param change Event representing change in local RPC routing table.
+     */
+    void onRouteChange(RouteChange<C, P> change);
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RouteChangePublisher.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RouteChangePublisher.java
new file mode 100644 (file)
index 0000000..dc6b6dd
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.routing;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * Publishes changes in local RPC routing table to registered listener.
+ *
+ * @param <C> Type, which is used to represent Routing context.
+ * @param <P> Type of data tree path, which is used to identify route.
+ */
+public interface RouteChangePublisher<C,P> {
+
+    <L extends RouteChangeListener<C,P>> ListenerRegistration<L> registerRouteChangeListener(L listener);
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RoutedRegistration.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RoutedRegistration.java
new file mode 100644 (file)
index 0000000..7b1cdea
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.routing;
+
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.concepts.Registration;
+
+/**
+ * Base interface for a routed RPC RPC implementation registration.
+ *
+ * @param <C> the context type used for routing
+ * @param <P> the path identifier type
+ * @param <S> the RPC implementation type
+ */
+public interface RoutedRegistration<C, P extends Path<P>, S> extends Registration {
+
+    /**
+     * Registers the RPC implementation associated with this registration for the given path
+     * identifier and context.
+     *
+     * @param context the context used for routing RPCs to this implementation.
+     * @param path the path identifier for which to register.
+     */
+    void registerPath(C context, P path);
+
+    /**
+     * Unregisters the RPC implementation associated with this registration for the given path
+     * identifier and context.
+     *
+     * @param context the context used for routing RPCs to this implementation.
+     * @param path the path identifier for which to unregister.
+     */
+    void unregisterPath(C context, P path);
+
+    @Override
+    void close();
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/Router.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/Router.java
new file mode 100644 (file)
index 0000000..6e45ed5
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.routing;
+
+import java.util.Map;
+import java.util.Set;
+
+public interface Router<C,P,D> extends //
+        RouteChangePublisher<C, P> {
+
+    Map<C, Set<P>> getAnnouncedPaths();
+}
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RoutingTable.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RoutingTable.java
new file mode 100644 (file)
index 0000000..9e72b68
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.routing;
+
+import java.util.Map;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+public interface RoutingTable<C, P extends Path<P>, T> {
+
+    C getIdentifier();
+
+    T getDefaultRoute();
+
+    Map<P,T> getRoutes();
+
+    T getRoute(P path);
+}
diff --git a/opendaylight/md-sal/sal-common-impl/pom.xml b/opendaylight/md-sal/sal-common-impl/pom.xml
new file mode 100644 (file)
index 0000000..1d9745c
--- /dev/null
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>sal-parent</artifactId>
+    <version>1.3.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>sal-common-impl</artifactId>
+  <packaging>bundle</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-common-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-common-util</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-data-impl</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-model-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-all</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-parser-impl</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <configuration>
+          <instructions>
+            <Export-Package>org.opendaylight.controller.md.sal.common.impl,
+                            org.opendaylight.controller.md.sal.common.impl.*</Export-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <scm>
+    <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+    <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+    <tag>HEAD</tag>
+    <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+  </scm>
+
+</project>
diff --git a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/ListenerRegistry.java b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/ListenerRegistry.java
new file mode 100644 (file)
index 0000000..f337b55
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Collections;
+import java.util.EventListener;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+public class ListenerRegistry<T extends EventListener> {
+
+    final Set<ListenerRegistration<T>> listeners;
+    final Set<ListenerRegistration<T>> unmodifiableView;
+
+    public ListenerRegistry() {
+        listeners = new HashSet<>();
+        unmodifiableView = Collections.unmodifiableSet(listeners);
+    }
+
+    public Iterable<ListenerRegistration<T>> getListeners() {
+        return unmodifiableView;
+    }
+
+
+    public ListenerRegistration<T> register(T listener) {
+        checkNotNull(listener, "Listener should not be null.");
+        ListenerRegistrationImpl<T> ret = new ListenerRegistrationImpl<T>(listener);
+        listeners.add(ret);
+        return ret;
+    }
+
+
+    @SuppressWarnings("rawtypes")
+    private void remove(ListenerRegistrationImpl registration) {
+        listeners.remove(registration);
+    }
+
+    private class ListenerRegistrationImpl<P extends EventListener> //
+            extends AbstractObjectRegistration<P> //
+            implements ListenerRegistration<P> {
+
+        public ListenerRegistrationImpl(P instance) {
+            super(instance);
+        }
+
+        @Override
+        protected void removeRegistration() {
+            ListenerRegistry.this.remove(this);
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/routing/RoutingUtils.java b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/routing/RoutingUtils.java
new file mode 100644 (file)
index 0000000..0672ba2
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.impl.routing;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChange;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class RoutingUtils {
+
+    public static <C,P> RouteChange<C,P> removalChange(C context,P path) {
+        final ImmutableMap<C, Set<P>> announcements = ImmutableMap.<C,Set<P>>of();
+        final ImmutableMap<C, Set<P>> removals = ImmutableMap.<C,Set<P>>of(context, ImmutableSet.of(path));
+        return new RouteChangeImpl<C,P>(announcements, removals);
+    }
+
+    public static <C,P> RouteChange<C,P> announcementChange(C context,P path) {
+        final ImmutableMap<C, Set<P>> announcements = ImmutableMap.<C,Set<P>>of(context, ImmutableSet.of(path));
+        final ImmutableMap<C, Set<P>> removals = ImmutableMap.<C,Set<P>>of();
+        return new RouteChangeImpl<C,P>(announcements, removals);
+    }
+
+
+    public static <C,P> RouteChange<C,P> change(Map<C, Set<P>> announcements,
+            Map<C, Set<P>> removals) {
+        final ImmutableMap<C, Set<P>> immutableAnnouncements = ImmutableMap.<C,Set<P>>copyOf(announcements);
+        final ImmutableMap<C, Set<P>> immutableRemovals = ImmutableMap.<C,Set<P>>copyOf(removals);
+        return new RouteChangeImpl<C,P>(immutableAnnouncements, immutableRemovals);
+    }
+
+
+    private static class RouteChangeImpl<C,P> implements RouteChange<C, P> {
+        private final Map<C, Set<P>> removal;
+        private final Map<C, Set<P>> announcement;
+
+        public RouteChangeImpl(ImmutableMap<C, Set<P>> announcement, ImmutableMap<C, Set<P>> removal) {
+            super();
+            this.removal = removal;
+            this.announcement = announcement;
+        }
+
+        @Override
+        public Map<C, Set<P>> getAnnouncements() {
+            return announcement;
+        }
+
+        @Override
+        public Map<C, Set<P>> getRemovals() {
+            return removal;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((announcement == null) ? 0 : announcement.hashCode());
+            result = prime * result + ((removal == null) ? 0 : removal.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null) {
+                return false;
+            }
+            if (getClass() != obj.getClass()) {
+                return false;
+            }
+            RouteChangeImpl<?, ?> other = (RouteChangeImpl<?, ?>) obj;
+            if (announcement == null) {
+                if (other.announcement != null) {
+                    return false;
+                }
+            } else if (!announcement.equals(other.announcement)) {
+                return false;
+            }
+            if (removal == null) {
+                if (other.removal != null) {
+                    return false;
+                }
+            } else if (!removal.equals(other.removal)) {
+                return false;
+            }
+            return true;
+        }
+    }
+
+
+
+}
diff --git a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/service/AbstractDataTransaction.java b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/service/AbstractDataTransaction.java
new file mode 100644 (file)
index 0000000..f83a778
--- /dev/null
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.impl.service;
+
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Deprecated
+public abstract class AbstractDataTransaction {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractDataTransaction.class);
+    private static final ListenableFuture<RpcResult<TransactionStatus>> SUCCESS_FUTURE =
+            Futures.immediateFuture(RpcResultBuilder.success(TransactionStatus.COMMITED).build());
+
+    public static ListenableFuture<RpcResult<TransactionStatus>> convertToLegacyCommitFuture(final CheckedFuture<Void,TransactionCommitFailedException> from) {
+        return Futures.transform(from, new AsyncFunction<Void, RpcResult<TransactionStatus>>() {
+            @Override
+            public ListenableFuture<RpcResult<TransactionStatus>> apply(final Void input) {
+                return SUCCESS_FUTURE;
+            }
+        });
+    }
+}
diff --git a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/AbstractLockableDelegator.java b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/AbstractLockableDelegator.java
new file mode 100644 (file)
index 0000000..4d756f1
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.impl.util;
+
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
+
+import org.opendaylight.yangtools.concepts.Delegator;
+
+import com.google.common.base.Preconditions;
+
+public class AbstractLockableDelegator<T> implements Delegator<T> {
+
+    private final ReentrantReadWriteLock delegateLock = new ReentrantReadWriteLock();
+    private final ReadLock delegateReadLock = delegateLock.readLock();
+    private final WriteLock delegateWriteLock = delegateLock.writeLock();
+
+
+    protected Lock getDelegateReadLock() {
+        return delegateReadLock;
+    }
+
+    private T delegate;
+
+    public AbstractLockableDelegator() {
+        // NOOP
+    }
+
+    public AbstractLockableDelegator(T initialDelegate) {
+        delegate = initialDelegate;
+    }
+
+    @Override
+    public T getDelegate() {
+        try {
+            delegateReadLock.lock();
+            return delegate;
+        } finally {
+            delegateReadLock.unlock();
+        }
+    }
+
+    public T retrieveDelegate() {
+        try {
+            delegateReadLock.lock();
+            Preconditions.checkState(delegate != null,"Delegate is null");
+            return delegate;
+        } finally {
+            delegateReadLock.unlock();
+        }
+    }
+
+    /**
+     *
+     * @param newDelegate
+     * @return oldDelegate
+     */
+    public final T changeDelegate(T newDelegate) {
+        try {
+            delegateWriteLock.lock();
+            T oldDelegate = delegate;
+            delegate = newDelegate;
+            onDelegateChanged(oldDelegate, newDelegate);
+            return oldDelegate;
+        } finally {
+            delegateWriteLock.unlock();
+        }
+    }
+
+
+    protected void onDelegateChanged(T oldDelegate, T newDelegate) {
+        // NOOP in abstract calss;
+    }
+}
diff --git a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizationException.java b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizationException.java
new file mode 100644 (file)
index 0000000..daecfdb
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.impl.util.compat;
+
+public class DataNormalizationException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public DataNormalizationException(String message) {
+        super(message);
+    }
+
+    public DataNormalizationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizationOperation.java b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizationOperation.java
new file mode 100644 (file)
index 0000000..d75f438
--- /dev/null
@@ -0,0 +1,643 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.impl.util.compat;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+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.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema;
+
+@Deprecated
+public abstract class DataNormalizationOperation<T extends PathArgument> implements Identifiable<T> {
+
+    private final T identifier;
+    private final Optional<DataSchemaNode> dataSchemaNode;
+
+    @Override
+    public T getIdentifier() {
+        return identifier;
+    };
+
+    protected DataNormalizationOperation(final T identifier, final SchemaNode schema) {
+        super();
+        this.identifier = identifier;
+        if(schema instanceof DataSchemaNode) {
+            this.dataSchemaNode = Optional.of((DataSchemaNode) schema);
+        } else {
+            this.dataSchemaNode = Optional.absent();
+        }
+    }
+
+    public boolean isMixin() {
+        return false;
+    }
+
+
+    public boolean isKeyedEntry() {
+        return false;
+    }
+
+    protected Set<QName> getQNameIdentifiers() {
+        return Collections.singleton(identifier.getNodeType());
+    }
+
+    public abstract DataNormalizationOperation<?> getChild(final PathArgument child) throws DataNormalizationException;
+
+    public abstract DataNormalizationOperation<?> getChild(QName child) throws DataNormalizationException;
+
+
+    public abstract boolean isLeaf();
+
+    public Optional<DataSchemaNode> getDataSchemaNode() {
+        // FIXME
+        return dataSchemaNode;
+    }
+
+    private static abstract class SimpleTypeNormalization<T extends PathArgument> extends DataNormalizationOperation<T> {
+
+        protected SimpleTypeNormalization(final T identifier, final DataSchemaNode potential) {
+            super(identifier,potential);
+        }
+
+        @Override
+        public DataNormalizationOperation<?> getChild(final PathArgument child) {
+            return null;
+        }
+
+        @Override
+        public DataNormalizationOperation<?> getChild(final QName child) {
+            return null;
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+            return null;
+        }
+
+        @Override
+        public boolean isLeaf() {
+            return true;
+        }
+
+    }
+
+    private static final class LeafNormalization extends SimpleTypeNormalization<NodeIdentifier> {
+
+        protected LeafNormalization(final LeafSchemaNode potential) {
+            super(new NodeIdentifier(potential.getQName()),potential);
+        }
+
+    }
+
+    private static final class LeafListEntryNormalization extends SimpleTypeNormalization<NodeWithValue> {
+
+        public LeafListEntryNormalization(final LeafListSchemaNode potential) {
+            super(new NodeWithValue(potential.getQName(), null),potential);
+        }
+
+        @Override
+        public boolean isKeyedEntry() {
+            return true;
+        }
+    }
+
+    private static abstract class CompositeNodeNormalizationOperation<T extends PathArgument> extends
+    DataNormalizationOperation<T> {
+
+        protected CompositeNodeNormalizationOperation(final T identifier, final DataSchemaNode schema) {
+            super(identifier,schema);
+        }
+
+        @Override
+        public boolean isLeaf() {
+            return false;
+        }
+
+
+    }
+
+    private static abstract class DataContainerNormalizationOperation<T extends PathArgument> extends
+    CompositeNodeNormalizationOperation<T> {
+
+        private final DataNodeContainer schema;
+        private final Map<QName, DataNormalizationOperation<?>> byQName;
+        private final Map<PathArgument, DataNormalizationOperation<?>> byArg;
+
+        protected DataContainerNormalizationOperation(final T identifier, final DataNodeContainer schema, final DataSchemaNode node) {
+            super(identifier,node);
+            this.schema = schema;
+            this.byArg = new ConcurrentHashMap<>();
+            this.byQName = new ConcurrentHashMap<>();
+        }
+
+        @Override
+        public DataNormalizationOperation<?> getChild(final PathArgument child) throws DataNormalizationException {
+            DataNormalizationOperation<?> potential = byArg.get(child);
+            if (potential != null) {
+                return potential;
+            }
+            potential = fromLocalSchema(child);
+            return register(potential);
+        }
+
+        private DataNormalizationOperation<?> fromLocalSchema(final PathArgument child) throws DataNormalizationException {
+            if (child instanceof AugmentationIdentifier) {
+                return fromSchemaAndQNameChecked(schema, ((AugmentationIdentifier) child).getPossibleChildNames()
+                        .iterator().next());
+            }
+            return fromSchemaAndQNameChecked(schema, child.getNodeType());
+        }
+
+        @Override
+        public DataNormalizationOperation<?> getChild(final QName child) throws DataNormalizationException {
+            DataNormalizationOperation<?> potential = byQName.get(child);
+            if (potential != null) {
+                return potential;
+            }
+            potential = fromLocalSchemaAndQName(schema, child);
+            return register(potential);
+        }
+
+        protected DataNormalizationOperation<?> fromLocalSchemaAndQName(final DataNodeContainer schema2, final QName child) throws DataNormalizationException {
+            return fromSchemaAndQNameChecked(schema2, child);
+        }
+
+        private DataNormalizationOperation<?> register(final DataNormalizationOperation<?> potential) {
+            if (potential != null) {
+                byArg.put(potential.getIdentifier(), potential);
+                for (final QName qName : potential.getQNameIdentifiers()) {
+                    byQName.put(qName, potential);
+                }
+            }
+            return potential;
+        }
+
+    }
+
+    private static final class ListItemNormalization extends
+    DataContainerNormalizationOperation<NodeIdentifierWithPredicates> {
+
+        private final List<QName> keyDefinition;
+
+        protected ListItemNormalization(final NodeIdentifierWithPredicates identifier, final ListSchemaNode schema) {
+            super(identifier, schema,schema);
+            keyDefinition = schema.getKeyDefinition();
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = Builders
+                    .mapEntryBuilder().withNodeIdentifier((NodeIdentifierWithPredicates) currentArg);
+            for (final Entry<QName, Object> keyValue : ((NodeIdentifierWithPredicates) currentArg).getKeyValues().entrySet()) {
+                builder.addChild(Builders.leafBuilder()
+                        //
+                        .withNodeIdentifier(new NodeIdentifier(keyValue.getKey())).withValue(keyValue.getValue())
+                        .build());
+            }
+            return builder.build();
+        }
+
+
+        @Override
+        public boolean isKeyedEntry() {
+            return true;
+        }
+    }
+
+    private static final class UnkeyedListItemNormalization extends DataContainerNormalizationOperation<NodeIdentifier> {
+
+        protected UnkeyedListItemNormalization(final ListSchemaNode schema) {
+            super(new NodeIdentifier(schema.getQName()), schema,schema);
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+            return Builders.unkeyedListEntryBuilder().withNodeIdentifier((NodeIdentifier) currentArg).build();
+        }
+
+    }
+
+    private static final class ContainerNormalization extends DataContainerNormalizationOperation<NodeIdentifier> {
+
+        protected ContainerNormalization(final ContainerSchemaNode schema) {
+            super(new NodeIdentifier(schema.getQName()),schema, schema);
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+            return Builders.containerBuilder().withNodeIdentifier((NodeIdentifier) currentArg).build();
+        }
+
+    }
+
+    private static abstract class MixinNormalizationOp<T extends PathArgument> extends
+    CompositeNodeNormalizationOperation<T> {
+
+        protected MixinNormalizationOp(final T identifier, final DataSchemaNode schema) {
+            super(identifier,schema);
+        }
+
+        @Override
+        public final boolean isMixin() {
+            return true;
+        }
+
+    }
+
+
+    private static final class OrderedLeafListMixinNormalization extends UnorderedLeafListMixinNormalization {
+
+
+        public OrderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
+            super(potential);
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+            return Builders.orderedLeafSetBuilder().withNodeIdentifier(getIdentifier()).build();
+        }
+    }
+
+    private static class UnorderedLeafListMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
+
+        private final DataNormalizationOperation<?> innerOp;
+
+        public UnorderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
+            super(new NodeIdentifier(potential.getQName()),potential);
+            innerOp = new LeafListEntryNormalization(potential);
+        }
+        @Override
+        public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+            return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier()).build();
+        }
+
+        @Override
+        public DataNormalizationOperation<?> getChild(final PathArgument child) {
+            if (child instanceof NodeWithValue) {
+                return innerOp;
+            }
+            return null;
+        }
+
+        @Override
+        public DataNormalizationOperation<?> getChild(final QName child) {
+            if (getIdentifier().getNodeType().equals(child)) {
+                return innerOp;
+            }
+            return null;
+        }
+    }
+
+    private static final class AugmentationNormalization extends DataContainerNormalizationOperation<AugmentationIdentifier> {
+
+        public AugmentationNormalization(final AugmentationSchema augmentation, final DataNodeContainer schema) {
+            //super();
+            super(augmentationIdentifierFrom(augmentation), augmentationProxy(augmentation,schema),null);
+        }
+
+        @Override
+        public boolean isMixin() {
+            return true;
+        }
+
+
+
+        @Override
+        protected DataNormalizationOperation<?> fromLocalSchemaAndQName(final DataNodeContainer schema, final QName child)
+                throws DataNormalizationException {
+            final Optional<DataSchemaNode> potential = findChildSchemaNode(schema, child);
+            if (!potential.isPresent()) {
+                return null;
+            }
+
+            final DataSchemaNode result = potential.get();
+            // We try to look up if this node was added by augmentation
+            if ((schema instanceof DataSchemaNode) && result.isAugmenting()) {
+                return fromAugmentation(schema, (AugmentationTarget) schema, result);
+            }
+            return fromDataSchemaNode(result);
+        }
+
+        @Override
+        protected Set<QName> getQNameIdentifiers() {
+            return getIdentifier().getPossibleChildNames();
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+            return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier()).build();
+        }
+
+    }
+
+    private static class UnorderedMapMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
+
+        private final ListItemNormalization innerNode;
+
+        public UnorderedMapMixinNormalization(final ListSchemaNode list) {
+            super(new NodeIdentifier(list.getQName()),list);
+            this.innerNode = new ListItemNormalization(new NodeIdentifierWithPredicates(list.getQName(),
+                    Collections.<QName, Object> emptyMap()), list);
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+            return Builders.mapBuilder().withNodeIdentifier(getIdentifier()).build();
+        }
+
+        @Override
+        public DataNormalizationOperation<?> getChild(final PathArgument child) {
+            if (child.getNodeType().equals(getIdentifier().getNodeType())) {
+                return innerNode;
+            }
+            return null;
+        }
+
+        @Override
+        public DataNormalizationOperation<?> getChild(final QName child) {
+            if (getIdentifier().getNodeType().equals(child)) {
+                return innerNode;
+            }
+            return null;
+        }
+
+    }
+
+
+    private static class UnkeyedListMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
+
+        private final UnkeyedListItemNormalization innerNode;
+
+        public UnkeyedListMixinNormalization(final ListSchemaNode list) {
+            super(new NodeIdentifier(list.getQName()),list);
+            this.innerNode = new UnkeyedListItemNormalization(list);
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+            return Builders.unkeyedListBuilder().withNodeIdentifier(getIdentifier()).build();
+        }
+
+        @Override
+        public DataNormalizationOperation<?> getChild(final PathArgument child) {
+            if (child.getNodeType().equals(getIdentifier().getNodeType())) {
+                return innerNode;
+            }
+            return null;
+        }
+
+        @Override
+        public DataNormalizationOperation<?> getChild(final QName child) {
+            if (getIdentifier().getNodeType().equals(child)) {
+                return innerNode;
+            }
+            return null;
+        }
+
+    }
+
+    private static final class OrderedMapMixinNormalization extends UnorderedMapMixinNormalization {
+
+        public OrderedMapMixinNormalization(final ListSchemaNode list) {
+            super(list);
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+            return Builders.orderedMapBuilder().withNodeIdentifier(getIdentifier()).build();
+        }
+
+    }
+
+    private static class ChoiceNodeNormalization extends MixinNormalizationOp<NodeIdentifier> {
+
+        private final ImmutableMap<QName, DataNormalizationOperation<?>> byQName;
+        private final ImmutableMap<PathArgument, DataNormalizationOperation<?>> byArg;
+
+        protected ChoiceNodeNormalization(final ChoiceSchemaNode schema) {
+            super(new NodeIdentifier(schema.getQName()),schema);
+            final ImmutableMap.Builder<QName, DataNormalizationOperation<?>> byQNameBuilder = ImmutableMap.builder();
+            final ImmutableMap.Builder<PathArgument, DataNormalizationOperation<?>> byArgBuilder = ImmutableMap.builder();
+
+            for (final ChoiceCaseNode caze : schema.getCases()) {
+                for (final DataSchemaNode cazeChild : caze.getChildNodes()) {
+                    final DataNormalizationOperation<?> childOp = fromDataSchemaNode(cazeChild);
+                    byArgBuilder.put(childOp.getIdentifier(), childOp);
+                    for (final QName qname : childOp.getQNameIdentifiers()) {
+                        byQNameBuilder.put(qname, childOp);
+                    }
+                }
+            }
+            byQName = byQNameBuilder.build();
+            byArg = byArgBuilder.build();
+        }
+
+        @Override
+        public DataNormalizationOperation<?> getChild(final PathArgument child) {
+            return byArg.get(child);
+        }
+
+        @Override
+        public DataNormalizationOperation<?> getChild(final QName child) {
+            return byQName.get(child);
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+            return Builders.choiceBuilder().withNodeIdentifier(getIdentifier()).build();
+        }
+    }
+
+    private static class AnyXmlNormalization extends DataNormalizationOperation<NodeIdentifier> {
+
+        protected AnyXmlNormalization( final AnyXmlSchemaNode schema) {
+            super( new NodeIdentifier(schema.getQName()), schema);
+        }
+
+        @Override
+        public DataNormalizationOperation<?> getChild( final PathArgument child ) throws DataNormalizationException {
+            return null;
+        }
+
+        @Override
+        public DataNormalizationOperation<?> getChild( final QName child ) throws DataNormalizationException {
+            return null;
+        }
+
+        @Override
+        public boolean isLeaf() {
+            return false;
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault( final PathArgument currentArg ) {
+            return null;
+        }
+    }
+
+    private static final Optional<DataSchemaNode> findChildSchemaNode(final DataNodeContainer parent,final QName child) {
+        DataSchemaNode potential = parent.getDataChildByName(child);
+        if (potential == null) {
+            final Iterable<ChoiceSchemaNode> choices = FluentIterable.from(parent.getChildNodes()).filter(ChoiceSchemaNode.class);
+            potential = findChoice(choices, child);
+        }
+        return Optional.fromNullable(potential);
+    }
+
+    private static DataNormalizationOperation<?> fromSchemaAndQNameChecked(final DataNodeContainer schema,
+            final QName child) throws DataNormalizationException {
+
+        final Optional<DataSchemaNode> potential = findChildSchemaNode(schema, child);
+        if (!potential.isPresent()) {
+            throw new DataNormalizationException(String.format("Supplied QName %s is not valid according to schema %s, potential children nodes: %s", child, schema,schema.getChildNodes()));
+        }
+
+        final DataSchemaNode result = potential.get();
+        // We try to look up if this node was added by augmentation
+        if ((schema instanceof DataSchemaNode) && result.isAugmenting()) {
+            return fromAugmentation(schema, (AugmentationTarget) schema, result);
+        }
+        return fromDataSchemaNode(result);
+    }
+
+    private static ChoiceSchemaNode findChoice(final Iterable<ChoiceSchemaNode> choices, final QName child) {
+        ChoiceSchemaNode foundChoice = null;
+        choiceLoop: for (final ChoiceSchemaNode choice : choices) {
+            for (final ChoiceCaseNode caze : choice.getCases()) {
+                if (findChildSchemaNode(caze, child).isPresent()) {
+                    foundChoice = choice;
+                    break choiceLoop;
+                }
+            }
+        }
+        return foundChoice;
+    }
+
+    public static AugmentationIdentifier augmentationIdentifierFrom(final AugmentationSchema augmentation) {
+        final ImmutableSet.Builder<QName> potentialChildren = ImmutableSet.builder();
+        for (final DataSchemaNode child : augmentation.getChildNodes()) {
+            potentialChildren.add(child.getQName());
+        }
+        return new AugmentationIdentifier(potentialChildren.build());
+    }
+
+    private static DataNodeContainer augmentationProxy(final AugmentationSchema augmentation, final DataNodeContainer schema) {
+        final Set<DataSchemaNode> children = new HashSet<>();
+        for (final DataSchemaNode augNode : augmentation.getChildNodes()) {
+            children.add(schema.getDataChildByName(augNode.getQName()));
+        }
+        return new EffectiveAugmentationSchema(augmentation, children);
+    }
+
+    /**
+     * Returns a DataNormalizationOperation for provided child node
+     *
+     * If supplied child is added by Augmentation this operation returns
+     * a DataNormalizationOperation for augmentation,
+     * otherwise returns a DataNormalizationOperation for child as
+     * call for {@link #fromDataSchemaNode(DataSchemaNode)}.
+     *
+     *
+     * @param parent
+     * @param parentAug
+     * @param child
+     * @return
+     */
+    private static DataNormalizationOperation<?> fromAugmentation(final DataNodeContainer parent,
+            final AugmentationTarget parentAug, final DataSchemaNode child) {
+        AugmentationSchema augmentation = null;
+        for (final AugmentationSchema aug : parentAug.getAvailableAugmentations()) {
+            final DataSchemaNode potential = aug.getDataChildByName(child.getQName());
+            if (potential != null) {
+                augmentation = aug;
+                break;
+            }
+
+        }
+        if (augmentation != null) {
+            return new AugmentationNormalization(augmentation, parent);
+        } else {
+            return fromDataSchemaNode(child);
+        }
+    }
+
+    public static DataNormalizationOperation<?> fromDataSchemaNode(final DataSchemaNode potential) {
+        if (potential instanceof ContainerSchemaNode) {
+            return new ContainerNormalization((ContainerSchemaNode) potential);
+        } else if (potential instanceof ListSchemaNode) {
+
+            return fromListSchemaNode((ListSchemaNode) potential);
+        } else if (potential instanceof LeafSchemaNode) {
+            return new LeafNormalization((LeafSchemaNode) potential);
+        } else if (potential instanceof ChoiceSchemaNode) {
+            return new ChoiceNodeNormalization((ChoiceSchemaNode) potential);
+        } else if (potential instanceof LeafListSchemaNode) {
+            return fromLeafListSchemaNode((LeafListSchemaNode) potential);
+        } else if (potential instanceof AnyXmlSchemaNode) {
+            return new AnyXmlNormalization( (AnyXmlSchemaNode) potential);
+        }
+        return null;
+    }
+
+    private static DataNormalizationOperation<?> fromListSchemaNode(final ListSchemaNode potential) {
+        final List<QName> keyDefinition = potential.getKeyDefinition();
+        if(keyDefinition == null || keyDefinition.isEmpty()) {
+            return new UnkeyedListMixinNormalization(potential);
+        }
+        if(potential.isUserOrdered()) {
+            return new OrderedMapMixinNormalization(potential);
+        }
+        return new UnorderedMapMixinNormalization(potential);
+    }
+
+    private static DataNormalizationOperation<?> fromLeafListSchemaNode(final LeafListSchemaNode potential) {
+        if(potential.isUserOrdered()) {
+            return new OrderedLeafListMixinNormalization(potential);
+        }
+        return new UnorderedLeafListMixinNormalization(potential);
+    }
+
+
+    public static DataNormalizationOperation<?> from(final SchemaContext ctx) {
+        return new ContainerNormalization(ctx);
+    }
+
+    public abstract NormalizedNode<?, ?> createDefault(PathArgument currentArg);
+}
diff --git a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizer.java b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizer.java
new file mode 100644 (file)
index 0000000..9876a57
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.impl.util.compat;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Iterator;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * @deprecated This class provides compatibility between XML semantics
+ * and {@link org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree}
+ */
+@Deprecated
+public class DataNormalizer {
+
+    private final DataNormalizationOperation<?> operation;
+
+    public DataNormalizer(final SchemaContext ctx) {
+        operation = DataNormalizationOperation.from(ctx);
+    }
+
+    public YangInstanceIdentifier toNormalized(final YangInstanceIdentifier legacy) {
+        ImmutableList.Builder<PathArgument> normalizedArgs = ImmutableList.builder();
+
+        DataNormalizationOperation<?> currentOp = operation;
+        Iterator<PathArgument> arguments = legacy.getPathArguments().iterator();
+
+        try {
+            while (arguments.hasNext()) {
+                PathArgument legacyArg = arguments.next();
+                currentOp = currentOp.getChild(legacyArg);
+                checkArgument(currentOp != null,
+                        "Legacy Instance Identifier %s is not correct. Normalized Instance Identifier so far %s",
+                        legacy, normalizedArgs.build());
+                while (currentOp.isMixin()) {
+                    normalizedArgs.add(currentOp.getIdentifier());
+                    currentOp = currentOp.getChild(legacyArg.getNodeType());
+                }
+                normalizedArgs.add(legacyArg);
+            }
+        } catch (DataNormalizationException e) {
+            throw new IllegalArgumentException(String.format("Failed to normalize path %s", legacy), e);
+        }
+
+        return YangInstanceIdentifier.create(normalizedArgs.build());
+    }
+
+    public DataNormalizationOperation<?> getOperation(final YangInstanceIdentifier legacy) throws DataNormalizationException {
+        DataNormalizationOperation<?> currentOp = operation;
+        Iterator<PathArgument> arguments = legacy.getPathArguments().iterator();
+
+        while (arguments.hasNext()) {
+            currentOp = currentOp.getChild(arguments.next());
+        }
+        return currentOp;
+    }
+
+    public YangInstanceIdentifier toLegacy(final YangInstanceIdentifier normalized) throws DataNormalizationException {
+        ImmutableList.Builder<PathArgument> legacyArgs = ImmutableList.builder();
+        DataNormalizationOperation<?> currentOp = operation;
+        for (PathArgument normalizedArg : normalized.getPathArguments()) {
+            currentOp = currentOp.getChild(normalizedArg);
+            if (!currentOp.isMixin()) {
+                legacyArgs.add(normalizedArg);
+            }
+        }
+        return YangInstanceIdentifier.create(legacyArgs.build());
+    }
+
+    public DataNormalizationOperation<?> getRootOperation() {
+        return operation;
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataSchemaContainerProxy.java b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataSchemaContainerProxy.java
new file mode 100644 (file)
index 0000000..d243c88
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.impl.util.compat;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+
+class DataSchemaContainerProxy implements DataNodeContainer {
+
+    private final Set<DataSchemaNode> realChildSchemas;
+    private final Map<QName, DataSchemaNode> mappedChildSchemas;
+
+    public DataSchemaContainerProxy(final Set<DataSchemaNode> realChildSchema) {
+        realChildSchemas = realChildSchema;
+        mappedChildSchemas = new HashMap<QName, DataSchemaNode>();
+        for(DataSchemaNode schema : realChildSchemas) {
+            mappedChildSchemas.put(schema.getQName(),schema);
+        }
+    }
+
+    @Override
+    public DataSchemaNode getDataChildByName(final QName name) {
+        return mappedChildSchemas.get(name);
+    }
+
+    @Override
+    public Set<DataSchemaNode> getChildNodes() {
+        return realChildSchemas;
+    }
+
+    @Override
+    public DataSchemaNode getDataChildByName(final String arg0) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Set<GroupingDefinition> getGroupings() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public Set<TypeDefinition<?>> getTypeDefinitions() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public Set<UsesNode> getUses() {
+        return Collections.emptySet();
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-common-impl/src/test/resources/normalization-test.yang b/opendaylight/md-sal/sal-common-impl/src/test/resources/normalization-test.yang
new file mode 100644 (file)
index 0000000..6df5306
--- /dev/null
@@ -0,0 +1,74 @@
+module normalization-test {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:normalization:test";
+    prefix "norm-test";
+
+    revision "2014-03-13" {
+        description "Initial revision.";
+    }
+
+    grouping outer-grouping {
+    }
+
+    container test {
+        list outer-list {
+            key id;
+            leaf id {
+                type uint16;
+            }
+            choice outer-choice {
+                case one {
+                    leaf one {
+                        type string;
+                    }
+                }
+                case two-three {
+                    leaf two {
+                        type string;
+                    }
+                    leaf three {
+                        type string;
+                    }
+               }
+           }
+           list inner-list {
+                key name;
+                ordered-by user;
+
+                leaf name {
+                    type string;
+                }
+                leaf value {
+                    type string;
+                }
+            }
+        }
+
+        list unkeyed-list {
+            leaf name {
+                type string;
+            }
+        }
+
+        leaf-list unordered-leaf-list {
+            type string;
+        }
+
+        leaf-list ordered-leaf-list {
+            ordered-by user;
+            type string;
+        }
+
+        container outer-container {
+        }
+
+        anyxml any-xml-data;
+    }
+
+    augment /norm-test:test/norm-test:outer-container {
+
+        leaf augmented-leaf {
+           type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-common-util/pom.xml b/opendaylight/md-sal/sal-common-util/pom.xml
new file mode 100644 (file)
index 0000000..2caad52
--- /dev/null
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>sal-parent</artifactId>
+    <version>1.3.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>sal-common-util</artifactId>
+
+  <packaging>bundle</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-common-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>concepts</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-common</artifactId>
+    </dependency>
+
+  </dependencies>
+  <scm>
+    <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+    <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+    <tag>HEAD</tag>
+    <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+  </scm>
+</project>
diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/AbstractMXBean.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/AbstractMXBean.java
new file mode 100644 (file)
index 0000000..a2db29d
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.common.util.jmx;
+
+import java.lang.management.ManagementFactory;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Abstract base for an MXBean implementation class.
+ * <p>
+ * This class is not intended for use outside of MD-SAL and its part of private
+ * implementation (still exported as public to be reused across MD-SAL implementation
+ * components) and may be removed in subsequent
+ * releases.
+ *
+ * @author Thomas Pantelis
+ */
+@Beta
+public abstract class AbstractMXBean {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractMXBean.class);
+
+    public static String BASE_JMX_PREFIX = "org.opendaylight.controller:";
+
+    private final MBeanServer server = ManagementFactory.getPlatformMBeanServer();
+
+    private final String mBeanName;
+    private final String mBeanType;
+    private final String mBeanCategory;
+
+    /**
+     * Constructor.
+     *
+     * @param mBeanName Used as the <code>name</code> property in the bean's ObjectName.
+     * @param mBeanType Used as the <code>type</code> property in the bean's ObjectName.
+     * @param mBeanCategory Used as the <code>Category</code> property in the bean's ObjectName.
+     */
+    protected AbstractMXBean(@Nonnull String mBeanName, @Nonnull String mBeanType,
+            @Nullable String mBeanCategory) {
+        this.mBeanName = mBeanName;
+        this.mBeanType = mBeanType;
+        this.mBeanCategory = mBeanCategory;
+    }
+
+    private ObjectName getMBeanObjectName() throws MalformedObjectNameException {
+        StringBuilder builder = new StringBuilder(BASE_JMX_PREFIX)
+                .append("type=").append(getMBeanType());
+
+        if(getMBeanCategory() != null) {
+            builder.append(",Category=").append(getMBeanCategory());
+        }
+
+        builder.append(",name=").append(getMBeanName());
+        return new ObjectName(builder.toString());
+    }
+
+    /**
+     * Registers this bean with the platform MBean server with the domain defined by
+     * {@link #BASE_JMX_PREFIX}.
+     *
+     * @return true is successfully registered, false otherwise.
+     */
+    public boolean registerMBean() {
+        boolean registered = false;
+        try {
+            // Object to identify MBean
+            final ObjectName mbeanName = this.getMBeanObjectName();
+
+            LOG.debug("Register MBean {}", mbeanName);
+
+            // unregistered if already registered
+            if(server.isRegistered(mbeanName)) {
+
+                LOG.debug("MBean {} found to be already registered", mbeanName);
+
+                try {
+                    unregisterMBean(mbeanName);
+                } catch(Exception e) {
+
+                    LOG.warn("unregister mbean {} resulted in exception {} ", mbeanName, e);
+                }
+            }
+            server.registerMBean(this, mbeanName);
+            registered = true;
+
+            LOG.debug("MBean {} registered successfully", mbeanName.getCanonicalName());
+        } catch(Exception e) {
+
+            LOG.error("registration failed {}", e);
+
+        }
+        return registered;
+    }
+
+    /**
+     * Unregisters this bean with the platform MBean server.
+     *
+     * @return true is successfully unregistered, false otherwise.
+     */
+    public boolean unregisterMBean() {
+        boolean unregister = false;
+        try {
+            ObjectName mbeanName = this.getMBeanObjectName();
+            unregisterMBean(mbeanName);
+            unregister = true;
+        } catch(Exception e) {
+
+            LOG.error("Failed when unregistering MBean {}", e);
+        }
+
+        return unregister;
+    }
+
+    private void unregisterMBean(ObjectName mbeanName) throws MBeanRegistrationException,
+            InstanceNotFoundException {
+        server.unregisterMBean(mbeanName);
+    }
+
+    /**
+     * Returns the <code>name</code> property of the bean's ObjectName.
+     */
+    public String getMBeanName() {
+        return mBeanName;
+    }
+
+    /**
+     * Returns the <code>type</code> property of the bean's ObjectName.
+     */
+    public String getMBeanType() {
+        return mBeanType;
+    }
+
+    /**
+     * Returns the <code>Category</code> property of the bean's ObjectName.
+     */
+    public String getMBeanCategory() {
+        return mBeanCategory;
+    }
+}
diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/QueuedNotificationManagerMXBean.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/QueuedNotificationManagerMXBean.java
new file mode 100644 (file)
index 0000000..9646adc
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.common.util.jmx;
+
+import java.util.List;
+
+import org.opendaylight.yangtools.util.concurrent.ListenerNotificationQueueStats;
+
+/**
+ * MXBean interface for {@link QueuedNotificationManager} statistic metrics.
+ *
+ * @author Thomas Pantelis
+ */
+public interface QueuedNotificationManagerMXBean {
+
+    /**
+     * Returns a list of stat instances for each current listener notification task in progress.
+     */
+    List<ListenerNotificationQueueStats> getCurrentListenerQueueStats();
+
+    /**
+     * Returns the configured maximum listener queue size.
+     */
+    int getMaxListenerQueueSize();
+}
diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/QueuedNotificationManagerMXBeanImpl.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/QueuedNotificationManagerMXBeanImpl.java
new file mode 100644 (file)
index 0000000..e6148fc
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.common.util.jmx;
+
+import java.util.List;
+
+import org.opendaylight.yangtools.util.concurrent.ListenerNotificationQueueStats;
+import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Implementation of the QueuedNotificationManagerMXBean interface.
+ *
+ * <p>
+ * This class is not intended for use outside of MD-SAL and its part of private
+ * implementation (still exported as public to be reused across MD-SAL implementation
+ * components) and may be removed in subsequent
+ * releases.
+ *
+ * @author Thomas Pantelis
+ */
+public class QueuedNotificationManagerMXBeanImpl extends AbstractMXBean
+                                                 implements QueuedNotificationManagerMXBean {
+
+    private final QueuedNotificationManager<?,?> manager;
+
+    public QueuedNotificationManagerMXBeanImpl( QueuedNotificationManager<?,?> manager,
+            String mBeanName, String mBeanType, String mBeanCategory ) {
+        super(mBeanName, mBeanType, mBeanCategory);
+        this.manager = Preconditions.checkNotNull( manager );
+    }
+
+    @Override
+    public List<ListenerNotificationQueueStats> getCurrentListenerQueueStats() {
+        return manager.getListenerNotificationQueueStats();
+    }
+
+    @Override
+    public int getMaxListenerQueueSize() {
+        return manager.getMaxQueueCapacity();
+    }
+
+    public QueuedNotificationManagerStats toQueuedNotificationManagerStats() {
+        return new QueuedNotificationManagerStats( getMaxListenerQueueSize(),
+                getCurrentListenerQueueStats() );
+    }
+}
diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/QueuedNotificationManagerStats.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/QueuedNotificationManagerStats.java
new file mode 100644 (file)
index 0000000..c6e5006
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.common.util.jmx;
+
+import java.beans.ConstructorProperties;
+import java.util.List;
+
+import org.opendaylight.yangtools.util.concurrent.ListenerNotificationQueueStats;
+
+/**
+ * A bean class that holds various QueuedNotificationManager statistic metrics. This class is
+ * suitable for mapping to the MXBean CompositeDataSupport type.
+ *
+ * <p>
+ * This class is not intended for use outside of MD-SAL and its part of private
+ * implementation (still exported as public to be reused across MD-SAL implementation
+ * components) and may be removed in subsequent
+ * releases.
+ * @author Thomas Pantelis
+ * @see QueuedNotificationManagerMXBeanImpl
+ */
+public class QueuedNotificationManagerStats {
+
+    private final int maxListenerQueueSize;
+    private final List<ListenerNotificationQueueStats> currentListenerQueueStats;
+
+    @ConstructorProperties({"maxListenerQueueSize","currentListenerQueueStats"})
+    public QueuedNotificationManagerStats( int maxListenerQueueSize,
+            List<ListenerNotificationQueueStats> currentListenerQueueStats ) {
+        super();
+        this.maxListenerQueueSize = maxListenerQueueSize;
+        this.currentListenerQueueStats = currentListenerQueueStats;
+    }
+
+    public List<ListenerNotificationQueueStats> getCurrentListenerQueueStats() {
+        return currentListenerQueueStats;
+    }
+
+    public int getMaxListenerQueueSize() {
+        return maxListenerQueueSize;
+    }
+}
diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStats.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStats.java
new file mode 100644 (file)
index 0000000..0a766c0
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.common.util.jmx;
+
+import java.beans.ConstructorProperties;
+
+/**
+ * A bean class that holds various thread executor statistic metrics. This class is suitable for
+ * mapping to the MXBean CompositeDataSupport type;
+ *
+ * @author Thomas Pantelis
+ * @see ThreadExecutorStatsMXBeanImpl
+ */
+public class ThreadExecutorStats {
+
+    private final long activeThreadCount;
+    private final long completedTaskCount;
+    private final long currentQueueSize;
+    private final long maxThreadPoolSize;
+    private final long totalTaskCount;
+    private final long largestThreadPoolSize;
+    private final long maxQueueSize;
+    private final long currentThreadPoolSize;
+
+    // The following fields are defined as Long because they may be null if we can't a value
+    // from the underlying executor.
+    private final Long largestQueueSize;
+    private final Long rejectedTaskCount;
+
+    @ConstructorProperties({"activeThreadCount","currentThreadPoolSize","largestThreadPoolSize",
+        "maxThreadPoolSize","currentQueueSize","largestQueueSize","maxQueueSize",
+        "completedTaskCount","totalTaskCount","rejectedTaskCount"})
+    public ThreadExecutorStats(long activeThreadCount, long currentThreadPoolSize,
+            long largestThreadPoolSize, long maxThreadPoolSize, long currentQueueSize,
+            Long largestQueueSize, long maxQueueSize, long completedTaskCount,
+            long totalTaskCount, Long rejectedTaskCount) {
+        this.activeThreadCount = activeThreadCount;
+        this.currentThreadPoolSize = currentThreadPoolSize;
+        this.largestQueueSize = largestQueueSize;
+        this.largestThreadPoolSize = largestThreadPoolSize;
+        this.maxThreadPoolSize = maxThreadPoolSize;
+        this.currentQueueSize = currentQueueSize;
+        this.maxQueueSize = maxQueueSize;
+        this.completedTaskCount = completedTaskCount;
+        this.totalTaskCount = totalTaskCount;
+        this.rejectedTaskCount = rejectedTaskCount;
+    }
+
+    public long getActiveThreadCount() {
+        return activeThreadCount;
+    }
+
+    public long getCompletedTaskCount() {
+        return completedTaskCount;
+    }
+
+    public Long getRejectedTaskCount() {
+        return rejectedTaskCount;
+    }
+
+    public long getCurrentQueueSize() {
+        return currentQueueSize;
+    }
+
+    public Long getLargestQueueSize() {
+        return largestQueueSize;
+    }
+
+    public long getMaxThreadPoolSize() {
+        return maxThreadPoolSize;
+    }
+
+    public long getTotalTaskCount() {
+        return totalTaskCount;
+    }
+
+    public long getLargestThreadPoolSize() {
+        return largestThreadPoolSize;
+    }
+
+    public long getMaxQueueSize() {
+        return maxQueueSize;
+    }
+
+    public long getCurrentThreadPoolSize() {
+        return currentThreadPoolSize;
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStatsMXBean.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStatsMXBean.java
new file mode 100644 (file)
index 0000000..d30bf88
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.common.util.jmx;
+
+/**
+ * MXBean interface for thread executor statistic metrics.
+ *
+ * @author Thomas Pantelis
+ */
+public interface ThreadExecutorStatsMXBean {
+
+    /**
+     * Returns the current thread pool size.
+     */
+    long getCurrentThreadPoolSize();
+
+    /**
+     * Returns the largest thread pool size.
+     */
+    long getLargestThreadPoolSize();
+
+    /**
+     * Returns the maximum thread pool size.
+     */
+    long getMaxThreadPoolSize();
+
+    /**
+     * Returns the current queue size.
+     */
+    long getCurrentQueueSize();
+
+    /**
+     * Returns the largest queue size, if available.
+     */
+    Long getLargestQueueSize();
+
+    /**
+     * Returns the maximum queue size.
+     */
+    long getMaxQueueSize();
+
+    /**
+     * Returns the active thread count.
+     */
+    long getActiveThreadCount();
+
+    /**
+     * Returns the completed task count.
+     */
+    long getCompletedTaskCount();
+
+    /**
+     * Returns the total task count.
+     */
+    long getTotalTaskCount();
+
+    /**
+     * Returns the rejected task count, if available.
+     */
+    Long getRejectedTaskCount();
+}
diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStatsMXBeanImpl.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStatsMXBeanImpl.java
new file mode 100644 (file)
index 0000000..3de49ae
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.common.util.jmx;
+
+import com.google.common.base.Preconditions;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadPoolExecutor;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.util.concurrent.CountingRejectedExecutionHandler;
+import org.opendaylight.yangtools.util.concurrent.TrackingLinkedBlockingQueue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * MXBean implementation of the ThreadExecutorStatsMXBean interface that retrieves statistics
+ * from a backing {@link java.util.concurrent.ExecutorService}.
+ *
+ * @author Thomas Pantelis
+ */
+public class ThreadExecutorStatsMXBeanImpl extends AbstractMXBean
+                                           implements ThreadExecutorStatsMXBean {
+    private static final Logger LOG = LoggerFactory.getLogger(ThreadExecutorStatsMXBeanImpl.class);
+    private final ThreadPoolExecutor executor;
+
+    /**
+     * Constructs an instance for the given {@link Executor}.
+     *
+     * @param executor the backing {@link Executor}
+     * @param mBeanName Used as the <code>name</code> property in the bean's ObjectName.
+     * @param mBeanType Used as the <code>type</code> property in the bean's ObjectName.
+     * @param mBeanCategory Used as the <code>Category</code> property in the bean's ObjectName.
+     */
+    public ThreadExecutorStatsMXBeanImpl(final ThreadPoolExecutor executor, final String mBeanName,
+            final String mBeanType, @Nullable final String mBeanCategory) {
+        super(mBeanName, mBeanType, mBeanCategory);
+        this.executor = Preconditions.checkNotNull(executor);
+    }
+
+    private static ThreadExecutorStatsMXBeanImpl createInternal(final Executor executor,
+            final String mBeanName, final String mBeanType, final String mBeanCategory) {
+        if (executor instanceof ThreadPoolExecutor) {
+            final ThreadExecutorStatsMXBeanImpl ret = new ThreadExecutorStatsMXBeanImpl(
+                    (ThreadPoolExecutor) executor, mBeanName, mBeanType, mBeanCategory);
+            return ret;
+        }
+
+        LOG.info("Executor {} is not supported", executor);
+        return null;
+    }
+
+    /**
+     * Creates a new bean if the backing executor is a ThreadPoolExecutor and registers it.
+     *
+     * @param executor the backing {@link Executor}
+     * @param mBeanName Used as the <code>name</code> property in the bean's ObjectName.
+     * @param mBeanType Used as the <code>type</code> property in the bean's ObjectName.
+     * @param mBeanCategory Used as the <code>Category</code> property in the bean's ObjectName.
+     * @return a registered ThreadExecutorStatsMXBeanImpl instance if the backing executor
+     *         is a ThreadPoolExecutor, otherwise null.
+     */
+    public static ThreadExecutorStatsMXBeanImpl create(final Executor executor, final String mBeanName,
+            final String mBeanType, @Nullable final String mBeanCategory) {
+        ThreadExecutorStatsMXBeanImpl ret = createInternal(executor, mBeanName, mBeanType, mBeanCategory);
+        if(ret != null) {
+            ret.registerMBean();
+        }
+
+        return ret;
+    }
+
+    /**
+     * Creates a new bean if the backing executor is a ThreadPoolExecutor.
+     *
+     * @param executor the backing {@link Executor}
+     * @return a ThreadExecutorStatsMXBeanImpl instance if the backing executor
+     *         is a ThreadPoolExecutor, otherwise null.
+     */
+    public static ThreadExecutorStatsMXBeanImpl create(final Executor executor) {
+        return createInternal(executor, "", "", null);
+    }
+
+    @Override
+    public long getCurrentThreadPoolSize() {
+        return executor.getPoolSize();
+    }
+
+    @Override
+    public long getLargestThreadPoolSize() {
+        return  executor.getLargestPoolSize();
+    }
+
+    @Override
+    public long getMaxThreadPoolSize() {
+        return executor.getMaximumPoolSize();
+    }
+
+    @Override
+    public long getCurrentQueueSize() {
+        return executor.getQueue().size();
+    }
+
+    @Override
+    public Long getLargestQueueSize() {
+        BlockingQueue<Runnable> queue = executor.getQueue();
+        if(queue instanceof TrackingLinkedBlockingQueue) {
+            return Long.valueOf(((TrackingLinkedBlockingQueue<?>)queue).getLargestQueueSize());
+        }
+
+        return null;
+    }
+
+    @Override
+    public long getMaxQueueSize() {
+        long queueSize = executor.getQueue().size();
+        return executor.getQueue().remainingCapacity() + queueSize;
+    }
+
+    @Override
+    public long getActiveThreadCount() {
+        return executor.getActiveCount();
+    }
+
+    @Override
+    public long getCompletedTaskCount() {
+        return  executor.getCompletedTaskCount();
+    }
+
+    @Override
+    public long getTotalTaskCount() {
+        return executor.getTaskCount();
+    }
+
+    @Override
+    public Long getRejectedTaskCount() {
+        RejectedExecutionHandler rejectedHandler = executor.getRejectedExecutionHandler();
+        if(rejectedHandler instanceof CountingRejectedExecutionHandler) {
+            return Long.valueOf(((CountingRejectedExecutionHandler)rejectedHandler)
+                                                                     .getRejectedTaskCount());
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns a {@link ThreadExecutorStats} instance containing a snapshot of the statistic
+     * metrics.
+     */
+    public ThreadExecutorStats toThreadExecutorStats() {
+        return new ThreadExecutorStats(getActiveThreadCount(), getCurrentThreadPoolSize(),
+                getLargestThreadPoolSize(), getMaxThreadPoolSize(), getCurrentQueueSize(),
+                getLargestQueueSize(), getMaxQueueSize(), getCompletedTaskCount(),
+                getTotalTaskCount(), getRejectedTaskCount());
+    }
+}
diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Arguments.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Arguments.java
new file mode 100644 (file)
index 0000000..17ce5df
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.common.util;
+
+public final class Arguments {
+
+    private Arguments() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    /**
+     * Checks if value is instance of provided class
+     *
+     *
+     * @param value Value to check
+     * @param type Type to check
+     * @return Reference which was checked
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T checkInstanceOf(Object value, Class<T> type) {
+        if(!type.isInstance(value)) {
+            throw new IllegalArgumentException(String.format("Value %s is not of type %s", value, type));
+        }
+        return (T) value;
+    }
+}
diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/CommitHandlerTransactions.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/CommitHandlerTransactions.java
new file mode 100644 (file)
index 0000000..ded2d59
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.common.util;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataModification;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+/**
+ * @deprecated Utility class for {@link DataCommitTransaction}, will be removed at the same time.
+ */
+@Deprecated
+public class CommitHandlerTransactions {
+
+    private static class AllwaysSuccessfulTransaction<P extends Path<P>,D> implements DataCommitTransaction<P, D> {
+
+        private final  DataModification<P, D> modification;
+
+        public AllwaysSuccessfulTransaction(DataModification<P, D> modification) {
+            this.modification = modification;
+        }
+        @Override
+        public RpcResult<Void> rollback() throws IllegalStateException {
+            return RpcResultBuilder.<Void>success().build();
+        }
+        @Override
+        public RpcResult<Void> finish() throws IllegalStateException {
+            return RpcResultBuilder.<Void>success().build();
+        }
+
+        @Override
+        public DataModification<P, D> getModification() {
+            return modification;
+        }
+    }
+
+    public static final <P extends Path<P>,D> AllwaysSuccessfulTransaction<P, D> allwaysSuccessfulTransaction(DataModification<P, D> modification)  {
+        return new AllwaysSuccessfulTransaction<>(modification);
+    }
+}
diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Futures.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Futures.java
new file mode 100644 (file)
index 0000000..3384e8f
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.common.util;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * @deprecated Use {@link com.google.common.util.concurrent.Futures} instead.
+ */
+@Deprecated
+public class Futures {
+
+    private Futures() {
+    }
+
+    public static <T> Future<T> immediateFuture(T result) {
+        return new ImmediateFuture<T>(result);
+    }
+
+    private static class ImmediateFuture<T> implements Future<T> {
+
+        private final T result;
+
+        public ImmediateFuture(T result) {
+            this.result = result;
+        }
+
+        @Override
+        public boolean cancel(boolean mayInterruptIfRunning) {
+            return false;
+        }
+
+        @Override
+        public boolean isCancelled() {
+            return false;
+        }
+
+        @Override
+        public boolean isDone() {
+            return true;
+        }
+
+        @Override
+        public T get() throws InterruptedException, ExecutionException {
+            return result;
+        }
+
+        @Override
+        public T get(long timeout, TimeUnit unit) throws InterruptedException,
+                ExecutionException, TimeoutException {
+            return result;
+        }
+
+    }
+}
diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/RpcErrors.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/RpcErrors.java
new file mode 100644 (file)
index 0000000..cacb167
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.common.util;
+
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+
+/**
+ * @deprecated Use {@link org.opendaylight.yangtools.yang.common.RpcResultBuilder}
+ */
+@Deprecated
+public class RpcErrors {
+
+    /**
+     * @param applicationTag
+     * @param tag
+     * @param info
+     * @param severity
+     * @param message
+     * @param errorType
+     * @param cause
+     * @return {@link RpcError} implementation
+     */
+    public static RpcError getRpcError(String applicationTag, String tag, String info,
+            ErrorSeverity severity, String message, ErrorType errorType, Throwable cause) {
+        RpcErrorTO ret = new RpcErrorTO(applicationTag, tag, info, severity, message,
+                errorType, cause);
+        return ret;
+    }
+
+    private static class RpcErrorTO implements RpcError {
+
+        private final String applicationTag;
+        private final String tag;
+        private final String info;
+        private final ErrorSeverity severity;
+        private final String message;
+        private final ErrorType errorType;
+        private final Throwable cause;
+
+        /**
+         * @param applicationTag
+         * @param tag
+         * @param info
+         * @param severity
+         * @param message
+         * @param errorType
+         * @param cause
+         */
+        protected RpcErrorTO(String applicationTag, String tag, String info,
+                ErrorSeverity severity, String message, ErrorType errorType, Throwable cause) {
+            super();
+            this.applicationTag = applicationTag;
+            this.tag = tag;
+            this.info = info;
+            this.severity = severity;
+            this.message = message;
+            this.errorType = errorType;
+            this.cause = cause;
+        }
+
+        @Override
+        public String getApplicationTag() {
+            return applicationTag;
+        }
+
+        @Override
+        public String getInfo() {
+            return info;
+        }
+
+        @Override
+        public String getMessage() {
+            return message;
+        }
+
+        @Override
+        public ErrorSeverity getSeverity() {
+            return severity;
+        }
+
+        @Override
+        public String getTag() {
+            return tag;
+        }
+
+        @Override
+        public Throwable getCause() {
+            return cause;
+        }
+
+        @Override
+        public ErrorType getErrorType() {
+            return errorType;
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Rpcs.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Rpcs.java
new file mode 100644 (file)
index 0000000..69458b6
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.common.util;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * @deprecated Use {@link org.opendaylight.yangtools.yang.common.RpcResultBuilder}
+ */
+@Deprecated
+public class Rpcs {
+
+    public static <T> RpcResult<T> getRpcResult(boolean successful) {
+        RpcResult<T> ret = new RpcResultTO<T>(successful, null, ImmutableList.<RpcError>of());
+        return ret;
+    }
+
+    public static <T> RpcResult<T> getRpcResult(boolean successful, T result,
+            Collection<RpcError> errors) {
+        RpcResult<T> ret = new RpcResultTO<T>(successful, result, errors);
+        return ret;
+    }
+
+    public static <T> RpcResult<T> getRpcResult(boolean successful, Collection<RpcError> errors) {
+        return new RpcResultTO<T>(successful, null, errors);
+    }
+
+    private static class RpcResultTO<T> implements RpcResult<T>, Serializable, Immutable {
+        private static final long serialVersionUID = 1L;
+        private final Collection<RpcError> errors;
+        private final T result;
+        private final boolean successful;
+
+        public RpcResultTO(boolean successful, T result,
+                Collection<RpcError> errors) {
+            this.successful = successful;
+            this.result = result;
+            this.errors = ImmutableList.copyOf(errors);
+        }
+
+        @Override
+        public boolean isSuccessful() {
+            return successful;
+        }
+
+        @Override
+        public T getResult() {
+            return result;
+        }
+
+        @Override
+        public Collection<RpcError> getErrors() {
+            return errors;
+        }
+
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-api/.gitignore b/opendaylight/md-sal/sal-dom-api/.gitignore
new file mode 100644 (file)
index 0000000..ea8c4bf
--- /dev/null
@@ -0,0 +1 @@
+/target
diff --git a/opendaylight/md-sal/sal-dom-api/pom.xml b/opendaylight/md-sal/sal-dom-api/pom.xml
new file mode 100644 (file)
index 0000000..30a9c7d
--- /dev/null
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>sal-parent</artifactId>
+    <version>1.3.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>sal-core-api</artifactId>
+  <packaging>bundle</packaging>
+  <dependencies>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>config-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-common-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-data-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-model-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-test-model</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-data-impl</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+      </plugin>
+    </plugins>
+  </build>
+  <scm>
+    <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+    <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+    <tag>HEAD</tag>
+    <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+  </scm>
+</project>
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/ClusteredDOMDataChangeListener.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/ClusteredDOMDataChangeListener.java
new file mode 100644 (file)
index 0000000..e5dbc54
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.api;
+
+/**
+ * <p>ClusteredDOMDataChangeListener is a marker interface to enable data change notifications on all instances in a cluster,
+ * where this listener is registered.</p>
+ *
+ *
+ * <p>Applications should implement ClusteredDOMDataChangeListener instead of DOMDataChangeListener, if they want to listen
+ * to data change notifications on any node of clustered datastore. DOMDataChangeListener enables data change notifications
+ * only at leader of the datastore shard.</p>
+ *
+ */
+
+public interface ClusteredDOMDataChangeListener extends DOMDataChangeListener{
+
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataBroker.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataBroker.java
new file mode 100644 (file)
index 0000000..0aba3ca
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainFactory;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Data Broker which provides data transaction and data change listener functionality
+ * using {@link NormalizedNode} data format.
+ *
+ * This interface is type capture of generic interfaces and returns type captures
+ * of results for client-code convenience.
+ *
+ */
+public interface DOMDataBroker extends
+        AsyncDataBroker<YangInstanceIdentifier, NormalizedNode<?, ?>, DOMDataChangeListener>,
+        TransactionChainFactory<YangInstanceIdentifier, NormalizedNode<?, ?>>, BrokerService, DOMExtensibleService<DOMDataBroker, DOMDataBrokerExtension> {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    DOMDataReadOnlyTransaction newReadOnlyTransaction();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    DOMDataReadWriteTransaction newReadWriteTransaction();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    DOMDataWriteTransaction newWriteOnlyTransaction();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    DOMTransactionChain createTransactionChain(TransactionChainListener listener);
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataBrokerExtension.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataBrokerExtension.java
new file mode 100644 (file)
index 0000000..abbcc40
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+/**
+ * Type capture of a {@link DOMServiceExtension} applicable to {@link DOMDataBroker}
+ * implementations.
+ */
+public interface DOMDataBrokerExtension extends DOMServiceExtension<DOMDataBroker, DOMDataBrokerExtension> {
+
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataChangeListener.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataChangeListener.java
new file mode 100644 (file)
index 0000000..c46529b
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * DOMDataChangeListener enables data change notifications only at leader of the datastore shard.
+ */
+
+public interface DOMDataChangeListener extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> {
+
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadOnlyTransaction.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadOnlyTransaction.java
new file mode 100644 (file)
index 0000000..41fb7ab
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadOnlyTransaction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataReadOnlyTransaction extends DOMDataReadTransaction, AsyncReadOnlyTransaction<YangInstanceIdentifier, NormalizedNode<?, ?>> {
+
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadTransaction.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadTransaction.java
new file mode 100644 (file)
index 0000000..05ca61a
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadTransaction;
+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;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+
+/**
+ * A transaction that provides read access to a logical data store.
+ * <p>
+ * For more information on usage and examples, please see the documentation in {@link AsyncReadTransaction}.
+ */
+public interface DOMDataReadTransaction extends AsyncReadTransaction<YangInstanceIdentifier, NormalizedNode<?, ?>> {
+
+    /**
+     * Reads data from provided logical data store located at the provided path.
+     *<p>
+     * If the target is a subtree, then the whole subtree is read (and will be
+     * accessible from the returned data object).
+     *
+     * @param store
+     *            Logical data store from which read should occur.
+     * @param path
+     *            Path which uniquely identifies subtree which client want to
+     *            read
+     * @return a CheckFuture containing the result of the read. The Future blocks until the
+     *         commit operation is complete. Once complete:
+     *         <ul>
+     *         <li>If the data at the supplied path exists, the Future returns an Optional object
+     *         containing the data.</li>
+     *         <li>If the data at the supplied path does not exist, the Future returns
+     *         Optional#absent().</li>
+     *         <li>If the read of the data fails, the Future will fail with a
+     *         {@link ReadFailedException} or an exception derived from ReadFailedException.</li>
+     *         </ul>
+     */
+    CheckedFuture<Optional<NormalizedNode<?,?>>, ReadFailedException> read(
+            LogicalDatastoreType store, YangInstanceIdentifier path);
+
+    /**
+     /**
+     * Checks if data is available in the logical data store located at provided path.
+     * <p>
+     *
+     * Note: a successful result from this method makes no guarantee that a subsequent call to {@link #read}
+     * will succeed. It is possible that the data resides in a data store on a remote node and, if that
+     * node goes down or a network failure occurs, a subsequent read would fail. Another scenario is if
+     * the data is deleted in between the calls to <code>exists</code> and <code>read</code>
+     *
+     * @param store
+     *            Logical data store from which read should occur.
+     * @param path
+     *            Path which uniquely identifies subtree which client want to
+     *            check existence of
+     * @return a CheckFuture containing the result of the check.
+     *         <ul>
+     *         <li>If the data at the supplied path exists, the Future returns a Boolean
+     *         whose value is true, false otherwise</li>
+     *         <li>If checking for the data fails, the Future will fail with a
+     *         {@link ReadFailedException} or an exception derived from ReadFailedException.</li>
+     *         </ul>
+     */
+    CheckedFuture<Boolean, ReadFailedException> exists(
+        LogicalDatastoreType store, YangInstanceIdentifier path);
+
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadWriteTransaction.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadWriteTransaction.java
new file mode 100644 (file)
index 0000000..d5fc5c9
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataReadWriteTransaction extends DOMDataReadTransaction, DOMDataWriteTransaction, AsyncReadWriteTransaction<YangInstanceIdentifier, NormalizedNode<?, ?>> {
+
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeChangeListener.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeChangeListener.java
new file mode 100644 (file)
index 0000000..2578790
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.Collection;
+import java.util.EventListener;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+
+/**
+ * Interface implemented by classes interested in receiving notifications about
+ * data tree changes. This interface differs from {@link DOMDataChangeListener}
+ * in that it provides a cursor-based view of the change, which has potentially
+ * lower overhead.
+ */
+public interface DOMDataTreeChangeListener extends EventListener {
+    /**
+     * Invoked when there was data change for the supplied path, which was used
+     * to register this listener.
+     *
+     * <p>
+     * This method may be also invoked during registration of the listener if
+     * there is any pre-existing data in the conceptual data tree for supplied
+     * path. This initial event will contain all pre-existing data as created.
+     *
+     * <p>
+     * A data change event may be triggered spuriously, e.g. such that data before
+     * and after compare as equal. Implementations of this interface are expected
+     * to recover from such events. Event producers are expected to exert reasonable
+     * effort to suppress such events.
+     *
+     * In other words, it is completely acceptable to observe
+     * a {@link org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode},
+     * which reports a {@link org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType}
+     * other than UNMODIFIED, while the before- and after- data items compare as
+     * equal.
+     *
+     * @param changes Collection of change events, may not be null or empty.
+     */
+    void onDataTreeChanged(@Nonnull Collection<DataTreeCandidate> changes);
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeChangeService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeChangeService.java
new file mode 100644 (file)
index 0000000..bdd853a
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * A {@link DOMServiceExtension} which allows users to register for changes to a
+ * subtree.
+ */
+public interface DOMDataTreeChangeService extends DOMDataBrokerExtension {
+    /**
+     * Registers a {@link DOMDataTreeChangeListener} to receive
+     * notifications when data changes under a given path in the conceptual data
+     * tree.
+     * <p>
+     * You are able to register for notifications  for any node or subtree
+     * which can be represented using {@link DOMDataTreeIdentifier}.
+     * <p>
+     *
+     * You are able to register for data change notifications for a subtree or leaf
+     * even if it does not exist. You will receive notification once that node is
+     * created.
+     * <p>
+     * If there is any pre-existing data in the data tree for the path for which you are
+     * registering, you will receive an initial data change event, which will
+     * contain all pre-existing data, marked as created.
+     *
+     * <p>
+     * This method returns a {@link ListenerRegistration} object. To
+     * "unregister" your listener for changes call the {@link ListenerRegistration#close()}
+     * method on the returned object.
+     * <p>
+     * You MUST explicitly unregister your listener when you no longer want to receive
+     * notifications. This is especially true in OSGi environments, where failure to
+     * do so during bundle shutdown can lead to stale listeners being still registered.
+     *
+     * @param treeId
+     *            Data tree identifier of the subtree which should be watched for
+     *            changes.
+     * @param listener
+     *            Listener instance which is being registered
+     * @return Listener registration object, which may be used to unregister
+     *         your listener using {@link ListenerRegistration#close()} to stop
+     *         delivery of change events.
+     */
+    @Nonnull <L extends DOMDataTreeChangeListener> ListenerRegistration<L> registerDataTreeChangeListener(@Nonnull DOMDataTreeIdentifier treeId, @Nonnull L listener);
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeIdentifier.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeIdentifier.java
new file mode 100644 (file)
index 0000000..407c466
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.api;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import java.io.Serializable;
+import java.util.Iterator;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * A unique identifier for a particular subtree. It is composed of the logical
+ * data store type and the instance identifier of the root node.
+ */
+public final class DOMDataTreeIdentifier implements Immutable, Path<DOMDataTreeIdentifier>, Serializable, Comparable<DOMDataTreeIdentifier> {
+    private static final long serialVersionUID = 1L;
+    private final YangInstanceIdentifier rootIdentifier;
+    private final LogicalDatastoreType datastoreType;
+
+    public DOMDataTreeIdentifier(final LogicalDatastoreType datastoreType, final YangInstanceIdentifier rootIdentifier) {
+        this.datastoreType = Preconditions.checkNotNull(datastoreType);
+        this.rootIdentifier = Preconditions.checkNotNull(rootIdentifier);
+    }
+
+    /**
+     * Return the logical data store type.
+     *
+     * @return Logical data store type. Guaranteed to be non-null.
+     */
+    public @Nonnull LogicalDatastoreType getDatastoreType() {
+        return datastoreType;
+    }
+
+    /**
+     * Return the {@link YangInstanceIdentifier} of the root node.
+     *
+     * @return Instance identifier corresponding to the root node.
+     */
+    public @Nonnull YangInstanceIdentifier getRootIdentifier() {
+        return rootIdentifier;
+    }
+
+    @Override
+    public boolean contains(final DOMDataTreeIdentifier other) {
+        return datastoreType == other.datastoreType && rootIdentifier.contains(other.rootIdentifier);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + datastoreType.hashCode();
+        result = prime * result + rootIdentifier.hashCode();
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof DOMDataTreeIdentifier)) {
+            return false;
+        }
+        DOMDataTreeIdentifier other = (DOMDataTreeIdentifier) obj;
+        if (datastoreType != other.datastoreType) {
+            return false;
+        }
+        return rootIdentifier.equals(other.rootIdentifier);
+    }
+
+    @Override
+    public int compareTo(final DOMDataTreeIdentifier o) {
+        int i = datastoreType.compareTo(o.datastoreType);
+        if (i != 0) {
+            return i;
+        }
+
+        final Iterator<PathArgument> mi = rootIdentifier.getPathArguments().iterator();
+        final Iterator<PathArgument> oi = o.rootIdentifier.getPathArguments().iterator();
+
+        while (mi.hasNext()) {
+            if (!oi.hasNext()) {
+                return 1;
+            }
+
+            final PathArgument ma = mi.next();
+            final PathArgument oa = oi.next();
+            i = ma.compareTo(oa);
+            if (i != 0) {
+                return i;
+            }
+        }
+
+        return oi.hasNext() ? -1 : 0;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).add("datastore", datastoreType).add("root", rootIdentifier).toString();
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeInaccessibleException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeInaccessibleException.java
new file mode 100644 (file)
index 0000000..eea1be5
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import com.google.common.base.Preconditions;
+
+
+/**
+ * Failure reported when a data tree is no longer accessible.
+ */
+public class DOMDataTreeInaccessibleException extends DOMDataTreeListeningException {
+    private static final long serialVersionUID = 1L;
+    private final DOMDataTreeIdentifier treeIdentifier;
+
+    public DOMDataTreeInaccessibleException(final DOMDataTreeIdentifier treeIdentifier, final String message) {
+        super(message);
+        this.treeIdentifier = Preconditions.checkNotNull(treeIdentifier);
+    }
+
+    public DOMDataTreeInaccessibleException(final DOMDataTreeIdentifier treeIdentifier, final String message, final Throwable cause) {
+        super(message);
+        this.treeIdentifier = Preconditions.checkNotNull(treeIdentifier);
+    }
+
+    public final DOMDataTreeIdentifier getTreeIdentifier() {
+        return treeIdentifier;
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeListener.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeListener.java
new file mode 100644 (file)
index 0000000..083cd10
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.Collection;
+import java.util.EventListener;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+
+/**
+ * Interface implemented by data consumers, e.g. processes wanting to act on data
+ * after it has been introduced to the conceptual data tree.
+ */
+public interface DOMDataTreeListener extends EventListener {
+    /**
+     * Invoked whenever one or more registered subtrees change. The logical changes are reported,
+     * as well as the roll up of new state for all subscribed subtrees.
+     *
+     * @param changes The set of changes being reported. Each subscribed subtree may be present
+     *                at most once.
+     * @param subtrees Per-subtree state as visible after the reported changes have been applied.
+     *                 This includes all the subtrees this listener is subscribed to, even those
+     *                 which have not changed.
+     */
+    void onDataTreeChanged(@Nonnull Collection<DataTreeCandidate> changes, @Nonnull Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>> subtrees);
+
+    /**
+     * Invoked when a subtree listening failure occurs. This can be triggered, for example, when
+     * a connection to external subtree source is broken. The listener will not receive any other
+     * callbacks, but its registration still needs to be closed to prevent resource leak.
+     *
+     * @param cause Collection of failure causes, may not be null or empty.
+     */
+    void onDataTreeFailed(@Nonnull Collection<DOMDataTreeListeningException> causes);
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeListeningException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeListeningException.java
new file mode 100644 (file)
index 0000000..e0d9b42
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+/**
+ * Base exception for various causes why and {@link DOMDataTreeListener}
+ * may be terminated by the {@link DOMDataTreeService} implementation.
+ */
+public class DOMDataTreeListeningException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public DOMDataTreeListeningException(final String message) {
+        super(message);
+    }
+
+    public DOMDataTreeListeningException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeLoopException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeLoopException.java
new file mode 100644 (file)
index 0000000..8f498a8
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Exception thrown when a loop is detected in the way {@link DOMDataTreeListener}
+ * and {@link DOMDataTreeProducer} instances would be connected.
+ */
+public class DOMDataTreeLoopException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public DOMDataTreeLoopException(final @Nonnull String message) {
+        super(message);
+    }
+
+    public DOMDataTreeLoopException(final @Nonnull String message, final @Nonnull Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducer.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducer.java
new file mode 100644 (file)
index 0000000..cbfa012
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.Collection;
+import javax.annotation.Nonnull;
+
+/**
+ * A data producer context. It allows transactions to be submitted to the subtrees
+ * specified at instantiation time. At any given time there may be a single transaction
+ * open. It needs to be either submitted or cancelled before another one can be open.
+ * Once a transaction is submitted, it will proceed to be committed asynchronously.
+ *
+ * Each instance has  an upper bound on the number of transactions which can be in-flight,
+ * once that capacity is exceeded, an attempt to create a new transaction will block
+ * until some transactions complete.
+ *
+ * Each {@link DOMDataTreeProducer} can be in two logical states, bound and unbound,
+ * which define the lifecycle rules for when is it legal to create and submit transactions
+ * in relationship with {@link DOMDataTreeListener} callbacks.
+ *
+ * When a producer is first created, it is unbound. In this state the producer can be
+ * accessed by any application thread to allocate or submit transactions, as long as
+ * the 'single open transaction' rule is maintained. The producer and any transaction
+ * object MUST NOT be accessed, directly or indirectly, from a {@link DOMDataTreeListener}
+ * callback.
+ *
+ * When a producer is referenced in a call to {@link DOMDataTreeService#registerListener(DOMDataTreeListener, java.util.Collection, boolean, java.util.Collection)},
+ * an attempt will be made to bind the producer to the specified {@link DOMDataTreeListener}.
+ * Such an attempt will fail the producer is already bound, or it has an open transaction.
+ * Once bound, the producer can only be accessed from within the {@link DOMDataTreeListener}
+ * callback on that particular instance. Any transaction which is not submitted by the
+ * time the callback returns will be implicitly cancelled. A producer becomes unbound
+ * when the listener it is bound to becomes unregistered.
+ */
+public interface DOMDataTreeProducer extends DOMDataTreeProducerFactory, AutoCloseable {
+    /**
+     * Allocate a new open transaction on this producer. Any and all transactions
+     * previously allocated must have been either submitted or cancelled by the
+     * time this method is invoked.
+     *
+     * @param barrier Indicates whether this transaction should be a barrier. A barrier
+     *                transaction is processed separately from any preceding transactions.
+     *                Non-barrier transactions may be merged and processed in a batch,
+     *                such that any observers see the modifications contained in them as
+     *                if the modifications were made in a single transaction.
+     * @return A new {@link DOMDataWriteTransaction}
+     * @throws {@link IllegalStateException} if a previous transaction was not closed.
+     * @throws {@link IllegalThreadStateException} if the calling thread context does not
+     *         match the lifecycle rules enforced by the producer state (e.g. bound or unbound).
+     *         This exception is thrown on a best effort basis and programs should not rely
+     *         on it for correct operation.
+     */
+    @Nonnull DOMDataWriteTransaction createTransaction(boolean isolated);
+
+    /**
+     * {@inheritDoc}
+     *
+     * When invoked on a {@link DOMDataTreeProducer}, this method has additional restrictions.
+     * There may not be an open transaction from this producer. The method needs to be
+     * invoked in appropriate context, e.g. bound or unbound.
+     *
+     * Specified subtrees must be accessible by this producer. Accessible means they are a subset
+     * of the subtrees specified when the producer is instantiated. The set is further reduced as
+     * child producers are instantiated -- if you create a producer for /a and then a child for
+     * /a/b, /a/b is not accessible from the first producer.
+     *
+     * Once this method returns successfully, this (parent) producer loses the ability to
+     * access the specified paths until the resulting (child) producer is shut down.
+     *
+     * @throws {@link IllegalStateException} if there is an open transaction
+     * @throws {@link IllegalArgumentException} if subtrees contains a subtree which is not
+     *         accessible by this producer
+     * @throws {@link IllegalThreadStateException} if the calling thread context does not
+     *         match the lifecycle rules enforced by the producer state (e.g. bound or unbound).
+     *         This exception is thrown on a best effort basis and programs should not rely
+     *         on it for correct operation.
+     */
+    @Override
+    @Nonnull DOMDataTreeProducer createProducer(@Nonnull Collection<DOMDataTreeIdentifier> subtrees);
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws DOMDataTreeProducerBusyException when there is an open transaction.
+     */
+    @Override
+    void close() throws DOMDataTreeProducerException;
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerBusyException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerBusyException.java
new file mode 100644 (file)
index 0000000..a83a5ca
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+/**
+ * Exception indicating that the {@link DOMDataTreeProducer} has an open user
+ * transaction and cannot be closed.
+ */
+public class DOMDataTreeProducerBusyException extends DOMDataTreeProducerException {
+    private static final long serialVersionUID = 1L;
+
+    public DOMDataTreeProducerBusyException(final String message) {
+        super(message);
+    }
+
+    public DOMDataTreeProducerBusyException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerException.java
new file mode 100644 (file)
index 0000000..16c2676
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+/**
+ * Base exception for all exceptions related to {@link DOMDataTreeProducer}s.
+ */
+public class DOMDataTreeProducerException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public DOMDataTreeProducerException(final String message) {
+        super(message);
+    }
+
+    public DOMDataTreeProducerException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerFactory.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerFactory.java
new file mode 100644 (file)
index 0000000..89ac8d1
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.Collection;
+import javax.annotation.Nonnull;
+
+/**
+ * Base source of {@link DOMDataTreeProducer}s. This interface is usually not used directly,
+ * but rather through one of its sub-interfaces.
+ */
+public interface DOMDataTreeProducerFactory {
+    /**
+     * Create a producer, which is able to access to a set of trees.
+     *
+     * @param subtrees The collection of subtrees the resulting producer should have access to.
+     * @return A {@link DOMDataTreeProducer} instance.
+     * @throws {@link IllegalArgumentException} if subtrees is empty.
+     */
+    @Nonnull DOMDataTreeProducer createProducer(@Nonnull Collection<DOMDataTreeIdentifier> subtrees);
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeService.java
new file mode 100644 (file)
index 0000000..21ff44c
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.Collection;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * A {@link DOMService} providing access to the conceptual data tree. Interactions
+ * with the data tree are split into data producers and consumers (listeners). Each
+ * of them operate on a set of subtrees, which need to be declared at instantiation time.
+ *
+ * Returned instances are not thread-safe and expected to be used by a single thread
+ * at a time. Furthermore, producers may not be accessed from consumer callbacks
+ * unless they were specified when the listener is registered.
+ *
+ * The service maintains a loop-free topology of producers and consumers. What this means
+ * is that a consumer is not allowed to access a producer, which affects any of the
+ * subtrees it is subscribed to. This restriction is in place to ensure the system does
+ * not go into a feedback loop, where it is impossible to block either a producer or
+ * a consumer without accumulating excess work in the backlog stemming from its previous
+ * activity.
+ */
+public interface DOMDataTreeService extends DOMDataTreeProducerFactory, DOMService {
+    /**
+     * Register a {@link DOMDataTreeListener} instance. Once registered, the listener
+     * will start receiving changes on the selected subtrees. If the listener cannot
+     * keep up with the rate of changes, and allowRxMerges is set to true, this service
+     * is free to merge the changes, so that a smaller number of them will be reported,
+     * possibly hiding some data transitions (like flaps).
+     *
+     * If the listener wants to write into any producer, that producer has to be mentioned
+     * in the call to this method. Those producers will be bound exclusively to the
+     * registration, so that accessing them outside of this listener's callback will trigger
+     * an error. Any producers mentioned must be idle, e.g. they may not have an open
+     * transaction at the time this method is invoked.
+     *
+     * Each listener instance can be registered at most once. Implementations of this
+     * interface have to guarantee that the listener's methods will not be invoked
+     * concurrently from multiple threads.
+     *
+     * @param listener {@link DOMDataTreeListener} that is being registered
+     * @param subtrees Conceptual subtree identifier of subtrees which should be monitored
+     *                 for changes. May not be null or empty.
+     * @param allowRxMerges True if the backend may perform ingress state compression.
+     * @param producers {@link DOMDataTreeProducer} instances to bind to the listener.
+     * @return A listener registration. Once closed, the listener will no longer be
+     *         invoked and the producers will be unbound.
+     * @throws IllegalArgumentException if subtrees is empty or the listener is already bound
+     * @throws DOMDataTreeLoopException if the registration of the listener to the specified
+     *                                  subtrees with specified producers would form a
+     *                                  feedback loop
+     */
+    @Nonnull <T extends DOMDataTreeListener> ListenerRegistration<T> registerListener(@Nonnull T listener,
+        @Nonnull Collection<DOMDataTreeIdentifier> subtrees, boolean allowRxMerges, @Nonnull Collection<DOMDataTreeProducer> producers);
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShard.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShard.java
new file mode 100644 (file)
index 0000000..e9ef003
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.EventListener;
+import javax.annotation.Nonnull;
+
+/**
+ * A single shard of the conceptual data tree. This interface defines the basic notifications
+ * a shard can receive. Each shard implementation is expected to also implement some of the
+ * datastore-level APIs. Which interfaces are required depends on the {@link DOMDataTreeShardingService}
+ * implementation.
+ */
+public interface DOMDataTreeShard extends EventListener {
+    /**
+     * Invoked whenever a child is getting attached as a more specific prefix under this shard.
+     *
+     * @param prefix Child's prefix
+     * @param child Child shard
+     */
+    void onChildAttached(@Nonnull DOMDataTreeIdentifier prefix, @Nonnull DOMDataTreeShard child);
+
+    /**
+     * Invoked whenever a child is getting detached as a more specific prefix under this shard.
+     *
+     * @param prefix Child's prefix
+     * @param child Child shard
+     */
+    void onChildDetached(@Nonnull DOMDataTreeIdentifier prefix, @Nonnull DOMDataTreeShard child);
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShardingConflictException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShardingConflictException.java
new file mode 100644 (file)
index 0000000..d44316d
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Exception thrown when an attempt to attach a conflicting shard to the global
+ * table.
+ */
+public class DOMDataTreeShardingConflictException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public DOMDataTreeShardingConflictException(final @Nonnull String message) {
+        super(message);
+    }
+
+    public DOMDataTreeShardingConflictException(final @Nonnull String message, final @Nonnull Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShardingService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShardingService.java
new file mode 100644 (file)
index 0000000..c087224
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * A {@link DOMService} providing access to details on how the conceptual data tree
+ * is distributed among providers (also known as shards). Each shard is tied to a
+ * single {@link DOMDataTreeIdentifier}. Based on those data tree identifiers, the
+ * shards are organized in a tree, where there is a logical parent/child relationship.
+ *
+ * It is not allowed to attach two shards to the same data tree identifier, which means
+ * the mapping of each piece of information has an unambiguous home. When accessing
+ * the information, the shard with the longest matching data tree identifier is used,
+ * which is why this interface treats it is a prefix.
+ *
+ * Whenever a parent/child relationship is changed, the parent is notified, so it can
+ * understand that a logical child has been attached.
+ */
+public interface DOMDataTreeShardingService extends DOMService {
+    /**
+     * Register a shard as responsible for a particular subtree prefix.
+     *
+     * @param prefix Data tree identifier, may not be null.
+     * @param shard Responsible shard instance
+     * @return A registration. To remove the shard's binding, close the registration.
+     * @throws DOMDataTreeShardingConflictException if the prefix is already bound
+     */
+    @Nonnull <T extends DOMDataTreeShard> ListenerRegistration<T> registerDataTreeShard(@Nonnull DOMDataTreeIdentifier prefix, @Nonnull T shard) throws DOMDataTreeShardingConflictException;
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataWriteTransaction.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataWriteTransaction.java
new file mode 100644 (file)
index 0000000..b8fe263
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * A transaction that provides mutation capabilities on a data tree.
+ * <p>
+ * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}.
+ */
+public interface DOMDataWriteTransaction extends AsyncWriteTransaction<YangInstanceIdentifier, NormalizedNode<?, ?>> {
+
+    /**
+     * Stores a piece of data at the specified path. This acts as an add / replace
+     * operation, which is to say that whole subtree will be replaced by the specified data.
+     * <p>
+     * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}.
+     * <p>
+     * If you need to make sure that a parent object exists but you do not want modify
+     * its pre-existing state by using put, consider using {@link #merge} instead.
+     *
+     * @param store
+     *            the logical data store which should be modified
+     * @param path
+     *            the data object path
+     * @param data
+     *            the data object to be written to the specified path
+     * @throws IllegalStateException
+     *             if the transaction has already been submitted
+     */
+    void put(LogicalDatastoreType store, YangInstanceIdentifier path, NormalizedNode<?, ?> data);
+
+    /**
+     * Merges a piece of data with the existing data at a specified path. Any pre-existing data
+     * which is not explicitly overwritten will be preserved. This means that if you store a container,
+     * its child lists will be merged.
+     * <p>
+     * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}.
+     *<p>
+     * If you require an explicit replace operation, use {@link #put} instead.
+     *
+     * @param store
+     *            the logical data store which should be modified
+     * @param path
+     *            the data object path
+     * @param data
+     *            the data object to be merged to the specified path
+     * @throws IllegalStateException
+     *             if the transaction has already been submitted
+     */
+    void merge(LogicalDatastoreType store, YangInstanceIdentifier path, NormalizedNode<?, ?> data);
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMEvent.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMEvent.java
new file mode 100644 (file)
index 0000000..1e19e19
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.Date;
+
+/**
+ * Generic event interface
+ */
+public interface DOMEvent {
+
+    /**
+     * Get the time of the event occurrence
+     *
+     * @return the event time
+     */
+    Date getEventTime();
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMExtensibleService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMExtensibleService.java
new file mode 100644 (file)
index 0000000..2233442
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Map;
+import javax.annotation.Nonnull;
+
+/**
+ * Marker interface for services which can support {@link DOMServiceExtension}.
+ * Aside for marking these, they also provide runtime query to detect whether
+ * a particular trait is in fact available.
+ *
+ * <T> Base {@link DOMService}
+ * <E> Extension type
+ */
+@Beta
+public interface DOMExtensibleService<T extends DOMExtensibleService<T, E>, E extends DOMServiceExtension<T, E>> extends DOMService {
+    /**
+     * Return a map of currently-supported extensions, along with accessor services
+     * which provide access to the specific functionality bound to this service.
+     *
+     * @return A map of supported functionality.
+     */
+    @Nonnull Map<Class<? extends E>, E> getSupportedExtensions();
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMMountPoint.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMMountPoint.java
new file mode 100644 (file)
index 0000000..3a04ee2
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+import com.google.common.base.Optional;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public interface DOMMountPoint extends Identifiable<YangInstanceIdentifier> {
+
+    <T extends DOMService> Optional<T> getService(Class<T> cls);
+
+    SchemaContext getSchemaContext();
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMMountPointService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMMountPointService.java
new file mode 100644 (file)
index 0000000..9286f06
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.base.Optional;
+
+
+public interface DOMMountPointService extends BrokerService {
+
+    Optional<DOMMountPoint> getMountPoint(YangInstanceIdentifier path);
+
+    DOMMountPointBuilder createMountPoint(YangInstanceIdentifier path);
+
+    ListenerRegistration<MountProvisionListener> registerProvisionListener(MountProvisionListener listener);
+
+    public interface DOMMountPointBuilder {
+
+        <T extends DOMService> DOMMountPointBuilder addService(Class<T> type,T impl);
+
+        DOMMountPointBuilder addInitialSchemaContext(SchemaContext ctx);
+
+        ObjectRegistration<DOMMountPoint> register();
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotification.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotification.java
new file mode 100644 (file)
index 0000000..d99001e
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * A single YANG notification.
+ */
+public interface DOMNotification {
+    /**
+     * Return the type of this notification.
+     *
+     * @return Notification type.
+     */
+    @Nonnull SchemaPath getType();
+
+    /**
+     * Return the body of this notification.
+     *
+     * @return Notification body.
+     */
+    @Nonnull ContainerNode getBody();
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationListener.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationListener.java
new file mode 100644 (file)
index 0000000..7085588
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.EventListener;
+import javax.annotation.Nonnull;
+
+/**
+ * Interface implemented by listeners interested in {@link DOMNotification}s.
+ */
+public interface DOMNotificationListener extends EventListener {
+    /**
+     * Invoked whenever a {@link DOMNotification} matching the subscription
+     * criteria is received.
+     *
+     * @param notification Received notification
+     */
+    void onNotification(@Nonnull DOMNotification notification);
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationPublishService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationPublishService.java
new file mode 100644 (file)
index 0000000..a7d2770
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.TimeUnit;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+
+/**
+ * A {@link DOMService} which allows its user to send {@link DOMNotification}s. It
+ * provides two styles of initiating the notification delivery, similar to
+ * {@link java.util.concurrent.BlockingQueue}:
+ * - a put-style method which waits until the implementation can accept the notification
+ *   for delivery, and
+ * - an offer-style method, which attempts to enqueue the notification, but allows
+ *   the caller to specify that it should never wait, or put an upper bound on how
+ *   long it is going to wait.
+ */
+public interface DOMNotificationPublishService extends DOMService, BrokerService {
+    /**
+     * Well-known value indicating that the implementation is currently not
+     * able to accept a notification.
+     */
+    ListenableFuture<Object> REJECTED = Futures.immediateFailedFuture(new DOMNotificationRejectedException("Unacceptable blocking conditions encountered"));
+
+    /**
+     * Publish a notification. The result of this method is a {@link ListenableFuture}
+     * which will complete once the notification has been delivered to all immediate
+     * registrants. The type of the object resulting from the future is not defined
+     * and implementations may use it to convey additional information related to the
+     * publishing process.
+     *
+     * Abstract subclasses can refine the return type as returning a promise of a
+     * more specific type, e.g.:
+     *
+     *     public interface DeliveryStatus { int getListenerCount(); }
+     *     ListenableFuture<? extends DeliveryStatus> putNotification(DOMNotification notification);
+     *
+     * Once the Future succeeds, the resulting object can be queried for traits using
+     * instanceof, e.g:
+     *
+     *     // Can block when (for example) the implemention's ThreadPool queue is full
+     *     Object o = service.putNotification(notif).get();
+     *     if (o instanceof DeliveryStatus) {
+     *         DeliveryStatus ds = (DeliveryStatus)o;
+     *         LOG.debug("Notification was received by {} listeners", ds.getListenerCount(););
+     *     }
+     * }
+     *
+     * In case an implementation is running out of resources, it can block the calling
+     * thread until enough resources become available to accept the notification for
+     * processing, or it is interrupted.
+     *
+     * Caution: completion here means that the implementation has completed processing
+     *          of the notification. This does not mean that all existing registrants
+     *          have seen the notification. Most importantly, the delivery process at
+     *          other cluster nodes may have not begun yet.
+     *
+     * @param notification Notification to be published.
+     * @return A listenable future which will report completion when the service
+     *         has finished propagating the notification to its immediate registrants.
+     * @throws InterruptedException if interrupted while waiting
+     * @throws NullPointerException if notification is null.
+     */
+    @Nonnull ListenableFuture<? extends Object> putNotification(@Nonnull DOMNotification notification) throws InterruptedException;
+
+    /**
+     * Attempt to publish a notification. The result of this method is a {@link ListenableFuture}
+     * which will complete once the notification has been delivered to all immediate
+     * registrants. The type of the object resulting from the future is not defined
+     * and implementations may use it to convey additional information related to the
+     * publishing process. Unlike {@link #putNotification(DOMNotification)}, this method
+     * is guaranteed not to block if the underlying implementation encounters contention.
+     *
+     * @param notification Notification to be published.
+     * @return A listenable future which will report completion when the service
+     *         has finished propagating the notification to its immediate registrants,
+     *         or {@value #REJECTED} if resource constraints prevent
+     *         the implementation from accepting the notification for delivery.
+     * @throws NullPointerException if notification is null.
+     */
+    @Nonnull ListenableFuture<? extends Object> offerNotification(@Nonnull DOMNotification notification);
+
+    /**
+     * Attempt to publish a notification. The result of this method is a {@link ListenableFuture}
+     * which will complete once the notification has been delivered to all immediate
+     * registrants. The type of the object resulting from the future is not defined
+     * and implementations may use it to convey additional information related to the
+     * publishing process. Unlike {@link #putNotification(DOMNotification)}, this method
+     * is guaranteed to block more than the specified timeout.
+     *
+     * @param notification Notification to be published.
+     * @param timeout how long to wait before giving up, in units of unit
+     * @param unit a TimeUnit determining how to interpret the timeout parameter
+     * @return A listenable future which will report completion when the service
+     *         has finished propagating the notification to its immediate registrants,
+     *         or {@value #REJECTED} if resource constraints prevent
+     *         the implementation from accepting the notification for delivery.
+     * @throws InterruptedException if interrupted while waiting
+     * @throws NullPointerException if notification or unit is null.
+     * @throws IllegalArgumentException if timeout is negative.
+     */
+    @Nonnull ListenableFuture<? extends Object> offerNotification(@Nonnull DOMNotification notification,
+        @Nonnegative long timeout, @Nonnull TimeUnit unit) throws InterruptedException;
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationRejectedException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationRejectedException.java
new file mode 100644 (file)
index 0000000..4d0aa66
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+/**
+ * <p>
+ * This exception indicates that given notification can not be processed by corresponding mechanism.
+ * More info can be provided in message.
+ * <p/>
+ * <p>
+ * Expected use: {@link DOMNotificationPublishService}
+ * </p>
+ */
+public class DOMNotificationRejectedException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public DOMNotificationRejectedException(final String message) {
+        super(message);
+    }
+
+    public DOMNotificationRejectedException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationService.java
new file mode 100644 (file)
index 0000000..036ea24
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.Collection;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * A {@link DOMService} which allows its users to subscribe to receive
+ * {@link DOMNotification}s.
+ */
+public interface DOMNotificationService extends DOMService, BrokerService {
+    /**
+     * Register a {@link DOMNotificationListener} to receive a set of notifications. As with
+     * other ListenerRegistration-based interfaces, registering an instance multiple times
+     * results in notifications being delivered for each registration.
+     *
+     * @param listener Notification instance to register
+     * @param types Notification types which should be delivered to the listener. Duplicate
+     *              entries are processed only once, null entries are ignored.
+     * @return Registration handle. Invoking {@link DOMNotificationListenerRegistration#close()}
+     *         will stop the delivery of notifications to the listener
+     * @throws IllegalArgumentException if types is empty or contains an invalid element, such as
+     *         null or a SchemaPath which does not represent a valid {@link DOMNotification} type.
+     * @throws NullPointerException if either of the arguments is null
+     */
+    <T extends DOMNotificationListener> ListenerRegistration<T> registerNotificationListener(@Nonnull T listener, @Nonnull Collection<SchemaPath> types);
+
+    /**
+     * Register a {@link DOMNotificationListener} to receive a set of notifications. As with
+     * other ListenerRegistration-based interfaces, registering an instance multiple times
+     * results in notifications being delivered for each registration.
+     *
+     * @param listener Notification instance to register
+     * @param types Notification types which should be delivered to the listener. Duplicate
+     *              entries are processed only once, null entries are ignored.
+     * @return Registration handle. Invoking {@link DOMNotificationListenerRegistration#close()}
+     *         will stop the delivery of notifications to the listener
+     * @throws IllegalArgumentException if types is empty or contains an invalid element, such as
+     *         null or a SchemaPath which does not represent a valid {@link DOMNotification} type.
+     * @throws NullPointerException if listener is null
+     */
+    // FIXME: Java 8: provide a default implementation of this method.
+    <T extends DOMNotificationListener> ListenerRegistration<T> registerNotificationListener(@Nonnull T listener, SchemaPath... types);
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcAvailabilityListener.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcAvailabilityListener.java
new file mode 100644 (file)
index 0000000..77d42a4
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.Collection;
+import java.util.EventListener;
+import javax.annotation.Nonnull;
+
+/**
+ * An {@link EventListener} used to track RPC implementations becoming (un)available
+ * to a {@link DOMRpcService}.
+ */
+public interface DOMRpcAvailabilityListener extends EventListener {
+    /**
+     * Method invoked whenever an RPC type becomes available.
+     *
+     * @param rpcs RPC types newly available
+     */
+    void onRpcAvailable(@Nonnull Collection<DOMRpcIdentifier> rpcs);
+
+    /**
+     * Method invoked whenever an RPC type becomes unavailable.
+     *
+     * @param rpcs RPC types which became unavailable
+     */
+    void onRpcUnavailable(@Nonnull Collection<DOMRpcIdentifier> rpcs);
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcException.java
new file mode 100644 (file)
index 0000000..7ea4f4c
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+/**
+ * Base class for failures that can occur during RPC invocation. This covers
+ * transport and protocol-level failures.
+ */
+public abstract class DOMRpcException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Construct an new instance with a message and an empty cause.
+     *
+     * @param message Exception message
+     */
+    protected DOMRpcException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Construct an new instance with a message and a cause.
+     *
+     * @param message Exception message
+     * @param cause Chained cause
+     */
+    protected DOMRpcException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcIdentifier.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcIdentifier.java
new file mode 100644 (file)
index 0000000..671671c
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import java.util.Objects;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * Identifier of a RPC context. This is an extension of the YANG RPC, which
+ * always has global context. It allows an RPC to have a instance identifier
+ * attached, so that there can be multiple implementations bound to different
+ * contexts concurrently.
+ */
+public abstract class DOMRpcIdentifier {
+
+    private static final YangInstanceIdentifier GLOBAL_CONTEXT = YangInstanceIdentifier.create(Collections.<PathArgument>emptySet());
+
+    private static final class Global extends DOMRpcIdentifier {
+        private Global(final @Nonnull SchemaPath type) {
+            super(type);
+        }
+
+        @Override
+        public YangInstanceIdentifier getContextReference() {
+            return GLOBAL_CONTEXT;
+        }
+    }
+
+    private static final class Local extends DOMRpcIdentifier {
+        private final YangInstanceIdentifier contextReference;
+
+        private Local(final @Nonnull SchemaPath type, final @Nonnull YangInstanceIdentifier contextReference) {
+            super(type);
+            this.contextReference = Preconditions.checkNotNull(contextReference);
+        }
+
+        @Override
+        public YangInstanceIdentifier getContextReference() {
+            return contextReference;
+        }
+    }
+
+    private final SchemaPath type;
+
+    private DOMRpcIdentifier(final SchemaPath type) {
+        this.type = Preconditions.checkNotNull(type);
+    }
+
+    /**
+     * Create a global RPC identifier.
+     *
+     * @param type RPC type, SchemaPath of its definition, may not be null
+     * @return A global RPC identifier, guaranteed to be non-null.
+     */
+    public static @Nonnull DOMRpcIdentifier create(final @Nonnull SchemaPath type) {
+        return new Global(type);
+    }
+
+    /**
+     * Create an RPC identifier with a particular context reference.
+     *
+     * @param type RPC type, SchemaPath of its definition, may not be null
+     * @param contextReference Context reference, null means a global RPC identifier.
+     * @return A global RPC identifier, guaranteed to be non-null.
+     */
+    public static @Nonnull DOMRpcIdentifier create(final @Nonnull SchemaPath type, final @Nullable YangInstanceIdentifier contextReference) {
+        if (contextReference == null || GLOBAL_CONTEXT.equals(contextReference)) {
+            return new Global(type);
+        } else {
+            return new Local(type, contextReference);
+        }
+    }
+
+    /**
+     * Return the RPC type.
+     *
+     * @return RPC type.
+     */
+    public final @Nonnull SchemaPath getType() {
+        return type;
+    }
+
+    /**
+     * Return the RPC context reference. Null value indicates global context.
+     *
+     * @return RPC context reference.
+     */
+    public abstract @Nonnull YangInstanceIdentifier getContextReference();
+
+    @Override
+    public final int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + type.hashCode();
+        result = prime * result + (getContextReference() == null ? 0 : getContextReference().hashCode());
+        return result;
+    }
+
+    @Override
+    public final boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof DOMRpcIdentifier)) {
+            return false;
+        }
+        DOMRpcIdentifier other = (DOMRpcIdentifier) obj;
+        if (!type.equals(other.type)) {
+            return false;
+        }
+        return Objects.equals(getContextReference(), other.getContextReference());
+    }
+
+    @Override
+    public final String toString() {
+        return MoreObjects.toStringHelper(this).omitNullValues().add("type", type).add("contextReference", getContextReference()).toString();
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcImplementation.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcImplementation.java
new file mode 100644 (file)
index 0000000..c246c76
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Interface implemented by an individual RPC implementation. This API allows for dispatch
+ * implementations, e.g. an individual object handling a multitude of RPCs.
+ */
+public interface DOMRpcImplementation {
+    /**
+     * Initiate invocation of the RPC. Implementations of this method are
+     * expected to not block on external resources.
+     *
+     * @param rpc RPC identifier which was invoked
+     * @param input Input arguments, null if the RPC does not take any.
+     * @return A {@link CheckedFuture} which will return either a result structure,
+     *         or report a subclass of {@link DOMRpcException} reporting a transport
+     *         error.
+     */
+    @Nonnull CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(@Nonnull DOMRpcIdentifier rpc, @Nullable NormalizedNode<?, ?> input);
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcImplementationNotAvailableException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcImplementationNotAvailableException.java
new file mode 100644 (file)
index 0000000..cca9a45
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import com.google.common.base.Preconditions;
+import javax.annotation.Nonnull;
+
+/**
+ * Exception indicating that no implementation of the requested RPC service is available.
+ */
+public class DOMRpcImplementationNotAvailableException extends DOMRpcException {
+    private static final long serialVersionUID = 1L;
+
+    public DOMRpcImplementationNotAvailableException(@Nonnull final String format, final Object... args) {
+        super(String.format(format, args));
+    }
+
+    public DOMRpcImplementationNotAvailableException(@Nonnull final Throwable cause, @Nonnull final String format, final Object... args) {
+        super(String.format(format, args), Preconditions.checkNotNull(cause));
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcImplementationRegistration.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcImplementationRegistration.java
new file mode 100644 (file)
index 0000000..0b8dff5
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+
+/**
+ * A registration of a {@link DOMRpcImplementation}. Used to track and revoke a registration
+ * with a {@link DOMRpcProviderService}.
+ *
+ * @param <T> RPC implementation type
+ */
+public interface DOMRpcImplementationRegistration<T extends DOMRpcImplementation> extends ObjectRegistration<T> {
+    @Override
+    void close();
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcProviderService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcProviderService.java
new file mode 100644 (file)
index 0000000..4a4f965
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.Set;
+import javax.annotation.Nonnull;
+
+/**
+ * A {@link DOMService} which allows registration of RPC implementations with a conceptual
+ * router. The client counterpart of this service is {@link DOMRpcService}.
+ */
+public interface DOMRpcProviderService extends DOMService {
+    /**
+     * Register an {@link DOMRpcImplementation} object with this service.
+     *
+     * @param implementation RPC implementation, must not be null
+     * @param rpcs Array of supported RPC identifiers. Must not be null, empty, or contain a null element.
+     *             Each identifier is added exactly once, no matter how many times it occurs.
+     * @return A {@link DOMRpcImplementationRegistration} object, guaranteed to be non-null.
+     * @throws NullPointerException if implementation or types is null
+     * @throws IllegalArgumentException if types is empty or contains a null element.
+     */
+    @Nonnull <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(@Nonnull T implementation, @Nonnull DOMRpcIdentifier... rpcs);
+
+    /**
+     * Register an {@link DOMRpcImplementation} object with this service.
+     *
+     * @param implementation RPC implementation, must not be null
+     * @param rpcs Set of supported RPC identifiers. Must not be null, empty, or contain a null element.
+     * @return A {@link DOMRpcImplementationRegistration} object, guaranteed to be non-null.
+     * @throws NullPointerException if implementation or types is null
+     * @throws IllegalArgumentException if types is empty or contains a null element.
+     */
+    @Nonnull <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(@Nonnull T implementation, @Nonnull Set<DOMRpcIdentifier> rpcs);
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcResult.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcResult.java
new file mode 100644 (file)
index 0000000..5893688
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.Collection;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Interface defining a result of an RPC call.
+ */
+public interface DOMRpcResult {
+    /**
+     * Returns a set of errors and warnings which occurred during processing
+     * the call.
+     *
+     * @return a Collection of {@link RpcError}, guaranteed to be non-null. In case
+     *         no errors are reported, an empty collection is returned.
+     */
+    @Nonnull Collection<RpcError> getErrors();
+
+    /**
+     * Returns the value result of the call or null if no result is available.
+     *
+     * @return Invocation result, null if the operation has not produced a result. This might
+     *         be the case if the operation does not produce a result, or if it failed.
+     */
+    @Nullable NormalizedNode<?, ?> getResult();
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMRpcService.java
new file mode 100644 (file)
index 0000000..48f6ab6
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * A {@link DOMService} which allows clients to invoke RPCs. The conceptual model of this
+ * service is that of a dynamic router, where the set of available RPC services can change
+ * dynamically. The service allows users to add a listener to track the process of
+ * RPCs becoming available.
+ */
+public interface DOMRpcService extends DOMService {
+    /**
+     * Initiate invocation of an RPC. This method is guaranteed to not block on any external
+     * resources.
+     *
+     * @param type SchemaPath of the RPC to be invoked
+     * @param input Input arguments, null if the RPC does not take any.
+     * @return A {@link CheckedFuture} which will return either a result structure,
+     *         or report a subclass of {@link DOMRpcException} reporting a transport
+     *         error.
+     */
+    @Nonnull CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(@Nonnull SchemaPath type, @Nullable NormalizedNode<?, ?> input);
+
+    /**
+     * Register a {@link DOMRpcAvailabilityListener} with this service to receive notifications
+     * about RPC implementations becoming (un)available. The listener will be invoked with the
+     * current implementations reported and will be kept uptodate as implementations come and go.
+     *
+     * Users should note that using a listener does not necessarily mean that {@link #invokeRpc(SchemaPath, NormalizedNode)}
+     * will not report a failure due to {@link DOMRpcImplementationNotAvailableException} and
+     * need to be ready to handle it. Implementations are encouraged to take reasonable precautions
+     * to prevent this scenario from occurring.
+     *
+     * @param listener {@link DOMRpcAvailabilityListener} instance to register
+     * @return A {@link DOMRpcAvailabilityListenerRegistration} representing this registration. Performing
+     *         a {@link DOMRpcAvailabilityListenerRegistration#close()} will cancel it. Returned object
+     *         is guaranteed to be non-null.
+     */
+    @Nonnull <T extends DOMRpcAvailabilityListener> ListenerRegistration<T> registerRpcListener(@Nonnull T listener);
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMService.java
new file mode 100644 (file)
index 0000000..0b6ce7a
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.sal.core.api.BrokerService;
+
+/**
+ * Marker interface for services which can be obtained from a {@link DOMMountPoint}
+ * instance. No further semantics are implied.
+ */
+public interface DOMService extends BrokerService {
+
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMServiceExtension.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMServiceExtension.java
new file mode 100644 (file)
index 0000000..552b289
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Marker interface for services which expose additional functionality on top
+ * of some base {@link DOMService}.
+ */
+@Beta
+public interface DOMServiceExtension<T extends DOMExtensibleService<T, E>, E extends DOMServiceExtension<T, E>> {
+
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMTransactionChain.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMTransactionChain.java
new file mode 100644 (file)
index 0000000..2d2fa48
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * A chain of DOM Data transactions.
+ *
+ * Transactions in a chain need to be committed in sequence and each
+ * transaction should see the effects of previous transactions as if they happened. A chain
+ * makes no guarantees of atomicity, in fact transactions are committed as soon as possible.
+ *
+ * <p>
+ * This interface is type capture of {@link TransactionChain} for DOM Data Contracts.
+ */
+public interface DOMTransactionChain extends TransactionChain<YangInstanceIdentifier, NormalizedNode<?, ?>> {
+
+    @Override
+    DOMDataReadOnlyTransaction newReadOnlyTransaction();
+
+    @Override
+    DOMDataReadWriteTransaction newReadWriteTransaction();
+
+    @Override
+    DOMDataWriteTransaction newWriteOnlyTransaction();
+
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/AbstractConsumer.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/AbstractConsumer.java
new file mode 100644 (file)
index 0000000..12c26a9
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api;
+
+import java.util.Collection;
+import java.util.Collections;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+public abstract class AbstractConsumer implements Consumer, BundleActivator,ServiceTrackerCustomizer<Broker, Broker> {
+
+    private BundleContext context;
+    private ServiceTracker<Broker, Broker> tracker;
+    private Broker broker;
+
+    @Override
+    public final void start(final BundleContext context) throws Exception {
+        this.context = context;
+        this.startImpl(context);
+        tracker = new ServiceTracker<>(context, Broker.class, this);
+        tracker.open();
+    }
+
+
+
+    @Override
+    public final void stop(final BundleContext context) throws Exception {
+        stopImpl(context);
+        broker = null;
+        tracker.close();
+    }
+
+    protected void startImpl(final BundleContext context) {
+        // NOOP
+    }
+    protected void stopImpl(final BundleContext context) {
+        // NOOP
+    }
+
+    @Override
+    public Collection<ConsumerFunctionality> getConsumerFunctionality() {
+        return Collections.emptySet();
+    }
+
+
+    @Override
+    public Broker addingService(final ServiceReference<Broker> reference) {
+        if(broker == null) {
+            broker = context.getService(reference);
+            broker.registerConsumer(this, context);
+            return broker;
+        }
+
+        return null;
+    }
+
+    @Override
+    public void modifiedService(final ServiceReference<Broker> reference, final Broker service) {
+        // NOOP
+    }
+
+    @Override
+    public void removedService(final ServiceReference<Broker> reference, final Broker service) {
+        stopImpl(context);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/AbstractProvider.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/AbstractProvider.java
new file mode 100644 (file)
index 0000000..e300279
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api;
+
+import java.util.Collection;
+import java.util.Collections;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+public abstract class AbstractProvider implements BundleActivator, Provider,ServiceTrackerCustomizer<Broker, Broker> {
+
+    private Broker broker;
+    private BundleContext context;
+    private ServiceTracker<Broker, Broker> tracker;
+
+    @Override
+    public Collection<ProviderFunctionality> getProviderFunctionality() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public final void start(final BundleContext context) throws Exception {
+        this.context = context;
+        this.startImpl(context);
+        tracker = new ServiceTracker<>(context, Broker.class, this);
+        tracker.open();
+    }
+
+    protected void startImpl(final BundleContext context) {
+        // NOOP
+    }
+    protected void stopImpl(final BundleContext context) {
+        // NOOP
+    }
+
+    @Override
+    public final void stop(final BundleContext context) throws Exception {
+        broker = null;
+        tracker.close();
+        tracker = null;
+        stopImpl(context);
+    }
+
+    @Override
+    public Broker addingService(final ServiceReference<Broker> reference) {
+        if(broker == null) {
+            broker = context.getService(reference);
+            broker.registerProvider(this, context);
+            return broker;
+        }
+
+        return null;
+    }
+
+    @Override
+    public void modifiedService(final ServiceReference<Broker> reference, final Broker service) {
+        // NOOP
+    }
+
+    @Override
+    public void removedService(final ServiceReference<Broker> reference, final Broker service) {
+        stopImpl(context);
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/Broker.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/Broker.java
new file mode 100644 (file)
index 0000000..500dce3
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api;
+
+import org.osgi.framework.BundleContext;
+
+/**
+ * Core component of the SAL layer responsible for wiring the SAL consumers.
+ *
+ * The responsibility of the broker is to maintain registration of SAL
+ * functionality {@link Consumer}s and {@link Provider}s, store provider and
+ * consumer specific context and functionality registration via
+ * {@link ConsumerSession} and provide access to infrastructure services, which
+ * removes direct dependencies between providers and consumers.
+ *
+ *
+ * The services are exposed via session.
+ *
+ * <h3>Session-based access</h3>
+ *
+ * The providers and consumers needs to register in order to use the
+ * binding-independent SAL layer and to expose functionality via SAL layer.
+ *
+ * For more information about session-based access see {@link ConsumerSession}
+ * and {@link ProviderSession}
+ *
+ *
+ *
+ */
+public interface Broker {
+
+    /**
+     * Registers the {@link Consumer}, which will use the SAL layer.
+     *
+     * <p>
+     * During the registration, the broker obtains the initial functionality
+     * from consumer, using the {@link Consumer#getConsumerFunctionality()}, and
+     * register that functionality into system and concrete infrastructure
+     * services.
+     *
+     * <p>
+     * Note that consumer could register additional functionality at later point
+     * by using service and functionality specific APIs.
+     *
+     * <p>
+     * The consumer is required to use returned session for all communication
+     * with broker or one of the broker services. The session is announced to
+     * the consumer by invoking
+     * {@link Consumer#onSessionInitiated(ConsumerSession)}.
+     *
+     * @param cons
+     *            Consumer to be registered.
+     * @return a session specific to consumer registration
+     * @throws IllegalArgumentException
+     *             If the consumer is <code>null</code>.
+     * @throws IllegalStateException
+     *             If the consumer is already registered.
+     */
+    ConsumerSession registerConsumer(Consumer cons);
+
+    /*
+     * @deprecated Use registerConsumer(Consumer cons) instead (BundleContext is no longer used)
+     */
+    @Deprecated
+    ConsumerSession registerConsumer(Consumer cons, BundleContext context);
+
+    /**
+     * Registers the {@link Provider}, which will use the SAL layer.
+     *
+     * <p>
+     * During the registration, the broker obtains the initial functionality
+     * from consumer, using the {@link Provider#getProviderFunctionality()}, and
+     * register that functionality into system and concrete infrastructure
+     * services.
+     * <p>
+     * The consumer is <b>required to use</b> returned session for all
+     * communication with broker or one of the broker services. The session is
+     * announced to the consumer by invoking
+     * {@link Provider#onSessionInitiated(ProviderSession)}.
+     *
+     *
+     * @param prov
+     *            Provider to be registered.
+     * @return a session unique to the provider registration.
+     * @throws IllegalArgumentException
+     *             If the provider is <code>null</code>.
+     * @throws IllegalStateException
+     *             If the consumer is already registered.
+     */
+    ProviderSession registerProvider(Provider prov);
+
+    /*
+     * @deprecated Use registerProvider(Provider cons) instead (BundleContext is no longer used)
+     */
+    @Deprecated
+    ProviderSession registerProvider(Provider prov, BundleContext context);
+
+    /**
+     * {@link Consumer} specific access to the SAL functionality.
+     *
+     * <p>
+     * ConsumerSession is {@link Consumer}-specific access to the SAL
+     * functionality and infrastructure services.
+     *
+     * <p>
+     * The session serves to store SAL context (e.g. registration of
+     * functionality) for the consumer and provides access to the SAL
+     * infrastructure services and other functionality provided by
+     * {@link Provider}s.
+     *
+     *
+     *
+     */
+    public interface ConsumerSession {
+
+        boolean isClosed();
+
+        /**
+         * Returns a session specific instance (implementation) of requested
+         * service
+         *
+         * @param service
+         *            Broker service
+         * @return Session specific implementation of service
+         */
+        <T extends BrokerService> T getService(Class<T> service);
+
+        /**
+         * Closes a session between consumer and broker.
+         *
+         * <p>
+         * The close operation unregisters a consumer and remove all registered
+         * functionality of the consumer from the system.
+         *
+         */
+        void close();
+    }
+
+    /**
+     * {@link Provider} specific access to the SAL functionality.
+     *
+     * <p>
+     * ProviderSession is {@link Provider}-specific access to the SAL
+     * functionality and infrastructure services, which also allows for exposing
+     * the provider's functionality to the other {@link Consumer}s.
+     *
+     * <p>
+     * The session serves to store SAL context (e.g. registration of
+     * functionality) for the providers and exposes access to the SAL
+     * infrastructure services, dynamic functionality registration and any other
+     * functionality provided by other {@link Provider}s.
+     *
+     */
+    public interface ProviderSession extends ConsumerSession {
+        /**
+         * Closes a session between provider and SAL.
+         *
+         * <p>
+         * The close operation unregisters a provider and remove all registered
+         * functionality of the provider from the system.
+         */
+        @Override
+        void close();
+
+        @Override
+        boolean isClosed();
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/BrokerService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/BrokerService.java
new file mode 100644 (file)
index 0000000..7c00c9c
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api;
+
+/**
+ *
+ * Session-specific instance of the broker functionality.
+ *
+ * <p>
+ * BrokerService is marker interface for infrastructure services provided by the
+ * SAL. These services are session-specific, each {@link Provider} and
+ * {@link Consumer} usually has own instance of the service with it's own
+ * context.
+ *
+ * <p>
+ * The consumer's (or provider's) instance of specific service could be obtained
+ * by invoking {@link org.opendaylight.controller.sal.core.api.Broker.ConsumerSession#getService(Class)} method on session
+ * assigned to the consumer.
+ *
+ * <p>
+ * {@link BrokerService} and {@link Provider} may seem similar, but provider
+ * provides YANG model-based functionality and {@link BrokerService} exposes the
+ * necessary supporting functionality to implement specific functionality of
+ * YANG and to reuse it in the development of {@link Consumer}s and
+ * {@link Provider}s.
+ *
+ *
+ */
+public interface BrokerService {
+
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/Consumer.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/Consumer.java
new file mode 100644 (file)
index 0000000..a5ab8ac
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api;
+
+import java.util.Collection;
+
+import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
+
+/**
+ *
+ * Defines the component of controller and supplies additional metadata. A
+ * component of the controller or application supplies a concrete implementation
+ * of this interface.
+ *
+ * A user-implemented component (application) which facilitates the SAL and SAL
+ * services to access infrastructure services or providers' functionality.
+ *
+ *
+ */
+public interface Consumer {
+
+    /**
+     * Callback signaling initialization of the consumer session to the SAL.
+     *
+     * The consumer MUST use the session for all communication with SAL or
+     * retrieving SAL infrastructure services.
+     *
+     * This method is invoked by {@link Broker#registerConsumer(Consumer)}
+     *
+     * @param session
+     *            Unique session between consumer and SAL.
+     */
+    public void onSessionInitiated(ConsumerSession session);
+
+    /**
+     * @deprecated - no longer used or needed
+     * *
+     * Suggested implementation until removed:
+     * @code {
+     * public Collection<ConsumerFunctionality> getConsumerFunctionality() {
+     *    return Collections.emptySet();
+     * }
+     * }
+     */
+    @Deprecated
+    public Collection<ConsumerFunctionality> getConsumerFunctionality();
+
+    /**
+     * @deprecated - no longer used or needed
+     */
+    @Deprecated
+    public interface ConsumerFunctionality {
+
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/Provider.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/Provider.java
new file mode 100644 (file)
index 0000000..4f32983
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api;
+
+import java.util.Collection;
+
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+
+/**
+ *
+ * Defines the component of controller and supplies additional metadata. A
+ * component of the controller or application supplies a concrete implementation
+ * of this interface.
+ *
+ * <p>
+ * A user-implemented component (application) which facilitates the SAL and SAL
+ * services to access infrastructure services and to provide functionality to
+ * {@link Consumer}s and other providers.
+ *
+ *
+ */
+public interface Provider {
+
+    /**
+     * Callback signaling initialization of the provider session to the SAL.
+     *
+     * <p>
+     * The provider <b>MUST use the session</b> for all communication with SAL
+     * or retrieving SAL infrastructure services.
+     *
+     * <p>
+     * This method is invoked by {@link Broker#registerConsumer(Consumer)}
+     *
+     * @param session
+     *            Unique session between provider and SAL.
+     */
+    public void onSessionInitiated(ProviderSession session);
+
+    /**
+     * @deprecated - No longer used or needed
+     *
+     * Suggested implementation until removed:
+     * @code {
+     * public Collection<ProviderFunctionality> getProviderFunctionality() {
+     *  return Collections.emptySet();
+     * }
+     * }
+     */
+    @Deprecated
+    public Collection<ProviderFunctionality> getProviderFunctionality();
+
+    /**
+     * @deprecated - no longer used or needed
+     */
+    @Deprecated
+    public interface ProviderFunctionality {
+
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/RpcImplementationUnavailableException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/RpcImplementationUnavailableException.java
new file mode 100644 (file)
index 0000000..45f1365
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api;
+
+/**
+ * Exception reported when no RPC implementation is found in the system.
+ *
+ * @deprecated Use {@link org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException} instead.
+ */
+@Deprecated
+public class RpcImplementationUnavailableException extends RuntimeException {
+    private static final long serialVersionUID = 1L;
+
+    public RpcImplementationUnavailableException(final String message) {
+        super(message);
+    }
+
+    public RpcImplementationUnavailableException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/RpcRoutingContext.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/RpcRoutingContext.java
new file mode 100644 (file)
index 0000000..66d290f
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api;
+
+import java.io.Serializable;
+
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public class RpcRoutingContext implements Immutable, Serializable {
+
+    /**
+     *
+     */
+    private static final long serialVersionUID = -9079324728075883325L;
+
+    private final QName context;
+    private final QName rpc;
+
+
+    private RpcRoutingContext(QName context, QName rpc) {
+        super();
+        this.context = context;
+        this.rpc = rpc;
+    }
+
+    public static final RpcRoutingContext create(QName context, QName rpc) {
+        return new RpcRoutingContext(context, rpc);
+    }
+
+    public QName getContext() {
+        return context;
+    }
+
+    public QName getRpc() {
+        return rpc;
+    }
+
+    @Override
+    public String toString() {
+        return "RpcRoutingContext [context=" + context + ", rpc=" + rpc + "]";
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((context == null) ? 0 : context.hashCode());
+        result = prime * result + ((rpc == null) ? 0 : rpc.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        RpcRoutingContext other = (RpcRoutingContext) obj;
+        if (context == null) {
+            if (other.context != null)
+                return false;
+        } else if (!context.equals(other.context))
+            return false;
+        if (rpc == null) {
+            if (other.rpc != null)
+                return false;
+        } else if (!rpc.equals(other.rpc))
+            return false;
+        return true;
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/model/SchemaService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/model/SchemaService.java
new file mode 100644 (file)
index 0000000..c3e979c
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api.model;
+
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+
+public interface SchemaService extends BrokerService {
+
+    /**
+     * Registers a YANG module to session and global context
+     *
+     * @param module
+     */
+    void addModule(Module module);
+
+    /**
+     * Unregisters a YANG module from session context
+     *
+     * @param module
+     */
+    void removeModule(Module module);
+
+    /**
+     * Returns session specific YANG schema context
+     * @return
+     */
+    SchemaContext getSessionContext();
+
+    /**
+     * Returns global schema context
+     *
+     * @return
+     */
+    SchemaContext getGlobalContext();
+
+    /**
+     * Register a listener for changes in schema context.
+     *
+     * @param listener Listener which should be registered
+     * @return Listener registration handle
+     */
+    ListenerRegistration<SchemaContextListener> registerSchemaContextListener(SchemaContextListener listener);
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountProvisionListener.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountProvisionListener.java
new file mode 100644 (file)
index 0000000..c4d0062
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.core.api.mount;
+
+import java.util.EventListener;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+public interface MountProvisionListener extends EventListener {
+
+    void onMountPointCreated(YangInstanceIdentifier path);
+
+    void onMountPointRemoved(YangInstanceIdentifier path);
+
+}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/package-info.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/package-info.java
new file mode 100644 (file)
index 0000000..395c09a
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+/**
+ * Core binding-independent SAL contracts and components
+ */
+package org.opendaylight.controller.sal.core.api;
diff --git a/opendaylight/md-sal/sal-dom-api/src/test/java/org/opendaylight/controller/md/sal/dom/api/AbstractDOMDataTreeServiceTestSuite.java b/opendaylight/md-sal/sal-dom-api/src/test/java/org/opendaylight/controller/md/sal/dom/api/AbstractDOMDataTreeServiceTestSuite.java
new file mode 100644 (file)
index 0000000..896c606
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import static org.junit.Assert.assertNotNull;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.net.URI;
+import java.util.Collections;
+import javax.annotation.Nonnull;
+import org.junit.Test;
+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.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+
+/**
+ * Abstract test suite demonstrating various access patterns on how a {@link DOMDataTreeService}
+ * can be used.
+ */
+public abstract class AbstractDOMDataTreeServiceTestSuite {
+    protected static final QNameModule TEST_MODULE = QNameModule.create(URI.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:store"), null);
+
+    protected static final YangInstanceIdentifier UNORDERED_CONTAINER_IID = YangInstanceIdentifier.create(
+        new NodeIdentifier(QName.create(TEST_MODULE, "lists")),
+        new NodeIdentifier(QName.create(TEST_MODULE, "unordered-container")));
+    protected static final DOMDataTreeIdentifier UNORDERED_CONTAINER_TREE = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, UNORDERED_CONTAINER_IID);
+
+    /**
+     * Return a reference to the service used in this test. The instance
+     * needs to be reused within the same test and must be isolated between
+     * tests.
+     *
+     * @return {@link DOMDataTreeService} instance.
+     */
+    protected abstract @Nonnull DOMDataTreeService service();
+
+    /**
+     * A simple unbound producer. It write some basic things into the data store based on the
+     * test model.
+     * @throws DOMDataTreeProducerException
+     * @throws TransactionCommitFailedException
+     */
+    @Test
+    public final void testBasicProducer() throws DOMDataTreeProducerException, TransactionCommitFailedException {
+        // Create a producer. It is an AutoCloseable resource, hence the try-with pattern
+        try (final DOMDataTreeProducer prod = service().createProducer(Collections.singleton(UNORDERED_CONTAINER_TREE))) {
+            assertNotNull(prod);
+
+            final DOMDataWriteTransaction tx = prod.createTransaction(true);
+            assertNotNull(tx);
+
+            tx.put(LogicalDatastoreType.OPERATIONAL, UNORDERED_CONTAINER_IID, ImmutableContainerNodeBuilder.create().build());
+
+            final CheckedFuture<Void, TransactionCommitFailedException> f = tx.submit();
+            assertNotNull(f);
+
+            f.checkedGet();
+        }
+    }
+
+    // TODO: simple listener
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/pom.xml b/opendaylight/md-sal/sal-dom-broker/pom.xml
new file mode 100644 (file)
index 0000000..17d45ff
--- /dev/null
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>sal-parent</artifactId>
+    <version>1.3.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>sal-broker-impl</artifactId>
+  <packaging>bundle</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.lmax</groupId>
+      <artifactId>disruptor</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-common-impl</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-common-util</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-core-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-core-spi</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-inmemory-datastore</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>util</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-data-impl</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-parser-impl</artifactId>
+    </dependency>
+
+    <dependency>
+        <groupId>org.opendaylight.yangtools.model</groupId>
+        <artifactId>ietf-yang-types</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>mockito-configuration</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-simple</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+            <Bundle-Activator>org.opendaylight.controller.sal.dom.broker.osgi.SchemaServiceActivator</Bundle-Activator>
+            <Export-Package>
+                            <!--  Legacy code -->
+                            org.opendaylight.controller.sal.dom.broker,
+                            org.opendaylight.controller.sal.dom.broker.impl,
+                            org.opendaylight.controller.sal.dom.broker.osgi,
+                            org.opendaylight.controller.sal.dom.broker.util,
+                            org.opendaylight.controller.sal.dom.broker.spi,
+                            <!--sal.broker.impl is exported for sal-netconf-connector to use SchemaAwareRpcRegistry.-->
+                            <!-- TODO Remove sal.broker.impl from export when SchemaAwareRpcRegistry is not used in connector anymore -->
+                            org.opendaylight.controller.md.sal.dom.broker.impl,
+                            org.opendaylight.controller.md.sal.dom.broker.impl.*,
+            </Export-Package>
+            <Import-Package>*</Import-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+      <!-- TODO - unite yang-maven-plugin configuration in md-sal-->
+      <plugin>
+        <groupId>org.opendaylight.yangtools</groupId>
+        <artifactId>yang-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>generate-sources</goal>
+            </goals>
+            <configuration>
+              <codeGenerators>
+                <generator>
+                  <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
+                  <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+                  <additionalConfiguration>
+                    <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
+                  </additionalConfiguration>
+                </generator>
+                <generator>
+                  <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+                  <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+                </generator>
+                <generator>
+                  <codeGeneratorClass>org.opendaylight.yangtools.yang.unified.doc.generator.maven.DocumentationGeneratorImpl</codeGeneratorClass>
+                  <outputBaseDir>target/site/models</outputBaseDir>
+                </generator>
+              </codeGenerators>
+              <inspectDependencies>true</inspectDependencies>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+  <scm>
+    <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+    <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+    <tag>HEAD</tag>
+    <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+  </scm>
+</project>
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMDataBroker.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMDataBroker.java
new file mode 100644 (file)
index 0000000..a5034cb
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import static com.google.common.base.Preconditions.checkState;
+import com.google.common.collect.ImmutableMap;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicLong;
+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.DOMDataTreeChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTreeChangePublisher;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractDOMDataBroker extends AbstractDOMForwardedTransactionFactory<DOMStore> implements DOMDataBroker, AutoCloseable {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractDOMDataBroker.class);
+
+    private final AtomicLong txNum = new AtomicLong();
+    private final AtomicLong chainNum = new AtomicLong();
+    private final Map<Class<? extends DOMDataBrokerExtension>, DOMDataBrokerExtension> extensions;
+    private volatile AutoCloseable closeable;
+
+    protected AbstractDOMDataBroker(final Map<LogicalDatastoreType, DOMStore> datastores) {
+        super(datastores);
+
+        boolean treeChange = true;
+        for (DOMStore ds : datastores.values()) {
+            if (!(ds instanceof DOMStoreTreeChangePublisher)) {
+                treeChange = false;
+                break;
+            }
+        }
+
+        if (treeChange) {
+            extensions = ImmutableMap.<Class<? extends DOMDataBrokerExtension>, DOMDataBrokerExtension>of(DOMDataTreeChangeService.class, new DOMDataTreeChangeService() {
+                @Override
+                public <L extends DOMDataTreeChangeListener> ListenerRegistration<L> registerDataTreeChangeListener(final DOMDataTreeIdentifier treeId, final L listener) {
+                    DOMStore publisher = getTxFactories().get(treeId.getDatastoreType());
+                    checkState(publisher != null, "Requested logical data store is not available.");
+
+                    return ((DOMStoreTreeChangePublisher)publisher).registerTreeChangeListener(treeId.getRootIdentifier(), listener);
+                }
+            });
+        } else {
+            extensions = Collections.emptyMap();
+        }
+    }
+
+    public void setCloseable(final AutoCloseable closeable) {
+        this.closeable = closeable;
+    }
+
+    @Override
+    public void close() {
+        super.close();
+
+        if(closeable != null) {
+            try {
+                closeable.close();
+            } catch(Exception e) {
+                LOG.debug("Error closing instance", e);
+            }
+        }
+    }
+
+    @Override
+    protected Object newTransactionIdentifier() {
+        return "DOM-" + txNum.getAndIncrement();
+    }
+
+    @Override
+    public ListenerRegistration<DOMDataChangeListener> registerDataChangeListener(final LogicalDatastoreType store,
+            final YangInstanceIdentifier path, final DOMDataChangeListener listener, final DataChangeScope triggeringScope) {
+
+        DOMStore potentialStore = getTxFactories().get(store);
+        checkState(potentialStore != null, "Requested logical data store is not available.");
+        return potentialStore.registerChangeListener(path, listener, triggeringScope);
+    }
+
+    @Override
+    public Map<Class<? extends DOMDataBrokerExtension>, DOMDataBrokerExtension> getSupportedExtensions() {
+        return extensions;
+    }
+
+    @Override
+    public DOMTransactionChain createTransactionChain(final TransactionChainListener listener) {
+        checkNotClosed();
+
+        final Map<LogicalDatastoreType, DOMStoreTransactionChain> backingChains = new EnumMap<>(LogicalDatastoreType.class);
+        for (Entry<LogicalDatastoreType, DOMStore> entry : getTxFactories().entrySet()) {
+            backingChains.put(entry.getKey(), entry.getValue().createTransactionChain());
+        }
+
+        final long chainId = chainNum.getAndIncrement();
+        LOG.debug("Transactoin chain {} created with listener {}, backing store chains {}", chainId, listener,
+                backingChains);
+        return new DOMDataBrokerTransactionChainImpl(chainId, backingChains, this, listener);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedCompositeTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedCompositeTransaction.java
new file mode 100644 (file)
index 0000000..b186d10
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.Collection;
+import java.util.Map;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransaction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Composite DOM Transaction backed by {@link DOMStoreTransaction}.
+ *
+ * Abstract base for composite transaction, which provides access only to common
+ * functionality as retrieval of subtransaction, close method and retrieval of
+ * identifier.
+ *
+ * @param <K>
+ *            Subtransaction distinguisher
+ * @param <T>
+ *            Subtransaction type
+ */
+abstract class AbstractDOMForwardedCompositeTransaction<K, T extends DOMStoreTransaction> implements
+        AsyncTransaction<YangInstanceIdentifier, NormalizedNode<?, ?>> {
+
+    private final Map<K, T> backingTxs;
+    private final Object identifier;
+
+    /**
+     *
+     * Creates new composite Transactions.
+     *
+     * @param identifier
+     *            Identifier of transaction.
+     * @param backingTxs
+     *            Key,value map of backing transactions.
+     */
+    protected AbstractDOMForwardedCompositeTransaction(final Object identifier, final Map<K, T> backingTxs) {
+        this.identifier = Preconditions.checkNotNull(identifier, "Identifier should not be null");
+        this.backingTxs = Preconditions.checkNotNull(backingTxs, "Backing transactions should not be null");
+    }
+
+    /**
+     * Returns subtransaction associated with supplied key.
+     *
+     * @param key
+     * @return
+     * @throws NullPointerException
+     *             if key is null
+     * @throws IllegalArgumentException
+     *             if no subtransaction is associated with key.
+     */
+    protected final T getSubtransaction(final K key) {
+        Preconditions.checkNotNull(key, "key must not be null.");
+
+        final T ret = backingTxs.get(key);
+        Preconditions.checkArgument(ret != null, "No subtransaction associated with %s", key);
+        return ret;
+    }
+
+    /**
+     * Returns immutable Iterable of all subtransactions.
+     *
+     */
+    protected Collection<T> getSubtransactions() {
+        return backingTxs.values();
+    }
+
+    @Override
+    public Object getIdentifier() {
+        return identifier;
+    }
+
+    protected void closeSubtransactions() {
+        /*
+         * We share one exception for all failures, which are added
+         * as supressedExceptions to it.
+         */
+        IllegalStateException failure = null;
+        for (T subtransaction : backingTxs.values()) {
+            try {
+                subtransaction.close();
+            } catch (Exception e) {
+                // If we did not allocated failure we allocate it
+                if (failure == null) {
+                    failure = new IllegalStateException("Uncaught exception occured during closing transaction", e);
+                } else {
+                    // We update it with additional exceptions, which occurred during error.
+                    failure.addSuppressed(e);
+                }
+            }
+        }
+        // If we have failure, we throw it at after all attempts to close.
+        if (failure != null) {
+            throw failure;
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedTransactionFactory.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedTransactionFactory.java
new file mode 100644 (file)
index 0000000..8ee928e
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+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.DOMDataReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionFactory;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+
+/**
+ *
+ * Abstract composite transaction factory.
+ *
+ * Provides an convenience common implementation for composite DOM Transactions,
+ * where subtransaction is identified by {@link LogicalDatastoreType} type and
+ * implementation of subtransaction is provided by
+ * {@link DOMStoreTransactionFactory}.
+ *
+ * <b>Note:</b>This class does not have thread-safe implementation of  {@link #close()},
+ *   implementation may allow accessing and allocating new transactions during closing
+ *   this instance.
+ *
+ * @param <T>
+ *            Type of {@link DOMStoreTransactionFactory} factory.
+ */
+abstract class AbstractDOMForwardedTransactionFactory<T extends DOMStoreTransactionFactory> implements AutoCloseable {
+    @SuppressWarnings("rawtypes")
+    private static final AtomicIntegerFieldUpdater<AbstractDOMForwardedTransactionFactory> UPDATER =
+            AtomicIntegerFieldUpdater.newUpdater(AbstractDOMForwardedTransactionFactory.class, "closed");
+    private final Map<LogicalDatastoreType, T> storeTxFactories;
+    private volatile int closed = 0;
+
+    protected AbstractDOMForwardedTransactionFactory(final Map<LogicalDatastoreType, ? extends T> txFactories) {
+        this.storeTxFactories = new EnumMap<>(txFactories);
+    }
+
+    /**
+     * Implementations must return unique identifier for each and every call of
+     * this method;
+     *
+     * @return new Unique transaction identifier.
+     */
+    protected abstract Object newTransactionIdentifier();
+
+    /**
+     * User-supplied implementation of {@link DOMDataWriteTransaction#submit()}
+     * for transaction.
+     *
+     * Callback invoked when {@link DOMDataWriteTransaction#submit()} is invoked
+     * on transaction created by this factory.
+     *
+     * @param transaction
+     *            Transaction on which {@link DOMDataWriteTransaction#commit()}
+     *            was invoked.
+     * @param cohorts
+     *            Iteratable of cohorts for subtransactions associated with
+     *            the transaction being committed.
+     * @return a CheckedFuture. if commit coordination on cohorts finished successfully,
+     *         nothing is returned from the Future, On failure,
+     *         the Future fails with a {@link TransactionCommitFailedException}.
+     */
+    protected abstract CheckedFuture<Void,TransactionCommitFailedException> submit(final DOMDataWriteTransaction transaction,
+            final Collection<DOMStoreThreePhaseCommitCohort> cohorts);
+
+    /**
+     * Creates a new composite read-only transaction
+     *
+     * Creates a new composite read-only transaction backed by one transaction
+     * per factory in {@link #getTxFactories()}.
+     *
+     * Subtransaction for reading is selected by supplied
+     * {@link LogicalDatastoreType} as parameter for
+     * {@link DOMDataReadOnlyTransaction#read(LogicalDatastoreType,org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)}
+     * .
+     *
+     * Id of returned transaction is retrieved via
+     * {@link #newTransactionIdentifier()}.
+     *
+     * @return New composite read-only transaction.
+     */
+    public final DOMDataReadOnlyTransaction newReadOnlyTransaction() {
+        checkNotClosed();
+
+        final Map<LogicalDatastoreType, DOMStoreReadTransaction> txns = new EnumMap<>(LogicalDatastoreType.class);
+        for (Entry<LogicalDatastoreType, T> store : storeTxFactories.entrySet()) {
+            txns.put(store.getKey(), store.getValue().newReadOnlyTransaction());
+        }
+        return new DOMForwardedReadOnlyTransaction(newTransactionIdentifier(), txns);
+    }
+
+    /**
+     * Creates a new composite write-only transaction
+     *
+     * <p>
+     * Creates a new composite write-only transaction backed by one write-only
+     * transaction per factory in {@link #getTxFactories()}.
+     *
+     * <p>
+     * Implementation of composite Write-only transaction is following:
+     *
+     * <ul>
+     * <li>
+     * {@link DOMDataWriteTransaction#put(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+     * - backing subtransaction is selected by {@link LogicalDatastoreType},
+     * {@link DOMStoreWriteTransaction#write(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+     * is invoked on selected subtransaction.
+     * <li>
+     * {@link DOMDataWriteTransaction#merge(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+     * - backing subtransaction is selected by {@link LogicalDatastoreType},
+     * {@link DOMStoreWriteTransaction#merge(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+     * is invoked on selected subtransaction.
+     * <li>
+     * {@link DOMDataWriteTransaction#delete(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)
+     * - backing subtransaction is selected by {@link LogicalDatastoreType},
+     * {@link DOMStoreWriteTransaction#delete(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)} is invoked on
+     * selected subtransaction.
+     * <li> {@link DOMDataWriteTransaction#commit()} - results in invoking
+     * {@link DOMStoreWriteTransaction#ready()}, gathering all resulting cohorts
+     * and then invoking finalized implementation callback
+     * {@link #submit(DOMDataWriteTransaction, Iterable)} with transaction which
+     * was commited and gathered results.
+     * </ul>
+     *
+     * Id of returned transaction is generated via
+     * {@link #newTransactionIdentifier()}.
+     *
+     * @return New composite write-only transaction associated with this
+     *         factory.
+     */
+    public final DOMDataWriteTransaction newWriteOnlyTransaction() {
+        checkNotClosed();
+
+        final Map<LogicalDatastoreType, DOMStoreWriteTransaction> txns = new EnumMap<>(LogicalDatastoreType.class);
+        for (Entry<LogicalDatastoreType, T> store : storeTxFactories.entrySet()) {
+            txns.put(store.getKey(), store.getValue().newWriteOnlyTransaction());
+        }
+        return new DOMForwardedWriteTransaction<DOMStoreWriteTransaction>(newTransactionIdentifier(), txns, this);
+    }
+
+    /**
+     * Creates a new composite write-only transaction
+     *
+     * <p>
+     * Creates a new composite write-only transaction backed by one write-only
+     * transaction per factory in {@link #getTxFactories()}.
+     * <p>
+     * Implementation of composite Write-only transaction is following:
+     *
+     * <ul>
+     * <li>
+     * {@link DOMDataWriteTransaction#read(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)}
+     * - backing subtransaction is selected by {@link LogicalDatastoreType},
+     * {@link DOMStoreWriteTransaction#read(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)} is invoked on
+     * selected subtransaction.
+     * <li>
+     * {@link DOMDataWriteTransaction#put(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+     * - backing subtransaction is selected by {@link LogicalDatastoreType},
+     * {@link DOMStoreWriteTransaction#write(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+     * is invoked on selected subtransaction.
+     * <li>
+     * {@link DOMDataWriteTransaction#merge(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+     * - backing subtransaction is selected by {@link LogicalDatastoreType},
+     * {@link DOMStoreWriteTransaction#merge(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+     * is invoked on selected subtransaction.
+     * <li>
+     * {@link DOMDataWriteTransaction#delete(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)
+     * - backing subtransaction is selected by {@link LogicalDatastoreType},
+     * {@link DOMStoreWriteTransaction#delete(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)} is invoked on
+     * selected subtransaction.
+     * <li> {@link DOMDataWriteTransaction#commit()} - results in invoking
+     * {@link DOMStoreWriteTransaction#ready()}, gathering all resulting cohorts
+     * and then invoking finalized implementation callback
+     * {@link #submit(DOMDataWriteTransaction, Iterable)} with transaction which
+     * was commited and gathered results.
+     * <li>
+     * </ul>
+     *
+     * Id of returned transaction is generated via
+     * {@link #newTransactionIdentifier()}.
+     *
+     * @return New composite read-write transaction associated with this
+     *         factory.
+     */
+    public final DOMDataReadWriteTransaction newReadWriteTransaction() {
+        checkNotClosed();
+
+        final Map<LogicalDatastoreType, DOMStoreReadWriteTransaction> txns = new EnumMap<>(LogicalDatastoreType.class);
+        for (Entry<LogicalDatastoreType, T> store : storeTxFactories.entrySet()) {
+            txns.put(store.getKey(), store.getValue().newReadWriteTransaction());
+        }
+        return new DOMForwardedReadWriteTransaction(newTransactionIdentifier(), txns, this);
+    }
+
+    /**
+     * Convenience accessor of backing factories intended to be used only by
+     * finalization of this class.
+     *
+     * <b>Note:</b>
+     * Finalization of this class may want to access other functionality of
+     * supplied Transaction factories.
+     *
+     * @return Map of backing transaction factories.
+     */
+    protected final Map<LogicalDatastoreType, T> getTxFactories() {
+        return storeTxFactories;
+    }
+
+    /**
+     * Checks if instance is not closed.
+     *
+     * @throws IllegalStateException If instance of this class was closed.
+     *
+     */
+    protected final void checkNotClosed() {
+        Preconditions.checkState(closed == 0, "Transaction factory was closed. No further operations allowed.");
+    }
+
+    @Override
+    public void close() {
+        final boolean success = UPDATER.compareAndSet(this, 0, 1);
+        Preconditions.checkState(success, "Transaction factory was already closed");
+    }
+}
+
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMRpcRoutingTableEntry.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMRpcRoutingTableEntry.java
new file mode 100644 (file)
index 0000000..08c4384
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+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;
+
+abstract class AbstractDOMRpcRoutingTableEntry {
+    private final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls;
+    private final SchemaPath schemaPath;
+
+    protected AbstractDOMRpcRoutingTableEntry(final SchemaPath schemaPath, final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls) {
+        this.schemaPath = Preconditions.checkNotNull(schemaPath);
+        this.impls = Preconditions.checkNotNull(impls);
+    }
+
+    protected final SchemaPath getSchemaPath() {
+        return schemaPath;
+    }
+
+    protected final List<DOMRpcImplementation> getImplementations(final YangInstanceIdentifier context) {
+        return impls.get(context);
+    }
+
+    final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> getImplementations() {
+        return impls;
+    }
+
+    public boolean containsContext(final YangInstanceIdentifier contextReference) {
+        return impls.containsKey(contextReference);
+    }
+
+    final Set<YangInstanceIdentifier> registeredIdentifiers() {
+        return impls.keySet();
+    }
+
+    /**
+     *
+     * @param implementation
+     * @param newRpcs List of new RPCs, must be mutable
+     * @return
+     */
+    final AbstractDOMRpcRoutingTableEntry add(final DOMRpcImplementation implementation, final List<YangInstanceIdentifier> newRpcs) {
+        final Builder<YangInstanceIdentifier, List<DOMRpcImplementation>> vb = ImmutableMap.builder();
+        for (final Entry<YangInstanceIdentifier, List<DOMRpcImplementation>> ve : impls.entrySet()) {
+            if (newRpcs.remove(ve.getKey())) {
+                final ArrayList<DOMRpcImplementation> i = new ArrayList<>(ve.getValue().size() + 1);
+                i.addAll(ve.getValue());
+                i.add(implementation);
+                vb.put(ve.getKey(), i);
+            } else {
+                vb.put(ve);
+            }
+        }
+        for(final YangInstanceIdentifier ii : newRpcs) {
+            final ArrayList<DOMRpcImplementation> impl = new ArrayList<>(1);
+            impl.add(implementation);
+            vb.put(ii,impl);
+        }
+
+        return newInstance(vb.build());
+    }
+
+    final AbstractDOMRpcRoutingTableEntry remove(final DOMRpcImplementation implementation, final List<YangInstanceIdentifier> removed) {
+        final Builder<YangInstanceIdentifier, List<DOMRpcImplementation>> vb = ImmutableMap.builder();
+        for (final Entry<YangInstanceIdentifier, List<DOMRpcImplementation>> ve : impls.entrySet()) {
+            if (removed.remove(ve.getKey())) {
+                final ArrayList<DOMRpcImplementation> i = new ArrayList<>(ve.getValue());
+                i.remove(implementation);
+                // We could trimToSize(), but that may perform another copy just to get rid
+                // of a single element. That is probably not worth the trouble.
+                if (!i.isEmpty()) {
+                    vb.put(ve.getKey(), i);
+                }
+            } else {
+                vb.put(ve);
+            }
+        }
+
+        final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> v = vb.build();
+        return v.isEmpty() ? null : newInstance(v);
+    }
+
+    protected abstract CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final NormalizedNode<?, ?> input);
+    protected abstract AbstractDOMRpcRoutingTableEntry newInstance(final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls);
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/CommitCoordinationTask.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/CommitCoordinationTask.java
new file mode 100644 (file)
index 0000000..da7a0d1
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Collection;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.yangtools.util.DurationStatisticsTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of blocking three-phase commit-coordination tasks without
+ * support of cancellation.
+ */
+final class CommitCoordinationTask implements Callable<Void> {
+    private static enum Phase {
+        canCommit,
+        preCommit,
+        doCommit,
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(CommitCoordinationTask.class);
+    private final Collection<DOMStoreThreePhaseCommitCohort> cohorts;
+    private final DurationStatisticsTracker commitStatTracker;
+    private final DOMDataWriteTransaction tx;
+
+    public CommitCoordinationTask(final DOMDataWriteTransaction transaction,
+            final Collection<DOMStoreThreePhaseCommitCohort> cohorts,
+            final DurationStatisticsTracker commitStatTracker) {
+        this.tx = Preconditions.checkNotNull(transaction, "transaction must not be null");
+        this.cohorts = Preconditions.checkNotNull(cohorts, "cohorts must not be null");
+        this.commitStatTracker = commitStatTracker;
+    }
+
+    @Override
+    public Void call() throws TransactionCommitFailedException {
+        final long startTime = commitStatTracker != null ? System.nanoTime() : 0;
+
+        Phase phase = Phase.canCommit;
+
+        try {
+            LOG.debug("Transaction {}: canCommit Started", tx.getIdentifier());
+            canCommitBlocking();
+
+            phase = Phase.preCommit;
+            LOG.debug("Transaction {}: preCommit Started", tx.getIdentifier());
+            preCommitBlocking();
+
+            phase = Phase.doCommit;
+            LOG.debug("Transaction {}: doCommit Started", tx.getIdentifier());
+            commitBlocking();
+
+            LOG.debug("Transaction {}: doCommit completed", tx.getIdentifier());
+            return null;
+        } catch (final TransactionCommitFailedException e) {
+            LOG.warn("Tx: {} Error during phase {}, starting Abort", tx.getIdentifier(), phase, e);
+            abortBlocking(e);
+            throw e;
+        } finally {
+            if (commitStatTracker != null) {
+                commitStatTracker.addDuration(System.nanoTime() - startTime);
+            }
+        }
+    }
+
+    /**
+     *
+     * Invokes canCommit on underlying cohorts and blocks till
+     * all results are returned.
+     *
+     * Valid state transition is from SUBMITTED to CAN_COMMIT,
+     * if currentPhase is not SUBMITTED throws IllegalStateException.
+     *
+     * @throws TransactionCommitFailedException
+     *             If one of cohorts failed can Commit
+     *
+     */
+    private void canCommitBlocking() throws TransactionCommitFailedException {
+        for (final ListenableFuture<?> canCommit : canCommitAll()) {
+            try {
+                final Boolean result = (Boolean)canCommit.get();
+                if (result == null || !result) {
+                    throw new TransactionCommitFailedException("Can Commit failed, no detailed cause available.");
+                }
+            } catch (InterruptedException | ExecutionException e) {
+                throw TransactionCommitFailedExceptionMapper.CAN_COMMIT_ERROR_MAPPER.apply(e);
+            }
+        }
+    }
+
+    /**
+     *
+     * Invokes canCommit on underlying cohorts and returns composite future
+     * which will contains {@link Boolean#TRUE} only and only if
+     * all cohorts returned true.
+     *
+     * Valid state transition is from SUBMITTED to CAN_COMMIT,
+     * if currentPhase is not SUBMITTED throws IllegalStateException.
+     *
+     * @return List of all cohorts futures from can commit phase.
+     *
+     */
+    private ListenableFuture<?>[] canCommitAll() {
+        final ListenableFuture<?>[] ops = new ListenableFuture<?>[cohorts.size()];
+        int i = 0;
+        for (final DOMStoreThreePhaseCommitCohort cohort : cohorts) {
+            ops[i++] = cohort.canCommit();
+        }
+        return ops;
+    }
+
+    /**
+     *
+     * Invokes preCommit on underlying cohorts and blocks till
+     * all results are returned.
+     *
+     * Valid state transition is from CAN_COMMIT to PRE_COMMIT, if current
+     * state is not CAN_COMMIT
+     * throws IllegalStateException.
+     *
+     * @throws TransactionCommitFailedException
+     *             If one of cohorts failed preCommit
+     *
+     */
+    private void preCommitBlocking() throws TransactionCommitFailedException {
+        final ListenableFuture<?>[] preCommitFutures = preCommitAll();
+        try {
+            for(final ListenableFuture<?> future : preCommitFutures) {
+                future.get();
+            }
+        } catch (InterruptedException | ExecutionException e) {
+            throw TransactionCommitFailedExceptionMapper.PRE_COMMIT_MAPPER.apply(e);
+        }
+    }
+
+    /**
+     *
+     * Invokes preCommit on underlying cohorts and returns future
+     * which will complete once all preCommit on cohorts completed or
+     * failed.
+     *
+     *
+     * Valid state transition is from CAN_COMMIT to PRE_COMMIT, if current
+     * state is not CAN_COMMIT
+     * throws IllegalStateException.
+     *
+     * @return List of all cohorts futures from can commit phase.
+     *
+     */
+    private ListenableFuture<?>[] preCommitAll() {
+        final ListenableFuture<?>[] ops = new ListenableFuture<?>[cohorts.size()];
+        int i = 0;
+        for (final DOMStoreThreePhaseCommitCohort cohort : cohorts) {
+            ops[i++] = cohort.preCommit();
+        }
+        return ops;
+    }
+
+    /**
+     *
+     * Invokes commit on underlying cohorts and blocks till
+     * all results are returned.
+     *
+     * Valid state transition is from PRE_COMMIT to COMMIT, if not throws
+     * IllegalStateException.
+     *
+     * @throws TransactionCommitFailedException
+     *             If one of cohorts failed preCommit
+     *
+     */
+    private void commitBlocking() throws TransactionCommitFailedException {
+        final ListenableFuture<?>[] commitFutures = commitAll();
+        try {
+            for(final ListenableFuture<?> future : commitFutures) {
+                future.get();
+            }
+        } catch (InterruptedException | ExecutionException e) {
+            throw TransactionCommitFailedExceptionMapper.COMMIT_ERROR_MAPPER.apply(e);
+        }
+    }
+
+    /**
+     *
+     * Invokes commit on underlying cohorts and returns future which
+     * completes
+     * once all commits on cohorts are completed.
+     *
+     * Valid state transition is from PRE_COMMIT to COMMIT, if not throws
+     * IllegalStateException
+     *
+     * @return List of all cohorts futures from can commit phase.
+     */
+    private ListenableFuture<?>[] commitAll() {
+        final ListenableFuture<?>[] ops = new ListenableFuture<?>[cohorts.size()];
+        int i = 0;
+        for (final DOMStoreThreePhaseCommitCohort cohort : cohorts) {
+            ops[i++] = cohort.commit();
+        }
+        return ops;
+    }
+
+    /**
+     * Aborts transaction.
+     *
+     * Invokes {@link DOMStoreThreePhaseCommitCohort#abort()} on all
+     * cohorts, blocks
+     * for all results. If any of the abort failed throws
+     * IllegalStateException,
+     * which will contains originalCause as suppressed Exception.
+     *
+     * If aborts we're successful throws supplied exception
+     *
+     * @param originalCause
+     *            Exception which should be used to fail transaction for
+     *            consumers of transaction
+     *            future and listeners of transaction failure.
+     * @param phase phase in which the problem ensued
+     * @throws TransactionCommitFailedException
+     *             on invocation of this method.
+     *             originalCa
+     * @throws IllegalStateException
+     *             if abort failed.
+     */
+    private void abortBlocking(final TransactionCommitFailedException originalCause) throws TransactionCommitFailedException {
+        Exception cause = originalCause;
+        try {
+            abortAsyncAll().get();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Tx: {} Error during Abort.", tx.getIdentifier(), e);
+            cause = new IllegalStateException("Abort failed.", e);
+            cause.addSuppressed(e);
+        }
+        Throwables.propagateIfPossible(cause, TransactionCommitFailedException.class);
+    }
+
+    /**
+     * Invokes abort on underlying cohorts and returns future which
+     * completes once all abort on cohorts are completed.
+     *
+     * @return Future which will complete once all cohorts completed
+     *         abort.
+     */
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    private ListenableFuture<Void> abortAsyncAll() {
+
+        final ListenableFuture<?>[] ops = new ListenableFuture<?>[cohorts.size()];
+        int i = 0;
+        for (final DOMStoreThreePhaseCommitCohort cohort : cohorts) {
+            ops[i++] = cohort.abort();
+        }
+
+        /*
+         * We are returning all futures as list, not only succeeded ones in
+         * order to fail composite future if any of them failed.
+         * See Futures.allAsList for this description.
+         */
+        return (ListenableFuture) Futures.allAsList(ops);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerTransactionChainImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerTransactionChainImpl.java
new file mode 100644 (file)
index 0000000..fd216ec
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+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.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * NormalizedNode implementation of {@link org.opendaylight.controller.md.sal.common.api.data.TransactionChain} which is backed
+ * by several {@link DOMStoreTransactionChain} differentiated by provided
+ * {@link org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType} type.
+ *
+ */
+final class DOMDataBrokerTransactionChainImpl extends AbstractDOMForwardedTransactionFactory<DOMStoreTransactionChain>
+        implements DOMTransactionChain {
+    private static enum State {
+        RUNNING,
+        CLOSING,
+        CLOSED,
+        FAILED,
+    }
+
+    private static final AtomicIntegerFieldUpdater<DOMDataBrokerTransactionChainImpl> COUNTER_UPDATER =
+            AtomicIntegerFieldUpdater.newUpdater(DOMDataBrokerTransactionChainImpl.class, "counter");
+    private static final AtomicReferenceFieldUpdater<DOMDataBrokerTransactionChainImpl, State> STATE_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(DOMDataBrokerTransactionChainImpl.class, State.class, "state");
+    private static final Logger LOG = LoggerFactory.getLogger(DOMDataBrokerTransactionChainImpl.class);
+    private final AtomicLong txNum = new AtomicLong();
+    private final AbstractDOMDataBroker broker;
+    private final TransactionChainListener listener;
+    private final long chainId;
+
+    private volatile State state = State.RUNNING;
+    private volatile int counter = 0;
+
+    /**
+     *
+     * @param chainId
+     *            ID of transaction chain
+     * @param chains
+     *            Backing {@link DOMStoreTransactionChain}s.
+     * @param coordinator
+     *            Commit Coordinator which should be used to coordinate commits
+     *            of transaction
+     *            produced by this chain.
+     * @param listener
+     *            Listener, which listens on transaction chain events.
+     * @throws NullPointerException
+     *             If any of arguments is null.
+     */
+    public DOMDataBrokerTransactionChainImpl(final long chainId,
+            final Map<LogicalDatastoreType, DOMStoreTransactionChain> chains,
+            final AbstractDOMDataBroker broker, final TransactionChainListener listener) {
+        super(chains);
+        this.chainId = chainId;
+        this.broker = Preconditions.checkNotNull(broker);
+        this.listener = Preconditions.checkNotNull(listener);
+    }
+
+    private void checkNotFailed() {
+        Preconditions.checkState(state != State.FAILED, "Transaction chain has failed");
+    }
+
+    @Override
+    protected Object newTransactionIdentifier() {
+        return "DOM-CHAIN-" + chainId + "-" + txNum.getAndIncrement();
+    }
+
+    @Override
+    public CheckedFuture<Void, TransactionCommitFailedException> submit(
+            final DOMDataWriteTransaction transaction, final Collection<DOMStoreThreePhaseCommitCohort> cohorts) {
+        checkNotFailed();
+        checkNotClosed();
+
+        final CheckedFuture<Void, TransactionCommitFailedException> ret = broker.submit(transaction, cohorts);
+
+        COUNTER_UPDATER.incrementAndGet(this);
+        Futures.addCallback(ret, new FutureCallback<Void>() {
+            @Override
+            public void onSuccess(final Void result) {
+                transactionCompleted();
+            }
+
+            @Override
+            public void onFailure(final Throwable t) {
+                transactionFailed(transaction, t);
+            }
+        });
+
+        return ret;
+    }
+
+    @Override
+    public void close() {
+        final boolean success = STATE_UPDATER.compareAndSet(this, State.RUNNING, State.CLOSING);
+        if (!success) {
+            LOG.debug("Chain {} is no longer running", this);
+            return;
+        }
+
+        super.close();
+        for (DOMStoreTransactionChain subChain : getTxFactories().values()) {
+            subChain.close();
+        }
+
+        if (counter == 0) {
+            finishClose();
+        }
+    }
+
+    private void finishClose() {
+        state = State.CLOSED;
+        listener.onTransactionChainSuccessful(this);
+    }
+
+    private void transactionCompleted() {
+        if (COUNTER_UPDATER.decrementAndGet(this) == 0 && state == State.CLOSING) {
+            finishClose();
+        }
+    }
+
+    private void transactionFailed(final DOMDataWriteTransaction tx, final Throwable cause) {
+        state = State.FAILED;
+        LOG.debug("Transaction chain {} failed.", this, cause);
+        listener.onTransactionChainFailed(this, tx, cause);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadOnlyTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadOnlyTransaction.java
new file mode 100644 (file)
index 0000000..124bf9f
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.Map;
+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.controller.sal.core.spi.data.DOMStoreReadTransaction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Read Only Transaction, which is composed of several
+ * {@link DOMStoreReadTransaction} transactions. Subtransaction is selected by
+ * {@link LogicalDatastoreType} type parameter in
+ * {@link #read(LogicalDatastoreType, YangInstanceIdentifier)}.
+ */
+class DOMForwardedReadOnlyTransaction extends
+        AbstractDOMForwardedCompositeTransaction<LogicalDatastoreType, DOMStoreReadTransaction> implements
+        DOMDataReadOnlyTransaction {
+
+    protected DOMForwardedReadOnlyTransaction(final Object identifier,
+            final Map<LogicalDatastoreType, DOMStoreReadTransaction> backingTxs) {
+        super(identifier, backingTxs);
+    }
+
+    @Override
+    public CheckedFuture<Optional<NormalizedNode<?,?>>, ReadFailedException> read(
+            final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+        return getSubtransaction(store).read(path);
+    }
+
+    @Override
+    public CheckedFuture<Boolean, ReadFailedException> exists(
+        final LogicalDatastoreType store,
+        final YangInstanceIdentifier path) {
+        return getSubtransaction(store).exists(path);
+    }
+
+    @Override
+    public void close() {
+        closeSubtransactions();
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadWriteTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadWriteTransaction.java
new file mode 100644 (file)
index 0000000..f56a49b
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.Map;
+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.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ *
+ * Read-Write Transaction, which is composed of several
+ * {@link DOMStoreReadWriteTransaction} transactions. Subtransaction is selected by
+ * {@link LogicalDatastoreType} type parameter in:
+ *
+ * <ul>
+ * <li>{@link #read(LogicalDatastoreType, YangInstanceIdentifier)}
+ * <li>{@link #put(LogicalDatastoreType, YangInstanceIdentifier, NormalizedNode)}
+ * <li>{@link #delete(LogicalDatastoreType, YangInstanceIdentifier)}
+ * <li>{@link #merge(LogicalDatastoreType, YangInstanceIdentifier, NormalizedNode)}
+ * </ul>
+ * {@link #commit()} will result in invocation of
+ * {@link DOMDataCommitImplementation#submit(org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction, Iterable)}
+ * invocation with all {@link org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort} for underlying
+ * transactions.
+ *
+ */
+final class DOMForwardedReadWriteTransaction extends DOMForwardedWriteTransaction<DOMStoreReadWriteTransaction> implements DOMDataReadWriteTransaction {
+    protected DOMForwardedReadWriteTransaction(final Object identifier,
+            final Map<LogicalDatastoreType, DOMStoreReadWriteTransaction> backingTxs,
+            final AbstractDOMForwardedTransactionFactory<?> commitImpl) {
+        super(identifier, backingTxs, commitImpl);
+    }
+
+    @Override
+    public CheckedFuture<Optional<NormalizedNode<?,?>>, ReadFailedException> read(
+            final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+        return getSubtransaction(store).read(path);
+    }
+
+    @Override
+    public CheckedFuture<Boolean, ReadFailedException> exists(
+        final LogicalDatastoreType store,
+        final YangInstanceIdentifier path) {
+        return getSubtransaction(store).exists(path);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedWriteTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedWriteTransaction.java
new file mode 100644 (file)
index 0000000..bc11f12
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+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 java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+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.common.impl.service.AbstractDataTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Read-Write Transaction, which is composed of several
+ * {@link DOMStoreWriteTransaction} transactions. A sub-transaction is selected by
+ * {@link LogicalDatastoreType} type parameter in:
+ *
+ * <ul>
+ * <li>{@link #put(LogicalDatastoreType, YangInstanceIdentifier, NormalizedNode)}
+ * <li>{@link #delete(LogicalDatastoreType, YangInstanceIdentifier)}
+ * <li>{@link #merge(LogicalDatastoreType, YangInstanceIdentifier, NormalizedNode)}
+ * </ul>
+ * <p>
+ * {@link #commit()} will result in invocation of
+ * {@link DOMDataCommitImplementation#submit(org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction, Iterable)}
+ * invocation with all {@link org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort} for underlying
+ * transactions.
+ *
+ * @param <T> Subtype of {@link DOMStoreWriteTransaction} which is used as
+ *            subtransaction.
+ */
+class DOMForwardedWriteTransaction<T extends DOMStoreWriteTransaction> extends
+        AbstractDOMForwardedCompositeTransaction<LogicalDatastoreType, T> implements DOMDataWriteTransaction {
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<DOMForwardedWriteTransaction, AbstractDOMForwardedTransactionFactory> IMPL_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(DOMForwardedWriteTransaction.class, AbstractDOMForwardedTransactionFactory.class, "commitImpl");
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<DOMForwardedWriteTransaction, Future> FUTURE_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(DOMForwardedWriteTransaction.class, Future.class, "commitFuture");
+    private static final Logger LOG = LoggerFactory.getLogger(DOMForwardedWriteTransaction.class);
+    private static final Future<?> CANCELLED_FUTURE = Futures.immediateCancelledFuture();
+
+    /**
+     * Implementation of real commit. It also acts as an indication that
+     * the transaction is running -- which we flip atomically using
+     * {@link #IMPL_UPDATER}.
+     */
+    private volatile AbstractDOMForwardedTransactionFactory<?> commitImpl;
+
+    /**
+     * Future task of transaction commit. It starts off as null, but is
+     * set appropriately on {@link #submit()} and {@link #cancel()} via
+     * {@link AtomicReferenceFieldUpdater#lazySet(Object, Object)}.
+     *
+     * Lazy set is safe for use because it is only referenced to in the
+     * {@link #cancel()} slow path, where we will busy-wait for it. The
+     * fast path gets the benefit of a store-store barrier instead of the
+     * usual store-load barrier.
+     */
+    private volatile Future<?> commitFuture;
+
+    protected DOMForwardedWriteTransaction(final Object identifier,
+            final Map<LogicalDatastoreType, T> backingTxs, final AbstractDOMForwardedTransactionFactory<?> commitImpl) {
+        super(identifier, backingTxs);
+        this.commitImpl = Preconditions.checkNotNull(commitImpl, "commitImpl must not be null.");
+    }
+
+    @Override
+    public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+        checkRunning(commitImpl);
+        getSubtransaction(store).write(path, data);
+    }
+
+    @Override
+    public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+        checkRunning(commitImpl);
+        getSubtransaction(store).delete(path);
+    }
+
+    @Override
+    public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+        checkRunning(commitImpl);
+        getSubtransaction(store).merge(path, data);
+    }
+
+    @Override
+    public boolean cancel() {
+        final AbstractDOMForwardedTransactionFactory<?> impl = IMPL_UPDATER.getAndSet(this, null);
+        if (impl != null) {
+            LOG.trace("Transaction {} cancelled before submit", getIdentifier());
+            FUTURE_UPDATER.lazySet(this, CANCELLED_FUTURE);
+            closeSubtransactions();
+            return true;
+        }
+
+        // The transaction is in process of being submitted or cancelled. Busy-wait
+        // for the corresponding future.
+        Future<?> future;
+        do {
+            future = commitFuture;
+        } while (future == null);
+
+        return future.cancel(false);
+    }
+
+    @Deprecated
+    @Override
+    public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+        return AbstractDataTransaction.convertToLegacyCommitFuture(submit());
+    }
+
+    @Override
+    public CheckedFuture<Void, TransactionCommitFailedException> submit() {
+        final AbstractDOMForwardedTransactionFactory<?> impl = IMPL_UPDATER.getAndSet(this, null);
+        checkRunning(impl);
+
+        final Collection<T> txns = getSubtransactions();
+        final Collection<DOMStoreThreePhaseCommitCohort> cohorts = new ArrayList<>(txns.size());
+
+        // FIXME: deal with errors thrown by backed (ready and submit can fail in theory)
+        for (DOMStoreWriteTransaction txn : txns) {
+            cohorts.add(txn.ready());
+        }
+
+        final CheckedFuture<Void, TransactionCommitFailedException> ret = impl.submit(this, cohorts);
+        FUTURE_UPDATER.lazySet(this, ret);
+        return ret;
+    }
+
+    private void checkRunning(final AbstractDOMForwardedTransactionFactory<?> impl) {
+        Preconditions.checkState(impl != null, "Transaction %s is no longer running", getIdentifier());
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMNotificationRouter.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMNotificationRouter.java
new file mode 100644 (file)
index 0000000..138de6b
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableMultimap.Builder;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.lmax.disruptor.EventHandler;
+import com.lmax.disruptor.InsufficientCapacityException;
+import com.lmax.disruptor.PhasedBackoffWaitStrategy;
+import com.lmax.disruptor.WaitStrategy;
+import com.lmax.disruptor.dsl.Disruptor;
+import com.lmax.disruptor.dsl.ProducerType;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
+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.spi.DOMNotificationSubscriptionListener;
+import org.opendaylight.controller.md.sal.dom.spi.DOMNotificationSubscriptionListenerRegistry;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Joint implementation of {@link DOMNotificationPublishService} and {@link DOMNotificationService}. Provides
+ * routing of notifications from publishers to subscribers.
+ *
+ * Internal implementation works by allocating a two-handler Disruptor. The first handler delivers notifications
+ * to subscribed listeners and the second one notifies whoever may be listening on the returned future. Registration
+ * state tracking is performed by a simple immutable multimap -- when a registration or unregistration occurs we
+ * re-generate the entire map from scratch and set it atomically. While registrations/unregistrations synchronize
+ * on this instance, notifications do not take any locks here.
+ *
+ * The fully-blocking {@link #publish(long, DOMNotification, Collection)} and non-blocking {@link #offerNotification(DOMNotification)}
+ * are realized using the Disruptor's native operations. The bounded-blocking {@link #offerNotification(DOMNotification, long, TimeUnit)}
+ * is realized by arming a background wakeup interrupt.
+ */
+public final class DOMNotificationRouter implements AutoCloseable, DOMNotificationPublishService,
+        DOMNotificationService, DOMNotificationSubscriptionListenerRegistry {
+
+    private static final Logger LOG = LoggerFactory.getLogger(DOMNotificationRouter.class);
+    private static final ListenableFuture<Void> NO_LISTENERS = Futures.immediateFuture(null);
+    private static final WaitStrategy DEFAULT_STRATEGY = PhasedBackoffWaitStrategy.withLock(1L, 30L, TimeUnit.MILLISECONDS);
+    private static final EventHandler<DOMNotificationRouterEvent> DISPATCH_NOTIFICATIONS = new EventHandler<DOMNotificationRouterEvent>() {
+        @Override
+        public void onEvent(final DOMNotificationRouterEvent event, final long sequence, final boolean endOfBatch) throws Exception {
+            event.deliverNotification();
+
+        }
+    };
+    private static final EventHandler<DOMNotificationRouterEvent> NOTIFY_FUTURE = new EventHandler<DOMNotificationRouterEvent>() {
+        @Override
+        public void onEvent(final DOMNotificationRouterEvent event, final long sequence, final boolean endOfBatch) {
+            event.setFuture();
+        }
+    };
+
+    private final Disruptor<DOMNotificationRouterEvent> disruptor;
+    private final ExecutorService executor;
+    private volatile Multimap<SchemaPath, ListenerRegistration<? extends DOMNotificationListener>> listeners = ImmutableMultimap.of();
+    private final ListenerRegistry<DOMNotificationSubscriptionListener> subscriptionListeners = ListenerRegistry.create();
+
+    @SuppressWarnings("unchecked")
+    private DOMNotificationRouter(final ExecutorService executor, final int queueDepth, final WaitStrategy strategy) {
+        this.executor = Preconditions.checkNotNull(executor);
+
+        disruptor = new Disruptor<>(DOMNotificationRouterEvent.FACTORY, queueDepth, executor, ProducerType.MULTI, strategy);
+        disruptor.handleEventsWith(DISPATCH_NOTIFICATIONS);
+        disruptor.after(DISPATCH_NOTIFICATIONS).handleEventsWith(NOTIFY_FUTURE);
+        disruptor.start();
+    }
+
+    public static DOMNotificationRouter create(final int queueDepth) {
+        final ExecutorService executor = Executors.newCachedThreadPool();
+
+        return new DOMNotificationRouter(executor, queueDepth, DEFAULT_STRATEGY);
+    }
+
+    public static DOMNotificationRouter create(final int queueDepth, final long spinTime, final long parkTime, final TimeUnit unit) {
+        final ExecutorService executor = Executors.newCachedThreadPool();
+        final WaitStrategy strategy = PhasedBackoffWaitStrategy.withLock(spinTime, parkTime, unit);
+
+        return new DOMNotificationRouter(executor, queueDepth, strategy);
+    }
+
+    @Override
+    public synchronized <T extends DOMNotificationListener> ListenerRegistration<T> registerNotificationListener(final T listener, final Collection<SchemaPath> types) {
+        final ListenerRegistration<T> reg = new AbstractListenerRegistration<T>(listener) {
+            @Override
+            protected void removeRegistration() {
+                final ListenerRegistration<T> me = this;
+
+                synchronized (DOMNotificationRouter.this) {
+                    replaceListeners(ImmutableMultimap.copyOf(Multimaps.filterValues(listeners, new Predicate<ListenerRegistration<? extends DOMNotificationListener>>() {
+                        @Override
+                        public boolean apply(final ListenerRegistration<? extends DOMNotificationListener> input) {
+                            return input != me;
+                        }
+                    })));
+                }
+            }
+        };
+
+        if (!types.isEmpty()) {
+            final Builder<SchemaPath, ListenerRegistration<? extends DOMNotificationListener>> b = ImmutableMultimap.builder();
+            b.putAll(listeners);
+
+            for (final SchemaPath t : types) {
+                b.put(t, reg);
+            }
+
+            replaceListeners(b.build());
+        }
+
+        return reg;
+    }
+
+    @Override
+    public <T extends DOMNotificationListener> ListenerRegistration<T> registerNotificationListener(final T listener, final SchemaPath... types) {
+        return registerNotificationListener(listener, Arrays.asList(types));
+    }
+
+    /**
+     * Swaps registered listeners and triggers notification update
+     *
+     * @param newListeners
+     */
+    private void replaceListeners(
+            final Multimap<SchemaPath, ListenerRegistration<? extends DOMNotificationListener>> newListeners) {
+        listeners = newListeners;
+        notifyListenerTypesChanged(newListeners.keySet());
+    }
+
+    private void notifyListenerTypesChanged(final Set<SchemaPath> typesAfter) {
+        final List<ListenerRegistration<DOMNotificationSubscriptionListener>> listenersAfter =ImmutableList.copyOf(subscriptionListeners.getListeners());
+        executor.submit(new Runnable() {
+
+            @Override
+            public void run() {
+                for (final ListenerRegistration<DOMNotificationSubscriptionListener> subListener : listenersAfter) {
+                    try {
+                        subListener.getInstance().onSubscriptionChanged(typesAfter);
+                    } catch (final Exception e) {
+                        LOG.warn("Uncaught exception during invoking listener {}", subListener.getInstance(), e);
+                    }
+                }
+            }
+        });
+    }
+
+    @Override
+    public <L extends DOMNotificationSubscriptionListener> ListenerRegistration<L> registerSubscriptionListener(
+            final L listener) {
+        final Set<SchemaPath> initialTypes = listeners.keySet();
+        executor.submit(new Runnable() {
+
+            @Override
+            public void run() {
+                listener.onSubscriptionChanged(initialTypes);
+            }
+        });
+        return subscriptionListeners.registerWithType(listener);
+    }
+
+    private ListenableFuture<Void> publish(final long seq, final DOMNotification notification, final Collection<ListenerRegistration<? extends DOMNotificationListener>> subscribers) {
+        final DOMNotificationRouterEvent event = disruptor.get(seq);
+        final ListenableFuture<Void> future = event.initialize(notification, subscribers);
+        disruptor.getRingBuffer().publish(seq);
+        return future;
+    }
+
+    @Override
+    public ListenableFuture<? extends Object> putNotification(final DOMNotification notification) throws InterruptedException {
+        final Collection<ListenerRegistration<? extends DOMNotificationListener>> subscribers = listeners.get(notification.getType());
+        if (subscribers.isEmpty()) {
+            return NO_LISTENERS;
+        }
+
+        final long seq = disruptor.getRingBuffer().next();
+        return publish(seq, notification, subscribers);
+    }
+
+    private ListenableFuture<? extends Object> tryPublish(final DOMNotification notification, final Collection<ListenerRegistration<? extends DOMNotificationListener>> subscribers) {
+        final long seq;
+        try {
+             seq = disruptor.getRingBuffer().tryNext();
+        } catch (final InsufficientCapacityException e) {
+            return DOMNotificationPublishService.REJECTED;
+        }
+
+        return publish(seq, notification, subscribers);
+    }
+
+    @Override
+    public ListenableFuture<? extends Object> offerNotification(final DOMNotification notification) {
+        final Collection<ListenerRegistration<? extends DOMNotificationListener>> subscribers = listeners.get(notification.getType());
+        if (subscribers.isEmpty()) {
+            return NO_LISTENERS;
+        }
+
+        return tryPublish(notification, subscribers);
+    }
+
+    @Override
+    public ListenableFuture<? extends Object> offerNotification(final DOMNotification notification, final long timeout,
+            final TimeUnit unit) throws InterruptedException {
+        final Collection<ListenerRegistration<? extends DOMNotificationListener>> subscribers = listeners.get(notification.getType());
+        if (subscribers.isEmpty()) {
+            return NO_LISTENERS;
+        }
+
+        // Attempt to perform a non-blocking publish first
+        final ListenableFuture<? extends Object> noBlock = tryPublish(notification, subscribers);
+        if (!DOMNotificationPublishService.REJECTED.equals(noBlock)) {
+            return noBlock;
+        }
+
+        /*
+         * FIXME: we need a background thread, which will watch out for blocking too long. Here
+         *        we will arm a tasklet for it and synchronize delivery of interrupt properly.
+         */
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    @Override
+    public void close() {
+        disruptor.shutdown();
+        executor.shutdown();
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMNotificationRouterEvent.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMNotificationRouterEvent.java
new file mode 100644 (file)
index 0000000..65c7166
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import com.lmax.disruptor.EventFactory;
+import java.util.Collection;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * A single notification event in the disruptor ringbuffer. These objects are reused,
+ * so they do have mutable state.
+ */
+final class DOMNotificationRouterEvent {
+    public static final EventFactory<DOMNotificationRouterEvent> FACTORY = new EventFactory<DOMNotificationRouterEvent>() {
+        @Override
+        public DOMNotificationRouterEvent newInstance() {
+            return new DOMNotificationRouterEvent();
+        }
+    };
+
+    private Collection<ListenerRegistration<? extends DOMNotificationListener>> subscribers;
+    private DOMNotification notification;
+    private SettableFuture<Void> future;
+
+    private DOMNotificationRouterEvent() {
+        // Hidden on purpose, initialized in initialize()
+    }
+
+    ListenableFuture<Void> initialize(final DOMNotification notification, final Collection<ListenerRegistration<? extends DOMNotificationListener>> subscribers) {
+        this.notification = Preconditions.checkNotNull(notification);
+        this.subscribers = Preconditions.checkNotNull(subscribers);
+        this.future = SettableFuture.create();
+        return this.future;
+    }
+
+    void deliverNotification() {
+        for (ListenerRegistration<? extends DOMNotificationListener> r : subscribers) {
+            final DOMNotificationListener l = r.getInstance();
+            if (l != null) {
+                l.onNotification(notification);
+            }
+        }
+    }
+
+    void setFuture() {
+        future.set(null);
+    }
+
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMRpcRouter.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMRpcRouter.java
new file mode 100644 (file)
index 0000000..14e75e2
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import javax.annotation.concurrent.GuardedBy;
+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.DOMRpcIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationRegistration;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcProviderService;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.md.sal.dom.spi.AbstractDOMRpcImplementationRegistration;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+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.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+public final class DOMRpcRouter implements AutoCloseable, DOMRpcService, DOMRpcProviderService, SchemaContextListener {
+    private static final ThreadFactory THREAD_FACTORY = new ThreadFactoryBuilder().setNameFormat("DOMRpcRouter-listener-%s").setDaemon(true).build();
+    private final ExecutorService listenerNotifier = Executors.newSingleThreadExecutor(THREAD_FACTORY);
+    @GuardedBy("this")
+    private Collection<ListenerRegistration<? extends DOMRpcAvailabilityListener>> listeners = Collections.emptyList();
+    private volatile DOMRpcRoutingTable routingTable = DOMRpcRoutingTable.EMPTY;
+
+    @Override
+    public <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(final T implementation, final DOMRpcIdentifier... rpcs) {
+        return registerRpcImplementation(implementation, ImmutableSet.copyOf(rpcs));
+    }
+
+    private static Collection<DOMRpcIdentifier> notPresentRpcs(final DOMRpcRoutingTable table, final Collection<DOMRpcIdentifier> candidates) {
+        return ImmutableSet.copyOf(Collections2.filter(candidates, new Predicate<DOMRpcIdentifier>() {
+            @Override
+            public boolean apply(final DOMRpcIdentifier input) {
+                return !table.contains(input);
+            }
+        }));
+    }
+
+    private synchronized void removeRpcImplementation(final DOMRpcImplementation implementation, final Set<DOMRpcIdentifier> rpcs) {
+        final DOMRpcRoutingTable oldTable = routingTable;
+        final DOMRpcRoutingTable newTable = oldTable.remove(implementation, rpcs);
+
+        final Collection<DOMRpcIdentifier> removedRpcs = notPresentRpcs(newTable, rpcs);
+        routingTable = newTable;
+        if(!removedRpcs.isEmpty()) {
+            final Collection<ListenerRegistration<? extends DOMRpcAvailabilityListener>> capturedListeners = listeners;
+            listenerNotifier.execute(new Runnable() {
+                @Override
+                public void run() {
+                    for (final ListenerRegistration<? extends DOMRpcAvailabilityListener> l : capturedListeners) {
+                        // Need to ensure removed listeners do not get notified
+                        synchronized (DOMRpcRouter.this) {
+                            if (listeners.contains(l)) {
+                                l.getInstance().onRpcUnavailable(removedRpcs);
+                            }
+                        }
+                    }
+                }
+            });
+        }
+    }
+
+    @Override
+    public synchronized <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(final T implementation, final Set<DOMRpcIdentifier> rpcs) {
+        final DOMRpcRoutingTable oldTable = routingTable;
+        final DOMRpcRoutingTable newTable = oldTable.add(implementation, rpcs);
+
+        final Collection<DOMRpcIdentifier> addedRpcs = notPresentRpcs(oldTable, rpcs);
+        routingTable = newTable;
+
+        if(!addedRpcs.isEmpty()) {
+            final Collection<ListenerRegistration<? extends DOMRpcAvailabilityListener>> capturedListeners = listeners;
+            listenerNotifier.execute(new Runnable() {
+                @Override
+                public void run() {
+                    for (final ListenerRegistration<? extends DOMRpcAvailabilityListener> l : capturedListeners) {
+                        // Need to ensure removed listeners do not get notified
+                        synchronized (DOMRpcRouter.this) {
+                            if (listeners.contains(l)) {
+                                l.getInstance().onRpcAvailable(addedRpcs);
+                            }
+                        }
+                    }
+                }
+            });
+        }
+
+        return new AbstractDOMRpcImplementationRegistration<T>(implementation) {
+            @Override
+            protected void removeRegistration() {
+                removeRpcImplementation(getInstance(), rpcs);
+            }
+        };
+    }
+
+    @Override
+    public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final SchemaPath type, final NormalizedNode<?, ?> input) {
+        return routingTable.invokeRpc(type, input);
+    }
+
+    private synchronized void removeListener(final ListenerRegistration<? extends DOMRpcAvailabilityListener> reg) {
+        listeners = ImmutableList.copyOf(Collections2.filter(listeners, new Predicate<Object>() {
+            @Override
+            public boolean apply(final Object input) {
+                return !reg.equals(input);
+            }
+        }));
+    }
+
+    @Override
+    public synchronized <T extends DOMRpcAvailabilityListener> ListenerRegistration<T> registerRpcListener(final T listener) {
+        final ListenerRegistration<T> ret = new AbstractListenerRegistration<T>(listener) {
+            @Override
+            protected void removeRegistration() {
+                removeListener(this);
+            }
+        };
+
+        final Builder<ListenerRegistration<? extends DOMRpcAvailabilityListener>> b = ImmutableList.builder();
+        b.addAll(listeners);
+        b.add(ret);
+        listeners = b.build();
+        final Map<SchemaPath, Set<YangInstanceIdentifier>> capturedRpcs = routingTable.getRpcs();
+
+        listenerNotifier.execute(new Runnable() {
+            @Override
+            public void run() {
+                for (final Entry<SchemaPath, Set<YangInstanceIdentifier>> e : capturedRpcs.entrySet()) {
+                    listener.onRpcAvailable(Collections2.transform(e.getValue(), new Function<YangInstanceIdentifier, DOMRpcIdentifier>() {
+                        @Override
+                        public DOMRpcIdentifier apply(final YangInstanceIdentifier input) {
+                            return DOMRpcIdentifier.create(e.getKey(), input);
+                        }
+                    }));
+                }
+            }
+        });
+
+        return ret;
+    }
+
+    @Override
+    public synchronized void onGlobalContextUpdated(final SchemaContext context) {
+        final DOMRpcRoutingTable oldTable = routingTable;
+        final DOMRpcRoutingTable newTable = oldTable.setSchemaContext(context);
+        routingTable = newTable;
+    }
+
+    @Override
+    public void close() {
+        listenerNotifier.shutdown();
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMRpcRoutingTable.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMRpcRoutingTable.java
new file mode 100644 (file)
index 0000000..7c070fd
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+final class DOMRpcRoutingTable {
+    private static final QName CONTEXT_REFERENCE = QName.cachedReference(QName.create("urn:opendaylight:yang:extension:yang-ext", "2013-07-09", "context-reference"));
+
+    static final DOMRpcRoutingTable EMPTY = new DOMRpcRoutingTable();
+    private static final Function<AbstractDOMRpcRoutingTableEntry, Set<YangInstanceIdentifier>> EXTRACT_IDENTIFIERS =
+            new Function<AbstractDOMRpcRoutingTableEntry, Set<YangInstanceIdentifier>>() {
+                @Override
+                public Set<YangInstanceIdentifier> apply(final AbstractDOMRpcRoutingTableEntry input) {
+                    return input.registeredIdentifiers();
+                }
+    };
+    private final Map<SchemaPath, AbstractDOMRpcRoutingTableEntry> rpcs;
+    private final SchemaContext schemaContext;
+
+    private DOMRpcRoutingTable() {
+        rpcs = Collections.emptyMap();
+        schemaContext = null;
+    }
+
+    private DOMRpcRoutingTable(final Map<SchemaPath, AbstractDOMRpcRoutingTableEntry> rpcs, final SchemaContext schemaContext) {
+        this.rpcs = Preconditions.checkNotNull(rpcs);
+        this.schemaContext = schemaContext;
+    }
+
+    private static ListMultimap<SchemaPath, YangInstanceIdentifier> decomposeIdentifiers(final Set<DOMRpcIdentifier> rpcs) {
+        final ListMultimap<SchemaPath, YangInstanceIdentifier> ret = LinkedListMultimap.create();
+        for (DOMRpcIdentifier i : rpcs) {
+            ret.put(i.getType(), i.getContextReference());
+        }
+        return ret;
+    }
+
+    DOMRpcRoutingTable add(final DOMRpcImplementation implementation, final Set<DOMRpcIdentifier> rpcs) {
+        if (rpcs.isEmpty()) {
+            return this;
+        }
+
+        // First decompose the identifiers to a multimap
+        final ListMultimap<SchemaPath, YangInstanceIdentifier> toAdd = decomposeIdentifiers(rpcs);
+
+        // Now iterate over existing entries, modifying them as appropriate...
+        final Builder<SchemaPath, AbstractDOMRpcRoutingTableEntry> mb = ImmutableMap.builder();
+        for (Entry<SchemaPath, AbstractDOMRpcRoutingTableEntry> re : this.rpcs.entrySet()) {
+            List<YangInstanceIdentifier> newRpcs = new ArrayList<>(toAdd.removeAll(re.getKey()));
+            if (!newRpcs.isEmpty()) {
+                final AbstractDOMRpcRoutingTableEntry ne = re.getValue().add(implementation, newRpcs);
+                mb.put(re.getKey(), ne);
+            } else {
+                mb.put(re);
+            }
+        }
+
+        // Finally add whatever is left in the decomposed multimap
+        for (Entry<SchemaPath, Collection<YangInstanceIdentifier>> e : toAdd.asMap().entrySet()) {
+            final Builder<YangInstanceIdentifier, List<DOMRpcImplementation>> vb = ImmutableMap.builder();
+            final List<DOMRpcImplementation> v = Collections.singletonList(implementation);
+            for (YangInstanceIdentifier i : e.getValue()) {
+                vb.put(i, v);
+            }
+
+            mb.put(e.getKey(), createRpcEntry(schemaContext, e.getKey(), vb.build()));
+        }
+
+        return new DOMRpcRoutingTable(mb.build(), schemaContext);
+    }
+
+    DOMRpcRoutingTable remove(final DOMRpcImplementation implementation, final Set<DOMRpcIdentifier> rpcs) {
+        if (rpcs.isEmpty()) {
+            return this;
+        }
+
+        // First decompose the identifiers to a multimap
+        final ListMultimap<SchemaPath, YangInstanceIdentifier> toRemove = decomposeIdentifiers(rpcs);
+
+        // Now iterate over existing entries, modifying them as appropriate...
+        final Builder<SchemaPath, AbstractDOMRpcRoutingTableEntry> b = ImmutableMap.builder();
+        for (Entry<SchemaPath, AbstractDOMRpcRoutingTableEntry> e : this.rpcs.entrySet()) {
+            final List<YangInstanceIdentifier> removed = new ArrayList<>(toRemove.removeAll(e.getKey()));
+            if (!removed.isEmpty()) {
+                final AbstractDOMRpcRoutingTableEntry ne = e.getValue().remove(implementation, removed);
+                if (ne != null) {
+                    b.put(e.getKey(), ne);
+                }
+            } else {
+                b.put(e);
+            }
+        }
+
+        // All done, whatever is in toRemove, was not there in the first place
+        return new DOMRpcRoutingTable(b.build(), schemaContext);
+    }
+
+    boolean contains(final DOMRpcIdentifier input) {
+        final AbstractDOMRpcRoutingTableEntry contexts = rpcs.get(input.getType());
+        return contexts != null && contexts.containsContext(input.getContextReference());
+    }
+
+    Map<SchemaPath, Set<YangInstanceIdentifier>> getRpcs() {
+        return Maps.transformValues(rpcs, EXTRACT_IDENTIFIERS);
+    }
+
+    private static RpcDefinition findRpcDefinition(final SchemaContext context, final SchemaPath schemaPath) {
+        if (context != null) {
+            final QName qname = schemaPath.getPathFromRoot().iterator().next();
+            final Module module = context.findModuleByNamespaceAndRevision(qname.getNamespace(), qname.getRevision());
+            if (module != null && module.getRpcs() != null) {
+                for (RpcDefinition rpc : module.getRpcs()) {
+                    if (qname.equals(rpc.getQName())) {
+                        return rpc;
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
+    private static AbstractDOMRpcRoutingTableEntry createRpcEntry(final SchemaContext context, final SchemaPath key, final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> implementations) {
+        final RpcDefinition rpcDef = findRpcDefinition(context, key);
+        if (rpcDef != null) {
+            final ContainerSchemaNode input = rpcDef.getInput();
+            if (input != null) {
+                for (DataSchemaNode c : input.getChildNodes()) {
+                    for (UnknownSchemaNode extension : c.getUnknownSchemaNodes()) {
+                        if (CONTEXT_REFERENCE.equals(extension.getNodeType())) {
+                            final YangInstanceIdentifier keyId = YangInstanceIdentifier.builder().node(c.getQName()).build();
+                            return new RoutedDOMRpcRoutingTableEntry(rpcDef, keyId, implementations);
+                        }
+                    }
+                }
+            }
+
+            return new GlobalDOMRpcRoutingTableEntry(rpcDef, implementations);
+        } else {
+            return new UnknownDOMRpcRoutingTableEntry(key, implementations);
+        }
+    }
+
+    CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final SchemaPath type, final NormalizedNode<?, ?> input) {
+        final AbstractDOMRpcRoutingTableEntry entry = rpcs.get(type);
+        if (entry == null) {
+            return Futures.<DOMRpcResult, DOMRpcException>immediateFailedCheckedFuture(new DOMRpcImplementationNotAvailableException("No implementation of RPC %s available", type));
+        }
+
+        return entry.invokeRpc(input);
+    }
+
+    DOMRpcRoutingTable setSchemaContext(final SchemaContext context) {
+        final Builder<SchemaPath, AbstractDOMRpcRoutingTableEntry> b = ImmutableMap.builder();
+
+        for (Entry<SchemaPath, AbstractDOMRpcRoutingTableEntry> e : rpcs.entrySet()) {
+            b.put(e.getKey(), createRpcEntry(context, e.getKey(), e.getValue().getImplementations()));
+        }
+
+        return new DOMRpcRoutingTable(b.build(), context);
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/GlobalDOMRpcRoutingTableEntry.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/GlobalDOMRpcRoutingTableEntry.java
new file mode 100644 (file)
index 0000000..18234f3
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.List;
+import java.util.Map;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+
+final class GlobalDOMRpcRoutingTableEntry extends AbstractDOMRpcRoutingTableEntry {
+    private static final YangInstanceIdentifier ROOT = YangInstanceIdentifier.builder().build();
+    private final DOMRpcIdentifier rpcId;
+
+    private GlobalDOMRpcRoutingTableEntry(final DOMRpcIdentifier rpcId, final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls) {
+        super(rpcId.getType(), impls);
+        this.rpcId = Preconditions.checkNotNull(rpcId);
+    }
+
+    // We do not need the RpcDefinition, but this makes sure we do not
+    // forward something we don't know to be an RPC.
+    GlobalDOMRpcRoutingTableEntry(final RpcDefinition def, final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls) {
+        super(def.getPath(), impls);
+        this.rpcId = DOMRpcIdentifier.create(def.getPath());
+    }
+
+    @Override
+    protected CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final NormalizedNode<?, ?> input) {
+        return getImplementations(ROOT).get(0).invokeRpc(rpcId, input);
+    }
+
+    @Override
+    protected GlobalDOMRpcRoutingTableEntry newInstance(final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls) {
+        return new GlobalDOMRpcRoutingTableEntry(rpcId, impls);
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongDataBroker.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongDataBroker.java
new file mode 100644 (file)
index 0000000..55f8a15
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import javax.annotation.Nonnull;
+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.DOMDataTreeChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.controller.md.sal.dom.spi.ForwardingDOMDataBroker;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * An implementation of a {@link DOMDataBroker}, which forwards most requests to a delegate.
+ *
+ * Its interpretation of the API contract is somewhat looser, specifically it does not
+ * guarantee transaction ordering between transactions allocated directly from the broker
+ * and its transaction chains.
+ */
+public final class PingPongDataBroker extends ForwardingDOMDataBroker implements AutoCloseable, DOMDataTreeChangeService {
+    private final DOMDataBroker delegate;
+
+    /**
+     * Instantiate a new broker, backed by the the specified delegate
+     * {@link DOMDataBroker}.
+     *
+     * @param delegate Backend broker, may not be null.
+     */
+    public PingPongDataBroker(@Nonnull final DOMDataBroker delegate) {
+        this.delegate = Preconditions.checkNotNull(delegate);
+    }
+
+    @Override
+    protected DOMDataBroker delegate() {
+        return delegate;
+    }
+
+    @Override
+    public PingPongTransactionChain createTransactionChain(final TransactionChainListener listener) {
+        return new PingPongTransactionChain(delegate, listener);
+    }
+
+    @Override
+    public void close() {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public <L extends DOMDataTreeChangeListener> ListenerRegistration<L> registerDataTreeChangeListener(final DOMDataTreeIdentifier treeId, final L listener) {
+        if (delegate instanceof DOMDataTreeChangeService) {
+            return ((DOMDataTreeChangeService)delegate).registerDataTreeChangeListener(treeId, listener);
+        }
+
+        throw new UnsupportedOperationException("Delegate " + delegate + " does not support required functionality");
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongFuture.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongFuture.java
new file mode 100644 (file)
index 0000000..68ef760
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.util.concurrent.AbstractCheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+
+/**
+ * A {@link java.util.concurrent.Future} used to report the status of an future
+ * {@link java.util.concurrent.Future}.
+ */
+final class PingPongFuture extends AbstractCheckedFuture<Void, TransactionCommitFailedException> {
+  protected PingPongFuture(final ListenableFuture<Void> delegate) {
+    super(delegate);
+  }
+
+  @Override
+  protected TransactionCommitFailedException mapException(final Exception e) {
+    if (e.getCause() instanceof TransactionCommitFailedException){
+      return (TransactionCommitFailedException) e.getCause();
+    } else {
+            return new TransactionCommitFailedException(e.getMessage(), e.getCause());
+    }
+  }
+}
+
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongTransaction.java
new file mode 100644 (file)
index 0000000..51b28e1
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.common.impl.service.AbstractDataTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+/**
+ * Transaction context. Tracks the relationship with the backend transaction.
+ * We never leak this class to the user and have it implement the {@link FutureCallback}
+ * interface so we have a simple way of propagating the result.
+ */
+final class PingPongTransaction implements FutureCallback<Void> {
+    private final CheckedFuture<Void, TransactionCommitFailedException> submitFuture;
+    private final ListenableFuture<RpcResult<TransactionStatus>> commitFuture;
+    private final DOMDataReadWriteTransaction delegate;
+    private final SettableFuture<Void> future;
+    private DOMDataReadWriteTransaction frontendTransaction;
+
+    PingPongTransaction(final DOMDataReadWriteTransaction delegate) {
+        this.delegate = Preconditions.checkNotNull(delegate);
+        future = SettableFuture.create();
+        submitFuture = new PingPongFuture(future);
+        commitFuture = AbstractDataTransaction.convertToLegacyCommitFuture(submitFuture);
+    }
+
+    DOMDataReadWriteTransaction getTransaction() {
+        return delegate;
+    }
+
+    DOMDataReadWriteTransaction getFrontendTransaction() {
+        return frontendTransaction;
+    }
+
+    CheckedFuture<Void, TransactionCommitFailedException> getSubmitFuture() {
+        return submitFuture;
+    }
+
+    ListenableFuture<RpcResult<TransactionStatus>> getCommitFuture() {
+        return commitFuture;
+    }
+
+    @Override
+    public void onSuccess(final Void result) {
+        future.set(result);
+    }
+
+    @Override
+    public void onFailure(final Throwable t) {
+        future.setException(t);
+    }
+
+    void recordFrontendTransaction(final DOMDataReadWriteTransaction tx) {
+        if (frontendTransaction != null) {
+            frontendTransaction = tx;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper.add("delegate", delegate);
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongTransactionChain.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongTransactionChain.java
new file mode 100644 (file)
index 0000000..20e8422
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.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.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.GuardedBy;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
+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.TransactionChain;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+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.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.controller.md.sal.dom.spi.ForwardingDOMDataReadWriteTransaction;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An implementation of {@link DOMTransactionChain}, which has a very specific
+ * behavior, which some users may find surprising. If keeps the general
+ * intent of the contract, but it makes sure there are never more than two
+ * transactions allocated at any given time: one of them is being committed,
+ * and while that is happening, the other one acts as the scratch pad. Once
+ * the committing transaction completes successfully, the scratch transaction
+ * is enqueued as soon as it is ready.
+ *
+ * This mode of operation means that there is no inherent isolation between
+ * the front-end transactions and transactions cannot be reasonably cancelled.
+ *
+ * It furthermore means that the transactions returned by {@link #newReadOnlyTransaction()}
+ * counts as an outstanding transaction and the user may not allocate multiple
+ * read-only transactions at the same time.
+ */
+public final class PingPongTransactionChain implements DOMTransactionChain {
+    private static final Logger LOG = LoggerFactory.getLogger(PingPongTransactionChain.class);
+    private final DOMTransactionChain delegate;
+
+    @GuardedBy("this")
+    private boolean failed;
+
+    /**
+     * This updater is used to manipulate the "ready" transaction. We perform only atomic
+     * get-and-set on it.
+     */
+    private static final AtomicReferenceFieldUpdater<PingPongTransactionChain, PingPongTransaction> READY_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(PingPongTransactionChain.class, PingPongTransaction.class, "readyTx");
+    private volatile PingPongTransaction readyTx;
+
+    /**
+     * This updater is used to manipulate the "locked" transaction. A locked transaction
+     * means we know that the user still holds a transaction and should at some point call
+     * us. We perform on compare-and-swap to ensure we properly detect when a user is
+     * attempting to allocated multiple transactions concurrently.
+     */
+    private static final AtomicReferenceFieldUpdater<PingPongTransactionChain, PingPongTransaction> LOCKED_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(PingPongTransactionChain.class, PingPongTransaction.class, "lockedTx");
+    private volatile PingPongTransaction lockedTx;
+
+    /**
+     * This updater is used to manipulate the "inflight" transaction. There can be at most
+     * one of these at any given time. We perform only compare-and-swap on these.
+     */
+    private static final AtomicReferenceFieldUpdater<PingPongTransactionChain, PingPongTransaction> INFLIGHT_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(PingPongTransactionChain.class, PingPongTransaction.class, "inflightTx");
+    private volatile PingPongTransaction inflightTx;
+
+    PingPongTransactionChain(final DOMDataBroker broker, final TransactionChainListener listener) {
+        this.delegate = broker.createTransactionChain(new TransactionChainListener() {
+            @Override
+            public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
+                LOG.debug("Delegate chain {} reported failure in {}", chain, transaction, cause);
+
+                final DOMDataReadWriteTransaction frontend;
+                final PingPongTransaction tx = inflightTx;
+                if (tx == null) {
+                    LOG.warn("Transaction chain {} failed with no pending transactions", chain);
+                    frontend = null;
+                } else {
+                    frontend = tx.getFrontendTransaction();
+                }
+
+                listener.onTransactionChainFailed(PingPongTransactionChain.this, frontend, cause);
+                delegateFailed();
+            }
+
+            @Override
+            public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
+                listener.onTransactionChainSuccessful(PingPongTransactionChain.this);
+            }
+        });
+    }
+
+    private synchronized void delegateFailed() {
+        failed = true;
+
+        /*
+         * If we do not have a locked transaction, we need to ensure that
+         * the backend transaction is cancelled. Otherwise we can defer
+         * until the user calls us.
+         */
+        if (lockedTx == null) {
+            processIfReady();
+        }
+    }
+
+    private synchronized PingPongTransaction slowAllocateTransaction() {
+        final DOMDataReadWriteTransaction delegateTx = delegate.newReadWriteTransaction();
+        final PingPongTransaction newTx = new PingPongTransaction(delegateTx);
+
+        if (!LOCKED_UPDATER.compareAndSet(this, null, newTx)) {
+            delegateTx.cancel();
+            throw new IllegalStateException(String.format("New transaction %s raced with transacion %s", newTx, lockedTx));
+        }
+
+        return newTx;
+    }
+
+    private PingPongTransaction allocateTransaction() {
+        // Step 1: acquire current state
+        final PingPongTransaction oldTx = READY_UPDATER.getAndSet(this, null);
+
+        // Slow path: allocate a delegate transaction
+        if (oldTx == null) {
+            return slowAllocateTransaction();
+        }
+
+        // Fast path: reuse current transaction. We will check
+        //            failures and similar on submit().
+        if (!LOCKED_UPDATER.compareAndSet(this, null, oldTx)) {
+            // Ouch. Delegate chain has not detected a duplicate
+            // transaction allocation. This is the best we can do.
+            oldTx.getTransaction().cancel();
+            throw new IllegalStateException(String.format("Reusable transaction %s raced with transaction %s", oldTx, lockedTx));
+        }
+
+        return oldTx;
+    }
+
+    /*
+     * This forces allocateTransaction() on a slow path, which has to happen after
+     * this method has completed executing. Also inflightTx may be updated outside
+     * the lock, hence we need to re-check.
+     */
+    @GuardedBy("this")
+    private void processIfReady() {
+        if (inflightTx == null) {
+            final PingPongTransaction tx = READY_UPDATER.getAndSet(this, null);
+            if (tx != null) {
+                processTransaction(tx);
+            }
+        }
+    }
+
+    /**
+     * Process a ready transaction. The caller needs to ensure that
+     * each transaction is seen only once by this method.
+     *
+     * @param tx Transaction which needs processing.
+     */
+    @GuardedBy("this")
+    private void processTransaction(@Nonnull final PingPongTransaction tx) {
+        if (failed) {
+            LOG.debug("Cancelling transaction {}", tx);
+            tx.getTransaction().cancel();
+            return;
+        }
+
+        LOG.debug("Submitting transaction {}", tx);
+        if (!INFLIGHT_UPDATER.compareAndSet(this, null, tx)) {
+            LOG.warn("Submitting transaction {} while {} is still running", tx, inflightTx);
+        }
+
+        Futures.addCallback(tx.getTransaction().submit(), new FutureCallback<Void>() {
+            @Override
+            public void onSuccess(final Void result) {
+                transactionSuccessful(tx, result);
+            }
+
+            @Override
+            public void onFailure(final Throwable t) {
+                transactionFailed(tx, t);
+            }
+        });
+    }
+
+    private void transactionSuccessful(final PingPongTransaction tx, final Void result) {
+        LOG.debug("Transaction {} completed successfully", tx);
+
+        final boolean success = INFLIGHT_UPDATER.compareAndSet(this, tx, null);
+        Preconditions.checkState(success, "Successful transaction %s while %s was submitted", tx, inflightTx);
+
+        synchronized (this) {
+            processIfReady();
+        }
+
+        // Can run unsynchronized
+        tx.onSuccess(result);
+    }
+
+    private void transactionFailed(final PingPongTransaction tx, final Throwable t) {
+        LOG.debug("Transaction {} failed", tx, t);
+
+        final boolean success = INFLIGHT_UPDATER.compareAndSet(this, tx, null);
+        Preconditions.checkState(success, "Failed transaction %s while %s was submitted", tx, inflightTx);
+
+        tx.onFailure(t);
+    }
+
+    private void readyTransaction(@Nonnull final PingPongTransaction tx) {
+        // First mark the transaction as not locked.
+        final boolean lockedMatch = LOCKED_UPDATER.compareAndSet(this, tx, null);
+        Preconditions.checkState(lockedMatch, "Attempted to submit transaction %s while we have %s", tx, lockedTx);
+        LOG.debug("Transaction {} unlocked", tx);
+
+        /*
+         * The transaction is ready. It will then be picked up by either next allocation,
+         * or a background transaction completion callback.
+         */
+        final boolean success = READY_UPDATER.compareAndSet(this, null, tx);
+        Preconditions.checkState(success, "Transaction %s collided on ready state", tx, readyTx);
+        LOG.debug("Transaction {} readied", tx);
+
+        /*
+         * We do not see a transaction being in-flight, so we need to take care of dispatching
+         * the transaction to the backend. We are in the ready case, we cannot short-cut
+         * the checking of readyTx, as an in-flight transaction may have completed between us
+         * setting the field above and us checking.
+         */
+        if (inflightTx == null) {
+            synchronized (this) {
+                processIfReady();
+            }
+        }
+    }
+
+    @Override
+    public synchronized void close() {
+        final PingPongTransaction notLocked = lockedTx;
+        Preconditions.checkState(notLocked == null, "Attempted to close chain with outstanding transaction %s", notLocked);
+
+        // Force allocations on slow path. We will complete the rest
+        final PingPongTransaction tx = READY_UPDATER.getAndSet(this, null);
+
+        // Make sure no transaction is outstanding. Otherwise sleep a bit and retry
+        while (inflightTx != null) {
+            LOG.debug("Busy-waiting for in-flight transaction {} to complete", inflightTx);
+            Thread.yield();
+            continue;
+        }
+
+        // If we have an outstanding transaction, send it down
+        if (tx != null) {
+            processTransaction(tx);
+        }
+
+        // All done, close the delegate. All new allocations should fail.
+        delegate.close();
+    }
+
+    @Override
+    public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
+        final PingPongTransaction tx = allocateTransaction();
+
+        return new DOMDataReadOnlyTransaction() {
+            @Override
+            public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(final LogicalDatastoreType store,
+                    final YangInstanceIdentifier path) {
+                return tx.getTransaction().read(store, path);
+            }
+
+            @Override
+            public CheckedFuture<Boolean, ReadFailedException> exists(final LogicalDatastoreType store,
+                    final YangInstanceIdentifier path) {
+                return tx.getTransaction().exists(store, path);
+            }
+
+            @Override
+            public Object getIdentifier() {
+                return tx.getTransaction().getIdentifier();
+            }
+
+            @Override
+            public void close() {
+                readyTransaction(tx);
+            }
+        };
+    }
+
+    @Override
+    public DOMDataReadWriteTransaction newReadWriteTransaction() {
+        final PingPongTransaction tx = allocateTransaction();
+        final DOMDataReadWriteTransaction ret = new ForwardingDOMDataReadWriteTransaction() {
+            @Override
+            protected DOMDataReadWriteTransaction delegate() {
+                return tx.getTransaction();
+            }
+
+            @Override
+            public CheckedFuture<Void, TransactionCommitFailedException> submit() {
+                readyTransaction(tx);
+                return tx.getSubmitFuture();
+            }
+
+            @Override
+            public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+                readyTransaction(tx);
+                return tx.getCommitFuture();
+            }
+
+            @Override
+            public boolean cancel() {
+                throw new UnsupportedOperationException("Transaction cancellation is not supported");
+            }
+        };
+
+        tx.recordFrontendTransaction(ret);
+        return ret;
+    }
+
+    @Override
+    public DOMDataWriteTransaction newWriteOnlyTransaction() {
+        return newReadWriteTransaction();
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/RoutedDOMRpcRoutingTableEntry.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/RoutedDOMRpcRoutingTableEntry.java
new file mode 100644 (file)
index 0000000..3d2eb97
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.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.Futures;
+import java.util.List;
+import java.util.Map;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+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.NormalizedNodes;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class RoutedDOMRpcRoutingTableEntry extends AbstractDOMRpcRoutingTableEntry {
+    private static final Logger LOG = LoggerFactory.getLogger(RoutedDOMRpcRoutingTableEntry.class);
+    private final DOMRpcIdentifier globalRpcId;
+    private final YangInstanceIdentifier keyId;
+
+    private RoutedDOMRpcRoutingTableEntry(final DOMRpcIdentifier globalRpcId, final YangInstanceIdentifier keyId, final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls) {
+        super(globalRpcId.getType(), impls);
+        this.keyId = Preconditions.checkNotNull(keyId);
+        this.globalRpcId = Preconditions.checkNotNull(globalRpcId);
+    }
+
+    RoutedDOMRpcRoutingTableEntry(final RpcDefinition def, final YangInstanceIdentifier keyId, final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls) {
+        super(def.getPath(), impls);
+        this.keyId = Preconditions.checkNotNull(keyId);
+        this.globalRpcId = DOMRpcIdentifier.create(def.getPath());
+    }
+
+    @Override
+    protected CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final NormalizedNode<?, ?> input) {
+        final Optional<NormalizedNode<?, ?>> maybeKey = NormalizedNodes.findNode(input, keyId);
+
+        // Routing key is present, attempt to deliver as a routed RPC
+        if (maybeKey.isPresent()) {
+            final NormalizedNode<?, ?> key = maybeKey.get();
+            final Object value = key.getValue();
+            if (value instanceof YangInstanceIdentifier) {
+                final YangInstanceIdentifier iid = (YangInstanceIdentifier) value;
+
+                // Find a DOMRpcImplementation for a specific iid
+                final List<DOMRpcImplementation> specificImpls = getImplementations(iid);
+                if (specificImpls != null) {
+                    return specificImpls.get(0).invokeRpc(DOMRpcIdentifier.create(getSchemaPath(), iid), input);
+                }
+
+                LOG.debug("No implementation for context {} found will now look for wildcard id", iid);
+
+                // Find a DOMRpcImplementation for a wild card. Usually remote-rpc-connector would register an
+                // implementation this way
+                final List<DOMRpcImplementation> mayBeRemoteImpls = getImplementations(YangInstanceIdentifier.EMPTY);
+
+                if(mayBeRemoteImpls != null){
+                    return mayBeRemoteImpls.get(0).invokeRpc(DOMRpcIdentifier.create(getSchemaPath(), iid), input);
+                }
+
+            } else {
+                LOG.warn("Ignoring wrong context value {}", value);
+            }
+        }
+
+        final List<DOMRpcImplementation> impls = getImplementations(null);
+        if (impls != null) {
+            return impls.get(0).invokeRpc(globalRpcId, input);
+        } else {
+            return Futures.<DOMRpcResult, DOMRpcException>immediateFailedCheckedFuture(new DOMRpcImplementationNotAvailableException("No implementation of RPC %s available", getSchemaPath()));
+        }
+    }
+
+    @Override
+    protected RoutedDOMRpcRoutingTableEntry newInstance(final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls) {
+        return new RoutedDOMRpcRoutingTableEntry(globalRpcId, keyId, impls);
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/SerializedDOMDataBroker.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/SerializedDOMDataBroker.java
new file mode 100644 (file)
index 0000000..209dfdc
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+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 com.google.common.util.concurrent.ListeningExecutorService;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.RejectedExecutionException;
+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.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.yangtools.util.DurationStatisticsTracker;
+import org.opendaylight.yangtools.util.concurrent.MappingCheckedFuture;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of blocking three phase commit coordinator, which which
+ * supports coordination on multiple {@link DOMStoreThreePhaseCommitCohort}.
+ *
+ * This implementation does not support cancellation of commit,
+ *
+ * In order to advance to next phase of three phase commit all subtasks of
+ * previous step must be finish.
+ *
+ * This executor does not have an upper bound on subtask timeout.
+ */
+public class SerializedDOMDataBroker extends AbstractDOMDataBroker {
+    private static final Logger LOG = LoggerFactory.getLogger(SerializedDOMDataBroker.class);
+    private final DurationStatisticsTracker commitStatsTracker = DurationStatisticsTracker.createConcurrent();
+    private final ListeningExecutorService executor;
+
+    /**
+     *
+     * Construct DOMDataCommitCoordinator which uses supplied executor to
+     * process commit coordinations.
+     *
+     * @param executor
+     */
+    public SerializedDOMDataBroker(final Map<LogicalDatastoreType, DOMStore> datastores, final ListeningExecutorService executor) {
+        super(datastores);
+        this.executor = Preconditions.checkNotNull(executor, "executor must not be null.");
+    }
+
+    public DurationStatisticsTracker getCommitStatsTracker() {
+        return commitStatsTracker;
+    }
+
+    @Override
+    protected CheckedFuture<Void,TransactionCommitFailedException> submit(final DOMDataWriteTransaction transaction,
+            final Collection<DOMStoreThreePhaseCommitCohort> cohorts) {
+        Preconditions.checkArgument(transaction != null, "Transaction must not be null.");
+        Preconditions.checkArgument(cohorts != null, "Cohorts must not be null.");
+        LOG.debug("Tx: {} is submitted for execution.", transaction.getIdentifier());
+
+        ListenableFuture<Void> commitFuture = null;
+        try {
+            commitFuture = executor.submit(new CommitCoordinationTask(transaction, cohorts,
+                    commitStatsTracker));
+        } catch(RejectedExecutionException e) {
+            LOG.error("The commit executor's queue is full - submit task was rejected. \n" +
+                      executor, e);
+            return Futures.immediateFailedCheckedFuture(
+                    new TransactionCommitFailedException(
+                        "Could not submit the commit task - the commit queue capacity has been exceeded.", e));
+        }
+
+        return MappingCheckedFuture.create(commitFuture,
+                TransactionCommitFailedExceptionMapper.COMMIT_ERROR_MAPPER);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardRegistration.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardRegistration.java
new file mode 100644 (file)
index 0000000..9a71089
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeShard;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+
+final class ShardRegistration<T extends DOMDataTreeShard> extends AbstractListenerRegistration<T> {
+    private final DOMDataTreeIdentifier prefix;
+    private final ShardedDOMDataTree tree;
+
+    protected ShardRegistration(final ShardedDOMDataTree tree, final DOMDataTreeIdentifier prefix, final T shard) {
+        super(shard);
+        this.tree = Preconditions.checkNotNull(tree);
+        this.prefix = Preconditions.checkNotNull(prefix);
+    }
+
+    DOMDataTreeIdentifier getPrefix() {
+        return prefix;
+    }
+
+    @Override
+    protected void removeRegistration() {
+        tree.removeShard(this);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataTree.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataTree.java
new file mode 100644 (file)
index 0000000..827baa5
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+import javax.annotation.concurrent.GuardedBy;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeProducer;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeService;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeShard;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeShardingConflictException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeShardingService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class ShardedDOMDataTree implements DOMDataTreeService, DOMDataTreeShardingService {
+    private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataTree.class);
+    private final Map<LogicalDatastoreType, ShardingTableEntry> shardingTables = new EnumMap<>(LogicalDatastoreType.class);
+    @GuardedBy("this")
+    private final Map<DOMDataTreeIdentifier, DOMDataTreeProducer> idToProducer = new TreeMap<>();
+
+    @GuardedBy("this")
+    private ShardingTableEntry lookupShard(final DOMDataTreeIdentifier prefix) {
+        final ShardingTableEntry t = shardingTables.get(prefix.getDatastoreType());
+        if (t == null) {
+            return null;
+        }
+
+        return t.lookup(prefix.getRootIdentifier());
+    }
+
+    @GuardedBy("this")
+    private void storeShard(final DOMDataTreeIdentifier prefix, final ShardRegistration<?> reg) {
+        ShardingTableEntry t = shardingTables.get(prefix.getDatastoreType());
+        if (t == null) {
+            t = new ShardingTableEntry();
+            shardingTables.put(prefix.getDatastoreType(), t);
+        }
+
+        t.store(prefix.getRootIdentifier(), reg);
+    }
+
+    void removeShard(final ShardRegistration<?> reg) {
+        final DOMDataTreeIdentifier prefix = reg.getPrefix();
+        final ShardRegistration<?> parentReg;
+
+        synchronized (this) {
+            final ShardingTableEntry t = shardingTables.get(prefix.getDatastoreType());
+            if (t == null) {
+                LOG.warn("Shard registration {} points to non-existent table", reg);
+                return;
+            }
+
+            t.remove(prefix.getRootIdentifier());
+            parentReg = lookupShard(prefix).getRegistration();
+
+            /*
+             * FIXME: adjust all producers. This is tricky, as we need different locking strategy,
+             *        simply because we risk AB/BA deadlock with a producer being split off from
+             *        a producer.
+             *
+             */
+        }
+
+        if (parentReg != null) {
+            parentReg.getInstance().onChildDetached(prefix, reg.getInstance());
+        }
+    }
+
+    @Override
+    public <T extends DOMDataTreeShard> ListenerRegistration<T> registerDataTreeShard(final DOMDataTreeIdentifier prefix, final T shard) throws DOMDataTreeShardingConflictException {
+        final ShardRegistration<T> reg;
+        final ShardRegistration<?> parentReg;
+
+        synchronized (this) {
+            /*
+             * Lookup the parent shard (e.g. the one which currently matches the prefix),
+             * and if it exists, check if its registration prefix does not collide with
+             * this registration.
+             */
+            final ShardingTableEntry parent = lookupShard(prefix);
+            parentReg = parent.getRegistration();
+            if (parentReg != null && prefix.equals(parentReg.getPrefix())) {
+                throw new DOMDataTreeShardingConflictException(String.format("Prefix %s is already occupied by shard %s", prefix, parentReg.getInstance()));
+            }
+
+            // FIXME: wrap the shard in a proper adaptor based on implemented interface
+
+            reg = new ShardRegistration<T>(this, prefix, shard);
+
+            storeShard(prefix, reg);
+
+            // FIXME: update any producers/registrations
+        }
+
+        // Notify the parent shard
+        if (parentReg != null) {
+            parentReg.getInstance().onChildAttached(prefix, shard);
+        }
+
+        return reg;
+    }
+
+    @GuardedBy("this")
+    private DOMDataTreeProducer findProducer(final DOMDataTreeIdentifier subtree) {
+        for (final Entry<DOMDataTreeIdentifier, DOMDataTreeProducer> e : idToProducer.entrySet()) {
+            if (e.getKey().contains(subtree)) {
+                return e.getValue();
+            }
+        }
+
+        return null;
+    }
+
+    synchronized void destroyProducer(final ShardedDOMDataTreeProducer producer) {
+        for (final DOMDataTreeIdentifier s : producer.getSubtrees()) {
+            final DOMDataTreeProducer r = idToProducer.remove(s);
+            if (!producer.equals(r)) {
+                LOG.error("Removed producer {} on subtree {} while removing {}", r, s, producer);
+            }
+        }
+    }
+
+    @GuardedBy("this")
+    private DOMDataTreeProducer createProducer(final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
+        // Record the producer's attachment points
+        final DOMDataTreeProducer ret = ShardedDOMDataTreeProducer.create(this, shardMap);
+        for (DOMDataTreeIdentifier s : shardMap.keySet()) {
+            idToProducer.put(s, ret);
+        }
+
+        return ret;
+    }
+
+    @Override
+    public synchronized DOMDataTreeProducer createProducer(final Collection<DOMDataTreeIdentifier> subtrees) {
+        Preconditions.checkArgument(!subtrees.isEmpty(), "Subtrees may not be empty");
+
+        final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap = new HashMap<>();
+        for (final DOMDataTreeIdentifier s : subtrees) {
+            // Attempting to create a disconnected producer -- all subtrees have to be unclaimed
+            final DOMDataTreeProducer producer = findProducer(s);
+            Preconditions.checkArgument(producer == null, "Subtree %s is attached to producer %s", s, producer);
+
+            shardMap.put(s, lookupShard(s).getRegistration().getInstance());
+        }
+
+        return createProducer(shardMap);
+    }
+
+    synchronized DOMDataTreeProducer createProducer(final ShardedDOMDataTreeProducer parent, final Collection<DOMDataTreeIdentifier> subtrees) {
+        Preconditions.checkNotNull(parent);
+
+        final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap = new HashMap<>();
+        for (final DOMDataTreeIdentifier s : subtrees) {
+            shardMap.put(s, lookupShard(s).getRegistration().getInstance());
+        }
+
+        return createProducer(shardMap);
+    }
+
+    @Override
+    public synchronized <T extends DOMDataTreeListener> ListenerRegistration<T> registerListener(final T listener, final Collection<DOMDataTreeIdentifier> subtrees, final boolean allowRxMerges, final Collection<DOMDataTreeProducer> producers) {
+        // FIXME Implement this.
+        throw new UnsupportedOperationException("Not implemented yet.");
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataTreeProducer.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataTreeProducer.java
new file mode 100644 (file)
index 0000000..9b485df
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableBiMap.Builder;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Queue;
+import java.util.Set;
+import javax.annotation.concurrent.GuardedBy;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeProducer;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeProducerBusyException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeProducerException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeShard;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class ShardedDOMDataTreeProducer implements DOMDataTreeProducer {
+    private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataTreeProducer.class);
+    private final BiMap<DOMDataTreeShard, DOMStoreTransactionChain> shardToChain;
+    private final Map<DOMDataTreeIdentifier, DOMDataTreeShard> idToShard;
+    private final ShardedDOMDataTree dataTree;
+
+    @GuardedBy("this")
+    private Map<DOMDataTreeIdentifier, DOMDataTreeProducer> children = Collections.emptyMap();
+    @GuardedBy("this")
+    private DOMDataWriteTransaction openTx;
+    @GuardedBy("this")
+    private boolean closed;
+
+    ShardedDOMDataTreeProducer(final ShardedDOMDataTree dataTree, final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap, final Set<DOMDataTreeShard> shards) {
+        this.dataTree = Preconditions.checkNotNull(dataTree);
+
+        // Create shard -> chain map
+        final Builder<DOMDataTreeShard, DOMStoreTransactionChain> cb = ImmutableBiMap.builder();
+        final Queue<Exception> es = new LinkedList<>();
+
+        for (final DOMDataTreeShard s : shards) {
+            if (s instanceof DOMStore) {
+                try {
+                    final DOMStoreTransactionChain c = ((DOMStore)s).createTransactionChain();
+                    LOG.trace("Using DOMStore chain {} to access shard {}", c, s);
+                    cb.put(s, c);
+                } catch (final Exception e) {
+                    LOG.error("Failed to instantiate chain for shard {}", s, e);
+                    es.add(e);
+                }
+            } else {
+                LOG.error("Unhandled shard instance type {}", s.getClass());
+            }
+        }
+        this.shardToChain = cb.build();
+
+        // An error was encountered, close chains and report the error
+        if (shardToChain.size() != shards.size()) {
+            for (final DOMStoreTransactionChain c : shardToChain.values()) {
+                try {
+                    c.close();
+                } catch (final Exception e) {
+                    LOG.warn("Exception raised while closing chain {}", c, e);
+                }
+            }
+
+            final IllegalStateException e = new IllegalStateException("Failed to completely allocate contexts", es.poll());
+            while (!es.isEmpty()) {
+                e.addSuppressed(es.poll());
+            }
+
+            throw e;
+        }
+
+        idToShard = ImmutableMap.copyOf(shardMap);
+    }
+
+    @Override
+    public synchronized DOMDataWriteTransaction createTransaction(final boolean isolated) {
+        Preconditions.checkState(!closed, "Producer is already closed");
+        Preconditions.checkState(openTx == null, "Transaction %s is still open", openTx);
+
+        // Allocate backing transactions
+        final Map<DOMDataTreeShard, DOMStoreWriteTransaction> shardToTx = new HashMap<>();
+        for (final Entry<DOMDataTreeShard, DOMStoreTransactionChain> e : shardToChain.entrySet()) {
+            shardToTx.put(e.getKey(), e.getValue().newWriteOnlyTransaction());
+        }
+
+        // Create the ID->transaction map
+        final ImmutableMap.Builder<DOMDataTreeIdentifier, DOMStoreWriteTransaction> b = ImmutableMap.builder();
+        for (final Entry<DOMDataTreeIdentifier, DOMDataTreeShard> e : idToShard.entrySet()) {
+            b.put(e.getKey(), shardToTx.get(e.getValue()));
+        }
+
+        final ShardedDOMDataWriteTransaction ret = new ShardedDOMDataWriteTransaction(this, b.build());
+        openTx = ret;
+        return ret;
+    }
+
+    @GuardedBy("this")
+    private boolean haveSubtree(final DOMDataTreeIdentifier subtree) {
+        for (final DOMDataTreeIdentifier i : idToShard.keySet()) {
+            if (i.contains(subtree)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @GuardedBy("this")
+    private DOMDataTreeProducer lookupChild(final DOMDataTreeIdentifier s) {
+        for (final Entry<DOMDataTreeIdentifier, DOMDataTreeProducer> e : children.entrySet()) {
+            if (e.getKey().contains(s)) {
+                return e.getValue();
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public synchronized DOMDataTreeProducer createProducer(final Collection<DOMDataTreeIdentifier> subtrees) {
+        Preconditions.checkState(!closed, "Producer is already closed");
+        Preconditions.checkState(openTx == null, "Transaction %s is still open", openTx);
+
+        for (final DOMDataTreeIdentifier s : subtrees) {
+            // Check if the subtree was visible at any time
+            if (!haveSubtree(s)) {
+                throw new IllegalArgumentException(String.format("Subtree %s was never available in producer %s", s, this));
+            }
+
+            // Check if the subtree has not been delegated to a child
+            final DOMDataTreeProducer child = lookupChild(s);
+            Preconditions.checkArgument(child == null, "Subtree %s is delegated to child producer %s", s, child);
+
+            // Check if part of the requested subtree is not delegated to a child.
+            for (final DOMDataTreeIdentifier c : children.keySet()) {
+                if (s.contains(c)) {
+                    throw new IllegalArgumentException(String.format("Subtree %s cannot be delegated as it is superset of already-delegated %s", s, c));
+                }
+            }
+        }
+
+        final DOMDataTreeProducer ret = dataTree.createProducer(this, subtrees);
+        final ImmutableMap.Builder<DOMDataTreeIdentifier, DOMDataTreeProducer> cb = ImmutableMap.builder();
+        cb.putAll(children);
+        for (final DOMDataTreeIdentifier s : subtrees) {
+            cb.put(s, ret);
+        }
+
+        children = cb.build();
+        return ret;
+    }
+
+    @Override
+    public synchronized void close() throws DOMDataTreeProducerException {
+        if (!closed) {
+            if (openTx != null) {
+                throw new DOMDataTreeProducerBusyException(String.format("Transaction %s is still open", openTx));
+            }
+
+            closed = true;
+            dataTree.destroyProducer(this);
+        }
+    }
+
+    static DOMDataTreeProducer create(final ShardedDOMDataTree dataTree, final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
+        /*
+         * FIXME: we do not allow multiple multiple shards in a producer because we do not implement the
+         *        synchronization primitives yet
+         */
+        final Set<DOMDataTreeShard> shards = ImmutableSet.copyOf(shardMap.values());
+        if (shards.size() > 1) {
+            throw new UnsupportedOperationException("Cross-shard producers are not supported yet");
+        }
+
+        return new ShardedDOMDataTreeProducer(dataTree, shardMap, shards);
+    }
+
+    Set<DOMDataTreeIdentifier> getSubtrees() {
+        return idToShard.keySet();
+    }
+
+    synchronized void cancelTransaction(final ShardedDOMDataWriteTransaction transaction) {
+        if (!openTx.equals(transaction)) {
+            LOG.warn("Transaction {} is not open in producer {}", transaction, this);
+            return;
+        }
+
+        LOG.debug("Transaction {} cancelled", transaction);
+        openTx = null;
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataWriteTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataWriteTransaction.java
new file mode 100644 (file)
index 0000000..33f15e3
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+import javax.annotation.concurrent.GuardedBy;
+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.common.impl.service.AbstractDataTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@NotThreadSafe
+final class ShardedDOMDataWriteTransaction implements DOMDataWriteTransaction {
+    private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataWriteTransaction.class);
+    private static final AtomicLong COUNTER = new AtomicLong();
+    private final Map<DOMDataTreeIdentifier, DOMStoreWriteTransaction> idToTransaction;
+    private final ShardedDOMDataTreeProducer producer;
+    private final String identifier;
+    @GuardedBy("this")
+    private boolean closed =  false;
+
+    ShardedDOMDataWriteTransaction(final ShardedDOMDataTreeProducer producer, final Map<DOMDataTreeIdentifier, DOMStoreWriteTransaction> idToTransaction) {
+        this.producer = Preconditions.checkNotNull(producer);
+        this.idToTransaction = Preconditions.checkNotNull(idToTransaction);
+        this.identifier = "SHARDED-DOM-" + COUNTER.getAndIncrement();
+    }
+
+    // FIXME: use atomic operations
+    @GuardedBy("this")
+    private DOMStoreWriteTransaction lookup(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+        final DOMDataTreeIdentifier id = new DOMDataTreeIdentifier(store, path);
+
+        for (Entry<DOMDataTreeIdentifier, DOMStoreWriteTransaction> e : idToTransaction.entrySet()) {
+            if (e.getKey().contains(id)) {
+                return e.getValue();
+            }
+        }
+
+        throw new IllegalArgumentException(String.format("Path %s is not acessible from transaction %s", id, this));
+    }
+
+    @Override
+    public String getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public synchronized boolean cancel() {
+        if (closed) {
+            return false;
+        }
+
+        LOG.debug("Cancelling transaction {}", identifier);
+        for (DOMStoreWriteTransaction tx : ImmutableSet.copyOf(idToTransaction.values())) {
+            tx.close();
+        }
+
+        closed = true;
+        producer.cancelTransaction(this);
+        return true;
+    }
+
+    @Override
+    public synchronized CheckedFuture<Void, TransactionCommitFailedException> submit() {
+        Preconditions.checkState(!closed, "Transaction %s is already closed", identifier);
+
+        final Set<DOMStoreWriteTransaction> txns = ImmutableSet.copyOf(idToTransaction.values());
+        final List<DOMStoreThreePhaseCommitCohort> cohorts = new ArrayList<>(txns.size());
+        for (DOMStoreWriteTransaction tx : txns) {
+            cohorts.add(tx.ready());
+        }
+
+        try {
+            return Futures.immediateCheckedFuture(new CommitCoordinationTask(this, cohorts, null).call());
+        } catch (TransactionCommitFailedException e) {
+            return Futures.immediateFailedCheckedFuture(e);
+        }
+    }
+
+    @Override
+    @Deprecated
+    public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+        return AbstractDataTransaction.convertToLegacyCommitFuture(submit());
+    }
+
+    @Override
+    public synchronized void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+        lookup(store, path).delete(path);
+    }
+
+    @Override
+    public synchronized void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+        lookup(store, path).write(path, data);
+    }
+
+    @Override
+    public synchronized void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+        lookup(store, path).merge(path, data);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardingTableEntry.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardingTableEntry.java
new file mode 100644 (file)
index 0000000..fcd0ebd
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class ShardingTableEntry implements Identifiable<PathArgument> {
+    private static final Logger LOG = LoggerFactory.getLogger(ShardingTableEntry.class);
+    private final Map<PathArgument, ShardingTableEntry> children = Collections.emptyMap();
+    private final PathArgument identifier;
+    private ShardRegistration<?> registration;
+
+    ShardingTableEntry() {
+        identifier = null;
+    }
+
+    ShardingTableEntry(final PathArgument identifier) {
+        this.identifier = Preconditions.checkNotNull(identifier);
+    }
+
+    @Override
+    public PathArgument getIdentifier() {
+        return identifier;
+    }
+
+    public ShardRegistration<?> getRegistration() {
+        return registration;
+    }
+
+    ShardingTableEntry lookup(final YangInstanceIdentifier id) {
+        final Iterator<PathArgument> it = id.getPathArguments().iterator();
+        ShardingTableEntry entry = this;
+
+        while (it.hasNext()) {
+            final PathArgument a = it.next();
+            final ShardingTableEntry child = entry.children.get(a);
+            if (child == null) {
+                LOG.debug("Lookup of {} stopped at {}", id, a);
+                break;
+            }
+
+            entry = child;
+        }
+
+        return entry;
+    }
+
+    void store(final YangInstanceIdentifier id, final ShardRegistration<?> reg) {
+        final Iterator<PathArgument> it = id.getPathArguments().iterator();
+        ShardingTableEntry entry = this;
+
+        while (it.hasNext()) {
+            final PathArgument a = it.next();
+            ShardingTableEntry child = entry.children.get(a);
+            if (child == null) {
+                child = new ShardingTableEntry(a);
+                entry.children.put(a, child);
+            }
+        }
+
+        Preconditions.checkState(entry.registration == null);
+        entry.registration = reg;
+    }
+
+    private boolean remove(final Iterator<PathArgument> it) {
+        if (it.hasNext()) {
+            final PathArgument arg = it.next();
+            final ShardingTableEntry child = children.get(arg);
+            if (child != null) {
+                if (child.remove(it)) {
+                    children.remove(arg);
+                }
+            } else {
+                LOG.warn("Cannot remove non-existent child {}", arg);
+            }
+        }
+
+        return registration == null && children.isEmpty();
+    }
+
+    void remove(final YangInstanceIdentifier id) {
+        this.remove(id.getPathArguments().iterator());
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/TransactionCommitFailedExceptionMapper.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/TransactionCommitFailedExceptionMapper.java
new file mode 100644 (file)
index 0000000..11e2a19
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
+
+/**
+ * Utility exception mapper which translates Exception to {@link TransactionCommitFailedException}.
+ *
+ * @see ExceptionMapper
+ */
+public final class TransactionCommitFailedExceptionMapper
+                           extends ExceptionMapper<TransactionCommitFailedException> {
+
+    public static final TransactionCommitFailedExceptionMapper PRE_COMMIT_MAPPER = create("preCommit");
+
+    public static final TransactionCommitFailedExceptionMapper CAN_COMMIT_ERROR_MAPPER = create("canCommit");
+
+    public static final TransactionCommitFailedExceptionMapper COMMIT_ERROR_MAPPER = create("commit");
+
+    private TransactionCommitFailedExceptionMapper(final String opName) {
+        super( opName, TransactionCommitFailedException.class );
+    }
+
+    public static TransactionCommitFailedExceptionMapper create(final String opName) {
+        return new TransactionCommitFailedExceptionMapper(opName);
+    }
+
+    @Override
+    protected TransactionCommitFailedException newWithCause( final String message, final Throwable cause ) {
+        return new TransactionCommitFailedException( message, cause );
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/UnknownDOMRpcRoutingTableEntry.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/UnknownDOMRpcRoutingTableEntry.java
new file mode 100644 (file)
index 0000000..aadfa4a
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import java.util.List;
+import java.util.Map;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+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;
+
+final class UnknownDOMRpcRoutingTableEntry extends AbstractDOMRpcRoutingTableEntry {
+    private final CheckedFuture<DOMRpcResult, DOMRpcException> unknownRpc;
+
+    UnknownDOMRpcRoutingTableEntry(final SchemaPath schemaPath, final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls) {
+        super(schemaPath, impls);
+        unknownRpc = Futures.<DOMRpcResult, DOMRpcException>immediateFailedCheckedFuture(
+            new DOMRpcImplementationNotAvailableException("SchemaPath %s is not resolved to an RPC", schemaPath));
+    }
+
+    @Override
+    protected CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final NormalizedNode<?, ?> input) {
+        return unknownRpc;
+    }
+
+    @Override
+    protected UnknownDOMRpcRoutingTableEntry newInstance(final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls) {
+        return new UnknownDOMRpcRoutingTableEntry(getSchemaPath(), impls);
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/jmx/CommitStatsMXBean.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/jmx/CommitStatsMXBean.java
new file mode 100644 (file)
index 0000000..40dc30e
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl.jmx;
+
+/**
+ * MXBean interface for retrieving write Tx commit statistics.
+ *
+ * @author Thomas Pantelis
+ */
+public interface CommitStatsMXBean {
+
+    /**
+     * Returns the total number of commits that have occurred.
+     */
+    long getTotalCommits();
+
+    /**
+     * Returns a string representing the time duration of the longest commit, in the appropriate
+     * scaled units, along with the date/time that it occurred.
+     */
+    String getLongestCommitTime();
+
+    /**
+     * Returns a string representing the time duration of the shortest commit, in the appropriate
+     * scaled units, along with the date/time that it occurred.
+     */
+    String getShortestCommitTime();
+
+    /**
+     * Returns a string representing average commit time duration, in the appropriate
+     * scaled units.
+     */
+    String getAverageCommitTime();
+
+    /**
+     * Clears the current stats to their defaults.
+     */
+    void clearStats();
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/jmx/CommitStatsMXBeanImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/jmx/CommitStatsMXBeanImpl.java
new file mode 100644 (file)
index 0000000..0d5306f
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl.jmx;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.util.jmx.AbstractMXBean;
+import org.opendaylight.yangtools.util.DurationStatisticsTracker;
+
+/**
+ * Implementation of the CommitStatsMXBean interface.
+ *
+ * @author Thomas Pantelis
+ */
+public class CommitStatsMXBeanImpl extends AbstractMXBean implements CommitStatsMXBean {
+
+    private final DurationStatisticsTracker commitStatsTracker;
+
+    /**
+     * Constructor.
+     *
+     * @param commitStatsTracker the DurationStatsTracker used to obtain the stats.
+     * @param mBeanType mBeanType Used as the <code>type</code> property in the bean's ObjectName.
+     */
+    public CommitStatsMXBeanImpl(@Nonnull DurationStatisticsTracker commitStatsTracker,
+            @Nonnull String mBeanType) {
+        super("CommitStats", mBeanType, null);
+        this.commitStatsTracker = commitStatsTracker;
+    }
+
+    @Override
+    public long getTotalCommits() {
+        return commitStatsTracker.getTotalDurations();
+    }
+
+    @Override
+    public String getLongestCommitTime() {
+        return commitStatsTracker.getDisplayableLongestDuration();
+    }
+
+    @Override
+    public String getShortestCommitTime() {
+        return commitStatsTracker.getDisplayableShortestDuration();
+    }
+
+    @Override
+    public String getAverageCommitTime() {
+        return commitStatsTracker.getDisplayableAverageDuration();
+    }
+
+    @Override
+    public void clearStats() {
+        commitStatsTracker.reset();
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/mount/DOMMountPointServiceImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/mount/DOMMountPointServiceImpl.java
new file mode 100644 (file)
index 0000000..c32576e
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl.mount;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.MutableClassToInstanceMap;
+import java.util.HashMap;
+import java.util.Map;
+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.DOMService;
+import org.opendaylight.controller.md.sal.dom.broker.spi.mount.SimpleDOMMountPoint;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class DOMMountPointServiceImpl implements DOMMountPointService {
+
+    private final Map<YangInstanceIdentifier, DOMMountPoint> mountPoints = new HashMap<>();
+
+    private final ListenerRegistry<MountProvisionListener> listeners = ListenerRegistry.create();
+
+    @Override
+    public Optional<DOMMountPoint> getMountPoint(final YangInstanceIdentifier path) {
+        return Optional.fromNullable(mountPoints.get(path));
+    }
+
+    @Override
+    public DOMMountPointBuilder createMountPoint(final YangInstanceIdentifier path) {
+        Preconditions.checkState(!mountPoints.containsKey(path), "Mount point already exists");
+        return new DOMMountPointBuilderImpl(path);
+    }
+
+    public void notifyMountCreated(final YangInstanceIdentifier identifier) {
+        for (final ListenerRegistration<MountProvisionListener> listener : listeners
+                .getListeners()) {
+            listener.getInstance().onMountPointCreated(identifier);
+        }
+    }
+
+    public void notifyMountRemoved(final YangInstanceIdentifier identifier) {
+        for (final ListenerRegistration<MountProvisionListener> listener : listeners
+                .getListeners()) {
+            listener.getInstance().onMountPointRemoved(identifier);
+        }
+    }
+
+    @Override
+    public ListenerRegistration<MountProvisionListener> registerProvisionListener(
+            final MountProvisionListener listener) {
+        return listeners.register(listener);
+    }
+
+    public ObjectRegistration<DOMMountPoint> registerMountPoint(final DOMMountPoint mountPoint) {
+        synchronized (mountPoints) {
+            Preconditions.checkState(!mountPoints.containsKey(mountPoint.getIdentifier()), "Mount point already exists");
+            mountPoints.put(mountPoint.getIdentifier(), mountPoint);
+        }
+        notifyMountCreated(mountPoint.getIdentifier());
+
+        return new MountRegistration(mountPoint);
+    }
+
+    public void unregisterMountPoint(final YangInstanceIdentifier mountPointId) {
+        synchronized (mountPoints) {
+            Preconditions.checkState(mountPoints.containsKey(mountPointId), "Mount point does not exist");
+            mountPoints.remove(mountPointId);
+        }
+        notifyMountRemoved(mountPointId);
+    }
+
+    public class DOMMountPointBuilderImpl implements DOMMountPointBuilder {
+
+        ClassToInstanceMap<DOMService> services = MutableClassToInstanceMap.create();
+        private SimpleDOMMountPoint mountPoint;
+        private final YangInstanceIdentifier path;
+        private SchemaContext schemaContext;
+
+        public DOMMountPointBuilderImpl(final YangInstanceIdentifier path) {
+            this.path = path;
+        }
+
+        @Override
+        public <T extends DOMService> DOMMountPointBuilder addService(final Class<T> type, final T impl) {
+            services.putInstance(type, impl);
+            return this;
+        }
+
+        @Override
+        public DOMMountPointBuilder addInitialSchemaContext(final SchemaContext ctx) {
+            schemaContext = ctx;
+            return this;
+        }
+
+        @Override
+        public ObjectRegistration<DOMMountPoint> register() {
+            Preconditions.checkState(mountPoint == null, "Mount point is already built.");
+            mountPoint = SimpleDOMMountPoint.create(path, services,schemaContext);
+            return registerMountPoint(mountPoint);
+        }
+    }
+
+    private final class MountRegistration implements ObjectRegistration<DOMMountPoint> {
+        private final DOMMountPoint mountPoint;
+
+        public MountRegistration(final DOMMountPoint mountPoint) {
+            this.mountPoint = mountPoint;
+        }
+
+        @Override
+        public DOMMountPoint getInstance() {
+            return mountPoint;
+        }
+
+        @Override
+        public void close() throws Exception {
+            unregisterMountPoint(mountPoint.getIdentifier());
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerImpl.java
new file mode 100644 (file)
index 0000000..03fbb18
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableClassToInstanceMap;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+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.DOMRpcIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationRegistration;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcProviderService;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.md.sal.dom.broker.impl.DOMRpcRouter;
+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.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BrokerImpl implements Broker, DOMRpcProviderService, DOMRpcService, AutoCloseable {
+    private final static Logger log = LoggerFactory.getLogger(BrokerImpl.class);
+
+    // Broker Generic Context
+    private final Set<ConsumerContextImpl> sessions = Collections
+            .synchronizedSet(new HashSet<ConsumerContextImpl>());
+    private final Set<ProviderContextImpl> providerSessions = Collections
+            .synchronizedSet(new HashSet<ProviderContextImpl>());
+
+    private AutoCloseable deactivator = null;
+
+    private DOMRpcRouter router = null;
+
+    private final ClassToInstanceMap<BrokerService> services;
+
+    public  BrokerImpl(final DOMRpcRouter router,final ClassToInstanceMap<BrokerService> services) {
+        this.router = Preconditions.checkNotNull(router, "RPC Router must not be null");
+        this.services = ImmutableClassToInstanceMap.copyOf(services);
+    }
+
+
+    @Override
+    public ConsumerSession registerConsumer(final Consumer consumer,
+            final BundleContext ctx) {
+        return registerConsumer(consumer);
+    }
+
+    @Override
+    public ProviderSession registerProvider(final Provider provider,
+            final BundleContext ctx) {
+        return registerProvider(provider);
+    }
+
+    // Validation
+    private void checkPredicates(final Provider prov) {
+        Preconditions.checkNotNull(prov, "Provider should not be null.");
+        for (final ProviderContextImpl session : providerSessions) {
+            if (prov.equals(session.getProvider())) {
+                throw new IllegalStateException("Provider already registered");
+            }
+        }
+
+    }
+
+    private void checkPredicates(final Consumer cons) {
+        Preconditions.checkNotNull(cons, "Consumer should not be null.");
+        for (final ConsumerContextImpl session : sessions) {
+            if (cons.equals(session.getConsumer())) {
+                throw new IllegalStateException("Consumer already registered");
+            }
+        }
+    }
+
+    // Private Factory methods
+    private ConsumerContextImpl newSessionFor(final Consumer provider) {
+        final ConsumerContextImpl ret = new ConsumerContextImpl(provider, this);
+        return ret;
+    }
+
+    private ProviderContextImpl newSessionFor(final Provider provider) {
+        final ProviderContextImpl ret = new ProviderContextImpl(provider, this);
+        return ret;
+    }
+
+    protected void consumerSessionClosed(
+            final ConsumerContextImpl consumerContextImpl) {
+        sessions.remove(consumerContextImpl);
+        providerSessions.remove(consumerContextImpl);
+    }
+
+    @Override
+    public void close() throws Exception {
+        if (deactivator != null) {
+            deactivator.close();
+            deactivator = null;
+        }
+    }
+
+    /**
+     * @return the deactivator
+     */
+    public AutoCloseable getDeactivator() {
+        return deactivator;
+    }
+
+    /**
+     * @param deactivator
+     *            the deactivator to set
+     */
+    public void setDeactivator(final AutoCloseable deactivator) {
+        this.deactivator = deactivator;
+    }
+
+    /**
+     * @return the router
+     */
+    public DOMRpcRouter getRouter() {
+        return router;
+    }
+
+    /**
+     * @param router
+     *            the router to set
+     */
+    public void setRouter(final DOMRpcRouter router) {
+        this.router = router;
+    }
+
+    protected <T extends BrokerService> Optional<T> getGlobalService(final Class<T> service) {
+        return Optional.fromNullable(services.getInstance(service));
+    }
+
+
+    @Override
+    public ConsumerSession registerConsumer(final Consumer consumer) {
+        checkPredicates(consumer);
+        log.trace("Registering consumer {}", consumer);
+        final ConsumerContextImpl session = newSessionFor(consumer);
+        consumer.onSessionInitiated(session);
+        sessions.add(session);
+        return session;
+    }
+
+
+    @Override
+    public ProviderSession registerProvider(final Provider provider) {
+        checkPredicates(provider);
+        final ProviderContextImpl session = newSessionFor(provider);
+        provider.onSessionInitiated(session);
+        providerSessions.add(session);
+        return session;
+    }
+
+
+    @Nonnull
+    @Override
+    public <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(@Nonnull final T implementation, @Nonnull final DOMRpcIdentifier... rpcs) {
+        return router.registerRpcImplementation(implementation, rpcs);
+    }
+
+    @Nonnull
+    @Override
+    public <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(@Nonnull final T implementation, @Nonnull final Set<DOMRpcIdentifier> rpcs) {
+        return router.registerRpcImplementation(implementation, rpcs);
+    }
+
+    @Nonnull
+    @Override
+    public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(@Nonnull final SchemaPath type, @Nullable final NormalizedNode<?, ?> input) {
+        return router.invokeRpc(type, input);
+    }
+
+    @Nonnull
+    @Override
+    public <T extends DOMRpcAvailabilityListener> ListenerRegistration<T> registerRpcListener(@Nonnull final T listener) {
+        return router.registerRpcListener(listener);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/ConsumerContextImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/ConsumerContextImpl.java
new file mode 100644 (file)
index 0000000..721b1e5
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.MutableClassToInstanceMap;
+import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.api.Consumer;
+import org.opendaylight.controller.sal.dom.broker.osgi.AbstractBrokerServiceProxy;
+import org.opendaylight.controller.sal.dom.broker.osgi.ProxyFactory;
+
+import javax.annotation.concurrent.GuardedBy;
+import java.util.Collection;
+
+class ConsumerContextImpl implements ConsumerSession {
+
+    private final ClassToInstanceMap<BrokerService> instantiatedServices = MutableClassToInstanceMap
+            .create();
+    private final Consumer consumer;
+
+    private BrokerImpl broker = null;
+    @GuardedBy("this")
+    private boolean closed = false;
+
+    public ConsumerContextImpl(final Consumer provider, final BrokerImpl brokerImpl) {
+        broker = brokerImpl;
+        consumer = provider;
+    }
+
+    @Override
+    public <T extends BrokerService> T getService(final Class<T> service) {
+        checkNotClosed();
+        final T localProxy = instantiatedServices.getInstance(service);
+        if (localProxy != null) {
+            return localProxy;
+        }
+        final Optional<T> serviceImpl = broker.getGlobalService(service);
+        if(serviceImpl.isPresent()) {
+            final T ret = ProxyFactory.createProxy(null,serviceImpl.get());
+            instantiatedServices.putInstance(service, ret);
+            return ret;
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void close() {
+        synchronized (this) {
+            if (closed) {
+                return;
+            }
+            this.closed = true;
+        }
+
+        Collection<BrokerService> toStop = instantiatedServices.values();
+        for (BrokerService brokerService : toStop) {
+            if (brokerService instanceof AbstractBrokerServiceProxy<?>) {
+                ((AbstractBrokerServiceProxy<?>) brokerService).close();
+            }
+        }
+        broker.consumerSessionClosed(this);
+        broker = null;
+    }
+
+
+    @Override
+    public synchronized boolean isClosed() {
+        return closed;
+    }
+
+    /**
+     * @return the broker
+     */
+    protected final  BrokerImpl getBrokerChecked() {
+        checkNotClosed();
+        return broker;
+    }
+
+    /**
+     * @return the _consumer
+     */
+    public Consumer getConsumer() {
+        return consumer;
+    }
+
+    protected final void checkNotClosed()  {
+        Preconditions.checkState(!closed, "Session is closed.");
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/GlobalBundleScanningSchemaServiceImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/GlobalBundleScanningSchemaServiceImpl.java
new file mode 100644 (file)
index 0000000..34c11dd
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker;
+
+import static com.google.common.base.Preconditions.checkState;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+import org.opendaylight.yangtools.yang.parser.repo.URLSchemaContextResolver;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.BundleTracker;
+import org.osgi.util.tracker.BundleTrackerCustomizer;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvider, SchemaService, ServiceTrackerCustomizer<SchemaContextListener, SchemaContextListener>, AutoCloseable {
+    private static final Logger LOG = LoggerFactory.getLogger(GlobalBundleScanningSchemaServiceImpl.class);
+
+    private final ListenerRegistry<SchemaContextListener> listeners = new ListenerRegistry<>();
+    private final URLSchemaContextResolver contextResolver = URLSchemaContextResolver.create("global-bundle");
+    private final BundleScanner scanner = new BundleScanner();
+    private final BundleContext context;
+
+    private ServiceTracker<SchemaContextListener, SchemaContextListener> listenerTracker;
+    private BundleTracker<Iterable<Registration>> bundleTracker;
+    private boolean starting = true;
+    private static GlobalBundleScanningSchemaServiceImpl instance;
+
+    private GlobalBundleScanningSchemaServiceImpl(final BundleContext context) {
+        this.context = Preconditions.checkNotNull(context);
+    }
+
+    public synchronized static GlobalBundleScanningSchemaServiceImpl createInstance(final BundleContext ctx) {
+        Preconditions.checkState(instance == null);
+        instance = new GlobalBundleScanningSchemaServiceImpl(ctx);
+        instance.start();
+        return instance;
+    }
+
+    public synchronized static GlobalBundleScanningSchemaServiceImpl getInstance() {
+        Preconditions.checkState(instance != null, "Global Instance was not instantiated");
+        return instance;
+    }
+
+    @VisibleForTesting
+    public static synchronized void destroyInstance() {
+        try {
+            instance.close();
+        } finally {
+            instance = null;
+        }
+    }
+
+    public BundleContext getContext() {
+        return context;
+    }
+
+    public void start() {
+        checkState(context != null);
+        LOG.debug("start() starting");
+
+        listenerTracker = new ServiceTracker<>(context, SchemaContextListener.class, GlobalBundleScanningSchemaServiceImpl.this);
+        bundleTracker = new BundleTracker<>(context, Bundle.RESOLVED | Bundle.STARTING |
+                Bundle.STOPPING | Bundle.ACTIVE, scanner);
+        bundleTracker.open();
+
+        LOG.debug("BundleTracker.open() complete");
+
+        listenerTracker.open();
+        starting = false;
+        tryToUpdateSchemaContext();
+
+        LOG.debug("start() complete");
+    }
+
+    @Override
+    public SchemaContext getSchemaContext() {
+        return getGlobalContext();
+    }
+
+    @Override
+    public SchemaContext getGlobalContext() {
+        return contextResolver.getSchemaContext().orNull();
+    }
+
+    @Override
+    public void addModule(final Module module) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public SchemaContext getSessionContext() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void removeModule(final Module module) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public synchronized ListenerRegistration<SchemaContextListener> registerSchemaContextListener(final SchemaContextListener listener) {
+        Optional<SchemaContext> potentialCtx = contextResolver.getSchemaContext();
+        if(potentialCtx.isPresent()) {
+            listener.onGlobalContextUpdated(potentialCtx.get());
+        }
+        return listeners.register(listener);
+    }
+
+    @Override
+    public void close() {
+        if (bundleTracker != null) {
+            bundleTracker.close();
+        }
+        if (listenerTracker != null) {
+            listenerTracker.close();
+        }
+
+        for (ListenerRegistration<SchemaContextListener> l : listeners.getListeners()) {
+            l.close();
+        }
+    }
+
+    private synchronized void updateContext(final SchemaContext snapshot) {
+        Object[] services = listenerTracker.getServices();
+        for (ListenerRegistration<SchemaContextListener> listener : listeners) {
+            try {
+                listener.getInstance().onGlobalContextUpdated(snapshot);
+            } catch (Exception e) {
+                LOG.error("Exception occured during invoking listener", e);
+            }
+        }
+        if (services != null) {
+            for (Object rawListener : services) {
+                final SchemaContextListener listener = (SchemaContextListener) rawListener;
+                try {
+                    listener.onGlobalContextUpdated(snapshot);
+                } catch (Exception e) {
+                    LOG.error("Exception occured during invoking listener {}", listener, e);
+                }
+            }
+        }
+    }
+
+    private class BundleScanner implements BundleTrackerCustomizer<Iterable<Registration>> {
+        @Override
+        public Iterable<Registration> addingBundle(final Bundle bundle, final BundleEvent event) {
+
+            if (bundle.getBundleId() == 0) {
+                return Collections.emptyList();
+            }
+
+            final Enumeration<URL> enumeration = bundle.findEntries("META-INF/yang", "*.yang", false);
+            if (enumeration == null) {
+                return Collections.emptyList();
+            }
+
+            final List<Registration> urls = new ArrayList<>();
+            while (enumeration.hasMoreElements()) {
+                final URL u = enumeration.nextElement();
+                try {
+                    urls.add(contextResolver.registerSource(u));
+                    LOG.debug("Registered {}", u);
+                } catch (Exception e) {
+                    LOG.warn("Failed to register {}, ignoring it", e);
+                }
+            }
+
+            if (!urls.isEmpty()) {
+                LOG.debug("Loaded {} new URLs from bundle {}, attempting to rebuild schema context",
+                        urls.size(), bundle.getSymbolicName());
+                tryToUpdateSchemaContext();
+            }
+
+            return ImmutableList.copyOf(urls);
+        }
+
+        @Override
+        public void modifiedBundle(final Bundle bundle, final BundleEvent event, final Iterable<Registration> object) {
+        }
+
+        /**
+         * If removing YANG files makes yang store inconsistent, method
+         * {@link #getYangStoreSnapshot()} will throw exception. There is no
+         * rollback.
+         */
+
+        @Override
+        public synchronized void removedBundle(final Bundle bundle, final BundleEvent event, final Iterable<Registration> urls) {
+            for (Registration url : urls) {
+                try {
+                    url.close();
+                } catch (Exception e) {
+                    LOG.warn("Failed do unregister URL {}, proceeding", url, e);
+                }
+            }
+
+            int numUrls = Iterables.size(urls);
+            if(numUrls > 0 ) {
+                if(LOG.isDebugEnabled()) {
+                    LOG.debug("removedBundle: {}, state: {}, # urls: {}", bundle.getSymbolicName(), bundle.getState(), numUrls);
+                }
+
+                tryToUpdateSchemaContext();
+            }
+        }
+    }
+
+    @Override
+    public synchronized SchemaContextListener addingService(final ServiceReference<SchemaContextListener> reference) {
+
+        SchemaContextListener listener = context.getService(reference);
+        SchemaContext _ctxContext = getGlobalContext();
+        if (getContext() != null && _ctxContext != null) {
+            listener.onGlobalContextUpdated(_ctxContext);
+        }
+        return listener;
+    }
+
+    public synchronized void tryToUpdateSchemaContext() {
+        if (starting) {
+            return;
+        }
+        Optional<SchemaContext> schema = contextResolver.getSchemaContext();
+        if(schema.isPresent()) {
+            if(LOG.isDebugEnabled()) {
+                LOG.debug("Got new SchemaContext: # of modules {}", schema.get().getAllModuleIdentifiers().size());
+            }
+
+            updateContext(schema.get());
+        }
+    }
+
+    @Override
+    public void modifiedService(final ServiceReference<SchemaContextListener> reference, final SchemaContextListener service) {
+        // NOOP
+    }
+
+    @Override
+    public void removedService(final ServiceReference<SchemaContextListener> reference, final SchemaContextListener service) {
+        context.ungetService(reference);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/ProviderContextImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/ProviderContextImpl.java
new file mode 100644 (file)
index 0000000..0c46c67
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker;
+
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.Provider;
+
+class ProviderContextImpl extends ConsumerContextImpl implements ProviderSession {
+    private final Provider provider;
+
+    public ProviderContextImpl(final Provider provider, final BrokerImpl broker) {
+        super(null, broker);
+        this.provider = provider;
+    }
+
+    @Override
+    public void close() {
+    }
+
+    /**
+     * @return the provider
+     */
+    public Provider getProvider() {
+        return provider;
+    }
+
+    /**
+     * @param provider
+     *            the provider to set
+     */
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/SchemaContextProviders.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/SchemaContextProviders.java
new file mode 100644 (file)
index 0000000..6806c5d
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker.impl;
+
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+
+public class SchemaContextProviders {
+
+    private SchemaContextProviders() {
+        throw new UnsupportedOperationException("Utility class.");
+    }
+
+    public static final SchemaContextProvider fromSchemaService(final SchemaService schemaService) {
+        if (schemaService instanceof SchemaContextProvider) {
+            return (SchemaContextProvider) schemaService;
+        }
+        return new SchemaServiceAdapter(schemaService);
+    }
+
+    private static final class SchemaServiceAdapter implements SchemaContextProvider, Delegator<SchemaService> {
+
+        private final SchemaService service;
+
+        public SchemaServiceAdapter(final SchemaService service) {
+            super();
+            this.service = service;
+        }
+
+        @Override
+        public SchemaContext getSchemaContext() {
+            return service.getGlobalContext();
+        }
+
+        @Override
+        public SchemaService getDelegate() {
+            return service;
+        }
+
+        @Override
+        public String toString() {
+            return "SchemaServiceAdapter [service=" + service + "]";
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/AbstractBrokerServiceProxy.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/AbstractBrokerServiceProxy.java
new file mode 100644 (file)
index 0000000..275107e
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker.osgi;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.osgi.framework.ServiceReference;
+
+public abstract class AbstractBrokerServiceProxy<T extends BrokerService> implements AutoCloseable, BrokerService {
+
+    private T delegate;
+    private final ServiceReference<T> reference;
+
+    public AbstractBrokerServiceProxy(final @Nullable ServiceReference<T> ref, final T delegate) {
+        this.delegate = checkNotNull(delegate, "Delegate should not be null.");
+        this.reference = ref;
+    }
+
+    protected final T getDelegate() {
+        checkState(delegate != null, "Proxy was closed and unregistered.");
+        return delegate;
+    }
+
+    protected final ServiceReference<T> getReference() {
+        return reference;
+    }
+
+    private final Set<Registration> registrations = Collections.synchronizedSet(new HashSet<Registration>());
+
+    protected <R extends Registration> R addRegistration(final R registration) {
+        if (registration != null) {
+            registrations.add(registration);
+        }
+        return registration;
+    }
+
+    protected void closeBeforeUnregistrations() {
+        // NOOP
+    }
+
+    protected void closeAfterUnregistrations() {
+        // NOOP
+    }
+
+    @Override
+    public void close() {
+        if (delegate != null) {
+            delegate = null;
+            RuntimeException potentialException = new RuntimeException(
+                    "Uncaught exceptions occured during unregistration");
+            boolean hasSuppressed = false;
+            for (Registration registration : registrations) {
+                try {
+                    registration.close();
+                } catch (Exception e) {
+                    potentialException.addSuppressed(e);
+                    hasSuppressed = true;
+                }
+            }
+            if (hasSuppressed) {
+                throw potentialException;
+            }
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/DOMDataBrokerProxy.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/DOMDataBrokerProxy.java
new file mode 100644 (file)
index 0000000..961e8ee
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.dom.broker.osgi;
+
+import java.util.Map;
+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.osgi.framework.ServiceReference;
+
+public class DOMDataBrokerProxy extends AbstractBrokerServiceProxy<DOMDataBroker> implements DOMDataBroker {
+
+    public DOMDataBrokerProxy(final ServiceReference<DOMDataBroker> ref, final DOMDataBroker delegate) {
+        super(ref, delegate);
+    }
+
+    @Override
+    public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
+        return getDelegate().newReadOnlyTransaction();
+    }
+
+    @Override
+    public DOMDataReadWriteTransaction newReadWriteTransaction() {
+        return getDelegate().newReadWriteTransaction();
+    }
+
+    @Override
+    public DOMDataWriteTransaction newWriteOnlyTransaction() {
+        return getDelegate().newWriteOnlyTransaction();
+    }
+
+    @Override
+    public ListenerRegistration<DOMDataChangeListener> registerDataChangeListener(final LogicalDatastoreType store,
+            final YangInstanceIdentifier path, final DOMDataChangeListener listener,
+            final org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope triggeringScope) {
+        return getDelegate().registerDataChangeListener(store, path, listener, triggeringScope);
+    }
+
+    @Override
+    public DOMTransactionChain createTransactionChain(final TransactionChainListener listener) {
+        return getDelegate().createTransactionChain(listener);
+    }
+
+    @Override
+    public Map<Class<? extends DOMDataBrokerExtension>, DOMDataBrokerExtension> getSupportedExtensions() {
+        return getDelegate().getSupportedExtensions();
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/DOMMountPointServiceProxy.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/DOMMountPointServiceProxy.java
new file mode 100644 (file)
index 0000000..1ce006e
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.dom.broker.osgi;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+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.osgi.framework.ServiceReference;
+
+public class DOMMountPointServiceProxy extends AbstractBrokerServiceProxy<DOMMountPointService> implements DOMMountPointService{
+
+
+    public DOMMountPointServiceProxy(final ServiceReference<DOMMountPointService> ref, final DOMMountPointService delegate) {
+        super(ref, delegate);
+    }
+
+    @Override
+    public Optional<DOMMountPoint> getMountPoint(final YangInstanceIdentifier path) {
+        return getDelegate().getMountPoint(path);
+    }
+
+    @Override
+    public DOMMountPointBuilder createMountPoint(final YangInstanceIdentifier path) {
+        return getDelegate().createMountPoint(path);
+    }
+
+    public ListenerRegistration<MountProvisionListener> registerProvisionListener(final MountProvisionListener listener) {
+        return getDelegate().registerProvisionListener(listener);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/ProxyFactory.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/ProxyFactory.java
new file mode 100644 (file)
index 0000000..c397eec
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker.osgi;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.osgi.framework.ServiceReference;
+
+import java.util.Arrays;
+
+@SuppressWarnings("unchecked")
+public class ProxyFactory {
+
+    public static <T extends BrokerService> T createProxy(
+            final ServiceReference<T> serviceRef, final T service) {
+
+        Object _createProxyImpl = ProxyFactory.createProxyImpl(serviceRef,
+                service);
+        return ((T) _createProxyImpl);
+    }
+
+    private static Object createProxyImpl(final ServiceReference<?> ref,
+            final DOMMountPointService service) {
+
+        return new DOMMountPointServiceProxy(
+                ((ServiceReference<DOMMountPointService>) ref), service);
+    }
+
+    private static Object createProxyImpl(final ServiceReference<?> ref,
+            final SchemaService service) {
+
+        return new SchemaServiceProxy(((ServiceReference<SchemaService>) ref),
+                service);
+    }
+
+    private static DOMDataBrokerProxy createProxyImpl(
+            final ServiceReference<?> ref, final DOMDataBroker service) {
+
+        return new DOMDataBrokerProxy(((ServiceReference<DOMDataBroker>) ref),
+                service);
+    }
+
+    private static Object createProxyImplFallback(final ServiceReference<?> reference,
+            final BrokerService service) {
+
+       return service;
+    }
+
+    private static Object createProxyImpl(final ServiceReference<?> ref,
+            final BrokerService service) {
+
+        if (service instanceof DOMDataBroker) {
+            return createProxyImpl(ref, (DOMDataBroker) service);
+        } else if (service instanceof SchemaService) {
+            return createProxyImpl(ref, (SchemaService) service);
+        } else if (service instanceof DOMMountPointService) {
+            return createProxyImpl(ref, (DOMMountPointService) service);
+        } else if (service != null) {
+            return createProxyImplFallback(ref, service);
+        } else {
+            throw new IllegalArgumentException("Unhandled parameter types: "
+                    + Arrays.<Object> asList(ref, service).toString());
+        }
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/SchemaServiceActivator.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/SchemaServiceActivator.java
new file mode 100644 (file)
index 0000000..37093c7
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker.osgi;
+
+import java.util.Hashtable;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.controller.sal.dom.broker.GlobalBundleScanningSchemaServiceImpl;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+public class SchemaServiceActivator implements BundleActivator {
+
+
+    private ServiceRegistration<SchemaService> schemaServiceReg;
+    private GlobalBundleScanningSchemaServiceImpl schemaService;
+
+    @Override
+    public void start(final BundleContext context) {
+        schemaService = GlobalBundleScanningSchemaServiceImpl.createInstance(context);
+        schemaServiceReg = context.registerService(SchemaService.class, schemaService, new Hashtable<String,String>());
+    }
+
+    @Override
+    public void stop(final BundleContext context) throws Exception {
+        schemaServiceReg.unregister();
+        schemaService.close();
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/SchemaServiceProxy.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/SchemaServiceProxy.java
new file mode 100644 (file)
index 0000000..d8d2346
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker.osgi;
+
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.osgi.framework.ServiceReference;
+
+public class SchemaServiceProxy extends AbstractBrokerServiceProxy<SchemaService> implements SchemaService {
+
+    public SchemaServiceProxy(ServiceReference<SchemaService> ref, SchemaService delegate) {
+        super(ref, delegate);
+    }
+
+    @Override
+    public void addModule(Module module) {
+        getDelegate().addModule(module);
+    }
+
+    @Override
+    public void removeModule(Module module) {
+        getDelegate().removeModule(module);
+    }
+
+    @Override
+    public SchemaContext getSessionContext() {
+        return null;
+    }
+
+    @Override
+    public SchemaContext getGlobalContext() {
+        return getDelegate().getGlobalContext();
+    }
+
+    @Override
+    public ListenerRegistration<SchemaContextListener> registerSchemaContextListener(SchemaContextListener listener) {
+        ListenerRegistration<SchemaContextListener> registration = getDelegate().registerSchemaContextListener(listener);
+        addRegistration(registration);
+        return registration;
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/package-info.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/package-info.java
new file mode 100644 (file)
index 0000000..9793108
--- /dev/null
@@ -0,0 +1,8 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker;
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/util/ProxySchemaContext.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/util/ProxySchemaContext.java
new file mode 100644 (file)
index 0000000..b0274a6
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.dom.broker.util;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.ConstraintDefinition;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
+/**
+ * ProxySchema Context for SchemaContextProviders
+ */
+public class ProxySchemaContext implements SchemaContext {
+
+    private final SchemaContextProvider schemaProvider;
+
+    public ProxySchemaContext(final SchemaContextProvider schemaProvider) {
+        this.schemaProvider = schemaProvider;
+    }
+
+    private SchemaContext getCurrentSchema() {
+        Preconditions.checkState(schemaProvider.getSchemaContext() != null, "Schema context unavailable from %s", schemaProvider);
+        return schemaProvider.getSchemaContext();
+    }
+
+    @Override
+    public Set<DataSchemaNode> getDataDefinitions() {
+        return getCurrentSchema().getDataDefinitions();
+    }
+
+    @Override
+    public Set<Module> getModules() {
+        return getCurrentSchema().getModules();
+    }
+
+    @Override
+    public Set<NotificationDefinition> getNotifications() {
+        return getCurrentSchema().getNotifications();
+    }
+
+    @Override
+    public Set<RpcDefinition> getOperations() {
+        return getCurrentSchema().getOperations();
+    }
+
+    @Override
+    public Set<ExtensionDefinition> getExtensions() {
+        return getCurrentSchema().getExtensions();
+    }
+
+    @Override
+    public Module findModuleByName(final String s, final Date date) {
+        return getCurrentSchema().findModuleByName(s, date);
+    }
+
+    @Override
+    public Set<Module> findModuleByNamespace(final URI uri) {
+        return getCurrentSchema().findModuleByNamespace(uri);
+    }
+
+    @Override
+    public Module findModuleByNamespaceAndRevision(final URI uri, final Date date) {
+        return getCurrentSchema().findModuleByNamespaceAndRevision(uri, date);
+    }
+
+    @Override
+    public Optional<String> getModuleSource(final ModuleIdentifier moduleIdentifier) {
+        return getCurrentSchema().getModuleSource(moduleIdentifier);
+    }
+
+    @Override
+    public Set<ModuleIdentifier> getAllModuleIdentifiers() {
+        return getCurrentSchema().getAllModuleIdentifiers();
+    }
+
+    @Override
+    public boolean isPresenceContainer() {
+        return getCurrentSchema().isPresenceContainer();
+    }
+
+    @Override
+    public Set<TypeDefinition<?>> getTypeDefinitions() {
+        return getCurrentSchema().getTypeDefinitions();
+    }
+
+    @Override
+    public Collection<DataSchemaNode> getChildNodes() {
+        return getCurrentSchema().getChildNodes();
+    }
+
+    @Override
+    public Set<GroupingDefinition> getGroupings() {
+        return getCurrentSchema().getGroupings();
+    }
+
+    @Override
+    public DataSchemaNode getDataChildByName(final QName qName) {
+        return getCurrentSchema().getDataChildByName(qName);
+    }
+
+    @Override
+    public DataSchemaNode getDataChildByName(final String s) {
+        return getCurrentSchema().getDataChildByName(s);
+    }
+
+    @Override
+    public Set<UsesNode> getUses() {
+        return getCurrentSchema().getUses();
+    }
+
+    @Override
+    public Set<AugmentationSchema> getAvailableAugmentations() {
+        return getCurrentSchema().getAvailableAugmentations();
+    }
+
+    @Override
+    public boolean isAugmenting() {
+        return getCurrentSchema().isAugmenting();
+    }
+
+    @Override
+    public boolean isAddedByUses() {
+        return getCurrentSchema().isAddedByUses();
+    }
+
+    @Override
+    public boolean isConfiguration() {
+        return getCurrentSchema().isConfiguration();
+    }
+
+    @Override
+    public ConstraintDefinition getConstraints() {
+        return getCurrentSchema().getConstraints();
+    }
+
+    @Override
+    public QName getQName() {
+        return getCurrentSchema().getQName();
+    }
+
+    @Override
+    public SchemaPath getPath() {
+        return getCurrentSchema().getPath();
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return getCurrentSchema().getUnknownSchemaNodes();
+    }
+
+    @Override
+    public String getDescription() {
+        return getCurrentSchema().getDescription();
+    }
+
+    @Override
+    public String getReference() {
+        return getCurrentSchema().getReference();
+    }
+
+    @Override
+    public Status getStatus() {
+        return getCurrentSchema().getStatus();
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/util/YangSchemaUtils.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/util/YangSchemaUtils.java
new file mode 100644 (file)
index 0000000..238ad10
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.base.Function;
+import com.google.common.collect.FluentIterable;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ConstraintDefinition;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+
+public final class YangSchemaUtils {
+
+    private static final Function<PathArgument, QName> QNAME_FROM_PATH_ARGUMENT = new Function<PathArgument, QName>(){
+
+        @Override
+        public QName apply(final PathArgument input) {
+            if(input == null) {
+                return null;
+            }
+            return input.getNodeType();
+        }
+    };
+
+    private YangSchemaUtils() {
+        throw new UnsupportedOperationException("Utility class.");
+    }
+
+    public static DataSchemaNode getSchemaNode(final SchemaContext schema,final YangInstanceIdentifier path) {
+        checkArgument(schema != null,"YANG Schema must not be null.");
+        checkArgument(path != null,"Path must not be null.");
+        return getSchemaNode(schema, FluentIterable.from(path.getPathArguments()).transform(QNAME_FROM_PATH_ARGUMENT));
+    }
+
+    public static DataSchemaNode getSchemaNode(final SchemaContext schema,final Iterable<QName> path) {
+        checkArgument(schema != null,"YANG Schema must not be null.");
+        checkArgument(path != null,"Path must not be null.");
+        if(!path.iterator().hasNext()){
+            return toRootDataNode(schema);
+        }
+
+        QName firstNode = path.iterator().next();
+        DataNodeContainer previous = schema.findModuleByNamespaceAndRevision(firstNode.getNamespace(),
+                firstNode.getRevision());
+        Iterator<QName> iterator = path.iterator();
+
+        while (iterator.hasNext()) {
+            checkArgument(previous!= null, "Supplied path does not resolve into valid schema node.");
+            QName arg = iterator.next();
+            DataSchemaNode currentNode = previous.getDataChildByName(arg);
+            if (currentNode == null && previous instanceof DataNodeContainer) {
+                currentNode = searchInChoices(previous, arg);
+            }
+            if (currentNode instanceof DataNodeContainer) {
+                previous = (DataNodeContainer) currentNode;
+            } else if (currentNode instanceof LeafSchemaNode || currentNode instanceof LeafListSchemaNode) {
+                checkArgument(!iterator.hasNext(), "Path nests inside leaf node, which is not allowed.");
+                return currentNode;
+            }
+            checkState(currentNode != null, "Current node should not be null for %s",path);
+        }
+        checkState(previous instanceof DataSchemaNode, "Schema node for %s should be instance of DataSchemaNode. Found %s",path,previous);
+        return (DataSchemaNode) previous;
+    }
+
+    private static DataSchemaNode searchInChoices(final DataNodeContainer node, final QName arg) {
+        for (DataSchemaNode child : node.getChildNodes()) {
+            if (child instanceof ChoiceSchemaNode) {
+                ChoiceSchemaNode choiceNode = (ChoiceSchemaNode) child;
+                DataSchemaNode potential = searchInCases(choiceNode, arg);
+                if (potential != null) {
+                    return potential;
+                }
+            }
+        }
+        return null;
+    }
+
+    private static DataSchemaNode searchInCases(final ChoiceSchemaNode choiceNode, final QName arg) {
+        Set<ChoiceCaseNode> cases = choiceNode.getCases();
+        for (ChoiceCaseNode caseNode : cases) {
+            DataSchemaNode node = caseNode.getDataChildByName(arg);
+            if (node != null) {
+                return node;
+            }
+        }
+        return null;
+    }
+
+    private static ContainerSchemaNode toRootDataNode(final SchemaContext schema) {
+        return new NetconfDataRootNode(schema);
+    }
+
+    private static final class NetconfDataRootNode implements ContainerSchemaNode {
+
+        public NetconfDataRootNode(final SchemaContext schema) {
+            // TODO Auto-generated constructor stub
+        }
+
+        @Override
+        public Set<TypeDefinition<?>> getTypeDefinitions() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        public Set<DataSchemaNode> getChildNodes() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        public Set<GroupingDefinition> getGroupings() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        public DataSchemaNode getDataChildByName(final QName name) {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        public DataSchemaNode getDataChildByName(final String name) {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        public Set<UsesNode> getUses() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        public Set<AugmentationSchema> getAvailableAugmentations() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        public boolean isAugmenting() {
+            // TODO Auto-generated method stub
+            return false;
+        }
+
+        @Override
+        public boolean isAddedByUses() {
+            // TODO Auto-generated method stub
+            return false;
+        }
+
+        @Override
+        public boolean isConfiguration() {
+            // TODO Auto-generated method stub
+            return false;
+        }
+
+        @Override
+        public ConstraintDefinition getConstraints() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        public QName getQName() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        public SchemaPath getPath() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        public String getDescription() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        public String getReference() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        public Status getStatus() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        public boolean isPresenceContainer() {
+            // TODO Auto-generated method stub
+            return false;
+        }
+
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/BlockingTransactionChainListener.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/BlockingTransactionChainListener.java
new file mode 100644 (file)
index 0000000..5b531fe
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+
+import com.google.common.util.concurrent.SettableFuture;
+
+/**
+ * Simple implementation of {@link TransactionChainListener} for testing.
+ *
+ * This transaction chain listener does not contain any logic, only update
+ * futures ({@link #getFailFuture()} and {@link #getSuccessFuture()} when
+ * transaction chain event is retrieved.
+ *
+ */
+class BlockingTransactionChainListener implements TransactionChainListener {
+
+    private final SettableFuture<Throwable> failFuture = SettableFuture.create();
+    private final SettableFuture<Void> successFuture = SettableFuture.create();
+
+    @Override
+    public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction,
+            final Throwable cause) {
+        failFuture.set(cause);
+    }
+
+    @Override
+    public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
+        successFuture.set(null);
+    }
+
+    public SettableFuture<Throwable> getFailFuture() {
+        return failFuture;
+    }
+
+    public SettableFuture<Void> getSuccessFuture() {
+        return successFuture;
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerPerformanceTest.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerPerformanceTest.java
new file mode 100644 (file)
index 0000000..41c906d
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+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.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.md.sal.dom.store.impl.TestModel;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DOMBrokerPerformanceTest {
+
+    private static final Logger log = LoggerFactory.getLogger(DOMBrokerPerformanceTest.class);
+
+    private static NormalizedNode<?, ?> outerList(final int i) {
+        return ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i);
+    }
+
+    private static YangInstanceIdentifier outerListPath(final int i) {
+        return YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)//
+                .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i) //
+                .build();
+    }
+
+    private SchemaContext schemaContext;
+    private AbstractDOMDataBroker domBroker;
+
+    private static <V> V measure(final String name, final Callable<V> callable) throws Exception {
+        // TODO Auto-generated method stub
+        log.debug("Measurement:{} Start", name);
+        long startNano = System.nanoTime();
+        try {
+            return callable.call();
+        } finally {
+            long endNano = System.nanoTime();
+            log.info("Measurement:\"{}\" Time:{} ms", name, (endNano - startNano) / 1000000.0d);
+        }
+    }
+
+    @Before
+    public void setupStore() {
+        InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER", MoreExecutors.newDirectExecutorService());
+        InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG", MoreExecutors.newDirectExecutorService());
+        schemaContext = TestModel.createTestContext();
+
+        operStore.onGlobalContextUpdated(schemaContext);
+        configStore.onGlobalContextUpdated(schemaContext);
+
+        ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType, DOMStore> builder() //
+                .put(CONFIGURATION, configStore) //
+                .put(OPERATIONAL, operStore) //
+                .build();
+        ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
+        domBroker = new SerializedDOMDataBroker(stores, executor);
+    }
+
+    @Test
+    public void testPerformance() throws Exception {
+        measure("Test Suite (all tests)", new Callable<Void>() {
+
+            @Override
+            public Void call() throws Exception {
+                smallTestSuite(10, 1000);
+                //smallTestSuite(10, 100);
+                smallTestSuite(100, 100);
+                //smallTestSuite(100, 100);
+                //smallTestSuite(1000, 10);
+                smallTestSuite(1000, 10);
+                //smallTestSuite(1000, 1000);
+                return null;
+            }
+        });
+    }
+
+    private void smallTestSuite(final int txNum, final int innerListWriteNum) throws Exception {
+        measure("TestSuite (Txs:" + txNum + " innerWrites:" + innerListWriteNum + ")", new Callable<Void>() {
+
+            @Override
+            public Void call() throws Exception {
+                measureOneTransactionTopContainer();
+                measureSeparateWritesOneLevel(txNum, innerListWriteNum);
+                return null;
+            }
+        });
+    }
+
+    private void measureSeparateWritesOneLevel(final int txNum, final int innerNum) throws Exception {
+        final List<DOMDataReadWriteTransaction> transactions = measure("Txs:"+ txNum + " Allocate",
+                new Callable<List<DOMDataReadWriteTransaction>>() {
+                    @Override
+                    public List<DOMDataReadWriteTransaction> call() throws Exception {
+                        List<DOMDataReadWriteTransaction> builder = new ArrayList<>(txNum);
+                        for (int i = 0; i < txNum; i++) {
+                            DOMDataReadWriteTransaction writeTx = domBroker.newReadWriteTransaction();
+                            builder.add(writeTx);
+                        }
+                        return builder;
+                    }
+                });
+        assertEquals(txNum, transactions.size());
+        measure("Txs:"+ txNum + " Writes:1", new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                int i = 0;
+                for (DOMDataReadWriteTransaction writeTx :transactions) {
+                    // Writes /test/outer-list/i in writeTx
+                    writeTx.put(OPERATIONAL, outerListPath(i), outerList(i));
+                    i++;
+                }
+                return null;
+            }
+        });
+
+        measure("Txs:"+ txNum +  " Writes:" + innerNum, new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                int i = 0;
+                for (DOMDataReadWriteTransaction writeTx :transactions) {
+                    // Writes /test/outer-list/i in writeTx
+                    YangInstanceIdentifier path = YangInstanceIdentifier.builder(outerListPath(i))
+                            .node(TestModel.INNER_LIST_QNAME).build();
+                    writeTx.put(OPERATIONAL, path, ImmutableNodes.mapNodeBuilder(TestModel.INNER_LIST_QNAME).build());
+                    for (int j = 0; j < innerNum; j++) {
+                        YangInstanceIdentifier innerPath = YangInstanceIdentifier.builder(path)
+                                .nodeWithKey(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, String.valueOf(j))
+                                .build();
+                        writeTx.put(
+                                OPERATIONAL,
+                                innerPath,
+                                ImmutableNodes.mapEntry(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME,
+                                        String.valueOf(j)));
+                    }
+                    i++;
+                }
+                return null;
+            }
+        });
+
+        measure("Txs:" + txNum + " Submit, Finish", new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                List<ListenableFuture<?>> allFutures = measure(txNum + " Submits",
+                        new Callable<List<ListenableFuture<?>>>() {
+                            @Override
+                            public List<ListenableFuture<?>> call() throws Exception {
+                                List<ListenableFuture<?>> builder = new ArrayList<>(txNum);
+                                for (DOMDataReadWriteTransaction tx :transactions) {
+                                    builder.add(tx.submit());
+                                }
+                                return builder;
+                            }
+                        });
+                Futures.allAsList(allFutures).get();
+                return null;
+            }
+        });
+
+        final DOMDataReadTransaction readTx = measure("Txs:1 (ro), Allocate", new Callable<DOMDataReadTransaction>() {
+            @Override
+            public DOMDataReadTransaction call() throws Exception {
+                return domBroker.newReadOnlyTransaction();
+
+            }
+        });
+
+
+        measure("Txs:1 (ro) Reads:" + txNum + " (1-level)" , new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                for (int i = 0; i < txNum; i++) {
+                    ListenableFuture<Optional<NormalizedNode<?, ?>>> potential = readTx.read(OPERATIONAL,
+                            outerListPath(i));
+                    assertTrue("outerList/" + i, potential.get().isPresent());
+                }
+                return null;
+            }
+        });
+
+        measure("Txs:1 (ro) Reads:" + txNum * innerNum + " (2-level)", new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                for (int i = 0; i < txNum; i++) {
+                    for (int j = 0; j < innerNum; j++) {
+                        YangInstanceIdentifier path = YangInstanceIdentifier
+                                .builder(outerListPath(i))
+                                //
+                                .node(TestModel.INNER_LIST_QNAME)
+                                .nodeWithKey(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, String.valueOf(j))
+                                .build();
+                        ListenableFuture<Optional<NormalizedNode<?, ?>>> potential = readTx.read(OPERATIONAL, path);
+                        assertTrue("outer-list/" + i + "/inner-list/" + j, potential.get().isPresent());
+                    }
+                }
+                return null;
+            }
+        });
+    }
+
+    private void measureOneTransactionTopContainer() throws Exception {
+
+        final DOMDataReadWriteTransaction writeTx = measure("Txs:1 Allocate", new Callable<DOMDataReadWriteTransaction>() {
+            @Override
+            public DOMDataReadWriteTransaction call() throws Exception {
+                return domBroker.newReadWriteTransaction();
+            }
+        });
+
+        measure("Txs:1 Write", new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                writeTx.put(OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+                writeTx.put(OPERATIONAL, TestModel.OUTER_LIST_PATH,
+                        ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build());
+                return null;
+            }
+        });
+
+        measure("Txs:1 Reads:1", new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                // Reads /test in writeTx
+                ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(OPERATIONAL,
+                        TestModel.TEST_PATH);
+                assertTrue(writeTxContainer.get().isPresent());
+                return null;
+            }
+        });
+
+        measure("Txs:1 Reads:1", new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                // Reads /test in writeTx
+                ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(OPERATIONAL,
+                        TestModel.TEST_PATH);
+                assertTrue(writeTxContainer.get().isPresent());
+                return null;
+            }
+        });
+
+        measure("Txs:1 Submit, Finish", new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                measure("Txs:1 Submit", new Callable<ListenableFuture<?>>() {
+                    @Override
+                    public ListenableFuture<?> call() throws Exception {
+                        return writeTx.submit();
+                    }
+                }).get();
+                return null;
+            }
+        });
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java
new file mode 100644 (file)
index 0000000..b310715
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ForwardingExecutorService;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitDeadlockException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+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.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.md.sal.dom.store.impl.TestModel;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.yangtools.util.concurrent.DeadlockDetectingListeningExecutorService;
+import org.opendaylight.yangtools.util.concurrent.SpecialExecutors;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class DOMBrokerTest {
+
+    private SchemaContext schemaContext;
+    private AbstractDOMDataBroker domBroker;
+    private ListeningExecutorService executor;
+    private ExecutorService futureExecutor;
+    private CommitExecutorService commitExecutor;
+
+    @Before
+    public void setupStore() {
+
+        InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER",
+                MoreExecutors.newDirectExecutorService());
+        InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG",
+                MoreExecutors.newDirectExecutorService());
+        schemaContext = TestModel.createTestContext();
+
+        operStore.onGlobalContextUpdated(schemaContext);
+        configStore.onGlobalContextUpdated(schemaContext);
+
+        ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType, DOMStore> builder() //
+                .put(CONFIGURATION, configStore) //
+                .put(OPERATIONAL, operStore) //
+                .build();
+
+        commitExecutor = new CommitExecutorService(Executors.newSingleThreadExecutor());
+        futureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool(1, 5, "FCB");
+        executor = new DeadlockDetectingListeningExecutorService(commitExecutor,
+                TransactionCommitDeadlockException.DEADLOCK_EXCEPTION_SUPPLIER, futureExecutor);
+        domBroker = new SerializedDOMDataBroker(stores, executor);
+    }
+
+    @After
+    public void tearDown() {
+        if( executor != null ) {
+            executor.shutdownNow();
+        }
+
+        if(futureExecutor != null) {
+            futureExecutor.shutdownNow();
+        }
+    }
+
+    @Test(timeout=10000)
+    public void testTransactionIsolation() throws InterruptedException, ExecutionException {
+
+        assertNotNull(domBroker);
+
+        DOMDataReadTransaction readTx = domBroker.newReadOnlyTransaction();
+        assertNotNull(readTx);
+
+        DOMDataReadWriteTransaction writeTx = domBroker.newReadWriteTransaction();
+        assertNotNull(writeTx);
+        /**
+         *
+         * Writes /test in writeTx
+         *
+         */
+        writeTx.put(OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+        /**
+         *
+         * Reads /test from writeTx Read should return container.
+         *
+         */
+        ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(OPERATIONAL,
+                TestModel.TEST_PATH);
+        assertTrue(writeTxContainer.get().isPresent());
+
+        /**
+         *
+         * Reads /test from readTx Read should return Absent.
+         *
+         */
+        ListenableFuture<Optional<NormalizedNode<?, ?>>> readTxContainer = readTx
+                .read(OPERATIONAL, TestModel.TEST_PATH);
+        assertFalse(readTxContainer.get().isPresent());
+    }
+
+    @Test(timeout=10000)
+    public void testTransactionCommit() throws InterruptedException, ExecutionException {
+
+        DOMDataReadWriteTransaction writeTx = domBroker.newReadWriteTransaction();
+        assertNotNull(writeTx);
+        /**
+         *
+         * Writes /test in writeTx
+         *
+         */
+        writeTx.put(OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+        /**
+         *
+         * Reads /test from writeTx Read should return container.
+         *
+         */
+        ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(OPERATIONAL,
+                TestModel.TEST_PATH);
+        assertTrue(writeTxContainer.get().isPresent());
+
+        writeTx.submit().get();
+
+        Optional<NormalizedNode<?, ?>> afterCommitRead = domBroker.newReadOnlyTransaction()
+                .read(OPERATIONAL, TestModel.TEST_PATH).get();
+        assertTrue(afterCommitRead.isPresent());
+    }
+
+    @Test(expected=TransactionCommitFailedException.class)
+    public void testRejectedCommit() throws Exception {
+
+        commitExecutor.delegate = Mockito.mock( ExecutorService.class );
+        Mockito.doThrow( new RejectedExecutionException( "mock" ) )
+            .when( commitExecutor.delegate ).execute( Mockito.any( Runnable.class ) );
+        Mockito.doNothing().when( commitExecutor.delegate ).shutdown();
+        Mockito.doReturn( Collections.emptyList() ).when( commitExecutor.delegate ).shutdownNow();
+        Mockito.doReturn( "" ).when( commitExecutor.delegate ).toString();
+        Mockito.doReturn( true ).when( commitExecutor.delegate )
+            .awaitTermination( Mockito.anyLong(), Mockito.any( TimeUnit.class ) );
+
+        DOMDataReadWriteTransaction writeTx = domBroker.newReadWriteTransaction();
+        writeTx.put( OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME) );
+
+        writeTx.submit().checkedGet( 5, TimeUnit.SECONDS );
+    }
+
+    /**
+     * Tests a simple DataChangeListener notification after a write.
+     */
+    @Test
+    public void testDataChangeListener() throws Throwable {
+
+        final NormalizedNode<?, ?> testNode = ImmutableNodes.containerNode( TestModel.TEST_QNAME );
+
+        TestDOMDataChangeListener dcListener = new TestDOMDataChangeListener();
+
+        domBroker.registerDataChangeListener( OPERATIONAL, TestModel.TEST_PATH,
+                                              dcListener, DataChangeScope.BASE );
+
+        final DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+        assertNotNull( writeTx );
+
+        writeTx.put( OPERATIONAL, TestModel.TEST_PATH, testNode );
+
+        AtomicReference<Throwable> caughtEx = submitTxAsync( writeTx );
+
+        dcListener.waitForChange();
+
+        if( caughtEx.get() != null ) {
+            throw caughtEx.get();
+        }
+
+        NormalizedNode<?, ?> actualNode = dcListener.change.getCreatedData().get( TestModel.TEST_PATH );
+        assertEquals( "Created node", testNode, actualNode );
+    }
+
+    /**
+     * Tests a DataChangeListener that does an async submit of a write Tx in its onDataChanged method.
+     * This should succeed without deadlock.
+     */
+    @Test
+    public void testDataChangeListenerDoingAsyncWriteTxSubmit() throws Throwable {
+
+        final AtomicReference<Throwable> caughtCommitEx = new AtomicReference<>();
+        final CountDownLatch commitCompletedLatch = new CountDownLatch( 1 );
+
+        TestDOMDataChangeListener dcListener = new TestDOMDataChangeListener() {
+            @Override
+            public void onDataChanged( final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change ) {
+
+                DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+                writeTx.put( OPERATIONAL, TestModel.TEST2_PATH,
+                             ImmutableNodes.containerNode( TestModel.TEST2_QNAME ) );
+                Futures.addCallback( writeTx.submit(), new FutureCallback<Void>() {
+                    @Override
+                    public void onSuccess( final Void result ) {
+                        commitCompletedLatch.countDown();
+                    }
+
+                    @Override
+                    public void onFailure( final Throwable t ) {
+                        caughtCommitEx.set( t );
+                        commitCompletedLatch.countDown();
+                    }
+                } );
+
+                super.onDataChanged( change );
+            }
+        };
+
+        domBroker.registerDataChangeListener( OPERATIONAL, TestModel.TEST_PATH,
+                                              dcListener, DataChangeScope.BASE );
+
+        final DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+        assertNotNull( writeTx );
+
+        writeTx.put( OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode( TestModel.TEST_QNAME ) );
+
+        AtomicReference<Throwable> caughtEx = submitTxAsync( writeTx );
+
+        dcListener.waitForChange();
+
+        if( caughtEx.get() != null ) {
+            throw caughtEx.get();
+        }
+
+        assertTrue( "Commit Future was not invoked", commitCompletedLatch.await( 5, TimeUnit.SECONDS ) );
+
+        if( caughtCommitEx.get() != null ) {
+            throw caughtCommitEx.get();
+        }
+    }
+
+    /**
+     * Tests a DataChangeListener that does a blocking submit of a write Tx in its onDataChanged method.
+     * This should throw an exception and not deadlock.
+     */
+    @Test(expected=TransactionCommitDeadlockException.class)
+    public void testDataChangeListenerDoingBlockingWriteTxSubmit() throws Throwable {
+
+        final AtomicReference<Throwable> caughtCommitEx = new AtomicReference<>();
+
+        TestDOMDataChangeListener dcListener = new TestDOMDataChangeListener() {
+            @Override
+            public void onDataChanged( final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change ) {
+                DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+                writeTx.put( OPERATIONAL, TestModel.TEST2_PATH,
+                             ImmutableNodes.containerNode( TestModel.TEST2_QNAME ) );
+                try {
+                    writeTx.submit().get();
+                } catch( ExecutionException e ) {
+                    caughtCommitEx.set( e.getCause() );
+                } catch( Exception e ) {
+                    caughtCommitEx.set( e );
+                }
+                finally {
+                    super.onDataChanged( change );
+                }
+            }
+        };
+
+        domBroker.registerDataChangeListener( OPERATIONAL, TestModel.TEST_PATH,
+                                              dcListener, DataChangeScope.BASE );
+
+        final DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+        assertNotNull( writeTx );
+
+        writeTx.put( OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode( TestModel.TEST_QNAME ) );
+
+        AtomicReference<Throwable> caughtEx = submitTxAsync( writeTx );
+
+        dcListener.waitForChange();
+
+        if( caughtEx.get() != null ) {
+            throw caughtEx.get();
+        }
+
+        if( caughtCommitEx.get() != null ) {
+            throw caughtCommitEx.get();
+        }
+    }
+
+    AtomicReference<Throwable> submitTxAsync( final DOMDataWriteTransaction writeTx ) {
+        final AtomicReference<Throwable> caughtEx = new AtomicReference<>();
+        new Thread() {
+            @Override
+            public void run() {
+
+                try {
+                    writeTx.submit();
+                } catch( Throwable e ) {
+                    caughtEx.set( e );
+                }
+            }
+
+        }.start();
+
+        return caughtEx;
+    }
+
+    static class TestDOMDataChangeListener implements DOMDataChangeListener {
+
+        volatile AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change;
+        private final CountDownLatch latch = new CountDownLatch( 1 );
+
+        @Override
+        public void onDataChanged( final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change ) {
+            this.change = change;
+            latch.countDown();
+        }
+
+        void waitForChange() throws InterruptedException {
+            assertTrue( "onDataChanged was not called", latch.await( 5, TimeUnit.SECONDS ) );
+        }
+    }
+
+    static class CommitExecutorService extends ForwardingExecutorService {
+
+        ExecutorService delegate;
+
+        public CommitExecutorService( final ExecutorService delegate ) {
+            this.delegate = delegate;
+        }
+
+        @Override
+        protected ExecutorService delegate() {
+            return delegate;
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataTreeListenerTest.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataTreeListenerTest.java
new file mode 100644 (file)
index 0000000..4bf205a
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ForwardingExecutorService;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import javax.annotation.Nonnull;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitDeadlockException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.md.sal.dom.store.impl.TestModel;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.util.concurrent.DeadlockDetectingListeningExecutorService;
+import org.opendaylight.yangtools.util.concurrent.SpecialExecutors;
+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.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.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class DOMDataTreeListenerTest {
+
+    private SchemaContext schemaContext;
+    private AbstractDOMDataBroker domBroker;
+    private ListeningExecutorService executor;
+    private ExecutorService futureExecutor;
+    private CommitExecutorService commitExecutor;
+
+    private static final DataContainerChild<?, ?> OUTER_LIST = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+            .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1))
+            .build();
+
+    private static final DataContainerChild<?, ?> OUTER_LIST_2 = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+            .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2))
+            .build();
+
+    private static final NormalizedNode<?, ?> TEST_CONTAINER = Builders.containerBuilder()
+            .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
+            .withChild(OUTER_LIST)
+            .build();
+
+    private static final NormalizedNode<?, ?> TEST_CONTAINER_2 = Builders.containerBuilder()
+            .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
+            .withChild(OUTER_LIST_2)
+            .build();
+
+    private static DOMDataTreeIdentifier ROOT_DATA_TREE_ID = new DOMDataTreeIdentifier(
+            LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
+
+    private static DOMDataTreeIdentifier OUTER_LIST_DATA_TREE_ID = new DOMDataTreeIdentifier(
+            LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH);
+
+    @Before
+    public void setupStore() {
+        InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER",
+                MoreExecutors.newDirectExecutorService());
+        InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG",
+                MoreExecutors.newDirectExecutorService());
+        schemaContext = TestModel.createTestContext();
+
+        operStore.onGlobalContextUpdated(schemaContext);
+        configStore.onGlobalContextUpdated(schemaContext);
+
+        ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType, DOMStore>builder() //
+                .put(CONFIGURATION, configStore) //
+                .put(OPERATIONAL, operStore) //
+                .build();
+
+        commitExecutor = new CommitExecutorService(Executors.newSingleThreadExecutor());
+        futureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool(1, 5, "FCB");
+        executor = new DeadlockDetectingListeningExecutorService(commitExecutor,
+                TransactionCommitDeadlockException.DEADLOCK_EXCEPTION_SUPPLIER, futureExecutor);
+        domBroker = new SerializedDOMDataBroker(stores, executor);
+    }
+
+    @After
+    public void tearDown() {
+        if (executor != null) {
+            executor.shutdownNow();
+        }
+
+        if (futureExecutor != null) {
+            futureExecutor.shutdownNow();
+        }
+    }
+
+    @Test
+    public void writeContainerEmptyTreeTest() throws InterruptedException {
+        CountDownLatch latch = new CountDownLatch(1);
+
+        DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
+        assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
+                dataTreeChangeService);
+
+        final TestDataTreeListener listener = new TestDataTreeListener(latch);
+        final ListenerRegistration<TestDataTreeListener> listenerReg =
+                dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
+
+        final DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
+        writeTx.submit();
+
+        latch.await(5, TimeUnit.SECONDS);
+
+        assertEquals(1, listener.getReceivedChanges().size());
+        final Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
+        assertEquals(1, changes.size());
+
+        DataTreeCandidate candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        DataTreeCandidateNode candidateRoot = candidate.getRootNode();
+        checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
+        listenerReg.close();
+    }
+
+    @Test
+    public void replaceContainerContainerInTreeTest() throws InterruptedException, TransactionCommitFailedException {
+        CountDownLatch latch = new CountDownLatch(2);
+
+        DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
+        assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
+                dataTreeChangeService);
+
+        DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
+        writeTx.submit().checkedGet();
+
+        final TestDataTreeListener listener = new TestDataTreeListener(latch);
+        final ListenerRegistration<TestDataTreeListener> listenerReg =
+                dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
+        writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
+        writeTx.submit();
+
+        latch.await(5, TimeUnit.SECONDS);
+
+        assertEquals(2, listener.getReceivedChanges().size());
+        Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
+        assertEquals(1, changes.size());
+
+        DataTreeCandidate candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        DataTreeCandidateNode candidateRoot = candidate.getRootNode();
+        checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
+
+        changes = listener.getReceivedChanges().get(1);
+        assertEquals(1, changes.size());
+
+        candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        candidateRoot = candidate.getRootNode();
+        checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.WRITE, candidateRoot);
+        listenerReg.close();
+    }
+
+    @Test
+    public void deleteContainerContainerInTreeTest() throws InterruptedException, TransactionCommitFailedException {
+        CountDownLatch latch = new CountDownLatch(2);
+
+        DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
+        assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
+                dataTreeChangeService);
+
+        DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
+        writeTx.submit().checkedGet();
+
+        final TestDataTreeListener listener = new TestDataTreeListener(latch);
+        final ListenerRegistration<TestDataTreeListener> listenerReg =
+                dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
+
+        writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
+        writeTx.submit();
+
+        latch.await(5, TimeUnit.SECONDS);
+
+        assertEquals(2, listener.getReceivedChanges().size());
+        Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
+        assertEquals(1, changes.size());
+
+        DataTreeCandidate candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        DataTreeCandidateNode candidateRoot = candidate.getRootNode();
+        checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
+
+        changes = listener.getReceivedChanges().get(1);
+        assertEquals(1, changes.size());
+
+        candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        candidateRoot = candidate.getRootNode();
+        checkChange(TEST_CONTAINER, null, ModificationType.DELETE, candidateRoot);
+        listenerReg.close();
+    }
+
+    @Test
+    public void replaceChildListContainerInTreeTest() throws InterruptedException, TransactionCommitFailedException {
+        CountDownLatch latch = new CountDownLatch(2);
+
+        DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
+        assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
+                dataTreeChangeService);
+
+        DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
+        writeTx.submit().checkedGet();
+
+        final TestDataTreeListener listener = new TestDataTreeListener(latch);
+        final ListenerRegistration<TestDataTreeListener> listenerReg =
+                dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
+
+        writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH, OUTER_LIST_2);
+        writeTx.submit();
+
+        latch.await(5, TimeUnit.SECONDS);
+
+        assertEquals(2, listener.getReceivedChanges().size());
+        Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
+        assertEquals(1, changes.size());
+
+        DataTreeCandidate candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        DataTreeCandidateNode candidateRoot = candidate.getRootNode();
+        checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
+
+        changes = listener.getReceivedChanges().get(1);
+        assertEquals(1, changes.size());
+
+        candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        candidateRoot = candidate.getRootNode();
+        checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.SUBTREE_MODIFIED, candidateRoot);
+        final DataTreeCandidateNode modifiedChild = candidateRoot.getModifiedChild(
+                new YangInstanceIdentifier.NodeIdentifier(TestModel.OUTER_LIST_QNAME));
+        assertNotNull(modifiedChild);
+        checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, modifiedChild);
+        listenerReg.close();
+    }
+
+    @Test
+    public void rootModificationChildListenerTest() throws InterruptedException, TransactionCommitFailedException {
+        CountDownLatch latch = new CountDownLatch(2);
+
+        DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
+        assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
+                dataTreeChangeService);
+
+        DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
+        writeTx.submit().checkedGet();
+
+        final TestDataTreeListener listener = new TestDataTreeListener(latch);
+        final ListenerRegistration<TestDataTreeListener> listenerReg =
+                dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
+
+        writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
+        writeTx.submit().checkedGet();
+
+        latch.await(1, TimeUnit.SECONDS);
+
+        assertEquals(2, listener.getReceivedChanges().size());
+        Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
+        assertEquals(1, changes.size());
+
+        DataTreeCandidate candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        DataTreeCandidateNode candidateRoot = candidate.getRootNode();
+        checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
+
+        changes = listener.getReceivedChanges().get(1);
+        assertEquals(1, changes.size());
+
+        candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        candidateRoot = candidate.getRootNode();
+        checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, candidateRoot);
+        listenerReg.close();
+    }
+
+    @Test
+    public void listEntryChangeNonRootRegistrationTest() throws InterruptedException, TransactionCommitFailedException {
+        CountDownLatch latch = new CountDownLatch(2);
+
+        DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
+        assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
+                dataTreeChangeService);
+
+        DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
+        writeTx.submit().checkedGet();
+
+        final TestDataTreeListener listener = new TestDataTreeListener(latch);
+        final ListenerRegistration<TestDataTreeListener> listenerReg =
+                dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
+
+        final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId1 =
+                new YangInstanceIdentifier.NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
+        final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId2 =
+                new YangInstanceIdentifier.NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
+        final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId3 =
+                new YangInstanceIdentifier.NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
+
+        final MapEntryNode outerListEntry1 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
+        final MapEntryNode outerListEntry2 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
+        final MapEntryNode outerListEntry3 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
+
+        final MapNode listAfter = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+                .withChild(outerListEntry2)
+                .withChild(outerListEntry3)
+                .build();
+
+        writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId1));
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId2),
+                outerListEntry2);
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId3),
+                outerListEntry3);
+        writeTx.submit();
+
+        latch.await(5, TimeUnit.SECONDS);
+
+        assertEquals(2, listener.getReceivedChanges().size());
+        Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
+        assertEquals(1, changes.size());
+
+        DataTreeCandidate candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        DataTreeCandidateNode candidateRoot = candidate.getRootNode();
+        checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
+
+        changes = listener.getReceivedChanges().get(1);
+        assertEquals(1, changes.size());
+
+        candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        candidateRoot = candidate.getRootNode();
+        checkChange(OUTER_LIST, listAfter, ModificationType.SUBTREE_MODIFIED, candidateRoot);
+        final DataTreeCandidateNode entry1Canditate = candidateRoot.getModifiedChild(outerListEntryId1);
+        checkChange(outerListEntry1, null, ModificationType.DELETE, entry1Canditate);
+        final DataTreeCandidateNode entry2Canditate = candidateRoot.getModifiedChild(outerListEntryId2);
+        checkChange(null, outerListEntry2, ModificationType.WRITE, entry2Canditate);
+        final DataTreeCandidateNode entry3Canditate = candidateRoot.getModifiedChild(outerListEntryId3);
+        checkChange(null, outerListEntry3, ModificationType.WRITE, entry3Canditate);
+        listenerReg.close();
+    }
+
+    private static void checkChange(final NormalizedNode<?, ?> expectedBefore,
+                                    final NormalizedNode<?, ?> expectedAfter,
+                                    final ModificationType expectedMod,
+                                    final DataTreeCandidateNode candidateNode) {
+        if (expectedBefore != null) {
+            assertTrue(candidateNode.getDataBefore().isPresent());
+            assertEquals(expectedBefore, candidateNode.getDataBefore().get());
+        } else {
+            assertFalse(candidateNode.getDataBefore().isPresent());
+        }
+
+        if (expectedAfter != null) {
+            assertTrue(candidateNode.getDataAfter().isPresent());
+            assertEquals(expectedAfter, candidateNode.getDataAfter().get());
+        } else {
+            assertFalse(candidateNode.getDataAfter().isPresent());
+        }
+
+        assertEquals(expectedMod, candidateNode.getModificationType());
+    }
+
+    private DOMDataTreeChangeService getDOMDataTreeChangeService() {
+        final DOMDataBrokerExtension extension = domBroker.getSupportedExtensions()
+                .get(DOMDataTreeChangeService.class);
+        if (extension == null) {
+            return null;
+        }
+        DOMDataTreeChangeService dataTreeChangeService = null;
+        if (extension instanceof DOMDataTreeChangeService) {
+            dataTreeChangeService = (DOMDataTreeChangeService) extension;
+        }
+        return dataTreeChangeService;
+    }
+
+
+    static class CommitExecutorService extends ForwardingExecutorService {
+
+        ExecutorService delegate;
+
+        public CommitExecutorService(final ExecutorService delegate) {
+            this.delegate = delegate;
+        }
+
+        @Override
+        protected ExecutorService delegate() {
+            return delegate;
+        }
+    }
+
+    static class TestDataTreeListener implements DOMDataTreeChangeListener {
+
+        private final List<Collection<DataTreeCandidate>> receivedChanges = new ArrayList<>();
+        private final CountDownLatch latch;
+
+        public TestDataTreeListener(final CountDownLatch latch) {
+            this.latch = latch;
+        }
+
+        @Override
+        public void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
+            receivedChanges.add(changes);
+            latch.countDown();
+        }
+
+        public List<Collection<DataTreeCandidate>> getReceivedChanges() {
+            return receivedChanges;
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMTransactionChainTest.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMTransactionChainTest.java
new file mode 100644 (file)
index 0000000..aba78f7
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+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.DOMTransactionChain;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.md.sal.dom.store.impl.TestModel;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class DOMTransactionChainTest {
+
+    private SchemaContext schemaContext;
+    private AbstractDOMDataBroker domBroker;
+
+    @Before
+    public void setupStore() {
+        InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER", MoreExecutors.newDirectExecutorService());
+        InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG", MoreExecutors.newDirectExecutorService());
+        schemaContext = TestModel.createTestContext();
+
+        operStore.onGlobalContextUpdated(schemaContext);
+        configStore.onGlobalContextUpdated(schemaContext);
+
+        ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType, DOMStore> builder() //
+                .put(CONFIGURATION, configStore) //
+                .put(OPERATIONAL, operStore) //
+                .build();
+
+        ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
+        domBroker = new SerializedDOMDataBroker(stores, executor);
+    }
+
+    @Test
+    public void testTransactionChainNoConflict() throws InterruptedException, ExecutionException, TimeoutException {
+        BlockingTransactionChainListener listener = new BlockingTransactionChainListener();
+        DOMTransactionChain txChain = domBroker.createTransactionChain(listener);
+        assertNotNull(txChain);
+
+        /**
+         * We alocate new read-write transaction and write /test
+         *
+         *
+         */
+        DOMDataReadWriteTransaction firstTx = allocateAndWrite(txChain);
+
+        /**
+         * First transaction is marked as ready, we are able to allocate chained
+         * transactions
+         */
+        ListenableFuture<Void> firstWriteTxFuture = firstTx.submit();
+
+        /**
+         * We alocate chained transaction - read transaction.
+         */
+        DOMDataReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
+
+        /**
+         *
+         * We test if we are able to read data from tx, read should not fail
+         * since we are using chained transaction.
+         *
+         *
+         */
+        assertTestContainerExists(secondReadTx);
+
+        /**
+         *
+         * We alocate next transaction, which is still based on first one, but
+         * is read-write.
+         *
+         */
+        DOMDataReadWriteTransaction thirdDeleteTx = allocateAndDelete(txChain);
+
+        /**
+         * We commit first transaction
+         *
+         */
+        assertCommitSuccessful(firstWriteTxFuture);
+
+        /**
+         *
+         * Allocates transaction from data store.
+         *
+         */
+        DOMDataReadTransaction storeReadTx = domBroker.newReadOnlyTransaction();
+
+        /**
+         * We verify transaction is commited to store, container should exists
+         * in datastore.
+         */
+        assertTestContainerExists(storeReadTx);
+
+        /**
+         * third transaction is sealed and commited
+         */
+        ListenableFuture<Void> thirdDeleteTxFuture = thirdDeleteTx.submit();
+        assertCommitSuccessful(thirdDeleteTxFuture);
+
+        /**
+         * We close transaction chain.
+         */
+        txChain.close();
+
+        listener.getSuccessFuture().get(1000, TimeUnit.MILLISECONDS);
+    }
+
+    @Test
+    public void testTransactionChainNotSealed() throws InterruptedException, ExecutionException, TimeoutException {
+        BlockingTransactionChainListener listener = new BlockingTransactionChainListener();
+        DOMTransactionChain txChain = domBroker.createTransactionChain(listener);
+        assertNotNull(txChain);
+
+        /**
+         * We alocate new read-write transaction and write /test
+         *
+         *
+         */
+        allocateAndWrite(txChain);
+
+        /**
+         * We alocate chained transaction - read transaction, note first one is
+         * still not commited to datastore, so this allocation should fail with
+         * IllegalStateException.
+         */
+        try {
+            txChain.newReadOnlyTransaction();
+            fail("Allocation of secondReadTx should fail with IllegalStateException");
+        } catch (Exception e) {
+            assertTrue(e instanceof IllegalStateException);
+        }
+    }
+
+    private static DOMDataReadWriteTransaction allocateAndDelete(final DOMTransactionChain txChain)
+            throws InterruptedException, ExecutionException {
+        DOMDataReadWriteTransaction tx = txChain.newReadWriteTransaction();
+
+        /**
+         * We test existence of /test in third transaction container should
+         * still be visible from first one (which is still uncommmited).
+         *
+         */
+        assertTestContainerExists(tx);
+
+        /**
+         * We delete node in third transaction
+         */
+        tx.delete(LogicalDatastoreType.OPERATIONAL, TestModel.TEST_PATH);
+        return tx;
+    }
+
+    private static DOMDataReadWriteTransaction allocateAndWrite(final DOMTransactionChain txChain)
+            throws InterruptedException, ExecutionException {
+        DOMDataReadWriteTransaction tx = txChain.newReadWriteTransaction();
+        assertTestContainerWrite(tx);
+        return tx;
+    }
+
+    private static void assertCommitSuccessful(final ListenableFuture<Void> future)
+            throws InterruptedException, ExecutionException {
+        future.get();
+    }
+
+    private static void assertTestContainerExists(final DOMDataReadTransaction readTx) throws InterruptedException,
+            ExecutionException {
+        ListenableFuture<Optional<NormalizedNode<?, ?>>> readFuture = readTx.read(OPERATIONAL, TestModel.TEST_PATH);
+        Optional<NormalizedNode<?, ?>> readedData = readFuture.get();
+        assertTrue(readedData.isPresent());
+    }
+
+    private static void assertTestContainerWrite(final DOMDataReadWriteTransaction tx) throws InterruptedException,
+            ExecutionException {
+        tx.put(OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        assertTestContainerExists(tx);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/MountPointServiceTest.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/MountPointServiceTest.java
new file mode 100644 (file)
index 0000000..ef34e03
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.base.Optional;
+import org.junit.Before;
+import org.junit.Test;
+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.DOMMountPointService.DOMMountPointBuilder;
+import org.opendaylight.controller.md.sal.dom.broker.impl.mount.DOMMountPointServiceImpl;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+public class MountPointServiceTest {
+
+    private DOMMountPointService mountService;
+    private static final YangInstanceIdentifier PATH = YangInstanceIdentifier.of(QName.create("namespace", "12-12-2012", "top"));
+
+    @Before
+    public void setup() {
+        mountService = new DOMMountPointServiceImpl();
+    }
+
+    @Test
+    public void createSimpleMountPoint() {
+        Optional<DOMMountPoint> mountNotPresent = mountService.getMountPoint(PATH);
+        assertFalse(mountNotPresent.isPresent());
+        DOMMountPointBuilder mountBuilder = mountService.createMountPoint(PATH);
+        mountBuilder.register();
+
+        Optional<DOMMountPoint> mountPresent = mountService.getMountPoint(PATH);
+        assertTrue(mountPresent.isPresent());
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/jmx/CommitStatsMXBeanImplTest.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/jmx/CommitStatsMXBeanImplTest.java
new file mode 100644 (file)
index 0000000..613b7a6
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl.jmx;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.util.DurationStatsTracker;
+
+/**
+ * Unit tests for CommitStatsMXBeanImpl.
+ *
+ * @author Thomas Pantelis
+ */
+public class CommitStatsMXBeanImplTest {
+
+    @Test
+    public void test() {
+
+        DurationStatsTracker commitStatsTracker = new DurationStatsTracker();
+        CommitStatsMXBeanImpl bean =
+                new CommitStatsMXBeanImpl(commitStatsTracker, "Test");
+
+        commitStatsTracker.addDuration(100);
+
+        assertEquals("getTotalCommits", 1L, bean.getTotalCommits());
+        assertNotNull(bean.getLongestCommitTime());
+        assertNotNull(bean.getShortestCommitTime());
+        assertNotNull(bean.getAverageCommitTime());
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestModel.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestModel.java
new file mode 100644 (file)
index 0000000..09835ec
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.Set;
+
+public class TestModel {
+
+    public static final QName TEST_QNAME = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test", "2014-03-13",
+            "test");
+    public static final QName TEST2_QNAME = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test", "2014-03-13",
+            "test2");
+    public static final QName OUTER_LIST_QNAME = QName.create(TEST_QNAME, "outer-list");
+    public static final QName INNER_LIST_QNAME = QName.create(TEST_QNAME, "inner-list");
+    public static final QName OUTER_CHOICE_QNAME = QName.create(TEST_QNAME, "outer-choice");
+    public static final QName ID_QNAME = QName.create(TEST_QNAME, "id");
+    public static final QName NAME_QNAME = QName.create(TEST_QNAME, "name");
+    public static final QName VALUE_QNAME = QName.create(TEST_QNAME, "value");
+    private static final String DATASTORE_TEST_YANG = "/odl-datastore-test.yang";
+
+    public static final YangInstanceIdentifier TEST_PATH = YangInstanceIdentifier.of(TEST_QNAME);
+    public static final YangInstanceIdentifier TEST2_PATH = YangInstanceIdentifier.of(TEST2_QNAME);
+    public static final YangInstanceIdentifier OUTER_LIST_PATH = YangInstanceIdentifier.builder(TEST_PATH).node(OUTER_LIST_QNAME).build();
+    public static final QName TWO_QNAME = QName.create(TEST_QNAME,"two");
+    public static final QName THREE_QNAME = QName.create(TEST_QNAME,"three");
+
+
+    public static final InputStream getDatastoreTestInputStream() {
+        return getInputStream(DATASTORE_TEST_YANG);
+    }
+
+    private static InputStream getInputStream(final String resourceName) {
+        return TestModel.class.getResourceAsStream(DATASTORE_TEST_YANG);
+    }
+
+    public static SchemaContext createTestContext() {
+        YangParserImpl parser = new YangParserImpl();
+        Set<Module> modules = parser.parseYangModelsFromStreams(Collections.singletonList(getDatastoreTestInputStream()));
+        return parser.resolveSchemaContext(modules);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/resources/odl-datastore-test.yang b/opendaylight/md-sal/sal-dom-broker/src/test/resources/odl-datastore-test.yang
new file mode 100644 (file)
index 0000000..5fbf470
--- /dev/null
@@ -0,0 +1,45 @@
+module odl-datastore-test {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test";
+    prefix "store-test";
+    
+    revision "2014-03-13" {
+        description "Initial revision.";
+    }
+
+    container test {
+        list outer-list {
+            key id;
+            leaf id {
+                type uint16;
+            }
+            choice outer-choice {
+                case one {
+                    leaf one {
+                        type string;
+                    }
+                }
+                case two-three {
+                    leaf two {
+                        type string;
+                    }
+                    leaf three {
+                        type string;
+                    }
+               }
+           }
+           list inner-list {
+                key name;
+                leaf name {
+                    type string;
+                }
+                leaf value {
+                    type string;
+                }
+            }
+        }
+    }
+
+    container test2 {
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-dom-spi/pom.xml b/opendaylight/md-sal/sal-dom-spi/pom.xml
new file mode 100644 (file)
index 0000000..bd1606a
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>sal-parent</artifactId>
+    <version>1.3.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>sal-core-spi</artifactId>
+  <packaging>bundle</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-core-api</artifactId>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.opendaylight.yangtools</groupId>
+        <artifactId>yang-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>config</id>
+            <goals>
+              <goal>generate-sources</goal>
+            </goals>
+            <configuration>
+              <codeGenerators>
+                <generator>
+                  <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
+                  <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+                  <additionalConfiguration>
+                    <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
+                  </additionalConfiguration>
+                </generator>
+                <generator>
+                  <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+                  <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+                </generator>
+              </codeGenerators>
+              <inspectDependencies>true</inspectDependencies>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <scm>
+    <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+    <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+    <tag>HEAD</tag>
+    <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+  </scm>
+</project>
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/broker/spi/mount/SimpleDOMMountPoint.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/broker/spi/mount/SimpleDOMMountPoint.java
new file mode 100644 (file)
index 0000000..f696b20
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.spi.mount;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMService;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableClassToInstanceMap;
+
+public class SimpleDOMMountPoint implements DOMMountPoint {
+
+    private final YangInstanceIdentifier identifier;
+    private final ClassToInstanceMap<DOMService> services;
+    private final SchemaContext schemaContext;
+
+    public static final SimpleDOMMountPoint create(final YangInstanceIdentifier identifier, final ClassToInstanceMap<DOMService> services, final SchemaContext ctx) {
+        return new SimpleDOMMountPoint(identifier, services, ctx);
+    }
+    private SimpleDOMMountPoint(final YangInstanceIdentifier identifier, final ClassToInstanceMap<DOMService> services, final SchemaContext ctx) {
+        this.identifier = identifier;
+        this.services = ImmutableClassToInstanceMap.copyOf(services);
+        this.schemaContext = ctx;
+    }
+
+    @Override
+    public YangInstanceIdentifier getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public SchemaContext getSchemaContext() {
+        return schemaContext;
+    }
+
+    @Override
+    public <T extends DOMService> Optional<T> getService(final Class<T> cls) {
+        return Optional.fromNullable(services.getInstance(cls));
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/broker/spi/rpc/RpcRoutingStrategy.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/broker/spi/rpc/RpcRoutingStrategy.java
new file mode 100644 (file)
index 0000000..6c8f37b
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.spi.rpc;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+public abstract class RpcRoutingStrategy implements Identifiable<QName> {
+
+    private static final QName CONTEXT_REFERENCE = QName.cachedReference(QName.create("urn:opendaylight:yang:extension:yang-ext",
+            "2013-07-09", "context-reference"));
+    private final QName identifier;
+
+    private RpcRoutingStrategy(final QName identifier) {
+        this.identifier = Preconditions.checkNotNull(identifier);
+    }
+
+    /**
+     * Returns leaf QName in which RPC Route is stored
+     *
+     *
+     * @return leaf QName in which RPC Route is stored
+     * @throws UnsupportedOperationException If RPC is not content routed.
+     *  ({@link #isContextBasedRouted()} returned <code>false</code>)
+     */
+    public abstract QName getLeaf();
+
+    /**
+     * Returns identity QName which represents RPC Routing context
+     *
+     * @return identity QName which represents RPC Routing context
+     * @throws UnsupportedOperationException If RPC is not content routed.
+     *  ({@link #isContextBasedRouted()} returned <code>false</code>)
+     */
+    public abstract QName getContext();
+
+    @Override
+    public final QName getIdentifier() {
+        return identifier;
+    }
+
+    /**
+     * Returns true if RPC is routed by context.
+     *
+     * @return true if RPC is routed by content.
+     */
+    public abstract boolean isContextBasedRouted();
+
+    public static RpcRoutingStrategy from(final RpcDefinition rpc) {
+        ContainerSchemaNode input = rpc.getInput();
+        if (input != null) {
+            for (DataSchemaNode schemaNode : input.getChildNodes()) {
+                Optional<QName> context = getRoutingContext(schemaNode);
+                if (context.isPresent()) {
+                    return new RoutedRpcStrategy(rpc.getQName(), context.get(), schemaNode.getQName());
+                }
+            }
+        }
+        return new GlobalRpcStrategy(rpc.getQName());
+    }
+
+    public static Optional<QName> getRoutingContext(final DataSchemaNode schemaNode) {
+        for (UnknownSchemaNode extension : schemaNode.getUnknownSchemaNodes()) {
+            if (CONTEXT_REFERENCE.equals(extension.getNodeType())) {
+                return Optional.fromNullable(extension.getQName());
+            }
+        }
+        return Optional.absent();
+    }
+
+    private static final class RoutedRpcStrategy extends RpcRoutingStrategy {
+        private final QName context;
+        private final QName leaf;
+
+        private RoutedRpcStrategy(final QName identifier, final QName ctx, final QName leaf) {
+            super(identifier);
+            this.context = Preconditions.checkNotNull(ctx);
+            this.leaf = Preconditions.checkNotNull(leaf);
+        }
+
+        @Override
+        public QName getContext() {
+            return context;
+        }
+
+        @Override
+        public QName getLeaf() {
+            return leaf;
+        }
+
+        @Override
+        public boolean isContextBasedRouted() {
+            return true;
+        }
+    }
+
+    private static final class GlobalRpcStrategy extends RpcRoutingStrategy {
+
+        public GlobalRpcStrategy(final QName identifier) {
+            super(identifier);
+        }
+
+        @Override
+        public boolean isContextBasedRouted() {
+            return false;
+        }
+
+        @Override
+        public QName getContext() {
+            throw new UnsupportedOperationException("Non-routed strategy does not have a context");
+        }
+
+        @Override
+        public QName getLeaf() {
+            throw new UnsupportedOperationException("Non-routed strategy does not have a context");
+        }
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/AbstractDOMDataTreeChangeListenerRegistration.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/AbstractDOMDataTreeChangeListenerRegistration.java
new file mode 100644 (file)
index 0000000..8033438
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+
+/**
+ * Abstract implementation of a ListenerRegistration constrained to subclasses
+ * of {@link DOMDataTreeChangeListener}.
+ *
+ * @param <T> type of listener
+ */
+public abstract class AbstractDOMDataTreeChangeListenerRegistration<T extends DOMDataTreeChangeListener> extends AbstractListenerRegistration<T> {
+    protected AbstractDOMDataTreeChangeListenerRegistration(final T listener) {
+        super(listener);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/AbstractDOMRpcImplementationRegistration.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/AbstractDOMRpcImplementationRegistration.java
new file mode 100644 (file)
index 0000000..f0ce2b6
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationRegistration;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+
+/**
+ * Abstract base class for {@link DOMRpcImplementationRegistration} implementations.
+ */
+public abstract class AbstractDOMRpcImplementationRegistration<T extends DOMRpcImplementation> extends AbstractObjectRegistration<T> implements DOMRpcImplementationRegistration<T> {
+    protected AbstractDOMRpcImplementationRegistration(final T instance) {
+        super(instance);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/AbstractDOMRpcProviderService.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/AbstractDOMRpcProviderService.java
new file mode 100644 (file)
index 0000000..c137426
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.collect.ImmutableSet;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationRegistration;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcProviderService;
+
+/**
+ * Convenience abstract base class for {@link DOMRpcProviderService} implementations.
+ */
+public abstract class AbstractDOMRpcProviderService implements DOMRpcProviderService {
+    @Override
+    public final <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(final T implementation, final DOMRpcIdentifier... types) {
+        return registerRpcImplementation(implementation, ImmutableSet.copyOf(types));
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/AbstractRegistrationTree.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/AbstractRegistrationTree.java
new file mode 100644 (file)
index 0000000..c1865c8
--- /dev/null
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * An abstract tree of registrations. Allows a read-only snapshot to be taken.
+ *
+ * @param <T> Type of registered object
+ */
+public abstract class AbstractRegistrationTree<T> {
+    private final ReadWriteLock rwLock = new ReentrantReadWriteLock(true);
+    private final RegistrationTreeNode<T> rootNode = new RegistrationTreeNode<>(null, null);
+
+    protected AbstractRegistrationTree() {
+
+    }
+
+    /**
+     * Acquire the read-write lock. This should be done before invoking {@link #findNodeFor(Iterable)}.
+     */
+    protected final void takeLock() {
+        rwLock.writeLock().lock();
+    }
+
+    /**
+     * Release the read-write lock. This should be done after invocation of {@link #findNodeFor(Iterable)}
+     * and modification of the returned node. Note that callers should do so in a finally block.
+     */
+    protected final void releaseLock() {
+        rwLock.writeLock().unlock();
+    }
+
+    /**
+     * Find an existing, or allocate a fresh, node for a particular path. Must be called with the
+     * read-write lock held.
+     *
+     * @param path Path to find a node for
+     * @return A registration node for the specified path
+     */
+    @Nonnull protected final RegistrationTreeNode<T> findNodeFor(@Nonnull final Iterable<PathArgument> path) {
+        RegistrationTreeNode<T> walkNode = rootNode;
+        for (final PathArgument arg : path) {
+            walkNode = walkNode.ensureChild(arg);
+        }
+
+        return walkNode;
+    }
+
+    /**
+     * Add a registration to a particular node. The node must have been returned via {@link #findNodeFor(Iterable)}
+     * and the lock must still be held.
+     *
+     * @param node Tree node
+     * @param registration Registration instance
+     */
+    protected final void addRegistration(@Nonnull final RegistrationTreeNode<T> node, @Nonnull final T registration) {
+        node.addRegistration(registration);
+    }
+
+    /**
+     * Remove a registration from a particular node. This method must not be called while the read-write lock
+     * is held.
+     *
+     * @param node Tree node
+     * @param registration Registration instance
+     */
+    protected final void removeRegistration(@Nonnull final RegistrationTreeNode<T> node, @Nonnull final T registration) {
+        // Take the write lock
+        rwLock.writeLock().lock();
+        try {
+            node.removeRegistration(registration);
+        } finally {
+            // Always release the lock
+            rwLock.writeLock().unlock();
+        }
+    }
+
+    /**
+     * Obtain a tree snapshot. This snapshot ensures a consistent view of
+     * registrations. The snapshot should be closed as soon as it is not required,
+     * because each unclosed instance blocks modification of this tree.
+     *
+     * @return A snapshot instance.
+     */
+    @Nonnull public final RegistrationTreeSnapshot<T> takeSnapshot() {
+        final RegistrationTreeSnapshot<T> ret = new RegistrationTreeSnapshot<>(rwLock.readLock(), rootNode);
+        rwLock.readLock().lock();
+        return ret;
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/DOMNotificationSubscriptionListener.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/DOMNotificationSubscriptionListener.java
new file mode 100644 (file)
index 0000000..85369da
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.annotations.Beta;
+import java.util.EventListener;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * Listener which is notified when subscriptions changes and
+ * provides set of notification types for which currently
+ * subscriptions are in place.
+ *
+ */
+@Beta
+public interface DOMNotificationSubscriptionListener extends EventListener {
+
+    /**
+     * Invoked when notification subscription changed
+     *
+     * @param currentTypes Set of notification types
+     * for which listeners are registered.
+     */
+    void onSubscriptionChanged(Set<SchemaPath> currentTypes);
+
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/DOMNotificationSubscriptionListenerRegistry.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/DOMNotificationSubscriptionListenerRegistry.java
new file mode 100644 (file)
index 0000000..77e0d35
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * Registry of {@link DOMNotificationSubscriptionListener}
+ * which listens for changes in notification types.
+ *
+ */
+@Beta
+public interface DOMNotificationSubscriptionListenerRegistry  {
+
+    <L extends DOMNotificationSubscriptionListener> ListenerRegistration<L> registerSubscriptionListener(L listener);
+
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/DefaultDOMRpcResult.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/DefaultDOMRpcResult.java
new file mode 100644 (file)
index 0000000..269fd35
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Utility class implementing {@link DefaultDOMRpcResult}.
+ */
+@Beta
+public final class DefaultDOMRpcResult implements DOMRpcResult, Immutable, Serializable {
+    private static final long serialVersionUID = 1L;
+    private final Collection<RpcError> errors;
+    private final NormalizedNode<?, ?> result;
+
+    private static Collection<RpcError> asCollection(final RpcError... errors) {
+        if (errors.length == 0) {
+            return Collections.emptyList();
+        } else {
+            return Arrays.asList(errors);
+        }
+    }
+
+    public DefaultDOMRpcResult(final NormalizedNode<?, ?> result, final RpcError... errors) {
+        this(result, asCollection(errors));
+    }
+
+    public DefaultDOMRpcResult(final RpcError... errors) {
+        this(null, asCollection(errors));
+    }
+
+    public DefaultDOMRpcResult(final NormalizedNode<?, ?> result) {
+        this(result, Collections.<RpcError>emptyList());
+    }
+
+    public DefaultDOMRpcResult(final NormalizedNode<?, ?> result, final @Nonnull Collection<RpcError> errors) {
+        this.result = result;
+        this.errors = Preconditions.checkNotNull(errors);
+    }
+
+    public DefaultDOMRpcResult(final @Nonnull Collection<RpcError> errors) {
+        this(null, errors);
+    }
+
+    @Override
+    public @Nonnull Collection<RpcError> getErrors() {
+        return errors;
+    }
+
+    @Override
+    public NormalizedNode<?, ?> getResult() {
+        return result;
+    }
+
+    @Override
+    public int hashCode() {
+        int ret = errors.hashCode();
+        if (result != null) {
+            ret = 31 * ret + result.hashCode();
+        }
+        return ret;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof DefaultDOMRpcResult)) {
+            return false;
+        }
+
+        final DefaultDOMRpcResult other = (DefaultDOMRpcResult) obj;
+        if (!errors.equals(other.errors)) {
+            return false;
+        }
+        return Objects.equals(result, other.result);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMDataBroker.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMDataBroker.java
new file mode 100644 (file)
index 0000000..40fb95f
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.collect.ForwardingObject;
+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;
+
+/**
+ * Utility {@link DOMDataBroker} implementation which forwards all interface
+ * method invocation to a delegate instance.
+ */
+public abstract class ForwardingDOMDataBroker extends ForwardingObject implements DOMDataBroker {
+    @Override
+    protected abstract @Nonnull DOMDataBroker delegate();
+
+    @Override
+    public ListenerRegistration<DOMDataChangeListener> registerDataChangeListener(final LogicalDatastoreType store,
+            final YangInstanceIdentifier path, final DOMDataChangeListener listener,
+            final DataChangeScope triggeringScope) {
+        return delegate().registerDataChangeListener(store, path, listener, triggeringScope);
+    }
+
+    @Override
+    public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
+        return delegate().newReadOnlyTransaction();
+    }
+
+    @Override
+    public DOMDataReadWriteTransaction newReadWriteTransaction() {
+        return delegate().newReadWriteTransaction();
+    }
+
+    @Override
+    public DOMDataWriteTransaction newWriteOnlyTransaction() {
+        return delegate().newWriteOnlyTransaction();
+    }
+
+    @Override
+    public DOMTransactionChain createTransactionChain(final TransactionChainListener listener) {
+        return delegate().createTransactionChain(listener);
+    }
+
+    @Override
+    public Map<Class<? extends DOMDataBrokerExtension>, DOMDataBrokerExtension> getSupportedExtensions() {
+        return delegate().getSupportedExtensions();
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMDataReadOnlyTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMDataReadOnlyTransaction.java
new file mode 100644 (file)
index 0000000..dcec6c3
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ForwardingObject;
+import com.google.common.util.concurrent.CheckedFuture;
+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.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Utility {@link DOMDataReadOnlyTransaction} implementation which forwards all interface
+ * method invocation to a delegate instance.
+ */
+public abstract class ForwardingDOMDataReadOnlyTransaction extends ForwardingObject implements DOMDataReadOnlyTransaction {
+    @Override
+    protected abstract @Nonnull DOMDataReadOnlyTransaction delegate();
+
+    @Override
+    public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+        return delegate().read(store, path);
+    }
+
+    @Override
+    public CheckedFuture<Boolean, ReadFailedException> exists(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+        return delegate().exists(store, path);
+    }
+
+    @Override
+    public Object getIdentifier() {
+        return delegate().getIdentifier();
+    }
+
+    @Override
+    public void close() {
+        delegate().close();
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMDataReadWriteTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMDataReadWriteTransaction.java
new file mode 100644 (file)
index 0000000..923bef7
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ForwardingObject;
+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.DOMDataReadWriteTransaction;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Utility {@link DOMDataReadWriteTransaction} implementation which forwards all interface
+ * method invocation to a delegate instance.
+ */
+public abstract class ForwardingDOMDataReadWriteTransaction extends ForwardingObject implements DOMDataReadWriteTransaction {
+    @Override
+    protected abstract @Nonnull DOMDataReadWriteTransaction delegate();
+
+    @Override
+    public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+        return delegate().read(store, path);
+    }
+
+    @Override
+    public CheckedFuture<Boolean, ReadFailedException> exists(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+        return delegate().exists(store, path);
+    }
+
+    @Override
+    public Object getIdentifier() {
+        return delegate().getIdentifier();
+    }
+
+    @Override
+    public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+        delegate().put(store, path, data);
+    }
+
+    @Override
+    public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+        delegate().merge(store, path, data);
+    }
+
+    @Override
+    public boolean cancel() {
+        return delegate().cancel();
+    }
+
+    @Override
+    public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+        delegate().delete(store, path);
+    }
+
+    @Override
+    public CheckedFuture<Void, TransactionCommitFailedException> submit() {
+        return delegate().submit();
+    }
+
+    @Override
+    @Deprecated
+    public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+        return delegate().commit();
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMDataWriteTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMDataWriteTransaction.java
new file mode 100644 (file)
index 0000000..2afb478
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.collect.ForwardingObject;
+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.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;
+
+/**
+ * Utility {@link DOMDataWriteTransaction} implementation which forwards all interface
+ * method invocation to a delegate instance.
+ */
+public abstract class ForwardingDOMDataWriteTransaction extends ForwardingObject implements DOMDataWriteTransaction {
+    @Override
+    protected abstract @Nonnull DOMDataWriteTransaction delegate();
+
+    @Override
+    public Object getIdentifier() {
+        return delegate().getIdentifier();
+    }
+
+    @Override
+    public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+        delegate().put(store, path, data);
+    }
+
+    @Override
+    public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+        delegate().merge(store, path, data);
+    }
+
+    @Override
+    public boolean cancel() {
+        return delegate().cancel();
+    }
+
+    @Override
+    public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+        delegate().delete(store, path);
+    }
+
+    @Override
+    public CheckedFuture<Void, TransactionCommitFailedException> submit() {
+        return delegate().submit();
+    }
+
+    @Override
+    @Deprecated
+    public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+        return delegate().commit();
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMNotificationPublishService.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMNotificationPublishService.java
new file mode 100644 (file)
index 0000000..6bc1107
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.collect.ForwardingObject;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.TimeUnit;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationPublishService;
+
+/**
+ * Utility implementations of {@link DOMNotificationPublishService} which forwards
+ * all requests to a delegate instance.
+ */
+public abstract class ForwardingDOMNotificationPublishService extends ForwardingObject implements DOMNotificationPublishService {
+    @Override
+    protected abstract DOMNotificationPublishService delegate();
+
+    @Override
+    public ListenableFuture<? extends Object> putNotification(final DOMNotification notification) throws InterruptedException {
+        return delegate().putNotification(notification);
+    }
+
+    @Override
+    public ListenableFuture<? extends Object> offerNotification(final DOMNotification notification) {
+        return delegate().offerNotification(notification);
+    }
+
+    @Override
+    public ListenableFuture<? extends Object> offerNotification(final DOMNotification notification, final long timeout,
+            final TimeUnit unit) throws InterruptedException {
+        return delegate().offerNotification(notification, timeout, unit);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMNotificationService.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMNotificationService.java
new file mode 100644 (file)
index 0000000..b40c428
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.collect.ForwardingObject;
+import java.util.Collection;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * Utility implementation of a {@link DOMNotificationService} which forwards all requests
+ * to a delegate instance.
+ */
+public abstract class ForwardingDOMNotificationService extends ForwardingObject implements DOMNotificationService {
+    @Override
+    protected abstract DOMNotificationService delegate();
+
+    @Override
+    public <T extends DOMNotificationListener> ListenerRegistration<T> registerNotificationListener(final T listener,
+            final Collection<SchemaPath> types) {
+        return delegate().registerNotificationListener(listener, types);
+    }
+
+    @Override
+    public <T extends DOMNotificationListener> ListenerRegistration<T> registerNotificationListener(final T listener,
+            final SchemaPath... types) {
+        return delegate().registerNotificationListener(listener, types);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMRpcImplementation.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMRpcImplementation.java
new file mode 100644 (file)
index 0000000..e93f941
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.collect.ForwardingObject;
+import com.google.common.util.concurrent.CheckedFuture;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Utility implementation which implements {@link DOMRpcImplementation} by forwarding it to
+ * a backing delegate.
+ */
+public abstract class ForwardingDOMRpcImplementation extends ForwardingObject implements DOMRpcImplementation {
+    @Override
+    protected abstract @Nonnull DOMRpcImplementation delegate();
+
+    @Override
+    public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final DOMRpcIdentifier type, final NormalizedNode<?, ?> input) {
+        return delegate().invokeRpc(type, input);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMRpcProviderService.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMRpcProviderService.java
new file mode 100644 (file)
index 0000000..99c4cad
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.collect.ForwardingObject;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationRegistration;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcProviderService;
+
+/**
+ * Utility class which implements {@link DOMRpcProviderService} by forwarding
+ * requests to a backing instance.
+ */
+public abstract class ForwardingDOMRpcProviderService extends ForwardingObject implements DOMRpcProviderService {
+    @Override
+    protected abstract @Nonnull DOMRpcProviderService delegate();
+
+    @Override
+    public <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(final T implementation, final DOMRpcIdentifier... types) {
+        return delegate().registerRpcImplementation(implementation, types);
+    }
+
+    @Override
+    public <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(final T implementation, final Set<DOMRpcIdentifier> types) {
+        return delegate().registerRpcImplementation(implementation, types);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMRpcResult.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMRpcResult.java
new file mode 100644 (file)
index 0000000..ba46d3f
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.collect.ForwardingObject;
+import java.util.Collection;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Utility class which implements {@link DOMRpcResult} by forwarding all methods
+ * to a backing instance.
+ */
+public abstract class ForwardingDOMRpcResult extends ForwardingObject implements DOMRpcResult {
+    @Override
+    protected abstract @Nonnull DOMRpcResult delegate();
+
+    @Override
+    public Collection<RpcError> getErrors() {
+        return delegate().getErrors();
+    }
+
+    @Override
+    public NormalizedNode<?, ?> getResult() {
+        return delegate().getResult();
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMRpcService.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMRpcService.java
new file mode 100644 (file)
index 0000000..976d086
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.collect.ForwardingObject;
+import com.google.common.util.concurrent.CheckedFuture;
+import javax.annotation.Nonnull;
+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.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * Utility {@link DOMRpcService} which forwards all requests to a backing delegate instance.
+ */
+public abstract class ForwardingDOMRpcService extends ForwardingObject implements DOMRpcService {
+    @Override
+    protected abstract @Nonnull DOMRpcService delegate();
+
+    @Override
+    public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final SchemaPath type, final NormalizedNode<?, ?> input) {
+        return delegate().invokeRpc(type, input);
+    }
+
+    @Override
+    public <T extends DOMRpcAvailabilityListener> ListenerRegistration<T> registerRpcListener(final T listener) {
+        return delegate().registerRpcListener(listener);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMTransactionChain.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMTransactionChain.java
new file mode 100644 (file)
index 0000000..e709e67
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.collect.ForwardingObject;
+import javax.annotation.Nonnull;
+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;
+
+/**
+ * Utility {@link DOMTransactionChain} implementation which forwards all interface
+ * method invocation to a delegate instance.
+ */
+public abstract class ForwardingDOMTransactionChain extends ForwardingObject implements DOMTransactionChain {
+    @Override
+    protected abstract @Nonnull DOMTransactionChain delegate();
+
+    @Override
+    public void close() {
+        delegate().close();
+    }
+
+    @Override
+    public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
+        return delegate().newReadOnlyTransaction();
+    }
+
+    @Override
+    public DOMDataReadWriteTransaction newReadWriteTransaction() {
+        return delegate().newReadWriteTransaction();
+    }
+
+    @Override
+    public DOMDataWriteTransaction newWriteOnlyTransaction() {
+        return delegate().newWriteOnlyTransaction();
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/RegistrationTreeNode.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/RegistrationTreeNode.java
new file mode 100644 (file)
index 0000000..41e80ea
--- /dev/null
@@ -0,0 +1,138 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is a single node within the registration tree. Note that the data returned from
+ * and instance of this class is guaranteed to have any relevance or consistency
+ * only as long as the {@link RegistrationTreeSnapshot} instance through which it is reached
+ * remains unclosed.
+ *
+ * @param <T> registration type
+ * @author Robert Varga
+ */
+public final class RegistrationTreeNode<T> implements Identifiable<PathArgument> {
+    private static final Logger LOG = LoggerFactory.getLogger(RegistrationTreeNode.class);
+
+    private final Map<PathArgument, RegistrationTreeNode<T>> children = new HashMap<>();
+    private final Collection<T> registrations = new ArrayList<>(2);
+    private final Collection<T> publicRegistrations = Collections.unmodifiableCollection(registrations);
+    private final Reference<RegistrationTreeNode<T>> parent;
+    private final PathArgument identifier;
+
+    RegistrationTreeNode(final RegistrationTreeNode<T> parent, final PathArgument identifier) {
+        this.parent = new WeakReference<>(parent);
+        this.identifier = identifier;
+    }
+
+    @Override
+    public PathArgument getIdentifier() {
+        return identifier;
+    }
+
+    /**
+     * Return the child matching a {@link PathArgument} specification.
+     *
+     * @param arg Child identifier
+     * @return Child matching exactly, or null.
+     */
+    public RegistrationTreeNode<T> getExactChild(@Nonnull final PathArgument arg) {
+        return children.get(Preconditions.checkNotNull(arg));
+    }
+
+    /**
+     * Return a collection children which match a {@link PathArgument} specification inexactly.
+     * This explicitly excludes the child returned by {@link #getExactChild(PathArgument)}.
+     *
+     * @param arg Child identifier
+     * @return Collection of children, guaranteed to be non-null.
+     */
+    public @Nonnull Collection<RegistrationTreeNode<T>> getInexactChildren(@Nonnull final PathArgument arg) {
+        Preconditions.checkNotNull(arg);
+        if (arg instanceof NodeWithValue || arg instanceof NodeIdentifierWithPredicates) {
+            /*
+             * TODO: This just all-or-nothing wildcards, which we have historically supported. Given
+             *       that the argument is supposed to have all the elements filled out, we could support
+             *       partial wildcards by iterating over the registrations and matching the maps for
+             *       partial matches.
+             */
+            final RegistrationTreeNode<T> child = children.get(new NodeIdentifier(arg.getNodeType()));
+            if (child == null) {
+                return Collections.emptyList();
+            } else {
+                return Collections.singletonList(child);
+            }
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    public Collection<T> getRegistrations() {
+        return publicRegistrations;
+    }
+
+    RegistrationTreeNode<T> ensureChild(@Nonnull final PathArgument child) {
+        RegistrationTreeNode<T> potential = children.get(Preconditions.checkNotNull(child));
+        if (potential == null) {
+            potential = new RegistrationTreeNode<T>(this, child);
+            children.put(child, potential);
+        }
+        return potential;
+    }
+
+    void addRegistration(@Nonnull final T registration) {
+        registrations.add(Preconditions.checkNotNull(registration));
+        LOG.debug("Registration {} added", registration);
+    }
+
+    void removeRegistration(@Nonnull final T registration) {
+        registrations.remove(Preconditions.checkNotNull(registration));
+        LOG.debug("Registration {} removed", registration);
+
+        // We have been called with the write-lock held, so we can perform some cleanup.
+        removeThisIfUnused();
+    }
+
+    private void removeThisIfUnused() {
+        final RegistrationTreeNode<T> p = parent.get();
+        if (p != null && registrations.isEmpty() && children.isEmpty()) {
+            p.removeChild(identifier);
+        }
+    }
+
+    private void removeChild(final PathArgument arg) {
+        children.remove(arg);
+        removeThisIfUnused();
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("identifier", identifier)
+                .add("registrations", registrations.size())
+                .add("children", children.size()).toString();
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/RegistrationTreeSnapshot.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/RegistrationTreeSnapshot.java
new file mode 100644 (file)
index 0000000..09b8b2f
--- /dev/null
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.base.Preconditions;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import java.util.concurrent.locks.Lock;
+
+/**
+ * A stable read-only snapshot of a {@link AbstractRegistrationTree}.
+ *
+ * @author Robert Varga
+ */
+public final class RegistrationTreeSnapshot<T> implements AutoCloseable {
+    @SuppressWarnings("rawtypes")
+    private static final AtomicIntegerFieldUpdater<RegistrationTreeSnapshot> CLOSED_UPDATER = AtomicIntegerFieldUpdater.newUpdater(RegistrationTreeSnapshot.class, "closed");
+    private final RegistrationTreeNode<T> node;
+    private final Lock lock;
+
+    // Used via CLOSED_UPDATER
+    @SuppressWarnings("unused")
+    private volatile int closed = 0;
+
+    RegistrationTreeSnapshot(final Lock lock, final RegistrationTreeNode<T> node) {
+        this.lock = Preconditions.checkNotNull(lock);
+        this.node = Preconditions.checkNotNull(node);
+    }
+
+    public RegistrationTreeNode<T> getRootNode() {
+        return node;
+    }
+
+    @Override
+    public void close() {
+        if (CLOSED_UPDATER.compareAndSet(this, 0, 1)) {
+            lock.unlock();
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/BrokerModule.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/BrokerModule.java
new file mode 100644 (file)
index 0000000..99438c8
--- /dev/null
@@ -0,0 +1,27 @@
+/*\r
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+package org.opendaylight.controller.sal.core.spi;\r
+\r
+import java.util.Set;\r
+\r
+import org.opendaylight.controller.sal.core.api.BrokerService;\r
+import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;\r
+import org.opendaylight.controller.sal.core.api.Consumer.ConsumerFunctionality;\r
+import org.opendaylight.controller.sal.core.api.Provider.ProviderFunctionality;\r
+\r
+public interface BrokerModule {\r
+\r
+    Set<Class<? extends BrokerService>> getProvidedServices();\r
+\r
+    Set<Class<? extends ConsumerFunctionality>> getSupportedConsumerFunctionality();\r
+\r
+    <T extends BrokerService> T getServiceForSession(Class<T> service,\r
+            ConsumerSession session);\r
+\r
+    Set<Class<? extends ProviderFunctionality>> getSupportedProviderFunctionality();\r
+}\r
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/ForwardingConsumerSession.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/ForwardingConsumerSession.java
new file mode 100644 (file)
index 0000000..a8c2d3e
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi;
+
+import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+
+public abstract class ForwardingConsumerSession implements ConsumerSession {
+
+    protected abstract ConsumerSession delegate();
+
+    @Override
+    public void close() {
+        delegate().close();
+    }
+
+    @Override
+    public <T extends BrokerService> T getService(Class<T> arg0) {
+        return delegate().getService(arg0);
+    }
+
+    @Override
+    public boolean isClosed() {
+        return delegate().isClosed();
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/ForwardingProviderSession.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/ForwardingProviderSession.java
new file mode 100644 (file)
index 0000000..87cce97
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.core.spi;
+
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+
+public abstract class ForwardingProviderSession implements ProviderSession {
+
+    protected abstract ProviderSession delegate();
+
+    @Override
+    public void close() {
+        delegate().close();
+    }
+
+    @Override
+    public <T extends BrokerService> T getService(Class<T> arg0) {
+        return delegate().getService(arg0);
+    }
+
+    @Override
+    public boolean isClosed() {
+        return delegate().isClosed();
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/AbstractDOMStoreTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/AbstractDOMStoreTransaction.java
new file mode 100644 (file)
index 0000000..106abca
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.base.Preconditions;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * Abstract DOM Store Transaction.
+ *
+ * Convenience super implementation of DOM Store transaction which provides
+ * common implementation of {@link #toString()} and {@link #getIdentifier()}.
+ *
+ * It can optionally capture the context where it was allocated.
+ *
+ * <T> identifier type
+ */
+@Beta
+public abstract class AbstractDOMStoreTransaction<T> implements DOMStoreTransaction {
+    private final Throwable debugContext;
+    private final T identifier;
+
+    protected AbstractDOMStoreTransaction(@Nonnull final T identifier) {
+        this(identifier, false);
+    }
+
+    protected AbstractDOMStoreTransaction(@Nonnull final T identifier, final boolean debug) {
+        this.identifier = Preconditions.checkNotNull(identifier, "Identifier must not be null.");
+        this.debugContext = debug ? new Throwable().fillInStackTrace() : null;
+    }
+
+    @Override
+    public final T getIdentifier() {
+        return identifier;
+    }
+
+    /**
+     * Return the context in which this transaction was allocated.
+     *
+     * @return The context in which this transaction was allocated, or null
+     *         if the context was not recorded.
+     */
+    @Nullable
+    public final Throwable getDebugContext() {
+        return debugContext;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
+    }
+
+    /**
+     * Add class-specific toString attributes.
+     *
+     * @param toStringHelper
+     *            ToStringHelper instance
+     * @return ToStringHelper instance which was passed in
+     */
+    protected ToStringHelper addToStringAttributes(@Nonnull final ToStringHelper toStringHelper) {
+        return toStringHelper.add("id", identifier);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/AbstractDOMStoreTreeChangePublisher.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/AbstractDOMStoreTreeChangePublisher.java
new file mode 100644 (file)
index 0000000..d191fc3
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.List;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.controller.md.sal.dom.spi.AbstractDOMDataTreeChangeListenerRegistration;
+import org.opendaylight.controller.md.sal.dom.spi.AbstractRegistrationTree;
+import org.opendaylight.controller.md.sal.dom.spi.RegistrationTreeNode;
+import org.opendaylight.controller.md.sal.dom.spi.RegistrationTreeSnapshot;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract base class for {@link DOMStoreTreeChangePublisher} implementations.
+ */
+public abstract class AbstractDOMStoreTreeChangePublisher extends AbstractRegistrationTree<AbstractDOMDataTreeChangeListenerRegistration<?>> implements DOMStoreTreeChangePublisher {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractDOMStoreTreeChangePublisher.class);
+
+    /**
+     * Callback for subclass to notify specified registrations of a candidate at a specified path. This method is guaranteed
+     * to be only called from within {@link #processCandidateTree(DataTreeCandidate)}.
+     *
+     * @param registrations Registrations which are affected by the candidate node
+     * @param path Path of changed candidate node. Guaranteed to match the path specified by the registration
+     * @param node Candidate node
+     */
+    protected abstract void notifyListeners(@Nonnull Collection<AbstractDOMDataTreeChangeListenerRegistration<?>> registrations, @Nonnull YangInstanceIdentifier path, @Nonnull DataTreeCandidateNode node);
+
+    /**
+     * Callback notifying the subclass that the specified registration is being closed and it's user no longer
+     * wishes to receive notifications. This notification is invoked while the {@link org.opendaylight.yangtools.concepts.ListenerRegistration#close()}
+     * method is executing. Subclasses can use this callback to properly remove any delayed notifications pending
+     * towards the registration.
+     *
+     * @param registration Registration which is being closed
+     */
+    protected abstract void registrationRemoved(@Nonnull AbstractDOMDataTreeChangeListenerRegistration<?> registration);
+
+    /**
+     * Process a candidate tree with respect to registered listeners.
+     *
+     * @param candidate candidate three which needs to be processed
+     */
+    protected final void processCandidateTree(@Nonnull final DataTreeCandidate candidate) {
+        final DataTreeCandidateNode node = candidate.getRootNode();
+        if (node.getModificationType() == ModificationType.UNMODIFIED) {
+            LOG.debug("Skipping unmodified candidate {}", candidate);
+            return;
+        }
+
+        try (final RegistrationTreeSnapshot<AbstractDOMDataTreeChangeListenerRegistration<?>> snapshot = takeSnapshot()) {
+            final List<PathArgument> toLookup = ImmutableList.copyOf(candidate.getRootPath().getPathArguments());
+            lookupAndNotify(toLookup, 0, snapshot.getRootNode(), candidate);
+        }
+    }
+
+    @Override
+    public final <L extends DOMDataTreeChangeListener> AbstractDOMDataTreeChangeListenerRegistration<L> registerTreeChangeListener(final YangInstanceIdentifier treeId, final L listener) {
+        // Take the write lock
+        takeLock();
+        try {
+            final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> node = findNodeFor(treeId.getPathArguments());
+            final AbstractDOMDataTreeChangeListenerRegistration<L> reg = new AbstractDOMDataTreeChangeListenerRegistration<L>(listener) {
+                @Override
+                protected void removeRegistration() {
+                    AbstractDOMStoreTreeChangePublisher.this.removeRegistration(node, this);
+                    registrationRemoved(this);
+                }
+            };
+
+            addRegistration(node, reg);
+            return reg;
+        } finally {
+            // Always release the lock
+            releaseLock();
+        }
+    }
+
+    private void lookupAndNotify(final List<PathArgument> args, final int offset, final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> node, final DataTreeCandidate candidate) {
+        if (args.size() != offset) {
+            final PathArgument arg = args.get(offset);
+
+            final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> exactChild = node.getExactChild(arg);
+            if (exactChild != null) {
+                lookupAndNotify(args, offset + 1, exactChild, candidate);
+            }
+
+            for (RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> c : node.getInexactChildren(arg)) {
+                lookupAndNotify(args, offset + 1, c, candidate);
+            }
+        } else {
+            notifyNode(candidate.getRootPath(), node, candidate.getRootNode());
+        }
+    }
+
+    private void notifyNode(final YangInstanceIdentifier path, final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> regNode, final DataTreeCandidateNode candNode) {
+        if (candNode.getModificationType() == ModificationType.UNMODIFIED) {
+            LOG.debug("Skipping unmodified candidate {}", path);
+            return;
+        }
+
+        final Collection<AbstractDOMDataTreeChangeListenerRegistration<?>> regs = regNode.getRegistrations();
+        if (!regs.isEmpty()) {
+            notifyListeners(regs, path, candNode);
+        }
+
+        for (DataTreeCandidateNode candChild : candNode.getChildNodes()) {
+            if (candChild.getModificationType() != ModificationType.UNMODIFIED) {
+                final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> regChild = regNode.getExactChild(candChild.getIdentifier());
+                if (regChild != null) {
+                    notifyNode(path.node(candChild.getIdentifier()), regChild, candChild);
+                }
+
+                for (RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> rc : regNode.getInexactChildren(candChild.getIdentifier())) {
+                    notifyNode(path.node(candChild.getIdentifier()), rc, candChild);
+                }
+            }
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/AbstractSnapshotBackedTransactionChain.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/AbstractSnapshotBackedTransactionChain.java
new file mode 100644 (file)
index 0000000..7683937
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract implementation of the {@link DOMStoreTransactionChain} interface relying on {@link DataTreeSnapshot} supplier
+ * and backend commit coordinator.
+ *
+ * @param <T> transaction identifier type
+ */
+@Beta
+public abstract class AbstractSnapshotBackedTransactionChain<T> extends TransactionReadyPrototype<T> implements DOMStoreTransactionChain {
+    private static abstract class State {
+        /**
+         * Allocate a new snapshot.
+         *
+         * @return A new snapshot
+         */
+        protected abstract DataTreeSnapshot getSnapshot();
+    }
+
+    private static final class Idle extends State {
+        private final AbstractSnapshotBackedTransactionChain<?> chain;
+
+        Idle(final AbstractSnapshotBackedTransactionChain<?> chain) {
+            this.chain = Preconditions.checkNotNull(chain);
+        }
+
+        @Override
+        protected DataTreeSnapshot getSnapshot() {
+            return chain.takeSnapshot();
+        }
+    }
+
+    /**
+     * We have a transaction out there.
+     */
+    private static final class Allocated extends State {
+        private static final AtomicReferenceFieldUpdater<Allocated, DataTreeSnapshot> SNAPSHOT_UPDATER =
+                AtomicReferenceFieldUpdater.newUpdater(Allocated.class, DataTreeSnapshot.class, "snapshot");
+        private final DOMStoreWriteTransaction transaction;
+        private volatile DataTreeSnapshot snapshot;
+
+        Allocated(final DOMStoreWriteTransaction transaction) {
+            this.transaction = Preconditions.checkNotNull(transaction);
+        }
+
+        public DOMStoreWriteTransaction getTransaction() {
+            return transaction;
+        }
+
+        @Override
+        protected DataTreeSnapshot getSnapshot() {
+            final DataTreeSnapshot ret = snapshot;
+            Preconditions.checkState(ret != null, "Previous transaction %s is not ready yet", transaction.getIdentifier());
+            return ret;
+        }
+
+        void setSnapshot(final DataTreeSnapshot snapshot) {
+            final boolean success = SNAPSHOT_UPDATER.compareAndSet(this, null, snapshot);
+            Preconditions.checkState(success, "Transaction %s has already been marked as ready", transaction.getIdentifier());
+        }
+    }
+
+    /**
+     * Chain is logically shut down, no further allocation allowed.
+     */
+    private static final class Shutdown extends State {
+        private final String message;
+
+        Shutdown(final String message) {
+            this.message = Preconditions.checkNotNull(message);
+        }
+
+        @Override
+        protected DataTreeSnapshot getSnapshot() {
+            throw new IllegalStateException(message);
+        }
+    }
+
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<AbstractSnapshotBackedTransactionChain, State> STATE_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(AbstractSnapshotBackedTransactionChain.class, State.class, "state");
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractSnapshotBackedTransactionChain.class);
+    private static final Shutdown CLOSED = new Shutdown("Transaction chain is closed");
+    private static final Shutdown FAILED = new Shutdown("Transaction chain has failed");
+    private final Idle idleState;
+    private volatile State state;
+
+    protected AbstractSnapshotBackedTransactionChain() {
+        idleState = new Idle(this);
+        state = idleState;
+    }
+
+    private Entry<State, DataTreeSnapshot> getSnapshot() {
+        final State localState = state;
+        return new SimpleEntry<>(localState, localState.getSnapshot());
+    }
+
+    private boolean recordTransaction(final State expected, final DOMStoreWriteTransaction transaction) {
+        final State state = new Allocated(transaction);
+        return STATE_UPDATER.compareAndSet(this, expected, state);
+    }
+
+    @Override
+    public final DOMStoreReadTransaction newReadOnlyTransaction() {
+        return newReadOnlyTransaction(nextTransactionIdentifier());
+    }
+
+    protected DOMStoreReadTransaction newReadOnlyTransaction(T transactionId) {
+        final Entry<State, DataTreeSnapshot> entry = getSnapshot();
+        return SnapshotBackedTransactions.newReadTransaction(transactionId, getDebugTransactions(), entry.getValue());
+    }
+
+    @Override
+    public final DOMStoreReadWriteTransaction newReadWriteTransaction() {
+        return newReadWriteTransaction(nextTransactionIdentifier());
+    }
+
+    protected DOMStoreReadWriteTransaction newReadWriteTransaction(T transactionId) {
+        Entry<State, DataTreeSnapshot> entry;
+        DOMStoreReadWriteTransaction ret;
+
+        do {
+            entry = getSnapshot();
+            ret = new SnapshotBackedReadWriteTransaction<T>(transactionId, getDebugTransactions(), entry.getValue(), this);
+        } while (!recordTransaction(entry.getKey(), ret));
+
+        return ret;
+    }
+
+    @Override
+    public final DOMStoreWriteTransaction newWriteOnlyTransaction() {
+        return newWriteOnlyTransaction(nextTransactionIdentifier());
+    }
+
+    protected DOMStoreWriteTransaction newWriteOnlyTransaction(T transactionId) {
+        Entry<State, DataTreeSnapshot> entry;
+        DOMStoreWriteTransaction ret;
+
+        do {
+            entry = getSnapshot();
+            ret = new SnapshotBackedWriteTransaction<T>(transactionId, getDebugTransactions(), entry.getValue(), this);
+        } while (!recordTransaction(entry.getKey(), ret));
+
+        return ret;
+    }
+
+    @Override
+    protected final void transactionAborted(final SnapshotBackedWriteTransaction<T> tx) {
+        final State localState = state;
+        if (localState instanceof Allocated) {
+            final Allocated allocated = (Allocated)localState;
+            if (allocated.getTransaction().equals(tx)) {
+                final boolean success = STATE_UPDATER.compareAndSet(this, localState, idleState);
+                if (!success) {
+                    LOG.warn("Transaction {} aborted, but chain {} state already transitioned from {} to {}, very strange",
+                        tx, this, localState, state);
+                }
+            }
+        }
+    }
+
+    @Override
+    protected final DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction<T> tx, final DataTreeModification tree) {
+        final State localState = state;
+
+        if (localState instanceof Allocated) {
+            final Allocated allocated = (Allocated)localState;
+            final DOMStoreWriteTransaction transaction = allocated.getTransaction();
+            Preconditions.checkState(tx.equals(transaction), "Mis-ordered ready transaction %s last allocated was %s", tx, transaction);
+            allocated.setSnapshot(tree);
+        } else {
+            LOG.debug("Ignoring transaction {} readiness due to state {}", tx, localState);
+        }
+
+        return createCohort(tx, tree);
+    }
+
+    @Override
+    public final void close() {
+        final State localState = state;
+
+        do {
+            Preconditions.checkState(!CLOSED.equals(localState), "Transaction chain {} has been closed", this);
+
+            if (FAILED.equals(localState)) {
+                LOG.debug("Ignoring user close in failed state");
+                return;
+            }
+        } while (!STATE_UPDATER.compareAndSet(this, localState, CLOSED));
+    }
+
+    /**
+     * Notify the base logic that a previously-submitted transaction has been committed successfully.
+     *
+     * @param transaction Transaction which completed successfully.
+     */
+    protected final void onTransactionCommited(final SnapshotBackedWriteTransaction<T> transaction) {
+        // If the committed transaction was the one we allocated last,
+        // we clear it and the ready snapshot, so the next transaction
+        // allocated refers to the data tree directly.
+        final State localState = state;
+
+        if (!(localState instanceof Allocated)) {
+            // This can legally happen if the chain is shut down before the transaction was committed
+            // by the backend.
+            LOG.debug("Ignoring successful transaction {} in state {}", transaction, localState);
+            return;
+        }
+
+        final Allocated allocated = (Allocated)localState;
+        final DOMStoreWriteTransaction tx = allocated.getTransaction();
+        if (!tx.equals(transaction)) {
+            LOG.debug("Ignoring non-latest successful transaction {} in state {}", transaction, allocated);
+            return;
+        }
+
+        if (!STATE_UPDATER.compareAndSet(this, localState, idleState)) {
+            LOG.debug("Transaction chain {} has already transitioned from {} to {}, not making it idle", this, localState, state);
+        }
+    }
+
+    /**
+     * Notify the base logic that a previously-submitted transaction has failed.
+     *
+     * @param transaction Transaction which failed.
+     * @param cause Failure cause
+     */
+    protected final void onTransactionFailed(final SnapshotBackedWriteTransaction<T> transaction, final Throwable cause) {
+        LOG.debug("Transaction chain {} failed on transaction {}", this, transaction, cause);
+        state = FAILED;
+    }
+
+    /**
+     * Return the next transaction identifier.
+     *
+     * @return transaction identifier.
+     */
+    protected abstract T nextTransactionIdentifier();
+
+    /**
+     * Inquire as to whether transactions should record their allocation context.
+     *
+     * @return True if allocation context should be recorded.
+     */
+    protected abstract boolean getDebugTransactions();
+
+    /**
+     * Take a fresh {@link DataTreeSnapshot} from the backend.
+     *
+     * @return A new snapshot.
+     */
+    protected abstract DataTreeSnapshot takeSnapshot();
+
+    /**
+     * Create a cohort for driving the transaction through the commit process.
+     *
+     * @param transaction Transaction handle
+     * @param modification {@link DataTreeModification} which needs to be applied to the backend
+     * @return A {@link DOMStoreThreePhaseCommitCohort} cohort.
+     */
+    protected abstract DOMStoreThreePhaseCommitCohort createCohort(final SnapshotBackedWriteTransaction<T> transaction, final DataTreeModification modification);
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStore.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStore.java
new file mode 100644 (file)
index 0000000..0bab570
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * DOM Data Store
+ *
+ * <p>
+ * DOM Data Store provides transactional tree-like storage for YANG-modeled
+ * entities described by YANG schema and represented by {@link NormalizedNode}.
+ *
+ * Read and write access to stored data is provided only via transactions
+ * created using {@link #newReadOnlyTransaction()},
+ * {@link #newWriteOnlyTransaction()} and {@link #newReadWriteTransaction()}, or
+ * by creating {@link org.opendaylight.controller.md.sal.common.api.data.TransactionChain}.
+ *
+ */
+public interface DOMStore extends DOMStoreTransactionFactory {
+
+    /**
+     * Registers {@link org.opendaylight.controller.md.sal.common.api.data.DataChangeListener} for Data Change callbacks which will
+     * be triggered on the change of provided subpath. What constitutes a change
+     * depends on the @scope parameter.
+     *
+     * Listener upon registration receives an initial callback
+     * {@link AsyncDataChangeListener#onDataChanged(org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent)}
+     * which contains stable view of data tree at the time of registration.
+     *
+     * Â @param path Path (subtree identifier) on which client listener will be
+     * invoked.
+     *
+     * @param listener
+     *            Instance of listener which should be invoked on
+     * @param scope
+     *            Scope of change which triggers callback.
+     * @return Listener Registration object, which client may use to close
+     *         registration / interest on receiving data changes.
+     *
+     */
+    <L extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerChangeListener(
+            YangInstanceIdentifier path, L listener, DataChangeScope scope);
+
+    /**
+     *
+     * Creates new transaction chain.
+     *
+     * Transactions in a chain need to be committed in sequence and each
+     * transaction should see the effects of previous transactions as if they
+     * happened.
+     *
+     * See {@link DOMStoreTransactionChain} for more information.
+     *
+     * @return Newly created transaction chain.
+     */
+    DOMStoreTransactionChain createTransactionChain();
+
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadTransaction.java
new file mode 100644 (file)
index 0000000..3068911
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+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 com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+
+public interface DOMStoreReadTransaction extends DOMStoreTransaction {
+
+    /**
+     * Reads data from provided logical data store located at provided path
+     *
+     * @param path
+     *            Path which uniquely identifies subtree which client want to
+     *            read
+     * @return a CheckFuture containing the result of the read. The Future blocks until the
+     *         commit operation is complete. Once complete:
+     *         <ul>
+     *         <li>If the data at the supplied path exists, the Future returns an Optional object
+     *         containing the data.</li>
+     *         <li>If the data at the supplied path does not exist, the Future returns
+     *         Optional#absent().</li>
+     *         <li>If the read of the data fails, the Future will fail with a
+     *         {@link ReadFailedException} or an exception derived from ReadFailedException.</li>
+     *         </ul>
+     */
+    CheckedFuture<Optional<NormalizedNode<?,?>>, ReadFailedException> read(YangInstanceIdentifier path);
+
+    /**
+     * Checks if data is available in the logical data store located at provided path.
+     * <p>
+     *
+     * Note: a successful result from this method makes no guarantee that a subsequent call to {@link #read}
+     * will succeed. It is possible that the data resides in a data store on a remote node and, if that
+     * node goes down or a network failure occurs, a subsequent read would fail. Another scenario is if
+     * the data is deleted in between the calls to <code>exists</code> and <code>read</code>
+     *
+     * @param path
+     *            Path which uniquely identifies subtree which client want to
+     *            check existence of
+     * @return a CheckFuture containing the result of the check.
+     *         <ul>
+     *         <li>If the data at the supplied path exists, the Future returns a Boolean
+     *         whose value is true, false otherwise</li>
+     *         <li>If checking for the data fails, the Future will fail with a
+     *         {@link ReadFailedException} or an exception derived from ReadFailedException.</li>
+     *         </ul>
+     */
+    CheckedFuture<Boolean, ReadFailedException> exists(YangInstanceIdentifier path);
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadWriteTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadWriteTransaction.java
new file mode 100644 (file)
index 0000000..7277406
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+/**
+ * Combination of a {@link DOMStoreReadTransaction} and {@link DOMStoreWriteTransaction}.
+ */
+public interface DOMStoreReadWriteTransaction extends DOMStoreReadTransaction, DOMStoreWriteTransaction {
+
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreThreePhaseCommitCohort.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreThreePhaseCommitCohort.java
new file mode 100644 (file)
index 0000000..986a153
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Interface implemented by the {@link DOMStore} and exposed for each {@link DOMStoreWriteTransaction}
+ * upon its transition to Ready state. The frontend (DOMStore user) uses this interface to drive the
+ * commit procedure across potentially multiple DOMStores using the Three-Phase-Commit (3PC) Protocol,
+ * as described in {@link https://en.wikipedia.org/wiki/Three-phase_commit}.
+ */
+public interface DOMStoreThreePhaseCommitCohort {
+
+    /**
+     * Sends transaction associated with this three phase commit instance to the
+     * participant, participant votes on the transaction, if the transaction
+     * should be committed or aborted.
+     *
+     * @return ListenableFuture with vote of the participant. Vote
+     *         {@link ListenableFuture#get()} is following:
+     *         <ul>
+     *         <li>
+     *         true if transaction is approved by data store.
+     *         <li>false if the transaction is not approved by data store and
+     *         should be aborted.
+     */
+    ListenableFuture<Boolean> canCommit();
+
+    /**
+     * Initiates a pre-commit phase of associated transaction on datastore.
+     *
+     * This message is valid only and only if and only if the participant responded
+     * on {@link #canCommit()} call with positive response.
+     *
+     * @return ListenableFuture representing acknowledgment for participant
+     *        that pre-commit message was received and processed.
+     */
+    ListenableFuture<Void> preCommit();
+
+    /**
+     * Initiates a abort phase of associated transaction on data store.
+     *
+     * @return ListenableFuture representing acknowledgment for participant
+     *        that abort message was received.
+     */
+    ListenableFuture<Void> abort();
+
+    /**
+     * Initiates a commit phase on of associated transaction on data store.
+     *
+     * @return ListenableFuture representing acknowledgment for participant
+     *        that commit message was received and commit of transaction was
+     *        processed.
+     */
+    ListenableFuture<Void> commit();
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTransaction.java
new file mode 100644 (file)
index 0000000..76ea78b
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+
+/**
+ * DOM Data Store transaction
+ *
+ * See {@link DOMStoreReadTransaction}, {@link DOMStoreWriteTransaction} and {@link DOMStoreReadWriteTransaction}
+ * for specific transaction types.
+ *
+ */
+public interface DOMStoreTransaction extends AutoCloseable, Identifiable<Object> {
+    /**
+     * Unique identifier of the transaction
+     */
+    @Override
+    public Object getIdentifier();
+
+    @Override
+    void close();
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTransactionChain.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTransactionChain.java
new file mode 100644 (file)
index 0000000..b916fdd
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+/**
+ * A chain of transactions. Transactions in a chain need to be committed in
+ * sequence and each transaction must see the effects of previous transactions
+ * as if they happened. A chain makes no guarantees of atomicity, in fact
+ * transactions are committed as soon as possible.
+ */
+public interface DOMStoreTransactionChain extends DOMStoreTransactionFactory, AutoCloseable {
+
+    /**
+     * Create a new read only transaction which will continue the chain. The
+     * previous write transaction has to be either READY or CANCELLED.
+     *
+     * If previous write transaction was already commited to data store, new
+     * read-only transaction is same as obtained via {@link DOMStore#newReadOnlyTransaction()}
+     * and contains merged result of previous one and current state of data store.
+     *
+     * Otherwise read-only transaction presents isolated view as if previous read-write
+     * transaction was successful. State which was introduced by other transactions
+     * outside this transaction chain after creation of previous transaction is not visible.
+     *
+     * @return New transaction in the chain.
+     * @throws IllegalStateException
+     *             if the previous transaction was not READY or CANCELLED, or
+     *             if the chain has been closed.
+     */
+    @Override
+    DOMStoreReadTransaction newReadOnlyTransaction();
+
+    /**
+     * Create a new read write transaction which will continue the chain. The
+     * previous read-write transaction has to be either COMMITED or CANCELLED.
+     *
+     * If previous write transaction was already commited to data store, new
+     * read-write transaction is same as obtained via {@link DOMStore#newReadWriteTransaction()}
+     * and contains merged result of previous one and current state of data store.
+     *
+     * Otherwise read-write transaction presents isolated view as if previous read-write
+     * transaction was successful. State which was introduced by other transactions
+     * outside this transaction chain after creation of previous transaction is not visible.
+     *
+     * @return New transaction in the chain.
+     * @throws IllegalStateException
+     *             if the previous transaction was not READY or CANCELLED, or
+     *             if the chain has been closed.
+     */
+    @Override
+    DOMStoreReadWriteTransaction newReadWriteTransaction();
+
+    /**
+     * Create a new write-only transaction which will continue the chain. The
+     * previous read-write transaction has to be either READY or CANCELLED.
+     *
+     *
+     * @return New transaction in the chain.
+     * @throws IllegalStateException
+     *             if the previous transaction was not READY or CANCELLED, or
+     *             if the chain has been closed.
+     */
+    @Override
+    DOMStoreWriteTransaction newWriteOnlyTransaction();
+
+    /**
+     * Closes Transaction Chain.
+     *
+     * Close method of transaction chain does not guarantee that
+     * last alocated transaction is ready or was submitted.
+     *
+     * @throws IllegalStateException If any of the outstanding created transactions was not canceled or ready.
+     */
+    @Override
+    void close();
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTransactionFactory.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTransactionFactory.java
new file mode 100644 (file)
index 0000000..433d575
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+/**
+ * Factory for DOM Store Transactions
+ *
+ * <p>
+ * Factory provides method to construct read-only, read-write and write-only
+ * transactions, which may be used to retrieve and modify stored information in
+ * Underlying {@link DOMStore} or {@link DOMStoreTransactionChain}.
+ *
+ * <p>
+ * See {@link DOMStore} or {@link DOMStoreTransactionChain} for concrete
+ * variations of this factory.
+ *
+ * <p>
+ * <b>Note:</b> This interface is used only to define common functionality
+ * between {@link DOMStore} and {@link DOMStoreTransactionChain}, which
+ * further specify behaviour of returned transactions.
+ *
+ */
+public interface DOMStoreTransactionFactory {
+
+    /**
+     *
+     * Creates a read only transaction
+     *
+     * <p>
+     * Creates a new read-only transaction, which provides read access to
+     * snapshot of current state.
+     *
+     * See {@link DOMStoreReadTransaction} for more information.
+     *
+     * @return new {@link DOMStoreReadTransaction}
+     * @throws IllegalStateException
+     *             If state of factory prevents allocating new transaction.
+     *
+     */
+    DOMStoreReadTransaction newReadOnlyTransaction();
+
+    /**
+     * Creates write only transaction
+     *
+     * <p>
+     * See {@link DOMStoreWriteTransaction} for more information.
+     *
+     * @return new {@link DOMStoreWriteTransaction}
+     * @throws IllegalStateException If state of factory prevents allocating new transaction.
+     */
+    DOMStoreWriteTransaction newWriteOnlyTransaction();
+
+    /**
+     * Creates Read-Write transaction
+     *
+     * <p>
+     * See {@link DOMStoreReadWriteTransaction} for more information.
+     *
+     * @return  new {@link DOMStoreWriteTransaction}
+     * @throws IllegalStateException If state of factory prevents allocating new transaction.
+     */
+    DOMStoreReadWriteTransaction newReadWriteTransaction();
+
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTreeChangePublisher.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTreeChangePublisher.java
new file mode 100644 (file)
index 0000000..5d75f88
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * Interface implemented by DOMStore implementations which allow registration
+ * of {@link DOMDataTreeChangeListener} instances.
+ */
+public interface DOMStoreTreeChangePublisher {
+    /**
+     * Registers a {@link DOMDataTreeChangeListener} to receive
+     * notifications when data changes under a given path in the conceptual data
+     * tree.
+     * <p>
+     * You are able to register for notifications  for any node or subtree
+     * which can be represented using {@link YangInstanceIdentifier}.
+     * <p>
+     *
+     * You are able to register for data change notifications for a subtree or leaf
+     * even if it does not exist. You will receive notification once that node is
+     * created.
+     * <p>
+     * If there is any pre-existing data in data tree on path for which you are
+     * registering, you will receive initial data change event, which will
+     * contain all pre-existing data, marked as created.
+     *
+     * <p>
+     * This method returns a {@link ListenerRegistration} object. To
+     * "unregister" your listener for changes call the {@link ListenerRegistration#close()}
+     * method on this returned object.
+     * <p>
+     * You MUST explicitly unregister your listener when you no longer want to receive
+     * notifications. This is especially true in OSGi environments, where failure to
+     * do so during bundle shutdown can lead to stale listeners being still registered.
+     *
+     * @param treeId
+     *            Data tree identifier of the subtree which should be watched for
+     *            changes.
+     * @param listener
+     *            Listener instance which is being registered
+     * @return Listener registration object, which may be used to unregister
+     *         your listener using {@link ListenerRegistration#close()} to stop
+     *         delivery of change events.
+     */
+    @Nonnull <L extends DOMDataTreeChangeListener> ListenerRegistration<L> registerTreeChangeListener(@Nonnull YangInstanceIdentifier treeId, @Nonnull L listener);
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreWriteTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreWriteTransaction.java
new file mode 100644 (file)
index 0000000..0a09052
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMStoreWriteTransaction extends DOMStoreTransaction {
+
+    /**
+     * Store a provided data at specified path. This acts as a add / replace
+     * operation, which is to say that whole subtree will be replaced by
+     * specified path.
+     *
+     * If you need add or merge of current object with specified use
+     * {@link #merge(org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType, org.opendaylight.yangtools.concepts.Path, Object)}
+     *
+     *
+     * @param path
+     * @param data
+     *            Data object to be written
+     *
+     * @throws IllegalStateException
+     *             if the client code already sealed transaction and invoked
+     *             {@link #ready()}
+     */
+    void write(YangInstanceIdentifier path, NormalizedNode<?, ?> data);
+
+    /**
+     * Store a provided data at specified path. This acts as a add / replace
+     * operation, which is to say that whole subtree will be replaced by
+     * specified path.
+     *
+     * If you need add or merge of current object with specified use
+     * {@link #merge(org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType, org.opendaylight.yangtools.concepts.Path, Object)}
+     *
+     *
+     * @param path
+     * @param data
+     *            Data object to be written
+     *
+     * @throws IllegalStateException
+     *             if the client code already sealed transaction and invoked
+     *             {@link #ready()}
+     */
+    void merge(YangInstanceIdentifier path, NormalizedNode<?, ?> data);
+
+    /**
+     *
+     * Deletes data and whole subtree located at provided path.
+     *
+     * @param path
+     *            Path to delete
+     * @throws IllegalStateException
+     *             if the client code already sealed transaction and invoked
+     *             {@link #ready()}
+     */
+    void delete(YangInstanceIdentifier path);
+
+    /**
+     *
+     * Seals transaction, and returns three-phase commit cohort associated
+     * with this transaction and DOM Store to be coordinated by coordinator.
+     *
+     * @return Three Phase Commit Cohort instance for this transaction.
+     */
+    DOMStoreThreePhaseCommitCohort ready();
+
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/ForwardingDOMStoreThreePhaseCommitCohort.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/ForwardingDOMStoreThreePhaseCommitCohort.java
new file mode 100644 (file)
index 0000000..4c817dd
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ForwardingObject;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Abstract base class for {@link DOMStoreThreePhaseCommitCohort} implementations,
+ * which forward most of their functionality to a backend {@link #delegate()}.
+ */
+@Beta
+public abstract class ForwardingDOMStoreThreePhaseCommitCohort extends ForwardingObject implements DOMStoreThreePhaseCommitCohort {
+    @Override
+    protected abstract DOMStoreThreePhaseCommitCohort delegate();
+
+    @Override
+    public ListenableFuture<Boolean> canCommit() {
+        return delegate().canCommit();
+    }
+
+    @Override
+    public ListenableFuture<Void> preCommit() {
+        return delegate().preCommit();
+    }
+
+    @Override
+    public ListenableFuture<Void> abort() {
+        return delegate().abort();
+    }
+
+    @Override
+    public ListenableFuture<Void> commit() {
+        return delegate().commit();
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedReadTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedReadTransaction.java
new file mode 100644 (file)
index 0000000..8e5957c
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.annotations.Beta;
+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.Futures;
+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.DataTreeSnapshot;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ * Implementation of read-only transaction backed by {@link DataTreeSnapshot}
+ *
+ * Implementation of read-only transaction backed by {@link DataTreeSnapshot}
+ * which delegates most of its calls to similar methods provided by underlying snapshot.
+ *
+ * <T> identifier type
+ */
+@Beta
+public final class SnapshotBackedReadTransaction<T> extends AbstractDOMStoreTransaction<T> implements DOMStoreReadTransaction {
+    private static final Logger LOG = LoggerFactory.getLogger(SnapshotBackedReadTransaction.class);
+    private volatile DataTreeSnapshot stableSnapshot;
+
+    /**
+     * Creates a new read-only transaction.
+     *
+     * @param identifier Transaction Identifier
+     * @param debug Enable transaction debugging
+     * @param snapshot Snapshot which will be modified.
+     */
+    SnapshotBackedReadTransaction(final T identifier, final boolean debug, final DataTreeSnapshot snapshot) {
+        super(identifier, debug);
+        this.stableSnapshot = Preconditions.checkNotNull(snapshot);
+        LOG.debug("ReadOnly Tx: {} allocated with snapshot {}", identifier, snapshot);
+    }
+
+    @Override
+    public void close() {
+        LOG.debug("Store transaction: {} : Closed", getIdentifier());
+        stableSnapshot = null;
+    }
+
+    @Override
+    public CheckedFuture<Optional<NormalizedNode<?,?>>, ReadFailedException> read(final YangInstanceIdentifier path) {
+        LOG.debug("Tx: {} Read: {}", getIdentifier(), path);
+        checkNotNull(path, "Path must not be null.");
+
+        final DataTreeSnapshot snapshot = stableSnapshot;
+        if (snapshot == null) {
+            return Futures.immediateFailedCheckedFuture(new ReadFailedException("Transaction is closed"));
+        }
+
+        try {
+            return Futures.immediateCheckedFuture(snapshot.readNode(path));
+        } catch (Exception e) {
+            LOG.error("Tx: {} Failed Read of {}", getIdentifier(), path, e);
+            return Futures.immediateFailedCheckedFuture(new ReadFailedException("Read failed",e));
+        }
+    }
+
+    @Override
+    public CheckedFuture<Boolean, ReadFailedException> exists(final YangInstanceIdentifier path) {
+        LOG.debug("Tx: {} Exists: {}", getIdentifier(), path);
+        checkNotNull(path, "Path must not be null.");
+
+        try {
+            return Futures.immediateCheckedFuture(read(path).checkedGet().isPresent());
+        } catch (ReadFailedException e) {
+            return Futures.immediateFailedCheckedFuture(e);
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedReadWriteTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedReadWriteTransaction.java
new file mode 100644 (file)
index 0000000..30abf87
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+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 com.google.common.util.concurrent.Futures;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
+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.DataTreeSnapshot;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of Read-Write transaction which is backed by {@link DataTreeSnapshot}
+ * and executed according to {@link TransactionReadyPrototype}.
+ *
+ * @param <T> identifier type
+ */
+@Beta
+public final class SnapshotBackedReadWriteTransaction<T> extends SnapshotBackedWriteTransaction<T> implements DOMStoreReadWriteTransaction {
+    private static final Logger LOG = LoggerFactory.getLogger(SnapshotBackedReadWriteTransaction.class);
+
+    SnapshotBackedReadWriteTransaction(final T identifier, final boolean debug,
+            final DataTreeSnapshot snapshot, final TransactionReadyPrototype<T> readyImpl) {
+        super(identifier, debug, snapshot, readyImpl);
+    }
+
+    @Override
+    public CheckedFuture<Optional<NormalizedNode<?,?>>, ReadFailedException> read(final YangInstanceIdentifier path) {
+        LOG.debug("Tx: {} Read: {}", getIdentifier(), path);
+        checkNotNull(path, "Path must not be null.");
+
+        final Optional<NormalizedNode<?, ?>> result;
+        try {
+            result = readSnapshotNode(path);
+        } catch (Exception e) {
+            LOG.error("Tx: {} Failed Read of {}", getIdentifier(), path, e);
+            return Futures.immediateFailedCheckedFuture(new ReadFailedException("Read failed", e));
+        }
+
+        if (result == null) {
+            return Futures.immediateFailedCheckedFuture(new ReadFailedException("Transaction is closed"));
+        } else {
+            return Futures.immediateCheckedFuture(result);
+        }
+    }
+
+    @Override
+    public CheckedFuture<Boolean, ReadFailedException> exists(final YangInstanceIdentifier path) {
+        try {
+            return Futures.immediateCheckedFuture(
+                read(path).checkedGet().isPresent());
+        } catch (ReadFailedException e) {
+            return Futures.immediateFailedCheckedFuture(e);
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedTransactions.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedTransactions.java
new file mode 100644 (file)
index 0000000..3368c8a
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+
+/**
+ * Public utility class for instantiating snapshot-backed transactions.
+ */
+@Beta
+public final class SnapshotBackedTransactions {
+    private SnapshotBackedTransactions() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    /**
+     * Creates a new read-only transaction.
+     *
+     * @param identifier Transaction Identifier
+     * @param debug Enable transaction debugging
+     * @param snapshot Snapshot which will be modified.
+     */
+    public static <T> SnapshotBackedReadTransaction<T> newReadTransaction(final T identifier, final boolean debug, final DataTreeSnapshot snapshot) {
+        return new SnapshotBackedReadTransaction<T>(identifier, debug, snapshot);
+    }
+
+    /**
+     * Creates a new read-write transaction.
+     *
+     * @param identifier transaction Identifier
+     * @param debug Enable transaction debugging
+     * @param snapshot Snapshot which will be modified.
+     * @param readyImpl Implementation of ready method.
+     */
+    public static <T> SnapshotBackedReadWriteTransaction<T> newReadWriteTransaction(final T identifier, final boolean debug,
+            final DataTreeSnapshot snapshot, final TransactionReadyPrototype<T> readyImpl) {
+        return new SnapshotBackedReadWriteTransaction<T>(identifier, debug, snapshot, readyImpl);
+    }
+
+    /**
+     * Creates a new write-only transaction.
+     *
+     * @param identifier transaction Identifier
+     * @param debug Enable transaction debugging
+     * @param snapshot Snapshot which will be modified.
+     * @param readyImpl Implementation of ready method.
+     */
+    public static <T> SnapshotBackedWriteTransaction<T> newWriteTransaction(final T identifier, final boolean debug,
+            final DataTreeSnapshot snapshot, final TransactionReadyPrototype<T> readyImpl) {
+        return new SnapshotBackedWriteTransaction<T>(identifier, debug, snapshot, readyImpl);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedWriteTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedWriteTransaction.java
new file mode 100644 (file)
index 0000000..a02d768
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import static com.google.common.base.Preconditions.checkState;
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+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.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of Write transaction which is backed by
+ * {@link DataTreeSnapshot} and executed according to
+ * {@link org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype}.
+ *
+ * @param <T> Identifier type
+ */
+@Beta
+public class SnapshotBackedWriteTransaction<T> extends AbstractDOMStoreTransaction<T> implements DOMStoreWriteTransaction {
+    private static final Logger LOG = LoggerFactory.getLogger(SnapshotBackedWriteTransaction.class);
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<SnapshotBackedWriteTransaction, TransactionReadyPrototype> READY_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(SnapshotBackedWriteTransaction.class, TransactionReadyPrototype.class, "readyImpl");
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<SnapshotBackedWriteTransaction, DataTreeModification> TREE_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(SnapshotBackedWriteTransaction.class, DataTreeModification.class, "mutableTree");
+
+    // non-null when not ready
+    private volatile TransactionReadyPrototype<T> readyImpl;
+    // non-null when not committed/closed
+    private volatile DataTreeModification mutableTree;
+
+    SnapshotBackedWriteTransaction(final T identifier, final boolean debug,
+            final DataTreeSnapshot snapshot, final TransactionReadyPrototype<T> readyImpl) {
+        super(identifier, debug);
+        this.readyImpl = Preconditions.checkNotNull(readyImpl, "readyImpl must not be null.");
+        mutableTree = snapshot.newModification();
+        LOG.debug("Write Tx: {} allocated with snapshot {}", identifier, snapshot);
+    }
+
+    @Override
+    public void write(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+        checkNotReady();
+
+        final DataTreeModification tree = mutableTree;
+        LOG.debug("Tx: {} Write: {}:{}", getIdentifier(), path, data);
+
+        try {
+            tree.write(path, data);
+            // FIXME: Add checked exception
+        } catch (Exception e) {
+            LOG.error("Tx: {}, failed to write {}:{} in {}", getIdentifier(), path, data, tree, e);
+            // Rethrow original ones if they are subclasses of RuntimeException
+            // or Error
+            Throwables.propagateIfPossible(e);
+            // FIXME: Introduce proper checked exception
+            throw new IllegalArgumentException("Illegal input data.", e);
+        }
+    }
+
+    @Override
+    public void merge(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+        checkNotReady();
+
+        final DataTreeModification tree = mutableTree;
+        LOG.debug("Tx: {} Merge: {}:{}", getIdentifier(), path, data);
+
+        try {
+            tree.merge(path, data);
+            // FIXME: Add checked exception
+        } catch (Exception e) {
+            LOG.error("Tx: {}, failed to write {}:{} in {}", getIdentifier(), path, data, tree, e);
+            // Rethrow original ones if they are subclasses of RuntimeException
+            // or Error
+            Throwables.propagateIfPossible(e);
+            // FIXME: Introduce proper checked exception
+            throw new IllegalArgumentException("Illegal input data.", e);
+        }
+    }
+
+    @Override
+    public void delete(final YangInstanceIdentifier path) {
+        checkNotReady();
+
+        final DataTreeModification tree = mutableTree;
+        LOG.debug("Tx: {} Delete: {}", getIdentifier(), path);
+
+        try {
+            tree.delete(path);
+            // FIXME: Add checked exception
+        } catch (Exception e) {
+            LOG.error("Tx: {}, failed to delete {} in {}", getIdentifier(), path, tree, e);
+            // Rethrow original ones if they are subclasses of RuntimeException
+            // or Error
+            Throwables.propagateIfPossible(e);
+            // FIXME: Introduce proper checked exception
+            throw new IllegalArgumentException("Illegal path to delete.", e);
+        }
+    }
+
+    /**
+     * Exposed for {@link SnapshotBackedReadWriteTransaction}'s sake only. The contract does
+     * not allow data access after the transaction has been closed or readied.
+     *
+     * @param path Path to read
+     * @return null if the the transaction has been closed;
+     */
+    final Optional<NormalizedNode<?, ?>> readSnapshotNode(final YangInstanceIdentifier path) {
+        return readyImpl == null ? null : mutableTree.readNode(path);
+    }
+
+    private final void checkNotReady() {
+        checkState(readyImpl != null, "Transaction %s is no longer open. No further modifications allowed.", getIdentifier());
+    }
+
+    @Override
+    public DOMStoreThreePhaseCommitCohort ready() {
+        @SuppressWarnings("unchecked")
+        final TransactionReadyPrototype<T> wasReady = READY_UPDATER.getAndSet(this, null);
+        checkState(wasReady != null, "Transaction %s is no longer open", getIdentifier());
+
+        LOG.debug("Store transaction: {} : Ready", getIdentifier());
+
+        final DataTreeModification tree = mutableTree;
+        TREE_UPDATER.lazySet(this, null);
+        tree.ready();
+        return wasReady.transactionReady(this, tree);
+    }
+
+    @Override
+    public void close() {
+        @SuppressWarnings("unchecked")
+        final TransactionReadyPrototype<T> wasReady = READY_UPDATER.getAndSet(this, null);
+        if (wasReady != null) {
+            LOG.debug("Store transaction: {} : Closed", getIdentifier());
+            TREE_UPDATER.lazySet(this, null);
+            wasReady.transactionAborted(this);
+        } else {
+            LOG.debug("Store transaction: {} : Closed after submit", getIdentifier());
+        }
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper.add("ready", readyImpl == null);
+    }
+
+    /**
+     * Prototype implementation of
+     * {@link #ready(org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction)}
+     *
+     * This class is intended to be implemented by Transaction factories
+     * responsible for allocation of {@link org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction} and
+     * providing underlying logic for applying implementation.
+     *
+     * @param <T> identifier type
+     */
+    public abstract static class TransactionReadyPrototype<T> {
+        /**
+         * Called when a transaction is closed without being readied. This is not invoked for
+         * transactions which are ready.
+         *
+         * @param tx Transaction which got aborted.
+         */
+        protected abstract void transactionAborted(final SnapshotBackedWriteTransaction<T> tx);
+
+        /**
+         * Returns a commit coordinator associated with supplied transactions.
+         *
+         * This call must not fail.
+         *
+         * @param tx
+         *            Transaction on which ready was invoked.
+         * @param tree
+         *            Modified data tree which has been constructed.
+         * @return DOMStoreThreePhaseCommitCohort associated with transaction
+         */
+        protected abstract DOMStoreThreePhaseCommitCohort transactionReady(SnapshotBackedWriteTransaction<T> tx, DataTreeModification tree);
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/package-info.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/package-info.java
new file mode 100644 (file)
index 0000000..ec3b698
--- /dev/null
@@ -0,0 +1,8 @@
+/*\r
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+package org.opendaylight.controller.sal.core.spi.data;
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/statistics/DOMStoreStatsTracker.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/statistics/DOMStoreStatsTracker.java
new file mode 100644 (file)
index 0000000..12713b2
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.core.spi.data.statistics;
+
+import java.util.concurrent.ExecutorService;
+
+import javax.annotation.Nonnull;
+
+import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager;
+
+/**
+ * Interface for a class that tracks statistics for a data store.
+ *
+ * @author Thomas Pantelis
+ */
+public interface DOMStoreStatsTracker {
+
+    /**
+     * Sets the executor used for DataChangeListener notifications.
+     *
+     * @param dclExecutor the executor
+     */
+    void setDataChangeListenerExecutor( @Nonnull ExecutorService dclExecutor );
+
+    /**
+     * Sets the executor used internally by the data store.
+     *
+     * @param dsExecutor the executor
+     */
+    void setDataStoreExecutor( @Nonnull ExecutorService dsExecutor );
+
+    /**
+     * Sets the QueuedNotificationManager use for DataChangeListener notifications,
+     *
+     * @param manager the manager
+     */
+    void setNotificationManager( @Nonnull QueuedNotificationManager<?, ?> manager );
+}
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/package-info.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/package-info.java
new file mode 100644 (file)
index 0000000..76493af
--- /dev/null
@@ -0,0 +1,8 @@
+/*\r
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+package org.opendaylight.controller.sal.core.spi;
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/pom.xml b/opendaylight/md-sal/sal-inmemory-datastore/pom.xml
new file mode 100644 (file)
index 0000000..a1ba2ec
--- /dev/null
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>sal-parent</artifactId>
+    <version>1.3.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>sal-inmemory-datastore</artifactId>
+
+  <packaging>bundle</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+    </dependency>
+
+    <!-- SAL Dependencies -->
+
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>config-api</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-binding-api</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-common-api</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-common-util</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-core-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-core-spi</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>concepts</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>util</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-binding</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-common</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-data-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-data-impl</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-parser-impl</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-dom-config</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+
+    <!-- Test Dependencies -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-simple</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>binding-generator-impl</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>mockito-configuration</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-test-model</artifactId>
+      <scope>test</scope>
+      </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+
+            <Export-Package>org.opendaylight.controller.md.sal.dom.store.impl.*</Export-Package>
+
+            <Import-Package>*</Import-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+        <configuration>
+          <includes>
+            <include>org.opendaylight.controller.*</include>
+          </includes>
+          <check>false</check>
+        </configuration>
+        <executions>
+          <execution>
+            <id>pre-test</id>
+            <goals>
+              <goal>prepare-agent</goal>
+            </goals>
+          </execution>
+          <execution>
+            <id>post-test</id>
+            <goals>
+              <goal>report</goal>
+            </goals>
+            <phase>test</phase>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.opendaylight.yangtools</groupId>
+        <artifactId>yang-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>config</id>
+            <goals>
+              <goal>generate-sources</goal>
+            </goals>
+            <configuration>
+              <codeGenerators>
+                <generator>
+                  <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
+                  <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+                  <additionalConfiguration>
+                    <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
+                  </additionalConfiguration>
+                </generator>
+                <generator>
+                  <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+                  <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+                </generator>
+              </codeGenerators>
+              <inspectDependencies>true</inspectDependencies>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+  <scm>
+    <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+    <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+    <tag>HEAD</tag>
+    <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Architecture:Clustering</url>
+  </scm>
+
+</project>
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModule.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModule.java
new file mode 100644 (file)
index 0000000..3f26266
--- /dev/null
@@ -0,0 +1,37 @@
+package org.opendaylight.controller.config.yang.inmemory_datastore_provider;
+
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreConfigProperties;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreFactory;
+import org.opendaylight.controller.md.sal.dom.store.impl.jmx.InMemoryDataStoreStats;
+
+public class InMemoryConfigDataStoreProviderModule extends org.opendaylight.controller.config.yang.inmemory_datastore_provider.AbstractInMemoryConfigDataStoreProviderModule {
+
+    public InMemoryConfigDataStoreProviderModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public InMemoryConfigDataStoreProviderModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final org.opendaylight.controller.config.yang.inmemory_datastore_provider.InMemoryConfigDataStoreProviderModule oldModule, final 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() {
+        InMemoryDOMDataStore dataStore = InMemoryDOMDataStoreFactory.create("DOM-CFG", getSchemaServiceDependency(),
+                getDebugTransactions(),
+                InMemoryDOMDataStoreConfigProperties.create(getMaxDataChangeExecutorPoolSize(),
+                        getMaxDataChangeExecutorQueueSize(), getMaxDataChangeListenerQueueSize(),
+                        getMaxDataStoreExecutorQueueSize()));
+
+        InMemoryDataStoreStats statsBean = new InMemoryDataStoreStats("InMemoryConfigDataStore", dataStore);
+        dataStore.setCloseable(statsBean);
+
+        return dataStore;
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModuleFactory.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModuleFactory.java
new file mode 100644 (file)
index 0000000..1931c14
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: opendaylight-inmemory-datastore-provider yang module local name: inmemory-config-datastore-provider
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Thu Jun 19 17:10:42 PDT 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.inmemory_datastore_provider;
+public class InMemoryConfigDataStoreProviderModuleFactory extends org.opendaylight.controller.config.yang.inmemory_datastore_provider.AbstractInMemoryConfigDataStoreProviderModuleFactory {
+
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModule.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModule.java
new file mode 100644 (file)
index 0000000..c91c53a
--- /dev/null
@@ -0,0 +1,37 @@
+package org.opendaylight.controller.config.yang.inmemory_datastore_provider;
+
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreConfigProperties;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreFactory;
+import org.opendaylight.controller.md.sal.dom.store.impl.jmx.InMemoryDataStoreStats;
+
+public class InMemoryOperationalDataStoreProviderModule extends org.opendaylight.controller.config.yang.inmemory_datastore_provider.AbstractInMemoryOperationalDataStoreProviderModule {
+
+    public InMemoryOperationalDataStoreProviderModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public InMemoryOperationalDataStoreProviderModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final org.opendaylight.controller.config.yang.inmemory_datastore_provider.InMemoryOperationalDataStoreProviderModule oldModule, final 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() {
+        InMemoryDOMDataStore dataStore = InMemoryDOMDataStoreFactory.create("DOM-OPER", getSchemaServiceDependency(),
+                getDebugTransactions(), InMemoryDOMDataStoreConfigProperties.create(getMaxDataChangeExecutorPoolSize(),
+                        getMaxDataChangeExecutorQueueSize(), getMaxDataChangeListenerQueueSize(),
+                        getMaxDataStoreExecutorQueueSize()));
+
+
+        InMemoryDataStoreStats statsBean = new InMemoryDataStoreStats("InMemoryOperationalDataStore", dataStore);
+
+        dataStore.setCloseable(statsBean);
+
+        return dataStore;
+    }
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModuleFactory.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModuleFactory.java
new file mode 100644 (file)
index 0000000..49bc6a2
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: opendaylight-inmemory-datastore-provider yang module local name: inmemory-operational-datastore-provider
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Thu Jun 19 17:10:42 PDT 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.inmemory_datastore_provider;
+public class InMemoryOperationalDataStoreProviderModuleFactory extends org.opendaylight.controller.config.yang.inmemory_datastore_provider.AbstractInMemoryOperationalDataStoreProviderModuleFactory {
+
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChainedTransactionCommitImpl.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChainedTransactionCommitImpl.java
new file mode 100644 (file)
index 0000000..35d891d
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+
+final class ChainedTransactionCommitImpl extends InMemoryDOMStoreThreePhaseCommitCohort {
+    private final DOMStoreTransactionChainImpl txChain;
+
+    ChainedTransactionCommitImpl(final InMemoryDOMDataStore store, final SnapshotBackedWriteTransaction<String> transaction,
+        final DataTreeModification modification, final DOMStoreTransactionChainImpl txChain) {
+        super(store, transaction, modification);
+        this.txChain = Preconditions.checkNotNull(txChain);
+    }
+
+    @Override
+    public ListenableFuture<Void> commit() {
+        ListenableFuture<Void> ret = super.commit();
+        txChain.transactionCommited(getTransaction());
+        return ret;
+    }
+
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMImmutableDataChangeEvent.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMImmutableDataChangeEvent.java
new file mode 100644 (file)
index 0000000..f457e3b
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import com.google.common.base.Preconditions;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public final class DOMImmutableDataChangeEvent implements
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> {
+
+
+    private static final RemoveEventFactory REMOVE_EVENT_FACTORY = new RemoveEventFactory();
+    private static final CreateEventFactory CREATE_EVENT_FACTORY = new CreateEventFactory();
+
+    private final NormalizedNode<?, ?> original;
+    private final NormalizedNode<?, ?> updated;
+    private final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> originalData;
+    private final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> createdData;
+    private final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> updatedData;
+    private final Set<YangInstanceIdentifier> removedPaths;
+    private final DataChangeScope scope;
+
+
+
+    private DOMImmutableDataChangeEvent(final Builder change) {
+        original = change.before;
+        updated = change.after;
+        originalData = Collections.unmodifiableMap(change.original);
+        createdData = Collections.unmodifiableMap(change.created);
+        updatedData = Collections.unmodifiableMap(change.updated);
+        removedPaths = Collections.unmodifiableSet(change.removed);
+        scope = change.scope;
+    }
+
+    public static final Builder builder(final DataChangeScope scope) {
+        return new Builder(scope);
+    }
+
+    protected DataChangeScope getScope() {
+        return scope;
+    }
+
+    @Override
+    public NormalizedNode<?, ?> getOriginalSubtree() {
+        return original;
+    }
+
+    @Override
+    public NormalizedNode<?, ?> getUpdatedSubtree() {
+        return updated;
+    }
+
+    @Override
+    public Map<YangInstanceIdentifier, NormalizedNode<?, ?>> getOriginalData() {
+        return originalData;
+    }
+
+    @Override
+    public Map<YangInstanceIdentifier, NormalizedNode<?, ?>> getCreatedData() {
+        return createdData;
+    }
+
+    @Override
+    public Map<YangInstanceIdentifier, NormalizedNode<?, ?>> getUpdatedData() {
+        return updatedData;
+    }
+
+    @Override
+    public Set<YangInstanceIdentifier> getRemovedPaths() {
+        return removedPaths;
+    }
+
+    @Override
+    public String toString() {
+        return "DOMImmutableDataChangeEvent [created=" + createdData.keySet() + ", updated=" + updatedData.keySet()
+                + ", removed=" + removedPaths + "]";
+    }
+
+    /**
+     * Simple event factory which creates event based on path and data
+     *
+     *
+     */
+    public interface SimpleEventFactory {
+        DOMImmutableDataChangeEvent create(YangInstanceIdentifier path, NormalizedNode<PathArgument,?> data);
+    }
+
+    /**
+     * Event factory which takes after state and creates event for it.
+     *
+     * Factory for events based on path and after state.
+     * After state is set as {@link #getUpdatedSubtree()} and is path,
+     * state mapping is also present in {@link #getUpdatedData()}.
+     *
+     * @return
+     */
+    public static final SimpleEventFactory getCreateEventFactory() {
+        return CREATE_EVENT_FACTORY;
+    }
+
+    /**
+     * Event factory which takes before state and creates event for it.
+     *
+     * Factory for events based on path and after state.
+     * After state is set as {@link #getOriginalSubtree()} and is path,
+     * state mapping is also present in {@link #getOriginalSubtree()}.
+     *
+     * Path is present in {@link #getRemovedPaths()}.
+     * @return
+     */
+    public static final SimpleEventFactory getRemoveEventFactory() {
+        return REMOVE_EVENT_FACTORY;
+    }
+    public static class Builder {
+
+        public DataChangeScope scope;
+        private NormalizedNode<?, ?> after;
+        private NormalizedNode<?, ?> before;
+
+        private final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> original = new HashMap<>();
+        private final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> created = new HashMap<>();
+        private final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> updated = new HashMap<>();
+        private final Set<YangInstanceIdentifier> removed = new HashSet<>();
+
+        private Builder(final DataChangeScope scope) {
+            Preconditions.checkNotNull(scope, "Data change scope should not be null.");
+            this.scope = scope;
+        }
+
+        public Builder setAfter(final NormalizedNode<?, ?> node) {
+            after = node;
+            return this;
+        }
+
+        public DOMImmutableDataChangeEvent build() {
+
+            return new DOMImmutableDataChangeEvent(this);
+        }
+
+        public void merge(final DOMImmutableDataChangeEvent nestedChanges) {
+
+            original.putAll(nestedChanges.getOriginalData());
+            created.putAll(nestedChanges.getCreatedData());
+            updated.putAll(nestedChanges.getUpdatedData());
+            removed.addAll(nestedChanges.getRemovedPaths());
+
+        }
+
+        public Builder setBefore(final NormalizedNode<?, ?> node) {
+            this.before = node;
+            return this;
+        }
+
+        public Builder addCreated(final YangInstanceIdentifier path, final NormalizedNode<?, ?> node) {
+            created.put(path, node);
+            return this;
+        }
+
+        public Builder addRemoved(final YangInstanceIdentifier path, final NormalizedNode<?, ?> node) {
+            original.put(path, node);
+            removed.add(path);
+            return this;
+        }
+
+        public Builder addUpdated(final YangInstanceIdentifier path, final NormalizedNode<?, ?> before,
+                final NormalizedNode<?, ?> after) {
+            original.put(path, before);
+            updated.put(path, after);
+            return this;
+        }
+
+        public boolean isEmpty() {
+            return created.isEmpty() && removed.isEmpty() && updated.isEmpty();
+        }
+    }
+
+    private static final class RemoveEventFactory implements SimpleEventFactory {
+
+        @Override
+        public DOMImmutableDataChangeEvent create(final YangInstanceIdentifier path, final NormalizedNode<PathArgument, ?> data) {
+            return builder(DataChangeScope.BASE) //
+                    .setBefore(data) //
+                    .addRemoved(path, data) //
+                    .build();
+        }
+
+    }
+
+    private static final class CreateEventFactory implements SimpleEventFactory {
+
+        @Override
+        public DOMImmutableDataChangeEvent create(final YangInstanceIdentifier path, final NormalizedNode<PathArgument, ?> data) {
+            return builder(DataChangeScope.BASE) //
+                    .setAfter(data) //
+                    .addCreated(path, data) //
+                    .build();
+        }
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMStoreTransactionChainImpl.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMStoreTransactionChainImpl.java
new file mode 100644 (file)
index 0000000..2cf79d8
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.sal.core.spi.data.AbstractSnapshotBackedTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+
+final class DOMStoreTransactionChainImpl extends AbstractSnapshotBackedTransactionChain<String> {
+    private final InMemoryDOMDataStore store;
+
+    DOMStoreTransactionChainImpl(final InMemoryDOMDataStore store) {
+        this.store = Preconditions.checkNotNull(store);
+    }
+
+    @Override
+    protected DOMStoreThreePhaseCommitCohort createCohort(final SnapshotBackedWriteTransaction<String> tx, final DataTreeModification modification) {
+        return new ChainedTransactionCommitImpl(store, tx, modification, this);
+    }
+
+    @Override
+    protected DataTreeSnapshot takeSnapshot() {
+        return store.takeSnapshot();
+    }
+
+    @Override
+    protected String nextTransactionIdentifier() {
+        return store.nextIdentifier();
+    }
+
+    @Override
+    protected boolean getDebugTransactions() {
+        return store.getDebugTransactions();
+    }
+
+    void transactionCommited(final SnapshotBackedWriteTransaction<String> transaction) {
+        super.onTransactionCommited(transaction);
+    }
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DataChangeListenerRegistration.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DataChangeListenerRegistration.java
new file mode 100644 (file)
index 0000000..b26f43b
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DataChangeListenerRegistration<L extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>> extends ListenerRegistration<L> {
+    @Override
+    L getInstance();
+
+    YangInstanceIdentifier getPath();
+
+    DataChangeScope getScope();
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java
new file mode 100644 (file)
index 0000000..a85d8ac
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTreeChangePublisher;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedTransactions;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.util.ExecutorServiceUtil;
+import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager;
+import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager.Invoker;
+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.data.impl.schema.tree.InMemoryDataTreeFactory;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * In-memory DOM Data Store
+ *
+ * Implementation of {@link DOMStore} which uses {@link DataTree} and other
+ * classes such as {@link SnapshotBackedWriteTransaction}.
+ * {@link org.opendaylight.controller.sal.core.spi.data.SnapshotBackedReadTransaction} and {@link ResolveDataChangeEventsTask}
+ * to implement {@link DOMStore} contract.
+ *
+ */
+public class InMemoryDOMDataStore extends TransactionReadyPrototype<String> implements DOMStore, Identifiable<String>, SchemaContextListener, AutoCloseable, DOMStoreTreeChangePublisher {
+    private static final Logger LOG = LoggerFactory.getLogger(InMemoryDOMDataStore.class);
+
+    private static final Invoker<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent> DCL_NOTIFICATION_MGR_INVOKER =
+            new Invoker<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent>() {
+                @Override
+                public void invokeListener(final DataChangeListenerRegistration<?> listener,
+                                           final DOMImmutableDataChangeEvent notification ) {
+                    final AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> inst = listener.getInstance();
+                    if (inst != null) {
+                        inst.onDataChanged(notification);
+                    }
+                }
+            };
+
+    private final DataTree dataTree = InMemoryDataTreeFactory.getInstance().create();
+    private final ListenerTree listenerTree = ListenerTree.create();
+    private final AtomicLong txCounter = new AtomicLong(0);
+
+    private final QueuedNotificationManager<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent> dataChangeListenerNotificationManager;
+    private final InMemoryDOMStoreTreeChangePublisher changePublisher;
+    private final ExecutorService dataChangeListenerExecutor;
+    private final boolean debugTransactions;
+    private final String name;
+
+    private volatile AutoCloseable closeable;
+
+    public InMemoryDOMDataStore(final String name, final ExecutorService dataChangeListenerExecutor) {
+        this(name, dataChangeListenerExecutor, InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_LISTENER_QUEUE_SIZE, false);
+    }
+
+    public InMemoryDOMDataStore(final String name, final ExecutorService dataChangeListenerExecutor,
+            final int maxDataChangeListenerQueueSize, final boolean debugTransactions) {
+        this.name = Preconditions.checkNotNull(name);
+        this.dataChangeListenerExecutor = Preconditions.checkNotNull(dataChangeListenerExecutor);
+        this.debugTransactions = debugTransactions;
+
+        dataChangeListenerNotificationManager =
+                new QueuedNotificationManager<>(this.dataChangeListenerExecutor,
+                        DCL_NOTIFICATION_MGR_INVOKER, maxDataChangeListenerQueueSize,
+                        "DataChangeListenerQueueMgr");
+        changePublisher = new InMemoryDOMStoreTreeChangePublisher(this.dataChangeListenerExecutor, maxDataChangeListenerQueueSize);
+    }
+
+    public void setCloseable(final AutoCloseable closeable) {
+        this.closeable = closeable;
+    }
+
+    public QueuedNotificationManager<?, ?> getDataChangeListenerNotificationManager() {
+        return dataChangeListenerNotificationManager;
+    }
+
+    @Override
+    public final String getIdentifier() {
+        return name;
+    }
+
+    @Override
+    public DOMStoreReadTransaction newReadOnlyTransaction() {
+        return SnapshotBackedTransactions.newReadTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot());
+    }
+
+    @Override
+    public DOMStoreReadWriteTransaction newReadWriteTransaction() {
+        return SnapshotBackedTransactions.newReadWriteTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot(), this);
+    }
+
+    @Override
+    public DOMStoreWriteTransaction newWriteOnlyTransaction() {
+        return SnapshotBackedTransactions.newWriteTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot(), this);
+    }
+
+    @Override
+    public DOMStoreTransactionChain createTransactionChain() {
+        return new DOMStoreTransactionChainImpl(this);
+    }
+
+    @Override
+    public synchronized void onGlobalContextUpdated(final SchemaContext ctx) {
+        dataTree.setSchemaContext(ctx);
+    }
+
+    @Override
+    public void close() {
+        ExecutorServiceUtil.tryGracefulShutdown(dataChangeListenerExecutor, 30, TimeUnit.SECONDS);
+
+        if(closeable != null) {
+            try {
+                closeable.close();
+            } catch(Exception e) {
+                LOG.debug("Error closing instance", e);
+            }
+        }
+    }
+
+    public final boolean getDebugTransactions() {
+        return debugTransactions;
+    }
+
+    final DataTreeSnapshot takeSnapshot() {
+        return dataTree.takeSnapshot();
+    }
+
+    @Override
+    public <L extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerChangeListener(
+            final YangInstanceIdentifier path, final L listener, final DataChangeScope scope) {
+
+        /*
+         * Make sure commit is not occurring right now. Listener has to be
+         * registered and its state capture enqueued at a consistent point.
+         *
+         * FIXME: improve this to read-write lock, such that multiple listener
+         * registrations can occur simultaneously
+         */
+        final DataChangeListenerRegistration<L> reg;
+        synchronized (this) {
+            LOG.debug("{}: Registering data change listener {} for {}", name, listener, path);
+
+            reg = listenerTree.registerDataChangeListener(path, listener, scope);
+
+            Optional<NormalizedNode<?, ?>> currentState = dataTree.takeSnapshot().readNode(path);
+            if (currentState.isPresent()) {
+                final NormalizedNode<?, ?> data = currentState.get();
+
+                final DOMImmutableDataChangeEvent event = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE) //
+                        .setAfter(data) //
+                        .addCreated(path, data) //
+                        .build();
+
+                dataChangeListenerNotificationManager.submitNotification(reg, event);
+            }
+        }
+
+        return new AbstractListenerRegistration<L>(listener) {
+            @Override
+            protected void removeRegistration() {
+                synchronized (InMemoryDOMDataStore.this) {
+                    reg.close();
+                }
+            }
+        };
+    }
+
+    @Override
+    public synchronized <L extends DOMDataTreeChangeListener> ListenerRegistration<L> registerTreeChangeListener(final YangInstanceIdentifier treeId, final L listener) {
+        /*
+         * Make sure commit is not occurring right now. Listener has to be
+         * registered and its state capture enqueued at a consistent point.
+         */
+        return changePublisher.registerTreeChangeListener(treeId, listener, dataTree.takeSnapshot());
+    }
+
+    @Override
+    protected void transactionAborted(final SnapshotBackedWriteTransaction<String> tx) {
+        LOG.debug("Tx: {} is closed.", tx.getIdentifier());
+    }
+
+    @Override
+    protected DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction<String> tx, final DataTreeModification modification) {
+        LOG.debug("Tx: {} is submitted. Modifications: {}", tx.getIdentifier(), modification);
+        return new InMemoryDOMStoreThreePhaseCommitCohort(this, tx, modification);
+    }
+
+    String nextIdentifier() {
+        return name + "-" + txCounter.getAndIncrement();
+    }
+
+    void validate(final DataTreeModification modification) throws DataValidationFailedException {
+        dataTree.validate(modification);
+    }
+
+    DataTreeCandidate prepare(final DataTreeModification modification) {
+        return dataTree.prepare(modification);
+    }
+
+    synchronized void commit(final DataTreeCandidate candidate) {
+        dataTree.commit(candidate);
+        changePublisher.publishChange(candidate);
+        ResolveDataChangeEventsTask.create(candidate, listenerTree).resolve(dataChangeListenerNotificationManager);
+    }
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStoreConfigProperties.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStoreConfigProperties.java
new file mode 100644 (file)
index 0000000..be89d53
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+/**
+ * Holds configuration properties when creating an {@link InMemoryDOMDataStore} instance via the
+ * {@link InMemoryDOMDataStoreFactory}
+ *
+ * @author Thomas Pantelis
+ * @see InMemoryDOMDataStoreFactory
+ */
+public class InMemoryDOMDataStoreConfigProperties {
+
+    public static final int DEFAULT_MAX_DATA_CHANGE_EXECUTOR_QUEUE_SIZE = 1000;
+    public static final int DEFAULT_MAX_DATA_CHANGE_EXECUTOR_POOL_SIZE = 20;
+    public static final int DEFAULT_MAX_DATA_CHANGE_LISTENER_QUEUE_SIZE = 1000;
+    public static final int DEFAULT_MAX_DATA_STORE_EXECUTOR_QUEUE_SIZE = 5000;
+
+    private static final InMemoryDOMDataStoreConfigProperties DEFAULT =
+            create(DEFAULT_MAX_DATA_CHANGE_EXECUTOR_POOL_SIZE,
+                    DEFAULT_MAX_DATA_CHANGE_EXECUTOR_QUEUE_SIZE,
+                    DEFAULT_MAX_DATA_CHANGE_LISTENER_QUEUE_SIZE,
+                    DEFAULT_MAX_DATA_STORE_EXECUTOR_QUEUE_SIZE);
+
+    private final int maxDataChangeExecutorQueueSize;
+    private final int maxDataChangeExecutorPoolSize;
+    private final int maxDataChangeListenerQueueSize;
+    private final int maxDataStoreExecutorQueueSize;
+
+    /**
+     * Constructs an instance with the given property values.
+     *
+     * @param maxDataChangeExecutorPoolSize
+     *            maximum thread pool size for the data change notification executor.
+     * @param maxDataChangeExecutorQueueSize
+     *            maximum queue size for the data change notification executor.
+     * @param maxDataChangeListenerQueueSize
+     *            maximum queue size for the data change listeners.
+     * @param maxDataStoreExecutorQueueSize
+     *            maximum queue size for the data store executor.
+     */
+    public static InMemoryDOMDataStoreConfigProperties create(int maxDataChangeExecutorPoolSize,
+            int maxDataChangeExecutorQueueSize, int maxDataChangeListenerQueueSize,
+            int maxDataStoreExecutorQueueSize) {
+        return new InMemoryDOMDataStoreConfigProperties(maxDataChangeExecutorPoolSize,
+                maxDataChangeExecutorQueueSize, maxDataChangeListenerQueueSize,
+                maxDataStoreExecutorQueueSize);
+    }
+
+    public static InMemoryDOMDataStoreConfigProperties create(int maxDataChangeExecutorPoolSize,
+            int maxDataChangeExecutorQueueSize, int maxDataChangeListenerQueueSize) {
+        return new InMemoryDOMDataStoreConfigProperties(maxDataChangeExecutorPoolSize,
+                maxDataChangeExecutorQueueSize, maxDataChangeListenerQueueSize,
+                DEFAULT_MAX_DATA_STORE_EXECUTOR_QUEUE_SIZE);
+    }
+
+    /**
+     * Returns the InMemoryDOMDataStoreConfigProperties instance with default values.
+     */
+    public static InMemoryDOMDataStoreConfigProperties getDefault() {
+        return DEFAULT;
+    }
+
+    private InMemoryDOMDataStoreConfigProperties(int maxDataChangeExecutorPoolSize,
+            int maxDataChangeExecutorQueueSize, int maxDataChangeListenerQueueSize,
+            int maxDataStoreExecutorQueueSize) {
+        this.maxDataChangeExecutorQueueSize = maxDataChangeExecutorQueueSize;
+        this.maxDataChangeExecutorPoolSize = maxDataChangeExecutorPoolSize;
+        this.maxDataChangeListenerQueueSize = maxDataChangeListenerQueueSize;
+        this.maxDataStoreExecutorQueueSize = maxDataStoreExecutorQueueSize;
+    }
+
+    /**
+     * Returns the maximum queue size for the data change notification executor.
+     */
+    public int getMaxDataChangeExecutorQueueSize() {
+        return maxDataChangeExecutorQueueSize;
+    }
+
+    /**
+     * Returns the maximum thread pool size for the data change notification executor.
+     */
+    public int getMaxDataChangeExecutorPoolSize() {
+        return maxDataChangeExecutorPoolSize;
+    }
+
+    /**
+     * Returns the maximum queue size for the data change listeners.
+     */
+    public int getMaxDataChangeListenerQueueSize() {
+        return maxDataChangeListenerQueueSize;
+    }
+
+    /**
+     * Returns the maximum queue size for the data store executor.
+     */
+    public int getMaxDataStoreExecutorQueueSize() {
+        return maxDataStoreExecutorQueueSize;
+    }
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStoreFactory.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStoreFactory.java
new file mode 100644 (file)
index 0000000..00af3df
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import java.util.concurrent.ExecutorService;
+import javax.annotation.Nullable;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yangtools.util.concurrent.SpecialExecutors;
+
+/**
+ * A factory for creating InMemoryDOMDataStore instances.
+ *
+ * @author Thomas Pantelis
+ */
+public final class InMemoryDOMDataStoreFactory {
+
+    private InMemoryDOMDataStoreFactory() {
+    }
+
+    public static InMemoryDOMDataStore create(final String name,
+            @Nullable final SchemaService schemaService) {
+        return create(name, schemaService, null);
+    }
+
+    /**
+     * Creates an InMemoryDOMDataStore instance.
+     *
+     * @param name the name of the data store
+     * @param schemaService the SchemaService to which to register the data store.
+     * @param properties configuration properties for the InMemoryDOMDataStore instance. If null,
+     *                   default property values are used.
+     * @return an InMemoryDOMDataStore instance
+     */
+    public static InMemoryDOMDataStore create(final String name,
+            @Nullable final SchemaService schemaService,
+            @Nullable final InMemoryDOMDataStoreConfigProperties properties) {
+        return create(name, schemaService, false, properties);
+    }
+
+    /**
+     * Creates an InMemoryDOMDataStore instance.
+     *
+     * @param name the name of the data store
+     * @param schemaService the SchemaService to which to register the data store.
+     * @param debugTransactions enable transaction debugging
+     * @param properties configuration properties for the InMemoryDOMDataStore instance. If null,
+     *                   default property values are used.
+     * @return an InMemoryDOMDataStore instance
+     */
+    public static InMemoryDOMDataStore create(final String name,
+            @Nullable final SchemaService schemaService, final boolean debugTransactions,
+            @Nullable final InMemoryDOMDataStoreConfigProperties properties) {
+
+        InMemoryDOMDataStoreConfigProperties actualProperties = properties;
+        if (actualProperties == null) {
+            actualProperties = InMemoryDOMDataStoreConfigProperties.getDefault();
+        }
+
+        // For DataChangeListener notifications we use an executor that provides the fastest
+        // task execution time to get higher throughput as DataChangeListeners typically provide
+        // much of the business logic for a data model. If the executor queue size limit is reached,
+        // subsequent submitted notifications will block the calling thread.
+        int dclExecutorMaxQueueSize = actualProperties.getMaxDataChangeExecutorQueueSize();
+        int dclExecutorMaxPoolSize = actualProperties.getMaxDataChangeExecutorPoolSize();
+
+        ExecutorService dataChangeListenerExecutor = SpecialExecutors.newBlockingBoundedFastThreadPool(
+                dclExecutorMaxPoolSize, dclExecutorMaxQueueSize, name + "-DCL" );
+
+        final InMemoryDOMDataStore dataStore = new InMemoryDOMDataStore(name, dataChangeListenerExecutor,
+                actualProperties.getMaxDataChangeListenerQueueSize(), debugTransactions);
+
+        if (schemaService != null) {
+            schemaService.registerSchemaContextListener(dataStore);
+        }
+
+        return dataStore;
+    }
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMStoreThreePhaseCommitCohort.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMStoreThreePhaseCommitCohort.java
new file mode 100644 (file)
index 0000000..8c32626
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static com.google.common.base.Preconditions.checkState;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.sal.core.spi.data.AbstractDOMStoreTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ConflictingModificationAppliedException;
+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;
+
+class InMemoryDOMStoreThreePhaseCommitCohort implements DOMStoreThreePhaseCommitCohort {
+    private static final Logger LOG = LoggerFactory.getLogger(InMemoryDOMStoreThreePhaseCommitCohort.class);
+    private static final ListenableFuture<Void> SUCCESSFUL_FUTURE = Futures.immediateFuture(null);
+    private static final ListenableFuture<Boolean> CAN_COMMIT_FUTURE = Futures.immediateFuture(Boolean.TRUE);
+    private final SnapshotBackedWriteTransaction<String> transaction;
+    private final DataTreeModification modification;
+    private final InMemoryDOMDataStore store;
+    private DataTreeCandidate candidate;
+
+    public InMemoryDOMStoreThreePhaseCommitCohort(final InMemoryDOMDataStore store, final SnapshotBackedWriteTransaction<String> writeTransaction, final DataTreeModification modification) {
+        this.transaction = Preconditions.checkNotNull(writeTransaction);
+        this.modification = Preconditions.checkNotNull(modification);
+        this.store = Preconditions.checkNotNull(store);
+    }
+
+    private static void warnDebugContext(final AbstractDOMStoreTransaction<?> transaction) {
+        final Throwable ctx = transaction.getDebugContext();
+        if (ctx != null) {
+            LOG.warn("Transaction {} has been allocated in the following context", transaction.getIdentifier(), ctx);
+        }
+    }
+
+    @Override
+    public final ListenableFuture<Boolean> canCommit() {
+        try {
+            store.validate(modification);
+            LOG.debug("Store Transaction: {} can be committed", getTransaction().getIdentifier());
+            return CAN_COMMIT_FUTURE;
+        } catch (ConflictingModificationAppliedException e) {
+            LOG.warn("Store Tx: {} Conflicting modification for {}.", getTransaction().getIdentifier(),
+                    e.getPath());
+            warnDebugContext(getTransaction());
+            return Futures.immediateFailedFuture(new OptimisticLockFailedException("Optimistic lock failed.", e));
+        } catch (DataValidationFailedException e) {
+            LOG.warn("Store Tx: {} Data Precondition failed for {}.", getTransaction().getIdentifier(),
+                    e.getPath(), e);
+            warnDebugContext(getTransaction());
+
+            // For debugging purposes, allow dumping of the modification. Coupled with the above
+            // precondition log, it should allow us to understand what went on.
+            LOG.trace("Store Tx: {} modifications: {} tree: {}", modification, store);
+
+            return Futures.immediateFailedFuture(new TransactionCommitFailedException("Data did not pass validation.", e));
+        } catch (Exception e) {
+            LOG.warn("Unexpected failure in validation phase", e);
+            return Futures.immediateFailedFuture(e);
+        }
+    }
+
+    @Override
+    public final ListenableFuture<Void> preCommit() {
+        try {
+            candidate = store.prepare(modification);
+            return SUCCESSFUL_FUTURE;
+        } catch (Exception e) {
+            LOG.warn("Unexpected failure in pre-commit phase", e);
+            return Futures.immediateFailedFuture(e);
+        }
+    }
+
+    @Override
+    public final ListenableFuture<Void> abort() {
+        candidate = null;
+        return SUCCESSFUL_FUTURE;
+    }
+
+    protected final SnapshotBackedWriteTransaction<String> getTransaction() {
+        return transaction;
+    }
+
+    @Override
+    public ListenableFuture<Void> commit() {
+        checkState(candidate != null, "Proposed subtree must be computed");
+
+        /*
+         * The commit has to occur atomically with regard to listener
+         * registrations.
+         */
+        store.commit(candidate);
+        return SUCCESSFUL_FUTURE;
+    }
+}
+
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMStoreTreeChangePublisher.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMStoreTreeChangePublisher.java
new file mode 100644 (file)
index 0000000..2f3b463
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import com.google.common.base.Optional;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.concurrent.ExecutorService;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.controller.md.sal.dom.spi.AbstractDOMDataTreeChangeListenerRegistration;
+import org.opendaylight.controller.sal.core.spi.data.AbstractDOMStoreTreeChangePublisher;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager;
+import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager.Invoker;
+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.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidates;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class InMemoryDOMStoreTreeChangePublisher extends AbstractDOMStoreTreeChangePublisher {
+    private static final Invoker<AbstractDOMDataTreeChangeListenerRegistration<?>, DataTreeCandidate> MANAGER_INVOKER =
+            new Invoker<AbstractDOMDataTreeChangeListenerRegistration<?>, DataTreeCandidate>() {
+                @Override
+                public void invokeListener(final AbstractDOMDataTreeChangeListenerRegistration<?> listener, final DataTreeCandidate notification) {
+                    // FIXME: this is inefficient, as we could grab the entire queue for the listener and post it
+                    final DOMDataTreeChangeListener inst = listener.getInstance();
+                    if (inst != null) {
+                        inst.onDataTreeChanged(Collections.singletonList(notification));
+                    }
+                }
+            };
+    private static final Logger LOG = LoggerFactory.getLogger(InMemoryDOMStoreTreeChangePublisher.class);
+    private final QueuedNotificationManager<AbstractDOMDataTreeChangeListenerRegistration<?>, DataTreeCandidate> notificationManager;
+
+    InMemoryDOMStoreTreeChangePublisher(final ExecutorService listenerExecutor, final int maxQueueSize) {
+        notificationManager = new QueuedNotificationManager<>(listenerExecutor, MANAGER_INVOKER, maxQueueSize, "DataTreeChangeListenerQueueMgr");
+    }
+
+    @Override
+    protected void notifyListeners(final Collection<AbstractDOMDataTreeChangeListenerRegistration<?>> registrations, final YangInstanceIdentifier path, final DataTreeCandidateNode node) {
+        final DataTreeCandidate candidate = DataTreeCandidates.newDataTreeCandidate(path, node);
+
+        for (AbstractDOMDataTreeChangeListenerRegistration<?> reg : registrations) {
+            LOG.debug("Enqueueing candidate {} to registration {}", candidate, registrations);
+            notificationManager.submitNotification(reg, candidate);
+        }
+    }
+
+    @Override
+    protected synchronized void registrationRemoved(final AbstractDOMDataTreeChangeListenerRegistration<?> registration) {
+        LOG.debug("Closing registration {}", registration);
+
+        // FIXME: remove the queue for this registration and make sure we clear it
+    }
+
+    <L extends DOMDataTreeChangeListener> ListenerRegistration<L> registerTreeChangeListener(final YangInstanceIdentifier treeId, final L listener, final DataTreeSnapshot snapshot) {
+        final AbstractDOMDataTreeChangeListenerRegistration<L> reg = registerTreeChangeListener(treeId, listener);
+
+        final Optional<NormalizedNode<?, ?>> node = snapshot.readNode(treeId);
+        if (node.isPresent()) {
+            final DataTreeCandidate candidate = DataTreeCandidates.fromNormalizedNode(treeId, node.get());
+            notificationManager.submitNotification(reg, candidate);
+        }
+
+        return reg;
+    }
+
+    synchronized void publishChange(@Nonnull final DataTreeCandidate candidate) {
+        // Runs synchronized with registrationRemoved()
+        processCandidateTree(candidate);
+    }
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java
new file mode 100644 (file)
index 0000000..07a9fb7
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+import java.util.Collection;
+import java.util.Map.Entry;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.dom.spi.RegistrationTreeSnapshot;
+import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.Builder;
+import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.SimpleEventFactory;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree;
+import org.opendaylight.yangtools.util.concurrent.NotificationManager;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Resolve Data Change Events based on modifications and listeners
+ *
+ * Computes data change events for all affected registered listeners in data
+ * tree.
+ */
+@Beta
+public final class ResolveDataChangeEventsTask {
+    private static final Logger LOG = LoggerFactory.getLogger(ResolveDataChangeEventsTask.class);
+
+    private final DataTreeCandidate candidate;
+    private final ListenerTree listenerRoot;
+
+    private Multimap<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent> collectedEvents;
+
+    private ResolveDataChangeEventsTask(final DataTreeCandidate candidate, final ListenerTree listenerTree) {
+        this.candidate = Preconditions.checkNotNull(candidate);
+        this.listenerRoot = Preconditions.checkNotNull(listenerTree);
+    }
+
+    /**
+     * Resolves and submits notification tasks to the specified manager.
+     */
+    public synchronized void resolve(final NotificationManager<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent> manager) {
+        try (final RegistrationTreeSnapshot<DataChangeListenerRegistration<?>> w = listenerRoot.takeSnapshot()) {
+            // Defensive: reset internal state
+            collectedEvents = ArrayListMultimap.create();
+
+            // Run through the tree
+            final ResolveDataChangeState s = ResolveDataChangeState.initial(candidate.getRootPath(), w.getRootNode());
+            resolveAnyChangeEvent(s, candidate.getRootNode());
+
+            /*
+             * Convert to tasks, but be mindful of multiple values -- those indicate multiple
+             * wildcard matches, which need to be merged.
+             */
+            for (Entry<DataChangeListenerRegistration<?>, Collection<DOMImmutableDataChangeEvent>> e : collectedEvents.asMap().entrySet()) {
+                final Collection<DOMImmutableDataChangeEvent> col = e.getValue();
+                final DOMImmutableDataChangeEvent event;
+
+                if (col.size() != 1) {
+                    final Builder b = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE);
+                    for (DOMImmutableDataChangeEvent i : col) {
+                        b.merge(i);
+                    }
+
+                    event = b.build();
+                    LOG.trace("Merged events {} into event {}", col, event);
+                } else {
+                    event = col.iterator().next();
+                }
+
+                manager.submitNotification(e.getKey(), event);
+            }
+        }
+    }
+
+    /**
+     * Resolves data change event for supplied node
+     *
+     * @param path
+     *            Path to current node in tree
+     * @param listeners
+     *            Collection of Listener registration nodes interested in
+     *            subtree
+     * @param modification
+     *            Modification of current node
+     * @param before
+     *            - Original (before) state of current node
+     * @param after
+     *            - After state of current node
+     * @return True if the subtree changed, false otherwise
+     */
+    private boolean resolveAnyChangeEvent(final ResolveDataChangeState state, final DataTreeCandidateNode node) {
+        final Optional<NormalizedNode<?, ?>> maybeBefore = node.getDataBefore();
+        final Optional<NormalizedNode<?, ?>> maybeAfter = node.getDataAfter();
+        final ModificationType type = node.getModificationType();
+
+        if (type != ModificationType.UNMODIFIED && !maybeAfter.isPresent() && !maybeBefore.isPresent()) {
+            LOG.debug("Modification at {} has type {}, but no before- and after-data. Assuming unchanged.",
+                    state.getPath(), type);
+            return false;
+        }
+
+        // no before and after state is present
+
+        switch (type) {
+        case SUBTREE_MODIFIED:
+            return resolveSubtreeChangeEvent(state, node);
+        case MERGE:
+        case WRITE:
+            Preconditions.checkArgument(maybeAfter.isPresent(),
+                    "Modification at {} has type {} but no after-data", state.getPath(), type);
+            if (!maybeBefore.isPresent()) {
+                @SuppressWarnings({ "unchecked", "rawtypes" })
+                final NormalizedNode<PathArgument, ?> afterNode = (NormalizedNode)maybeAfter.get();
+                resolveSameEventRecursivelly(state, afterNode, DOMImmutableDataChangeEvent.getCreateEventFactory());
+                return true;
+            }
+
+            return resolveReplacedEvent(state, maybeBefore.get(), maybeAfter.get());
+        case DELETE:
+            Preconditions.checkArgument(maybeBefore.isPresent(),
+                    "Modification at {} has type {} but no before-data", state.getPath(), type);
+
+            @SuppressWarnings({ "unchecked", "rawtypes" })
+            final NormalizedNode<PathArgument, ?> beforeNode = (NormalizedNode)maybeBefore.get();
+            resolveSameEventRecursivelly(state, beforeNode, DOMImmutableDataChangeEvent.getRemoveEventFactory());
+            return true;
+        case UNMODIFIED:
+            return false;
+        }
+
+        throw new IllegalStateException(String.format("Unhandled node state %s at %s", type, state.getPath()));
+    }
+
+    private boolean resolveReplacedEvent(final ResolveDataChangeState state,
+            final NormalizedNode<?, ?> beforeData, final NormalizedNode<?, ?> afterData) {
+
+        if (beforeData instanceof NormalizedNodeContainer<?, ?, ?>) {
+            /*
+             * Node is a container (contains a child) and we have interested
+             * listeners registered for it, that means we need to do
+             * resolution of changes on children level and can not
+             * shortcut resolution.
+             */
+            LOG.trace("Resolving subtree replace event for {} before {}, after {}", state.getPath(), beforeData, afterData);
+            @SuppressWarnings("unchecked")
+            NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> beforeCont = (NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>>) beforeData;
+            @SuppressWarnings("unchecked")
+            NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> afterCont = (NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>>) afterData;
+            return resolveNodeContainerReplaced(state, beforeCont, afterCont);
+        }
+
+        // Node is a Leaf type (does not contain child nodes)
+        // so normal equals method is sufficient for determining change.
+        if (beforeData.equals(afterData)) {
+            LOG.trace("Skipping equal leaf {}", state.getPath());
+            return false;
+        }
+
+        LOG.trace("Resolving leaf replace event for {} , before {}, after {}", state.getPath(), beforeData, afterData);
+        DOMImmutableDataChangeEvent event = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE).addUpdated(state.getPath(), beforeData, afterData).build();
+        state.addEvent(event);
+        state.collectEvents(beforeData, afterData, collectedEvents);
+        return true;
+    }
+
+    private boolean resolveNodeContainerReplaced(final ResolveDataChangeState state,
+            final NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> beforeCont,
+                    final NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> afterCont) {
+        if (!state.needsProcessing()) {
+            LOG.trace("Not processing replaced container {}", state.getPath());
+            return true;
+        }
+
+        // We look at all children from before and compare it with after state.
+        boolean childChanged = false;
+        for (NormalizedNode<PathArgument, ?> beforeChild : beforeCont.getValue()) {
+            final PathArgument childId = beforeChild.getIdentifier();
+
+            if (resolveNodeContainerChildUpdated(state.child(childId), beforeChild, afterCont.getChild(childId))) {
+                childChanged = true;
+            }
+        }
+
+        for (NormalizedNode<PathArgument, ?> afterChild : afterCont.getValue()) {
+            final PathArgument childId = afterChild.getIdentifier();
+
+            /*
+             * We have already iterated of the before-children, so have already
+             * emitted modify/delete events. This means the child has been
+             * created.
+             */
+            if (!beforeCont.getChild(childId).isPresent()) {
+                resolveSameEventRecursivelly(state.child(childId), afterChild, DOMImmutableDataChangeEvent.getCreateEventFactory());
+                childChanged = true;
+            }
+        }
+
+        if (childChanged) {
+            DOMImmutableDataChangeEvent event = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE)
+                    .addUpdated(state.getPath(), beforeCont, afterCont).build();
+            state.addEvent(event);
+        }
+
+        state.collectEvents(beforeCont, afterCont, collectedEvents);
+        return childChanged;
+    }
+
+    private boolean resolveNodeContainerChildUpdated(final ResolveDataChangeState state,
+            final NormalizedNode<PathArgument, ?> before, final Optional<NormalizedNode<PathArgument, ?>> after) {
+        if (after.isPresent()) {
+            // REPLACE or SUBTREE Modified
+            return resolveReplacedEvent(state, before, after.get());
+        }
+
+        // AFTER state is not present - child was deleted.
+        resolveSameEventRecursivelly(state, before, DOMImmutableDataChangeEvent.getRemoveEventFactory());
+        return true;
+    }
+
+    private void resolveSameEventRecursivelly(final ResolveDataChangeState state,
+            final NormalizedNode<PathArgument, ?> node, final SimpleEventFactory eventFactory) {
+        if (!state.needsProcessing()) {
+            LOG.trace("Skipping child {}", state.getPath());
+            return;
+        }
+
+        // We have listeners for this node or it's children, so we will try
+        // to do additional processing
+        if (node instanceof NormalizedNodeContainer<?, ?, ?>) {
+            LOG.trace("Resolving subtree recursive event for {}, type {}", state.getPath(), eventFactory);
+
+            // Node has children, so we will try to resolve it's children
+            // changes.
+            @SuppressWarnings("unchecked")
+            NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> container = (NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>>) node;
+            for (NormalizedNode<PathArgument, ?> child : container.getValue()) {
+                final PathArgument childId = child.getIdentifier();
+
+                LOG.trace("Resolving event for child {}", childId);
+                resolveSameEventRecursivelly(state.child(childId), child, eventFactory);
+            }
+        }
+
+        final DOMImmutableDataChangeEvent event = eventFactory.create(state.getPath(), node);
+        LOG.trace("Adding event {} at path {}", event, state.getPath());
+        state.addEvent(event);
+        state.collectEvents(event.getOriginalSubtree(), event.getUpdatedSubtree(), collectedEvents);
+    }
+
+    private boolean resolveSubtreeChangeEvent(final ResolveDataChangeState state, final DataTreeCandidateNode modification) {
+        final Optional<NormalizedNode<?, ?>> maybeBefore = modification.getDataBefore();
+        final Optional<NormalizedNode<?, ?>> maybeAfter = modification.getDataAfter();
+
+        Preconditions.checkArgument(maybeBefore.isPresent(), "Subtree change with before-data not present at path %s", state.getPath());
+        Preconditions.checkArgument(maybeAfter.isPresent(), "Subtree change with after-data not present at path %s", state.getPath());
+
+        if (!state.needsProcessing()) {
+            LOG.trace("Not processing modified subtree {}", state.getPath());
+            return true;
+        }
+
+        DataChangeScope scope = null;
+        for (DataTreeCandidateNode childMod : modification.getChildNodes()) {
+            final ResolveDataChangeState childState = state.child(childMod.getIdentifier());
+
+            switch (childMod.getModificationType()) {
+            case WRITE:
+            case MERGE:
+            case DELETE:
+                if (resolveAnyChangeEvent(childState, childMod)) {
+                    scope = DataChangeScope.ONE;
+                }
+                break;
+            case SUBTREE_MODIFIED:
+                if (resolveSubtreeChangeEvent(childState, childMod) && scope == null) {
+                    scope = DataChangeScope.SUBTREE;
+                }
+                break;
+            case UNMODIFIED:
+                // no-op
+                break;
+            }
+        }
+
+        final NormalizedNode<?, ?> before = maybeBefore.get();
+        final NormalizedNode<?, ?> after = maybeAfter.get();
+
+        if (scope != null) {
+            DOMImmutableDataChangeEvent one = DOMImmutableDataChangeEvent.builder(scope).addUpdated(state.getPath(), before, after).build();
+            state.addEvent(one);
+        }
+
+        state.collectEvents(before, after, collectedEvents);
+        return scope != null;
+    }
+
+    public static ResolveDataChangeEventsTask create(final DataTreeCandidate candidate, final ListenerTree listenerTree) {
+        return new ResolveDataChangeEventsTask(candidate, listenerTree);
+    }
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeState.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeState.java
new file mode 100644 (file)
index 0000000..e9b91e5
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.dom.spi.RegistrationTreeNode;
+import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.Builder;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Recursion state used in {@link ResolveDataChangeEventsTask}. Instances of this
+ * method track which listeners are affected by a particular change node. It takes
+ * care of properly inheriting SUB/ONE listeners and also provides a means to
+ * understand when actual processing need not occur.
+ */
+final class ResolveDataChangeState {
+    private static final Logger LOG = LoggerFactory.getLogger(ResolveDataChangeState.class);
+    /**
+     * Inherited from all parents
+     */
+    private final Iterable<Builder> inheritedSub;
+    /**
+     * Inherited from immediate parent
+     */
+    private final Collection<Builder> inheritedOne;
+    private final YangInstanceIdentifier nodeId;
+    private final Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> nodes;
+
+    private final Map<DataChangeListenerRegistration<?>, Builder> subBuilders;
+    private final Map<DataChangeListenerRegistration<?>, Builder> oneBuilders;
+    private final Map<DataChangeListenerRegistration<?>, Builder> baseBuilders;
+
+    private ResolveDataChangeState(final YangInstanceIdentifier nodeId,
+            final Iterable<Builder> inheritedSub, final Collection<Builder> inheritedOne,
+            final Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> nodes) {
+        this.nodeId = Preconditions.checkNotNull(nodeId);
+        this.nodes = Preconditions.checkNotNull(nodes);
+        this.inheritedSub = Preconditions.checkNotNull(inheritedSub);
+        this.inheritedOne = Preconditions.checkNotNull(inheritedOne);
+
+        /*
+         * Collect the nodes which need to be propagated from us to the child.
+         */
+        final Map<DataChangeListenerRegistration<?>, Builder> sub = new HashMap<>();
+        final Map<DataChangeListenerRegistration<?>, Builder> one = new HashMap<>();
+        final Map<DataChangeListenerRegistration<?>, Builder> base = new HashMap<>();
+        for (RegistrationTreeNode<DataChangeListenerRegistration<?>> n : nodes) {
+            for (DataChangeListenerRegistration<?> l : n.getRegistrations()) {
+                final Builder b = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE);
+                switch (l.getScope()) {
+                case BASE:
+                    base.put(l, b);
+                    break;
+                case ONE:
+                    one.put(l, b);
+                    break;
+                case SUBTREE:
+                    sub.put(l, b);
+                    break;
+                }
+            }
+        }
+
+        baseBuilders = maybeEmpty(base);
+        oneBuilders = maybeEmpty(one);
+        subBuilders = maybeEmpty(sub);
+    }
+
+    private static <K, V> Map<K, V> maybeEmpty(final Map<K, V> map) {
+        if (map.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        return map;
+    }
+
+    /**
+     * Create an initial state handle at a particular root node.
+     *
+     * @param rootId root instance identifier
+     * @param registrationTreeNode root node
+     * @return
+     */
+    public static ResolveDataChangeState initial(final YangInstanceIdentifier rootId, final RegistrationTreeNode<DataChangeListenerRegistration<?>> registrationTreeNode) {
+        return new ResolveDataChangeState(rootId, Collections.<Builder>emptyList(),
+            Collections.<Builder>emptyList(), Collections.singletonList(registrationTreeNode));
+    }
+
+    /**
+     * Create a state handle for iterating over a particular child.
+     *
+     * @param childId ID of the child
+     * @return State handle
+     */
+    public ResolveDataChangeState child(final PathArgument childId) {
+        /*
+         * We instantiate a concatenation only when needed:
+         *
+         * 1) If our collection is empty, we reuse the parent's. This is typically the case
+         *    for intermediate node, which should be the vast majority.
+         * 2) If the parent's iterable is a Collection and it is empty, reuse our collection.
+         *    This is the case for the first node which defines a subtree listener in a
+         *    particular subtree.
+         * 3) Concatenate the two collections. This happens when we already have some
+         *    subtree listeners and we encounter a node which adds a few more.
+         *
+         * This allows us to lower number of objects allocated and also
+         * speeds up Iterables.isEmpty() in needsProcessing().
+         *
+         * Note that the check for Collection in 2) relies on precisely this logic, which
+         * ensures that we simply cannot see an empty concatenation, but rather start off with
+         * an empty collection, then switch to a non-empty collection and finally switch to
+         * a concatenation. This saves us from instantiating iterators, which a trivial
+         * Iterables.isEmpty() would do as soon as we cross case 3).
+         */
+        final Iterable<Builder> sb;
+        if (!subBuilders.isEmpty()) {
+            if (inheritedSub instanceof Collection && ((Collection<?>) inheritedSub).isEmpty()) {
+                sb = subBuilders.values();
+            } else {
+                sb = Iterables.concat(inheritedSub, subBuilders.values());
+            }
+        } else {
+            sb = inheritedSub;
+        }
+
+        return new ResolveDataChangeState(nodeId.node(childId), sb,
+            oneBuilders.values(), getListenerChildrenWildcarded(nodes, childId));
+    }
+
+    /**
+     * Get the current path
+     *
+     * @return Current path.
+     */
+    public YangInstanceIdentifier getPath() {
+        return nodeId;
+    }
+
+    /**
+     * Check if this child needs processing.
+     *
+     * @return True if processing needs to occur, false otherwise.
+     */
+    public boolean needsProcessing() {
+        // May have underlying listeners, so we need to process
+        if (!nodes.isEmpty()) {
+            return true;
+        }
+        // Have ONE listeners
+        if (!inheritedOne.isEmpty()) {
+            return true;
+        }
+
+        /*
+         * Have SUBTREE listeners
+         *
+         * This is slightly magical replacement for !Iterables.isEmpty(inheritedSub).
+         * It relies on the logic in child(), which gives us the guarantee that when
+         * inheritedSub is not a Collection, it is guaranteed to be non-empty (which
+         * means we need to process). If it is a collection, we still need to check
+         * it for emptiness.
+         *
+         * Unlike Iterables.isEmpty() this code does not instantiate any temporary
+         * objects and is thus more efficient.
+         */
+        if (inheritedSub instanceof Collection) {
+            return !((Collection<?>) inheritedSub).isEmpty();
+        }
+
+        // Non-Collection => non-empty => have to process
+        return true;
+    }
+
+    /**
+     * Add an event to all current listeners.
+     *
+     * @param event
+     */
+    public void addEvent(final DOMImmutableDataChangeEvent event) {
+        // Subtree builders get always notified
+        for (Builder b : subBuilders.values()) {
+            b.merge(event);
+        }
+        for (Builder b : inheritedSub) {
+            b.merge(event);
+        }
+
+        if (event.getScope() == DataChangeScope.ONE || event.getScope() == DataChangeScope.BASE) {
+            for (Builder b : oneBuilders.values()) {
+                b.merge(event);
+            }
+        }
+
+        if (event.getScope() == DataChangeScope.BASE) {
+            for (Builder b : inheritedOne) {
+                b.merge(event);
+            }
+            for (Builder b : baseBuilders.values()) {
+                b.merge(event);
+            }
+        }
+    }
+
+    /**
+     * Gather all non-empty events into the provided map.
+     *
+     * @param before before-image
+     * @param after after-image
+     * @param map target map
+     */
+    public void collectEvents(final NormalizedNode<?, ?> before, final NormalizedNode<?, ?> after,
+            final Multimap<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent> map) {
+        for (Entry<DataChangeListenerRegistration<?>, Builder> e : baseBuilders.entrySet()) {
+            final Builder b = e.getValue();
+            if (!b.isEmpty()) {
+                map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
+            }
+        }
+        for (Entry<DataChangeListenerRegistration<?>, Builder> e : oneBuilders.entrySet()) {
+            final Builder b = e.getValue();
+            if (!b.isEmpty()) {
+                map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
+            }
+        }
+        for (Entry<DataChangeListenerRegistration<?>, Builder> e : subBuilders.entrySet()) {
+            final Builder b = e.getValue();
+            if (!b.isEmpty()) {
+                map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
+            }
+        }
+
+        LOG.trace("Collected events {}", map);
+    }
+
+    private static Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> getListenerChildrenWildcarded(final Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> parentNodes,
+            final PathArgument child) {
+        if (parentNodes.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        final List<RegistrationTreeNode<DataChangeListenerRegistration<?>>> result = new ArrayList<>();
+        if (child instanceof NodeWithValue || child instanceof NodeIdentifierWithPredicates) {
+            NodeIdentifier wildcardedIdentifier = new NodeIdentifier(child.getNodeType());
+            addChildNodes(result, parentNodes, wildcardedIdentifier);
+        }
+        addChildNodes(result, parentNodes, child);
+        return result;
+    }
+
+    private static void addChildNodes(final List<RegistrationTreeNode<DataChangeListenerRegistration<?>>> result, final Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> parentNodes, final PathArgument childIdentifier) {
+        for (RegistrationTreeNode<DataChangeListenerRegistration<?>> node : parentNodes) {
+            RegistrationTreeNode<DataChangeListenerRegistration<?>> child = node.getExactChild(childIdentifier);
+            if (child != null) {
+                result.add(child);
+            }
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/jmx/InMemoryDataStoreStats.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/jmx/InMemoryDataStoreStats.java
new file mode 100644 (file)
index 0000000..cb91b4c
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.store.impl.jmx;
+
+import org.opendaylight.controller.md.sal.common.util.jmx.AbstractMXBean;
+import org.opendaylight.controller.md.sal.common.util.jmx.QueuedNotificationManagerMXBeanImpl;
+import org.opendaylight.controller.md.sal.common.util.jmx.ThreadExecutorStatsMXBeanImpl;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager;
+
+/**
+ * Wrapper class for data store MXbeans.
+ *
+ * @author Thomas Pantelis
+ */
+public class InMemoryDataStoreStats implements AutoCloseable {
+
+    private final AbstractMXBean notificationExecutorStatsBean;
+    private final QueuedNotificationManagerMXBeanImpl notificationManagerStatsBean;
+
+    public InMemoryDataStoreStats(final String mBeanType, final QueuedNotificationManager<?, ?> manager) {
+
+        notificationManagerStatsBean = new QueuedNotificationManagerMXBeanImpl(manager,
+                "notification-manager", mBeanType, null);
+        notificationManagerStatsBean.registerMBean();
+
+        notificationExecutorStatsBean = ThreadExecutorStatsMXBeanImpl.create(manager.getExecutor(),
+                "notification-executor", mBeanType, null);
+        if (notificationExecutorStatsBean != null) {
+            notificationExecutorStatsBean.registerMBean();
+        }
+    }
+
+    public InMemoryDataStoreStats(final String name, final InMemoryDOMDataStore dataStore) {
+        this(name, dataStore.getDataChangeListenerNotificationManager());
+    }
+
+    @Override
+    public void close() throws Exception {
+        if(notificationExecutorStatsBean != null) {
+            notificationExecutorStatsBean.unregisterMBean();
+        }
+
+        if(notificationManagerStatsBean != null) {
+            notificationManagerStatsBean.unregisterMBean();
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/DataChangeListenerRegistrationImpl.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/DataChangeListenerRegistrationImpl.java
new file mode 100644 (file)
index 0000000..5c06b00
--- /dev/null
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+abstract class DataChangeListenerRegistrationImpl<T extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>> extends AbstractListenerRegistration<T> implements DataChangeListenerRegistration<T> {
+    public DataChangeListenerRegistrationImpl(final T listener) {
+        super(listener);
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/ListenerNode.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/ListenerNode.java
new file mode 100644 (file)
index 0000000..578320e
--- /dev/null
@@ -0,0 +1,77 @@
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import java.util.Collection;
+import org.opendaylight.controller.md.sal.dom.spi.RegistrationTreeNode;
+import org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.StoreTreeNode;
+
+/**
+ * This is a single node within the listener tree. Note that the data returned from
+ * and instance of this class is guaranteed to have any relevance or consistency
+ * only as long as the {@link ListenerWalker} instance through which it is reached remains
+ * unclosed.
+ *
+ * @author Robert Varga
+ *
+ * @deprecated Use {@link RegistrationTreeNode} instead.
+ */
+@Deprecated
+public class ListenerNode implements StoreTreeNode<ListenerNode>, Identifiable<PathArgument> {
+    final RegistrationTreeNode<DataChangeListenerRegistration<?>> delegate;
+
+    ListenerNode(final RegistrationTreeNode<DataChangeListenerRegistration<?>> delegate) {
+        this.delegate = Preconditions.checkNotNull(delegate);
+    }
+
+    @Override
+    public PathArgument getIdentifier() {
+        return delegate.getIdentifier();
+    }
+
+    @Override
+    public Optional<ListenerNode> getChild(final PathArgument child) {
+        final RegistrationTreeNode<DataChangeListenerRegistration<?>> c = delegate.getExactChild(child);
+        if (c == null) {
+            return Optional.absent();
+        }
+
+        return Optional.of(new ListenerNode(c));
+    }
+
+    /**
+     * Return the list of current listeners. This collection is guaranteed
+     * to be immutable only while the walker, through which this node is
+     * reachable remains unclosed.
+     *
+     * @return the list of current listeners
+     */
+    public Collection<DataChangeListenerRegistration<?>> getListeners() {
+        return delegate.getRegistrations();
+    }
+
+    @Override
+    public int hashCode() {
+        return delegate.hashCode();
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return delegate.equals(obj);
+    }
+
+    @Override
+    public String toString() {
+        return delegate.toString();
+    }
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/ListenerTree.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/ListenerTree.java
new file mode 100644 (file)
index 0000000..c0ad313
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.spi.AbstractRegistrationTree;
+import org.opendaylight.controller.md.sal.dom.spi.RegistrationTreeNode;
+import org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * A set of listeners organized as a tree by node to which they listen. This class
+ * allows for efficient lookup of listeners when we walk the DataTreeCandidate.
+ *
+ * @author Robert Varga
+ */
+public final class ListenerTree extends AbstractRegistrationTree<DataChangeListenerRegistration<?>> {
+    private ListenerTree() {
+        // Private to disallow direct instantiation
+    }
+
+    /**
+     * Create a new empty instance of the listener tree.
+     *
+     * @return An empty instance.
+     */
+    public static ListenerTree create() {
+        return new ListenerTree();
+    }
+
+    /**
+     * Registers listener on this node.
+     *
+     * @param path Full path on which listener is registered.
+     * @param listener Listener
+     * @param scope Scope of triggering event.
+     * @return Listener registration
+     */
+    public <L extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>> DataChangeListenerRegistration<L> registerDataChangeListener(final YangInstanceIdentifier path,
+            final L listener, final DataChangeScope scope) {
+
+        // Take the write lock
+        takeLock();
+        try {
+            final RegistrationTreeNode<DataChangeListenerRegistration<?>> node = findNodeFor(path.getPathArguments());
+            DataChangeListenerRegistration<L> reg = new DataChangeListenerRegistrationImpl<L>(listener) {
+                @Override
+                public DataChangeScope getScope() {
+                    return scope;
+                }
+
+                @Override
+                public YangInstanceIdentifier getPath() {
+                    return path;
+                }
+
+                @Override
+                protected void removeRegistration() {
+                    /*
+                     * TODO: Here's an interesting problem. The way the datastore works, it
+                     *       enqueues requests towards the listener, so the listener will be
+                     *       notified at some point in the future. Now if the registration is
+                     *       closed, we will prevent any new events from being delivered, but
+                     *       we have no way to purge that queue.
+                     *
+                     *       While this does not directly violate the ListenerRegistration
+                     *       contract, it is probably not going to be liked by the users.
+                     */
+                    ListenerTree.this.removeRegistration(node, this);
+                }
+            };
+
+            addRegistration(node, reg);
+            return reg;
+        } finally {
+            // Always release the lock
+            releaseLock();
+        }
+    }
+
+    /**
+     * Obtain a tree walking context. This context ensures a consistent view of
+     * the listener registrations. The context should be closed as soon as it
+     * is not required, because each unclosed instance blocks modification of
+     * the listener tree.
+     *
+     * @return A walker instance.
+     *
+     * @deprecated Use {@link #takeSnapshot()} instead.
+     */
+    @Deprecated
+    public ListenerWalker getWalker() {
+        /*
+         * TODO: The only current user of this method is local to the datastore.
+         *       Since this class represents a read-lock, losing a reference to
+         *       it is a _major_ problem, as the registration process will get
+         *       wedged, eventually grinding the system to a halt. Should an
+         *       external user exist, make the Walker a phantom reference, which
+         *       will cleanup the lock if not told to do so.
+         */
+        return new ListenerWalker(takeSnapshot());
+    }
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/ListenerWalker.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/ListenerWalker.java
new file mode 100644 (file)
index 0000000..f8169c4
--- /dev/null
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.dom.spi.RegistrationTreeSnapshot;
+import org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration;
+
+/**
+ * A walking context, pretty much equivalent to an iterator, but it
+ * exposes the underlying tree structure.
+ *
+ * @author Robert Varga
+ *
+ * @deprecated Superseded by {@link RegistrationTreeSnapshot}.
+ */
+@Deprecated
+public class ListenerWalker implements AutoCloseable {
+    private final RegistrationTreeSnapshot<DataChangeListenerRegistration<?>> delegate;
+
+    ListenerWalker(final RegistrationTreeSnapshot<DataChangeListenerRegistration<?>> delegate) {
+        this.delegate = Preconditions.checkNotNull(delegate);
+    }
+
+    public ListenerNode getRootNode() {
+        return new ListenerNode(delegate.getRootNode());
+    }
+
+    @Override
+    public void close() {
+        delegate.close();
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/yang/opendaylight-inmemory-datastore-provider.yang b/opendaylight/md-sal/sal-inmemory-datastore/src/main/yang/opendaylight-inmemory-datastore-provider.yang
new file mode 100644 (file)
index 0000000..5ffe4d6
--- /dev/null
@@ -0,0 +1,103 @@
+module opendaylight-inmemory-datastore-provider {
+
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:inmemory-datastore-provider";
+    prefix "inmemory-datastore-provider";
+
+    import config { prefix config; revision-date 2013-04-05; }
+    import rpc-context { prefix rpcx; revision-date 2013-06-17; }
+    import opendaylight-config-dom-datastore {prefix config-dom-store-spi;}
+    import opendaylight-operational-dom-datastore {prefix operational-dom-store-spi;}
+    import opendaylight-md-sal-dom {prefix sal;}
+
+    description
+        "InMemory datastore provider implementation for config & operational datastore";
+
+    revision "2014-06-17" {
+        description
+        "Initial revision.";
+    }
+
+    // This is the definition of the service implementation as a module identity.
+    identity inmemory-config-datastore-provider {
+            base config:module-type;
+            config:provided-service config-dom-store-spi:config-dom-datastore;
+            config:java-name-prefix InMemoryConfigDataStoreProvider;
+    }
+
+        // This is the definition of the service implementation as a module identity.
+
+    identity inmemory-operational-datastore-provider {
+            base config:module-type;
+            config:provided-service operational-dom-store-spi:operational-dom-datastore;
+            config:java-name-prefix InMemoryOperationalDataStoreProvider;
+    }
+
+    grouping datastore-configuration {
+            leaf max-data-change-executor-queue-size {
+                default 1000;
+                type uint16;
+                description "The maximum queue size for the data change notification executor.";
+            }
+
+            leaf max-data-change-executor-pool-size {
+                default 20;
+                type uint16;
+                description "The maximum thread pool size for the data change notification executor.";
+            }
+
+            leaf max-data-change-listener-queue-size {
+                default 1000;
+                type uint16;
+                description "The maximum queue size for the data change listeners.";
+            }
+            leaf max-data-store-executor-queue-size {
+                default 5000;
+                type uint16;
+                description "The maximum queue size for the data store executor.";
+            }
+            leaf debug-transactions {
+                type boolean;
+                default false;
+                description "Enable transaction lifecycle debugging.";
+            }
+    }
+
+    // Augments the 'configuration' choice node under modules/module.
+    augment "/config:modules/config:module/config:configuration" {
+        case inmemory-config-datastore-provider {
+            when "/config:modules/config:module/config:type = 'inmemory-config-datastore-provider'";
+
+            container inmemory-config-datastore-provider {
+                container schema-service {
+                    uses config:service-ref {
+                        refine type {
+                            mandatory false;
+                            config:required-identity sal:schema-service;
+                        }
+                    }
+                }
+                uses datastore-configuration;
+            }
+        }
+    }
+
+    // Augments the 'configuration' choice node under modules/module.
+    augment "/config:modules/config:module/config:configuration" {
+        case inmemory-operational-datastore-provider {
+            when "/config:modules/config:module/config:type = 'inmemory-operational-datastore-provider'";
+
+            container inmemory-operational-datastore-provider {
+                container schema-service {
+                    uses config:service-ref {
+                        refine type {
+                            mandatory false;
+                            config:required-identity sal:schema-service;
+                        }
+                    }
+                }
+                uses datastore-configuration;
+            }
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/AbstractDataChangeListenerTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/AbstractDataChangeListenerTest.java
new file mode 100644 (file)
index 0000000..8d32962
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import java.util.Collection;
+import java.util.Map;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.opendaylight.controller.md.sal.dom.store.impl.DatastoreTestTask.WriteTransactionCustomizer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.TwoLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.top.level.list.NestedList;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.yangtools.util.concurrent.SpecialExecutors;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+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.OrderedMapNode;
+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.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public abstract class AbstractDataChangeListenerTest {
+
+    protected static final YangInstanceIdentifier TOP_LEVEL = YangInstanceIdentifier
+            .of(Top.QNAME);
+    private static final QName NAME_QNAME = QName.create(Top.QNAME, "name");
+    protected static final String FOO = "foo";
+    protected static final String BAR = "bar";
+    protected static final String BAZ = "baz";
+
+    private InMemoryDOMDataStore datastore;
+    private SchemaContext schemaContext;
+    private TestDCLExecutorService dclExecutorService;
+
+    @Before
+    public final void setup() throws Exception {
+        YangModuleInfo moduleInfo = BindingReflections
+                .getModuleInfo(TwoLevelList.class);
+        ModuleInfoBackedContext context = ModuleInfoBackedContext.create();
+        context.registerModuleInfo(moduleInfo);
+        schemaContext = context.tryToCreateSchemaContext().get();
+
+        dclExecutorService = new TestDCLExecutorService(
+                SpecialExecutors.newBlockingBoundedFastThreadPool(1, 10, "DCL" ));
+
+        datastore = new InMemoryDOMDataStore("TEST", dclExecutorService);
+        datastore.onGlobalContextUpdated(schemaContext);
+    }
+
+    @After
+    public void tearDown() {
+        if( dclExecutorService != null ) {
+            dclExecutorService.shutdownNow();
+        }
+    }
+
+    /**
+     * Create a new test task. The task will operate on the backed database,
+     * and will use the proper background executor service.
+     *
+     * @return Test task initialized to clean up {@value #TOP_LEVEL} and its
+     *         children.
+     */
+    public final DatastoreTestTask newTestTask() {
+        return new DatastoreTestTask(datastore, dclExecutorService).cleanup(DatastoreTestTask
+                .simpleDelete(TOP_LEVEL));
+    }
+
+
+    public static final YangInstanceIdentifier path(final String topName,
+            final String nestedName) {
+        return path(topName).node(NestedList.QNAME).node(
+                new NodeIdentifierWithPredicates(NestedList.QNAME, NAME_QNAME,
+                        nestedName));
+    }
+
+    public static final YangInstanceIdentifier path(final String topName) {
+        return TOP_LEVEL.node(TopLevelList.QNAME).node(
+                new NodeIdentifierWithPredicates(TopLevelList.QNAME,
+                        NAME_QNAME, topName));
+    }
+
+    protected static DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> top() {
+        return Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(Top.QNAME));
+    }
+
+
+
+    protected static void assertEmpty(final Collection<?> set) {
+        Assert.assertTrue(set.isEmpty());
+    }
+
+    protected static void assertEmpty(final Map<?,?> set) {
+        Assert.assertTrue(set.isEmpty());
+    }
+
+    protected static <K> void assertContains(final Collection<K> set, final K... values) {
+        for (K key : values) {
+            Assert.assertTrue(set.contains(key));
+        }
+
+    }
+
+    protected static <K> void assertNotContains(final Collection<K> set, final K... values) {
+        for (K key : values) {
+            Assert.assertFalse(set.contains(key));
+        }
+    }
+
+    protected static <K> void assertContains(final Map<K,?> map, final K... values) {
+        for (K key : values) {
+            Assert.assertTrue(map.containsKey(key));
+        }
+    }
+
+    protected static <K> void assertNotContains(final Map<K,?> map, final K... values) {
+        for (K key : values) {
+            Assert.assertFalse(map.containsKey(key));
+        }
+    }
+
+    protected static CollectionNodeBuilder<MapEntryNode, MapNode> topLevelMap() {
+        return ImmutableNodes.mapNodeBuilder(TopLevelList.QNAME);
+    }
+
+    protected static CollectionNodeBuilder<MapEntryNode, OrderedMapNode> nestedMap() {
+        return Builders.orderedMapBuilder().withNodeIdentifier(new NodeIdentifier(NestedList.QNAME));
+    }
+
+    public static DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> topLevelList(
+            final String key) {
+        return ImmutableNodes.mapEntryBuilder(TopLevelList.QNAME, NAME_QNAME,
+                key);
+    }
+
+    public static DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> nestedList(
+            final String key) {
+        return ImmutableNodes
+                .mapEntryBuilder(NestedList.QNAME, NAME_QNAME, key);
+    }
+
+    public static final WriteTransactionCustomizer writeOneTopMultipleNested(
+            final String topName, final String... nestedName) {
+        CollectionNodeBuilder<MapEntryNode, OrderedMapNode> nestedMapBuilder = nestedMap();
+        for (String nestedItem : nestedName) {
+            nestedMapBuilder.addChild(nestedList(nestedItem).build());
+        }
+
+        final ContainerNode data = top().addChild(
+                topLevelMap().addChild(
+                        topLevelList(topName)
+                                .addChild(nestedMapBuilder.build()).build())
+                        .build()).build();
+
+        return DatastoreTestTask.simpleWrite(TOP_LEVEL, data);
+    }
+
+    public static final  WriteTransactionCustomizer deleteNested(final String topName,
+            final String nestedName) {
+        return DatastoreTestTask.simpleDelete(path(topName, nestedName));
+    }
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DatastoreTestTask.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DatastoreTestTask.java
new file mode 100644 (file)
index 0000000..98d79be
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.SettableFuture;
+
+public class DatastoreTestTask {
+
+    private final DOMStore store;
+    private AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> changeListener;
+
+    private WriteTransactionCustomizer setup;
+    private WriteTransactionCustomizer write;
+    private ReadTransactionVerifier read;
+    private WriteTransactionCustomizer cleanup;
+    private YangInstanceIdentifier changePath;
+    private DataChangeScope changeScope;
+    private volatile boolean postSetup = false;
+    private final ChangeEventListener internalListener;
+    private final TestDCLExecutorService dclExecutorService;
+
+    public DatastoreTestTask(final DOMStore datastore, final TestDCLExecutorService dclExecutorService) {
+        this.store = datastore;
+        this.dclExecutorService = dclExecutorService;
+        internalListener = new ChangeEventListener();
+    }
+
+    public DatastoreTestTask changeListener(final YangInstanceIdentifier path, final DataChangeScope scope,
+            final AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> changeListener) {
+        this.changeListener = changeListener;
+        this.changePath = path;
+        this.changeScope = scope;
+        return this;
+    }
+
+    public DatastoreTestTask changeListener(final YangInstanceIdentifier path, final DataChangeScope scope) {
+        this.changePath = path;
+        this.changeScope = scope;
+        return this;
+    }
+
+    public DatastoreTestTask setup(final WriteTransactionCustomizer setup) {
+        this.setup = setup;
+        return this;
+    }
+
+    public DatastoreTestTask test(final WriteTransactionCustomizer write) {
+        this.write = write;
+        return this;
+    }
+
+    public DatastoreTestTask read(final ReadTransactionVerifier read) {
+        this.read = read;
+        return this;
+    }
+
+    public DatastoreTestTask cleanup(final WriteTransactionCustomizer cleanup) {
+        this.cleanup = cleanup;
+        return this;
+    }
+
+    public void run() throws InterruptedException, ExecutionException, TimeoutException {
+        if (setup != null) {
+            execute(setup);
+        }
+        ListenerRegistration<ChangeEventListener> registration = null;
+        if (changePath != null) {
+            registration = store.registerChangeListener(changePath, internalListener, changeScope);
+        }
+
+        Preconditions.checkState(write != null, "Write Transaction must be set.");
+
+        postSetup = true;
+        dclExecutorService.afterTestSetup();
+
+        execute(write);
+        if (registration != null) {
+            registration.close();
+        }
+
+        if (changeListener != null) {
+            changeListener.onDataChanged(getChangeEvent());
+        }
+        if (read != null) {
+            read.verify(store.newReadOnlyTransaction());
+        }
+        if (cleanup != null) {
+            execute(cleanup);
+        }
+    }
+
+    public AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> getChangeEvent() {
+        try {
+            return internalListener.receivedChange.get(10, TimeUnit.SECONDS);
+        } catch( Exception e ) {
+            fail( "Error getting the AsyncDataChangeEvent from the Future: " + e );
+        }
+
+        // won't get here
+        return null;
+    }
+
+    public void verifyNoChangeEvent() {
+        try {
+            Object unexpected = internalListener.receivedChange.get(500, TimeUnit.MILLISECONDS);
+            fail( "Got unexpected AsyncDataChangeEvent from the Future: " + unexpected );
+        } catch( TimeoutException e ) {
+            // Expected
+        } catch( Exception e ) {
+            fail( "Error getting the AsyncDataChangeEvent from the Future: " + e );
+        }
+    }
+
+    private void execute(final WriteTransactionCustomizer writeCustomizer) throws InterruptedException,
+            ExecutionException {
+        DOMStoreReadWriteTransaction tx = store.newReadWriteTransaction();
+        writeCustomizer.customize(tx);
+        DOMStoreThreePhaseCommitCohort cohort = tx.ready();
+        assertTrue(cohort.canCommit().get());
+        cohort.preCommit().get();
+        cohort.commit().get();
+    }
+
+    public interface WriteTransactionCustomizer {
+        public void customize(DOMStoreReadWriteTransaction tx);
+    }
+
+    public interface ReadTransactionVerifier {
+        public void verify(DOMStoreReadTransaction tx);
+    }
+
+    private final class ChangeEventListener implements
+            AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> {
+
+        protected final SettableFuture<AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>>> receivedChange = SettableFuture
+                .create();
+
+        @Override
+        public void onDataChanged(final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
+            if (postSetup) {
+                receivedChange.set(change);
+            }
+        }
+    }
+
+    public static final WriteTransactionCustomizer simpleWrite(final YangInstanceIdentifier path,
+            final NormalizedNode<?, ?> data) {
+        return new WriteTransactionCustomizer() {
+
+            @Override
+            public void customize(final DOMStoreReadWriteTransaction tx) {
+                tx.write(path, data);
+            }
+        };
+    }
+
+    public static final WriteTransactionCustomizer simpleMerge(final YangInstanceIdentifier path,
+            final NormalizedNode<?, ?> data) {
+        return new WriteTransactionCustomizer() {
+
+            @Override
+            public void customize(final DOMStoreReadWriteTransaction tx) {
+                tx.merge(path, data);
+            }
+        };
+    }
+
+    public static final WriteTransactionCustomizer simpleDelete(final YangInstanceIdentifier path) {
+        return new WriteTransactionCustomizer() {
+            @Override
+            public void customize(final DOMStoreReadWriteTransaction tx) {
+                tx.delete(path);
+            }
+        };
+    }
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DefaultDataChangeListenerTestSuite.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DefaultDataChangeListenerTestSuite.java
new file mode 100644 (file)
index 0000000..af58f63
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import java.util.concurrent.ExecutionException;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.store.impl.DatastoreTestTask.WriteTransactionCustomizer;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+
+/**
+ * Base template for a test suite for testing DataChangeListener functionality.
+ */
+public abstract class DefaultDataChangeListenerTestSuite extends AbstractDataChangeListenerTest {
+
+    protected static final String FOO_SIBLING = "foo-sibling";
+
+    /**
+     * Callback invoked when the test suite can modify task parameters.
+     *
+     * @param task Update task configuration as needed
+     */
+    abstract protected void customizeTask(DatastoreTestTask task);
+
+    @Test
+    public final void putTopLevelOneNested() throws Exception {
+
+        DatastoreTestTask task = newTestTask().test(writeOneTopMultipleNested(FOO, BAR));
+        customizeTask(task);
+        task.run();
+        putTopLevelOneNested(task);
+    }
+
+    @Test
+    public final void existingTopWriteSibling() throws Exception {
+        DatastoreTestTask task = newTestTask().setup(writeOneTopMultipleNested(FOO)).test(
+                new WriteTransactionCustomizer() {
+                    @Override
+                    public void customize(final DOMStoreReadWriteTransaction tx) {
+                        tx.write(path(FOO_SIBLING), topLevelList(FOO_SIBLING).build());
+                    }
+                });
+        customizeTask(task);
+        task.run();
+        existingTopWriteSibling(task);
+    }
+
+    protected abstract void existingTopWriteSibling(DatastoreTestTask task) throws InterruptedException, ExecutionException;
+
+
+    @Test
+    public final void existingTopWriteTwoNested() throws Exception {
+        DatastoreTestTask task = newTestTask().setup(writeOneTopMultipleNested(FOO)).test(
+                new WriteTransactionCustomizer() {
+                    @Override
+                    public void customize(final DOMStoreReadWriteTransaction tx) {
+                        tx.write(path(FOO,BAR), nestedList(BAR).build());
+                        tx.write(path(FOO,BAZ), nestedList(BAZ).build());
+                    }
+                });
+        customizeTask(task);
+        task.run();
+        existingTopWriteTwoNested(task);
+    }
+
+    protected abstract void existingTopWriteTwoNested(DatastoreTestTask task) throws InterruptedException, ExecutionException;
+
+
+    @Test
+    public final void existingOneNestedWriteAdditionalNested() throws Exception {
+        DatastoreTestTask task = newTestTask().setup(writeOneTopMultipleNested(FOO, BAR)).test(
+                new WriteTransactionCustomizer() {
+                    @Override
+                    public void customize(final DOMStoreReadWriteTransaction tx) {
+                        tx.write(path(FOO,BAZ), nestedList(BAZ).build());
+                    }
+                });
+        customizeTask(task);
+        task.run();
+        existingOneNestedWriteAdditionalNested(task);
+    }
+
+    protected abstract void existingOneNestedWriteAdditionalNested(DatastoreTestTask task) throws InterruptedException, ExecutionException;
+
+    protected abstract void putTopLevelOneNested(DatastoreTestTask task) throws Exception;
+
+    @Test
+    public final void replaceTopLevelNestedChanged() throws Exception {
+        DatastoreTestTask task = newTestTask().setup(writeOneTopMultipleNested(FOO, BAR)).test(
+                writeOneTopMultipleNested(FOO, BAZ));
+        customizeTask(task);
+        task.run();
+        replaceTopLevelNestedChanged(task);
+    }
+
+    protected abstract void replaceTopLevelNestedChanged(DatastoreTestTask task) throws InterruptedException,
+            ExecutionException;
+
+    @Test
+    public final void putTopLevelWithTwoNested() throws Exception {
+
+        DatastoreTestTask task = newTestTask().test(writeOneTopMultipleNested(FOO, BAR, BAZ));
+        customizeTask(task);
+        task.run();
+        putTopLevelWithTwoNested(task);
+    }
+
+    protected abstract void putTopLevelWithTwoNested(DatastoreTestTask task) throws InterruptedException,
+            ExecutionException;
+
+    @Test
+    public final void twoNestedExistsOneIsDeleted() throws Exception {
+
+        DatastoreTestTask task = newTestTask().setup(writeOneTopMultipleNested(FOO, BAR, BAZ)).test(
+                deleteNested(FOO, BAZ));
+        customizeTask(task);
+        task.run();
+        twoNestedExistsOneIsDeleted(task);
+    }
+
+    protected abstract void twoNestedExistsOneIsDeleted(DatastoreTestTask task) throws InterruptedException,
+            ExecutionException;
+
+    @Test
+    public final void nestedListExistsRootDeleted() throws Exception {
+
+        DatastoreTestTask task = newTestTask().cleanup(null).setup(writeOneTopMultipleNested(FOO, BAR, BAZ))
+                .test(DatastoreTestTask.simpleDelete(TOP_LEVEL));
+        customizeTask(task);
+        task.run();
+        nestedListExistsRootDeleted(task);
+    }
+
+    protected abstract void nestedListExistsRootDeleted(DatastoreTestTask task) throws InterruptedException,
+            ExecutionException;
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java
new file mode 100644 (file)
index 0000000..568f883
--- /dev/null
@@ -0,0 +1,480 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedTransactions;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+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.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class InMemoryDataStoreTest {
+
+    private SchemaContext schemaContext;
+    private InMemoryDOMDataStore domStore;
+
+    @Before
+    public void setupStore() {
+        domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.newDirectExecutorService());
+        schemaContext = TestModel.createTestContext();
+        domStore.onGlobalContextUpdated(schemaContext);
+    }
+
+    @Test
+    public void testTransactionIsolation() throws InterruptedException, ExecutionException {
+
+        assertNotNull(domStore);
+
+        DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
+        assertNotNull(readTx);
+
+        DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+        assertNotNull(writeTx);
+
+        /**
+         * Writes /test in writeTx
+         */
+        NormalizedNode<?, ?> testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
+        writeTx.write(TestModel.TEST_PATH, testNode);
+
+        /**
+         * Reads /test from writeTx Read should return container.
+         */
+        ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
+        assertEquals("read: isPresent", true, writeTxContainer.get().isPresent());
+        assertEquals("read: data", testNode, writeTxContainer.get().get());
+
+        /**
+         * Reads /test from readTx Read should return Absent.
+         */
+        ListenableFuture<Optional<NormalizedNode<?, ?>>> readTxContainer = readTx.read(TestModel.TEST_PATH);
+        assertEquals("read: isPresent", false, readTxContainer.get().isPresent());
+    }
+
+    @Test
+    public void testTransactionCommit() throws InterruptedException, ExecutionException {
+
+        DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+        assertNotNull(writeTx);
+
+        /**
+         * Writes /test in writeTx
+         */
+        NormalizedNode<?, ?> testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
+        writeTx.write(TestModel.TEST_PATH, testNode);
+
+        /**
+         * Reads /test from writeTx Read should return container.
+         */
+        ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
+        assertEquals("read: isPresent", true, writeTxContainer.get().isPresent());
+        assertEquals("read: data", testNode, writeTxContainer.get().get());
+
+        DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
+
+        assertThreePhaseCommit(cohort);
+
+        Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
+                .get();
+        assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
+        assertEquals("After commit read: data", testNode, afterCommitRead.get());
+    }
+
+    @Test
+    public void testDelete() throws Exception {
+
+        DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
+        assertNotNull( writeTx );
+
+        // Write /test and commit
+
+        writeTx.write( TestModel.TEST_PATH, ImmutableNodes.containerNode( TestModel.TEST_QNAME ) );
+
+        assertThreePhaseCommit( writeTx.ready() );
+
+        Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().
+                read(TestModel.TEST_PATH ).get();
+        assertEquals( "After commit read: isPresent", true, afterCommitRead.isPresent() );
+
+        // Delete /test and verify
+
+        writeTx = domStore.newWriteOnlyTransaction();
+
+        writeTx.delete( TestModel.TEST_PATH );
+
+        assertThreePhaseCommit( writeTx.ready() );
+
+        afterCommitRead = domStore.newReadOnlyTransaction().
+                read(TestModel.TEST_PATH ).get();
+        assertEquals( "After commit read: isPresent", false, afterCommitRead.isPresent() );
+    }
+
+    @Test
+    public void testMerge() throws Exception {
+
+        DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
+        assertNotNull( writeTx );
+
+        ContainerNode containerNode = ImmutableContainerNodeBuilder.create()
+                .withNodeIdentifier( new NodeIdentifier( TestModel.TEST_QNAME ) )
+                .addChild( ImmutableNodes.mapNodeBuilder( TestModel.OUTER_LIST_QNAME )
+                        .addChild( ImmutableNodes.mapEntry( TestModel.OUTER_LIST_QNAME,
+                                                            TestModel.ID_QNAME, 1 ) ).build() ).build();
+
+        writeTx.merge( TestModel.TEST_PATH, containerNode );
+
+        assertThreePhaseCommit( writeTx.ready() );
+
+        Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().
+                read(TestModel.TEST_PATH ).get();
+        assertEquals( "After commit read: isPresent", true, afterCommitRead.isPresent() );
+        assertEquals( "After commit read: data", containerNode, afterCommitRead.get() );
+
+        // Merge a new list entry node
+
+        writeTx = domStore.newWriteOnlyTransaction();
+        assertNotNull( writeTx );
+
+        containerNode = ImmutableContainerNodeBuilder.create()
+                .withNodeIdentifier( new NodeIdentifier( TestModel.TEST_QNAME ) )
+                .addChild( ImmutableNodes.mapNodeBuilder( TestModel.OUTER_LIST_QNAME )
+                        .addChild( ImmutableNodes.mapEntry( TestModel.OUTER_LIST_QNAME,
+                                                            TestModel.ID_QNAME, 1 ) )
+                        .addChild( ImmutableNodes.mapEntry( TestModel.OUTER_LIST_QNAME,
+                                                            TestModel.ID_QNAME, 2 ) ).build() ).build();
+
+        writeTx.merge( TestModel.TEST_PATH, containerNode );
+
+        assertThreePhaseCommit( writeTx.ready() );
+
+        afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH ).get();
+        assertEquals( "After commit read: isPresent", true, afterCommitRead.isPresent() );
+        assertEquals( "After commit read: data", containerNode, afterCommitRead.get() );
+    }
+
+
+    @Test
+    public void testExistsForExistingData() throws Exception {
+
+        DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+        assertNotNull( writeTx );
+
+        ContainerNode containerNode = ImmutableContainerNodeBuilder.create()
+            .withNodeIdentifier( new NodeIdentifier( TestModel.TEST_QNAME ) )
+            .addChild( ImmutableNodes.mapNodeBuilder( TestModel.OUTER_LIST_QNAME )
+                .addChild( ImmutableNodes.mapEntry( TestModel.OUTER_LIST_QNAME,
+                    TestModel.ID_QNAME, 1 ) ).build() ).build();
+
+        writeTx.merge( TestModel.TEST_PATH, containerNode );
+
+        CheckedFuture<Boolean, ReadFailedException> exists =
+            writeTx.exists(TestModel.TEST_PATH);
+
+        assertEquals(true, exists.checkedGet());
+
+        DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
+
+        ready.preCommit().get();
+
+        ready.commit().get();
+
+        DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
+        assertNotNull( readTx );
+
+        exists =
+            readTx.exists(TestModel.TEST_PATH);
+
+        assertEquals(true, exists.checkedGet());
+    }
+
+    @Test
+    public void testExistsForNonExistingData() throws Exception {
+
+        DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+        assertNotNull( writeTx );
+
+        CheckedFuture<Boolean, ReadFailedException> exists =
+            writeTx.exists(TestModel.TEST_PATH);
+
+        assertEquals(false, exists.checkedGet());
+
+        DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
+        assertNotNull( readTx );
+
+        exists =
+            readTx.exists(TestModel.TEST_PATH);
+
+        assertEquals(false, exists.checkedGet());
+    }
+
+    @Test(expected=ReadFailedException.class)
+    public void testExistsThrowsReadFailedException() throws Exception {
+
+        DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
+        assertNotNull( readTx );
+
+        readTx.close();
+
+        readTx.exists(TestModel.TEST_PATH).checkedGet();
+    }
+
+
+
+    @Test(expected=ReadFailedException.class)
+    public void testReadWithReadOnlyTransactionClosed() throws Throwable {
+
+        DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
+        assertNotNull( readTx );
+
+        readTx.close();
+
+        doReadAndThrowEx( readTx );
+    }
+
+    @Test(expected=ReadFailedException.class)
+    public void testReadWithReadOnlyTransactionFailure() throws Throwable {
+
+        DataTreeSnapshot mockSnapshot = Mockito.mock( DataTreeSnapshot.class );
+        Mockito.doThrow( new RuntimeException( "mock ex" ) ).when( mockSnapshot )
+        .readNode( Mockito.any( YangInstanceIdentifier.class ) );
+
+        DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadTransaction("1", true, mockSnapshot);
+
+        doReadAndThrowEx( readTx );
+    }
+
+    @Test(expected=ReadFailedException.class)
+    public void testReadWithReadWriteTransactionClosed() throws Throwable {
+
+        DOMStoreReadTransaction readTx = domStore.newReadWriteTransaction();
+        assertNotNull( readTx );
+
+        readTx.close();
+
+        doReadAndThrowEx( readTx );
+    }
+
+    @Test(expected=ReadFailedException.class)
+    public void testReadWithReadWriteTransactionFailure() throws Throwable {
+
+        DataTreeSnapshot mockSnapshot = Mockito.mock( DataTreeSnapshot.class );
+        DataTreeModification mockModification = Mockito.mock( DataTreeModification.class );
+        Mockito.doThrow( new RuntimeException( "mock ex" ) ).when( mockModification )
+        .readNode( Mockito.any( YangInstanceIdentifier.class ) );
+        Mockito.doReturn( mockModification ).when( mockSnapshot ).newModification();
+        @SuppressWarnings("unchecked")
+        TransactionReadyPrototype<String> mockReady = Mockito.mock( TransactionReadyPrototype.class );
+        DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadWriteTransaction("1", false, mockSnapshot, mockReady);
+
+        doReadAndThrowEx( readTx );
+    }
+
+    private static void doReadAndThrowEx( final DOMStoreReadTransaction readTx ) throws Throwable {
+        try {
+            readTx.read(TestModel.TEST_PATH).get();
+        } catch( ExecutionException e ) {
+            throw e.getCause();
+        }
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void testWriteWithTransactionReady() throws Exception {
+
+        DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
+
+        writeTx.ready();
+
+        // Should throw ex
+        writeTx.write( TestModel.TEST_PATH, ImmutableNodes.containerNode( TestModel.TEST_QNAME ) );
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void testReadyWithTransactionAlreadyReady() throws Exception {
+
+        DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
+
+        writeTx.ready();
+
+        // Should throw ex
+        writeTx.ready();
+    }
+
+    @Test
+    public void testTransactionAbort() throws InterruptedException, ExecutionException {
+
+        DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+        assertNotNull(writeTx);
+
+        assertTestContainerWrite(writeTx);
+
+        DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
+
+        assertTrue(cohort.canCommit().get().booleanValue());
+        cohort.preCommit().get();
+        cohort.abort().get();
+
+        Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
+                .get();
+        assertFalse(afterCommitRead.isPresent());
+    }
+
+    @Test
+    public void testTransactionChain() throws InterruptedException, ExecutionException {
+        DOMStoreTransactionChain txChain = domStore.createTransactionChain();
+        assertNotNull(txChain);
+
+        /**
+         * We alocate new read-write transaction and write /test
+         *
+         *
+         */
+        DOMStoreReadWriteTransaction firstTx = txChain.newReadWriteTransaction();
+        assertTestContainerWrite(firstTx);
+
+        /**
+         * First transaction is marked as ready, we are able to allocate chained
+         * transactions
+         */
+        DOMStoreThreePhaseCommitCohort firstWriteTxCohort = firstTx.ready();
+
+        /**
+         * We alocate chained transaction - read transaction, note first one is
+         * still not commited to datastore.
+         */
+        DOMStoreReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
+
+        /**
+         *
+         * We test if we are able to read data from tx, read should not fail
+         * since we are using chained transaction.
+         *
+         *
+         */
+        assertTestContainerExists(secondReadTx);
+
+        /**
+         *
+         * We alocate next transaction, which is still based on first one, but
+         * is read-write.
+         *
+         */
+        DOMStoreReadWriteTransaction thirdDeleteTx = txChain.newReadWriteTransaction();
+
+        /**
+         * We test existence of /test in third transaction container should
+         * still be visible from first one (which is still uncommmited).
+         *
+         *
+         */
+        assertTestContainerExists(thirdDeleteTx);
+
+        /**
+         * We delete node in third transaction
+         */
+        thirdDeleteTx.delete(TestModel.TEST_PATH);
+
+        /**
+         * third transaction is sealed.
+         */
+        DOMStoreThreePhaseCommitCohort thirdDeleteTxCohort = thirdDeleteTx.ready();
+
+        /**
+         * We commit first transaction
+         *
+         */
+        assertThreePhaseCommit(firstWriteTxCohort);
+
+        // Alocates store transacion
+        DOMStoreReadTransaction storeReadTx = domStore.newReadOnlyTransaction();
+        /**
+         * We verify transaction is commited to store, container should exists
+         * in datastore.
+         */
+        assertTestContainerExists(storeReadTx);
+        /**
+         * We commit third transaction
+         *
+         */
+        assertThreePhaseCommit(thirdDeleteTxCohort);
+    }
+
+    @Test
+    @Ignore
+    public void testTransactionConflict() throws InterruptedException, ExecutionException {
+        DOMStoreReadWriteTransaction txOne = domStore.newReadWriteTransaction();
+        DOMStoreReadWriteTransaction txTwo = domStore.newReadWriteTransaction();
+        assertTestContainerWrite(txOne);
+        assertTestContainerWrite(txTwo);
+
+        /**
+         * Commits transaction
+         */
+        assertThreePhaseCommit(txOne.ready());
+
+        /**
+         * Asserts that txTwo could not be commited
+         */
+        assertFalse(txTwo.ready().canCommit().get());
+    }
+
+    private static void assertThreePhaseCommit(final DOMStoreThreePhaseCommitCohort cohort)
+            throws InterruptedException, ExecutionException {
+        assertTrue(cohort.canCommit().get().booleanValue());
+        cohort.preCommit().get();
+        cohort.commit().get();
+    }
+
+    private static Optional<NormalizedNode<?, ?>> assertTestContainerWrite(final DOMStoreReadWriteTransaction writeTx)
+            throws InterruptedException, ExecutionException {
+        /**
+         *
+         * Writes /test in writeTx
+         *
+         */
+        writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+        return assertTestContainerExists(writeTx);
+    }
+
+    /**
+     * Reads /test from readTx Read should return container.
+     */
+    private static Optional<NormalizedNode<?, ?>> assertTestContainerExists(final DOMStoreReadTransaction readTx)
+            throws InterruptedException, ExecutionException {
+
+        ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
+        assertTrue(writeTxContainer.get().isPresent());
+        return writeTxContainer.get();
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/RootScopeSubtreeTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/RootScopeSubtreeTest.java
new file mode 100644 (file)
index 0000000..43b339e
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import java.util.concurrent.ExecutionException;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class RootScopeSubtreeTest extends DefaultDataChangeListenerTestSuite {
+
+    @Override
+    protected void customizeTask(final DatastoreTestTask task) {
+        task.changeListener(TOP_LEVEL, DataChangeScope.SUBTREE);
+    }
+
+    @Override
+    public void putTopLevelOneNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+        assertContains(change.getCreatedData(), TOP_LEVEL, path(FOO), path(FOO, BAR));
+        assertEmpty(change.getUpdatedData());
+        assertEmpty(change.getRemovedPaths());
+    }
+
+    @Override
+    public void replaceTopLevelNestedChanged(final DatastoreTestTask task) throws InterruptedException,
+            ExecutionException {
+
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+        assertContains(change.getCreatedData(), path(FOO, BAZ));
+        assertContains(change.getUpdatedData(), TOP_LEVEL, path(FOO));
+        assertContains(change.getRemovedPaths(), path(FOO, BAR));
+    }
+
+    @Override
+    protected void putTopLevelWithTwoNested(final DatastoreTestTask task) throws InterruptedException,
+            ExecutionException {
+
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+        assertContains(change.getCreatedData(), TOP_LEVEL, path(FOO), path(FOO, BAR), path(FOO, BAZ));
+        assertEmpty(change.getUpdatedData());
+        assertEmpty(change.getRemovedPaths());
+    }
+
+    @Override
+    protected void twoNestedExistsOneIsDeleted(final DatastoreTestTask task) throws InterruptedException,
+            ExecutionException {
+
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+        assertEmpty(change.getCreatedData());
+        assertContains(change.getUpdatedData(), TOP_LEVEL, path(FOO));
+        assertContains(change.getRemovedPaths(), path(FOO, BAZ));
+    }
+
+    @Override
+    protected void nestedListExistsRootDeleted(final DatastoreTestTask task) throws InterruptedException,
+            ExecutionException {
+
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+        assertEmpty(change.getCreatedData());
+        assertEmpty(change.getUpdatedData());
+        assertContains(change.getRemovedPaths(), TOP_LEVEL, path(FOO), path(FOO, BAR), path(FOO, BAZ));
+    }
+
+    @Override
+    protected void existingOneNestedWriteAdditionalNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+        assertContains(change.getCreatedData(), path(FOO,BAZ));
+        assertNotContains(change.getCreatedData(), path(FOO,BAR));
+        assertContains(change.getUpdatedData(), TOP_LEVEL, path(FOO));
+        assertEmpty(change.getRemovedPaths());
+    }
+
+    @Override
+    protected void existingTopWriteTwoNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+        assertContains(change.getCreatedData(), path(FOO,BAR),path(FOO,BAZ));
+        assertContains(change.getUpdatedData(), TOP_LEVEL, path(FOO));
+        assertNotContains(change.getUpdatedData(), path(FOO,BAR));
+        assertEmpty(change.getRemovedPaths());
+    }
+
+    @Override
+    protected void existingTopWriteSibling(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+        assertContains(change.getCreatedData(), path(FOO_SIBLING));
+        assertContains(change.getUpdatedData(), TOP_LEVEL);
+        assertNotContains(change.getUpdatedData(), path(FOO));
+        assertEmpty(change.getRemovedPaths());
+    }
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/SchemaUpdateForTransactionTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/SchemaUpdateForTransactionTest.java
new file mode 100644 (file)
index 0000000..e7af4df
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static org.junit.Assert.assertNotNull;
+import com.google.common.base.Throwables;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.bi.ba.rpcservice.rev140701.RockTheHouseInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class SchemaUpdateForTransactionTest {
+
+    private static final YangInstanceIdentifier TOP_PATH = YangInstanceIdentifier.of(Top.QNAME);
+    private SchemaContext schemaContext;
+    private InMemoryDOMDataStore domStore;
+
+    @Before
+    public void setupStore() {
+        domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.newDirectExecutorService());
+        loadSchemas(RockTheHouseInput.class);
+    }
+
+    public void loadSchemas(final Class<?>... classes) {
+        YangModuleInfo moduleInfo;
+        try {
+            ModuleInfoBackedContext context = ModuleInfoBackedContext.create();
+            for (Class<?> clz : classes) {
+                moduleInfo = BindingReflections.getModuleInfo(clz);
+
+                context.registerModuleInfo(moduleInfo);
+            }
+            schemaContext = context.tryToCreateSchemaContext().get();
+            domStore.onGlobalContextUpdated(schemaContext);
+        } catch (Exception e) {
+            Throwables.propagateIfPossible(e);
+        }
+    }
+
+    /**
+     * Test suite tests allocating transaction when schema context
+     * does not contain module necessary for client write,
+     * then triggering update of global schema context
+     * and then performing write (according to new module).
+     *
+     * If transaction between allocation and schema context was
+     * unmodified, it is safe to change its schema context
+     * to new one (e.g. it will be same as if allocated after
+     * schema context update.)
+     *
+     * @throws InterruptedException
+     * @throws ExecutionException
+     */
+    @Test
+    public void testTransactionSchemaUpdate() throws InterruptedException, ExecutionException {
+
+        assertNotNull(domStore);
+
+        // We allocate transaction, initial schema context does not
+        // contain Lists model
+        DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+        assertNotNull(writeTx);
+
+        // we trigger schema context update to contain Lists model
+        loadSchemas(RockTheHouseInput.class, Top.class);
+
+        /**
+         *
+         * Writes /test in writeTx, this write should not fail
+         * with IllegalArgumentException since /test is in
+         * schema context.
+         *
+         */
+        writeTx.write(TOP_PATH, ImmutableNodes.containerNode(Top.QNAME));
+
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestDCLExecutorService.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestDCLExecutorService.java
new file mode 100644 (file)
index 0000000..a01933c
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import com.google.common.util.concurrent.ForwardingExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.concurrent.ExecutorService;
+
+/**
+ * A forwarding Executor used by unit tests for DataChangeListener notifications
+ *
+ * @author Thomas Pantelis
+ */
+public class TestDCLExecutorService extends ForwardingExecutorService {
+
+    // Start with a same thread executor to avoid timing issues during test setup.
+    private volatile ExecutorService currentExecutor = MoreExecutors.newDirectExecutorService();
+
+    // The real executor to use when test setup is complete.
+    private final ExecutorService postSetupExecutor;
+
+
+    public TestDCLExecutorService( final ExecutorService postSetupExecutor ) {
+        this.postSetupExecutor = postSetupExecutor;
+    }
+
+    @Override
+    protected ExecutorService delegate() {
+        return currentExecutor;
+    }
+
+    public void afterTestSetup() {
+        // Test setup complete - switch to the real executor.
+        currentExecutor = postSetupExecutor;
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestModel.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestModel.java
new file mode 100644 (file)
index 0000000..66e71a3
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.Set;
+
+public class TestModel {
+
+    public static final QName TEST_QNAME = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test", "2014-03-13",
+        "test");
+    public static final QName OUTER_LIST_QNAME = QName.create(TEST_QNAME, "outer-list");
+    public static final QName INNER_LIST_QNAME = QName.create(TEST_QNAME, "inner-list");
+    public static final QName OUTER_CHOICE_QNAME = QName.create(TEST_QNAME, "outer-choice");
+    public static final QName ID_QNAME = QName.create(TEST_QNAME, "id");
+    public static final QName NAME_QNAME = QName.create(TEST_QNAME, "name");
+    public static final QName VALUE_QNAME = QName.create(TEST_QNAME, "value");
+    private static final String DATASTORE_TEST_YANG = "/odl-datastore-test.yang";
+
+    public static final YangInstanceIdentifier TEST_PATH = YangInstanceIdentifier.of(TEST_QNAME);
+    public static final YangInstanceIdentifier OUTER_LIST_PATH = YangInstanceIdentifier.builder(TEST_PATH).node(OUTER_LIST_QNAME).build();
+    public static final QName TWO_QNAME = QName.create(TEST_QNAME, "two");
+    public static final QName THREE_QNAME = QName.create(TEST_QNAME, "three");
+
+
+    public static final InputStream getDatastoreTestInputStream() {
+        return getInputStream(DATASTORE_TEST_YANG);
+    }
+
+    private static InputStream getInputStream(final String resourceName) {
+        return TestModel.class.getResourceAsStream(DATASTORE_TEST_YANG);
+    }
+
+    public static SchemaContext createTestContext() {
+        YangParserImpl parser = new YangParserImpl();
+        Set<Module> modules = parser.parseYangModelsFromStreams(Collections.singletonList(getDatastoreTestInputStream()));
+        return parser.resolveSchemaContext(modules);
+    }
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeBaseTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeBaseTest.java
new file mode 100644 (file)
index 0000000..ddbba76
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.concurrent.ExecutionException;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class WildcardedScopeBaseTest extends DefaultDataChangeListenerTestSuite {
+
+    private static final YangInstanceIdentifier TOP_LEVEL_LIST_ALL = TOP_LEVEL.node(TopLevelList.QNAME).node(
+            TopLevelList.QNAME);
+
+    @Override
+    protected void customizeTask(final DatastoreTestTask task) {
+        task.changeListener(TOP_LEVEL_LIST_ALL, DataChangeScope.BASE);
+    }
+
+    @Override
+    public void putTopLevelOneNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+        assertNotNull(change);
+
+        /*
+         * Created data must not contain nested-list item, since that is two-level deep.
+         */
+        assertNotContains(change.getCreatedData(), TOP_LEVEL,path(FOO, BAR));
+        assertContains(change.getCreatedData(), path(FOO) );
+
+        assertEmpty(change.getUpdatedData());
+        assertEmpty(change.getRemovedPaths());
+
+    }
+
+    @Override
+    public void replaceTopLevelNestedChanged(final DatastoreTestTask task) throws InterruptedException,
+            ExecutionException {
+
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+        assertNotNull(change);
+        /*
+         * Created data must NOT contain nested-list item since scope is base, and change is two
+         * level deep.
+         */
+        assertNotContains(change.getCreatedData(), path(FOO, BAZ));
+        assertContains(change.getUpdatedData(), path(FOO));
+        assertNotContains(change.getUpdatedData(), TOP_LEVEL);
+        /*
+         * Removed data must NOT contain nested-list item since scope is base, and change is two
+         * level deep.
+         */
+        assertNotContains(change.getRemovedPaths(), path(FOO, BAR));
+
+    }
+
+    @Override
+    protected void putTopLevelWithTwoNested(final DatastoreTestTask task) throws InterruptedException,
+            ExecutionException {
+
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+        assertNotNull(change);
+        assertFalse(change.getCreatedData().isEmpty());
+
+        // Base event should contain only changed item, no details about child.
+        assertContains(change.getCreatedData(), path(FOO));
+        assertNotContains(change.getCreatedData(), TOP_LEVEL,path(FOO, BAR), path(FOO, BAZ));
+        assertEmpty(change.getUpdatedData());
+        assertEmpty(change.getRemovedPaths());
+
+    }
+
+    @Override
+    protected void twoNestedExistsOneIsDeleted(final DatastoreTestTask task) throws InterruptedException,
+            ExecutionException {
+
+        /*
+         * Base listener should be notified only and only if actual node changed its state,
+         * since deletion of child, did not result in change of node we are listening
+         * for, we should not be getting data change event
+         * and this means settable future containing receivedDataChangeEvent is not done.
+         *
+         */
+        task.verifyNoChangeEvent();
+    }
+
+    @Override
+    public void nestedListExistsRootDeleted(final DatastoreTestTask task) throws InterruptedException,
+            ExecutionException {
+
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+        assertEmpty(change.getCreatedData());
+        assertEmpty(change.getUpdatedData());
+
+        assertNotContains(change.getUpdatedData(), TOP_LEVEL);
+        /*
+         *  Scope base listener event should contain top-level-list item and nested list path
+         *  and should not contain baz, bar which are two-level deep
+         */
+        assertContains(change.getRemovedPaths(), path(FOO));
+        assertNotContains(change.getRemovedPaths(),path(FOO, BAZ),path(FOO,BAR));
+    }
+
+    @Override
+    protected void existingOneNestedWriteAdditionalNested(final DatastoreTestTask task) {
+        /*
+         * One listener should be notified only and only if actual node changed its state,
+         * since deletion of nested child (in this case /nested-list/nested-list[foo],
+         * did not result in change of node we are listening
+         * for, we should not be getting data change event
+         * and this means settable future containing receivedDataChangeEvent is not done.
+         *
+         */
+        task.verifyNoChangeEvent();
+    }
+
+    @Override
+    protected void existingTopWriteTwoNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+        /*
+         * One listener should be notified only and only if actual node changed its state,
+         * since deletion of nested child (in this case /nested-list/nested-list[foo],
+         * did not result in change of node we are listening
+         * for, we should not be getting data change event
+         * and this means settable future containing receivedDataChangeEvent is not done.
+         *
+         */
+        task.verifyNoChangeEvent();
+    }
+
+    @Override
+    protected void existingTopWriteSibling(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+        assertContains(change.getCreatedData(), path(FOO_SIBLING));
+        assertNotContains(change.getUpdatedData(), path(FOO), TOP_LEVEL);
+        assertEmpty(change.getRemovedPaths());
+    }
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeOneTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeOneTest.java
new file mode 100644 (file)
index 0000000..75f9fce
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.concurrent.ExecutionException;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.top.level.list.NestedList;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class WildcardedScopeOneTest extends DefaultDataChangeListenerTestSuite {
+
+    private static final YangInstanceIdentifier TOP_LEVEL_LIST_ALL = TOP_LEVEL.node(TopLevelList.QNAME).node(
+            TopLevelList.QNAME);
+
+    @Override
+    protected void customizeTask(final DatastoreTestTask task) {
+        task.changeListener(TOP_LEVEL_LIST_ALL, DataChangeScope.ONE);
+    }
+
+    @Override
+    public void putTopLevelOneNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+        assertNotNull(change);
+
+        assertNotContains(change.getCreatedData(), TOP_LEVEL,path(FOO, BAR));
+        assertContains(change.getCreatedData(), path(FOO), path(FOO).node(NestedList.QNAME));
+
+        assertEmpty(change.getUpdatedData());
+        assertEmpty(change.getRemovedPaths());
+
+    }
+
+    @Override
+    public void replaceTopLevelNestedChanged(final DatastoreTestTask task) throws InterruptedException,
+            ExecutionException {
+
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+        assertNotNull(change);
+        /*
+         * Created data must NOT contain nested-list item since scope is base, and change is two
+         * level deep.
+         */
+        assertNotContains(change.getCreatedData(), path(FOO, BAZ));
+        assertContains(change.getUpdatedData(), path(FOO),path(FOO).node(NestedList.QNAME));
+        assertNotContains(change.getUpdatedData(), TOP_LEVEL);
+        /*
+         * Removed data must NOT contain nested-list item since scope is base, and change is two
+         * level deep.
+         */
+        assertNotContains(change.getRemovedPaths(), path(FOO, BAR));
+
+    }
+
+    @Override
+    protected void putTopLevelWithTwoNested(final DatastoreTestTask task) throws InterruptedException,
+            ExecutionException {
+
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+        assertNotNull(change);
+        assertFalse(change.getCreatedData().isEmpty());
+
+        // Base event should contain only changed item, and details about immediate child.
+        assertContains(change.getCreatedData(), path(FOO),path(FOO).node(NestedList.QNAME));
+        assertNotContains(change.getCreatedData(), TOP_LEVEL,path(FOO, BAR), path(FOO, BAZ));
+        assertEmpty(change.getUpdatedData());
+        assertEmpty(change.getRemovedPaths());
+
+    }
+
+    @Override
+    protected void twoNestedExistsOneIsDeleted(final DatastoreTestTask task) throws InterruptedException,
+            ExecutionException {
+
+        /*
+         * One listener should be notified only and only if actual node changed its state,
+         * since deletion of nested child (in this case /nested-list/nested-list[foo],
+         * did not result in change of node we are listening
+         * for, we should not be getting data change event
+         * and this means settable future containing receivedDataChangeEvent is not done.
+         *
+         */
+        task.verifyNoChangeEvent();
+    }
+
+    @Override
+    public void nestedListExistsRootDeleted(final DatastoreTestTask task) throws InterruptedException,
+            ExecutionException {
+
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+        assertEmpty(change.getCreatedData());
+        assertEmpty(change.getUpdatedData());
+
+        assertNotContains(change.getUpdatedData(), TOP_LEVEL);
+        assertContains(change.getRemovedPaths(), path(FOO),path(FOO).node(NestedList.QNAME));
+        assertNotContains(change.getRemovedPaths(), path(FOO, BAZ),path(FOO,BAR));
+    }
+
+    @Override
+    protected void existingOneNestedWriteAdditionalNested(final DatastoreTestTask task) {
+        /*
+         * One listener should be notified only and only if actual node changed its state,
+         * since deletion of nested child (in this case /nested-list/nested-list[foo],
+         * did not result in change of node we are listening
+         * for, we should not be getting data change event
+         * and this means settable future containing receivedDataChangeEvent is not done.
+         *
+         */
+        task.verifyNoChangeEvent();
+    }
+
+    @Override
+    protected void existingTopWriteTwoNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+        /*
+         * One listener should be notified only and only if actual node changed its state,
+         * since deletion of nested child (in this case /nested-list/nested-list[foo],
+         * did not result in change of node we are listening
+         * for, we should not be getting data change event
+         * and this means settable future containing receivedDataChangeEvent is not done.
+         *
+         */
+        task.verifyNoChangeEvent();
+    }
+
+    @Override
+    protected void existingTopWriteSibling(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+        assertContains(change.getCreatedData(), path(FOO_SIBLING));
+        assertNotContains(change.getUpdatedData(),path(FOO), TOP_LEVEL);
+        assertEmpty(change.getRemovedPaths());
+    }
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeSubtreeTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeSubtreeTest.java
new file mode 100644 (file)
index 0000000..a7fa24f
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.concurrent.ExecutionException;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class WildcardedScopeSubtreeTest extends DefaultDataChangeListenerTestSuite {
+
+    private static final YangInstanceIdentifier TOP_LEVEL_LIST_ALL = TOP_LEVEL.node(TopLevelList.QNAME).node(
+            TopLevelList.QNAME);
+
+    @Override
+    protected void customizeTask(final DatastoreTestTask task) {
+        task.changeListener(TOP_LEVEL_LIST_ALL, DataChangeScope.SUBTREE);
+    }
+
+    @Override
+    public void putTopLevelOneNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+        assertNotContains(change.getCreatedData(), TOP_LEVEL);
+        assertContains(change.getCreatedData(), path(FOO), path(FOO, BAR));
+        assertEmpty(change.getUpdatedData());
+        assertEmpty(change.getRemovedPaths());
+
+    }
+
+    @Override
+    public void replaceTopLevelNestedChanged(final DatastoreTestTask task) throws InterruptedException,
+            ExecutionException {
+
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+        assertNotNull(change);
+
+        assertContains(change.getCreatedData(), path(FOO, BAZ));
+        assertContains(change.getUpdatedData(), path(FOO));
+        assertNotContains(change.getUpdatedData(), TOP_LEVEL);
+        assertContains(change.getRemovedPaths(), path(FOO, BAR));
+
+    }
+
+    @Override
+    protected void putTopLevelWithTwoNested(final DatastoreTestTask task) throws InterruptedException,
+            ExecutionException {
+
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+        assertNotNull(change);
+        assertFalse(change.getCreatedData().isEmpty());
+
+        assertContains(change.getCreatedData(), path(FOO), path(FOO, BAR), path(FOO, BAZ));
+        assertNotContains(change.getCreatedData(), TOP_LEVEL);
+        assertEmpty(change.getUpdatedData());
+        assertEmpty(change.getRemovedPaths());
+
+    }
+
+    @Override
+    protected void twoNestedExistsOneIsDeleted(final DatastoreTestTask task) throws InterruptedException,
+            ExecutionException {
+
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+        assertNotNull(change);
+        assertTrue(change.getCreatedData().isEmpty());
+        assertContains(change.getUpdatedData(), path(FOO));
+        assertNotContains(change.getUpdatedData(), TOP_LEVEL);
+        assertContains(change.getRemovedPaths(),path(FOO, BAZ));
+    }
+
+    @Override
+    public void nestedListExistsRootDeleted(final DatastoreTestTask task) throws InterruptedException,
+            ExecutionException {
+
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+        assertEmpty(change.getCreatedData());
+        assertEmpty(change.getUpdatedData());
+
+        assertNotContains(change.getUpdatedData(), TOP_LEVEL);
+        assertContains(change.getRemovedPaths(), path(FOO),path(FOO, BAZ),path(FOO,BAR));
+    }
+
+    @Override
+    protected void existingOneNestedWriteAdditionalNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+        assertContains(change.getCreatedData(), path(FOO,BAZ));
+        assertNotContains(change.getCreatedData(), path(FOO,BAR));
+        assertContains(change.getUpdatedData(), path(FOO));
+        assertNotContains(change.getUpdatedData(), TOP_LEVEL);
+        assertEmpty(change.getRemovedPaths());
+    }
+
+    @Override
+    protected void existingTopWriteTwoNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+        assertContains(change.getCreatedData(), path(FOO,BAR),path(FOO,BAZ));
+        assertContains(change.getUpdatedData(), path(FOO));
+        assertNotContains(change.getUpdatedData(), TOP_LEVEL, path(FOO,BAR));
+        assertEmpty(change.getRemovedPaths());
+    }
+
+    @Override
+    protected void existingTopWriteSibling(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+        assertContains(change.getCreatedData(), path(FOO_SIBLING));
+        assertNotContains(change.getUpdatedData(), path(FOO), TOP_LEVEL);
+        assertEmpty(change.getRemovedPaths());
+    }
+}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/resources/odl-datastore-test.yang b/opendaylight/md-sal/sal-inmemory-datastore/src/test/resources/odl-datastore-test.yang
new file mode 100644 (file)
index 0000000..17541fe
--- /dev/null
@@ -0,0 +1,42 @@
+module odl-datastore-test {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test";
+    prefix "store-test";
+    
+    revision "2014-03-13" {
+        description "Initial revision.";
+    }
+
+    container test {
+        list outer-list {
+            key id;
+            leaf id {
+                type uint16;
+            }
+            choice outer-choice {
+                case one {
+                    leaf one {
+                        type string;
+                    }
+                }
+                case two-three {
+                    leaf two {
+                        type string;
+                    }
+                    leaf three {
+                        type string;
+                    }
+               }
+           }
+           list inner-list {
+                key name;
+                leaf name {
+                    type string;
+                }
+                leaf value {
+                    type string;
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-test-model/pom.xml b/opendaylight/md-sal/sal-test-model/pom.xml
new file mode 100644 (file)
index 0000000..446e167
--- /dev/null
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>sal-parent</artifactId>
+        <groupId>org.opendaylight.controller</groupId>
+        <version>1.3.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>sal-test-model</artifactId>
+    <packaging>bundle</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-binding</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools.model</groupId>
+            <artifactId>yang-ext</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <configuration>
+                    <includes>
+                        <include>org.opendaylight.controller.*</include>
+                    </includes>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>pre-test</id>
+                        <goals>
+                            <goal>prepare-agent</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>post-test</id>
+                        <goals>
+                            <goal>report</goal>
+                        </goals>
+                        <phase>test</phase>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate-sources</goal>
+                        </goals>
+                        <configuration>
+                            <codeGenerators>
+                                <generator>
+                                    <codeGeneratorClass>
+                                        org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl
+                                    </codeGeneratorClass>
+                                    <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+                                </generator>
+                            </codeGenerators>
+                            <inspectDependencies>true</inspectDependencies>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+    <scm>
+        <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+        <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+        <tag>HEAD</tag>
+        <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+    </scm>
+
+</project>
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-test-model/src/main/java/org/opendaylight/controller/md/sal/test/model/util/ListsBindingUtils.java b/opendaylight/md-sal/sal-test-model/src/main/java/org/opendaylight/controller/md/sal/test/model/util/ListsBindingUtils.java
new file mode 100644 (file)
index 0000000..af7a329
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.test.model.util;
+
+import com.google.common.collect.ImmutableList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeLeafOnlyUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeLeafOnlyUsesAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ListViaUses;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ListViaUsesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ListViaUsesKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.TopBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.top.level.list.NestedList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.top.level.list.NestedListKey;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.Arrays;
+
+public class ListsBindingUtils {
+
+    private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.create(Top.class);
+
+    private ListsBindingUtils() {
+        throw new UnsupportedOperationException();
+    }
+
+    public static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
+    public static final TopLevelListKey TOP_BAR_KEY = new TopLevelListKey("bar");
+    public static final ListViaUsesKey USES_ONE_KEY = new ListViaUsesKey("one");
+    public static final ListViaUsesKey USES_TWO_KEY = new ListViaUsesKey("two");
+
+
+    public static InstanceIdentifier<TopLevelList> path(final TopLevelListKey key) {
+        return TOP_PATH.child(TopLevelList.class, key);
+    }
+
+    public static InstanceIdentifier<NestedList> path(final TopLevelListKey top,final NestedListKey nested) {
+        return path(top).child(NestedList.class, nested);
+    }
+
+    public static InstanceIdentifier<ListViaUses> path(final TopLevelListKey top,final ListViaUsesKey uses) {
+        return path(top).augmentation(TreeComplexUsesAugment.class).child(ListViaUses.class, uses);
+    }
+
+    public static <T extends DataObject & Augmentation<TopLevelList>> InstanceIdentifier<T> path(final TopLevelListKey key, final Class<T> augmentation) {
+        return path(key).augmentation(augmentation);
+    }
+
+    public static Top top(final TopLevelList... listItems) {
+        return new TopBuilder().setTopLevelList(Arrays.asList(listItems)).build();
+    }
+
+    public static TopLevelList topLevelList(final TopLevelListKey key) {
+        return new TopLevelListBuilder().setKey(key).build();
+    }
+
+    public static TopLevelList topLevelList(final TopLevelListKey key, final TreeComplexUsesAugment augment) {
+        TopLevelListBuilder builder = new TopLevelListBuilder().setKey(key);
+        builder.addAugmentation(TreeComplexUsesAugment.class, augment);
+        return builder.build();
+    }
+
+    public static TreeComplexUsesAugment complexUsesAugment(final ListViaUsesKey... keys) {
+        ImmutableList.Builder<ListViaUses> listViaUses = ImmutableList.<ListViaUses> builder();
+        for (ListViaUsesKey key : keys) {
+            listViaUses.add(new ListViaUsesBuilder().setKey(key).build());
+        }
+        return new TreeComplexUsesAugmentBuilder().setListViaUses(listViaUses.build()).build();
+    }
+
+    public static TreeLeafOnlyUsesAugment leafOnlyUsesAugment(String leafFromGroupingValue) {
+
+        return new TreeLeafOnlyUsesAugmentBuilder().setLeafFromGrouping(leafFromGroupingValue).build();
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-mdsal-augment-test.yang b/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-mdsal-augment-test.yang
new file mode 100644 (file)
index 0000000..ddd7687
--- /dev/null
@@ -0,0 +1,93 @@
+module opendaylight-mdsal-augment-test {
+
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:augment";
+    prefix aug-test;
+
+    import opendaylight-mdsal-list-test {
+        prefix test;
+    }
+    import yang-ext {
+        prefix ext;
+    }
+
+    description
+        "This module contains a collection of YANG augmentations used for
+        some test cases.";
+
+    revision 2014-07-09 {
+        description
+        "Test model for testing data broker with nested lists.";
+    }
+
+    grouping leaf-from-grouping {
+        leaf leaf-from-grouping {
+            type string;
+        }
+    }
+
+    grouping complex-from-grouping {
+        container container-with-uses {
+            uses leaf-from-grouping;
+        }
+        list list-via-uses {
+            key "name";
+            leaf name {
+                type string;
+            }
+        }
+    
+    }
+
+    augment "/test:top/test:top-level-list" {
+        ext:augment-identifier tree-leaf-only-uses-augment;
+        uses leaf-from-grouping;
+    }
+
+    augment "/test:put-top/test:input/test:top-level-list" {
+        ext:augment-identifier rpc-leaf-only-uses-augment;
+        uses leaf-from-grouping;
+    }
+
+    augment "/test:top/test:top-level-list" {
+        ext:augment-identifier tree-complex-uses-augment;
+        uses complex-from-grouping;
+    }
+
+    augment "/test:put-top/test:input/test:top-level-list" {
+        ext:augment-identifier rpc-complex-uses-augment;
+        uses complex-from-grouping;
+    }
+
+    augment "/test:top/test:top-level-list" {
+        ext:augment-identifier tree-leaf-only-augment;
+
+        leaf simple-value {
+            type string;
+        }
+    }
+    
+    augment "/test:top/test:top-level-list" {
+        ext:augment-identifier tree-second-leaf-only-augment;
+
+        leaf second-simple-value {
+            type string;
+        }
+    }
+
+    augment "/test:put-top/test:input/test:top-level-list" {
+        ext:augment-identifier rpc-leaf-only-augment;
+
+        leaf simple-value {
+            type string;
+        }
+    }
+
+    augment "/test:put-top/test:input/test:top-level-list" {
+        ext:augment-identifier rpc-second-leaf-only-augment;
+
+        leaf second-simple-value {
+            type string;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-mdsal-list-test.yang b/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-mdsal-list-test.yang
new file mode 100644 (file)
index 0000000..d4775af
--- /dev/null
@@ -0,0 +1,55 @@
+module opendaylight-mdsal-list-test {
+
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:list";
+    prefix list-test;
+
+
+    description
+        "This module contains a collection of YANG definitions used for
+        some test cases.";
+
+    revision 2014-07-01 {
+        description
+        "Test model for testing data broker with nested lists.";
+    }
+
+    grouping two-level-list {
+        list top-level-list {
+            description
+                "Top Level List";
+            key "name";
+            leaf name {
+                type string;
+            }
+            list nested-list {
+                key "name";
+                leaf name {
+                    type string;
+                }
+                leaf type {
+                    type string;
+                    mandatory true;
+                    description
+                        "Mandatory type of list.";
+                }
+                ordered-by user;
+                description
+                    "A list of service functions that compose the service chain";
+            }
+        }
+    }
+
+    container top {
+        uses two-level-list;
+    }
+
+    notification two-level-list-changed {
+        uses two-level-list;
+    }
+
+    rpc put-top {
+        input {
+            uses two-level-list;
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-of-migration-test-model.yang b/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-of-migration-test-model.yang
new file mode 100644 (file)
index 0000000..24ff5dd
--- /dev/null
@@ -0,0 +1,156 @@
+module opendaylight-of-migration-test-model {
+
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:of-migration-test-model";
+    prefix of-migration-test;
+
+    import opendaylight-mdsal-list-test {prefix test;}
+    import yang-ext {prefix ext;}
+    import opendaylight-mdsal-augment-test {prefix aug;}
+    import opendaylight-test-routed-rpc {prefix routed;}
+
+    description
+        "This module contains a collection of YANG definitions used for
+        test cases that used to depend on flow model.";
+
+    revision 2015-02-10 {
+    }
+
+    typedef bit-flags {
+        type bits {
+            bit FLAG_ONE;
+            bit FLAG_TWO;
+            bit FLAG_THREE;
+            bit FLAG_FOUR;
+            bit FLAG_FIVE;
+        }
+    }
+
+    typedef custom-enum {
+        type enumeration {
+            enum type1;
+            enum type2;
+            enum type3;
+        }
+    }
+
+    grouping enum-grouping {
+        leaf attr-enum {
+            type custom-enum;
+        }
+    }
+
+    grouping aug-grouping {
+        container cont1 {
+            leaf attr-str {
+                type string;
+            }
+        }
+
+        container cont2 {
+            list contlist1 {
+                key "attr-str";
+
+                leaf attr-str {
+                    type string;
+                }
+
+                uses enum-grouping;
+            }
+        }
+
+        leaf attr-str1 {
+            type string;
+        }
+
+        leaf attr-str2 {
+            type string;
+        }
+
+        leaf attr-str3 {
+            type string;
+        }
+
+        leaf attr-str4 {
+            type string;
+        }
+
+        list list1 {
+            key "attr-str";
+            leaf attr-str {
+                type string;
+            }
+
+            list list1-1 {
+                key "attr-int";
+                leaf attr-int {
+                    type int32;
+                }
+
+                leaf attr-str {
+                    type string;
+                }
+
+                leaf flags {
+                    type bit-flags;
+                }
+            }
+
+            list list1-2 {
+                key "attr-int";
+                leaf attr-int {
+                    type int32;
+                }
+
+                leaf attr-str {
+                    type string;
+                }
+            }
+        }
+    }
+
+    augment "/test:top/test:top-level-list" {
+        ext:augment-identifier tll-complex-augment;
+        uses aug-grouping;
+    }
+
+    augment "/test:top/test:top-level-list/list1/list1-1"  {
+        ext:augment-identifier list11-simple-augment;
+
+        leaf attr-str2 {
+            type string;
+        }
+
+        container cont {
+            leaf attr-int {
+                type int32;
+            }
+        }
+    }
+
+    augment "/test:top/test:top-level-list/test:nested-list/"  {
+        ext:augment-identifier nested-list-simple-augment;
+
+        leaf type {
+            type string;
+        }
+    }
+
+    rpc knock-knock {
+        input {
+            leaf knocker-id {
+                ext:context-reference routed:test-context;
+                type instance-identifier;
+            }
+
+            leaf question {
+                type string;
+            }
+        }
+
+        output {
+            leaf answer {
+                type string;
+            }
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-sal-test-store.yang b/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-sal-test-store.yang
new file mode 100644 (file)
index 0000000..1d3f6df
--- /dev/null
@@ -0,0 +1,49 @@
+module opendaylight-sal-test-store {
+       yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:store";
+    prefix "binding-impl";
+
+    description
+        "Regression Test model for data store";
+
+    revision "2014-04-22" {
+        description
+            "Initial revision";
+    }
+
+    grouping name-value {
+        leaf name {
+            type string;
+        }
+        leaf value {
+            type string;
+        }
+    }
+
+    container lists {
+        config false;
+        container unordered-container {
+            list unordered-list {
+                key "name";
+                uses name-value;
+            }
+        }
+        container ordered-container {
+            list ordered-list {
+                ordered-by user;
+                key "name";
+                uses name-value;
+            }
+        }
+        container unkeyed-container {
+            /*
+                Following list nodes not specify key, which
+                makes impossible to uniquely identify list items
+                over time.
+            */
+            list unkeyed-list {
+                uses name-value;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-test-notification.yang b/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-test-notification.yang
new file mode 100644 (file)
index 0000000..31ec7ae
--- /dev/null
@@ -0,0 +1,25 @@
+module opendaylight-test-notification {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:bi:ba:notification";
+    prefix "ntf";
+
+    description
+        "Test model for testing of registering notification listener and publishing of notification.";
+
+    revision "2015-02-05" {
+        description
+            "Initial revision";
+    }
+
+    notification out-of-pixie-dust-notification {
+        description "Just a testing notification that we can not fly for now.";
+
+        leaf reason {
+            type string;
+        }
+
+        leaf days-till-new-dust {
+            type uint16;
+        }
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-test-routed-rpc.yang b/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-test-routed-rpc.yang
new file mode 100644 (file)
index 0000000..c3a9824
--- /dev/null
@@ -0,0 +1,46 @@
+module opendaylight-test-routed-rpc {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:rpc:routing";
+    prefix "rpc";
+    import yang-ext { prefix ext; }
+
+    description
+        "Test model for testing of registering rpc service on binding independent mount point 
+        and retrieving rpc service via binding aware mount point.";
+
+    revision "2014-07-01" {
+        description
+            "Initial revision";
+    }
+
+    identity test-context {
+        description "Test Context";
+    }
+    
+    typedef encapsulated-route {
+        type instance-identifier;
+    }
+    
+    grouping route-in-grouping {
+        leaf route {
+            type instance-identifier;
+            ext:context-reference test-context;
+        }
+    }
+    
+    grouping encapsulated-route-in-grouping {
+        leaf route {
+            type encapsulated-route;
+            ext:context-reference test-context;
+        }
+    }
+
+    rpc routed-simple-route {
+        input {
+            leaf route {
+                type instance-identifier;
+                ext:context-reference test-context;
+            }
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-test-rpc-service.yang b/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-test-rpc-service.yang
new file mode 100644 (file)
index 0000000..3412eb5
--- /dev/null
@@ -0,0 +1,22 @@
+module opendaylight-test-rpc-service {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:bi:ba:rpcservice";
+    prefix "rpc";
+
+    description
+        "Test model for testing of registering rpc service on binding independent mount point 
+        and retrieving rpc service via binding aware mount point.";
+
+    revision "2014-07-01" {
+        description
+            "Initial revision";
+    }
+
+    rpc rock-the-house {
+        input {
+            leaf zip-code {
+                type string;
+            }
+        }
+    }
+}
\ No newline at end of file