Merge "Update config-module-archetype."
authorTony Tkacik <ttkacik@cisco.com>
Tue, 8 Apr 2014 11:58:16 +0000 (11:58 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Tue, 8 Apr 2014 11:58:16 +0000 (11:58 +0000)
294 files changed:
opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/internal/Activator.java
opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/internal/ArpHandler.java
opendaylight/commons/opendaylight/pom.xml
opendaylight/commons/protocol-framework/pom.xml
opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/controller/config/yang/protocol/framework/NeverReconnectStrategyFactoryModule.java [new file with mode: 0644]
opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/controller/config/yang/protocol/framework/NeverReconnectStrategyFactoryModuleFactory.java [new file with mode: 0644]
opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/controller/config/yang/protocol/framework/ReconnectImmediatelyStrategyFactoryModule.java [new file with mode: 0644]
opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/controller/config/yang/protocol/framework/ReconnectImmediatelyStrategyFactoryModuleFactory.java [new file with mode: 0644]
opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/controller/config/yang/protocol/framework/TimedReconnectStrategyFactoryModule.java [new file with mode: 0644]
opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/controller/config/yang/protocol/framework/TimedReconnectStrategyFactoryModuleFactory.java [new file with mode: 0644]
opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/AbstractDispatcher.java
opendaylight/commons/protocol-framework/src/main/yang/odl-protocol-framework-cfg.yang [new file with mode: 0644]
opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/controller/config/yang/protocol/framework/GlobalEventExecutorUtil.java [new file with mode: 0644]
opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/controller/config/yang/protocol/framework/NeverReconnectStrategyModuleTest.java [new file with mode: 0644]
opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/controller/config/yang/protocol/framework/ReconnectImmediatelyStrategyModuleTest.java [new file with mode: 0644]
opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/controller/config/yang/protocol/framework/TimedReconnectStrategyModuleTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigRegistryImpl.java
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerImpl.java
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/ConfigManagerActivator.java
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/mapping/ModuleInfoBundleTracker.java
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/test/SimpleConfigurationTest.java
opendaylight/config/config-persister-directory-adapter/pom.xml [deleted file]
opendaylight/config/config-persister-directory-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryPersister.java [deleted file]
opendaylight/config/config-persister-directory-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryStorageAdapter.java [deleted file]
opendaylight/config/config-persister-directory-adapter/src/main/resources/footer.txt [deleted file]
opendaylight/config/config-persister-directory-adapter/src/main/resources/header.txt [deleted file]
opendaylight/config/config-persister-directory-adapter/src/main/resources/middle.txt [deleted file]
opendaylight/config/config-persister-directory-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryStorageAdapterTest.java [deleted file]
opendaylight/config/config-persister-directory-adapter/src/test/resources/oneFile/controller.config.txt [deleted file]
opendaylight/config/config-persister-directory-adapter/src/test/resources/oneFileExpected/expectedCapabilities.txt [deleted file]
opendaylight/config/config-persister-directory-adapter/src/test/resources/oneFileExpected/expectedSnapshot.xml [deleted file]
opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFiles/controller.config1.txt [deleted file]
opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFiles/controller.config2.txt [deleted file]
opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected1/expectedCapabilities.txt [deleted file]
opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected1/expectedSnapshot.xml [deleted file]
opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected2/expectedCapabilities.txt [deleted file]
opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected2/expectedSnapshot.xml [deleted file]
opendaylight/config/config-persister-directory-autodetect-adapter/pom.xml [deleted file]
opendaylight/config/config-persister-directory-autodetect-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/autodetect/AutodetectDirectoryPersister.java [deleted file]
opendaylight/config/config-persister-directory-autodetect-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/autodetect/AutodetectDirectoryStorageAdapter.java [deleted file]
opendaylight/config/config-persister-directory-autodetect-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/autodetect/FileType.java [deleted file]
opendaylight/config/config-persister-directory-autodetect-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/directory/autodetect/AutodetectDirectoryPersisterTest.java [deleted file]
opendaylight/config/config-persister-directory-autodetect-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/directory/autodetect/FileTypeTest.java [deleted file]
opendaylight/config/config-persister-directory-autodetect-adapter/src/test/resources/bad_controller.xml.config [deleted file]
opendaylight/config/config-persister-directory-autodetect-adapter/src/test/resources/combined/1controller.txt.config [deleted file]
opendaylight/config/config-persister-directory-autodetect-adapter/src/test/resources/combined/2controller.xml.config [deleted file]
opendaylight/config/config-persister-directory-autodetect-adapter/src/test/resources/test.txt.config [deleted file]
opendaylight/config/config-persister-directory-autodetect-adapter/src/test/resources/test.xml.config [deleted file]
opendaylight/config/config-persister-directory-autodetect-adapter/src/test/resources/unknown.config [deleted file]
opendaylight/config/config-persister-directory-xml-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/xml/XmlDirectoryPersister.java
opendaylight/config/config-persister-directory-xml-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/xml/XmlDirectoryStorageAdapter.java
opendaylight/config/config-persister-directory-xml-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/directory/xml/DirectoryStorageAdapterTest.java
opendaylight/config/config-persister-directory-xml-adapter/src/test/resources/twoFiles/controller.config2.xml2 [moved from opendaylight/config/config-persister-directory-xml-adapter/src/test/resources/twoFiles/controller.config2.xml with 100% similarity]
opendaylight/config/config-persister-directory-xml-adapter/src/test/resources/twoFiles_corrupt/controller.config1.xml [new file with mode: 0644]
opendaylight/config/config-persister-directory-xml-adapter/src/test/resources/twoFiles_corrupt/controller.config2.xml [new file with mode: 0644]
opendaylight/config/config-persister-file-adapter/pom.xml [deleted file]
opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java [deleted file]
opendaylight/config/config-persister-file-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapterTest.java [deleted file]
opendaylight/config/pom.xml
opendaylight/distribution/opendaylight/pom.xml
opendaylight/distribution/opendaylight/src/assemble/bin.xml
opendaylight/distribution/opendaylight/src/main/resources/configuration/RSA.pk [deleted file]
opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini
opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.xml
opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowConfig.java
opendaylight/hosttracker/implementation/src/main/java/org/opendaylight/controller/hosttracker/internal/HostTracker.java
opendaylight/md-sal/clustered-data-store/integrationtest/pom.xml
opendaylight/md-sal/compatibility/flow-management-compatibility/src/main/java/org/opendaylight/controller/md/frm/compatibility/FRMRuntimeDataProvider.xtend
opendaylight/md-sal/compatibility/flow-management-compatibility/src/main/java/org/opendaylight/controller/md/frm/compatibility/SampleConsumer.java
opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/switchmanager/CompatibleSwitchManager.xtend
opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyReader.xtend
opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/AdSalTopologyMapping.xtend
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/ComponentActivator.xtend
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryAndReadAdapter.xtend
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryMapping.java
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/MDFlowMapping.xtend
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/NodeMapping.java
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/topology/TopologyProvider.xtend
opendaylight/md-sal/inventory-manager/pom.xml
opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/FlowCapableInventoryProvider.xtend
opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/InventoryMapping.xtend
opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/NodeChangeCommiter.java [new file with mode: 0644]
opendaylight/md-sal/model/model-flow-service/src/main/yang/sal-port.yang
opendaylight/md-sal/model/model-flow-statistics/src/main/yang/opendaylight-statistics-types.yang
opendaylight/md-sal/sal-binding-api/pom.xml
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataBroker.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataChangeListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataReadTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataReadWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/BindingAwareBroker.java
opendaylight/md-sal/sal-binding-broker/pom.xml
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/ForwardedCompatibleDataBrokerImplModule.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/ForwardedCompatibleDataBrokerImplModuleFactory.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/BindingToNormalizedNodeCodec.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBackwardsCompatibleDataBroker.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBindingDataBroker.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/LegacyDataChangeEvent.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RpcProviderRegistryImpl.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingIndependentConnector.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/forward/DomForwardedBroker.java
opendaylight/md-sal/sal-binding-broker/src/main/yang/opendaylight-binding-broker-impl.yang
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/UnionSerializationTest.java
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java
opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/DOMCodecBug01Test.java
opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/DOMCodecBug02Test.java
opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/DOMCodecBug03Test.java
opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/PutAugmentationTest.java
opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/ChangeOriginatedInDomBrokerTest.java
opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java
opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/AbstractTest.java
opendaylight/md-sal/sal-common-api/pom.xml
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/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/DataChangeListener.java
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataCommitHandler.java
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/TransactionChain.java
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChainListener.java
opendaylight/md-sal/sal-common-impl/pom.xml
opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/AbstractDataModification.java
opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/AbstractRegistration.java [deleted file]
opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/AbstractRoutedRegistration.java
opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/service/ImmutableDataChangeEvent.java
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-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/DOMDataChangeListener.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/DOMDataWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/Broker.java
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountInstance.java
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountProvisionService.java
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountService.java
opendaylight/md-sal/sal-dom-api/src/main/yang/opendaylight-md-sal-dom.yang
opendaylight/md-sal/sal-dom-broker/pom.xml
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomBrokerImplModule.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModuleFactory.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/BackwardsCompatibleDataBroker.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/BackwardsCompatibleTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChangeListenerNotifyTask.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMImmutableDataChangeEvent.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DataAndMetadataSnapshot.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DataChangeEventResolver.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DataChangeListenerRegistration.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ModificationApplyOperation.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/MutableDataTree.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/OperationWithModification.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SchemaAwareApplyOperation.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SchemaAwareApplyOperationRoot.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/StoreUtils.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/ListenerRegistrationNode.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/ModificationType.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/NodeModification.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/StoreMetadataNode.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/StoreNodeCompositeBuilder.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/StoreTreeNode.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/TreeNodeUtils.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerConfigActivator.xtend
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/GlobalBundleScanningSchemaServiceImpl.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/MountPointImpl.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/NotificationRouterImpl.java
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/ProxyFactory.xtend
opendaylight/md-sal/sal-dom-broker/src/main/yang/opendaylight-dom-broker-impl.yang
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/DataNormalizerTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/ModificationMetadataTreeTest.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/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/DOMStoreWriteTransaction.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-netconf-connector/pom.xml
opendaylight/md-sal/sal-remoterpc-connector/implementation/pom.xml
opendaylight/md-sal/sal-remoterpc-connector/integrationtest/test-it/pom.xml
opendaylight/md-sal/sal-rest-connector/pom.xml
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java
opendaylight/md-sal/sal-rest-connector/src/test/resources/full-versions/test-module/test-module
opendaylight/md-sal/samples/l2switch/implementation/pom.xml [new file with mode: 0644]
opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/L2SwitchProvider.java [new file with mode: 0644]
opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/addresstracker/AddressTracker.java [new file with mode: 0644]
opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/flow/FlowWriterService.java [new file with mode: 0644]
opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/flow/FlowWriterServiceImpl.java [new file with mode: 0644]
opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/inventory/InventoryService.java [new file with mode: 0644]
opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/packet/PacketHandler.java [new file with mode: 0644]
opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/topology/NetworkGraphDijkstra.java [new file with mode: 0644]
opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/topology/NetworkGraphService.java [new file with mode: 0644]
opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/topology/TopologyLinkDataChangeHandler.java [new file with mode: 0644]
opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/util/InstanceIdentifierUtils.java [new file with mode: 0644]
opendaylight/md-sal/samples/l2switch/implementation/src/test/java/org/opendaylight/controller/sample/l2switch/md/flow/FlowWriterServiceImplTest.java [new file with mode: 0644]
opendaylight/md-sal/samples/l2switch/implementation/src/test/java/org/opendaylight/controller/sample/l2switch/md/topology/NetworkGraphDijkstraTest.java [new file with mode: 0644]
opendaylight/md-sal/samples/l2switch/implementation/src/test/java/org/opendaylight/controller/sample/l2switch/md/topology/TopologyLinkDataChangeHandlerTest.java [new file with mode: 0644]
opendaylight/md-sal/samples/l2switch/model/pom.xml [new file with mode: 0644]
opendaylight/md-sal/samples/l2switch/model/src/main/yang/l2-address-tracker.yang [new file with mode: 0644]
opendaylight/md-sal/samples/l2switch/pom.xml [new file with mode: 0644]
opendaylight/md-sal/samples/pom.xml
opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/config/yang/config/toaster_consumer/impl/ToasterConsumerModule.java
opendaylight/md-sal/samples/toaster-it/src/test/java/org/opendaylight/controller/sample/toaster/it/ToasterTest.java
opendaylight/md-sal/samples/toaster-provider/src/main/java/org/opendaylight/controller/config/yang/config/toaster_provider/impl/ToasterProviderModule.java
opendaylight/md-sal/samples/toaster-provider/src/main/java/org/opendaylight/controller/sample/toaster/provider/OpendaylightToaster.java
opendaylight/md-sal/samples/toaster-provider/src/main/yang/toaster-provider-impl.yang
opendaylight/md-sal/statistics-manager/pom.xml
opendaylight/md-sal/test/sal-rest-connector-it/pom.xml
opendaylight/md-sal/test/sal-rest-connector-it/src/test/java/org/opendaylight/controller/test/restconf/it/ServiceProviderController.java
opendaylight/md-sal/topology-manager/pom.xml
opendaylight/md-sal/topology-manager/src/main/java/org/opendaylight/md/controller/topology/manager/FlowCapableTopologyProvider.xtend
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/Activator.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceFactoryImpl.java
opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java
opendaylight/netconf/config-persister-impl/pom.xml
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolder.java
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusher.java
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherConfiguration.java [deleted file]
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherConfigurationBuilder.java [deleted file]
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterAggregator.java
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/Util.java [deleted file]
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java
opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolderTest.java
opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterTest.java
opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/MockNetconfEndpoint.java [deleted file]
opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/MockedBundleContext.java
opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/TestingExceptionHandler.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceSnapshot.java
opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java
opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfConfigPersisterITTest.java
opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java
opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/pax/IdentityRefNetconfTest.java
opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/HandlingPriority.java
opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationChainedExecution.java
opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceFactory.java
opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringActivator.java
opendaylight/netconf/netconf-ssh/pom.xml
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/AuthProvider.java
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/PEMGenerator.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/osgi/NetconfSSHActivator.java [moved from opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/osgi/NetconfSSHActivator.java with 80% similarity]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/SocketThread.java
opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/KeyGeneratorTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/SSHServerTest.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/NetconfUtil.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/mapping/AbstractNetconfOperation.java
opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/NetconfUtilTest.java
opendaylight/netconf/pom.xml
opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronSubnet.java
opendaylight/northbound/networkconfiguration/bridgedomain/src/main/java/org/opendaylight/controller/networkconfig/bridgedomain/northbound/BridgeDomainNorthbound.java
opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronSubnetsNorthbound.java
opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/core/internal/SecureMessageReadWriteService.java
opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/DiscoveryService.java
opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/FlowConverter.java
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlDst.java [new file with mode: 0644]
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlSrc.java [new file with mode: 0644]
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlType.java [new file with mode: 0644]
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlVlan.java [new file with mode: 0644]
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlVlanPriority.java [new file with mode: 0644]
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/InPort.java [new file with mode: 0644]
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/Match.java [new file with mode: 0644]
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/MatchField.java [new file with mode: 0644]
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwDst.java [new file with mode: 0644]
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwProtocol.java [new file with mode: 0644]
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwSrc.java [new file with mode: 0644]
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwTos.java [new file with mode: 0644]
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/TpDst.java [new file with mode: 0644]
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/TpSrc.java [new file with mode: 0644]
opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/match/MatchExtensibleTest.java [new file with mode: 0644]
opendaylight/sal/networkconfiguration/api/src/main/java/org/opendaylight/controller/sal/networkconfig/bridgedomain/BridgeDomainConfigServiceException.java [new file with mode: 0644]
opendaylight/sal/networkconfiguration/api/src/main/java/org/opendaylight/controller/sal/networkconfig/bridgedomain/IPluginInBridgeDomainConfigService.java
opendaylight/sal/networkconfiguration/implementation/src/main/java/org/opendaylight/controller/sal/networkconfig/bridgedomain/internal/BridgeDomainConfigService.java
opendaylight/samples/simpleforwarding/src/main/java/org/opendaylight/controller/samples/simpleforwarding/internal/Activator.java
opendaylight/samples/simpleforwarding/src/main/java/org/opendaylight/controller/samples/simpleforwarding/internal/SimpleBroadcastHandlerImpl.java
opendaylight/samples/simpleforwarding/src/main/java/org/opendaylight/controller/samples/simpleforwarding/internal/SimpleForwardingImpl.java
opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/Devices.java
opendaylight/web/flows/src/main/java/org/opendaylight/controller/flows/web/Flows.java
opendaylight/web/flows/src/main/resources/js/page.js

index b253179c87185d50834240f654a524409a0fa392..b7639bed2b2b41db2c5459cd0b8babe976b92bfb 100644 (file)
@@ -24,7 +24,6 @@ import org.opendaylight.controller.hosttracker.hostAware.IHostFinder;
 import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
 import org.opendaylight.controller.sal.packet.IDataPacketService;
 import org.opendaylight.controller.sal.packet.IListenDataPacket;
-import org.opendaylight.controller.sal.routing.IRouting;
 import org.opendaylight.controller.switchmanager.ISwitchManager;
 import org.opendaylight.controller.topologymanager.ITopologyManager;
 import org.slf4j.Logger;
@@ -44,6 +43,7 @@ public class Activator extends ComponentActivatorAbstractBase {
      * instantiated in order to get an fully working implementation
      * Object
      */
+    @Override
     public Object[] getImplementations() {
         Object[] res = { ArpHandler.class };
         return res;
@@ -62,6 +62,7 @@ public class Activator extends ComponentActivatorAbstractBase {
      * also optional per-container different behavior if needed, usually
      * should not be the case though.
      */
+    @Override
     public void configureInstance(Component c, Object imp, String containerName) {
         if (imp.equals(ArpHandler.class)) {
             // export the service
@@ -100,10 +101,6 @@ public class Activator extends ComponentActivatorAbstractBase {
                    "setClusterContainerService", "unsetClusterContainerService")
                    .setRequired(true));
 
-            c.add(createContainerServiceDependency(containerName).setService(
-                   IRouting.class).setCallbacks("setRouting","unsetRouting")
-                   .setRequired(false));
-
             // the Host Listener is optional
             c.add(createContainerServiceDependency(containerName).setService(
                     IfHostListener.class).setCallbacks("setHostListener",
index fa883829cccf6c9f69044f3346e364ea4c2f9c5d..e5491824397c1678b26b819b4e2691d0295f95a6 100644 (file)
@@ -64,6 +64,31 @@ import org.opendaylight.controller.topologymanager.ITopologyManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * The ArpHandler offers services to react on ARP requests and replies
+ * sent by network hosts. Moreover it allows for creating ARP messages
+ * by the controller itself.
+ *
+ * The ARP Handler on ODL doesn't use the requester MAC address in
+ * order to avoid to have to build a spanning tree where to forward
+ * ARP Requests. The ARP requests are broadcast packets so in order to
+ * reach everywhere need to be flooded, when you flood in a network
+ * that is not a tree (all the networks has some level of redundancy)
+ * that would create forwarding loops without a spanning tree. Given
+ * the need is only to send out the ARP requests toward all the hosts
+ * we actually don't need to implement a flooding mechanism in software
+ * (which would be expensive) we just send out the ARP request toward
+ * all the ports that are suspected to be host ports on all the
+ * switches (from the controller). Now the condition for which a port
+ * is marked as host port could potentially be incorrect so when the
+ * controller sends out the ARP Request that could come back to the
+ * controller and could cause another request not needed. So changing
+ * the source MAC address of the request to be the one of the controller,
+ * controller can protect itself from honoring twice the same request.
+ * This enables an ARP handler resolution, without the need of spanning
+ * tree and limiting software flooding to the minimum required.
+ */
+
 public class ArpHandler implements IHostFinder, IListenDataPacket, ICacheUpdateAware<ARPEvent, Boolean> {
     private static final Logger log = LoggerFactory.getLogger(ArpHandler.class);
     static final String ARP_EVENT_CACHE_NAME = "arphandler.arpRequestReplyEvent";
@@ -113,16 +138,6 @@ public class ArpHandler implements IHostFinder, IListenDataPacket, ICacheUpdateA
         }
     }
 
-    void setRouting(IRouting r) {
-        this.routing = r;
-    }
-
-    void unsetRouting(IRouting r) {
-        if (this.routing == r) {
-            this.routing = null;
-        }
-    }
-
     void setHostListener(IfHostListener s) {
         if (this.hostListeners != null) {
             this.hostListeners.add(s);
@@ -507,41 +522,18 @@ public class ArpHandler implements IHostFinder, IListenDataPacket, ICacheUpdateA
 
         // see if we know about the host
         // Hosttracker hosts db key implementation
-        IHostId id = HostIdFactory.create(dIP, null);
-        HostNodeConnector host = hostTracker.hostFind(id);
+        HostNodeConnector host = hostTracker.hostFind(dIP);
 
         if (host == null) {
-            // if we don't, know about the host, try to find it
+            // if we don't know about the host, try to find it
             log.trace("Punted IP pkt to {}, sending bcast ARP event...", dIP);
             /*
              * unknown destination host, initiate bcast ARP request
              */
             arpRequestReplyEvent.put(new ARPRequest(dIP, subnet), false);
 
-        } else if (routing == null || routing.getRoute(p.getNode(), host.getnodeconnectorNode()) != null) {
-            /*
-             * if IRouting is available, make sure that this packet can get it's
-             * destination normally before teleporting it there. If it's not
-             * available, then assume it's reachable.
-             *
-             * TODO: come up with a way to do this in the absence of IRouting
-             */
-
-            log.trace("forwarding punted IP pkt to {} received at {}", dIP, p);
-
-            /*
-             * if we know where the host is and there's a path from where this
-             * packet was punted to where the host is, then deliver it to the
-             * host for now
-             */
-            NodeConnector nc = host.getnodeConnector();
-
-            // re-encode the Ethernet packet (the parent of the IPv4 packet)
-            RawPacket rp = this.dataPacketService.encodeDataPacket(pkt.getParent());
-            rp.setOutgoingNodeConnector(nc);
-            this.dataPacketService.transmitDataPacket(rp);
         } else {
-            log.trace("ignoring punted IP pkt to {} because there is no route from {}", dIP, p);
+            log.trace("Ignoring punted IP pkt to known host: {} (received on: {})", dIP, p);
         }
     }
 
index 9f51fbac87c8f175bac78c75f9c0b247cc30bc9c..3690b8542bc0d5b867fdc0a5bcc42a62fa99bf19 100644 (file)
@@ -91,7 +91,7 @@
     <commons.httpclient.version>0.1.2-SNAPSHOT</commons.httpclient.version>
     <concepts.version>0.5.2-SNAPSHOT</concepts.version>
     <protocol-framework.version>0.5.0-SNAPSHOT</protocol-framework.version>
-    <netty.version>4.0.10.Final</netty.version>
+    <netty.version>4.0.17.Final</netty.version>
     <commons.io.version>2.4</commons.io.version>
     <bundlescanner.version>0.4.2-SNAPSHOT</bundlescanner.version>
     <usermanager.version>0.4.2-SNAPSHOT</usermanager.version>
           <artifactId>concepts</artifactId>
           <version>${concepts.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal-remoterpc-connector</artifactId>
+            <version>${mdsal.version}</version>
+        </dependency>
 
 
         <!-- config-->
           <artifactId>config-persister-api</artifactId>
           <version>${config.version}</version>
         </dependency>
-        <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>config-persister-file-adapter</artifactId>
-          <version>${config.version}</version>
-        </dependency>
         <dependency>
           <groupId>org.opendaylight.controller</groupId>
           <artifactId>config-persister-file-xml-adapter</artifactId>
           <version>${config.version}</version>
         </dependency>
-        <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>config-persister-directory-adapter</artifactId>
-          <version>${config.version}</version>
-        </dependency>
         <dependency>
           <groupId>org.opendaylight.controller</groupId>
           <artifactId>config-persister-directory-xml-adapter</artifactId>
           <version>${config.version}</version>
         </dependency>
-        <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>config-persister-directory-autodetect-adapter</artifactId>
-          <version>${config.version}</version>
-        </dependency>
 
         <dependency>
           <groupId>org.opendaylight.controller</groupId>
             <artifactId>configuration</artifactId>
             <version>${configuration.version}</version>
           </dependency>
-
+            <dependency>
+                <groupId>org.bouncycastle</groupId>
+                <artifactId>bcprov-jdk15on</artifactId>
+                <version>1.50</version>
+            </dependency>
+            <dependency>
+                <groupId>org.bouncycastle</groupId>
+                <artifactId>bcpkix-jdk15on</artifactId>
+                <version>1.50</version>
+            </dependency>
     </dependencies>
   </dependencyManagement>
 
index a03518235b6521ba1c297a7b04ab8db87e0ed872..650d2dd35bb22586bcd064bc583402a90787b457 100644 (file)
         <maven>3.0.4</maven>
     </prerequisites>
 
+    <properties>
+        <jmxGeneratorPath>${project.build.directory}/generated-sources/config</jmxGeneratorPath>
+        <salGeneratorPath>${project.build.directory}/generated-sources/sal</salGeneratorPath>
+    </properties>
+
     <dependencies>
         <dependency>
             <groupId>io.netty</groupId>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>config-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>netty-config-api</artifactId>
+        </dependency>
 
         <!-- Testing dependencies -->
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>config-manager</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>config-manager</artifactId>
+            <version>${config.version}</version>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>config-util</artifactId>
+            <version>${config.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>netty-event-executor-config</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
                     </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>
+                          </codeGenerators>
+                          <inspectDependencies>true</inspectDependencies>
+                      </configuration>
+                  </execution>
+              </executions>
+              <dependencies>
+                  <dependency>
+                      <groupId>org.opendaylight.controller</groupId>
+                      <artifactId>yang-jmx-generator-plugin</artifactId>
+                      <version>${config.version}</version>
+                  </dependency>
+                  <dependency>
+                      <groupId>org.opendaylight.yangtools</groupId>
+                      <artifactId>maven-sal-api-gen-plugin</artifactId>
+                      <version>${yangtools.version}</version>
+                  </dependency>
+              </dependencies>
+          </plugin>
+          <plugin>
+              <groupId>org.codehaus.mojo</groupId>
+              <artifactId>build-helper-maven-plugin</artifactId>
+              <executions>
+                  <execution>
+                      <id>add-source</id>
+                      <phase>generate-sources</phase>
+                      <goals>
+                          <goal>add-source</goal>
+                      </goals>
+                      <configuration>
+                          <sources>
+                              <source>${jmxGeneratorPath}</source>
+                              <source>${salGeneratorPath}</source>
+                          </sources>
+                      </configuration>
+                  </execution>
+              </executions>
+          </plugin>
         </plugins>
     </build>
 </project>
diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/controller/config/yang/protocol/framework/NeverReconnectStrategyFactoryModule.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/controller/config/yang/protocol/framework/NeverReconnectStrategyFactoryModule.java
new file mode 100644 (file)
index 0000000..a97f65e
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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.config.yang.protocol.framework;
+
+import io.netty.util.concurrent.EventExecutor;
+
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.protocol.framework.NeverReconnectStrategy;
+import org.opendaylight.protocol.framework.ReconnectStrategy;
+import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
+
+/**
+*
+*/
+public final class NeverReconnectStrategyFactoryModule extends org.opendaylight.controller.config.yang.protocol.framework.AbstractNeverReconnectStrategyFactoryModule
+ {
+
+    public NeverReconnectStrategyFactoryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public NeverReconnectStrategyFactoryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+            NeverReconnectStrategyFactoryModule oldModule, java.lang.AutoCloseable oldInstance) {
+
+        super(identifier, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    protected void customValidation(){
+        JmxAttributeValidationException.checkNotNull(getTimeout(), "value is not set.", timeoutJmxAttribute);
+        JmxAttributeValidationException.checkCondition(getTimeout() >= 0, "value " + getTimeout() + " is less than 0",
+                timeoutJmxAttribute);
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        return new NeverReconnectStrategyFactoryCloseable(getExecutorDependency(), getTimeout());
+    }
+
+    private static final class NeverReconnectStrategyFactoryCloseable implements ReconnectStrategyFactory, AutoCloseable {
+
+        private final EventExecutor executor;
+        private final int timeout;
+
+        public NeverReconnectStrategyFactoryCloseable(final EventExecutor executor, final int timeout) {
+            this.executor = executor;
+            this.timeout = timeout;
+        }
+
+        @Override
+        public void close() throws Exception {
+            // no-op
+        }
+
+        @Override
+        public ReconnectStrategy createReconnectStrategy() {
+            return new NeverReconnectStrategy(this.executor, this.timeout);
+        }
+
+    }
+}
diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/controller/config/yang/protocol/framework/NeverReconnectStrategyFactoryModuleFactory.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/controller/config/yang/protocol/framework/NeverReconnectStrategyFactoryModuleFactory.java
new file mode 100644 (file)
index 0000000..bbdc939
--- /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.config.yang.protocol.framework;
+
+/**
+*
+*/
+public class NeverReconnectStrategyFactoryModuleFactory extends org.opendaylight.controller.config.yang.protocol.framework.AbstractNeverReconnectStrategyFactoryModuleFactory
+{
+
+
+}
diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/controller/config/yang/protocol/framework/ReconnectImmediatelyStrategyFactoryModule.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/controller/config/yang/protocol/framework/ReconnectImmediatelyStrategyFactoryModule.java
new file mode 100644 (file)
index 0000000..580a53b
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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.config.yang.protocol.framework;
+
+import io.netty.util.concurrent.EventExecutor;
+
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.protocol.framework.ReconnectImmediatelyStrategy;
+import org.opendaylight.protocol.framework.ReconnectStrategy;
+import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
+
+/**
+*
+*/
+public final class ReconnectImmediatelyStrategyFactoryModule extends org.opendaylight.controller.config.yang.protocol.framework.AbstractReconnectImmediatelyStrategyFactoryModule
+ {
+
+    public ReconnectImmediatelyStrategyFactoryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public ReconnectImmediatelyStrategyFactoryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+            ReconnectImmediatelyStrategyFactoryModule oldModule, java.lang.AutoCloseable oldInstance) {
+
+        super(identifier, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    protected void customValidation(){
+        JmxAttributeValidationException.checkNotNull(getTimeout(), "value is not set.", timeoutJmxAttribute);
+        JmxAttributeValidationException.checkCondition(getTimeout() >= 0, "value " + getTimeout() + " is less than 0",
+                timeoutJmxAttribute);
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        return new ReconnectImmediatelyStrategyFactoryCloseable(getExecutorDependency(), getTimeout());
+    }
+
+    private static final class ReconnectImmediatelyStrategyFactoryCloseable implements ReconnectStrategyFactory, AutoCloseable {
+
+        private final EventExecutor executor;
+        private final int timeout;
+
+        public ReconnectImmediatelyStrategyFactoryCloseable(final EventExecutor executor, final int timeout) {
+            this.executor = executor;
+            this.timeout = timeout;
+        }
+
+        @Override
+        public void close() throws Exception {
+            // no-op
+        }
+
+        @Override
+        public ReconnectStrategy createReconnectStrategy() {
+            return new ReconnectImmediatelyStrategy(this.executor, this.timeout);
+        }
+
+    }
+}
diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/controller/config/yang/protocol/framework/ReconnectImmediatelyStrategyFactoryModuleFactory.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/controller/config/yang/protocol/framework/ReconnectImmediatelyStrategyFactoryModuleFactory.java
new file mode 100644 (file)
index 0000000..6e72458
--- /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.config.yang.protocol.framework;
+
+/**
+*
+*/
+public class ReconnectImmediatelyStrategyFactoryModuleFactory extends org.opendaylight.controller.config.yang.protocol.framework.AbstractReconnectImmediatelyStrategyFactoryModuleFactory
+{
+
+
+}
diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/controller/config/yang/protocol/framework/TimedReconnectStrategyFactoryModule.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/controller/config/yang/protocol/framework/TimedReconnectStrategyFactoryModule.java
new file mode 100644 (file)
index 0000000..b8849c7
--- /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.config.yang.protocol.framework;
+
+import io.netty.util.concurrent.EventExecutor;
+
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.protocol.framework.ReconnectStrategy;
+import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
+import org.opendaylight.protocol.framework.TimedReconnectStrategy;
+
+import com.google.common.base.Preconditions;
+
+/**
+*
+*/
+public final class TimedReconnectStrategyFactoryModule extends org.opendaylight.controller.config.yang.protocol.framework.AbstractTimedReconnectStrategyFactoryModule
+ {
+
+    public TimedReconnectStrategyFactoryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public TimedReconnectStrategyFactoryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+            TimedReconnectStrategyFactoryModule oldModule, java.lang.AutoCloseable oldInstance) {
+
+        super(identifier, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    protected void customValidation(){
+        JmxAttributeValidationException.checkNotNull(getSleepFactor(), "value is not set.", sleepFactorJmxAttribute);
+        JmxAttributeValidationException.checkCondition(getSleepFactor().doubleValue() >= 1, "value " + getSleepFactor()
+                + " is less than 1", sleepFactorJmxAttribute);
+
+        JmxAttributeValidationException.checkNotNull(getConnectTime(), "value is not set.", connectTimeJmxAttribute);
+        JmxAttributeValidationException.checkCondition(getConnectTime() >= 0, "value " + getConnectTime()
+                + " is less than 0", connectTimeJmxAttribute);
+
+        JmxAttributeValidationException.checkNotNull(getMinSleep(), "value is not set.", minSleepJmxAttribute);
+        JmxAttributeValidationException.checkCondition(getMaxSleep() == null || getMinSleep() <= getMaxSleep(),
+                "value " + getMinSleep() + " is greter than MaxSleep " + getMaxSleep(), minSleepJmxAttribute);
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        return new TimedReconnectStrategyFactoryCloseable(getExecutorDependency(),
+                getConnectTime(), getMinSleep(), getSleepFactor().doubleValue(), getMaxSleep(), getMaxAttempts(),
+                getDeadline());
+    }
+
+    private static final class TimedReconnectStrategyFactoryCloseable implements ReconnectStrategyFactory, AutoCloseable {
+
+        private final EventExecutor executor;
+        private final Long deadline, maxAttempts, maxSleep;
+        private final double sleepFactor;
+        private final int connectTime;
+        private final long minSleep;
+
+        public TimedReconnectStrategyFactoryCloseable(final EventExecutor executor, final int connectTime, final long minSleep, final double sleepFactor,
+                final Long maxSleep, final Long maxAttempts, final Long deadline) {
+            Preconditions.checkArgument(maxSleep == null || minSleep <= maxSleep);
+            Preconditions.checkArgument(sleepFactor >= 1);
+            Preconditions.checkArgument(connectTime >= 0);
+            this.executor = Preconditions.checkNotNull(executor);
+            this.deadline = deadline;
+            this.maxAttempts = maxAttempts;
+            this.minSleep = minSleep;
+            this.maxSleep = maxSleep;
+            this.sleepFactor = sleepFactor;
+            this.connectTime = connectTime;
+        }
+
+        @Override
+        public void close() throws Exception {
+            // no-op
+        }
+
+        @Override
+        public ReconnectStrategy createReconnectStrategy() {
+            return new TimedReconnectStrategy(this.executor,
+                    this.connectTime, this.minSleep, this.sleepFactor, this.maxSleep, this.maxAttempts,
+                    this.deadline);
+        }
+
+    }
+}
diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/controller/config/yang/protocol/framework/TimedReconnectStrategyFactoryModuleFactory.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/controller/config/yang/protocol/framework/TimedReconnectStrategyFactoryModuleFactory.java
new file mode 100644 (file)
index 0000000..7f92e3a
--- /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.config.yang.protocol.framework;
+
+/**
+*
+*/
+public class TimedReconnectStrategyFactoryModuleFactory extends org.opendaylight.controller.config.yang.protocol.framework.AbstractTimedReconnectStrategyFactoryModuleFactory
+{
+
+
+}
index e90af73785eff5d2027b7f4fad5cd7e4a0105710..5e55cddf8e766509336041264dcecd7af06a2932 100644 (file)
@@ -89,11 +89,23 @@ public abstract class AbstractDispatcher<S extends ProtocolSession<?>, L extends
         });
         b.childOption(ChannelOption.SO_KEEPALIVE, true);
 
+        customizeBootstrap(b);
+
         // Bind and start to accept incoming connections.
         final ChannelFuture f = b.bind(address);
         LOG.debug("Initiated server {} at {}.", f, address);
         return f;
+    }
 
+    /**
+     * Customize a server bootstrap before the server is created. This allows
+     * subclasses to assign non-default server options before the server is
+     * created.
+     *
+     * @param b Server bootstrap
+     */
+    protected void customizeBootstrap(final ServerBootstrap b) {
+        // The default is a no-op
     }
 
     /**
@@ -116,11 +128,25 @@ public abstract class AbstractDispatcher<S extends ProtocolSession<?>, L extends
                         initializer.initializeChannel(ch, p);
                     }
                 });
+
+        customizeBootstrap(b);
+
         p.connect();
         LOG.debug("Client created.");
         return p;
     }
 
+    /**
+     * Customize a client bootstrap before the connection is attempted. This
+     * allows subclasses to assign non-default options before the client is
+     * created.
+     *
+     * @param b Client bootstrap
+     */
+    protected void customizeBootstrap(final Bootstrap b) {
+        // The default is a no-op
+    }
+
     /**
      * Creates a client.
      *
@@ -138,7 +164,6 @@ public abstract class AbstractDispatcher<S extends ProtocolSession<?>, L extends
         p.connect();
 
         return p;
-
     }
 
     /**
diff --git a/opendaylight/commons/protocol-framework/src/main/yang/odl-protocol-framework-cfg.yang b/opendaylight/commons/protocol-framework/src/main/yang/odl-protocol-framework-cfg.yang
new file mode 100644 (file)
index 0000000..1856369
--- /dev/null
@@ -0,0 +1,151 @@
+// vi: set smarttab et sw=4 tabstop=4:
+module protocol-framework {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:protocol:framework";
+    prefix "pf";
+
+    import config { prefix config; revision-date 2013-04-05; }
+    import netty { prefix netty; revision-date 2013-11-19; }
+
+    organization "Cisco Systems, Inc.";
+
+    contact "Milos Fabian <milfabia@cisco.com>";
+
+    description
+        "This module contains the base YANG definitions for
+         protocol framework.
+
+        Copyright (c)2013 Cisco Systems, Inc. All rights reserved.;
+
+        This program and the accompanying materials are made available
+        under the terms of the Eclipse Public License v1.0 which
+        accompanies this distribution, and is available at
+        http://www.eclipse.org/legal/epl-v10.html";
+
+    revision "2014-03-13" {
+        description
+            "Changed reconnect-strategy to reconnect-strategy-factory - represents ReconnectStrategyFactory.
+            reconnect-strategy-factory modules provides reconnect-strategy-factory service.";
+    }
+
+    revision "2013-11-09" {
+        description
+            "Initial revision";
+    }
+
+    identity reconnect-strategy-factory {
+        description
+            "Service representing a reconnect strategy factory.";
+
+        base "config:service-type";
+        config:java-class "org.opendaylight.protocol.framework.ReconnectStrategyFactory";
+    }
+
+    identity never-reconnect-strategy-factory {
+        base config:module-type;
+        config:provided-service reconnect-strategy-factory;
+        config:java-name-prefix NeverReconnectStrategyFactory;
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case never-reconnect-strategy-factory {
+            when "/config:modules/config:module/config:type = 'never-reconnect-strategy-factory'";
+
+            leaf timeout {
+                mandatory true;
+                type int32;
+            }
+
+            container executor {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity netty:netty-event-executor;
+                    }
+                }
+            }
+        }
+    }
+
+    identity reconnect-immediately-strategy-factory {
+        base config:module-type;
+        config:provided-service reconnect-strategy-factory;
+        config:java-name-prefix ReconnectImmediatelyStrategyFactory;
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case reconnect-immediately-strategy-factory {
+            when "/config:modules/config:module/config:type = 'reconnect-immediately-strategy-factory'";
+
+            leaf timeout {
+                mandatory true;
+                type int32;
+            }
+
+            container executor {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity netty:netty-event-executor;
+                    }
+                }
+            }
+        }
+    }
+
+    identity timed-reconnect-strategy-factory {
+        base config:module-type;
+        config:provided-service reconnect-strategy-factory;
+        config:java-name-prefix TimedReconnectStrategyFactory;
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case timed-reconnect-strategy-factory {
+            when "/config:modules/config:module/config:type = 'timed-reconnect-strategy-factory'";
+
+            leaf deadline {
+                type int64;
+                units "epoch nanoseconds";
+            }
+
+            leaf max-attempts {
+                mandatory true;
+                type int64;
+            }
+
+            leaf max-sleep {
+                mandatory true;
+                type int64;
+                units "milliseconds";
+            }
+
+            leaf min-sleep {
+                mandatory true;
+                type int64;
+                units "milliseconds";
+            }
+
+            leaf sleep-factor {
+                mandatory true;
+                type decimal64 {
+                    fraction-digits 2;
+                }
+            }
+
+            leaf connect-time {
+                mandatory true;
+                type int32;
+                units "milliseconds";
+            }
+
+            container executor {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity netty:netty-event-executor;
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/controller/config/yang/protocol/framework/GlobalEventExecutorUtil.java b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/controller/config/yang/protocol/framework/GlobalEventExecutorUtil.java
new file mode 100644 (file)
index 0000000..695e36c
--- /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.config.yang.protocol.framework;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.opendaylight.controller.config.yang.netty.eventexecutor.GlobalEventExecutorModuleFactory;
+
+final class GlobalEventExecutorUtil {
+
+    private GlobalEventExecutorUtil() {
+        throw new UnsupportedOperationException();
+    }
+
+    public static ObjectName create(final ConfigTransactionJMXClient transaction) throws InstanceAlreadyExistsException {
+        try {
+            return transaction.lookupConfigBean(GlobalEventExecutorModuleFactory.NAME,
+                    GlobalEventExecutorModuleFactory.SINGLETON_NAME);
+        } catch (InstanceNotFoundException e) {
+            try {
+                return transaction.createModule(GlobalEventExecutorModuleFactory.NAME,
+                        GlobalEventExecutorModuleFactory.SINGLETON_NAME);
+            } catch (InstanceAlreadyExistsException e1) {
+                throw new IllegalStateException(e1);
+            }
+        }
+    }
+
+}
diff --git a/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/controller/config/yang/protocol/framework/NeverReconnectStrategyModuleTest.java b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/controller/config/yang/protocol/framework/NeverReconnectStrategyModuleTest.java
new file mode 100644 (file)
index 0000000..d77c9aa
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * 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.config.yang.protocol.framework;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.opendaylight.controller.config.yang.netty.eventexecutor.GlobalEventExecutorModuleFactory;
+
+public class NeverReconnectStrategyModuleTest extends AbstractConfigTest {
+
+    private static final String INSTANCE_NAME = "never-reconect-strategy-factory-impl";
+    private static final String FACTORY_NAME = NeverReconnectStrategyFactoryModuleFactory.NAME;
+
+    @Before
+    public void setUp() throws Exception {
+        super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(
+                new NeverReconnectStrategyFactoryModuleFactory(), new GlobalEventExecutorModuleFactory()));
+    }
+
+    @Test
+    public void testValidationExceptionTimeoutNotSet() throws Exception {
+        try {
+            createInstance(null);
+            fail();
+        } catch (ValidationException e) {
+            assertTrue(e.getMessage().contains("Timeout value is not set."));
+        }
+    }
+
+    @Test
+    public void testValidationExceptionTimeoutMinValue() throws Exception {
+        try {
+            createInstance(-1);
+            fail();
+        } catch (ValidationException e) {
+            assertTrue(e.getMessage().contains("is less than 0"));
+        }
+    }
+
+    @Test
+    public void testCreateBean() throws Exception {
+        final CommitStatus status = createInstance();
+        assertBeanCount(1, FACTORY_NAME);
+        assertStatus(status, 2, 0, 0);
+    }
+
+    @Test
+    public void testReusingOldInstance() throws Exception {
+        createInstance();
+        final ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+        assertBeanCount(1, FACTORY_NAME);
+        final CommitStatus status = transaction.commit();
+        assertBeanCount(1, FACTORY_NAME);
+        assertStatus(status, 0, 0, 2);
+    }
+
+    @Test
+    public void testReconfigure() throws Exception {
+        createInstance();
+        final ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+        assertBeanCount(1, FACTORY_NAME);
+        final NeverReconnectStrategyFactoryModuleMXBean mxBean = transaction.newMBeanProxy(
+                transaction.lookupConfigBean(FACTORY_NAME, INSTANCE_NAME), NeverReconnectStrategyFactoryModuleMXBean.class);
+        mxBean.setTimeout(200);
+        final CommitStatus status = transaction.commit();
+        assertBeanCount(1, FACTORY_NAME);
+        assertStatus(status, 0, 1, 1);
+    }
+
+    private CommitStatus createInstance() throws Exception {
+        return createInstance(500);
+    }
+
+    private CommitStatus createInstance(final Integer timeout) throws InstanceAlreadyExistsException,
+            ConflictingVersionException, ValidationException {
+        final ConfigTransactionJMXClient transaction = this.configRegistryClient.createTransaction();
+        createInstance(transaction, timeout);
+        return transaction.commit();
+    }
+
+    private static ObjectName createInstance(final ConfigTransactionJMXClient transaction, final Integer timeout)
+            throws InstanceAlreadyExistsException {
+        final ObjectName nameCreated = transaction.createModule(FACTORY_NAME, INSTANCE_NAME);
+        final NeverReconnectStrategyFactoryModuleMXBean mxBean = transaction.newMBeanProxy(nameCreated,
+                NeverReconnectStrategyFactoryModuleMXBean.class);
+        mxBean.setTimeout(timeout);
+        mxBean.setExecutor(GlobalEventExecutorUtil.create(transaction));
+        return nameCreated;
+    }
+
+}
diff --git a/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/controller/config/yang/protocol/framework/ReconnectImmediatelyStrategyModuleTest.java b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/controller/config/yang/protocol/framework/ReconnectImmediatelyStrategyModuleTest.java
new file mode 100644 (file)
index 0000000..24a0e91
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * 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.config.yang.protocol.framework;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.opendaylight.controller.config.yang.netty.eventexecutor.GlobalEventExecutorModuleFactory;
+
+public class ReconnectImmediatelyStrategyModuleTest extends AbstractConfigTest {
+
+    private static final String INSTANCE_NAME = "reconnect-immediately-strategy-factory-impl";
+    private static final String FACTORY_NAME = ReconnectImmediatelyStrategyFactoryModuleFactory.NAME;
+
+    @Before
+    public void setUp() throws Exception {
+        super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(
+                new ReconnectImmediatelyStrategyFactoryModuleFactory(), new GlobalEventExecutorModuleFactory()));
+    }
+
+    @Test
+    public void testValidationExceptionTimeoutNotSet() throws Exception {
+        try {
+            createInstance(null);
+            fail();
+        } catch (ValidationException e) {
+            assertTrue(e.getMessage().contains("Timeout value is not set."));
+        }
+    }
+
+    @Test
+    public void testValidationExceptionTimeoutMinValue() throws Exception {
+        try {
+            createInstance(-1);
+            fail();
+        } catch (ValidationException e) {
+            assertTrue(e.getMessage().contains("is less than 0"));
+        }
+    }
+
+    @Test
+    public void testCreateBean() throws Exception {
+        final CommitStatus status = createInstance();
+        assertBeanCount(1, FACTORY_NAME);
+        assertStatus(status, 2, 0, 0);
+    }
+
+    @Test
+    public void testReusingOldInstance() throws Exception {
+        createInstance();
+        final ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+        assertBeanCount(1, FACTORY_NAME);
+        final CommitStatus status = transaction.commit();
+        assertBeanCount(1, FACTORY_NAME);
+        assertStatus(status, 0, 0, 2);
+    }
+
+    @Test
+    public void testReconfigure() throws Exception {
+        createInstance();
+        final ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+        assertBeanCount(1, FACTORY_NAME);
+        final ReconnectImmediatelyStrategyFactoryModuleMXBean mxBean = transaction.newMBeanProxy(
+                transaction.lookupConfigBean(FACTORY_NAME, INSTANCE_NAME),
+                ReconnectImmediatelyStrategyFactoryModuleMXBean.class);
+        mxBean.setTimeout(200);
+        final CommitStatus status = transaction.commit();
+        assertBeanCount(1, FACTORY_NAME);
+        assertStatus(status, 0, 1, 1);
+    }
+
+    private CommitStatus createInstance() throws Exception {
+        return createInstance(500);
+    }
+
+    private CommitStatus createInstance(final Integer timeout) throws Exception {
+        final ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+        createInstance(transaction, timeout);
+        return transaction.commit();
+    }
+
+    private static ObjectName createInstance(final ConfigTransactionJMXClient transaction, final Integer timeout)
+            throws InstanceAlreadyExistsException {
+        final ObjectName nameCreated = transaction.createModule(FACTORY_NAME, INSTANCE_NAME);
+        final ReconnectImmediatelyStrategyFactoryModuleMXBean mxBean = transaction.newMBeanProxy(nameCreated,
+                ReconnectImmediatelyStrategyFactoryModuleMXBean.class);
+        mxBean.setTimeout(timeout);
+        mxBean.setExecutor(GlobalEventExecutorUtil.create(transaction));
+        return nameCreated;
+    }
+
+}
diff --git a/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/controller/config/yang/protocol/framework/TimedReconnectStrategyModuleTest.java b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/controller/config/yang/protocol/framework/TimedReconnectStrategyModuleTest.java
new file mode 100644 (file)
index 0000000..d051a00
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * 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.config.yang.protocol.framework;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.math.BigDecimal;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.opendaylight.controller.config.yang.netty.eventexecutor.GlobalEventExecutorModuleFactory;
+
+public class TimedReconnectStrategyModuleTest extends AbstractConfigTest {
+
+    private static final String INSTANCE_NAME = "timed-reconect-stategy-facotry-impl";
+    private static final String FACTORY_NAME = TimedReconnectStrategyFactoryModuleFactory.NAME;
+
+    @Before
+    public void setUp() throws Exception {
+        super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(
+                new TimedReconnectStrategyFactoryModuleFactory(), new GlobalEventExecutorModuleFactory()));
+    }
+
+    @Test
+    public void testValidationExceptionSleepFactorNotSet() throws Exception {
+        try {
+            createInstance(500, 100L, null, 500L, 10L, 10000L);
+            fail();
+        } catch (ValidationException e) {
+            assertTrue(e.getMessage().contains("SleepFactor value is not set."));
+        }
+    }
+
+    @Test
+    public void testValidationExceptionSleepFactorMinValue() throws Exception {
+        try {
+            createInstance(500, 100L, new BigDecimal(0.5), 500L, 10L, 10000L);
+            fail();
+        } catch (ValidationException e) {
+            assertTrue(e.getMessage().contains("is less than 1"));
+        }
+    }
+
+    @Test
+    public void testValidationExceptionConnectTimeNotSet() throws Exception {
+        try {
+            createInstance(null, 100L, new BigDecimal(1.0), 500L, 10L, 10000L);
+            fail();
+        } catch (ValidationException e) {
+            assertTrue(e.getMessage().contains("ConnectTime value is not set."));
+        }
+    }
+
+    @Test
+    public void testValidationExceptionConnectTimeMinValue() throws Exception {
+        try {
+            createInstance(-1, 100L, new BigDecimal(1.0), 500L, 10L, 10000L);
+            fail();
+        } catch (ValidationException e) {
+            assertTrue(e.getMessage().contains("is less than 0"));
+        }
+    }
+
+    @Test
+    public void testValidationExceptionMinSleepNotSet() throws Exception {
+        try {
+            createInstance(100, null, new BigDecimal(1.0), 100L, 10L, 10000L);
+            fail();
+        } catch (ValidationException e) {
+            assertTrue(e.getMessage().contains("MinSleep value is not set."));
+        }
+    }
+
+    @Test
+    public void testValidationExceptionMaxSleep() throws Exception {
+        try {
+            createInstance(100, 300L, new BigDecimal(1.0), 100L, 10L, 10000L);
+            fail();
+        } catch (ValidationException e) {
+            assertTrue(e.getMessage().contains("is greter than MaxSleep"));
+        }
+    }
+
+    @Test
+    public void testCreateBean() throws Exception {
+        final CommitStatus status = createInstance();
+        assertBeanCount(1, FACTORY_NAME);
+        assertStatus(status, 2, 0, 0);
+    }
+
+    @Test
+    public void testReusingOldInstance() throws Exception {
+        createInstance();
+        final ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+        assertBeanCount(1, FACTORY_NAME);
+        final CommitStatus status = transaction.commit();
+        assertBeanCount(1, FACTORY_NAME);
+        assertStatus(status, 0, 0, 2);
+    }
+
+    @Test
+    public void testReconfigure() throws Exception {
+        createInstance();
+        final ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+        assertBeanCount(1, FACTORY_NAME);
+        final TimedReconnectStrategyFactoryModuleMXBean mxBean = transaction.newMBeanProxy(
+                transaction.lookupConfigBean(FACTORY_NAME, INSTANCE_NAME), TimedReconnectStrategyFactoryModuleMXBean.class);
+        assertEquals(mxBean.getMinSleep(), new Long(100));
+        mxBean.setMinSleep(200L);
+        assertEquals(mxBean.getMinSleep(), new Long(200));
+        final CommitStatus status = transaction.commit();
+        assertBeanCount(1, FACTORY_NAME);
+        assertStatus(status, 0, 1, 1);
+
+    }
+
+    private CommitStatus createInstance() throws Exception {
+        return createInstance(500, 100L, new BigDecimal(1.0), 500L, 10L, 10000L);
+    }
+
+    private CommitStatus createInstance(final Integer connectTime, final Long minSleep, final BigDecimal sleepFactor,
+            final Long maxSleep, final Long maxAttempts, final Long deadline) throws ConflictingVersionException,
+            ValidationException, InstanceAlreadyExistsException {
+        ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+        createInstance(transaction, INSTANCE_NAME, connectTime, minSleep, sleepFactor, maxSleep, maxAttempts, deadline);
+        return transaction.commit();
+    }
+
+    public static ObjectName createInstance(final ConfigTransactionJMXClient transaction, final String InstanceName)
+            throws Exception {
+        return createInstance(transaction, InstanceName, 500, 100L, new BigDecimal(1.0), 500L, 10L, 10000L);
+    }
+
+    private static ObjectName createInstance(final ConfigTransactionJMXClient transaction, final String instanceName,
+            final Integer connectTime, final Long minSleep, final BigDecimal sleepFactor, final Long maxSleep,
+            final Long maxAttempts, final Long deadline) throws InstanceAlreadyExistsException {
+        final ObjectName nameCreated = transaction.createModule(FACTORY_NAME, instanceName);
+        final TimedReconnectStrategyFactoryModuleMXBean mxBean = transaction.newMBeanProxy(nameCreated,
+                TimedReconnectStrategyFactoryModuleMXBean.class);
+        mxBean.setConnectTime(connectTime);
+        mxBean.setDeadline(deadline);
+        mxBean.setMaxAttempts(maxAttempts);
+        mxBean.setMaxSleep(maxSleep);
+        mxBean.setMinSleep(minSleep);
+        mxBean.setSleepFactor(sleepFactor);
+        mxBean.setExecutor(GlobalEventExecutorUtil.create(transaction));
+        return nameCreated;
+    }
+
+}
index dd510a1ed7099a3e087e7e4aa935b0881d6f84fb..fb0718a721f15a6f702fdd0134acab5a5f3f3da6 100644 (file)
@@ -161,6 +161,8 @@ public class ConfigRegistryImpl implements AutoCloseable, ConfigRegistryImplMXBe
 
         Map<String, Map.Entry<ModuleFactory, BundleContext>> allCurrentFactories = Collections.unmodifiableMap(
                 resolver.getAllFactories());
+
+        // closed by transaction controller
         ConfigTransactionLookupRegistry txLookupRegistry = new ConfigTransactionLookupRegistry(new TransactionIdentifier(
                 transactionName), factory, allCurrentFactories);
         ServiceReferenceWritableRegistry writableRegistry = ServiceReferenceRegistryImpl.createSRWritableRegistry(
index 84f76c993692055899f7aea55a477c44c970f576..6b7251c302975911c598ccbc0dce4fd09678403e 100644 (file)
@@ -407,12 +407,14 @@ class ConfigTransactionControllerImpl implements
     }
 
     private void internalAbort() {
+        logger.trace("Aborting {}", this);
         transactionStatus.setAborted();
         close();
     }
 
     public void close() {
         dependencyResolverManager.close();
+        txLookupRegistry.close();
     }
 
     @Override
index 308b137403fad9ec60b671813c752ebacf083629..6381836af856f06c6eba19eccfaa4529da6c1194 100644 (file)
@@ -14,9 +14,7 @@ import org.opendaylight.controller.config.manager.impl.osgi.mapping.ModuleInfoBu
 import org.opendaylight.controller.config.manager.impl.osgi.mapping.RefreshingSCPModuleInfoRegistry;
 import org.opendaylight.controller.config.manager.impl.util.OsgiRegistrationUtil;
 import org.opendaylight.controller.config.spi.ModuleFactory;
-import org.opendaylight.yangtools.concepts.ObjectRegistration;
 import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
-import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
 import org.osgi.util.tracker.ServiceTracker;
@@ -25,7 +23,6 @@ import javax.management.InstanceAlreadyExistsException;
 import javax.management.MBeanServer;
 import java.lang.management.ManagementFactory;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.List;
 
 import static org.opendaylight.controller.config.manager.impl.util.OsgiRegistrationUtil.registerService;
@@ -57,11 +54,12 @@ public class ConfigManagerActivator implements BundleActivator {
         // track bundles containing factories
         BlankTransactionServiceTracker blankTransactionServiceTracker = new BlankTransactionServiceTracker(
                 configRegistry);
-        ModuleFactoryBundleTracker moduleFactoryBundleTracker = new ModuleFactoryBundleTracker(
+        ModuleFactoryBundleTracker primaryModuleFactoryBundleTracker = new ModuleFactoryBundleTracker(
                 blankTransactionServiceTracker);
 
         // start extensible tracker
-        ExtensibleBundleTracker<Collection<ObjectRegistration<YangModuleInfo>>> bundleTracker = new ExtensibleBundleTracker<>(context, moduleInfoBundleTracker, moduleFactoryBundleTracker);
+        ExtensibleBundleTracker<?> bundleTracker = new ExtensibleBundleTracker<>(context,
+                primaryModuleFactoryBundleTracker, moduleInfoBundleTracker);
         bundleTracker.open();
 
         // register config registry to OSGi
index 8e93583e04459c3acb5dfe33b851507081090bd7..48fdd8855de12c9735a9a10a25ae5b5a9382b7f0 100644 (file)
@@ -7,9 +7,16 @@
  */
 package org.opendaylight.controller.config.manager.impl.osgi.mapping;
 
+import static java.lang.String.format;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
 import org.apache.commons.io.IOUtils;
 import org.opendaylight.yangtools.concepts.ObjectRegistration;
-import org.opendaylight.yangtools.concepts.Registration;
 import org.opendaylight.yangtools.sal.binding.generator.api.ModuleInfoRegistry;
 import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
 import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
@@ -19,14 +26,6 @@ import org.osgi.util.tracker.BundleTrackerCustomizer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.InputStream;
-import java.net.URL;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
-
-import static java.lang.String.format;
-
 /**
  * Tracks bundles and attempts to retrieve YangModuleInfo, which is then fed into ModuleInfoRegistry
  */
@@ -78,7 +77,7 @@ public final class ModuleInfoBundleTracker implements BundleTrackerCustomizer<Co
             return;
         }
 
-        for (Registration<YangModuleInfo> reg : regs) {
+        for (ObjectRegistration<YangModuleInfo> reg : regs) {
             try {
                 reg.close();
             } catch (Exception e) {
index 28408abed2fb564c05688302cc0d3b20be0465a9..9852a45853b1fad9f0bbd9ab4e3011bb06f8d29b 100644 (file)
@@ -326,26 +326,18 @@ public class SimpleConfigurationTest extends AbstractConfigTest {
     }
 
     @Test
-    public void testAbort() throws InstanceAlreadyExistsException, ValidationException {
+    public void testAbort() throws Exception {
         ConfigTransactionJMXClient transaction = configRegistryClient
                 .createTransaction();
         assertEquals(1, configRegistryClient.getOpenConfigs().size());
 
         transaction.abortConfig();
+        assertEquals(0, configRegistryClient.getOpenConfigs().size());
         try {
-            transaction.createModule(TestingFixedThreadPoolModuleFactory.NAME,
-                    fixed1);
-            fail();
-        } catch (IllegalStateException e) {
-            assertEquals("Configuration was aborted", e.getMessage());
-        }
-        try {
-            transaction.validateConfig();
+            platformMBeanServer.getMBeanInfo(transaction.getObjectName());
             fail();
-        } catch (IllegalStateException e) {
-            assertEquals("Configuration was aborted", e.getMessage());
+        }catch(InstanceNotFoundException e){
         }
-        assertEquals(0, configRegistryClient.getOpenConfigs().size());
     }
 
     @Test
diff --git a/opendaylight/config/config-persister-directory-adapter/pom.xml b/opendaylight/config/config-persister-directory-adapter/pom.xml
deleted file mode 100644 (file)
index 86b8c4d..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-<?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>
-        <artifactId>config-subsystem</artifactId>
-        <groupId>org.opendaylight.controller</groupId>
-        <version>0.2.5-SNAPSHOT</version>
-        <relativePath>..</relativePath>
-    </parent>
-    <artifactId>config-persister-directory-adapter</artifactId>
-    <name>${project.artifactId}</name>
-    <packaging>bundle</packaging>
-
-    <dependencies>
-        <!-- compile dependencies -->
-        <dependency>
-            <groupId>${project.groupId}</groupId>
-            <artifactId>config-persister-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-lang3</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>commons-io</groupId>
-            <artifactId>commons-io</artifactId>
-        </dependency>
-
-        <!-- test dependencies -->
-        <dependency>
-            <groupId>org.opendaylight.yangtools</groupId>
-            <artifactId>mockito-configuration</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>${project.groupId}</groupId>
-            <artifactId>config-persister-api</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-
-    </dependencies>
-
-    <build>
-        <plugins>
-            <!-- workaround for creating version according to OSGi specification (major.minor.micro[.qualifier] -->
-            <plugin>
-                <groupId>org.codehaus.groovy.maven</groupId>
-                <artifactId>gmaven-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <phase>generate-sources</phase>
-                        <goals>
-                            <goal>execute</goal>
-                        </goals>
-                        <configuration>
-                            <source>
-                                System.setProperty("osgiversion", "${project.version}".replace('-', '.'))
-                            </source>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <configuration>
-                    <instructions>
-                        <Fragment-Host>${project.groupId}.config-persister-impl;bundle-version=${osgiversion}
-                        </Fragment-Host>
-                        <Provide-Capability>org.opendaylight.controller.config.persister.storage.adapter
-                        </Provide-Capability>
-                        <Import-Package>
-                            com.google.common.base,
-                            com.google.common.io,
-                            org.apache.commons.io,
-                            org.opendaylight.controller.config.persist.api,
-                            org.slf4j
-                        </Import-Package>
-                    </instructions>
-                </configuration>
-            </plugin>
-        </plugins>
-    </build>
-
-</project>
diff --git a/opendaylight/config/config-persister-directory-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryPersister.java b/opendaylight/config/config-persister-directory-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryPersister.java
deleted file mode 100644 (file)
index eb8ef8c..0000000
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * 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.config.persist.storage.directory;
-
-import com.google.common.base.Charsets;
-import com.google.common.io.Files;
-import org.apache.commons.io.IOUtils;
-import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
-import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolderImpl;
-import org.opendaylight.controller.config.persist.api.Persister;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-
-public class DirectoryPersister implements Persister {
-    private static final Logger logger = LoggerFactory.getLogger(DirectoryPersister.class);
-    private static final Charset ENCODING = Charsets.UTF_8;
-
-    public static final String MODULES_START = "//MODULES START";
-    static final String SERVICES_START = "//SERVICES START";
-    static final String CAPABILITIES_START = "//CAPABILITIES START";
-
-
-    private final File storage;
-    private static final String header, middle, footer;
-
-    static {
-        header = readResource("header.txt");
-        middle = readResource("middle.txt");
-        footer = readResource("footer.txt");
-    }
-
-    public DirectoryPersister(File storage) {
-        checkArgument(storage.exists() && storage.isDirectory(), "Storage directory does not exist: " + storage);
-        this.storage = storage;
-    }
-
-    private static String readResource(String resource) {
-        try {
-            return IOUtils.toString(DirectoryPersister.class.getResourceAsStream("/" + resource));
-        } catch (IOException e) {
-            throw new IllegalStateException("Cannot load " + resource, e);
-        }
-    }
-
-    @Override
-    public void persistConfig(ConfigSnapshotHolder holder) throws IOException {
-        throw new UnsupportedOperationException("This adapter is read only. Please set readonly=true on " + getClass());
-    }
-
-    @Override
-    public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
-        File[] filesArray = storage.listFiles();
-        if (filesArray == null || filesArray.length == 0) {
-            return Collections.emptyList();
-        }
-        List<File> sortedFiles = new ArrayList<>(Arrays.asList(filesArray));
-        Collections.sort(sortedFiles);
-        // combine all found files
-        logger.debug("Reading files in following order: {}", sortedFiles);
-
-        List<ConfigSnapshotHolder> result = new ArrayList<>();
-        for (File file : sortedFiles) {
-            logger.trace("Adding file '{}' to combined result", file);
-
-            ConfigSnapshotHolder configSnapshotHolder = loadLastConfig(file);
-            result.add(configSnapshotHolder);
-        }
-        return result;
-    }
-
-    public static ConfigSnapshotHolder loadLastConfig(File file) throws IOException {
-        final MyLineProcessor lineProcessor = new MyLineProcessor(file.getAbsolutePath());
-        Files.readLines(file, ENCODING, lineProcessor);
-        return lineProcessor.getConfigSnapshotHolder(header, middle, footer);
-    }
-
-
-    @Override
-    public void close() {
-
-    }
-
-    @Override
-    public String toString() {
-        return "FileStorageAdapter [storage=" + storage + "]";
-    }
-}
-
-class MyLineProcessor implements com.google.common.io.LineProcessor<String> {
-    private final String fileNameForReporting;
-
-    private boolean inModules, inServices, inCapabilities;
-    private final StringBuffer modulesBuffer = new StringBuffer(), servicesBuilder = new StringBuffer();
-    private final SortedSet<String> caps = new TreeSet<>();
-
-    MyLineProcessor(String fileNameForReporting) {
-        this.fileNameForReporting = fileNameForReporting;
-    }
-
-    @Override
-    public String getResult() {
-        return null;
-    }
-
-    @Override
-    public boolean processLine(String line) throws IOException {
-
-        String lineWithNewLine = line + System.lineSeparator();
-        if (line.equals(DirectoryPersister.MODULES_START)) {
-            checkState(inModules == false && inServices == false && inCapabilities == false);
-            inModules = true;
-        } else if (line.equals(DirectoryPersister.SERVICES_START)) {
-            checkState(inModules == true && inServices == false && inCapabilities == false);
-            inModules = false;
-            inServices = true;
-        } else if (line.equals(DirectoryPersister.CAPABILITIES_START)) {
-            checkState(inModules == false && inServices == true && inCapabilities == false);
-            inServices = false;
-            inCapabilities = true;
-        } else if (inModules) {
-            modulesBuffer.append(lineWithNewLine);
-        } else if (inServices) {
-            servicesBuilder.append(lineWithNewLine);
-        } else {
-            caps.add(line);
-        }
-        return true;
-    }
-
-    private void checkFileConsistency(){
-        checkState(inCapabilities, "File %s is missing delimiters in this order: %s", fileNameForReporting,
-                Arrays.asList(DirectoryPersister.MODULES_START,
-                        DirectoryPersister.SERVICES_START,
-                        DirectoryPersister.CAPABILITIES_START));
-    }
-
-    String getModules() {
-        checkFileConsistency();
-        return modulesBuffer.toString();
-    }
-
-    String getServices() {
-        checkFileConsistency();
-        return servicesBuilder.toString();
-    }
-
-    SortedSet<String> getCapabilities() {
-        checkFileConsistency();
-        return caps;
-    }
-
-    ConfigSnapshotHolder getConfigSnapshotHolder(String header, String middle, String footer) {
-        String combinedSnapshot = header + getModules() + middle + getServices() + footer;
-        ConfigSnapshotHolder result = new ConfigSnapshotHolderImpl(combinedSnapshot, getCapabilities(), fileNameForReporting);
-        return result;
-    }
-
-}
-
diff --git a/opendaylight/config/config-persister-directory-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryStorageAdapter.java b/opendaylight/config/config-persister-directory-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryStorageAdapter.java
deleted file mode 100644 (file)
index 69c8fba..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.config.persist.storage.directory;
-
-import com.google.common.base.Preconditions;
-import org.opendaylight.controller.config.persist.api.Persister;
-import org.opendaylight.controller.config.persist.api.PropertiesProvider;
-import org.opendaylight.controller.config.persist.api.StorageAdapter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-
-/**
- * StorageAdapter that retrieves initial configuration from a directory. If multiple files are present, snapshot and
- * required capabilities will be merged together. Writing to this persister is not supported.
- */
-public class DirectoryStorageAdapter implements StorageAdapter {
-    private static final Logger logger = LoggerFactory.getLogger(DirectoryStorageAdapter.class);
-
-    public static final String DIRECTORY_STORAGE_PROP = "directoryStorage";
-
-
-    @Override
-    public Persister instantiate(PropertiesProvider propertiesProvider) {
-        String fileStorageProperty = propertiesProvider.getProperty(DIRECTORY_STORAGE_PROP);
-        Preconditions.checkNotNull(fileStorageProperty, "Unable to find " + propertiesProvider.getFullKeyForReporting(DIRECTORY_STORAGE_PROP));
-        File storage  = new File(fileStorageProperty);
-        logger.debug("Using {}", storage);
-        return new DirectoryPersister(storage);
-    }
-
-}
diff --git a/opendaylight/config/config-persister-directory-adapter/src/main/resources/footer.txt b/opendaylight/config/config-persister-directory-adapter/src/main/resources/footer.txt
deleted file mode 100644 (file)
index d4dcc62..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-    </services>
-</data>
diff --git a/opendaylight/config/config-persister-directory-adapter/src/main/resources/header.txt b/opendaylight/config/config-persister-directory-adapter/src/main/resources/header.txt
deleted file mode 100644 (file)
index 90ed41c..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
-    <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
diff --git a/opendaylight/config/config-persister-directory-adapter/src/main/resources/middle.txt b/opendaylight/config/config-persister-directory-adapter/src/main/resources/middle.txt
deleted file mode 100644 (file)
index f728cfd..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-    </modules>
-    <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
diff --git a/opendaylight/config/config-persister-directory-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryStorageAdapterTest.java b/opendaylight/config/config-persister-directory-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/directory/DirectoryStorageAdapterTest.java
deleted file mode 100644 (file)
index 278d0d2..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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.config.persist.storage.directory;
-
-import java.io.File;
-import java.util.Collections;
-import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import org.apache.commons.io.IOUtils;
-import org.junit.Test;
-import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
-import org.opendaylight.controller.config.persist.api.Persister;
-import org.opendaylight.controller.config.persist.test.PropertiesProviderTest;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-public class DirectoryStorageAdapterTest {
-    Persister tested;
-
-    private Persister instantiatePersisterFromAdapter(File file){
-        PropertiesProviderTest pp = new PropertiesProviderTest();
-        pp.addProperty("directoryStorage",file.getPath());
-        DirectoryStorageAdapter dsa = new DirectoryStorageAdapter();
-        return dsa.instantiate(pp);
-    }
-    @Test
-    public void testEmptyDirectory() throws Exception {
-        File folder = new File("target/emptyFolder");
-        folder.mkdir();
-
-        tested =  instantiatePersisterFromAdapter(folder);
-        assertEquals(Collections.<ConfigSnapshotHolder>emptyList(), tested.loadLastConfigs());
-
-        try {
-            tested.persistConfig(new ConfigSnapshotHolder() {
-                @Override
-                public String getConfigSnapshot() {
-                    throw new RuntimeException();
-                }
-
-                @Override
-                public SortedSet<String> getCapabilities() {
-                    throw new RuntimeException();
-                }
-            });
-            fail();
-        } catch (UnsupportedOperationException e) {
-
-        }
-    }
-
-    private File getFolder(String folderName) {
-        File result = new File(("src/test/resources/" +
-                folderName).replace("/", File.separator));
-        assertTrue(result + " is not a directory", result.isDirectory());
-        return result;
-    }
-
-    @Test
-    public void testOneFile() throws Exception {
-        File folder = getFolder("oneFile");
-
-        tested = instantiatePersisterFromAdapter(folder);
-
-        List<ConfigSnapshotHolder> results = tested.loadLastConfigs();
-        assertEquals(1, results.size());
-        ConfigSnapshotHolder result = results.get(0);
-        assertSnapshot(result, "oneFileExpected");
-    }
-
-
-    @Test
-    public void testTwoFiles() throws Exception {
-        File folder = getFolder("twoFiles");
-        tested = instantiatePersisterFromAdapter(folder);
-
-        List<ConfigSnapshotHolder> results = tested.loadLastConfigs();
-        assertEquals(2, results.size());
-        assertSnapshot(results.get(0), "twoFilesExpected1");
-        assertSnapshot(results.get(1), "twoFilesExpected2");
-    }
-
-    private void assertSnapshot(ConfigSnapshotHolder result, String directory) throws Exception {
-        SortedSet<String> expectedCapabilities = new TreeSet<>(IOUtils.readLines(getClass().getResourceAsStream("/" + directory + "/expectedCapabilities.txt")));
-        String expectedSnapshot = IOUtils.toString(getClass().getResourceAsStream("/" + directory + "/expectedSnapshot.xml"));
-        expectedSnapshot = expectedSnapshot.replaceAll("\r","");
-        String _snapshot = result.getConfigSnapshot();
-        _snapshot = _snapshot.replaceAll("\r","");
-        assertEquals(expectedCapabilities, result.getCapabilities());
-        assertEquals(expectedSnapshot, _snapshot);
-    }
-
-}
diff --git a/opendaylight/config/config-persister-directory-adapter/src/test/resources/oneFile/controller.config.txt b/opendaylight/config/config-persister-directory-adapter/src/test/resources/oneFile/controller.config.txt
deleted file mode 100644 (file)
index 99b4cb9..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-//MODULES START
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:schema-service-singleton</type>
-            <name>yang-schema-service</name>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:hash-map-data-store</type>
-            <name>hash-map-data-store</name>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
-            <name>dom-broker</name>
-            <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
-                <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
-                <name>ref_hash-map-data-store</name>
-            </data-store>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
-            <name>binding-broker-impl</name>
-            <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
-                <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
-                <name>ref_binding-notification-broker</name>
-            </notification-service>
-            <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
-                <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
-                <name>ref_binding-data-broker</name>
-            </data-broker>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:runtime-generated-mapping</type>
-            <name>runtime-mapping-singleton</name>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
-            <name>binding-notification-broker</name>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-broker</type>
-            <name>binding-data-broker</name>
-            <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
-                <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
-                <name>ref_dom-broker</name>
-            </dom-broker>
-            <mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
-                <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
-                <name>ref_runtime-mapping-singleton</name>
-            </mapping-service>
-        </module>
-//SERVICES START
-        <service>
-            <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
-            <instance>
-                <name>ref_yang-schema-service</name>
-                <provider>/config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
-            <instance>
-                <name>ref_binding-notification-broker</name>
-                <provider>/config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
-            <instance>
-                <name>ref_hash-map-data-store</name>
-                <provider>/config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
-            <instance>
-                <name>ref_binding-broker-impl</name>
-                <provider>/config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
-            <instance>
-                <name>ref_runtime-mapping-singleton</name>
-                <provider>/config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
-            <instance>
-                <name>ref_dom-broker</name>
-                <provider>/config/modules/module[name='dom-broker-impl']/instance[name='dom-broker']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
-            <instance>
-                <name>ref_binding-data-broker</name>
-                <provider>/config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker']</provider>
-            </instance>
-        </service>
-//CAPABILITIES START
-urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:threadpool?module=threadpool&revision=2013-04-09
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:config?module=config&revision=2013-04-05
-urn:ietf:params:netconf:capability:candidate:1.0
-urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04
-urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor?module=netty-event-executor&revision=2013-11-12
-urn:ietf:params:xml:ns:yang:rpc-context?module=rpc-context&revision=2013-06-17
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28
-urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2010-09-24
-urn:ietf:params:netconf:capability:rollback-on-error:1.0
-urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2010-09-24
-urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?module=threadpool-impl&revision=2013-04-05
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:logback:config?module=config-logging&revision=2013-07-16
-urn:opendaylight:yang:extension:yang-ext?module=yang-ext&revision=2013-07-09
-urn:opendaylight:params:xml:ns:yang:iana?module=iana&revision=2013-08-16
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:ieee754?module=ieee754&revision=2013-08-19
diff --git a/opendaylight/config/config-persister-directory-adapter/src/test/resources/oneFileExpected/expectedCapabilities.txt b/opendaylight/config/config-persister-directory-adapter/src/test/resources/oneFileExpected/expectedCapabilities.txt
deleted file mode 100644 (file)
index 84c85b7..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:threadpool?module=threadpool&revision=2013-04-09
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:config?module=config&revision=2013-04-05
-urn:ietf:params:netconf:capability:candidate:1.0
-urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04
-urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor?module=netty-event-executor&revision=2013-11-12
-urn:ietf:params:xml:ns:yang:rpc-context?module=rpc-context&revision=2013-06-17
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28
-urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2010-09-24
-urn:ietf:params:netconf:capability:rollback-on-error:1.0
-urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2010-09-24
-urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?module=threadpool-impl&revision=2013-04-05
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:logback:config?module=config-logging&revision=2013-07-16
-urn:opendaylight:yang:extension:yang-ext?module=yang-ext&revision=2013-07-09
-urn:opendaylight:params:xml:ns:yang:iana?module=iana&revision=2013-08-16
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:ieee754?module=ieee754&revision=2013-08-19
diff --git a/opendaylight/config/config-persister-directory-adapter/src/test/resources/oneFileExpected/expectedSnapshot.xml b/opendaylight/config/config-persister-directory-adapter/src/test/resources/oneFileExpected/expectedSnapshot.xml
deleted file mode 100644 (file)
index a6a57d7..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
-    <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:schema-service-singleton</type>
-            <name>yang-schema-service</name>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:hash-map-data-store</type>
-            <name>hash-map-data-store</name>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
-            <name>dom-broker</name>
-            <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
-                <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
-                <name>ref_hash-map-data-store</name>
-            </data-store>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
-            <name>binding-broker-impl</name>
-            <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
-                <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
-                <name>ref_binding-notification-broker</name>
-            </notification-service>
-            <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
-                <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
-                <name>ref_binding-data-broker</name>
-            </data-broker>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:runtime-generated-mapping</type>
-            <name>runtime-mapping-singleton</name>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
-            <name>binding-notification-broker</name>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-broker</type>
-            <name>binding-data-broker</name>
-            <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
-                <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
-                <name>ref_dom-broker</name>
-            </dom-broker>
-            <mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
-                <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
-                <name>ref_runtime-mapping-singleton</name>
-            </mapping-service>
-        </module>
-    </modules>
-    <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
-        <service>
-            <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
-            <instance>
-                <name>ref_yang-schema-service</name>
-                <provider>/config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
-            <instance>
-                <name>ref_binding-notification-broker</name>
-                <provider>/config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
-            <instance>
-                <name>ref_hash-map-data-store</name>
-                <provider>/config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
-            <instance>
-                <name>ref_binding-broker-impl</name>
-                <provider>/config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
-            <instance>
-                <name>ref_runtime-mapping-singleton</name>
-                <provider>/config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
-            <instance>
-                <name>ref_dom-broker</name>
-                <provider>/config/modules/module[name='dom-broker-impl']/instance[name='dom-broker']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
-            <instance>
-                <name>ref_binding-data-broker</name>
-                <provider>/config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker']</provider>
-            </instance>
-        </service>
-    </services>
-</data>
diff --git a/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFiles/controller.config1.txt b/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFiles/controller.config1.txt
deleted file mode 100644 (file)
index 6ae48fb..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-//MODULES START
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:schema-service-singleton</type>
-            <name>yang-schema-service</name>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:hash-map-data-store</type>
-            <name>hash-map-data-store</name>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
-            <name>dom-broker</name>
-            <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
-                <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
-                <name>ref_hash-map-data-store</name>
-            </data-store>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
-            <name>binding-broker-impl</name>
-            <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
-                <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
-                <name>ref_binding-notification-broker</name>
-            </notification-service>
-            <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
-                <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
-                <name>ref_binding-data-broker</name>
-            </data-broker>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:runtime-generated-mapping</type>
-            <name>runtime-mapping-singleton</name>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
-            <name>binding-notification-broker</name>
-        </module>
-//SERVICES START
-        <service>
-            <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
-            <instance>
-                <name>ref_yang-schema-service</name>
-                <provider>/config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
-            <instance>
-                <name>ref_binding-notification-broker</name>
-                <provider>/config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
-            <instance>
-                <name>ref_hash-map-data-store</name>
-                <provider>/config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
-            <instance>
-                <name>ref_binding-broker-impl</name>
-                <provider>/config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
-            <instance>
-                <name>ref_runtime-mapping-singleton</name>
-                <provider>/config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
-            <instance>
-                <name>ref_dom-broker</name>
-                <provider>/config/modules/module[name='dom-broker-impl']/instance[name='dom-broker']</provider>
-            </instance>
-        </service>
-//CAPABILITIES START
-urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27
diff --git a/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFiles/controller.config2.txt b/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFiles/controller.config2.txt
deleted file mode 100644 (file)
index ad9138f..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-//MODULES START
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-broker</type>
-            <name>binding-data-broker</name>
-            <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
-                <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
-                <name>ref_dom-broker</name>
-            </dom-broker>
-            <mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
-                <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
-                <name>ref_runtime-mapping-singleton</name>
-            </mapping-service>
-        </module>
-//SERVICES START
-        <service>
-            <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
-            <instance>
-                <name>ref_binding-data-broker</name>
-                <provider>/config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker']</provider>
-            </instance>
-        </service>
-//CAPABILITIES START
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:threadpool?module=threadpool&revision=2013-04-09
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:config?module=config&revision=2013-04-05
-urn:ietf:params:netconf:capability:candidate:1.0
-urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04
-urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor?module=netty-event-executor&revision=2013-11-12
-urn:ietf:params:xml:ns:yang:rpc-context?module=rpc-context&revision=2013-06-17
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28
-urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2010-09-24
-urn:ietf:params:netconf:capability:rollback-on-error:1.0
-urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2010-09-24
-urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?module=threadpool-impl&revision=2013-04-05
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:logback:config?module=config-logging&revision=2013-07-16
-urn:opendaylight:yang:extension:yang-ext?module=yang-ext&revision=2013-07-09
-urn:opendaylight:params:xml:ns:yang:iana?module=iana&revision=2013-08-16
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:ieee754?module=ieee754&revision=2013-08-19
diff --git a/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected1/expectedCapabilities.txt b/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected1/expectedCapabilities.txt
deleted file mode 100644 (file)
index ef35fdd..0000000
+++ /dev/null
@@ -1 +0,0 @@
-urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27
diff --git a/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected1/expectedSnapshot.xml b/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected1/expectedSnapshot.xml
deleted file mode 100644 (file)
index 2b1d06e..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
-    <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:schema-service-singleton</type>
-            <name>yang-schema-service</name>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:hash-map-data-store</type>
-            <name>hash-map-data-store</name>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
-            <name>dom-broker</name>
-            <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
-                <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
-                <name>ref_hash-map-data-store</name>
-            </data-store>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
-            <name>binding-broker-impl</name>
-            <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
-                <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
-                <name>ref_binding-notification-broker</name>
-            </notification-service>
-            <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
-                <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
-                <name>ref_binding-data-broker</name>
-            </data-broker>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:runtime-generated-mapping</type>
-            <name>runtime-mapping-singleton</name>
-        </module>
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
-            <name>binding-notification-broker</name>
-        </module>
-    </modules>
-    <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
-        <service>
-            <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
-            <instance>
-                <name>ref_yang-schema-service</name>
-                <provider>/config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
-            <instance>
-                <name>ref_binding-notification-broker</name>
-                <provider>/config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
-            <instance>
-                <name>ref_hash-map-data-store</name>
-                <provider>/config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
-            <instance>
-                <name>ref_binding-broker-impl</name>
-                <provider>/config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
-            <instance>
-                <name>ref_runtime-mapping-singleton</name>
-                <provider>/config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton']</provider>
-            </instance>
-        </service>
-        <service>
-            <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
-            <instance>
-                <name>ref_dom-broker</name>
-                <provider>/config/modules/module[name='dom-broker-impl']/instance[name='dom-broker']</provider>
-            </instance>
-        </service>
-    </services>
-</data>
diff --git a/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected2/expectedCapabilities.txt b/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected2/expectedCapabilities.txt
deleted file mode 100644 (file)
index 9924111..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:threadpool?module=threadpool&revision=2013-04-09
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:config?module=config&revision=2013-04-05
-urn:ietf:params:netconf:capability:candidate:1.0
-urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04
-urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor?module=netty-event-executor&revision=2013-11-12
-urn:ietf:params:xml:ns:yang:rpc-context?module=rpc-context&revision=2013-06-17
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28
-urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2010-09-24
-urn:ietf:params:netconf:capability:rollback-on-error:1.0
-urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2010-09-24
-urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?module=threadpool-impl&revision=2013-04-05
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:logback:config?module=config-logging&revision=2013-07-16
-urn:opendaylight:yang:extension:yang-ext?module=yang-ext&revision=2013-07-09
-urn:opendaylight:params:xml:ns:yang:iana?module=iana&revision=2013-08-16
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:ieee754?module=ieee754&revision=2013-08-19
diff --git a/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected2/expectedSnapshot.xml b/opendaylight/config/config-persister-directory-adapter/src/test/resources/twoFilesExpected2/expectedSnapshot.xml
deleted file mode 100644 (file)
index 887cb2c..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
-    <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
-        <module>
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-broker</type>
-            <name>binding-data-broker</name>
-            <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
-                <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
-                <name>ref_dom-broker</name>
-            </dom-broker>
-            <mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
-                <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
-                <name>ref_runtime-mapping-singleton</name>
-            </mapping-service>
-        </module>
-    </modules>
-    <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
-        <service>
-            <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
-            <instance>
-                <name>ref_binding-data-broker</name>
-                <provider>/config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker']</provider>
-            </instance>
-        </service>
-    </services>
-</data>
diff --git a/opendaylight/config/config-persister-directory-autodetect-adapter/pom.xml b/opendaylight/config/config-persister-directory-autodetect-adapter/pom.xml
deleted file mode 100644 (file)
index 5e556c0..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-<?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>
-        <artifactId>config-subsystem</artifactId>
-        <groupId>org.opendaylight.controller</groupId>
-        <version>0.2.5-SNAPSHOT</version>
-        <relativePath>..</relativePath>
-    </parent>
-    <artifactId>config-persister-directory-autodetect-adapter</artifactId>
-    <name>${project.artifactId}</name>
-    <packaging>bundle</packaging>
-
-    <dependencies>
-        <!-- compile dependencies -->
-        <dependency>
-            <groupId>${project.groupId}</groupId>
-            <artifactId>config-persister-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-lang3</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.opendaylight.controller</groupId>
-            <artifactId>config-persister-directory-adapter</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.opendaylight.controller</groupId>
-            <artifactId>config-persister-directory-xml-adapter</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>commons-io</groupId>
-            <artifactId>commons-io</artifactId>
-        </dependency>
-
-        <!-- test dependencies -->
-        <dependency>
-            <groupId>org.opendaylight.yangtools</groupId>
-            <artifactId>mockito-configuration</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>${project.groupId}</groupId>
-            <artifactId>config-persister-api</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-
-    </dependencies>
-
-    <build>
-        <plugins>
-            <!-- workaround for creating version according to OSGi specification (major.minor.micro[.qualifier] -->
-            <plugin>
-                <groupId>org.codehaus.groovy.maven</groupId>
-                <artifactId>gmaven-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <phase>generate-sources</phase>
-                        <goals>
-                            <goal>execute</goal>
-                        </goals>
-                        <configuration>
-                            <source>
-                                System.setProperty("osgiversion", "${project.version}".replace('-', '.'))
-                            </source>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <configuration>
-                    <instructions>
-                        <Fragment-Host>${project.groupId}.config-persister-impl;bundle-version=${osgiversion}
-                        </Fragment-Host>
-                        <Provide-Capability>org.opendaylight.controller.config.persister.storage.adapter
-                        </Provide-Capability>
-                        <Import-Package>
-                            com.google.common.base,
-                            com.google.common.io,
-                            org.apache.commons.io,
-                            org.opendaylight.controller.config.persist.api,
-                            org.slf4j,
-                            com.google.common.collect,
-                            javax.xml.bind,
-                            javax.xml.bind.annotation,
-                            javax.xml.transform,
-                            javax.xml.transform.stream,
-                            org.eclipse.persistence.jaxb,
-                            org.apache.commons.lang3
-                        </Import-Package>
-                        <Private-Package>
-                            org.opendaylight.controller.config.persist.storage.file.xml.model,
-                            org.opendaylight.controller.config.persist.storage.directory,
-                            org.opendaylight.controller.config.persist.storage.directory.xml,
-                        </Private-Package>
-                    </instructions>
-                </configuration>
-            </plugin>
-        </plugins>
-    </build>
-
-</project>
diff --git a/opendaylight/config/config-persister-directory-autodetect-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/autodetect/AutodetectDirectoryPersister.java b/opendaylight/config/config-persister-directory-autodetect-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/autodetect/AutodetectDirectoryPersister.java
deleted file mode 100644 (file)
index 2028d62..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.config.persist.storage.directory.autodetect;
-
-import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
-import org.opendaylight.controller.config.persist.api.Persister;
-import org.opendaylight.controller.config.persist.storage.directory.DirectoryPersister;
-import org.opendaylight.controller.config.persist.storage.directory.xml.XmlDirectoryPersister;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.xml.bind.JAXBException;
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-public class AutodetectDirectoryPersister implements Persister {
-    private static final Logger logger = LoggerFactory.getLogger(AutodetectDirectoryPersister.class);
-
-    private final File storage;
-
-    public AutodetectDirectoryPersister(File storage) {
-        checkArgument(storage.exists() && storage.isDirectory(), "Storage directory does not exist: " + storage);
-        this.storage = storage;
-    }
-
-    @Override
-    public void persistConfig(ConfigSnapshotHolder holder) throws IOException {
-        throw new UnsupportedOperationException("This adapter is read only. Please set readonly=true on " + getClass());
-    }
-
-    @Override
-    public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
-        File[] filesArray = storage.listFiles();
-        if (filesArray == null || filesArray.length == 0) {
-            return Collections.emptyList();
-        }
-        List<File> sortedFiles = new ArrayList<>(Arrays.asList(filesArray));
-        Collections.sort(sortedFiles);
-
-        // combine all found files
-        logger.debug("Reading files in following order: {}", sortedFiles);
-
-        List<ConfigSnapshotHolder> result = new ArrayList<>();
-        for (File file : sortedFiles) {
-            logger.trace("Adding file '{}' to combined result", file);
-
-            FileType fileType = FileType.getFileType(file);
-            logger.trace("File '{}' detected as {} storage", file, fileType);
-
-            ConfigSnapshotHolder snapshot = loadLastConfig(file, fileType);
-            result.add(snapshot);
-        }
-        return result;
-    }
-
-    private ConfigSnapshotHolder loadLastConfig(File file, FileType fileType) throws IOException {
-        switch (fileType) {
-        case plaintext:
-            logger.warn("Plaintext configuration files are deprecated, and will not be supported in future versions. " +
-                    "Use xml files instead");
-            return DirectoryPersister.loadLastConfig(file);
-        case xml:
-            try {
-                return XmlDirectoryPersister.loadLastConfig(file);
-            } catch (JAXBException e) {
-                logger.warn("Unable to restore configuration snapshot from {}", file, e);
-                throw new IllegalStateException("Unable to restore configuration snapshot from " + file, e);
-            }
-        default:
-            throw new IllegalStateException("Unknown storage type " + fileType);
-        }
-    }
-
-    @Override
-    public void close() {
-
-    }
-
-    @Override
-    public String toString() {
-        final StringBuffer sb = new StringBuffer("AutodetectDirectoryPersister{");
-        sb.append("storage=").append(storage);
-        sb.append('}');
-        return sb.toString();
-    }
-}
diff --git a/opendaylight/config/config-persister-directory-autodetect-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/autodetect/AutodetectDirectoryStorageAdapter.java b/opendaylight/config/config-persister-directory-autodetect-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/autodetect/AutodetectDirectoryStorageAdapter.java
deleted file mode 100644 (file)
index f856ddd..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.config.persist.storage.directory.autodetect;
-
-import com.google.common.base.Preconditions;
-import org.opendaylight.controller.config.persist.api.Persister;
-import org.opendaylight.controller.config.persist.api.PropertiesProvider;
-import org.opendaylight.controller.config.persist.api.StorageAdapter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-
-/**
- * StorageAdapter that retrieves initial configuration from a directory. If multiple files are present, snapshot and
- * required capabilities will be merged together. Writing to this persister is not supported.
- */
-public class AutodetectDirectoryStorageAdapter implements StorageAdapter {
-    private static final Logger logger = LoggerFactory.getLogger(AutodetectDirectoryStorageAdapter.class);
-
-    public static final String DIRECTORY_STORAGE_PROP = "directoryStorage";
-
-
-    @Override
-    public Persister instantiate(PropertiesProvider propertiesProvider) {
-        String fileStorageProperty = propertiesProvider.getProperty(DIRECTORY_STORAGE_PROP);
-        Preconditions.checkNotNull(fileStorageProperty, "Unable to find " + propertiesProvider.getFullKeyForReporting(DIRECTORY_STORAGE_PROP));
-        File storage  = new File(fileStorageProperty);
-        logger.debug("Using {}", storage);
-        return new AutodetectDirectoryPersister(storage);
-    }
-
-}
diff --git a/opendaylight/config/config-persister-directory-autodetect-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/autodetect/FileType.java b/opendaylight/config/config-persister-directory-autodetect-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/autodetect/FileType.java
deleted file mode 100644 (file)
index 779a887..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.config.persist.storage.directory.autodetect;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Charsets;
-import com.google.common.io.Files;
-import org.apache.commons.lang3.StringUtils;
-import org.opendaylight.controller.config.persist.storage.directory.DirectoryPersister;
-import org.opendaylight.controller.config.persist.storage.file.xml.model.ConfigSnapshot;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Arrays;
-
-enum FileType {
-
-    plaintext, xml;
-
-    public static final String XML_STORAGE_FIRST_LINE = "<" + ConfigSnapshot.SNAPSHOT_ROOT_ELEMENT_NAME + ">";
-    private static final String XML_FILE_DEFINITION_LINE = "<?xml";
-
-    static FileType getFileType(File file) {
-        String firstLine = readFirstLine(file);
-        if(isPlaintextStorage(firstLine)) {
-            return plaintext;
-        } else if(isXmlStorage(firstLine))
-            return xml;
-
-        throw new IllegalArgumentException("File " + file + " is not of permitted storage type: " + Arrays.toString(FileType.values()));
-    }
-
-    private static boolean isXmlStorage(String firstLine) {
-        boolean isXml = false;
-        isXml |= firstLine.startsWith(XML_STORAGE_FIRST_LINE);
-        isXml |= firstLine.startsWith(XML_FILE_DEFINITION_LINE);
-        return isXml;
-    }
-
-    private static boolean isPlaintextStorage(String firstLine) {
-        return firstLine.startsWith(DirectoryPersister.MODULES_START);
-
-    }
-
-    @VisibleForTesting
-    static String readFirstLine(File file) {
-        FirstLineReadingProcessor callback = new FirstLineReadingProcessor();
-        try {
-            return Files.readLines(file, Charsets.UTF_8, callback);
-        } catch (IOException e) {
-            throw new IllegalArgumentException("Unable to detect file type of file " + file, e);
-        }
-    }
-
-
-    private static class FirstLineReadingProcessor implements com.google.common.io.LineProcessor<String> {
-        private String firstNonBlankLine;
-
-        @Override
-        public boolean processLine(String line) throws IOException {
-            if(isEmptyLine(line)) {
-                return true;
-            } else {
-                firstNonBlankLine = line.trim();
-                return false;
-            }
-        }
-
-        private boolean isEmptyLine(String line) {
-            return StringUtils.isBlank(line);
-        }
-
-        @Override
-        public String getResult() {
-            return firstNonBlankLine;
-        }
-    }
-}
diff --git a/opendaylight/config/config-persister-directory-autodetect-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/directory/autodetect/AutodetectDirectoryPersisterTest.java b/opendaylight/config/config-persister-directory-autodetect-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/directory/autodetect/AutodetectDirectoryPersisterTest.java
deleted file mode 100644 (file)
index bcade93..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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.config.persist.storage.directory.autodetect;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-import junit.framework.Assert;
-import org.junit.Test;
-import org.junit.matchers.JUnitMatchers;
-import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
-import org.opendaylight.controller.config.persist.test.PropertiesProviderTest;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-
-public class AutodetectDirectoryPersisterTest {
-
-    @Test
-    public void testCombined() throws Exception {
-        File resourcePath = FileTypeTest.getResourceAsFile("/combined/1controller.txt.config");
-        File parentFile = resourcePath.getParentFile();
-
-        AutodetectDirectoryStorageAdapter adapter = new AutodetectDirectoryStorageAdapter();
-
-        PropertiesProviderTest pp = new PropertiesProviderTest();
-        pp.addProperty("directoryStorage",parentFile.getPath());
-        AutodetectDirectoryPersister persister = (AutodetectDirectoryPersister) adapter.instantiate(pp);
-        List<ConfigSnapshotHolder> configs = persister.loadLastConfigs();
-
-        Assert.assertEquals(2, configs.size());
-        String snapFromTxt = configs.get(0).getConfigSnapshot();
-        org.junit.Assert.assertThat(snapFromTxt, JUnitMatchers.containsString("<config>txt</config>"));
-        org.junit.Assert.assertThat(snapFromTxt, JUnitMatchers.containsString("<service>txt</service>"));
-
-        String snapFromXml = configs.get(1).getConfigSnapshot();
-        org.junit.Assert.assertThat(snapFromXml, JUnitMatchers.containsString("<config>xml</config>"));
-
-        Assert.assertEquals(configs.get(0).getCapabilities(), configs.get(1).getCapabilities());
-    }
-
-    @Test
-    public void testInvalidXml() throws Exception {
-        File resourcePath = FileTypeTest.getResourceAsFile("/bad_controller.xml.config");
-        File parentFile = resourcePath.getParentFile();
-
-        AutodetectDirectoryStorageAdapter adapter = new AutodetectDirectoryStorageAdapter();
-
-        PropertiesProviderTest pp = new PropertiesProviderTest();
-        pp.addProperty("directoryStorage",parentFile.getPath());
-        AutodetectDirectoryPersister persister = (AutodetectDirectoryPersister) adapter.instantiate(pp);
-        try {
-            List<ConfigSnapshotHolder> configs = persister.loadLastConfigs();
-            fail("An exception of type " + IllegalStateException.class + " was expected");
-        } catch (IllegalStateException ise){
-            String message = ise.getMessage();
-            assertThat(message, JUnitMatchers.containsString("Unable to restore configuration snapshot from "));
-        }
-
-    }
-
-    @Test
-    public void testPersistConfig() throws Exception {
-        File resourcePath = FileTypeTest.getResourceAsFile("/combined/1controller.txt.config");
-        File parentFile = resourcePath.getParentFile();
-
-        AutodetectDirectoryStorageAdapter adapter = new AutodetectDirectoryStorageAdapter();
-
-        PropertiesProviderTest pp = new PropertiesProviderTest();
-        pp.addProperty("directoryStorage",parentFile.getPath());
-        AutodetectDirectoryPersister persister = (AutodetectDirectoryPersister) adapter.instantiate(pp);
-        List<ConfigSnapshotHolder> configs = null;
-        try {
-            configs = persister.loadLastConfigs();
-        } catch (IOException e) {
-            fail("An exception of type " + UnsupportedOperationException.class + " was expected");
-        }
-        Assert.assertEquals(2, configs.size());
-        try {
-            persister.persistConfig(configs.get(0));
-        } catch (UnsupportedOperationException uoe){
-            String message = uoe.getMessage();
-            assertThat(message,JUnitMatchers.containsString("This adapter is read only. Please set readonly=true on class"));
-        }
-    }
-
-}
diff --git a/opendaylight/config/config-persister-directory-autodetect-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/directory/autodetect/FileTypeTest.java b/opendaylight/config/config-persister-directory-autodetect-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/directory/autodetect/FileTypeTest.java
deleted file mode 100644 (file)
index bbc272d..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.config.persist.storage.directory.autodetect;
-
-import junit.framework.Assert;
-import org.junit.Test;
-import org.junit.matchers.JUnitMatchers;
-
-import java.io.File;
-
-public class FileTypeTest {
-
-    @Test
-    public void testPlaintext() throws Exception {
-        File file = getResourceAsFile("/test.txt.config");
-
-        FileType type = FileType.getFileType(file);
-        Assert.assertEquals(FileType.plaintext, type);
-
-    }
-
-    @Test
-    public void testXml() throws Exception {
-        File file = getResourceAsFile("/test.xml.config");
-
-        FileType type = FileType.getFileType(file);
-        Assert.assertEquals(FileType.xml, type);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testUnknown() throws Exception {
-        File file = getResourceAsFile("/unknown.config");
-
-        try {
-            FileType.getFileType(file);
-        } catch (IllegalArgumentException e) {
-            org.junit.Assert.assertThat(e.getMessage(), JUnitMatchers.containsString("File " + file + " is not of permitted storage type"));
-            throw e;
-        }
-    }
-
-    static File getResourceAsFile(String resource) {
-        String f = FileTypeTest.class.getResource(resource).getFile();
-        return new File(f);
-    }
-
-}
diff --git a/opendaylight/config/config-persister-directory-autodetect-adapter/src/test/resources/bad_controller.xml.config b/opendaylight/config/config-persister-directory-autodetect-adapter/src/test/resources/bad_controller.xml.config
deleted file mode 100644 (file)
index 3f6a337..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<snapshot>
-    <required-capabilities>
-        <capability
-        <capability>cap2</capability>
-        <capability>capa a</capability>
-    </required-capabilities>
-    <configuration>
-        <config>xml</config>
-    </configuration>
-</snapshot>
\ No newline at end of file
diff --git a/opendaylight/config/config-persister-directory-autodetect-adapter/src/test/resources/combined/1controller.txt.config b/opendaylight/config/config-persister-directory-autodetect-adapter/src/test/resources/combined/1controller.txt.config
deleted file mode 100644 (file)
index f72cd1c..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-//MODULES START
-        <config>txt</config>
-//SERVICES START
-        <service>txt</service>
-//CAPABILITIES START
-cap1&rev
-cap2
-capa a
\ No newline at end of file
diff --git a/opendaylight/config/config-persister-directory-autodetect-adapter/src/test/resources/combined/2controller.xml.config b/opendaylight/config/config-persister-directory-autodetect-adapter/src/test/resources/combined/2controller.xml.config
deleted file mode 100644 (file)
index 988f866..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<snapshot>
-    <required-capabilities>
-        <capability>cap1&amp;rev</capability>
-        <capability>cap2</capability>
-        <capability>capa a</capability>
-    </required-capabilities>
-    <configuration>
-        <config>xml</config>
-    </configuration>
-</snapshot>
\ No newline at end of file
diff --git a/opendaylight/config/config-persister-directory-autodetect-adapter/src/test/resources/test.txt.config b/opendaylight/config/config-persister-directory-autodetect-adapter/src/test/resources/test.txt.config
deleted file mode 100644 (file)
index edf37ca..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-//MODULES START
-configuration
\ No newline at end of file
diff --git a/opendaylight/config/config-persister-directory-autodetect-adapter/src/test/resources/test.xml.config b/opendaylight/config/config-persister-directory-autodetect-adapter/src/test/resources/test.xml.config
deleted file mode 100644 (file)
index fe94299..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-
-<snapshot>
\ No newline at end of file
diff --git a/opendaylight/config/config-persister-directory-autodetect-adapter/src/test/resources/unknown.config b/opendaylight/config/config-persister-directory-autodetect-adapter/src/test/resources/unknown.config
deleted file mode 100644 (file)
index c4218c8..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-unknown
-file type
\ No newline at end of file
index 0ca47edf0e5540cef5db8c2bcbb8f0d8f9dde611..7f8ebd7fddf8e7ee93a665106040ef4c83c5fb96 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.controller.config.persist.storage.directory.xml;
 
+import com.google.common.base.Optional;
+import com.google.common.io.Files;
 import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
 import org.opendaylight.controller.config.persist.api.Persister;
 import org.opendaylight.controller.config.persist.storage.file.xml.model.ConfigSnapshot;
@@ -17,11 +19,13 @@ import javax.xml.bind.JAXBContext;
 import javax.xml.bind.JAXBException;
 import javax.xml.bind.Unmarshaller;
 import java.io.File;
+import java.io.FilenameFilter;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 import java.util.SortedSet;
 
 import static com.google.common.base.Preconditions.checkArgument;
@@ -30,10 +34,26 @@ public class XmlDirectoryPersister implements Persister {
     private static final Logger logger = LoggerFactory.getLogger(XmlDirectoryPersister.class);
 
     private final File storage;
+    private final Optional<FilenameFilter> extensionsFilter;
 
+    /**
+     * Creates XmlDirectoryPersister that picks up all files in specified folder
+     */
     public XmlDirectoryPersister(File storage) {
+        this(storage, Optional.<FilenameFilter>absent());
+    }
+
+    /**
+     * Creates XmlDirectoryPersister that picks up files only with specified file extension
+     */
+    public XmlDirectoryPersister(File storage, Set<String> fileExtensions) {
+        this(storage, Optional.of(getFilter(fileExtensions)));
+    }
+
+    private XmlDirectoryPersister(File storage, Optional<FilenameFilter> extensionsFilter) {
         checkArgument(storage.exists() && storage.isDirectory(), "Storage directory does not exist: " + storage);
         this.storage = storage;
+        this.extensionsFilter = extensionsFilter;
     }
 
     @Override
@@ -43,7 +63,7 @@ public class XmlDirectoryPersister implements Persister {
 
     @Override
     public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
-        File[] filesArray = storage.listFiles();
+        File[] filesArray = extensionsFilter.isPresent() ? storage.listFiles(extensionsFilter.get()) : storage.listFiles();
         if (filesArray == null || filesArray.length == 0) {
             return Collections.emptyList();
         }
@@ -55,19 +75,30 @@ public class XmlDirectoryPersister implements Persister {
         List<ConfigSnapshotHolder> result = new ArrayList<>();
         for (File file : sortedFiles) {
             logger.trace("Adding file '{}' to combined result", file);
-            ConfigSnapshotHolder h = fromXmlSnapshot(file);
-            result.add(h);
+            Optional<ConfigSnapshotHolder> h = fromXmlSnapshot(file);
+            // Ignore non valid snapshot
+            if(h.isPresent() == false) {
+                continue;
+            }
+
+            result.add(h.get());
         }
         return result;
     }
 
-    private ConfigSnapshotHolder fromXmlSnapshot(File file) {
+    private Optional<ConfigSnapshotHolder> fromXmlSnapshot(File file) {
         try {
-            return loadLastConfig(file);
+            return Optional.of(loadLastConfig(file));
         } catch (JAXBException e) {
-            logger.warn("Unable to restore configuration snapshot from {}", file, e);
-            throw new IllegalStateException("Unable to restore configuration snapshot from " + file, e);
+            // In case of parse error, issue a warning, ignore and continue
+            logger.warn(
+                    "Unable to parse configuration snapshot from {}. Initial config from {} will be IGNORED in this run. " +
+                    "Note that subsequent config files may fail due to this problem. " +
+                    "Xml markup in this file needs to be fixed, for detailed information see enclosed exception.",
+                    file, file, e);
         }
+
+        return Optional.absent();
     }
 
     public static ConfigSnapshotHolder loadLastConfig(File file) throws JAXBException {
@@ -96,6 +127,17 @@ public class XmlDirectoryPersister implements Persister {
         };
     }
 
+    private static FilenameFilter getFilter(final Set<String>fileExtensions) {
+        checkArgument(fileExtensions.isEmpty() == false, "No file extension provided", fileExtensions);
+
+        return new FilenameFilter() {
+            @Override
+            public boolean accept(File dir, String name) {
+                String ext = Files.getFileExtension(name);
+                return fileExtensions.contains(ext);
+            }
+        };
+    }
 
     @Override
     public void close() {
index ab6fb1577cc49261b134b2db57ad15dd76c63938..40f8c283ddd5a8e49ff7eb2c8464bddd65178a98 100644 (file)
@@ -9,6 +9,8 @@
 package org.opendaylight.controller.config.persist.storage.directory.xml;
 
 import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Sets;
 import org.opendaylight.controller.config.persist.api.Persister;
 import org.opendaylight.controller.config.persist.api.PropertiesProvider;
 import org.opendaylight.controller.config.persist.api.StorageAdapter;
@@ -16,6 +18,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.File;
+import java.util.Set;
 
 /**
  * StorageAdapter that retrieves initial configuration from a directory. If multiple files are present, snapshot and
@@ -25,6 +28,8 @@ public class XmlDirectoryStorageAdapter implements StorageAdapter {
     private static final Logger logger = LoggerFactory.getLogger(XmlDirectoryStorageAdapter.class);
 
     public static final String DIRECTORY_STORAGE_PROP = "directoryStorage";
+    public static final String INCLUDE_EXT_PROP = "includeExtensions";
+    private static final char EXTENSIONS_SEPARATOR = ',';
 
 
     @Override
@@ -32,8 +37,21 @@ public class XmlDirectoryStorageAdapter implements StorageAdapter {
         String fileStorageProperty = propertiesProvider.getProperty(DIRECTORY_STORAGE_PROP);
         Preconditions.checkNotNull(fileStorageProperty, "Unable to find " + propertiesProvider.getFullKeyForReporting(DIRECTORY_STORAGE_PROP));
         File storage  = new File(fileStorageProperty);
-        logger.debug("Using {}", storage);
-        return new XmlDirectoryPersister(storage);
+        String fileExtensions = propertiesProvider.getProperty(INCLUDE_EXT_PROP);
+
+        logger.debug("Using storage: {}", storage);
+
+        if(fileExtensions != null) {
+            logger.debug("Using extensions: {}", fileExtensions);
+            return new XmlDirectoryPersister(storage, splitExtensions(fileExtensions));
+        } else {
+            return new XmlDirectoryPersister(storage);
+        }
+    }
+
+    private Set<String> splitExtensions(String fileExtensions) {
+        return Sets.newHashSet(Splitter.on(EXTENSIONS_SEPARATOR).trimResults().omitEmptyStrings()
+                .split(fileExtensions));
     }
 
 }
index 8e34bc79903bcb54c712416afa906246c2ef3a5e..da442ef4b2fb669b5b1bd0e9a389616cc6755b97 100644 (file)
@@ -12,6 +12,8 @@ import java.io.File;
 import java.util.Collections;
 import java.util.List;
 import java.util.SortedSet;
+
+import com.google.common.base.Optional;
 import org.junit.Test;
 import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
 import org.opendaylight.controller.config.persist.api.Persister;
@@ -26,13 +28,21 @@ public class DirectoryStorageAdapterTest {
     Persister tested;
     Logger logger = LoggerFactory.getLogger(DirectoryStorageAdapterTest.class.toString());
 
-    private Persister instantiatePersisterFromAdapter(File file){
+    private Persister instantiatePersisterFromAdapter(File file, Optional<String> extensions){
         PropertiesProviderTest pp = new PropertiesProviderTest();
-        pp.addProperty("directoryStorage",file.getPath());
+        pp.addProperty(XmlDirectoryStorageAdapter.DIRECTORY_STORAGE_PROP,file.getPath());
+        if(extensions.isPresent()) {
+            pp.addProperty(XmlDirectoryStorageAdapter.INCLUDE_EXT_PROP, extensions.get());
+        }
+
         XmlDirectoryStorageAdapter dsa = new XmlDirectoryStorageAdapter();
         return dsa.instantiate(pp);
     }
 
+    private Persister instantiatePersisterFromAdapter(File file){
+        return instantiatePersisterFromAdapter(file, Optional.<String>absent());
+    }
+
     @Test
     public void testEmptyDirectory() throws Exception {
         File folder = new File("target/emptyFolder");
@@ -69,15 +79,22 @@ public class DirectoryStorageAdapterTest {
     @Test
     public void testOneFile() throws Exception {
         File folder = getFolder("oneFile");
-        tested = instantiatePersisterFromAdapter(folder);
+        tested = instantiatePersisterFromAdapter(folder, Optional.of("xml"));
 
-        logger.info("Testing : "+tested.toString());
+        logger.info("Testing : " + tested.toString());
         List<ConfigSnapshotHolder> results = tested.loadLastConfigs();
         assertEquals(1, results.size());
         ConfigSnapshotHolder result = results.get(0);
         assertResult(result, "<config>1</config>", "cap1&rev", "cap2", "capa a");
     }
 
+    @Test
+    public void testOneFileWrongExtension() throws Exception {
+        File folder = getFolder("oneFile");
+        tested = instantiatePersisterFromAdapter(folder, Optional.of("aa, bb"));
+        logger.info("Testing : " + tested.toString());
+    }
+
     private void assertResult(ConfigSnapshotHolder result, String s, String... caps) {
         assertEquals(s, result.getConfigSnapshot().replaceAll("\\s", ""));
         int i = 0;
@@ -87,16 +104,45 @@ public class DirectoryStorageAdapterTest {
     }
 
     @Test
-    public void testTwoFiles() throws Exception {
+    public void testTwoFilesAllExtensions() throws Exception {
         File folder = getFolder("twoFiles");
         tested = instantiatePersisterFromAdapter(folder);
-        logger.info("Testing : "+tested.toString());
+        logger.info("Testing : " + tested.toString());
         List<ConfigSnapshotHolder> results = tested.loadLastConfigs();
         assertEquals(2, results.size());
 
         assertResult(results.get(0), "<config>1</config>", "cap1-a", "cap2-a", "capa a-a");
         assertResult(results.get(1), "<config>2</config>", "cap1-b", "cap2-b", "capa a-b");
+    }
+
+    @Test
+    public void testTwoFilesTwoExtensions() throws Exception {
+        File folder = getFolder("twoFiles");
+        tested = instantiatePersisterFromAdapter(folder, Optional.of("xml, xml2"));
+        logger.info("Testing : " + tested.toString());
+        assertEquals(2, tested.loadLastConfigs().size());
+    }
+
+    @Test
+    public void testTwoFilesOnlyOneExtension() throws Exception {
+        File folder = getFolder("twoFiles");
+        tested = instantiatePersisterFromAdapter(folder, Optional.of("xml"));
+        logger.info("Testing : " + tested.toString());
+        List<ConfigSnapshotHolder> results = tested.loadLastConfigs();
+        assertEquals(1, results.size());
 
+        assertResult(results.get(0), "<config>1</config>", "cap1-a", "cap2-a", "capa a-a");
+    }
+
+    @Test
+    public void testTwoFilesOneInvalid() throws Exception {
+        File folder = getFolder("twoFiles_corrupt");
+        tested = instantiatePersisterFromAdapter(folder, Optional.of("xml"));
+        logger.info("Testing : " + tested.toString());
+        List<ConfigSnapshotHolder> results = tested.loadLastConfigs();
+        assertEquals(1, results.size());
+
+        assertResult(results.get(0), "<config>1</config>", "cap1-a", "cap2-a", "capa a-a");
     }
 
 }
diff --git a/opendaylight/config/config-persister-directory-xml-adapter/src/test/resources/twoFiles_corrupt/controller.config1.xml b/opendaylight/config/config-persister-directory-xml-adapter/src/test/resources/twoFiles_corrupt/controller.config1.xml
new file mode 100644 (file)
index 0000000..28f112e
--- /dev/null
@@ -0,0 +1,10 @@
+<snapshot>
+    <required-capabilities>
+        <capability>cap1-a</capability>
+        <capability>cap2-a</capability>
+        <capability>capa a-a</capability>
+    </required-capabilities>
+    <configuration>
+        <config>1</config>
+    </configuration>
+</snapshot>
\ No newline at end of file
diff --git a/opendaylight/config/config-persister-directory-xml-adapter/src/test/resources/twoFiles_corrupt/controller.config2.xml b/opendaylight/config/config-persister-directory-xml-adapter/src/test/resources/twoFiles_corrupt/controller.config2.xml
new file mode 100644 (file)
index 0000000..ed781d5
--- /dev/null
@@ -0,0 +1,10 @@
+<snapshotInvalid>
+    <required-capabilities>
+        <capability>cap1-b</capability>
+        <capability>cap2-b</capability>
+        <capability>capa a-b</capability>
+    </required-capabilities>
+    <configuration>
+        <config>2</config>
+    </configuration>
+</snapshotInvalid>
\ No newline at end of file
diff --git a/opendaylight/config/config-persister-file-adapter/pom.xml b/opendaylight/config/config-persister-file-adapter/pom.xml
deleted file mode 100644 (file)
index a8ea93e..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-<?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>
-        <artifactId>config-subsystem</artifactId>
-        <groupId>org.opendaylight.controller</groupId>
-        <version>0.2.5-SNAPSHOT</version>
-        <relativePath>..</relativePath>
-    </parent>
-    <artifactId>config-persister-file-adapter</artifactId>
-    <name>${project.artifactId}</name>
-    <packaging>bundle</packaging>
-
-    <dependencies>
-        <!-- compile dependencies -->
-        <dependency>
-            <groupId>${project.groupId}</groupId>
-            <artifactId>config-persister-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-lang3</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-        </dependency>
-
-        <!-- test dependencies -->
-        <dependency>
-            <groupId>org.opendaylight.yangtools</groupId>
-            <artifactId>mockito-configuration</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>${project.groupId}</groupId>
-            <artifactId>config-persister-api</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
-
-    <build>
-        <plugins>
-            <!-- workaround for creating version according to OSGi specification (major.minor.micro[.qualifier] -->
-            <plugin>
-                <groupId>org.codehaus.groovy.maven</groupId>
-                <artifactId>gmaven-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <phase>generate-sources</phase>
-                        <goals>
-                            <goal>execute</goal>
-                        </goals>
-                        <configuration>
-                            <source>
-                                System.setProperty("osgiversion", "${project.version}".replace('-', '.'))
-                            </source>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <configuration>
-                    <instructions>
-                        <Fragment-Host>${project.groupId}.config-persister-impl;bundle-version=${osgiversion}
-                        </Fragment-Host>
-                        <Provide-Capability>org.opendaylight.controller.config.persister.storage.adapter
-                        </Provide-Capability>
-                    </instructions>
-                </configuration>
-            </plugin>
-        </plugins>
-    </build>
-
-</project>
diff --git a/opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java b/opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java
deleted file mode 100644 (file)
index 1b9948b..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * 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.config.persist.storage.file;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Charsets;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.io.Files;
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.Charset;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import org.apache.commons.lang3.StringUtils;
-import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
-import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolderImpl;
-import org.opendaylight.controller.config.persist.api.Persister;
-import org.opendaylight.controller.config.persist.api.PropertiesProvider;
-import org.opendaylight.controller.config.persist.api.StorageAdapter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * StorageAdapter that stores configuration in a plain file.
- */
-public class FileStorageAdapter implements StorageAdapter, Persister {
-    private static final Logger logger = LoggerFactory.getLogger(FileStorageAdapter.class);
-
-
-    private static final Charset ENCODING = Charsets.UTF_8;
-
-    public static final String FILE_STORAGE_PROP = "fileStorage";
-    public static final String NUMBER_OF_BACKUPS = "numberOfBackups";
-
-
-    private static final String SEPARATOR_E_PURE = "//END OF CONFIG";
-    private static final String SEPARATOR_E = newLine(SEPARATOR_E_PURE);
-
-    private static final String SEPARATOR_M_PURE = "//END OF SNAPSHOT";
-    private static final String SEPARATOR_M = newLine(SEPARATOR_M_PURE);
-
-    private static final String SEPARATOR_S = newLine("//START OF CONFIG");
-
-    private static final String SEPARATOR_SL_PURE = "//START OF CONFIG-LAST";
-    private static final String SEPARATOR_SL = newLine(SEPARATOR_SL_PURE);
-
-    private static Integer numberOfStoredBackups;
-    private File storage;
-
-    @Override
-    public Persister instantiate(PropertiesProvider propertiesProvider) {
-        File storage = extractStorageFileFromProperties(propertiesProvider);
-        logger.debug("Using file {}", storage.getAbsolutePath());
-        // Create file if it does not exist
-        File parentFile = storage.getAbsoluteFile().getParentFile();
-        if (parentFile.exists() == false) {
-            logger.debug("Creating parent folders {}", parentFile);
-            parentFile.mkdirs();
-        }
-        if (storage.exists() == false) {
-            logger.debug("Storage file does not exist, creating empty file");
-            try {
-                boolean result = storage.createNewFile();
-                if (result == false)
-                    throw new RuntimeException("Unable to create storage file " + storage);
-            } catch (IOException e) {
-                throw new RuntimeException("Unable to create storage file " + storage, e);
-            }
-        }
-        if (numberOfStoredBackups == 0) {
-            throw new RuntimeException(NUMBER_OF_BACKUPS
-                    + " property should be either set to positive value, or ommited. Can not be set to 0.");
-        }
-        setFileStorage(storage);
-        return this;
-    }
-
-    @VisibleForTesting
-    void setFileStorage(File storage) {
-        this.storage = storage;
-    }
-
-    @VisibleForTesting
-    void setNumberOfBackups(Integer numberOfBackups) {
-        numberOfStoredBackups = numberOfBackups;
-    }
-
-    private static File extractStorageFileFromProperties(PropertiesProvider propertiesProvider) {
-        String fileStorageProperty = propertiesProvider.getProperty(FILE_STORAGE_PROP);
-        Preconditions.checkNotNull(fileStorageProperty, "Unable to find " + propertiesProvider.getFullKeyForReporting(FILE_STORAGE_PROP));
-        File result = new File(fileStorageProperty);
-        String numberOfBAckupsAsString = propertiesProvider.getProperty(NUMBER_OF_BACKUPS);
-        if (numberOfBAckupsAsString != null) {
-            numberOfStoredBackups = Integer.valueOf(numberOfBAckupsAsString);
-        } else {
-            numberOfStoredBackups = Integer.MAX_VALUE;
-        }
-        logger.trace("Property {} set to {}", NUMBER_OF_BACKUPS, numberOfStoredBackups);
-        return result;
-    }
-
-    private static String newLine(String string) {
-        return string + "\n";
-    }
-
-    @Override
-    public void persistConfig(ConfigSnapshotHolder holder) throws IOException {
-        Preconditions.checkNotNull(storage, "Storage file is null");
-
-        String content = Files.toString(storage, ENCODING);
-        if (numberOfStoredBackups == Integer.MAX_VALUE) {
-            resetLastConfig(content);
-            persistLastConfig(holder);
-        } else {
-            if (numberOfStoredBackups == 1) {
-                Files.write("", storage, ENCODING);
-                persistLastConfig(holder);
-            } else {
-                int count = StringUtils.countMatches(content, SEPARATOR_S);
-                if ((count + 1) < numberOfStoredBackups) {
-                    resetLastConfig(content);
-                    persistLastConfig(holder);
-                } else {
-                    String contentSubString = StringUtils.substringBefore(content, SEPARATOR_E);
-                    contentSubString = contentSubString.concat(SEPARATOR_E_PURE);
-                    content = StringUtils.substringAfter(content, contentSubString);
-                    resetLastConfig(content);
-                    persistLastConfig(holder);
-                }
-            }
-        }
-    }
-
-    private void resetLastConfig(String content) throws IOException {
-        content = content.replaceFirst(SEPARATOR_SL, SEPARATOR_S);
-        Files.write(content, storage, ENCODING);
-    }
-
-    private void persistLastConfig(ConfigSnapshotHolder holder) throws IOException {
-        Files.append(SEPARATOR_SL, storage, ENCODING);
-        String snapshotAsString = holder.getConfigSnapshot();
-        Files.append(newLine(snapshotAsString), storage, ENCODING);
-        Files.append(SEPARATOR_M, storage, ENCODING);
-        Files.append(toStringCaps(holder.getCapabilities()), storage, ENCODING);
-        Files.append(SEPARATOR_E, storage, ENCODING);
-    }
-
-    private CharSequence toStringCaps(Set<String> capabilities) {
-        StringBuilder b = new StringBuilder();
-        for (String capability : capabilities) {
-            b.append(newLine(capability));
-        }
-        return b.toString();
-    }
-
-    @Override
-    public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
-        Preconditions.checkNotNull(storage, "Storage file is null");
-
-        if (!storage.exists()) {
-            return Collections.emptyList();
-        }
-
-        final LineProcessor lineProcessor = new LineProcessor();
-        Files.readLines(storage, ENCODING, lineProcessor);
-
-        if (lineProcessor.getConfigSnapshot().isPresent() == false) {
-            return Collections.emptyList();
-        } else {
-            return Arrays.<ConfigSnapshotHolder>asList(new ConfigSnapshotHolderImpl(lineProcessor.getConfigSnapshot().get(),
-                    lineProcessor.getCapabilities(), storage.getAbsolutePath()));
-        }
-
-    }
-
-    private static final class LineProcessor implements com.google.common.io.LineProcessor<String> {
-
-        private boolean inLastConfig, inLastSnapshot;
-        private final StringBuffer snapshotBuffer = new StringBuffer();
-        private final SortedSet<String> caps = new TreeSet<>();
-
-        @Override
-        public String getResult() {
-            return null;
-        }
-
-        @Override
-        public boolean processLine(String line) throws IOException {
-            if (inLastConfig && line.equals(SEPARATOR_E_PURE)) {
-                inLastConfig = false;
-                return false;
-            }
-
-            if (inLastConfig && line.equals(SEPARATOR_M_PURE)) {
-                inLastSnapshot = false;
-                return true;
-            }
-
-            if (inLastConfig) {
-                if (inLastSnapshot) {
-                    snapshotBuffer.append(line);
-                    snapshotBuffer.append(System.lineSeparator());
-                } else {
-                    caps.add(line);
-                }
-            }
-
-            if (line.equals(SEPARATOR_SL_PURE)) {
-                inLastConfig = true;
-                inLastSnapshot = true;
-            }
-
-            return true;
-        }
-
-        Optional<String> getConfigSnapshot() {
-            final String xmlContent = snapshotBuffer.toString();
-            if (xmlContent.equals("")) {
-                return Optional.absent();
-            } else {
-                return Optional.of(xmlContent);
-            }
-        }
-
-        SortedSet<String> getCapabilities() {
-            return caps;
-        }
-
-    }
-
-    @Override
-    public void close() {
-
-    }
-
-    @Override
-    public String toString() {
-        return "FileStorageAdapter [storage=" + storage + "]";
-    }
-
-}
diff --git a/opendaylight/config/config-persister-file-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapterTest.java b/opendaylight/config/config-persister-file-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapterTest.java
deleted file mode 100644 (file)
index 0366dbc..0000000
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * 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.config.persist.storage.file;
-
-import com.google.common.base.Charsets;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Collections2;
-import java.io.File;
-import java.nio.file.Files;
-import java.util.Collection;
-import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mockito;
-import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
-import org.opendaylight.controller.config.persist.api.Persister;
-import org.opendaylight.controller.config.persist.test.PropertiesProviderTest;
-import static junit.framework.Assert.assertFalse;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
-public class FileStorageAdapterTest {
-
-    private static int i;
-    private File file;
-
-    @Before
-    public void setUp() throws Exception {
-        file = Files.createTempFile("testFilePersist", ".txt").toFile();
-        if (!file.exists())
-            return;
-        com.google.common.io.Files.write("", file, Charsets.UTF_8);
-        i = 1;
-    }
-
-
-    @Test
-    public void testFileAdapterAsPersister() throws Exception {
-        FileStorageAdapter storage = new FileStorageAdapter();
-        PropertiesProviderTest pp = new PropertiesProviderTest();
-        pp.addProperty("fileStorage",file.getPath());
-        pp.addProperty("numberOfBackups",Integer.toString(Integer.MAX_VALUE));
-
-        Persister configPersister = storage.instantiate(pp);
-        final ConfigSnapshotHolder holder = new ConfigSnapshotHolder() {
-            @Override
-            public String getConfigSnapshot() {
-                return createConfig();
-            }
-
-            @Override
-            public SortedSet<String> getCapabilities() {
-                return createCaps();
-            }
-        };
-        configPersister.persistConfig(holder);
-
-        configPersister.persistConfig(holder);
-
-        Collection<String> readLines = Collections2.filter(com.google.common.io.Files.readLines(file, Charsets.UTF_8),
-                new Predicate<String>() {
-
-                    @Override
-                    public boolean apply(String input) {
-                        if (input.equals(""))
-                            return false;
-                        return true;
-                    }
-                });
-        assertEquals(14, readLines.size());
-
-        List<ConfigSnapshotHolder> lastConf = storage.loadLastConfigs();
-        assertEquals(1, lastConf.size());
-        ConfigSnapshotHolder configSnapshotHolder = lastConf.get(0);
-        assertEquals("<config>2</config>",
-                configSnapshotHolder.getConfigSnapshot().replaceAll("\\s", ""));
-        assertEquals(createCaps(), configSnapshotHolder.getCapabilities());
-    }
-    @Test
-    public void testFileAdapter() throws Exception {
-        FileStorageAdapter storage = new FileStorageAdapter();
-        storage.setFileStorage(file);
-        storage.setNumberOfBackups(Integer.MAX_VALUE);
-        final ConfigSnapshotHolder holder = new ConfigSnapshotHolder() {
-            @Override
-            public String getConfigSnapshot() {
-                return createConfig();
-            }
-
-            @Override
-            public SortedSet<String> getCapabilities() {
-                return createCaps();
-            }
-        };
-        storage.persistConfig(holder);
-
-        storage.persistConfig(holder);
-
-        Collection<String> readLines = Collections2.filter(com.google.common.io.Files.readLines(file, Charsets.UTF_8),
-                new Predicate<String>() {
-
-                    @Override
-                    public boolean apply(String input) {
-                        if (input.equals(""))
-                            return false;
-                        return true;
-                    }
-                });
-        assertEquals(14, readLines.size());
-
-        List<ConfigSnapshotHolder> lastConf = storage.loadLastConfigs();
-        assertEquals(1, lastConf.size());
-        ConfigSnapshotHolder configSnapshotHolder = lastConf.get(0);
-        assertEquals("<config>2</config>",
-                configSnapshotHolder.getConfigSnapshot().replaceAll("\\s", ""));
-        assertEquals(createCaps(), configSnapshotHolder.getCapabilities());
-    }
-
-    private SortedSet<String> createCaps() {
-        SortedSet<String> caps = new TreeSet<>();
-
-        caps.add("cap1");
-        caps.add("cap2");
-        caps.add("capaaaa as dasfasdf s2");
-        return caps;
-    }
-
-    @Test
-    public void testFileAdapterOneBackup() throws Exception {
-        FileStorageAdapter storage = new FileStorageAdapter();
-        storage.setFileStorage(file);
-        storage.setNumberOfBackups(1);
-        final ConfigSnapshotHolder holder = new ConfigSnapshotHolder() {
-            @Override
-            public String getConfigSnapshot() {
-                return createConfig();
-            }
-
-            @Override
-            public SortedSet<String> getCapabilities() {
-                return createCaps();
-            }
-        };
-        storage.persistConfig(holder);
-
-        storage.persistConfig(holder);
-
-        Collection<String> readLines = Collections2.filter(com.google.common.io.Files.readLines(file, Charsets.UTF_8),
-                new Predicate<String>() {
-
-                    @Override
-                    public boolean apply(String input) {
-                        if (input.equals(""))
-                            return false;
-                        return true;
-                    }
-                });
-        assertEquals(7, readLines.size());
-
-        List<ConfigSnapshotHolder> lastConf = storage.loadLastConfigs();
-        assertEquals(1, lastConf.size());
-        ConfigSnapshotHolder configSnapshotHolder = lastConf.get(0);
-        assertEquals("<config>2</config>",
-                configSnapshotHolder.getConfigSnapshot().replaceAll("\\s", ""));
-    }
-
-    @Test
-    public void testFileAdapterOnlyTwoBackups() throws Exception {
-        FileStorageAdapter storage = new FileStorageAdapter();
-        storage.setFileStorage(file);
-        storage.setNumberOfBackups(2);
-        final ConfigSnapshotHolder holder = new ConfigSnapshotHolder() {
-            @Override
-            public String getConfigSnapshot() {
-                return createConfig();
-            }
-
-            @Override
-            public SortedSet<String> getCapabilities() {
-                return createCaps();
-            }
-        };
-        storage.persistConfig(holder);
-
-        storage.persistConfig(holder);
-        storage.persistConfig(holder);
-
-        Collection<String> readLines = Collections2.filter(com.google.common.io.Files.readLines(file, Charsets.UTF_8),
-                new Predicate<String>() {
-
-                    @Override
-                    public boolean apply(String input) {
-                        if (input.equals(""))
-                            return false;
-                        return true;
-                    }
-                });
-
-        assertEquals(14, readLines.size());
-
-        List<ConfigSnapshotHolder> lastConf = storage.loadLastConfigs();
-        assertEquals(1, lastConf.size());
-        ConfigSnapshotHolder configSnapshotHolder = lastConf.get(0);
-        assertEquals("<config>3</config>",
-                configSnapshotHolder.getConfigSnapshot().replaceAll("\\s", ""));
-        assertFalse(readLines.contains(holder.getConfigSnapshot()));
-    }
-
-    @Test
-    public void testNoLastConfig() throws Exception {
-        File file = Files.createTempFile("testFilePersist", ".txt").toFile();
-        if (!file.exists())
-            return;
-        FileStorageAdapter storage = new FileStorageAdapter();
-        storage.setFileStorage(file);
-
-        List<ConfigSnapshotHolder> elementOptional = storage.loadLastConfigs();
-        assertThat(elementOptional.size(), is(0));
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void testNoProperties() throws Exception {
-        FileStorageAdapter storage = new FileStorageAdapter();
-        storage.loadLastConfigs();
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void testNoProperties2() throws Exception {
-        FileStorageAdapter storage = new FileStorageAdapter();
-        storage.persistConfig(new ConfigSnapshotHolder() {
-            @Override
-            public String getConfigSnapshot() {
-                return Mockito.mock(String.class);
-            }
-
-            @Override
-            public SortedSet<String> getCapabilities() {
-                return new TreeSet<>();
-            }
-        } );
-    }
-
-    static String createConfig() {
-        return "<config>" + i++ + "</config>";
-    }
-
-}
index 6b55b6ad16ab85e6f15dd30eb96e2dbc9ed1ad44..d700075e950030e77856de1b84a2382878653055 100644 (file)
@@ -23,7 +23,6 @@
         <module>config-plugin-parent</module>
         <module>config-util</module>
         <module>config-persister-api</module>
-        <module>config-persister-file-adapter</module>
         <module>config-persister-file-xml-adapter</module>
         <module>yang-jmx-generator</module>
         <module>yang-jmx-generator-plugin</module>
@@ -35,9 +34,7 @@
         <module>netty-threadgroup-config</module>
         <module>netty-event-executor-config</module>
         <module>netty-timer-config</module>
-        <module>config-persister-directory-adapter</module>
         <module>config-persister-directory-xml-adapter</module>
-        <module>config-persister-directory-autodetect-adapter</module>
         <module>yang-test-plugin</module>
         <module>shutdown-api</module>
         <module>shutdown-impl</module>
index c9934356142b7951b9c17cbe28f584e1b2936aa0..776fb7a0f6816b7de10b41b087739a71f482bbd9 100644 (file)
 <?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>
-  <prerequisites>
-    <maven>3.0</maven>
-  </prerequisites>
-  <scm>
-    <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
-    <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
-    <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main</url>
-    <tag>HEAD</tag>
-  </scm>
-  <parent>
-    <groupId>org.opendaylight.controller</groupId>
-    <artifactId>commons.opendaylight</artifactId>
-    <version>1.4.2-SNAPSHOT</version>
-    <relativePath>../../commons/opendaylight</relativePath>
-  </parent>
+<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>
+    <prerequisites>
+        <maven>3.0</maven>
+    </prerequisites>
+    <scm>
+        <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+        <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+        <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main</url>
+        <tag>HEAD</tag>
+    </scm>
+    <parent>
+        <groupId>org.opendaylight.controller</groupId>
+        <artifactId>commons.opendaylight</artifactId>
+        <version>1.4.2-SNAPSHOT</version>
+        <relativePath>../../commons/opendaylight</relativePath>
+    </parent>
+    <artifactId>distribution.opendaylight</artifactId>
+    <version>0.1.2-SNAPSHOT</version>
+    <packaging>pom</packaging>
 
-  <profiles>
-    <profile>
-      <id>notduringrelease</id>
-      <activation>
-        <property>
-          <name>!DOINGRELEASE</name>
-        </property>
-      </activation>
-      <dependencies>
-        <!-- md-sal -->
+
+    <profiles>
+        <profile>
+            <id>notduringrelease</id>
+            <activation>
+                <property>
+                    <name>!DOINGRELEASE</name>
+                </property>
+            </activation>
+            <dependencies>
+                <!-- md-sal -->
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>sal-common</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>sal-common-util</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>sal-netconf-connector</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>sal-core-api</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>sal-broker-impl</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>sal-remote</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>sal-restconf-broker</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>sal-core-spi</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>sal-common-api</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>sal-common-impl</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>sal-binding-api</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>sal-binding-config</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>sal-binding-broker-impl</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>sal-compatibility</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>sal-connector-api</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>sal-rest-connector</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller.model</groupId>
+                    <artifactId>model-inventory</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller.model</groupId>
+                    <artifactId>model-flow-base</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller.model</groupId>
+                    <artifactId>model-flow-service</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller.model</groupId>
+                    <artifactId>model-flow-statistics</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller.model</groupId>
+                    <artifactId>model-flow-management</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller.md</groupId>
+                    <artifactId>inventory-manager</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller.md</groupId>
+                    <artifactId>forwardingrules-manager</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller.md</groupId>
+                    <artifactId>topology-lldp-discovery</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller.md</groupId>
+                    <artifactId>topology-manager</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller.model</groupId>
+                    <artifactId>model-topology</artifactId>
+                    <version>1.1-SNAPSHOT</version>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.yangtools.model</groupId>
+                    <artifactId>ietf-topology</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>sal-binding-util</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller.md</groupId>
+                    <artifactId>statistics-manager</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>concepts</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>protocol-framework</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.yangtools</groupId>
+                    <artifactId>concepts</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.yangtools</groupId>
+                    <artifactId>restconf-client-api</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.yangtools</groupId>
+                    <artifactId>restconf-client-impl</artifactId>
+                </dependency>
+
+                <!-- clustering -->
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>remoterpc-routingtable.implementation</artifactId>
+                    <version>${mdsal.version}</version>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>sal-remoterpc-connector</artifactId>
+                </dependency>
+
+                <!-- config-->
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>config-api</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>config-manager</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>yang-jmx-generator</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>logback-config</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>config-persister-api</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>config-persister-file-xml-adapter</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>config-persister-directory-xml-adapter</artifactId>
+                </dependency>
+
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>shutdown-api</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>shutdown-impl</artifactId>
+                </dependency>
+
+                <!-- Netconf -->
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>netconf-api</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>netconf-impl</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>netconf-util</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>netconf-client</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>netconf-mapping-api</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>netconf-ssh</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>config-netconf-connector</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>netconf-monitoring</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>${project.groupId}</groupId>
+                    <artifactId>ietf-netconf-monitoring</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>${project.groupId}</groupId>
+                    <artifactId>ietf-netconf-monitoring-extension</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>config-persister-impl</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.apache.servicemix.bundles</groupId>
+                    <artifactId>org.apache.servicemix.bundles.xerces</artifactId>
+                    <version>2.11.0_1</version>
+                </dependency>
+                <dependency>
+                    <groupId>org.eclipse.birt.runtime.3_7_1</groupId>
+                    <artifactId>org.apache.xml.resolver</artifactId>
+                    <version>1.2.0</version>
+                </dependency>
+                <dependency>
+                    <groupId>org.bouncycastle</groupId>
+                    <artifactId>bcprov-jdk15on</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.bouncycastle</groupId>
+                    <artifactId>bcpkix-jdk15on</artifactId>
+                </dependency>
+
+                <!-- threadpool -->
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>threadpool-config-api</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>netty-config-api</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>threadpool-config-impl</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>netty-threadgroup-config</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>netty-event-executor-config</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>netty-timer-config</artifactId>
+                </dependency>
+
+                <!-- toaster example I'm pretty sure we should trim -->
+                <dependency>
+                    <groupId>org.opendaylight.controller.samples</groupId>
+                    <artifactId>sample-toaster</artifactId>
+                    <version>${mdsal.version}</version>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller.samples</groupId>
+                    <artifactId>sample-toaster-consumer</artifactId>
+                    <version>${mdsal.version}</version>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller.samples</groupId>
+                    <artifactId>sample-toaster-provider</artifactId>
+                    <version>${mdsal.version}</version>
+                </dependency>
+                <!-- yangtools dependencies I'm pretty sure we can trim -->
+                <dependency>
+                    <groupId>org.opendaylight.yangtools</groupId>
+                    <artifactId>yang-binding</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.yangtools</groupId>
+                    <artifactId>binding-type-provider</artifactId>
+                    <version>${yangtools.version}</version>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.yangtools</groupId>
+                    <artifactId>binding-generator-spi</artifactId>
+                    <version>${yangtools.version}</version>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.yangtools</groupId>
+                    <artifactId>binding-generator-api</artifactId>
+                    <version>${yangtools.version}</version>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.yangtools</groupId>
+                    <artifactId>binding-generator-impl</artifactId>
+                    <version>${yangtools.version}</version>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.yangtools</groupId>
+                    <artifactId>binding-generator-util</artifactId>
+                    <version>${yangtools.version}</version>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.yangtools</groupId>
+                    <artifactId>binding-model-api</artifactId>
+                    <version>${yangtools.version}</version>
+                </dependency>
+                <dependency>
+                    <groupId>commons-lang</groupId>
+                    <artifactId>commons-lang</artifactId>
+                    <version>2.4</version>
+                </dependency>
+
+                <dependency>
+                    <groupId>org.opendaylight.yangtools.thirdparty</groupId>
+                    <artifactId>antlr4-runtime-osgi-nohead</artifactId>
+                    <version>4.0</version>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.yangtools.thirdparty</groupId>
+                    <artifactId>xtend-lib-osgi</artifactId>
+                    <version>2.4.3</version>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.yangtools</groupId>
+                    <artifactId>yang-parser-api</artifactId>
+                    <version>${yangtools.version}</version>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.yangtools</groupId>
+                    <artifactId>yang-model-util</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.yangtools</groupId>
+                    <artifactId>yang-parser-impl</artifactId>
+                    <version>${yangtools.version}</version>
+                </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-data-util</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.yangtools</groupId>
+                    <artifactId>yang-model-api</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.yangtools.model</groupId>
+                    <artifactId>yang-ext</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.controller.thirdparty</groupId>
+                    <artifactId>ganymed</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.zeromq</groupId>
+                    <artifactId>jeromq</artifactId>
+                    <version>0.3.1</version>
+                </dependency>
+                <!-- yang model dependencies -->
+                <dependency>
+                    <groupId>org.opendaylight.yangtools.model</groupId>
+                    <artifactId>ietf-inet-types</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.yangtools.model</groupId>
+                    <artifactId>ietf-yang-types</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.opendaylight.yangtools.model</groupId>
+                    <artifactId>opendaylight-l2-types</artifactId>
+                </dependency>
+            </dependencies>
+        </profile>
+        <profile>
+            <id>integrationtests</id>
+            <activation>
+                <activeByDefault>false</activeByDefault>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-dependency-plugin</artifactId>
+                        <version>2.8</version>
+                        <dependencies>
+                            <dependency>
+                                <groupId>org.opendaylight.controller</groupId>
+                                <artifactId>sanitytest</artifactId>
+                                <version>${controller.version}</version>
+                            </dependency>
+                        </dependencies>
+                        <executions>
+                            <execution>
+                                <id>copy</id>
+                                <phase>package</phase>
+                                <goals>
+                                    <goal>copy</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                        <configuration>
+                            <artifactItems>
+                                <artifactItem>
+                                    <groupId>org.opendaylight.controller</groupId>
+                                    <artifactId>sanitytest</artifactId>
+                                    <type>jar</type>
+                                </artifactItem>
+                            </artifactItems>
+                        </configuration>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>exec-maven-plugin</artifactId>
+                        <version>1.2.1</version>
+                        <executions>
+                            <execution>
+                                <id>sanity-test</id>
+                                <phase>package</phase>
+                                <goals>
+                                    <goal>exec</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                        <configuration>
+                            <executable>${java.home}/bin/java</executable>
+                            <arguments>
+                                <argument>-cp</argument>
+                                <argument>./target/dependency/*</argument>
+                                <argument>org.opendaylight.controller.distribution.Sanity</argument>
+                            </arguments>
+                            <environmentVariables>
+                                <JAVA_HOME>
+                                    ${java.home}
+                                </JAVA_HOME>
+                            </environmentVariables>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+            <dependencies>
+                <dependency>
+                    <groupId>org.opendaylight.controller</groupId>
+                    <artifactId>sanitytest</artifactId>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+
+
+    <dependencies>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>sal-common</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>forwarding.staticrouting</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>sal-common-util</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>clustering.services</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>sal-netconf-connector</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>clustering.services-implementation</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>sal-core-api</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>configuration</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>sal-broker-impl</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>configuration.implementation</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>sal-remote</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>routing.dijkstra_implementation</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>sal-restconf-broker</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>arphandler</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>sal-core-spi</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>hosttracker</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>sal-common-api</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>hosttracker.implementation</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>sal-common-impl</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>containermanager</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>sal-binding-api</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>containermanager.implementation</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>sal-binding-config</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>appauth</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>sal-binding-broker-impl</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>switchmanager</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>sal-compatibility</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>switchmanager.implementation</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>sal-connector-api</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>statisticsmanager</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>sal-rest-connector</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>statisticsmanager.implementation</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller.model</groupId>
-          <artifactId>model-inventory</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>topologymanager</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller.model</groupId>
-          <artifactId>model-flow-base</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>usermanager</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller.model</groupId>
-          <artifactId>model-flow-service</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>usermanager.implementation</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller.model</groupId>
-          <artifactId>model-flow-statistics</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>connectionmanager</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller.model</groupId>
-          <artifactId>model-flow-management</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>connectionmanager.implementation</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller.md</groupId>
-          <artifactId>inventory-manager</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>security</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller.md</groupId>
-          <artifactId>forwardingrules-manager</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>forwardingrulesmanager</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller.md</groupId>
-          <artifactId>topology-lldp-discovery</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>forwardingrulesmanager.implementation</artifactId>
         </dependency>
+
+        <!-- SAL bundles -->
         <dependency>
-          <groupId>org.opendaylight.controller.md</groupId>
-          <artifactId>topology-manager</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller.model</groupId>
-          <artifactId>model-topology</artifactId>
-          <version>1.1-SNAPSHOT</version>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal.implementation</artifactId>
         </dependency>
+
+        <!-- SAL Extension bundles -->
+
         <dependency>
-         <groupId>org.opendaylight.yangtools.model</groupId>
-         <artifactId>ietf-topology</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal.connection</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>sal-binding-util</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal.connection.implementation</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller.md</groupId>
-          <artifactId>statistics-manager</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal.networkconfiguration</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>concepts</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal.networkconfiguration.implementation</artifactId>
         </dependency>
+
+        <!--  Web bundles -->
+
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>protocol-framework</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>web</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.yangtools</groupId>
-          <artifactId>concepts</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>flows.web</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.yangtools</groupId>
-          <artifactId>restconf-client-api</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>devices.web</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.yangtools</groupId>
-          <artifactId>restconf-client-impl</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>troubleshoot.web</artifactId>
         </dependency>
-
-        <!-- clustering -->
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>remoterpc-routingtable.implementation</artifactId>
-          <version>${mdsal.version}</version>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>topology.web</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>sal-remoterpc-connector</artifactId>
-          <version>${mdsal.version}</version>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>osgi-brandfragment.web</artifactId>
         </dependency>
 
-        <!-- config-->
+        <!--  Neutron -->
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>config-api</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>networkconfig.neutron</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>config-manager</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>networkconfig.neutron.implementation</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>yang-jmx-generator</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>networkconfig.neutron.northbound</artifactId>
         </dependency>
+
+        <!-- Northbound bundles -->
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>logback-config</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>commons.northbound</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>config-persister-api</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>bundlescanner</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>config-persister-file-adapter</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>bundlescanner.implementation</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>config-persister-file-xml-adapter</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>topology.northbound</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>config-persister-directory-adapter</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>forwarding.staticrouting.northbound</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>config-persister-directory-xml-adapter</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>statistics.northbound</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>config-persister-directory-autodetect-adapter</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>flowprogrammer.northbound</artifactId>
         </dependency>
-
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>shutdown-api</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>hosttracker.northbound</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>shutdown-impl</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>subnets.northbound</artifactId>
         </dependency>
-
-       <!-- Netconf -->
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>netconf-api</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>switchmanager.northbound</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>netconf-impl</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>containermanager.northbound</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>netconf-util</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>networkconfig.bridgedomain.northbound</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>netconf-client</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>httpservice-bridge</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>netconf-mapping-api</artifactId>
+            <groupId>org.jolokia</groupId>
+            <artifactId>jolokia-osgi</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>netconf-ssh</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>jolokia-bridge</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>config-netconf-connector</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>connectionmanager.northbound</artifactId>
         </dependency>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>netconf-monitoring</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>usermanager.northbound</artifactId>
         </dependency>
         <dependency>
-          <groupId>${project.groupId}</groupId>
-          <artifactId>ietf-netconf-monitoring</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>controllermanager.northbound</artifactId>
         </dependency>
+        <!-- Debug and logging -->
+
         <dependency>
-          <groupId>${project.groupId}</groupId>
-          <artifactId>ietf-netconf-monitoring-extension</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>logging.bridge</artifactId>
         </dependency>
+
+        <!-- Southbound bundles -->
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>config-persister-impl</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>protocol_plugins.openflow</artifactId>
         </dependency>
+
+        <!-- samples -->
         <dependency>
-            <groupId>org.apache.servicemix.bundles</groupId>
-            <artifactId>org.apache.servicemix.bundles.xerces</artifactId>
-            <version>2.11.0_1</version>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>samples.loadbalancer</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.eclipse.birt.runtime.3_7_1</groupId>
-            <artifactId>org.apache.xml.resolver</artifactId>
-            <version>1.2.0</version>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>samples.loadbalancer.northbound</artifactId>
         </dependency>
-
-         <!-- threadpool -->
-          <dependency>
-              <groupId>org.opendaylight.controller</groupId>
-              <artifactId>threadpool-config-api</artifactId>
-          </dependency>
-          <dependency>
-              <groupId>org.opendaylight.controller</groupId>
-              <artifactId>netty-config-api</artifactId>
-          </dependency>
-          <dependency>
-              <groupId>org.opendaylight.controller</groupId>
-              <artifactId>threadpool-config-impl</artifactId>
-          </dependency>
-          <dependency>
-              <groupId>org.opendaylight.controller</groupId>
-              <artifactId>netty-threadgroup-config</artifactId>
-          </dependency>
-          <dependency>
-              <groupId>org.opendaylight.controller</groupId>
-              <artifactId>netty-event-executor-config</artifactId>
-          </dependency>
-          <dependency>
-              <groupId>org.opendaylight.controller</groupId>
-              <artifactId>netty-timer-config</artifactId>
-          </dependency>
-
-          <!-- toaster example I'm pretty sure we should trim -->
-         <dependency>
-          <groupId>org.opendaylight.controller.samples</groupId>
-          <artifactId>sample-toaster</artifactId>
-          <version>${mdsal.version}</version>
-         </dependency>
-         <dependency>
-          <groupId>org.opendaylight.controller.samples</groupId>
-          <artifactId>sample-toaster-consumer</artifactId>
-          <version>${mdsal.version}</version>
-         </dependency>
-         <dependency>
-          <groupId>org.opendaylight.controller.samples</groupId>
-          <artifactId>sample-toaster-provider</artifactId>
-          <version>${mdsal.version}</version>
-         </dependency>
-         <!-- yangtools dependencies I'm pretty sure we can trim -->
-         <dependency>
-          <groupId>org.opendaylight.yangtools</groupId>
-          <artifactId>yang-binding</artifactId>
-         </dependency>
-         <dependency>
-          <groupId>org.opendaylight.yangtools</groupId>
-          <artifactId>binding-type-provider</artifactId>
-          <version>${yangtools.version}</version>
-         </dependency>
-         <dependency>
-          <groupId>org.opendaylight.yangtools</groupId>
-          <artifactId>binding-generator-spi</artifactId>
-          <version>${yangtools.version}</version>
-         </dependency>
-         <dependency>
-          <groupId>org.opendaylight.yangtools</groupId>
-          <artifactId>binding-generator-api</artifactId>
-          <version>${yangtools.version}</version>
-         </dependency>
-         <dependency>
-          <groupId>org.opendaylight.yangtools</groupId>
-          <artifactId>binding-generator-impl</artifactId>
-          <version>${yangtools.version}</version>
-         </dependency>
-         <dependency>
-          <groupId>org.opendaylight.yangtools</groupId>
-          <artifactId>binding-generator-util</artifactId>
-          <version>${yangtools.version}</version>
-         </dependency>
-         <dependency>
-          <groupId>org.opendaylight.yangtools</groupId>
-          <artifactId>binding-model-api</artifactId>
-          <version>${yangtools.version}</version>
-         </dependency>
-         <dependency>
-           <groupId>commons-lang</groupId>
-           <artifactId>commons-lang</artifactId>
-           <version>2.4</version>
-         </dependency>
-
-         <dependency>
-          <groupId>org.opendaylight.yangtools.thirdparty</groupId>
-          <artifactId>antlr4-runtime-osgi-nohead</artifactId>
-          <version>4.0</version>
-         </dependency>
-         <dependency>
-          <groupId>org.opendaylight.yangtools.thirdparty</groupId>
-          <artifactId>xtend-lib-osgi</artifactId>
-          <version>2.4.3</version>
-         </dependency>
-         <dependency>
-          <groupId>org.opendaylight.yangtools</groupId>
-          <artifactId>yang-parser-api</artifactId>
-          <version>${yangtools.version}</version>
-         </dependency>
-         <dependency>
-          <groupId>org.opendaylight.yangtools</groupId>
-          <artifactId>yang-model-util</artifactId>
-         </dependency>
-         <dependency>
-          <groupId>org.opendaylight.yangtools</groupId>
-          <artifactId>yang-parser-impl</artifactId>
-          <version>${yangtools.version}</version>
-         </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-data-util</artifactId>
-         </dependency>
-         <dependency>
-          <groupId>org.opendaylight.yangtools</groupId>
-          <artifactId>yang-model-api</artifactId>
-         </dependency>
-         <dependency>
-          <groupId>org.opendaylight.yangtools.model</groupId>
-          <artifactId>yang-ext</artifactId>
-         </dependency>
-        <dependency>
-         <groupId>org.opendaylight.controller.thirdparty</groupId>
-         <artifactId>ganymed</artifactId>
-        </dependency>
-        <dependency>
-              <groupId>org.zeromq</groupId>
-          <artifactId>jeromq</artifactId>
-          <version>0.3.1</version>
-        </dependency>
-        <!-- yang model dependencies -->
-        <dependency>
-          <groupId>org.opendaylight.yangtools.model</groupId>
-          <artifactId>ietf-inet-types</artifactId>
-        </dependency>
-        <dependency>
-          <groupId>org.opendaylight.yangtools.model</groupId>
-          <artifactId>ietf-yang-types</artifactId>
-        </dependency>
-        <dependency>
-          <groupId>org.opendaylight.yangtools.model</groupId>
-          <artifactId>opendaylight-l2-types</artifactId>
-        </dependency>
-      </dependencies>
-    </profile>
-    <profile>
-      <id>integrationtests</id>
-      <activation>
-        <activeByDefault>false</activeByDefault>
-      </activation>
-      <build>
-        <plugins>
-          <plugin>
-            <groupId>org.apache.maven.plugins</groupId>
-            <artifactId>maven-dependency-plugin</artifactId>
-            <version>2.8</version>
-            <dependencies>
-              <dependency>
-                <groupId>org.opendaylight.controller</groupId>
-                <artifactId>sanitytest</artifactId>
-                <version>${controller.version}</version>
-             </dependency>
-            </dependencies>
-            <executions>
-              <execution>
-                <id>copy</id>
-                <phase>package</phase>
-                <goals>
-                  <goal>copy</goal>
-                </goals>
-              </execution>
-            </executions>
-            <configuration>
-              <artifactItems>
-                <artifactItem>
-                  <groupId>org.opendaylight.controller</groupId>
-                  <artifactId>sanitytest</artifactId>
-                  <type>jar</type>
-                </artifactItem>
-              </artifactItems>
-            </configuration>
-          </plugin>
-          <plugin>
-            <groupId>org.codehaus.mojo</groupId>
-            <artifactId>exec-maven-plugin</artifactId>
-            <version>1.2.1</version>
-            <executions>
-              <execution>
-                <id>sanity-test</id>
-                <phase>package</phase>
-                <goals>
-                  <goal>exec</goal>
-                </goals>
-              </execution>
-            </executions>
-            <configuration>
-              <executable>${java.home}/bin/java</executable>
-              <arguments>
-                <argument>-cp</argument>
-                <argument>./target/dependency/*</argument>
-                <argument>org.opendaylight.controller.distribution.Sanity</argument>
-              </arguments>
-              <environmentVariables>
-                <JAVA_HOME>
-                  ${java.home}
-                </JAVA_HOME>
-              </environmentVariables>
-            </configuration>
-          </plugin>
-        </plugins>
-      </build>
-      <dependencies>
         <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>sanitytest</artifactId>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>samples.simpleforwarding</artifactId>
         </dependency>
-      </dependencies>
-    </profile>
-  </profiles>
-
-  <artifactId>distribution.opendaylight</artifactId>
-  <version>0.1.2-SNAPSHOT</version>
-  <packaging>pom</packaging>
-
-  <dependencies>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>forwarding.staticrouting</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>clustering.services</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>clustering.services-implementation</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>configuration</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>configuration.implementation</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>routing.dijkstra_implementation</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>arphandler</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>hosttracker</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>hosttracker.implementation</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>containermanager</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>containermanager.implementation</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>appauth</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>switchmanager</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>switchmanager.implementation</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>statisticsmanager</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>statisticsmanager.implementation</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>topologymanager</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>usermanager</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>usermanager.implementation</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>connectionmanager</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>connectionmanager.implementation</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>security</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>forwardingrulesmanager</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>forwardingrulesmanager.implementation</artifactId>
-    </dependency>
-
-    <!-- SAL bundles -->
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>sal</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>sal.implementation</artifactId>
-    </dependency>
-
-  <!-- SAL Extension bundles -->
-
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>sal.connection</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>sal.connection.implementation</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>sal.networkconfiguration</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>sal.networkconfiguration.implementation</artifactId>
-    </dependency>
-
-    <!--  Web bundles -->
-
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>web</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>flows.web</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>devices.web</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>troubleshoot.web</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>topology.web</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>osgi-brandfragment.web</artifactId>
-    </dependency>
 
-    <!--  Neutron -->
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>networkconfig.neutron</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>networkconfig.neutron.implementation</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>networkconfig.neutron.northbound</artifactId>
-    </dependency>
-
-    <!-- Northbound bundles -->
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>commons.northbound</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>bundlescanner</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>bundlescanner.implementation</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>topology.northbound</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>forwarding.staticrouting.northbound</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>statistics.northbound</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>flowprogrammer.northbound</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>hosttracker.northbound</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>subnets.northbound</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>switchmanager.northbound</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>containermanager.northbound</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>networkconfig.bridgedomain.northbound</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>httpservice-bridge</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.jolokia</groupId>
-      <artifactId>jolokia-osgi</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>jolokia-bridge</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>connectionmanager.northbound</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>usermanager.northbound</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>controllermanager.northbound</artifactId>
-    </dependency>
-    <!-- Debug and logging -->
-
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>logging.bridge</artifactId>
-    </dependency>
-
-    <!-- Southbound bundles -->
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>protocol_plugins.openflow</artifactId>
-    </dependency>
-
-    <!-- samples -->
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>samples.loadbalancer</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>samples.loadbalancer.northbound</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>samples.simpleforwarding</artifactId>
-    </dependency>
-
-    <!-- Third party depedencies -->
-    <dependency>
-      <groupId>org.slf4j</groupId>
-      <artifactId>jcl-over-slf4j</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.slf4j</groupId>
-      <artifactId>slf4j-api</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.slf4j</groupId>
-      <artifactId>log4j-over-slf4j</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>ch.qos.logback</groupId>
-      <artifactId>logback-core</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>ch.qos.logback</groupId>
-      <artifactId>logback-classic</artifactId>
-    </dependency>
+        <!-- Third party depedencies -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>jcl-over-slf4j</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>log4j-over-slf4j</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+        </dependency>
 
-    <dependency>
-        <groupId>com.fasterxml.jackson.core</groupId>
-        <artifactId>jackson-databind</artifactId>
-    </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
 
-    <dependency>
-       <groupId>com.fasterxml.jackson.core</groupId>
-       <artifactId>jackson-annotations</artifactId>
-    </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+        </dependency>
 
-    <dependency>
-       <groupId>com.fasterxml.jackson.core</groupId>
-       <artifactId>jackson-core</artifactId>
-    </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+        </dependency>
 
-    <dependency>
-      <groupId>com.fasterxml.jackson.jaxrs</groupId>
-      <artifactId>jackson-jaxrs-json-provider</artifactId>
-    </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.jaxrs</groupId>
+            <artifactId>jackson-jaxrs-json-provider</artifactId>
+        </dependency>
 
-      <dependency>
-         <groupId>com.fasterxml.jackson.jaxrs</groupId>
-         <artifactId>jackson-jaxrs-base</artifactId>
-      </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.jaxrs</groupId>
+            <artifactId>jackson-jaxrs-base</artifactId>
+        </dependency>
 
-    <dependency>
-      <groupId>com.fasterxml.jackson.module</groupId>
-      <artifactId>jackson-module-jaxb-annotations</artifactId>
-    </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.module</groupId>
+            <artifactId>jackson-module-jaxb-annotations</artifactId>
+        </dependency>
 
-    <dependency>
-      <groupId>org.codehaus.jettison</groupId>
-      <artifactId>jettison</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>com.google.guava</groupId>
-      <artifactId>guava</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.javassist</groupId>
-      <artifactId>javassist</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>commons-io</groupId>
-      <artifactId>commons-io</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>commons-codec</groupId>
-      <artifactId>commons-codec</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>commons-fileupload</groupId>
-      <artifactId>commons-fileupload</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>commons-net</groupId>
-      <artifactId>commons-net</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>equinoxSDK381</groupId>
-      <artifactId>javax.servlet</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>equinoxSDK381</groupId>
-      <artifactId>javax.servlet.jsp</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>equinoxSDK381</groupId>
-      <artifactId>org.eclipse.equinox.ds</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>equinoxSDK381</groupId>
-      <artifactId>org.eclipse.equinox.util</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>equinoxSDK381</groupId>
-      <artifactId>org.eclipse.osgi.services</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>equinoxSDK381</groupId>
-      <artifactId>org.eclipse.osgi</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>equinoxSDK381</groupId>
-      <artifactId>org.apache.felix.gogo.command</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>equinoxSDK381</groupId>
-      <artifactId>org.apache.felix.gogo.runtime</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>equinoxSDK381</groupId>
-      <artifactId>org.apache.felix.gogo.shell</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>equinoxSDK381</groupId>
-      <artifactId>org.eclipse.equinox.cm</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>equinoxSDK381</groupId>
-      <artifactId>org.eclipse.equinox.console</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>equinoxSDK381</groupId>
-      <artifactId>org.eclipse.equinox.launcher</artifactId>
-    </dependency>
-    <!-- Gemini Web -->
-    <dependency>
-      <groupId>geminiweb</groupId>
-      <artifactId>org.eclipse.gemini.web.core</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>geminiweb</groupId>
-      <artifactId>org.eclipse.gemini.web.extender</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>geminiweb</groupId>
-      <artifactId>org.eclipse.gemini.web.tomcat</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>geminiweb</groupId>
-      <artifactId>org.eclipse.virgo.kernel.equinox.extensions</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>geminiweb</groupId>
-      <artifactId>org.eclipse.virgo.util.common</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>geminiweb</groupId>
-      <artifactId>org.eclipse.virgo.util.io</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>geminiweb</groupId>
-      <artifactId>org.eclipse.virgo.util.math</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>geminiweb</groupId>
-      <artifactId>org.eclipse.virgo.util.osgi</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>geminiweb</groupId>
-      <artifactId>org.eclipse.virgo.util.osgi.manifest</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>geminiweb</groupId>
-      <artifactId>org.eclipse.virgo.util.parser.manifest</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.felix</groupId>
-      <artifactId>org.apache.felix.dependencymanager</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.felix</groupId>
-      <artifactId>org.apache.felix.dependencymanager.shell</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>com.google.code.gson</groupId>
-      <artifactId>gson</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.jboss.spec.javax.transaction</groupId>
-      <artifactId>jboss-transaction-api_1.1_spec</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.felix</groupId>
-      <artifactId>org.apache.felix.fileinstall</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.commons</groupId>
-      <artifactId>commons-lang3</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>virgomirror</groupId>
-      <artifactId>org.eclipse.jdt.core.compiler.batch</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>eclipselink</groupId>
-      <artifactId>javax.persistence</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>orbit</groupId>
-      <artifactId>javax.activation</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>orbit</groupId>
-      <artifactId>javax.annotation</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>orbit</groupId>
-      <artifactId>javax.ejb</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>orbit</groupId>
-      <artifactId>javax.el</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>orbit</groupId>
-      <artifactId>javax.mail.glassfish</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>orbit</groupId>
-      <artifactId>javax.xml.rpc</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>orbit</groupId>
-      <artifactId>org.apache.catalina</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>orbit</groupId>
-      <artifactId>org.apache.catalina.ha</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>orbit</groupId>
-      <artifactId>org.apache.catalina.tribes</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>orbit</groupId>
-      <artifactId>org.apache.coyote</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>orbit</groupId>
-      <artifactId>org.apache.el</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>orbit</groupId>
-      <artifactId>org.apache.jasper</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>orbit</groupId>
-      <artifactId>org.apache.juli.extras</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>orbit</groupId>
-      <artifactId>org.apache.tomcat.api</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>orbit</groupId>
-      <artifactId>org.apache.tomcat.util</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>orbit</groupId>
-      <artifactId>javax.servlet.jsp.jstl</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>orbit</groupId>
-      <artifactId>javax.servlet.jsp.jstl.impl</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>eclipselink</groupId>
-      <artifactId>javax.resource</artifactId>
-    </dependency>
-    <!-- Add Pax Exam -->
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>org.springframework.asm</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>org.springframework.aop</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>org.springframework.context</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>org.springframework.context.support</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>org.springframework.core</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>org.springframework.beans</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>org.springframework.expression</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>org.springframework.web</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.aopalliance</groupId>
-      <artifactId>com.springsource.org.aopalliance</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>org.springframework.web.servlet</artifactId>
-    </dependency>
-    <!-- Spring security -->
-    <dependency>
-      <groupId>org.springframework.security</groupId>
-      <artifactId>spring-security-config</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework.security</groupId>
-      <artifactId>spring-security-core</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework.security</groupId>
-      <artifactId>spring-security-web</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework.security</groupId>
-      <artifactId>spring-security-taglibs</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>org.springframework.transaction</artifactId>
-    </dependency>
-    <!-- Visual VM hook -->
-    <dependency>
-      <groupId>org.ow2.chameleon.management</groupId>
-      <artifactId>chameleon-mbeans</artifactId>
-    </dependency>
-    <!-- Jersey for JAXRS -->
-    <dependency>
-      <groupId>com.sun.jersey</groupId>
-      <artifactId>jersey-core</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>com.sun.jersey</groupId>
-      <artifactId>jersey-server</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>com.sun.jersey</groupId>
-      <artifactId>jersey-client</artifactId>
-    </dependency>
+        <dependency>
+            <groupId>org.codehaus.jettison</groupId>
+            <artifactId>jettison</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.javassist</groupId>
+            <artifactId>javassist</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-codec</groupId>
+            <artifactId>commons-codec</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-fileupload</groupId>
+            <artifactId>commons-fileupload</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-net</groupId>
+            <artifactId>commons-net</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>equinoxSDK381</groupId>
+            <artifactId>javax.servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>equinoxSDK381</groupId>
+            <artifactId>javax.servlet.jsp</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>equinoxSDK381</groupId>
+            <artifactId>org.eclipse.equinox.ds</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>equinoxSDK381</groupId>
+            <artifactId>org.eclipse.equinox.util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>equinoxSDK381</groupId>
+            <artifactId>org.eclipse.osgi.services</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>equinoxSDK381</groupId>
+            <artifactId>org.eclipse.osgi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>equinoxSDK381</groupId>
+            <artifactId>org.apache.felix.gogo.command</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>equinoxSDK381</groupId>
+            <artifactId>org.apache.felix.gogo.runtime</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>equinoxSDK381</groupId>
+            <artifactId>org.apache.felix.gogo.shell</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>equinoxSDK381</groupId>
+            <artifactId>org.eclipse.equinox.cm</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>equinoxSDK381</groupId>
+            <artifactId>org.eclipse.equinox.console</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>equinoxSDK381</groupId>
+            <artifactId>org.eclipse.equinox.launcher</artifactId>
+        </dependency>
+        <!-- Gemini Web -->
+        <dependency>
+            <groupId>geminiweb</groupId>
+            <artifactId>org.eclipse.gemini.web.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>geminiweb</groupId>
+            <artifactId>org.eclipse.gemini.web.extender</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>geminiweb</groupId>
+            <artifactId>org.eclipse.gemini.web.tomcat</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>geminiweb</groupId>
+            <artifactId>org.eclipse.virgo.kernel.equinox.extensions</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>geminiweb</groupId>
+            <artifactId>org.eclipse.virgo.util.common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>geminiweb</groupId>
+            <artifactId>org.eclipse.virgo.util.io</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>geminiweb</groupId>
+            <artifactId>org.eclipse.virgo.util.math</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>geminiweb</groupId>
+            <artifactId>org.eclipse.virgo.util.osgi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>geminiweb</groupId>
+            <artifactId>org.eclipse.virgo.util.osgi.manifest</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>geminiweb</groupId>
+            <artifactId>org.eclipse.virgo.util.parser.manifest</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.dependencymanager</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.dependencymanager.shell</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.spec.javax.transaction</groupId>
+            <artifactId>jboss-transaction-api_1.1_spec</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.fileinstall</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>virgomirror</groupId>
+            <artifactId>org.eclipse.jdt.core.compiler.batch</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>eclipselink</groupId>
+            <artifactId>javax.persistence</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>orbit</groupId>
+            <artifactId>javax.activation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>orbit</groupId>
+            <artifactId>javax.annotation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>orbit</groupId>
+            <artifactId>javax.ejb</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>orbit</groupId>
+            <artifactId>javax.el</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>orbit</groupId>
+            <artifactId>javax.mail.glassfish</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>orbit</groupId>
+            <artifactId>javax.xml.rpc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>orbit</groupId>
+            <artifactId>org.apache.catalina</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>orbit</groupId>
+            <artifactId>org.apache.catalina.ha</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>orbit</groupId>
+            <artifactId>org.apache.catalina.tribes</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>orbit</groupId>
+            <artifactId>org.apache.coyote</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>orbit</groupId>
+            <artifactId>org.apache.el</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>orbit</groupId>
+            <artifactId>org.apache.jasper</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>orbit</groupId>
+            <artifactId>org.apache.juli.extras</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>orbit</groupId>
+            <artifactId>org.apache.tomcat.api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>orbit</groupId>
+            <artifactId>org.apache.tomcat.util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>orbit</groupId>
+            <artifactId>javax.servlet.jsp.jstl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>orbit</groupId>
+            <artifactId>javax.servlet.jsp.jstl.impl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>eclipselink</groupId>
+            <artifactId>javax.resource</artifactId>
+        </dependency>
+        <!-- Add Pax Exam -->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>org.springframework.asm</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>org.springframework.aop</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>org.springframework.context</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>org.springframework.context.support</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>org.springframework.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>org.springframework.beans</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>org.springframework.expression</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>org.springframework.web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.aopalliance</groupId>
+            <artifactId>com.springsource.org.aopalliance</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>org.springframework.web.servlet</artifactId>
+        </dependency>
+        <!-- Spring security -->
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-config</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-taglibs</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>org.springframework.transaction</artifactId>
+        </dependency>
+        <!-- Visual VM hook -->
+        <dependency>
+            <groupId>org.ow2.chameleon.management</groupId>
+            <artifactId>chameleon-mbeans</artifactId>
+        </dependency>
+        <!-- Jersey for JAXRS -->
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-server</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-client</artifactId>
+        </dependency>
 
-    <dependency>
-      <groupId>org.ow2.asm</groupId>
-      <artifactId>asm-all</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.persistence</groupId>
-      <artifactId>org.eclipse.persistence.moxy</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.persistence</groupId>
-      <artifactId>org.eclipse.persistence.core</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.persistence</groupId>
-      <artifactId>org.eclipse.persistence.antlr</artifactId>
-    </dependency>
+        <dependency>
+            <groupId>org.ow2.asm</groupId>
+            <artifactId>asm-all</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.persistence</groupId>
+            <artifactId>org.eclipse.persistence.moxy</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.persistence</groupId>
+            <artifactId>org.eclipse.persistence.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.persistence</groupId>
+            <artifactId>org.eclipse.persistence.antlr</artifactId>
+        </dependency>
 
-    <dependency>
-      <groupId>org.eclipse.equinox.http</groupId>
-      <artifactId>servlet</artifactId>
-    </dependency>
-    <!-- felix webconsole -->
-    <dependency>
-      <groupId>org.apache.felix</groupId>
-      <artifactId>org.apache.felix.webconsole</artifactId>
-      <classifier>all</classifier>
-    </dependency>
+        <dependency>
+            <groupId>org.eclipse.equinox.http</groupId>
+            <artifactId>servlet</artifactId>
+        </dependency>
+        <!-- felix webconsole -->
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.webconsole</artifactId>
+            <classifier>all</classifier>
+        </dependency>
 
-    <!-- Third parties from opendaylight released -->
-    <dependency>
-      <groupId>org.opendaylight.controller.thirdparty</groupId>
-      <artifactId>net.sf.jung2</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller.thirdparty</groupId>
-      <artifactId>org.openflow.openflowj</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller.thirdparty</groupId>
-      <artifactId>com.sun.jersey.jersey-servlet</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller.thirdparty</groupId>
-      <artifactId>org.apache.catalina.filters.CorsFilter</artifactId>
-    </dependency>
+        <!-- Third parties from opendaylight released -->
+        <dependency>
+            <groupId>org.opendaylight.controller.thirdparty</groupId>
+            <artifactId>net.sf.jung2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller.thirdparty</groupId>
+            <artifactId>org.openflow.openflowj</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller.thirdparty</groupId>
+            <artifactId>com.sun.jersey.jersey-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller.thirdparty</groupId>
+            <artifactId>org.apache.catalina.filters.CorsFilter</artifactId>
+        </dependency>
 
-    <!--Netty-->
-    <dependency>
-      <groupId>io.netty</groupId>
-      <artifactId>netty-handler</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>io.netty</groupId>
-      <artifactId>netty-codec</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>io.netty</groupId>
-      <artifactId>netty-buffer</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>io.netty</groupId>
-      <artifactId>netty-transport</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>io.netty</groupId>
-      <artifactId>netty-common</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>io.netty</groupId>
-      <artifactId>netty-codec-http</artifactId>
-    </dependency>
+        <!--Netty-->
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-handler</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-codec</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-buffer</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-transport</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-codec-http</artifactId>
+        </dependency>
 
-    <!-- testing dependencies I'm pretty sure we should trim -->
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>clustering.test</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>commons.httpclient</artifactId>
-    </dependency>
-  </dependencies>
+        <!-- testing dependencies I'm pretty sure we should trim -->
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>clustering.test</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>commons.httpclient</artifactId>
+        </dependency>
+    </dependencies>
 
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.codehaus.mojo</groupId>
-        <artifactId>buildnumber-maven-plugin</artifactId>
-        <version>1.2</version>
-        <executions>
-          <execution>
-            <phase>validate</phase>
-            <goals>
-              <goal>create</goal>
-            </goals>
-          </execution>
-        </executions>
-        <configuration>
-          <doCheck>false</doCheck>
-          <doUpdate>false</doUpdate>
-          <revisionOnScmFailure>VersionUnknown</revisionOnScmFailure>
-        </configuration>
-      </plugin>
-      <plugin>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <version>2.3</version>
-        <executions>
-          <execution>
-            <id>distro-assembly</id>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <configuration>
-              <descriptors>
-                <descriptor>src/assemble/bin.xml</descriptor>
-              </descriptors>
-              <finalName>${project.artifactId}</finalName>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>buildnumber-maven-plugin</artifactId>
+                <version>1.2</version>
+                <executions>
+                    <execution>
+                        <phase>validate</phase>
+                        <goals>
+                            <goal>create</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <doCheck>false</doCheck>
+                    <doUpdate>false</doUpdate>
+                    <revisionOnScmFailure>VersionUnknown</revisionOnScmFailure>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <version>2.3</version>
+                <executions>
+                    <execution>
+                        <id>distro-assembly</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                        <configuration>
+                            <descriptors>
+                                <descriptor>src/assemble/bin.xml</descriptor>
+                            </descriptors>
+                            <finalName>${project.artifactId}</finalName>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
 
-        <!--Make checkstyle ignore initial xml configuration files by overriding its configuration from parent-->
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-checkstyle-plugin</artifactId>
-        <version>${checkstyle.version}</version>
-        <configuration>
-            <excludes>**\/target\/,**\/bin\/,**\/target-ide\/,**\/configuration\/initial\/</excludes>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
+            <!--Make checkstyle ignore initial xml configuration files by overriding its configuration from parent-->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-checkstyle-plugin</artifactId>
+                <version>${checkstyle.version}</version>
+                <configuration>
+                    <excludes>**\/target\/,**\/bin\/,**\/target-ide\/,**\/configuration\/initial\/</excludes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
 </project>
index 6f8878bee950b87a3b234f00e19570a8b6256cfc..0ff4c9a83c37a4182c9d26061b80d3d8b0e9a636 100644 (file)
@@ -69,6 +69,7 @@
       </directory>
       <excludes>
         <exclude>version.properties</exclude>
+        <exclude>configuration/config.ini</exclude>
       </excludes>
       <outputDirectory>
         opendaylight/
       <outputDirectory>opendaylight</outputDirectory>
       <filtered>true</filtered>
     </file>
+      <file>
+        <source>src/main/resources/configuration/config.ini</source>
+        <outputDirectory>opendaylight/configuration</outputDirectory>
+        <filtered>true</filtered>
+      </file>
   </files>
 </assembly>
diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/RSA.pk b/opendaylight/distribution/opendaylight/src/main/resources/configuration/RSA.pk
deleted file mode 100644 (file)
index c0266c7..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEogIBAAKCAQEAuC9hbEacpewvylI0mwFwjy3Wou2hpr/ncN9BBiFDSaG5yW2k
-3Oy+SCAcFCL+ZKWb6cc6Ch4gUeCwyEHRojZguuhliKtak9YQf6qbvpPLe00842Lx
-iqNAGurMpzizCDsGFq8ChaAkBZQI3TvcHuPoSUWSMJ+K8xHpRyUdVr6g2yEjezKJ
-sTXBtWaeCCh6YUafFujuDJk7fvYcPW7Je5KRBBStIKvxcMW0zB+7eq04deTHwGbJ
-gGjKWilQ72hsDDP3Hbp5CJMAYg1r4GlCmFx3KyHRGztgWgNgaD7nNpKCkTLjtmA6
-b4x7TA+jrzZ6Af2z5TMrI4dv5w1SrxHaZ+ziLQIDAQABAoIBAHTndeGgq/rQf8De
-Do+4CTaHtK0zQSAyu/azbXUzlZ7drKuCEVs8VMY4wzmwwGEnkF+A2YDkgEUX5X0l
-8aYQ97KKoS9u+43MGCrAIhyDeGrpqlT1TzRcy+qJz53v6gq2U/X/3QztiQ+VV078
-mIluxNgE9XYxPaNsYfGLSCTv1+9c8y/hjGVX2kwFK+u4ut0ZZETggNa8UxfaHVDS
-fIJQX9Gm3J3GSUV30fDGMBIUW6ESLc2L8b7u8Mp9TRP39ZeQSuEUjBe8MYKv0Rel
-oEpjZvcnniMTpFbLpndBYn7/AoIiEBvtCN8faVTuRRcvvLcsRm09IctzKQYnMh6M
-6PLKV+ECgYEA8HFRYaKHUzxpzE/fyon82GQbzqFFY0/bbWrfWICMfNbIgshJUie6
-FmH5iUFMfeqaT7v557HFM0GB9FeIeSbvd88YmiBAcRopZ3DfMkDH+DT73yJ+/TKG
-2nrQtdhyuTIs4bwHqeS2BBJYs7PK9R2rratF3l34Tf7mjlvyOgygHdUCgYEAxBo2
-8hEBlAVNcNb1hTYUxe1w1B6675/mFlmw98Xmj9dRYfICXNhahs8tX3/lsBEd+vBu
-fI0oyHaff8m5bPgGzD1ZMybfeROujNrgxaKVk7Ef0FDRRCop4bm18OroFlFAt9l8
-wMp++ToACbdvQvL/mjWMPYlIxhB/YxHswICZZvkCgYAexxKYwdo6sGAGlC7cWT9x
-X5cjowcjyEQZRHXkeUgCbufpvcOM7aLnXJE5nY8yCwbHsBM0MlBA2GDPKylAANjk
-aDEJAZneIHAuWodngl1Wi0m2bU7+ECqs6s2uiU9eH2sZVh1RBQK7kLGkBx6ys6KX
-L3ZZGYRAT6GplWFzRsx0JQKBgCeVlxPD5QqpC1nEumi6YvUVGdpnnZpzL3HBhxxs
-wT612wKnZFyze4qM1X7ahVXGDsQxtkvD/sCAWW/lG13orw6ZL6FIroF1PJ3ILOkY
-CZN3hJF7TtKwpCWhZB2OfWzL2AGEkE8mUP0j/Q/5DCd6f6f0OSvOw3bfq6cm3iB5
-lP2ZAoGAXsRN5TZTX4AQ2xTlrDQ8A5XgcvyWQpJOmEXMTyHV7VaJVzmNWFVAvndK
-5UIq8ALDwB2t7vjmMUW6euvIwqtXiop7G79UOb3e3NhzeyWFGQyBLqCRznGaXQTT
-dlFy73xhukZMhFnj006bjKCYvOPnwuGl3+0fuWil5Rq3jOuY5c8=
------END RSA PRIVATE KEY-----
index 0d223b8df2b3381cc26d248a6467da3efb68f1eb..9df653cf94f00af395839711d6a347e2b1b04a0d 100644 (file)
@@ -8,7 +8,7 @@ osgi.bundles=\
     reference\:file\:../lib/slf4j-api-1.7.2.jar@1:start,\
     reference\:file\:../lib/logback-classic-1.0.9.jar@1:start,\
     reference\:file\:../lib/logback-core-1.0.9.jar@1:start,\
-    reference\:file\:../lib/logging.bridge-0.4.2-SNAPSHOT@1:start,\
+    reference\:file\:../lib/logging.bridge-${controller.version}@1:start,\
     reference\:file\:../lib/jersey-core-1.17.jar@2:start,\
     reference\:file\:../lib/jersey-server-1.17.jar@2:start
 
@@ -26,22 +26,12 @@ netconf.ssh.pk.path = ./configuration/RSA.pk
 
 netconf.config.persister.active=1,2
 # read startup configuration
-#netconf.config.persister.1.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.DirectoryStorageAdapter
-#netconf.config.persister.1.properties.directoryStorage=configuration/initial/
-#netconf.config.persister.1.readonly=true
-
-netconf.config.persister.1.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.autodetect.AutodetectDirectoryStorageAdapter
+netconf.config.persister.1.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.xml.XmlDirectoryStorageAdapter
 netconf.config.persister.1.properties.directoryStorage=configuration/initial/
+# include only xml files, files with other extensions will be skipped, multiple extensions are permitted e.g. netconf.config.persister.1.properties.includeExtensions=xml,cfg,config
+netconf.config.persister.1.properties.includeExtensions=xml
 netconf.config.persister.1.readonly=true
 
-#netconf.config.persister.3.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.xml.XmlDirectoryStorageAdapter
-#netconf.config.persister.3.properties.directoryStorage=configuration/initialXml/
-#netconf.config.persister.3.readonly=true
-
-#netconf.config.persister.4.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter
-#netconf.config.persister.4.properties.fileStorage=configuration/current/controller.currentconfig.txt
-#netconf.config.persister.4.properties.numberOfBackups=1
-
 netconf.config.persister.2.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.xml.XmlFileStorageAdapter
 netconf.config.persister.2.properties.fileStorage=configuration/current/controller.currentconfig.xml
 netconf.config.persister.2.properties.numberOfBackups=1
@@ -49,7 +39,7 @@ netconf.config.persister.2.properties.numberOfBackups=1
 # Set Default start level for framework
 osgi.bundles.defaultStartLevel=4
 # Extra packages to import from the boot class loader
-org.osgi.framework.system.packages.extra=sun.reflect,sun.reflect.misc,sun.misc
+org.osgi.framework.system.packages.extra=sun.reflect,sun.reflect.misc,sun.misc,sun.nio.ch
 # This is not Eclipse App
 eclipse.ignoreApp=true
 # Don't shutdown equinox if the eclipse App has ended,
index de9ac232e871a6f018839ba9c15974a34fbc29b3..619ab06d8d3c3da7ca181949b4fff7ddd7596e6e 100644 (file)
@@ -1,26 +1,62 @@
-<snapshot>
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<snapshot>
     <configuration>
-    
         <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
             <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
                 <module>
                     <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:schema-service-singleton</type>
                     <name>yang-schema-service</name>
                 </module>
+                <!-- To enable use of new in-memory datastore and new implementations
+                     of data brokers, comment out all parts of this
+                     xml which are marked with DATA-BROKER and uncomment all parts
+                     of this xml which are marked with NEW-DATA-BROKER
+                -->
+                <!-- DATA-BROKER start-->
                 <module>
                     <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:hash-map-data-store</type>
                     <name>hash-map-data-store</name>
                 </module>
+                <!-- DATA BROKER end -->
+                <!-- NEW-DATA-BROKER start -->
+                <!--
+                <module>
+                    <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-inmemory-data-broker</type>
+                    <name>async-data-broker</name>
+                    <schema-service>
+                        <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+                        <name>yang-schema-service</name>
+                    </schema-service>
+                </module>
+                -->
+                <!-- NEW-DATA-BROKER end -->
                 <module>
                     <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
                     <name>dom-broker</name>
+                    <!-- DATA-BROKER start -->
                     <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
                         <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
                         <!-- to switch to the clustered data store, comment out the hash-map-data-store <name> and uncomment the cluster-data-store one -->
                         <name>hash-map-data-store</name>
                         <!-- <name>cluster-data-store</name> -->
                     </data-store>
+                    <!-- DATA-BROKER end -->
+                    <!-- NEW-DATA-BROKER start -->
+                    <!--
+                    <async-data-broker>
+                        <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type>
+                        <name>async-data-broker</name>
+                    </async-data-broker>
+                    -->
+                    <!-- NEW-DATA-BROKER end -->
                 </module>
                 <module>
                     <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
@@ -42,6 +78,7 @@
                     <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
                     <name>binding-notification-broker</name>
                 </module>
+                <!-- DATA-BROKER start -->
                 <module>
                     <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-broker</type>
                     <name>binding-data-broker</name>
                         <name>runtime-mapping-singleton</name>
                     </mapping-service>
                 </module>
-                
+                <!-- DATA-BROKER end -->
+                <!-- NEW-DATA-BROKER start -->
+                <!--
+                <module>
+                    <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-compatible-broker</type>
+                    <name>binding-data-broker</name>
+                    <dom-async-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+                        <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+                        <name>dom-broker</name>
+                    </dom-async-broker>
+                    <binding-mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+                        <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
+                        <name>runtime-mapping-singleton</name>
+                    </binding-mapping-service>
+                </module>
+                -->
+                <!-- NEW-DATA-BROKER end -->
             </modules>
-            
             <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
-                       <service>
-                               <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
-                               <instance>
-                                       <name>yang-schema-service</name>
-                                       <provider>/modules/module[type='schema-service-singleton'][name='yang-schema-service']</provider>
-                               </instance>
-                       </service>
-                       <service>
-                               <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
-                               <instance>
-                                       <name>binding-notification-broker</name>
-                                       <provider>/modules/module[type='binding-notification-broker'][name='binding-notification-broker']</provider>
-                               </instance>
-                       </service>
-                       <service>
-                               <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
-                               <instance>
-                                       <name>hash-map-data-store</name>
-                                       <provider>/modules/module[type='hash-map-data-store'][name='hash-map-data-store']</provider>
-                               </instance>
-                       </service>
-                       <service>
-                               <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
-                               <instance>
-                                       <name>binding-osgi-broker</name>
-                                       <provider>/modules/module[type='binding-broker-impl'][name='binding-broker-impl']</provider>
-                               </instance>
-                       </service>
-                       <service>
-                               <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-rpc-registry</type>
-                               <instance>
-                                       <name>binding-rpc-broker</name>
-                                       <provider>/modules/module[type='binding-broker-impl'][name='binding-broker-impl']</provider>
-                               </instance>
-                       </service>
-                       <service>
-                               <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
-                               <instance>
-                                       <name>runtime-mapping-singleton</name>
-                                       <provider>/modules/module[type='runtime-generated-mapping'][name='runtime-mapping-singleton']</provider>
-                               </instance>
-                       </service>
-                       <service>
-                       <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
-                               <instance>
-                                       <name>dom-broker</name>
-                                       <provider>/modules/module[type='dom-broker-impl'][name='dom-broker']</provider>
-                               </instance>
-                       </service>
-                       <service>
-                               <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
-                               <instance>
-                                       <name>binding-data-broker</name>
-                                       <provider>/modules/module[type='binding-data-broker'][name='binding-data-broker']</provider>
-                               </instance>
-                       </service>
+                <service>
+                    <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+                    <instance>
+                        <name>yang-schema-service</name>
+                        <provider>/modules/module[type='schema-service-singleton'][name='yang-schema-service']</provider>
+                    </instance>
+                </service>
+                <service>
+                    <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
+                    <instance>
+                        <name>binding-notification-broker</name>
+                        <provider>/modules/module[type='binding-notification-broker'][name='binding-notification-broker']</provider>
+                    </instance>
+                </service>
+                <!-- DATA-BROKER start -->
+                <service>
+                    <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
+                    <instance>
+                        <name>hash-map-data-store</name>
+                        <provider>/modules/module[type='hash-map-data-store'][name='hash-map-data-store']</provider>
+                    </instance>
+                </service>
+                <!-- DATA-BROKER end -->
+                <!-- NEW-DATA-BROKER start -->
+                <!--
+                <service>
+                    <type  xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type>
+                    <instance>
+                        <name>async-data-broker</name>
+                        <provider>/modules/module[type='dom-inmemory-data-broker'][name='async-data-broker']</provider>
+                    </instance>
+                </service>
+                -->
+                <!-- NEW-DATA-BROKER end -->
+                <service>
+                    <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
+                    <instance>
+                        <name>binding-osgi-broker</name>
+                        <provider>/modules/module[type='binding-broker-impl'][name='binding-broker-impl']</provider>
+                    </instance>
+                </service>
+                <service>
+                    <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-rpc-registry</type>
+                    <instance>
+                        <name>binding-rpc-broker</name>
+                        <provider>/modules/module[type='binding-broker-impl'][name='binding-broker-impl']</provider>
+                    </instance>
+                </service>
+                <service>
+                    <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
+                    <instance>
+                        <name>runtime-mapping-singleton</name>
+                        <provider>/modules/module[type='runtime-generated-mapping'][name='runtime-mapping-singleton']</provider>
+                    </instance>
+                </service>
+                <service>
+                <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+                    <instance>
+                        <name>dom-broker</name>
+                        <provider>/modules/module[type='dom-broker-impl'][name='dom-broker']</provider>
+                    </instance>
+                </service>
+
+                <service>
+                    <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
+                    <instance>
+                        <name>binding-data-broker</name>
+                        <!--  DATA-BROKER start -->
+                        <provider>/modules/module[type='binding-data-broker'][name='binding-data-broker']</provider>
+                        <!--  DATA-BROKER end -->
+                        <!-- NEW-DATA-BROKER start -->
+                        <!--
+                        <provider>/modules/module[type='binding-data-compatible-broker'][name='binding-data-broker']</provider>
+                        -->
+                        <!--  NEW-DATA-BROKER end -->
+                    </instance>
+                </service>
 
             </services>
         </data>
-
     </configuration>
-    
     <required-capabilities>
         <capability>urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor?module=netty-event-executor&amp;revision=2013-11-12</capability>
         <capability>urn:opendaylight:params:xml:ns:yang:controller:threadpool?module=threadpool&amp;revision=2013-04-09</capability>
         <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&amp;revision=2013-10-28</capability>
         <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&amp;revision=2013-10-28</capability>
     </required-capabilities>
-
 </snapshot>
-
index 3ad08ca207b4a49a125274969f1f07f14d43990e..c89cfc1b9e8914148bc58fc6f3a375baca026e53 100644 (file)
@@ -28,6 +28,7 @@ import org.opendaylight.controller.sal.action.Controller;
 import org.opendaylight.controller.sal.action.Drop;
 import org.opendaylight.controller.sal.action.Enqueue;
 import org.opendaylight.controller.sal.action.Flood;
+import org.opendaylight.controller.sal.action.FloodAll;
 import org.opendaylight.controller.sal.action.HwPath;
 import org.opendaylight.controller.sal.action.Loopback;
 import org.opendaylight.controller.sal.action.Output;
@@ -791,6 +792,27 @@ public class FlowConfig extends ConfigurationObject implements Serializable {
                     continue;
                 }
 
+                sstr = Pattern.compile(ActionType.ENQUEUE + "=(.*)").matcher(actiongrp);
+                if (sstr.matches()) {
+                    for (String t : sstr.group(1).split(",")) {
+                        if (t != null) {
+                            String parts[] = t.split(":");
+                            String nc = String.format("%s|%s@%s", node.getType(), parts[0], node.toString());
+                            if (NodeConnector.fromString(nc) == null) {
+                                return new Status(StatusCode.BADREQUEST, String.format("Enqueue port is not valid"));
+                            }
+                            if (parts.length > 1) {
+                                try {
+                                    Integer.parseInt(parts[1]);
+                                } catch (NumberFormatException e) {
+                                    return new Status(StatusCode.BADREQUEST, String.format("Enqueue %s is not in the range 0 - 2147483647", parts[1]));
+                                }
+                            }
+                        }
+                    }
+                    continue;
+                }
+
                 sstr = Pattern.compile(ActionType.SET_VLAN_PCP.toString() + "=(.*)").matcher(actiongrp);
                 if (sstr.matches()) {
                     if ((sstr.group(1) != null) && !isVlanPriorityValid(sstr.group(1))) {
@@ -1019,6 +1041,12 @@ public class FlowConfig extends ConfigurationObject implements Serializable {
                     continue;
                 }
 
+                sstr = Pattern.compile(ActionType.FLOOD_ALL.toString()).matcher(actiongrp);
+                if (sstr.matches()) {
+                    actionList.add(new FloodAll());
+                    continue;
+                }
+
                 sstr = Pattern.compile(ActionType.SW_PATH.toString()).matcher(actiongrp);
                 if (sstr.matches()) {
                     actionList.add(new SwPath());
index 734a392bc121f7b33a20408908a3281193aec2aa..ce49b599e18f90833f1850bc23bfb74b457aabaf 100644 (file)
@@ -105,7 +105,7 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw
     static final String ACTIVE_HOST_CACHE = "hosttracker.ActiveHosts";
     static final String INACTIVE_HOST_CACHE = "hosttracker.InactiveHosts";
     private static final Logger logger = LoggerFactory.getLogger(HostTracker.class);
-    protected final Set<IHostFinder> hostFinder = new CopyOnWriteArraySet<IHostFinder>();;
+    protected final Set<IHostFinder> hostFinders = new CopyOnWriteArraySet<IHostFinder>();
     protected ConcurrentMap<IHostId, HostNodeConnector> hostsDB;
     /*
      * Following is a list of hosts which have been requested by NB APIs to be
@@ -256,16 +256,12 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw
     }
 
     public void setArpHandler(IHostFinder hostFinder) {
-        if (this.hostFinder != null) {
-            this.hostFinder.add(hostFinder);
-        }
+        this.hostFinders.add(hostFinder);
     }
 
     public void unsetArpHandler(IHostFinder hostFinder) {
-        if (this.hostFinder != null) {
-            logger.debug("Arp Handler Service removed!");
-            this.hostFinder.remove(hostFinder);
-        }
+        logger.debug("Arp Handler Service removed!");
+        this.hostFinders.remove(hostFinder);
     }
 
     public void setTopologyManager(ITopologyManager s) {
@@ -352,8 +348,8 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw
          * already handles the null return
          */
 
-        if (hostFinder == null) {
-            logger.debug("Exiting hostFind, null hostFinder");
+        if (hostFinders.isEmpty()) {
+            logger.debug("No available host finders, exiting hostFind()");
             return null;
         }
 
@@ -369,7 +365,7 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw
         logger.debug("hostFind(): Host Not Found for IP: {}, Inititated Host Discovery ...", id);
 
         /* host is not found, initiate a discovery */
-        for (IHostFinder hf : hostFinder) {
+        for (IHostFinder hf : hostFinders) {
             InetAddress addr = decodeIPFromId(id);
             hf.find(addr);
         }
@@ -936,12 +932,12 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw
             for (Entry<IHostId, ARPPending> entry : failedARPReqList.entrySet()) {
                 ARPPending arphost;
                 arphost = entry.getValue();
-                if (hostFinder == null) {
-                    logger.warn("ARPHandler Services are not available on subnet addition");
+                if (hostFinders.isEmpty()) {
+                    logger.debug("ARPHandler Services are not available on subnet addition");
                     continue;
                 }
                 logger.debug("Sending the ARP from FailedARPReqList fors IP: {}", decodeIPFromId(arphost.getHostId()));
-                for (IHostFinder hf : hostFinder) {
+                for (IHostFinder hf : hostFinders) {
                     hf.find(decodeIPFromId(arphost.getHostId()));
                 }
             }
@@ -977,11 +973,11 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw
                          * next one. Before sending the ARP, check if ARPHandler
                          * is available or not
                          */
-                        if (hostFinder == null) {
+                        if (hostFinders.isEmpty()) {
                             logger.warn("ARPHandler Services are not available for Outstanding ARPs");
                             continue;
                         }
-                        for (IHostFinder hf : hostFinder) {
+                        for (IHostFinder hf : hostFinders) {
                             hf.find(decodeIPFromId(arphost.getHostId()));
                         }
                         arphost.sent_count++;
@@ -1063,7 +1059,7 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw
                                             HexEncode.bytesToHexString(host.getDataLayerAddressBytes()) });
                         }
                         host.setArpSendCountDown(arp_cntdown);
-                        if (hostFinder == null) {
+                        if (hostFinders.isEmpty()) {
                             /*
                              * If hostfinder is not available, then can't send
                              * the probe. However, continue the age out the
@@ -1073,7 +1069,7 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw
                             logger.trace("ARPHandler is not avaialable, can't send the probe");
                             continue;
                         }
-                        for (IHostFinder hf : hostFinder) {
+                        for (IHostFinder hf : hostFinders) {
                             hf.probe(host);
                         }
                     }
@@ -1417,7 +1413,7 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw
         for (Entry<IHostId, ARPPending> entry : failedARPReqList.entrySet()) {
             arphost = entry.getValue();
             logger.trace("Sending the ARP from FailedARPReqList fors IP: {}", arphost.getHostId());
-            if (hostFinder == null) {
+            if (hostFinders.isEmpty()) {
                 logger.warn("ARPHandler is not available at interface  up");
                 logger.warn("Since this event is missed, host(s) connected to interface {} may not be discovered",
                         nodeConnector);
@@ -1430,7 +1426,7 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw
                 byte[] dataLayerAddress = NetUtils.getBroadcastMACAddr();
                 host = new HostNodeConnector(dataLayerAddress, decodeIPFromId(arphost.getHostId()), nodeConnector,
                         (short) 0);
-                for (IHostFinder hf : hostFinder) {
+                for (IHostFinder hf : hostFinders) {
                     hf.probe(host);
                 }
             } catch (ConstructionException e) {
index 74e7d1afe74910bcc55a3a1e1a3b9c52a546060c..01e5ae0372b06d6bf54138dd9bb9df948f1d551b 100644 (file)
         </dependency>
         <dependency>
             <groupId>org.opendaylight.controller</groupId>
-            <artifactId>config-persister-file-adapter</artifactId>
+            <artifactId>config-persister-file-xml-adapter</artifactId>
             <version>${config.version}</version>
         </dependency>
         <dependency>
             <artifactId>config-persister-impl</artifactId>
             <version>${config.version}</version>
         </dependency>
-        <dependency>
-            <groupId>org.opendaylight.controller</groupId>
-            <artifactId>config-persister-file-adapter</artifactId>
-            <version>${config.version}</version>
-        </dependency>
         <dependency>
             <groupId>org.opendaylight.controller</groupId>
             <artifactId>netconf-impl</artifactId>
index b90c76378d3b53e015979eb04f42089c42629b49..2d4e7d2c604e49be1dad6a226a28de536371478e 100644 (file)
@@ -34,7 +34,7 @@ import org.opendaylight.yangtools.yang.common.RpcError
 
 class FRMRuntimeDataProvider implements RuntimeDataProvider, DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject> {
 
-    static val FLOWS_PATH = InstanceIdentifier.builder().node(Flows).toInstance;
+    static val FLOWS_PATH = InstanceIdentifier.builder(Flows).toInstance;
 
     @Property
     var DataProviderService dataService;
index 0b77ea7989cb24f8b11eae172f8dd1ed775e03ac..a91cef6136c5cf98c633416d05441dcba70b8c39 100644 (file)
@@ -27,7 +27,7 @@ public class SampleConsumer {
 
         DataModificationTransaction transaction = dataService.beginTransaction();
         Flow flow = createSampleFlow("foo", null);
-        InstanceIdentifier<Flow> path = InstanceIdentifier.builder().node(Flows.class).node(Flow.class, flow.getKey())
+        InstanceIdentifier<Flow> path = InstanceIdentifier.builder(Flows.class).child(Flow.class, flow.getKey())
                 .toInstance();
         transaction.putConfigurationData(path, flow);
 
index 72508416a6c00475ddb7c51c54eb9f67b63bf630..20d375fc78eb3b7e26d425c9614c448eb3ec0d13 100644 (file)
@@ -7,35 +7,34 @@
  */
 package org.opendaylight.controller.md.compatibility.switchmanager
 
-import org.opendaylight.controller.switchmanager.ISwitchManager
-import org.opendaylight.controller.sal.core.NodeConnector
-import org.opendaylight.controller.sal.core.Property
-import java.util.List
-import org.opendaylight.controller.sal.core.Node
 import java.net.InetAddress
+import java.net.NetworkInterface
+import java.net.SocketException
+import java.util.ArrayList
+import java.util.Collections
+import java.util.HashSet
+import java.util.List
+import java.util.Map
 import org.opendaylight.controller.sal.binding.api.data.DataBrokerService
-import static extension org.opendaylight.controller.sal.compatibility.NodeMapping.*
-import org.opendaylight.controller.sal.core.Description
-import org.opendaylight.controller.sal.core.Tier
 import org.opendaylight.controller.sal.core.Bandwidth
+import org.opendaylight.controller.sal.core.Description
 import org.opendaylight.controller.sal.core.ForwardingMode
 import org.opendaylight.controller.sal.core.MacAddress
-
-import org.slf4j.LoggerFactory
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
-import org.opendaylight.yangtools.yang.binding.DataObject
-import java.net.NetworkInterface
-import java.net.SocketException
-import java.util.Collections
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes
-import java.util.ArrayList
+import org.opendaylight.controller.sal.core.Node
+import org.opendaylight.controller.sal.core.NodeConnector
+import org.opendaylight.controller.sal.core.Property
+import org.opendaylight.controller.sal.core.Tier
+import org.opendaylight.controller.switchmanager.ISwitchManager
 import org.opendaylight.controller.switchmanager.Switch
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId
-import java.util.Map
-import java.util.HashSet
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortState
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey
+import org.opendaylight.yangtools.yang.binding.DataObject
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
+import org.slf4j.LoggerFactory
+
+import static extension org.opendaylight.controller.sal.compatibility.NodeMapping.*
 
 class CompatibleSwitchManager extends ConfigurableSwitchManager implements ISwitchManager {
 
@@ -118,7 +117,7 @@ class CompatibleSwitchManager extends ConfigurableSwitchManager implements ISwit
     }
 
     override getNetworkDevices() {
-        val path = InstanceIdentifier.builder().node(Nodes).toInstance;
+        val path = InstanceIdentifier.builder(Nodes).toInstance;
         val data = dataService.readOperationalData(path) as Nodes;
         val ret = new ArrayList<Switch>();
         for (node : data.node) {
@@ -173,7 +172,7 @@ class CompatibleSwitchManager extends ConfigurableSwitchManager implements ISwit
     }
 
     override getNodes() {
-        val path = InstanceIdentifier.builder().node(Nodes).toInstance;
+        val path = InstanceIdentifier.builder(Nodes).toInstance;
         val data = dataService.readOperationalData(path) as Nodes;
         val ret = new HashSet<Node>();
         for (node : data.node) {
index 37d4577e0a977b293d254ee6916d34d7ad2a5170..6ebe20b84a383d1a9a68c93eb7a6f05c4cea258a 100644 (file)
@@ -7,32 +7,27 @@
  */
 package org.opendaylight.controller.md.compatibility.topology
 
+import java.util.ArrayList
+import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider
+import org.opendaylight.controller.sal.core.Edge
+import org.opendaylight.controller.sal.core.NodeConnector
 import org.opendaylight.controller.switchmanager.ISwitchManager
 import org.opendaylight.controller.topologymanager.ITopologyManager
-import org.opendaylight.controller.md.sal.common.api.data.DataReader
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
-import org.opendaylight.yangtools.yang.binding.DataObject
-import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.DestinationBuilder
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.SourceBuilder
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology
-import org.opendaylight.controller.md.compatibility.topology.TopologyMapping
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkBuilder
-
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder
-import java.util.ArrayList
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey
-import org.opendaylight.controller.sal.core.NodeConnector
-import org.opendaylight.controller.sal.topology.TopoEdgeUpdate
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointBuilder
-import org.opendaylight.controller.sal.core.Edge
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.SourceBuilder
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.DestinationBuilder
+import org.opendaylight.yangtools.yang.binding.DataObject
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
 
 class TopologyReader implements RuntimeDataProvider {
 
@@ -53,7 +48,7 @@ class TopologyReader implements RuntimeDataProvider {
 
     new() {
         _topologyKey = new TopologyKey(new TopologyId("compatibility:ad-sal"));
-        _topologyPath = InstanceIdentifier.builder().node(NetworkTopology).child(Topology, topologyKey).toInstance;
+        _topologyPath = InstanceIdentifier.builder(NetworkTopology).child(Topology, topologyKey).toInstance;
         _mapping = new TopologyMapping(topologyKey, topologyPath);
     }
 
index 905b8380086af34dac70cb3d6c5d4b3a89359742..aa238a8a8e3a86f572097ec7c8617725620dcf11 100644 (file)
@@ -16,12 +16,8 @@ import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.
 import java.util.Map
 import org.opendaylight.controller.sal.core.Edge
 import java.util.Set
-import java.util.List
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node
-import java.util.Collections
-import com.google.common.collect.FluentIterable
 import java.util.HashSet
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId
 import org.opendaylight.controller.sal.compatibility.NodeMapping
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
@@ -30,7 +26,6 @@ import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.Destination
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey
 import java.util.HashMap
 
@@ -42,11 +37,11 @@ class AdSalTopologyMapping {
 
     new(TopologyKey topology) {
         topologyMapping = topology;
-        _topologyPath = InstanceIdentifier.builder.node(NetworkTopology).child(Topology, topology).toInstance;
+        _topologyPath = InstanceIdentifier.builder(NetworkTopology).child(Topology, topology).toInstance;
     }
 
     def InstanceIdentifier<TerminationPoint> toTerminationPoint(NodeConnector connector) {
-        InstanceIdentifier.builder(topologyPath).node(Node).child(TerminationPoint, connector.toTerminationPointKey()).toInstance;
+        InstanceIdentifier.builder(topologyPath).child(Node).child(TerminationPoint, connector.toTerminationPointKey()).toInstance;
     }
 
     def Map<Edge, Set<org.opendaylight.controller.sal.core.Property>> toEdgePropertiesMap(Iterable<Link> links) {
index a59c2c1636514dba415dc933774ec4c6d16353cf..00ce31233578f4774c7d854442092408df542489 100644 (file)
@@ -251,9 +251,9 @@ package class SalCompatibilityProvider implements BindingAwareProvider {
         topology.dataService = session.getSALService(DataProviderService)
         tpProvider.dataService = session.getSALService(DataProviderService)
 
-        inventory.start();
+        inventory.startAdapter();
 
-        tpProvider.start();
+        tpProvider.startAdapter();
 
         subscribe.registerNotificationListener(dataPacket)
     }
index 0c211fd0aa432547071654bda94233d69e2496c0..f54defdf14498960fc73726b541f6775f0110362 100644 (file)
@@ -122,12 +122,15 @@ class InventoryAndReadAdapter implements IPluginInReadService,
     private final Lock nodeToNodeConnectorsLock = new ReentrantLock();
 
 
-    def start(){
+    def startAdapter(){
         inventoryNotificationProvider.dataProviderService = dataProviderService;
         inventoryNotificationProvider.inventoryPublisher = inventoryPublisher;
         // inventoryNotificationProvider.start();
     }
 
+    def start(){
+    }
+
     def setInventoryPublisher(IPluginOutInventoryService listener){
         inventoryPublisher.add(listener);
     }
index 2dce505d2860a06c42114cbec8c415fbc602da00..29904220d710933c71320f0069e78aef03e2046d 100644 (file)
@@ -44,8 +44,7 @@ public class InventoryMapping {
   public static NodeRef toNodeRef(final Node node) {
     final NodeId nodeId = new NodeId(InventoryMapping.toNodeId(node));
     final NodeKey nodeKey = new NodeKey(nodeId);
-    final InstanceIdentifierBuilder<? extends Object> builder = InstanceIdentifier.builder();
-    final InstanceIdentifierBuilder<Nodes> nodes = builder.<Nodes>node(Nodes.class);
+    final InstanceIdentifierBuilder<Nodes> nodes = InstanceIdentifier.builder(Nodes.class);
     final InstanceIdentifierBuilder<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node> child =
             nodes.<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node, NodeKey>child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, nodeKey);
     final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node> path = child.toInstance();
index fe545b09888c6153b225f8467bab4456134f4cfe..6a9712b1c6a41a093bd905996606290b4e33289e 100644 (file)
@@ -132,8 +132,10 @@ public class MDFlowMapping {
 
         val sourceActions = sourceFlow.actions;
         val targetActions = new ArrayList<Action>();
+        var int action = 0;
         for (sourceAction : sourceActions) {
-            targetActions.add(sourceAction.toAction());
+            targetActions.add(sourceAction.toAction(action));
+            action = action + 1;
         }
         instructions = targetActions.toApplyInstruction();
         match = sourceFlow.match.toMatch();
@@ -154,8 +156,10 @@ public class MDFlowMapping {
     
        val sourceActions = sourceFlow.actions;
        val targetActions = new ArrayList<Action>();
+       var int action = 0;
        for (sourceAction : sourceActions) {
-           targetActions.add(sourceAction.toAction());
+           targetActions.add(sourceAction.toAction(action));
+           action = action+ 1;
        }
        instructions = targetActions.toApplyInstruction();
        match = sourceFlow.match.toMatch();
@@ -199,44 +203,44 @@ public class MDFlowMapping {
         return it.build();
     }
 
-    public static dispatch def toAction(Controller sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(Controller sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
         actionBuilder.action = new ControllerActionCaseBuilder().setControllerAction(new ControllerActionBuilder().build()).build();
         return actionBuilder.build();
     }
 
-    public static dispatch def toAction(Drop sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(Drop sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
         actionBuilder.action = new DropActionCaseBuilder().setDropAction(new DropActionBuilder().build()).build();
         return actionBuilder.build();
     }
 
-    public static dispatch def toAction(Flood sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(Flood sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
         actionBuilder.action = new FloodActionCaseBuilder().setFloodAction(new FloodActionBuilder().build).build();
         return actionBuilder.build();
     }
 
-    public static dispatch def toAction(FloodAll sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(FloodAll sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
         actionBuilder.action = new FloodAllActionCaseBuilder().setFloodAllAction(new FloodAllActionBuilder().build()).build();
         return actionBuilder.build();
     }
 
-    public static dispatch def toAction(HwPath sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(HwPath sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
         actionBuilder.action = new HwPathActionCaseBuilder().setHwPathAction(new HwPathActionBuilder().build()).build();
         return actionBuilder.build();
     }
 
-    public static dispatch def toAction(Loopback sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(Loopback sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
         actionBuilder.action = new LoopbackActionCaseBuilder().setLoopbackAction(new LoopbackActionBuilder().build()).build();
         return actionBuilder.build();
     }
 
-    public static dispatch def toAction(Output sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(Output sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
         val it = new OutputActionBuilder();
         outputNodeConnector = sourceAction.port.toUri;
         actionBuilder.action = new OutputActionCaseBuilder().setOutputAction(it.build()).build();
@@ -244,14 +248,14 @@ public class MDFlowMapping {
 
     }
 
-    public static dispatch def toAction(PopVlan sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(PopVlan sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
         actionBuilder.action = new PopVlanActionCaseBuilder().build();
         return actionBuilder.build();
     }
 
-    public static dispatch def toAction(PushVlan sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(PushVlan sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
         val it = new PushVlanActionBuilder();
         cfi = new VlanCfi(sourceAction.cfi);
         vlanId = new VlanId(sourceAction.vlanId);
@@ -261,32 +265,32 @@ public class MDFlowMapping {
         return actionBuilder.build();
     }
 
-    public static dispatch def toAction(SetDlDst sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(SetDlDst sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
         val it = new SetDlDstActionBuilder();
         address = sourceAction.dlAddress.toMacAddress();
         actionBuilder.action = new SetDlDstActionCaseBuilder().setSetDlDstAction(it.build()).build;
         return actionBuilder.build();
     }
 
-    public static dispatch def toAction(SetDlSrc sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(SetDlSrc sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
         val it = new SetDlSrcActionBuilder();
         address = sourceAction.dlAddress.toMacAddress;
         actionBuilder.action = new SetDlSrcActionCaseBuilder().setSetDlSrcAction(it.build()).build;
         return actionBuilder.build();
     }
 
-    public static dispatch def toAction(SetDlType sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(SetDlType sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
         val it = new SetDlTypeActionBuilder();
         dlType = new EtherType(sourceAction.dlType as long);
         actionBuilder.action = new SetDlTypeActionCaseBuilder().setSetDlTypeAction(it.build()).build();
         return actionBuilder.build();
     }
 
-    public static dispatch def toAction(SetNextHop sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(SetNextHop sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
         val it = new SetNextHopActionBuilder();
         val inetAddress = sourceAction.address;
         address = inetAddress.toInetAddress;
@@ -294,8 +298,8 @@ public class MDFlowMapping {
         return actionBuilder.build();
     }
 
-    public static dispatch def toAction(SetNwDst sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(SetNwDst sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
         val it = new SetNwDstActionBuilder();
         val inetAddress = sourceAction.address;
         address = inetAddress.toInetAddress;
@@ -303,8 +307,8 @@ public class MDFlowMapping {
         return actionBuilder.build();
     }
 
-    public static dispatch def toAction(SetNwSrc sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(SetNwSrc sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
         val it = new SetNwSrcActionBuilder();
         val inetAddress = sourceAction.address;
         address = inetAddress.toInetAddress;
@@ -312,40 +316,40 @@ public class MDFlowMapping {
         return actionBuilder.build();
     }
 
-    public static dispatch def toAction(SetNwTos sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(SetNwTos sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
         val it = new SetNwTosActionBuilder();
         tos = sourceAction.nwTos;
         actionBuilder.action = new SetNwTosActionCaseBuilder().setSetNwTosAction(it.build).build;
         return actionBuilder.build();
     }
 
-    public static dispatch def toAction(SetTpDst sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(SetTpDst sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
         val it = new SetTpDstActionBuilder();
         port = new PortNumber(sourceAction.port);
         actionBuilder.action = new SetTpDstActionCaseBuilder().setSetTpDstAction(it.build()).build();
         return actionBuilder.build();
     }
 
-    public static dispatch def toAction(SetTpSrc sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(SetTpSrc sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
         val it = new SetTpSrcActionBuilder();
         port = new PortNumber(sourceAction.port);
         actionBuilder.action = new SetTpSrcActionCaseBuilder().setSetTpSrcAction(it.build()).build();
         return actionBuilder.build();
     }
 
-    public static dispatch def toAction(SetVlanCfi sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(SetVlanCfi sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
         val it = new SetVlanCfiActionBuilder();
         vlanCfi = new VlanCfi(sourceAction.cfi);
         actionBuilder.action = new SetVlanCfiActionCaseBuilder().setSetVlanCfiAction(it.build()).build();
         return actionBuilder.build();
     }
 
-    public static dispatch def toAction(SetVlanId sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(SetVlanId sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
 
         val it = new SetVlanIdActionBuilder();
         vlanId = new VlanId(sourceAction.vlanId);
@@ -353,16 +357,16 @@ public class MDFlowMapping {
         return actionBuilder.build();
     }
 
-    public static dispatch def toAction(SetVlanPcp sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(SetVlanPcp sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
         val it = new SetVlanPcpActionBuilder();
         vlanPcp = new VlanPcp(sourceAction.pcp as short);
         actionBuilder.action = new SetVlanPcpActionCaseBuilder().setSetVlanPcpAction(it.build).build;
         return actionBuilder.build();
     }
 
-    public static dispatch def toAction(SwPath sourceAction) {
-        val actionBuilder = new ActionBuilder();
+    public static dispatch def toAction(SwPath sourceAction, int order) {
+        val actionBuilder = new ActionBuilder().setOrder(order);
         actionBuilder.action = new SwPathActionCaseBuilder().setSwPathAction(new SwPathActionBuilder().build()).build();
         return actionBuilder.build();
     }
@@ -405,8 +409,10 @@ public class MDFlowMapping {
 
         val sourceActions = sourceFlow.actions;
         val targetActions = new ArrayList<Action>();
+        var int action = 0;
         for (sourceAction : sourceActions) {
-            targetActions.add(sourceAction.toAction());
+            targetActions.add(sourceAction.toAction(action));
+            action = action + 1;
         }
         instructions = targetActions.toApplyInstruction();
         match = sourceFlow.match.toMatch();
index 0486f3422cdfd4ceadeebb06c1307242201c0c58..fad37ae4f34cdd4fecc1eb3fb6ee6a7c39cb35bc 100644 (file)
@@ -163,7 +163,7 @@ public class NodeMapping {
     Preconditions.checkArgument(MD_SAL_TYPE.equals(node.getType()));
     final String nodeId = Arguments.<String>checkInstanceOf(node.getID(), String.class);
     final NodeKey nodeKey = new NodeKey(new NodeId(nodeId));
-    final InstanceIdentifier<Node> nodePath = InstanceIdentifier.builder().node(Nodes.class).child(NODE_CLASS, nodeKey).toInstance();
+    final InstanceIdentifier<Node> nodePath = InstanceIdentifier.builder(Nodes.class).child(NODE_CLASS, nodeKey).toInstance();
     return new NodeRef(nodePath);
   }
   
index 3df826e36e01a485352c1301d6b8311350b2e260..4aef75d9916565dc20c3c18a87149669116f4753 100644 (file)
@@ -31,8 +31,15 @@ class TopologyProvider implements AutoCloseable{
     DataProviderService dataService;
     
     Registration<DataCommitHandler<InstanceIdentifier<? extends DataObject>,DataObject>> commitHandlerRegistration;
-    
+
     def void start() {
+
+    }
+    def void startAdapter() {
+        if(dataService == null){
+            LOG.error("dataService not set");
+            return;
+        }
         commitHandler = new TopologyCommitHandler(dataService)
         commitHandler.setTopologyPublisher(topologyPublisher)
         val InstanceIdentifier<? extends DataObject> path = InstanceIdentifier.builder(NetworkTopology)
@@ -49,7 +56,9 @@ class TopologyProvider implements AutoCloseable{
     
     def setTopologyPublisher(IPluginOutTopologyService topologyPublisher) {
         _topologyPublisher = topologyPublisher;
-        commitHandler.setTopologyPublisher(topologyPublisher);
+        if(commitHandler != null){
+            commitHandler.setTopologyPublisher(topologyPublisher);
+        }
     }
     
 }
index 4d0465cba7b5ee768cca028a1fb56ce948f43550..362a3496574237a4b48aba49dff16fa859231e67 100644 (file)
             <groupId>org.eclipse.xtend</groupId>
             <artifactId>org.eclipse.xtend.lib</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
 
     <build>
index 43f48a50e56175a2297e78e32478f80404ff839d..ec8da8693fd5dd83379377786a6c3dc015797d34 100644 (file)
@@ -60,75 +60,4 @@ class FlowCapableInventoryProvider implements AutoCloseable {
         listenerRegistration?.close();
     }
     
-}
-
-class NodeChangeCommiter implements OpendaylightInventoryListener {
-
-    static val LOG = LoggerFactory.getLogger(NodeChangeCommiter);
-
-    @Property
-    val FlowCapableInventoryProvider manager;
-
-    new(FlowCapableInventoryProvider manager) {
-        _manager = manager;
-    }
-
-    override onNodeConnectorRemoved(NodeConnectorRemoved connector) {
-        val ref = connector.nodeConnectorRef;
-
-        // Check path
-        val it = manager.startChange()
-
-        LOG.debug("removing node connector : " + ref.value.toString());
-
-        removeOperationalData(ref.value as InstanceIdentifier<? extends DataObject>);
-        commit()
-    }
-
-    override onNodeConnectorUpdated(NodeConnectorUpdated connector) {
-        val ref = connector.nodeConnectorRef;
-
-        val flowConnector = connector.getAugmentation(FlowCapableNodeConnectorUpdated);
-
-        val it = manager.startChange()
-        val data = new NodeConnectorBuilder(connector);
-        data.key = (new NodeConnectorKey(connector.id))
-        if (flowConnector != null) {
-            val augment = flowConnector.toInventoryAugment()
-            data.addAugmentation(FlowCapableNodeConnector, augment)
-        }
-
-        LOG.debug("updating node connector : " + ref.value.toString());
-
-        putOperationalData(ref.value as InstanceIdentifier<NodeConnector>, data.build());
-        commit()
-    }
-
-    override onNodeRemoved(NodeRemoved node) {
-        val ref = node.nodeRef;
-        val it = manager.startChange()
-
-        LOG.debug("removing node : " + ref.value.toString());
-
-        removeOperationalData(ref.value as InstanceIdentifier<? extends DataObject>);
-        commit()
-    }
-
-    override onNodeUpdated(NodeUpdated node) {
-        val ref = node.nodeRef;
-        val flowNode = node.getAugmentation(FlowCapableNodeUpdated);
-
-        val it = manager.startChange()
-        val data = new NodeBuilder(node);
-        data.key = (new NodeKey(node.id))
-        if (flowNode != null) {
-            val augment = flowNode.toInventoryAugment();
-            data.addAugmentation(FlowCapableNode, augment)
-        }
-
-        LOG.debug("updating node : " + ref.value.toString());
-
-        putOperationalData(ref.value as InstanceIdentifier<Node>, data.build())
-        commit()
-    }
-}
+}
\ No newline at end of file
index 7668a3165ed0f6e9713626a8b1c50a5d8006497d..6f1ef5e5bb9c81c5359d2e6a55accc9287877bde 100644 (file)
@@ -8,7 +8,6 @@
 package org.opendaylight.controller.md.inventory.manager
 
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorUpdated
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowNodeConnector
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorBuilder
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode
diff --git a/opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/NodeChangeCommiter.java b/opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/NodeChangeCommiter.java
new file mode 100644 (file)
index 0000000..ebb3de7
--- /dev/null
@@ -0,0 +1,159 @@
+/**
+ * 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.inventory.manager;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.OpendaylightInventoryListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Objects;
+
+@SuppressWarnings("all")
+public class NodeChangeCommiter implements OpendaylightInventoryListener {
+    private final static Logger LOG = LoggerFactory.getLogger(NodeChangeCommiter.class);
+
+    private final FlowCapableInventoryProvider manager;
+
+    public NodeChangeCommiter(final FlowCapableInventoryProvider manager) {
+        this.manager = manager;
+    }
+
+    public FlowCapableInventoryProvider getManager() {
+        return this.manager;
+    }
+
+    @Override
+    public synchronized void onNodeConnectorRemoved(final NodeConnectorRemoved connector) {
+
+        final NodeConnectorRef ref = connector.getNodeConnectorRef();
+        final DataModificationTransaction it = this.getManager().startChange();
+        NodeChangeCommiter.LOG.debug("removing node connector {} ", ref.getValue());
+        it.removeOperationalData(ref.getValue());
+        Future<RpcResult<TransactionStatus>> commitResult = it.commit();
+        try {
+            commitResult.get();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Node Connector {} not removed.", ref.getValue(), e);
+        }
+
+    }
+
+    @Override
+    public synchronized void onNodeConnectorUpdated(final NodeConnectorUpdated connector) {
+
+        final NodeConnectorRef ref = connector.getNodeConnectorRef();
+        final FlowCapableNodeConnectorUpdated flowConnector = connector
+                .getAugmentation(FlowCapableNodeConnectorUpdated.class);
+        final DataModificationTransaction it = this.getManager().startChange();
+        final NodeConnectorBuilder data = new NodeConnectorBuilder(connector);
+        NodeConnectorId id = connector.getId();
+        NodeConnectorKey nodeConnectorKey = new NodeConnectorKey(id);
+        data.setKey(nodeConnectorKey);
+        boolean notEquals = (!Objects.equal(flowConnector, null));
+        if (notEquals) {
+            final FlowCapableNodeConnector augment = InventoryMapping.toInventoryAugment(flowConnector);
+            data.addAugmentation(FlowCapableNodeConnector.class, augment);
+        }
+        InstanceIdentifier<? extends Object> value = ref.getValue();
+        String string = value.toString();
+        String plus = ("updating node connector : " + string);
+        NodeChangeCommiter.LOG.debug(plus);
+        InstanceIdentifier<? extends Object> value1 = ref.getValue();
+        NodeConnector build = data.build();
+        it.putOperationalData((value1), build);
+        Future<RpcResult<TransactionStatus>> commitResult = it.commit();
+        try {
+            commitResult.get();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Node Connector {} not updated.", ref.getValue(), e);
+        }
+
+    }
+
+    @Override
+    public synchronized void onNodeRemoved(final NodeRemoved node) {
+
+        final NodeRef ref = node.getNodeRef();
+        FlowCapableInventoryProvider manager = this.getManager();
+        final DataModificationTransaction it = manager.startChange();
+        InstanceIdentifier<? extends Object> value = ref.getValue();
+        String string = value.toString();
+        String plus = ("removing node : " + string);
+        NodeChangeCommiter.LOG.debug(plus);
+        InstanceIdentifier<? extends Object> value1 = ref.getValue();
+        it.removeOperationalData((value1));
+        Future<RpcResult<TransactionStatus>> commitResult = it.commit();
+        try {
+            commitResult.get();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Node {} not removed.", ref.getValue(), e);
+        }
+    }
+
+    @Override
+    public synchronized void onNodeUpdated(final NodeUpdated node) {
+
+        final NodeRef ref = node.getNodeRef();
+        final FlowCapableNodeUpdated flowNode = node
+                .<FlowCapableNodeUpdated> getAugmentation(FlowCapableNodeUpdated.class);
+        FlowCapableInventoryProvider manager = this.getManager();
+        final DataModificationTransaction it = manager.startChange();
+        NodeBuilder nodeBuilder = new NodeBuilder(node);
+        final NodeBuilder data = nodeBuilder;
+        NodeId id = node.getId();
+        NodeKey nodeKey = new NodeKey(id);
+        data.setKey(nodeKey);
+        boolean equals = Objects.equal(flowNode, null);
+        if (equals) {
+            return;
+        }
+        final FlowCapableNode augment = InventoryMapping.toInventoryAugment(flowNode);
+        data.addAugmentation(FlowCapableNode.class, augment);
+        InstanceIdentifier<? extends Object> value = ref.getValue();
+        InstanceIdentifierBuilder<Node> builder = InstanceIdentifier.<Node> builder(((InstanceIdentifier<Node>) value));
+        InstanceIdentifierBuilder<FlowCapableNode> augmentation = builder
+                .<FlowCapableNode> augmentation(FlowCapableNode.class);
+        final InstanceIdentifier<FlowCapableNode> path = augmentation.build();
+        NodeChangeCommiter.LOG.debug("updating node :{} ", path);
+        it.putOperationalData(path, augment);
+
+        Future<RpcResult<TransactionStatus>> commitResult = it.commit();
+        try {
+            commitResult.get();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Node  {} not updated.", ref.getValue(), e);
+        }
+
+    }
+}
index cf6d232db330aca58f33cc81661f042dcc690266..047300a61b5bd0f2d19404b2a207be088b83062a 100644 (file)
@@ -38,12 +38,6 @@ module sal-port {
             uses tr:transaction-aware;
         }
     }
-     
-    rpc get-port {
-        output {
-            uses port-type:flow-capable-port;
-        }
-    }
     
     notification port-updated {
         uses port-update;
index c4cccc11023f33fd77ba0b27abf15e4d195b155d..19d6eafa789204b252caa2e8da233808dbce150e 100644 (file)
@@ -2,7 +2,7 @@ module opendaylight-statistics-types {
     namespace "urn:opendaylight:model:statistics:types";
     prefix stat-types;
 
-    import ietf-yang-types {prefix yang;}
+    import ietf-yang-types {prefix yang; revision-date "2010-09-24";}
     
     revision "2013-09-25" {
         description "Initial revision of flow service";
index f90031b2054a12794b22cfc2cf330d3d5f4934d5..00129b98415f5e1b9811d437c51ddf1a17689291 100644 (file)
   </scm>
 
     <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>yang-common</artifactId>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.controller</groupId>
-            <artifactId>sal-common</artifactId>
+            <artifactId>sal-common-api</artifactId>
         </dependency>
-
         <dependency>
-            <groupId>org.opendaylight.controller</groupId>
-            <artifactId>sal-common-api</artifactId>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
         </dependency>
         <dependency>
             <groupId>org.osgi</groupId>
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataBroker.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataBroker.java
new file mode 100644 (file)
index 0000000..c6a9efe
--- /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.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.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface BindingDataBroker extends AsyncDataBroker<InstanceIdentifier<?>, DataObject, BindingDataChangeListener>{
+    @Override
+    BindingDataReadTransaction newReadOnlyTransaction();
+
+    @Override
+    BindingDataReadWriteTransaction newReadWriteTransaction();
+
+    @Override
+    BindingDataWriteTransaction newWriteOnlyTransaction();
+
+    @Override
+    ListenerRegistration<BindingDataChangeListener> registerDataChangeListener(LogicalDatastoreType store,
+            InstanceIdentifier<?> path, BindingDataChangeListener listener, DataChangeScope triggeringScope);
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataChangeListener.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataChangeListener.java
new file mode 100644 (file)
index 0000000..94ac2d2
--- /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 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;
+
+public interface BindingDataChangeListener 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/BindingDataReadTransaction.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataReadTransaction.java
new file mode 100644 (file)
index 0000000..93df3eb
--- /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.AsyncReadTransaction;
+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.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public interface BindingDataReadTransaction extends AsyncReadTransaction<InstanceIdentifier<?>, DataObject> {
+    @Override
+    ListenableFuture<Optional<DataObject>> read(LogicalDatastoreType store, InstanceIdentifier<?> path);
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataReadWriteTransaction.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataReadWriteTransaction.java
new file mode 100644 (file)
index 0000000..0dcf020
--- /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.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;
+
+/**
+ * Logical capture of a combination of both {@link BindingDataReadTransaction} and
+ * {@link BindingDataWriteTransaction}.
+ */
+public interface BindingDataReadWriteTransaction extends BindingDataReadTransaction, BindingDataWriteTransaction, AsyncReadWriteTransaction<InstanceIdentifier<?>, DataObject> {
+
+}
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataWriteTransaction.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataWriteTransaction.java
new file mode 100644 (file)
index 0000000..e989f73
--- /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.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;
+
+public interface BindingDataWriteTransaction extends AsyncWriteTransaction<InstanceIdentifier<?>, DataObject> {
+    @Override
+    void put(LogicalDatastoreType store, InstanceIdentifier<?> path, DataObject data);
+
+    @Override
+    void delete(LogicalDatastoreType store, InstanceIdentifier<?> path);
+}
index db0d674e63df81febc8de6b7a0eacbbd6f308cb7..5b700703bc0b522357f42ec2580e9b64e94c924e 100644 (file)
@@ -11,7 +11,7 @@ import org.opendaylight.controller.md.sal.common.api.routing.RoutedRegistration;
 import org.opendaylight.controller.sal.binding.api.BindingAwareProvider.ProviderFunctionality;
 import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
 import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
-import org.opendaylight.yangtools.concepts.Registration;
+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;
@@ -19,20 +19,20 @@ 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}
@@ -42,34 +42,34 @@ import org.osgi.framework.BundleContext;
  * <li>Data Store access and modification - see {@link DataBrokerService} and
  * {@link 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 {
     /**
      * 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
@@ -82,24 +82,24 @@ public interface BindingAwareBroker {
 
     /**
      * 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.
@@ -112,26 +112,26 @@ public interface BindingAwareBroker {
 
     /**
      * {@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
@@ -143,19 +143,19 @@ public interface BindingAwareBroker {
 
     /**
      * {@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 {
 
@@ -166,7 +166,7 @@ public interface BindingAwareBroker {
         void unregisterFunctionality(ProviderFunctionality functionality);
     }
 
-    public interface RpcRegistration<T extends RpcService> extends Registration<T> {
+    public interface RpcRegistration<T extends RpcService> extends ObjectRegistration<T> {
 
         Class<T> getServiceType();
     }
@@ -177,9 +177,9 @@ public interface BindingAwareBroker {
         /**
          * Register particular instance identifier to be processed by this
          * RpcService
-         * 
-         * Deprecated in favor of {@link RoutedRegistration#registerPath(Object, Object)}. 
-         * 
+         *
+         * Deprecated in favor of {@link RoutedRegistration#registerPath(Object, Object)}.
+         *
          * @param context
          * @param instance
          */
@@ -189,9 +189,9 @@ public interface BindingAwareBroker {
         /**
          * Unregister particular instance identifier to be processed by this
          * RpcService
-         * 
-         * Deprecated in favor of {@link RoutedRegistration#unregisterPath(Object, Object)}. 
-         * 
+         *
+         * Deprecated in favor of {@link RoutedRegistration#unregisterPath(Object, Object)}.
+         *
          * @param context
          * @param instance
          */
index dea455bfa1a53e3f7c02175c99905747098a9620..dafb3ef6343c895385ebe99aee565e7d082182cd 100644 (file)
                             org.opendaylight.controller.sal.binding.impl.*,
                             org.opendaylight.controller.sal.binding.codegen,
                             org.opendaylight.controller.sal.binding.codegen.*,
+                            org.opendaylight.controller.md.sal.binding.impl,
                             <!--org.opendaylight.controller.sal.binding.dom.*,-->
                             org.opendaylight.controller.sal.binding.osgi.*,
                             org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.binding.impl.rev131028
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/ForwardedCompatibleDataBrokerImplModule.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/ForwardedCompatibleDataBrokerImplModule.java
new file mode 100644 (file)
index 0000000..647ca85
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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.config.yang.md.sal.binding.impl;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.opendaylight.controller.md.sal.binding.impl.ForwardedBackwardsCompatibleDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder;
+import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingDomConnectorDeployer;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
+import org.osgi.framework.BundleContext;
+
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+/**
+*
+*/
+public final class ForwardedCompatibleDataBrokerImplModule extends
+        org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractForwardedCompatibleDataBrokerImplModule
+        implements Provider {
+
+    private BundleContext bundleContext;
+
+    public ForwardedCompatibleDataBrokerImplModule(
+            final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+            final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public ForwardedCompatibleDataBrokerImplModule(
+            final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+            final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+            final ForwardedCompatibleDataBrokerImplModule oldModule, final java.lang.AutoCloseable oldInstance) {
+
+        super(identifier, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    protected void customValidation() {
+        // Add custom validation for module attributes here.
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        ListeningExecutorService listeningExecutor = SingletonHolder.getDefaultCommitExecutor();
+        BindingIndependentMappingService mappingService = getBindingMappingServiceDependency();
+
+        Broker domBroker = getDomAsyncBrokerDependency();
+        ProviderSession session = domBroker.registerProvider(this, getBundleContext());
+        DOMDataBroker domDataBroker = session.getService(DOMDataBroker.class);
+        ForwardedBackwardsCompatibleDataBroker dataBroker = new ForwardedBackwardsCompatibleDataBroker(domDataBroker,
+                mappingService, listeningExecutor);
+
+        session.getService(SchemaService.class).registerSchemaServiceListener(dataBroker);
+
+        dataBroker.setConnector(BindingDomConnectorDeployer.createConnector(getBindingMappingServiceDependency()));
+        dataBroker.setDomProviderContext(session);
+        return dataBroker;
+    }
+
+    public BundleContext getBundleContext() {
+        return bundleContext;
+    }
+
+    public void setBundleContext(final BundleContext bundleContext2) {
+        this.bundleContext = bundleContext2;
+    }
+
+    @Override
+    public void onSessionInitiated(final ProviderSession session) {
+
+    }
+
+    @Override
+    public Collection<ProviderFunctionality> getProviderFunctionality() {
+        return Collections.emptySet();
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/ForwardedCompatibleDataBrokerImplModuleFactory.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/ForwardedCompatibleDataBrokerImplModuleFactory.java
new file mode 100644 (file)
index 0000000..f019775
--- /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.config.yang.md.sal.binding.impl;
+
+import org.opendaylight.controller.config.api.DependencyResolver;
+import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
+import org.opendaylight.controller.config.spi.Module;
+import org.osgi.framework.BundleContext;
+
+
+/**
+*
+*/
+public class ForwardedCompatibleDataBrokerImplModuleFactory extends org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractForwardedCompatibleDataBrokerImplModuleFactory
+{
+
+
+    @Override
+    public Module createModule(final String instanceName, final DependencyResolver dependencyResolver, final BundleContext bundleContext) {
+        ForwardedCompatibleDataBrokerImplModule module = (ForwardedCompatibleDataBrokerImplModule) super.createModule(instanceName, dependencyResolver, bundleContext);
+        module.setBundleContext(bundleContext);
+        return module;
+    }
+
+    @Override
+    public Module createModule(final String instanceName, final DependencyResolver dependencyResolver,
+            final DynamicMBeanWithInstance old, final BundleContext bundleContext) throws Exception {
+        ForwardedCompatibleDataBrokerImplModule module = (ForwardedCompatibleDataBrokerImplModule)  super.createModule(instanceName, dependencyResolver, old, bundleContext);
+        module.setBundleContext(bundleContext);
+        return module;
+    }
+
+}
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..8a32b0b
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * 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.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.eclipse.xtext.xbase.lib.Exceptions;
+import org.opendaylight.controller.md.sal.binding.api.BindingDataChangeListener;
+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.sal.binding.impl.connect.dom.BindingIndependentConnector;
+import org.opendaylight.controller.sal.binding.impl.forward.DomForwardedBroker;
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+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.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
+import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractForwardedDataBroker implements Delegator<DOMDataBroker>, DomForwardedBroker, SchemaContextListener {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractForwardedDataBroker.class);
+    // The Broker to whom we do all forwarding
+    private final DOMDataBroker domDataBroker;
+
+    // Mapper to convert from Binding Independent objects to Binding Aware
+    // objects
+    private final BindingIndependentMappingService mappingService;
+
+    private final BindingToNormalizedNodeCodec codec;
+    private BindingIndependentConnector connector;
+    private ProviderSession context;
+
+    protected AbstractForwardedDataBroker(final DOMDataBroker domDataBroker,
+            final BindingIndependentMappingService mappingService) {
+        this.domDataBroker = domDataBroker;
+        this.mappingService = mappingService;
+        this.codec = new BindingToNormalizedNodeCodec(mappingService);
+    }
+
+    protected BindingToNormalizedNodeCodec getCodec() {
+        return codec;
+    }
+
+    protected BindingIndependentMappingService getMappingService() {
+        return mappingService;
+    }
+
+    @Override
+    public DOMDataBroker getDelegate() {
+        return domDataBroker;
+    }
+
+    @Override
+    public void onGlobalContextUpdated(final SchemaContext ctx) {
+        codec.onGlobalContextUpdated(ctx);
+    }
+
+    public ListenerRegistration<BindingDataChangeListener> registerDataChangeListener(final LogicalDatastoreType store,
+            final InstanceIdentifier<?> path, final BindingDataChangeListener listener,
+            final DataChangeScope triggeringScope) {
+        DOMDataChangeListener domDataChangeListener = new TranslatingDataChangeInvoker(store, path, listener,
+                triggeringScope);
+        org.opendaylight.yangtools.yang.data.api.InstanceIdentifier domPath = codec.toNormalized(path);
+        ListenerRegistration<DOMDataChangeListener> domRegistration = domDataBroker.registerDataChangeListener(store, domPath, domDataChangeListener, triggeringScope);
+        return new ListenerRegistrationImpl(listener, domRegistration);
+    }
+
+    protected Map<InstanceIdentifier<?>, DataObject> fromDOMToData(
+            final Map<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized) {
+        Map<InstanceIdentifier<?>, DataObject> newMap = new HashMap<>();
+        for (Map.Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, ? extends NormalizedNode<?, ?>> entry : normalized
+                .entrySet()) {
+            try {
+                Entry<InstanceIdentifier<? extends DataObject>, DataObject> binding = getCodec().toBinding(entry);
+                newMap.put(binding.getKey(), binding.getValue());
+            } catch (DeserializationException e) {
+                LOG.debug("Ommiting {}",entry,e);
+            }
+        }
+        return newMap;
+    }
+
+    private class TranslatingDataChangeInvoker implements DOMDataChangeListener {
+        private final BindingDataChangeListener bindingDataChangeListener;
+        private final LogicalDatastoreType store;
+        private final InstanceIdentifier<?> path;
+        private final DataChangeScope triggeringScope;
+
+        public TranslatingDataChangeInvoker(final LogicalDatastoreType store, final InstanceIdentifier<?> path,
+                final BindingDataChangeListener bindingDataChangeListener, final DataChangeScope triggeringScope) {
+            this.store = store;
+            this.path = path;
+            this.bindingDataChangeListener = bindingDataChangeListener;
+            this.triggeringScope = triggeringScope;
+        }
+
+        @Override
+        public void onDataChanged(
+                final AsyncDataChangeEvent<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> change) {
+            bindingDataChangeListener.onDataChanged(new TranslatedDataChangeEvent(change,path));
+        }
+    }
+
+    private class TranslatedDataChangeEvent implements AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> {
+        private final AsyncDataChangeEvent<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> domEvent;
+        private InstanceIdentifier<?> path;
+
+        public TranslatedDataChangeEvent(
+                final AsyncDataChangeEvent<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> change) {
+            this.domEvent = change;
+        }
+
+        public TranslatedDataChangeEvent(
+                final AsyncDataChangeEvent<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> change,
+                final InstanceIdentifier<?> path) {
+            this.domEvent = change;
+            this.path = path;
+        }
+
+        @Override
+        public Map<InstanceIdentifier<?>, DataObject> getCreatedData() {
+            return fromDOMToData(domEvent.getCreatedData());
+        }
+
+        @Override
+        public Map<InstanceIdentifier<?>, DataObject> getUpdatedData() {
+            return fromDOMToData(domEvent.getUpdatedData());
+
+        }
+
+        @Override
+        public Set<InstanceIdentifier<?>> getRemovedPaths() {
+            final Set<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier> removedPaths = domEvent
+                    .getRemovedPaths();
+            final Set<InstanceIdentifier<?>> output = new HashSet<>();
+            for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier instanceIdentifier : removedPaths) {
+                try {
+                    output.add(mappingService.fromDataDom(instanceIdentifier));
+                } catch (DeserializationException e) {
+                    Exceptions.sneakyThrow(e);
+                }
+            }
+
+            return output;
+        }
+
+        @Override
+        public Map<InstanceIdentifier<?>, ? extends DataObject> getOriginalData() {
+            return fromDOMToData(domEvent.getOriginalData());
+
+        }
+
+        @Override
+        public DataObject getOriginalSubtree() {
+
+            return toBindingData(path,domEvent.getOriginalSubtree());
+        }
+
+        @Override
+        public DataObject getUpdatedSubtree() {
+
+            return toBindingData(path,domEvent.getUpdatedSubtree());
+        }
+
+        @Override
+        public String toString() {
+            return "TranslatedDataChangeEvent [domEvent=" + domEvent + "]";
+        }
+    }
+
+    private static class ListenerRegistrationImpl extends AbstractListenerRegistration<BindingDataChangeListener> {
+        private final ListenerRegistration<DOMDataChangeListener> registration;
+
+        public ListenerRegistrationImpl(final BindingDataChangeListener listener,
+                final ListenerRegistration<DOMDataChangeListener> registration) {
+            super(listener);
+            this.registration = registration;
+        }
+
+        @Override
+        protected void removeRegistration() {
+            registration.close();
+        }
+    }
+
+    protected DataObject toBindingData(final InstanceIdentifier<?> path, final NormalizedNode<?, ?> data) {
+        try {
+            return getCodec().toBinding(path, data);
+        } catch (DeserializationException e) {
+            return null;
+        }
+    }
+
+
+    @Override
+    public BindingIndependentConnector getConnector() {
+        return this.connector;
+    }
+
+    @Override
+    public ProviderSession getDomProviderContext() {
+        return this.context;
+    }
+
+    @Override
+    public void setConnector(final BindingIndependentConnector connector) {
+        this.connector = connector;
+    }
+
+    @Override
+    public void setDomProviderContext(final ProviderSession domProviderContext) {
+       this.context = domProviderContext;
+    }
+
+    @Override
+    public void startForwarding() {
+        // NOOP
+    }
+
+
+
+
+}
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..4f9c429
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * 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.EnumMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Nullable;
+
+import org.eclipse.xtext.xbase.lib.Exceptions;
+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.impl.util.compat.DataNormalizationOperation;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.yangtools.concepts.Delegator;
+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.InstanceIdentifier.PathArgument;
+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;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public class AbstractForwardedTransaction<T extends AsyncTransaction<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>>>
+        implements Delegator<T> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractForwardedTransaction.class);
+    private final T delegate;
+    private final static CacheBuilder<Object, Object> CACHE_BUILDER = CacheBuilder.newBuilder()
+            .expireAfterWrite(10, TimeUnit.MILLISECONDS).maximumSize(100);
+    private final BindingToNormalizedNodeCodec codec;
+    private final EnumMap<LogicalDatastoreType, Cache<InstanceIdentifier<?>, DataObject>> cacheMap;
+
+    protected AbstractForwardedTransaction(final T delegate, final BindingToNormalizedNodeCodec codec) {
+        super();
+        this.delegate = delegate;
+        this.codec = codec;
+
+        this.cacheMap = new EnumMap<>(LogicalDatastoreType.class);
+        cacheMap.put(LogicalDatastoreType.OPERATIONAL, CACHE_BUILDER.<InstanceIdentifier<?>, DataObject> build());
+        cacheMap.put(LogicalDatastoreType.CONFIGURATION, CACHE_BUILDER.<InstanceIdentifier<?>, DataObject> build());
+
+    }
+
+    @Override
+    public T getDelegate() {
+        return delegate;
+    }
+
+    protected final BindingToNormalizedNodeCodec getCodec() {
+        return codec;
+    }
+
+    protected ListenableFuture<Optional<DataObject>> transformFuture(final LogicalDatastoreType store,
+            final InstanceIdentifier<?> path, final ListenableFuture<Optional<NormalizedNode<?, ?>>> future) {
+        return Futures.transform(future, new Function<Optional<NormalizedNode<?, ?>>, Optional<DataObject>>() {
+            @Nullable
+            @Override
+            public Optional<DataObject> apply(@Nullable final Optional<NormalizedNode<?, ?>> normalizedNode) {
+                try {
+                    final DataObject dataObject = normalizedNode.isPresent() ? codec.toBinding(path,
+                            normalizedNode.get()) : null;
+                    if(dataObject != null) {
+                        updateCache(store, path, dataObject);
+                    }
+                    return Optional.fromNullable(dataObject);
+                } catch (DeserializationException e) {
+                    Exceptions.sneakyThrow(e);
+                }
+                return null;
+            }
+        });
+    }
+
+    protected void doPut(final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType store,
+            final InstanceIdentifier<?> path, final DataObject data) {
+        invalidateCache(store, path);
+        final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = codec
+                .toNormalizedNode(path, data);
+        writeTransaction.put(store, normalized.getKey(), normalized.getValue());
+    }
+
+    protected void doPutWithEnsureParents(final DOMDataReadWriteTransaction writeTransaction,
+            final LogicalDatastoreType store, final InstanceIdentifier<?> path, final DataObject data) {
+        invalidateCache(store, path);
+        final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = codec
+                .toNormalizedNode(path, data);
+
+        org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalizedPath = normalized.getKey();
+        try {
+            List<PathArgument> currentArguments = new ArrayList<>();
+            DataNormalizationOperation<?> currentOp = codec.getDataNormalizer().getRootOperation();
+            Iterator<PathArgument> iterator = normalizedPath.getPath().iterator();
+            while (iterator.hasNext()) {
+                PathArgument currentArg = iterator.next();
+                currentOp = currentOp.getChild(currentArg);
+                currentArguments.add(currentArg);
+                org.opendaylight.yangtools.yang.data.api.InstanceIdentifier currentPath = new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(
+                        currentArguments);
+                boolean isPresent = writeTransaction.read(store, currentPath).get().isPresent();
+                if (isPresent == false && iterator.hasNext()) {
+                    writeTransaction.put(store, currentPath, currentOp.createDefault(currentArg));
+                }
+            }
+        } catch (InterruptedException | ExecutionException e) {
+            e.printStackTrace();
+        }
+        //LOG .info("Tx: {} : Putting data {}",getDelegate().getIdentifier(),normalized.getKey());
+        writeTransaction.put(store, normalized.getKey(), normalized.getValue());
+
+    }
+
+    protected void doMerge(final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType store,
+            final InstanceIdentifier<?> path, final DataObject data) {
+        invalidateCache(store, path);
+        final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = codec
+                .toNormalizedNode(path, data);
+        writeTransaction.merge(store, normalized.getKey(), normalized.getValue());
+    }
+
+    protected void doDelete(final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType store,
+            final InstanceIdentifier<?> path) {
+        invalidateCache(store, path);
+        final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = codec.toNormalized(path);
+        writeTransaction.delete(store, normalized);
+    }
+
+    protected ListenableFuture<RpcResult<TransactionStatus>> doCommit(final DOMDataWriteTransaction writeTransaction) {
+        return writeTransaction.commit();
+    }
+
+    protected void doCancel(final DOMDataWriteTransaction writeTransaction) {
+        writeTransaction.cancel();
+    }
+
+    protected ListenableFuture<Optional<DataObject>> doRead(final DOMDataReadTransaction readTransaction,
+            final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
+        final DataObject dataObject = getFromCache(store, path);
+        if (dataObject == null) {
+            final ListenableFuture<Optional<NormalizedNode<?, ?>>> future = readTransaction.read(store,
+                    codec.toNormalized(path));
+            return transformFuture(store, path, future);
+        } else {
+            return Futures.immediateFuture(Optional.of(dataObject));
+        }
+    }
+
+    private DataObject getFromCache(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
+        Cache<InstanceIdentifier<?>, DataObject> cache = cacheMap.get(store);
+        if (cache != null) {
+            return cache.getIfPresent(path);
+        }
+        return null;
+    }
+
+    private void updateCache(final LogicalDatastoreType store, final InstanceIdentifier<?> path,
+            final DataObject dataObject) {
+        // Check if cache exists. If not create one.
+        Cache<InstanceIdentifier<?>, DataObject> cache = cacheMap.get(store);
+        if (cache == null) {
+            cache = CacheBuilder.newBuilder().maximumSize(1000).expireAfterWrite(1, TimeUnit.MINUTES).build();
+
+        }
+
+        cache.put(path, dataObject);
+    }
+
+    private void invalidateCache(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
+        // FIXME: Optimization: invalidate only parents and children of path
+        Cache<InstanceIdentifier<?>, DataObject> cache = cacheMap.get(store);
+        cache.invalidateAll();
+        LOG.trace("Cache invalidated");
+    }
+
+}
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..f885b32
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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.AbstractMap.SimpleEntry;
+import java.util.Map.Entry;
+
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
+import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableList;
+
+public class BindingToNormalizedNodeCodec implements SchemaContextListener {
+
+    private static final Logger LOG = LoggerFactory.getLogger(BindingToNormalizedNodeCodec.class);
+
+    private final BindingIndependentMappingService bindingToLegacy;
+    private DataNormalizer legacyToNormalized;
+
+    public BindingToNormalizedNodeCodec(final BindingIndependentMappingService mappingService) {
+        super();
+        this.bindingToLegacy = mappingService;
+    }
+
+    public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalized(
+            final InstanceIdentifier<? extends DataObject> binding) {
+        return legacyToNormalized.toNormalized(bindingToLegacy.toDataDom(binding));
+    }
+
+    public Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
+            final InstanceIdentifier<? extends DataObject> bindingPath, final DataObject bindingObject) {
+        return toNormalizedNode(toEntry(bindingPath, bindingObject));
+
+    }
+
+    public Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
+            final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> binding) {
+        Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalizedEntry = legacyToNormalized.toNormalized(bindingToLegacy.toDataDom(binding));
+        if(Augmentation.class.isAssignableFrom(binding.getKey().getTargetType())) {
+
+            for(DataContainerChild<? extends PathArgument, ?> child : ((DataContainerNode<?>) normalizedEntry.getValue()).getValue()) {
+               if(child instanceof AugmentationNode) {
+                   ImmutableList<PathArgument> childArgs = ImmutableList.<PathArgument>builder()
+                           .addAll(normalizedEntry.getKey().getPath())
+                           .add(child.getIdentifier())
+                           .build();
+                   org.opendaylight.yangtools.yang.data.api.InstanceIdentifier childPath = new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(childArgs);
+                   return new SimpleEntry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>>(childPath,child);
+               }
+            }
+
+        }
+        return normalizedEntry;
+
+
+    }
+
+    public InstanceIdentifier<? extends DataObject> toBinding(
+            final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
+            throws DeserializationException {
+
+        org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath = legacyToNormalized
+                .toLegacy(normalized);
+        return bindingToLegacy.fromDataDom(legacyPath);
+    }
+
+    private static final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> toEntry(
+            final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> key,
+            final DataObject value) {
+        return new SimpleEntry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject>(
+                key, value);
+    }
+
+    public DataObject toBinding(final InstanceIdentifier<?> path, final NormalizedNode<?, ?> normalizedNode)
+            throws DeserializationException {
+        return bindingToLegacy.dataObjectFromDataDom(path, (CompositeNode) DataNormalizer.toLegacy(normalizedNode));
+    }
+
+    public DataNormalizer getDataNormalizer() {
+        return legacyToNormalized;
+    }
+
+    public Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> toBinding(
+            final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized)
+            throws DeserializationException {
+        InstanceIdentifier<? extends DataObject> bindingPath = toBinding(normalized.getKey());
+        DataObject bindingData = toBinding(bindingPath, normalized.getValue());
+        return toEntry(bindingPath, bindingData);
+    }
+
+    @Override
+    public void onGlobalContextUpdated(final SchemaContext arg0) {
+        legacyToNormalized = new DataNormalizer(arg0);
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBackwardsCompatibleDataBroker.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBackwardsCompatibleDataBroker.java
new file mode 100644 (file)
index 0000000..ee76073
--- /dev/null
@@ -0,0 +1,441 @@
+/*
+ * 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.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.ExecutionException;
+
+import org.opendaylight.controller.md.sal.binding.api.BindingDataChangeListener;
+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.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+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.common.util.Rpcs;
+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.concepts.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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;
+
+public class ForwardedBackwardsCompatibleDataBroker extends AbstractForwardedDataBroker implements DataProviderService, AutoCloseable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ForwardedBackwardsCompatibleDataBroker.class);
+
+    private final ConcurrentHashMap<InstanceIdentifier<?>, CommitHandlerRegistrationImpl> commitHandlers = new ConcurrentHashMap<>();
+    private final ListenerRegistry<DataChangeListener> fakeRegistry = ListenerRegistry.create();
+    private final ListeningExecutorService executorService;
+
+    public ForwardedBackwardsCompatibleDataBroker(final DOMDataBroker domDataBroker,
+            final BindingIndependentMappingService mappingService, final ListeningExecutorService executor) {
+        super(domDataBroker, mappingService);
+        executorService = executor;
+        LOG.info("ForwardedBackwardsCompatibleBroker started.");
+    }
+
+    @Override
+    public DataModificationTransaction beginTransaction() {
+        return new ForwardedBackwardsCompatibleTransacion(getDelegate().newReadWriteTransaction(), getCodec());
+    }
+
+    @Override
+    public DataObject readConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
+        DataModificationTransaction tx = beginTransaction();
+        return tx.readConfigurationData(path);
+    }
+
+    @Override
+    public DataObject readOperationalData(final InstanceIdentifier<? extends DataObject> path) {
+        DataModificationTransaction tx = beginTransaction();
+        return tx.readOperationalData(path);
+    }
+
+    @Override
+    public Registration<DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject>> registerCommitHandler(
+            final InstanceIdentifier<? extends DataObject> path,
+            final DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject> commitHandler) {
+
+
+        //transformingCommitHandler = new TransformingDataChangeListener
+        //fakeCommitHandler =  registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, path, listener, DataChangeScope.SUBTREE);
+
+        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) {
+
+
+        BindingDataChangeListener asyncOperListener = new BackwardsCompatibleOperationalDataChangeInvoker(listener);
+        BindingDataChangeListener asyncCfgListener = new BackwardsCompatibleConfigurationDataChangeInvoker(listener);
+
+        ListenerRegistration<BindingDataChangeListener> cfgReg = registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, path, asyncCfgListener, DataChangeScope.SUBTREE);
+        ListenerRegistration<BindingDataChangeListener> operReg = registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, path, asyncOperListener, DataChangeScope.SUBTREE);
+
+        return new LegacyListenerRegistration(listener,cfgReg,operReg);
+    }
+
+    @Override
+    public Registration<DataReader<InstanceIdentifier<? extends DataObject>, DataObject>> registerDataReader(
+            final InstanceIdentifier<? extends DataObject> path,
+            final DataReader<InstanceIdentifier<? extends DataObject>, DataObject> reader) {
+        throw new UnsupportedOperationException("Data reader contract is not supported.");
+    }
+
+    @Override
+    public void close() throws Exception {
+        // TODO Auto-generated method stub
+
+    }
+
+    public ListenableFuture<RpcResult<TransactionStatus>> commit(final ForwardedBackwardsCompatibleTransacion tx) {
+
+        final List<DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject>> subTrans = new ArrayList<>();
+        LOG.debug("Tx: {} Submitted.",tx.getIdentifier());
+        ListenableFuture<Boolean> requestCommit = executorService.submit(new Callable<Boolean>() {
+
+            @Override
+            public Boolean call() throws Exception {
+                try {
+                    for (CommitHandlerRegistrationImpl handler : commitHandlers.values()) {
+
+                        DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> subTx = handler
+                                .getInstance().requestCommit(tx);
+                        subTrans.add(subTx);
+                    }
+                } catch (Exception e) {
+                    LOG.error("Tx: {} Rollback.",tx.getIdentifier(),e);
+                    for (DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> subTx : subTrans) {
+                        subTx.rollback();
+                    }
+                    return false;
+                }
+                LOG.debug("Tx: {} Can Commit True.",tx.getIdentifier());
+                return true;
+            }
+
+        });
+
+        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 tx.getDelegate().commit();
+                }
+                return Futures.immediateFuture(Rpcs.getRpcResult(false, TransactionStatus.FAILED, Collections.<RpcError>emptySet()));
+            }
+        });
+
+        return Futures.transform(dataStoreCommit, new Function<RpcResult<TransactionStatus>,RpcResult<TransactionStatus>>() {
+            @Override
+            public RpcResult<TransactionStatus> apply(final RpcResult<TransactionStatus> input) {
+                if(input.isSuccessful()) {
+                    for(DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> subTx : subTrans ) {
+                        subTx.finish();
+                    }
+                } else {
+                    LOG.error("Tx: {} Rollback - Datastore commit failed.",tx.getIdentifier());
+                    for(DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> subTx : subTrans ) {
+                        subTx.rollback();
+                    }
+                }
+                return input;
+            }
+        });
+    }
+
+    private class ForwardedBackwardsCompatibleTransacion extends
+            AbstractForwardedTransaction<DOMDataReadWriteTransaction> implements DataModificationTransaction {
+
+        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;
+
+        @Override
+        public final TransactionStatus getStatus() {
+            return status;
+        }
+
+        protected ForwardedBackwardsCompatibleTransacion(final DOMDataReadWriteTransaction delegate,
+                final BindingToNormalizedNodeCodec codec) {
+            super(delegate, codec);
+            LOG.debug("Tx {} allocated.",getIdentifier());
+        }
+
+        @Override
+        public void putOperationalData(final InstanceIdentifier<? extends DataObject> path, final DataObject data) {
+
+            doPutWithEnsureParents(getDelegate(), LogicalDatastoreType.OPERATIONAL, path, data);
+        }
+
+        @Override
+        public void putConfigurationData(final InstanceIdentifier<? extends DataObject> path, final DataObject data) {
+            DataObject originalObj = readConfigurationData(path);
+            if (originalObj != null) {
+                original.put(path, originalObj);
+
+            } else {
+                created.put(path, data);
+            }
+            updated.put(path, data);
+            doPutWithEnsureParents(getDelegate(), LogicalDatastoreType.CONFIGURATION, path, data);
+        }
+
+        @Override
+        public void removeOperationalData(final InstanceIdentifier<? extends DataObject> path) {
+            doDelete(getDelegate(), LogicalDatastoreType.OPERATIONAL, path);
+
+        }
+
+        @Override
+        public void removeConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
+            doDelete(getDelegate(), LogicalDatastoreType.CONFIGURATION, 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 doRead(getDelegate(), 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 doRead(getDelegate(), LogicalDatastoreType.CONFIGURATION, path).get().orNull();
+            } catch (InterruptedException | ExecutionException e) {
+                LOG.error("Read of {} failed.", path,e);
+                return null;
+            }
+        }
+
+        @Override
+        public Object getIdentifier() {
+            return getDelegate().getIdentifier();
+        }
+
+        private void changeStatus(TransactionStatus status) {
+            LOG.trace("Transaction {} changed status to {}", getIdentifier(), status);
+            this.status = status;
+        }
+
+        @Override
+        public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+            final ListenableFuture<RpcResult<TransactionStatus>> f = ForwardedBackwardsCompatibleDataBroker.this.commit(this);
+
+            changeStatus(TransactionStatus.SUBMITED);
+
+            Futures.addCallback(f, new FutureCallback<RpcResult<TransactionStatus>>() {
+                @Override
+                public void onSuccess(RpcResult<TransactionStatus> result) {
+                    changeStatus(result.getResult());
+                }
+
+                @Override
+                public void onFailure(Throwable t) {
+                    LOG.error("Transaction {} failed to complete", getIdentifier(), t);
+                    changeStatus(TransactionStatus.FAILED);
+                }
+            });
+
+            return f;
+        }
+
+        @Override
+        public ListenerRegistration<DataTransactionListener> registerListener(final DataTransactionListener listener) {
+            throw new UnsupportedOperationException();
+        }
+
+    }
+
+    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<BindingDataChangeListener> cfgReg;
+        private final ListenerRegistration<BindingDataChangeListener> operReg;
+
+        public LegacyListenerRegistration(final DataChangeListener listener,
+                final ListenerRegistration<BindingDataChangeListener> cfgReg,
+                final ListenerRegistration<BindingDataChangeListener> 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 BindingDataChangeListener, 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) {
+
+            DataChangeEvent legacyChange = LegacyDataChangeEvent.createOperational(change);
+            delegate.onDataChanged(legacyChange);
+
+        }
+
+        @Override
+        public DataChangeListener getDelegate() {
+            return (DataChangeListener) delegate;
+        }
+
+    }
+
+    private static class BackwardsCompatibleConfigurationDataChangeInvoker implements BindingDataChangeListener, Delegator<DataChangeListener> {
+
+
+        @SuppressWarnings("rawtypes")
+        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) {
+
+            DataChangeEvent legacyChange = LegacyDataChangeEvent.createConfiguration(change);
+
+            delegate.onDataChanged(legacyChange);
+
+        }
+
+        @Override
+        public DataChangeListener getDelegate() {
+            return (DataChangeListener) delegate;
+        }
+
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBindingDataBroker.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBindingDataBroker.java
new file mode 100644 (file)
index 0000000..fb06b13
--- /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 org.opendaylight.controller.md.sal.binding.api.BindingDataBroker;
+import org.opendaylight.controller.md.sal.binding.api.BindingDataReadTransaction;
+import org.opendaylight.controller.md.sal.binding.api.BindingDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.binding.api.BindingDataWriteTransaction;
+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.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * 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
+ *
+ * TODO : All references in this class to CompositeNode should be switched to
+ * NormalizedNode once the MappingService is updated
+ *
+ */
+public class ForwardedBindingDataBroker extends AbstractForwardedDataBroker implements BindingDataBroker {
+
+    public ForwardedBindingDataBroker(final DOMDataBroker domDataBroker, final BindingIndependentMappingService mappingService) {
+        super(domDataBroker, mappingService);
+    }
+
+    @Override
+    public BindingDataReadTransaction newReadOnlyTransaction() {
+        return new BindingDataReadTransactionImpl(getDelegate().newReadOnlyTransaction(),getCodec());
+    }
+
+    @Override
+    public BindingDataReadWriteTransaction newReadWriteTransaction() {
+        return new BindingDataReadWriteTransactionImpl(getDelegate().newReadWriteTransaction(),getCodec());
+    }
+
+    @Override
+    public BindingDataWriteTransaction newWriteOnlyTransaction() {
+        return new BindingDataWriteTransactionImpl<DOMDataWriteTransaction>(getDelegate().newWriteOnlyTransaction(),getCodec());
+    }
+
+    private abstract class AbstractBindingTransaction<T extends AsyncTransaction<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>>>
+            extends AbstractForwardedTransaction<T> implements AsyncTransaction<InstanceIdentifier<?>, DataObject> {
+
+        protected AbstractBindingTransaction(final T delegate, final BindingToNormalizedNodeCodec codec) {
+            super(delegate, codec);
+        }
+
+        @Override
+        public Object getIdentifier() {
+            return getDelegate().getIdentifier();
+        }
+
+        @Override
+        public void close() {
+            getDelegate().close();
+        }
+
+    }
+
+    private class BindingDataReadTransactionImpl extends AbstractBindingTransaction<DOMDataReadTransaction> implements
+            BindingDataReadTransaction {
+
+        protected BindingDataReadTransactionImpl(final DOMDataReadTransaction delegate,
+                final BindingToNormalizedNodeCodec codec) {
+            super(delegate, codec);
+        }
+
+        @Override
+        public ListenableFuture<Optional<DataObject>> read(final LogicalDatastoreType store,
+                final InstanceIdentifier<?> path) {
+            return doRead(getDelegate(), store, path);
+        }
+    }
+
+    private class BindingDataWriteTransactionImpl<T extends DOMDataWriteTransaction> extends
+            AbstractBindingTransaction<T> implements BindingDataWriteTransaction {
+
+        protected BindingDataWriteTransactionImpl(final T delegate, final BindingToNormalizedNodeCodec codec) {
+            super(delegate, codec);
+
+        }
+
+        @Override
+        public void cancel() {
+            doCancel(getDelegate());
+        }
+
+        @Override
+        public void put(final LogicalDatastoreType store, final InstanceIdentifier<?> path, final DataObject data) {
+            doPut(getDelegate(), store, path, data);
+        }
+
+        @Override
+        public void merge(final LogicalDatastoreType store, final InstanceIdentifier<?> path, final DataObject data) {
+            doMerge(getDelegate(), store, path, data);
+        }
+
+        @Override
+        public void delete(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
+            doDelete(getDelegate(), store, path);
+        }
+
+        @Override
+        public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+            return doCommit(getDelegate());
+        }
+    }
+
+    private class BindingDataReadWriteTransactionImpl extends
+            BindingDataWriteTransactionImpl<DOMDataReadWriteTransaction> implements BindingDataReadWriteTransaction {
+
+        protected BindingDataReadWriteTransactionImpl(final DOMDataReadWriteTransaction delegate,
+                final BindingToNormalizedNodeCodec codec) {
+            super(delegate, codec);
+        }
+
+        @Override
+        public ListenableFuture<Optional<DataObject>> read(final LogicalDatastoreType store,
+                final InstanceIdentifier<?> path) {
+            return doRead(getDelegate(), store, path);
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/LegacyDataChangeEvent.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/LegacyDataChangeEvent.java
new file mode 100644 (file)
index 0000000..8cb4a70
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * 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.Collections;
+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;
+
+public abstract class LegacyDataChangeEvent implements
+        DataChangeEvent<InstanceIdentifier<? extends DataObject>, DataObject> {
+
+    private LegacyDataChangeEvent() {
+    }
+
+    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 final static class OperationalChangeEvent extends LegacyDataChangeEvent {
+
+        private final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> delegate;
+
+        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() {
+            return delegate.getUpdatedData();
+        }
+
+        @Override
+        public String toString() {
+            return "OperationalChangeEvent [delegate=" + delegate + "]";
+        }
+
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    private final static class ConfigurationChangeEvent extends LegacyDataChangeEvent {
+
+        private final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> delegate;
+
+        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() {
+            return delegate.getUpdatedData();
+        }
+
+        @Override
+        public String toString() {
+            return "ConfigurationChangeEvent [delegate=" + delegate + "]";
+        }
+
+    }
+
+}
index e0c7d260b5309c2242045821a1d827ae8931c6f1..542dfa7e7bca61a0fc4ff33e31239c3c3872e73b 100644 (file)
@@ -247,7 +247,8 @@ public class RpcProviderRegistryImpl implements //
 
         public RpcProxyRegistration(Class<T> type, T service, RpcProviderRegistryImpl registry) {
             super(service);
-            serviceType = type;
+            this.serviceType = type;
+            this.registry =  registry;
         }
 
         @Override
index e6e935c920b613c577d4b9f890092a22c13cb49d..37c0dfa60750280b9fe0963bf223a131b0e64753 100644 (file)
@@ -28,6 +28,7 @@ import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 
+import org.opendaylight.controller.md.sal.binding.impl.AbstractForwardedDataBroker;
 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.DataCommitHandler;
@@ -133,7 +134,7 @@ public class BindingIndependentConnector implements //
     private final Function<InstanceIdentifier<?>, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier> toDOMInstanceIdentifier = new Function<InstanceIdentifier<?>, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier>() {
 
         @Override
-        public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier apply(InstanceIdentifier<?> input) {
+        public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier apply(final InstanceIdentifier<?> input) {
             return mappingService.toDataDom(input);
         }
 
@@ -162,7 +163,7 @@ public class BindingIndependentConnector implements //
     }
 
     @Override
-    public DataObject readOperationalData(InstanceIdentifier<? extends DataObject> path) {
+    public DataObject readOperationalData(final InstanceIdentifier<? extends DataObject> path) {
         try {
             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biPath = mappingService.toDataDom(path);
             CompositeNode result = biDataService.readOperationalData(biPath);
@@ -173,7 +174,7 @@ public class BindingIndependentConnector implements //
     }
 
     private DataObject potentialAugmentationRead(InstanceIdentifier<? extends DataObject> path,
-            org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biPath, CompositeNode result)
+            final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biPath, final CompositeNode result)
             throws DeserializationException {
         Class<? extends DataObject> targetType = path.getTargetType();
         if (Augmentation.class.isAssignableFrom(targetType)) {
@@ -188,7 +189,7 @@ public class BindingIndependentConnector implements //
     }
 
     @Override
-    public DataObject readConfigurationData(InstanceIdentifier<? extends DataObject> path) {
+    public DataObject readConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
         try {
             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biPath = mappingService.toDataDom(path);
             CompositeNode result = biDataService.readConfigurationData(biPath);
@@ -199,7 +200,7 @@ public class BindingIndependentConnector implements //
     }
 
     private DataModificationTransaction createBindingToDomTransaction(
-            DataModification<InstanceIdentifier<? extends DataObject>, DataObject> source) {
+            final DataModification<InstanceIdentifier<? extends DataObject>, DataObject> source) {
         DataModificationTransaction target = biDataService.beginTransaction();
         LOG.debug("Created DOM Transaction {} for {},", target.getIdentifier(),source.getIdentifier());
         for (InstanceIdentifier<? extends DataObject> entry : source.getRemovedConfigurationData()) {
@@ -231,7 +232,7 @@ public class BindingIndependentConnector implements //
     }
 
     private org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction createDomToBindingTransaction(
-            DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> source) {
+            final DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> source) {
         org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction target = baDataService
                 .beginTransaction();
         for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry : source.getRemovedConfigurationData()) {
@@ -280,7 +281,7 @@ public class BindingIndependentConnector implements //
         return biDataService;
     }
 
-    protected void setDomDataService(org.opendaylight.controller.sal.core.api.data.DataProviderService biDataService) {
+    protected void setDomDataService(final org.opendaylight.controller.sal.core.api.data.DataProviderService biDataService) {
         this.biDataService = biDataService;
     }
 
@@ -288,7 +289,7 @@ public class BindingIndependentConnector implements //
         return baDataService;
     }
 
-    protected void setBindingDataService(DataProviderService baDataService) {
+    protected void setBindingDataService(final DataProviderService baDataService) {
         this.baDataService = baDataService;
     }
 
@@ -296,11 +297,15 @@ public class BindingIndependentConnector implements //
         return baRpcRegistry;
     }
 
-    protected void setBindingRpcRegistry(RpcProviderRegistry rpcRegistry) {
+    protected void setBindingRpcRegistry(final RpcProviderRegistry rpcRegistry) {
         this.baRpcRegistry = rpcRegistry;
     }
 
     public void startDataForwarding() {
+        if(baDataService instanceof AbstractForwardedDataBroker) {
+            dataForwarding = true;
+            return;
+        }
         checkState(!dataForwarding, "Connector is already forwarding data.");
         baDataReaderRegistration = baDataService.registerDataReader(ROOT, this);
         baCommitHandlerRegistration = baDataService.registerCommitHandler(ROOT, bindingToDomCommitHandler);
@@ -331,7 +336,7 @@ public class BindingIndependentConnector implements //
         }
     }
 
-    protected void setMappingService(BindingIndependentMappingService mappingService) {
+    protected void setMappingService(final BindingIndependentMappingService mappingService) {
         this.mappingService = mappingService;
     }
 
@@ -341,17 +346,17 @@ public class BindingIndependentConnector implements //
     }
 
     @Override
-    public void onSessionInitiated(ProviderSession session) {
+    public void onSessionInitiated(final ProviderSession session) {
         setDomDataService(session.getService(org.opendaylight.controller.sal.core.api.data.DataProviderService.class));
         setDomRpcRegistry(session.getService(RpcProvisionRegistry.class));
 
     }
 
-    public <T extends RpcService> void onRpcRouterCreated(Class<T> serviceType, RpcRouter<T> router) {
+    public <T extends RpcService> void onRpcRouterCreated(final Class<T> serviceType, final RpcRouter<T> router) {
 
     }
 
-    public void setDomRpcRegistry(RpcProvisionRegistry registry) {
+    public void setDomRpcRegistry(final RpcProvisionRegistry registry) {
         biRpcRegistry = registry;
     }
 
@@ -373,8 +378,8 @@ public class BindingIndependentConnector implements //
         private final DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> modification;
 
         public DomToBindingTransaction(
-                org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction backing,
-                DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> modification) {
+                final org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction backing,
+                final DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> modification) {
             super();
             this.backing = backing;
             this.modification = modification;
@@ -412,8 +417,8 @@ public class BindingIndependentConnector implements //
         private final DataModificationTransaction backing;
         private final DataModification<InstanceIdentifier<? extends DataObject>, DataObject> modification;
 
-        public BindingToDomTransaction(DataModificationTransaction backing,
-                DataModification<InstanceIdentifier<? extends DataObject>, DataObject> modification) {
+        public BindingToDomTransaction(final DataModificationTransaction backing,
+                final DataModification<InstanceIdentifier<? extends DataObject>, DataObject> modification) {
             this.backing = backing;
             this.modification = modification;
             domOpenedTransactions.put(backing.getIdentifier(), this);
@@ -451,7 +456,7 @@ public class BindingIndependentConnector implements //
 
         @Override
         public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> requestCommit(
-                DataModification<InstanceIdentifier<? extends DataObject>, DataObject> bindingTransaction) {
+                final DataModification<InstanceIdentifier<? extends DataObject>, DataObject> bindingTransaction) {
 
             /**
              * Transaction was created as DOM transaction, in that case we do
@@ -474,7 +479,7 @@ public class BindingIndependentConnector implements //
             DataCommitHandler<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> {
 
         @Override
-        public void onRegister(DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject> registration) {
+        public void onRegister(final DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject> registration) {
 
             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier domPath = mappingService.toDataDom(registration
                     .getPath());
@@ -482,14 +487,14 @@ public class BindingIndependentConnector implements //
         }
 
         @Override
-        public void onUnregister(DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject> registration) {
+        public void onUnregister(final DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject> registration) {
             // NOOP for now
             // FIXME: do registration based on only active commit handlers.
         }
 
         @Override
         public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> requestCommit(
-                DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> domTransaction) {
+                final DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> domTransaction) {
             Object identifier = domTransaction.getIdentifier();
 
             /**
@@ -527,34 +532,34 @@ public class BindingIndependentConnector implements //
             return registryImpl;
         }
 
-        public void setRegistryImpl(RpcProviderRegistryImpl registryImpl) {
+        public void setRegistryImpl(final RpcProviderRegistryImpl registryImpl) {
             this.registryImpl = registryImpl;
         }
 
         @Override
-        public void onGlobalRpcRegistered(Class<? extends RpcService> cls) {
+        public void onGlobalRpcRegistered(final Class<? extends RpcService> cls) {
             getRpcForwarder(cls, null);
         }
 
         @Override
-        public void onGlobalRpcUnregistered(Class<? extends RpcService> cls) {
+        public void onGlobalRpcUnregistered(final Class<? extends RpcService> cls) {
             // NOOP
         }
 
         @Override
-        public void onRpcRouterCreated(RpcRouter<?> router) {
+        public void onRpcRouterCreated(final RpcRouter<?> router) {
             Class<? extends BaseIdentity> ctx = router.getContexts().iterator().next();
             getRpcForwarder(router.getServiceType(), ctx);
         }
 
         @Override
-        public void onRouteChange(RouteChange<RpcContextIdentifier, InstanceIdentifier<?>> change) {
+        public void onRouteChange(final RouteChange<RpcContextIdentifier, InstanceIdentifier<?>> change) {
             for (Entry<RpcContextIdentifier, Set<InstanceIdentifier<?>>> entry : change.getAnnouncements().entrySet()) {
                 bindingRoutesAdded(entry);
             }
         }
 
-        private void bindingRoutesAdded(Entry<RpcContextIdentifier, Set<InstanceIdentifier<?>>> entry) {
+        private void bindingRoutesAdded(final Entry<RpcContextIdentifier, Set<InstanceIdentifier<?>>> entry) {
             Class<? extends BaseIdentity> context = entry.getKey().getRoutingContext();
             Class<? extends RpcService> service = entry.getKey().getRpcService();
             if (context != null) {
@@ -562,8 +567,8 @@ public class BindingIndependentConnector implements //
             }
         }
 
-        private DomToBindingRpcForwarder getRpcForwarder(Class<? extends RpcService> service,
-                Class<? extends BaseIdentity> context) {
+        private DomToBindingRpcForwarder getRpcForwarder(final Class<? extends RpcService> service,
+                final Class<? extends BaseIdentity> context) {
             DomToBindingRpcForwarder potential = forwarders.get(service);
             if (potential != null) {
                 return potential;
@@ -588,7 +593,7 @@ public class BindingIndependentConnector implements //
         private final Map<QName, RpcInvocationStrategy> strategiesByQName = new HashMap<>();
         private final WeakHashMap<Method, RpcInvocationStrategy> strategiesByMethod = new WeakHashMap<>();
 
-        public DomToBindingRpcForwarder(Class<? extends RpcService> service) {
+        public DomToBindingRpcForwarder(final Class<? extends RpcService> service) {
             this.rpcServiceType = new WeakReference<Class<? extends RpcService>>(service);
             this.supportedRpcs = mappingService.getRpcQNamesFor(service);
             try {
@@ -611,7 +616,7 @@ public class BindingIndependentConnector implements //
          * @param service
          * @param context
          */
-        public DomToBindingRpcForwarder(Class<? extends RpcService> service, Class<? extends BaseIdentity> context) {
+        public DomToBindingRpcForwarder(final Class<? extends RpcService> service, final Class<? extends BaseIdentity> context) {
             this.rpcServiceType = new WeakReference<Class<? extends RpcService>>(service);
             this.supportedRpcs = mappingService.getRpcQNamesFor(service);
             Builder<RoutedRpcRegistration> registrationsBuilder = ImmutableSet
@@ -630,8 +635,8 @@ public class BindingIndependentConnector implements //
             registrations = registrationsBuilder.build();
         }
 
-        public void registerPaths(Class<? extends BaseIdentity> context, Class<? extends RpcService> service,
-                Set<InstanceIdentifier<?>> set) {
+        public void registerPaths(final Class<? extends BaseIdentity> context, final Class<? extends RpcService> service,
+                final Set<InstanceIdentifier<?>> set) {
             QName ctx = BindingReflections.findQName(context);
             for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier path : FluentIterable.from(set).transform(
                     toDOMInstanceIdentifier)) {
@@ -643,7 +648,7 @@ public class BindingIndependentConnector implements //
 
 
         @Override
-        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+        public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
             if (EQUALS_METHOD.equals(method)) {
                 return false;
             }
@@ -657,8 +662,8 @@ public class BindingIndependentConnector implements //
             return strategy.forwardToDomBroker(null);
         }
 
-        public void removePaths(Class<? extends BaseIdentity> context, Class<? extends RpcService> service,
-                Set<InstanceIdentifier<?>> set) {
+        public void removePaths(final Class<? extends BaseIdentity> context, final Class<? extends RpcService> service,
+                final Set<InstanceIdentifier<?>> set) {
             QName ctx = BindingReflections.findQName(context);
             for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier path : FluentIterable.from(set).transform(
                     toDOMInstanceIdentifier)) {
@@ -686,7 +691,7 @@ public class BindingIndependentConnector implements //
         }
 
         @Override
-        public RpcResult<CompositeNode> invokeRpc(QName rpc, CompositeNode domInput) {
+        public RpcResult<CompositeNode> invokeRpc(final QName rpc, final CompositeNode domInput) {
             checkArgument(rpc != null);
             checkArgument(domInput != null);
 
@@ -702,7 +707,7 @@ public class BindingIndependentConnector implements //
             }
         }
 
-        private RpcInvocationStrategy resolveInvocationStrategy(QName rpc) {
+        private RpcInvocationStrategy resolveInvocationStrategy(final QName rpc) {
             return strategiesByQName.get(rpc);
         }
 
@@ -750,7 +755,7 @@ public class BindingIndependentConnector implements //
         protected final Method targetMethod;
         protected final QName rpc;
 
-        public RpcInvocationStrategy(QName rpc, Method targetMethod) {
+        public RpcInvocationStrategy(final QName rpc, final Method targetMethod) {
             this.targetMethod = targetMethod;
             this.rpc = rpc;
         }
@@ -760,7 +765,7 @@ public class BindingIndependentConnector implements //
         public abstract RpcResult<CompositeNode> uncheckedInvoke(RpcService rpcService, CompositeNode domInput)
                 throws Exception;
 
-        public RpcResult<CompositeNode> invokeOn(RpcService rpcService, CompositeNode domInput) throws Exception {
+        public RpcResult<CompositeNode> invokeOn(final RpcService rpcService, final CompositeNode domInput) throws Exception {
             return uncheckedInvoke(rpcService, domInput);
         }
     }
@@ -774,8 +779,8 @@ public class BindingIndependentConnector implements //
         private final WeakReference<Class> outputClass;
 
         @SuppressWarnings({ "rawtypes", "unchecked" })
-        public DefaultInvocationStrategy(QName rpc, Method targetMethod, Class<?> outputClass,
-                Class<? extends DataContainer> inputClass) {
+        public DefaultInvocationStrategy(final QName rpc, final Method targetMethod, final Class<?> outputClass,
+                final Class<? extends DataContainer> inputClass) {
             super(rpc, targetMethod);
             this.outputClass = new WeakReference(outputClass);
             this.inputClass = new WeakReference(inputClass);
@@ -783,7 +788,7 @@ public class BindingIndependentConnector implements //
 
         @SuppressWarnings("unchecked")
         @Override
-        public RpcResult<CompositeNode> uncheckedInvoke(RpcService rpcService, CompositeNode domInput) throws Exception {
+        public RpcResult<CompositeNode> uncheckedInvoke(final RpcService rpcService, final CompositeNode domInput) throws Exception {
             DataContainer bindingInput = mappingService.dataObjectFromDataDom(inputClass.get(), domInput);
             Future<RpcResult<?>> futureResult = (Future<RpcResult<?>>) targetMethod.invoke(rpcService, bindingInput);
             if (futureResult == null) {
@@ -799,7 +804,7 @@ public class BindingIndependentConnector implements //
         }
 
         @Override
-        public Future<RpcResult<?>> forwardToDomBroker(DataObject input) {
+        public Future<RpcResult<?>> forwardToDomBroker(final DataObject input) {
             if(biRpcRegistry != null) {
                 CompositeNode xml = mappingService.toDataDom(input);
                 CompositeNode wrappedXml = ImmutableCompositeNode.create(rpc, ImmutableList.<Node<?>> of(xml));
@@ -818,12 +823,12 @@ public class BindingIndependentConnector implements //
 
     private class NoInputNoOutputInvocationStrategy extends RpcInvocationStrategy {
 
-        public NoInputNoOutputInvocationStrategy(QName rpc, Method targetMethod) {
+        public NoInputNoOutputInvocationStrategy(final QName rpc, final Method targetMethod) {
             super(rpc, targetMethod);
         }
 
         @Override
-        public RpcResult<CompositeNode> uncheckedInvoke(RpcService rpcService, CompositeNode domInput) throws Exception {
+        public RpcResult<CompositeNode> uncheckedInvoke(final RpcService rpcService, final CompositeNode domInput) throws Exception {
             @SuppressWarnings("unchecked")
             Future<RpcResult<Void>> result = (Future<RpcResult<Void>>) targetMethod.invoke(rpcService);
             RpcResult<Void> bindingResult = result.get();
@@ -831,7 +836,7 @@ public class BindingIndependentConnector implements //
         }
 
         @Override
-        public Future<RpcResult<?>> forwardToDomBroker(DataObject input) {
+        public Future<RpcResult<?>> forwardToDomBroker(final DataObject input) {
             return Futures.immediateFuture(null);
         }
     }
@@ -843,15 +848,15 @@ public class BindingIndependentConnector implements //
         private final WeakReference<Class> inputClass;
 
         @SuppressWarnings({ "rawtypes", "unchecked" })
-        public NoOutputInvocationStrategy(QName rpc, Method targetMethod,
-                Class<? extends DataContainer> inputClass) {
+        public NoOutputInvocationStrategy(final QName rpc, final Method targetMethod,
+                final Class<? extends DataContainer> inputClass) {
             super(rpc,targetMethod);
             this.inputClass = new WeakReference(inputClass);
         }
 
 
         @Override
-        public RpcResult<CompositeNode> uncheckedInvoke(RpcService rpcService, CompositeNode domInput) throws Exception {
+        public RpcResult<CompositeNode> uncheckedInvoke(final RpcService rpcService, final CompositeNode domInput) throws Exception {
             DataContainer bindingInput = mappingService.dataObjectFromDataDom(inputClass.get(), domInput);
             Future<RpcResult<?>> result = (Future<RpcResult<?>>) targetMethod.invoke(rpcService, bindingInput);
             if (result == null) {
@@ -862,7 +867,7 @@ public class BindingIndependentConnector implements //
         }
 
         @Override
-        public Future<RpcResult<?>> forwardToDomBroker(DataObject input) {
+        public Future<RpcResult<?>> forwardToDomBroker(final DataObject input) {
             if(biRpcRegistry != null) {
                 CompositeNode xml = mappingService.toDataDom(input);
                 CompositeNode wrappedXml = ImmutableCompositeNode.create(rpc,ImmutableList.<Node<?>>of(xml));
@@ -892,12 +897,12 @@ public class BindingIndependentConnector implements //
         return mappingService;
     }
 
-    public void setBindingNotificationService(NotificationProviderService baService) {
+    public void setBindingNotificationService(final NotificationProviderService baService) {
         this.baNotifyService = baService;
 
     }
 
-    public void setDomNotificationService(NotificationPublishService domService) {
+    public void setDomNotificationService(final NotificationPublishService domService) {
         this.domNotificationService = domService;
     }
 
@@ -912,7 +917,7 @@ public class BindingIndependentConnector implements //
         }
 
         @Override
-        public void onNotification(CompositeNode notification) {
+        public void onNotification(final CompositeNode notification) {
             QName qname = notification.getNodeType();
             WeakReference<Class<? extends Notification>> potential = notifications.get(qname);
             if (potential != null) {
@@ -929,7 +934,7 @@ public class BindingIndependentConnector implements //
         }
 
         @Override
-        public void onNotificationSubscribtion(Class<? extends Notification> notificationType) {
+        public void onNotificationSubscribtion(final Class<? extends Notification> notificationType) {
             QName qname = BindingReflections.findQName(notificationType);
             if (qname != null) {
                 WeakReference<Class<? extends Notification>> already = notifications.putIfAbsent(qname,
index 8d6f3314520d3fc31f21b60a004915c1cc938263..db98b456f05c9060f04f8154eecf1e6fc5cf35ad 100644 (file)
@@ -10,12 +10,12 @@ package org.opendaylight.controller.sal.binding.impl.forward;
 import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentConnector;
 import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
 
-interface DomForwardedBroker {
+public interface DomForwardedBroker {
 
     public BindingIndependentConnector getConnector();
-    
+
     public void setConnector(BindingIndependentConnector connector);
-    
+
     public void setDomProviderContext(ProviderSession domProviderContext);
 
     public ProviderSession getDomProviderContext();
index b95cb73f1c793fa04491093b226707b3756a375c..428025a58df58180085c4811f441f7f5a0e5d702 100644 (file)
@@ -1,5 +1,5 @@
 module opendaylight-sal-binding-broker-impl {
-       yang-version 1;
+    yang-version 1;
     namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl";
     prefix "binding-impl";
 
@@ -10,7 +10,7 @@ module opendaylight-sal-binding-broker-impl {
 
     description
         "Service definition for Binding Aware MD-SAL.";
+
     revision "2013-10-28" {
         description
             "Initial revision";
@@ -28,20 +28,27 @@ module opendaylight-sal-binding-broker-impl {
         config:provided-service sal:binding-rpc-registry;
         config:java-name-prefix BindingBrokerImpl;
     }
-    
+
     identity binding-data-broker {
         base config:module-type;
         config:provided-service sal:binding-data-broker;
         config:provided-service sal:binding-data-consumer-broker;
         config:java-name-prefix DataBrokerImpl;
     }
-    
+
+    identity binding-data-compatible-broker {
+        base config:module-type;
+        config:provided-service sal:binding-data-broker;
+        config:provided-service sal:binding-data-consumer-broker;
+        config:java-name-prefix ForwardedCompatibleDataBrokerImpl;
+    }
+
     identity binding-rpc-broker {
         base config:module-type;
         config:provided-service sal:binding-rpc-registry;
         config:java-name-prefix RpcBrokerImpl;
     }
-    
+
     identity binding-notification-broker {
         base config:module-type;
         config:provided-service sal:binding-notification-service;
@@ -58,7 +65,7 @@ module opendaylight-sal-binding-broker-impl {
     augment "/config:modules/config:module/config:configuration" {
         case binding-broker-impl {
             when "/config:modules/config:module/config:type = 'binding-broker-impl'";
-            
+
             /*
             container rpc-registry {
                 uses config:service-ref {
@@ -68,7 +75,7 @@ module opendaylight-sal-binding-broker-impl {
                     }
                 }
             }*/
-            
+
             container data-broker {
                 uses config:service-ref {
                     refine type {
@@ -77,7 +84,7 @@ module opendaylight-sal-binding-broker-impl {
                     }
                 }
             }
-            
+
             container notification-service {
                 uses config:service-ref {
                     refine type {
@@ -108,10 +115,34 @@ module opendaylight-sal-binding-broker-impl {
                         config:required-identity binding-dom-mapping-service;
                     }
                 }
-            } 
+            }
+        }
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case binding-data-compatible-broker {
+            when "/config:modules/config:module/config:type = 'binding-data-compatible-broker'";
+
+            container dom-async-broker {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity dom:dom-broker-osgi-registry;
+                    }
+                }
+            }
+
+            container binding-mapping-service {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity binding-dom-mapping-service;
+                    }
+                }
+            }
         }
     }
-    
+
 
     augment "/config:modules/config:module/config:state" {
         case runtime-generated-mapping {
@@ -139,4 +170,4 @@ module opendaylight-sal-binding-broker-impl {
             uses common:notification-state;
         }
     }
-}
\ No newline at end of file
+}
index 248cca09db0fd2e42a799a3d4f719eb39f151a11..36a172d09c3f1a99bee21586f9a2b47c75103d9f 100644 (file)
@@ -7,24 +7,17 @@
  */
 package org.opendaylight.controller.sal.binding.test.bugfix;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
 import org.junit.Test;
 import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpPrefix;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.igp.node.attributes.igp.node.attributes.Prefix;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.igp.node.attributes.igp.node.attributes.PrefixBuilder;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 
-
-
-
-
-
-
-
-import static org.junit.Assert.*;
-
 public class UnionSerializationTest extends AbstractDataServiceTest {
     
     public static final String PREFIX_STRING = "192.168.0.1/32";
@@ -42,7 +35,7 @@ public class UnionSerializationTest extends AbstractDataServiceTest {
         assertNotNull(serialized.getFirstSimpleByName(Prefix.QNAME));
         assertEquals(PREFIX_STRING, serialized.getFirstSimpleByName(Prefix.QNAME).getValue());
         
-        Prefix deserialized = (Prefix) testContext.getBindingToDomMappingService().dataObjectFromDataDom(InstanceIdentifier.builder().node(Prefix.class).build(), serialized);
+        Prefix deserialized = (Prefix) testContext.getBindingToDomMappingService().dataObjectFromDataDom(Prefix.class, serialized);
         assertNotNull(deserialized);
         assertNotNull(deserialized.getPrefix());
         assertNotNull(deserialized.getPrefix().getIpv4Prefix());
index ef3e9483315d93fe0237b32652e49f49c84532ab..4bad2bbb86140a5f04ac6a704b84e0b314cad76b 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.controller.sal.binding.test.util;
 
+import static com.google.common.base.Preconditions.checkState;
+
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.List;
@@ -15,6 +17,12 @@ import java.util.concurrent.Future;
 
 import javassist.ClassPool;
 
+import org.opendaylight.controller.md.sal.binding.impl.ForwardedBackwardsCompatibleDataBroker;
+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.DOMDataBrokerImpl;
+import org.opendaylight.controller.md.sal.dom.broker.impl.compat.BackwardsCompatibleDataBroker;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
 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;
@@ -33,6 +41,7 @@ import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
 import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
 import org.opendaylight.controller.sal.core.api.data.DataStore;
 import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
 import org.opendaylight.controller.sal.dom.broker.BrokerImpl;
 import org.opendaylight.controller.sal.dom.broker.MountPointManagerImpl;
 import org.opendaylight.controller.sal.dom.broker.impl.DataStoreStatsWrapper;
@@ -48,6 +57,7 @@ import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
 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.parser.impl.YangParserImpl;
 import org.reflections.Reflections;
 import org.reflections.scanners.ResourcesScanner;
@@ -57,10 +67,9 @@ import org.slf4j.LoggerFactory;
 import com.google.common.base.Predicate;
 import com.google.common.collect.ClassToInstanceMap;
 import com.google.common.collect.ImmutableClassToInstanceMap;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.util.concurrent.ListeningExecutorService;
 
-import static com.google.common.base.Preconditions.*;
-
 public class BindingTestContext implements AutoCloseable, SchemaContextProvider {
 
     public static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier TREE_ROOT = org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
@@ -76,13 +85,14 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider
     private BindingIndependentConnector baConnectImpl;
 
     private org.opendaylight.controller.sal.dom.broker.DataBrokerImpl biDataImpl;
+    private org.opendaylight.controller.sal.core.api.data.DataProviderService biDataLegacyBroker;
     private BrokerImpl biBrokerImpl;
     private HashMapDataStore rawDataStore;
     private SchemaAwareDataStoreAdapter schemaAwareDataStore;
     private DataStoreStatsWrapper dataStoreStats;
     private DataStore dataStore;
 
-    private boolean dataStoreStatisticsEnabled = false;
+    private final boolean dataStoreStatisticsEnabled = false;
 
     private final ListeningExecutorService executor;
     private final ClassPool classPool;
@@ -93,11 +103,22 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider
 
     private SchemaContext schemaContext;
 
+    private ImmutableMap<LogicalDatastoreType, DOMStore> newDatastores;
+
+    private BackwardsCompatibleDataBroker biCompatibleBroker;
+
+    private final List<SchemaContextListener> schemaListeners = new ArrayList<>();
+
+    private DataProviderService baData;
+
+    private DOMDataBroker newDOMDataBroker;
+
+    @Override
     public SchemaContext getSchemaContext() {
         return schemaContext;
     }
 
-    protected BindingTestContext(ListeningExecutorService executor, ClassPool classPool, boolean startWithSchema) {
+    protected BindingTestContext(final ListeningExecutorService executor, final ClassPool classPool, final boolean startWithSchema) {
         this.executor = executor;
         this.classPool = classPool;
         this.startWithSchema = startWithSchema;
@@ -125,31 +146,52 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider
         checkState(executor != null, "Executor needs to be set");
         biDataImpl = new org.opendaylight.controller.sal.dom.broker.DataBrokerImpl();
         biDataImpl.setExecutor(executor);
+        biDataLegacyBroker = biDataImpl;
+    }
+
+    public void startNewDomDataBroker() {
+        checkState(executor != null, "Executor needs to be set");
+        InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER", executor);
+        InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG", executor);
+        newDatastores = ImmutableMap.<LogicalDatastoreType, DOMStore>builder()
+                .put(LogicalDatastoreType.OPERATIONAL, operStore)
+                .put(LogicalDatastoreType.CONFIGURATION, configStore)
+                .build();
+
+        newDOMDataBroker = new DOMDataBrokerImpl(newDatastores, executor);
+
+        biCompatibleBroker = new BackwardsCompatibleDataBroker(newDOMDataBroker);
+
+        schemaListeners.add(configStore);
+        schemaListeners.add(operStore);
+        schemaListeners.add(biCompatibleBroker);
+        biDataLegacyBroker = biCompatibleBroker;
     }
 
     public void startBindingDataBroker() {
         checkState(executor != null, "Executor needs to be set");
         baDataImpl = new DataBrokerImpl();
         baDataImpl.setExecutor(executor);
+        baData = baDataImpl;
     }
 
     public void startBindingBroker() {
         checkState(executor != null, "Executor needs to be set");
-        checkState(baDataImpl != null, "Binding Data Broker must be started");
+        checkState(baData != null, "Binding Data Broker must be started");
         checkState(baNotifyImpl != null, "Notification Service must be started");
         baBrokerImpl = new DomForwardedBindingBrokerImpl("test");
 
         baBrokerImpl.getMountManager().setDataCommitExecutor(executor);
         baBrokerImpl.getMountManager().setNotificationExecutor(executor);
         baBrokerImpl.setRpcBroker(new RpcProviderRegistryImpl("test"));
-        baBrokerImpl.setDataBroker(baDataImpl);
+        baBrokerImpl.setDataBroker(baData);
         baBrokerImpl.setNotificationBroker(baNotifyImpl);
         baBrokerImpl.start();
     }
 
     public void startForwarding() {
-        checkState(baDataImpl != null, "Binding Data Broker needs to be started");
-        checkState(biDataImpl != null, "DOM Data Broker needs to be started.");
+        checkState(baData != null, "Binding Data Broker needs to be started");
+        checkState(biDataLegacyBroker != null, "DOM Data Broker needs to be started.");
         checkState(mappingServiceImpl != null, "DOM Mapping Service needs to be started.");
 
         baConnectImpl = BindingDomConnectorDeployer.createConnector(getBindingToDomMappingService());
@@ -160,11 +202,11 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider
     }
 
     private ProviderSession createMockContext() {
-        // TODO Auto-generated method stub
+
         final ClassToInstanceMap<BrokerService> domBrokerServices = ImmutableClassToInstanceMap
                 .<BrokerService> builder()
                 //
-                .put(org.opendaylight.controller.sal.core.api.data.DataProviderService.class, biDataImpl) //
+                .put(org.opendaylight.controller.sal.core.api.data.DataProviderService.class, biDataLegacyBroker) //
                 .put(RpcProvisionRegistry.class, biBrokerImpl.getRouter()) //
                 .put(MountProvisionService.class, biMountImpl) //
                 .build();
@@ -172,12 +214,12 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider
         return new ProviderSession() {
 
             @Override
-            public Future<RpcResult<CompositeNode>> rpc(QName rpc, CompositeNode input) {
+            public Future<RpcResult<CompositeNode>> rpc(final QName rpc, final CompositeNode input) {
                 throw new UnsupportedOperationException();
             }
 
             @Override
-            public <T extends BrokerService> T getService(Class<T> service) {
+            public <T extends BrokerService> T getService(final Class<T> service) {
                 return domBrokerServices.getInstance(service);
             }
 
@@ -197,23 +239,23 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider
 
             @Override
             public ListenerRegistration<RpcRegistrationListener> addRpcRegistrationListener(
-                    RpcRegistrationListener listener) {
+                    final RpcRegistrationListener listener) {
                 return null;
             }
 
             @Override
-            public RpcRegistration addRpcImplementation(QName rpcType, RpcImplementation implementation)
+            public RpcRegistration addRpcImplementation(final QName rpcType, final RpcImplementation implementation)
                     throws IllegalArgumentException {
                 return null;
             }
 
             @Override
-            public RoutedRpcRegistration addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation) {
+            public RoutedRpcRegistration addRoutedRpcImplementation(final QName rpcType, final RpcImplementation implementation) {
                 return null;
             }
 
             @Override
-            public RoutedRpcRegistration addMountedRpcImplementation(QName rpcType, RpcImplementation implementation) {
+            public RoutedRpcRegistration addMountedRpcImplementation(final QName rpcType, final RpcImplementation implementation) {
                 return null;
             }
         };
@@ -226,29 +268,33 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider
         mappingServiceImpl.init();
     }
 
-    public void updateYangSchema(String[] files) {
+    public void updateYangSchema(final String[] files) {
         schemaContext = getContext(files);
+
         if (schemaAwareDataStore != null) {
             schemaAwareDataStore.onGlobalContextUpdated(schemaContext);
         }
         if (mappingServiceImpl != null) {
             mappingServiceImpl.onGlobalContextUpdated(schemaContext);
         }
+        for(SchemaContextListener listener : schemaListeners) {
+            listener.onGlobalContextUpdated(schemaContext);
+        }
     }
 
     public static String[] getAllYangFilesOnClasspath() {
         Predicate<String> predicate = new Predicate<String>() {
             @Override
-            public boolean apply(String input) {
+            public boolean apply(final String input) {
                 return input.endsWith(".yang");
             }
         };
         Reflections reflection = new Reflections("META-INF.yang", new ResourcesScanner());
         Set<String> result = reflection.getResources(predicate);
-        return (String[]) result.toArray(new String[result.size()]);
+        return result.toArray(new String[result.size()]);
     }
 
-    private static SchemaContext getContext(String[] yangFiles) {
+    private static SchemaContext getContext(final String[] yangFiles) {
         ClassLoader loader = BindingTestContext.class.getClassLoader();
         List<InputStream> streams = new ArrayList<>();
         for (String string : yangFiles) {
@@ -260,7 +306,7 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider
         return parser.resolveSchemaContext(modules);
     }
 
-    public void start() {
+    public void startLegacy() {
         startBindingDataBroker();
         startBindingNotificationBroker();
         startBindingBroker();
@@ -275,6 +321,28 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider
         }
     }
 
+    public void start() {
+        startNewDomDataBroker();
+        startDomBroker();
+        startDomMountPoint();
+        startBindingToDomMappingService();
+        startNewBindingDataBroker();
+
+        startBindingNotificationBroker();
+        startBindingBroker();
+
+        startForwarding();
+        if (startWithSchema) {
+            loadYangSchemaFromClasspath();
+        }
+    }
+
+    public void startNewBindingDataBroker() {
+        ForwardedBackwardsCompatibleDataBroker forwarded = new ForwardedBackwardsCompatibleDataBroker(newDOMDataBroker, mappingServiceImpl, executor);
+        schemaListeners.add(forwarded);
+        baData = forwarded;
+    }
+
     private void startDomMountPoint() {
         biMountImpl = new MountPointManagerImpl();
         biMountImpl.setDataBroker(getDomDataBroker());
@@ -285,6 +353,7 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider
         biBrokerImpl = new BrokerImpl();
         biBrokerImpl.setExecutor(executor);
         biBrokerImpl.setRouter(new SchemaAwareRpcBroker("/", this));
+
     }
 
     public void startBindingNotificationBroker() {
@@ -299,11 +368,11 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider
     }
 
     public DataProviderService getBindingDataBroker() {
-        return baDataImpl;
+        return baData;
     }
 
     public org.opendaylight.controller.sal.core.api.data.DataProviderService getDomDataBroker() {
-        return biDataImpl;
+        return biDataLegacyBroker;
     }
 
     public DataStore getDomDataStore() {
index d016754385d1b4ee53e2feb512a75e460b6b37db..471935248506b9cf4c3cdf53d37e8ad43568b33b 100644 (file)
@@ -19,6 +19,7 @@ import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
+import org.junit.Ignore;
 import org.junit.Test;
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
@@ -115,8 +116,14 @@ public class DOMCodecBug01Test extends AbstractDataServiceTest {
      *
      * Reported by Depthi V V
      *
+     * @deprecated This test tests indirect generation, which should be tested
+     *    different way. the test creates conflicting transactions
+     *    and assumes correct commit - to test codec generation
+     *
      */
     @Test
+    @Ignore
+    @Deprecated
     public void testIndirectGeneration() throws Exception {
 
         ExecutorService basePool = Executors.newFixedThreadPool(2);
@@ -218,7 +225,7 @@ public class DOMCodecBug01Test extends AbstractDataServiceTest {
 
     private class CreateFlowTask implements Callable<Void> {
 
-        public CreateFlowTask(Object startSync) {
+        public CreateFlowTask(final Object startSync) {
         }
 
         @Override
index 9d604406983b5bd3a7bb18b875d626a0d77e697c..6784c0cc919ae1f637a1b7c14e0addebcfc3400e 100644 (file)
@@ -66,8 +66,8 @@ public class DOMCodecBug02Test extends AbstractDataServiceTest {
     private static final NodeRef NODE_REF = new NodeRef(NODE_INSTANCE_ID_BA);
 
     /**
-     * This test is ignored, till found out better way to test generation
-     * of classes without leaking of instances from previous run
+     * This test is ignored, till found out better way to test generation of
+     * classes without leaking of instances from previous run
      *
      * @throws Exception
      */
@@ -93,17 +93,17 @@ public class DOMCodecBug02Test extends AbstractDataServiceTest {
 
         ExecutorService testExecutor = Executors.newFixedThreadPool(1);
         testContext.loadYangSchemaFromClasspath();
-        Future<Future<RpcResult<TransactionStatus>>> future = testExecutor.submit(new Callable<Future<RpcResult<TransactionStatus>>>() {
-            @Override
-            public Future<RpcResult<TransactionStatus>> call() throws Exception {
-                NodesBuilder nodesBuilder = new NodesBuilder();
-                nodesBuilder.setNode(Collections.<Node> emptyList());
-                DataModificationTransaction transaction = baDataService.beginTransaction();
-                transaction.putOperationalData(NODES_INSTANCE_ID_BA, nodesBuilder.build());
-                return transaction.commit();
-            }
-        });
-
+        Future<Future<RpcResult<TransactionStatus>>> future = testExecutor
+                .submit(new Callable<Future<RpcResult<TransactionStatus>>>() {
+                    @Override
+                    public Future<RpcResult<TransactionStatus>> call() throws Exception {
+                        NodesBuilder nodesBuilder = new NodesBuilder();
+                        nodesBuilder.setNode(Collections.<Node> emptyList());
+                        DataModificationTransaction transaction = baDataService.beginTransaction();
+                        transaction.putOperationalData(NODES_INSTANCE_ID_BA, nodesBuilder.build());
+                        return transaction.commit();
+                    }
+                });
 
         RpcResult<TransactionStatus> result = future.get().get();
         assertEquals(TransactionStatus.COMMITED, result.getResult());
index 6a050efb35679af9ef283bbef313a09cc92b24d9..e89371337e0a27af96679ca91437862c2cd2f2ef 100644 (file)
@@ -14,6 +14,7 @@ import static org.junit.Assert.assertNull;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 import org.junit.Test;
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
@@ -43,6 +44,8 @@ import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 
+import com.google.common.util.concurrent.SettableFuture;
+
 public class DOMCodecBug03Test extends AbstractDataServiceTest implements DataChangeListener {
 
     private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
@@ -85,7 +88,7 @@ public class DOMCodecBug03Test extends AbstractDataServiceTest implements DataCh
                     .node(SUPPORTED_ACTIONS_QNAME) //
                     .toInstance();
 
-    private DataChangeEvent<InstanceIdentifier<?>, DataObject> receivedChangeEvent;
+    private final SettableFuture<DataChangeEvent<InstanceIdentifier<?>, DataObject>> receivedChangeEvent = SettableFuture.create();
 
 
 
@@ -120,9 +123,10 @@ public class DOMCodecBug03Test extends AbstractDataServiceTest implements DataCh
         RpcResult<TransactionStatus> result = transaction.commit().get();
         assertEquals(TransactionStatus.COMMITED, result.getResult());
 
-        assertNotNull(receivedChangeEvent);
+        DataChangeEvent<InstanceIdentifier<?>, DataObject> potential = receivedChangeEvent.get(1000,TimeUnit.MILLISECONDS);
+        assertNotNull(potential);
 
-        verifyNodes((Nodes) receivedChangeEvent.getUpdatedOperationalSubtree(),original);
+        verifyNodes((Nodes) potential.getUpdatedOperationalSubtree(),original);
         assertBindingIndependentVersion(NODE_INSTANCE_ID_BI);
         Nodes nodes = checkForNodes();
         verifyNodes(nodes,original);
@@ -186,7 +190,7 @@ public class DOMCodecBug03Test extends AbstractDataServiceTest implements DataCh
         assertNull(node);
     }
 
-    private void verifyNodes(Nodes nodes,Node original) {
+    private void verifyNodes(final Nodes nodes,final Node original) {
         assertNotNull(nodes);
         assertNotNull(nodes.getNode());
         assertEquals(1, nodes.getNode().size());
@@ -203,7 +207,7 @@ public class DOMCodecBug03Test extends AbstractDataServiceTest implements DataCh
     }
 
     private void assertBindingIndependentVersion(
-            org.opendaylight.yangtools.yang.data.api.InstanceIdentifier nodeId) {
+            final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier nodeId) {
         CompositeNode node = biDataService.readOperationalData(nodeId);
         assertNotNull(node);
     }
@@ -213,8 +217,8 @@ public class DOMCodecBug03Test extends AbstractDataServiceTest implements DataCh
     }
 
     @Override
-    public void onDataChanged(DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
-        receivedChangeEvent = change;
+    public void onDataChanged(final DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+        receivedChangeEvent.set(change);
     }
 
 }
index 90fa2be21103a7bf89ec8191a2a4d9fad4b301f4..027a8eeb9f103005c7fbf2140c7aa842e0471de3 100644 (file)
@@ -14,7 +14,9 @@ import static org.junit.Assert.assertTrue;
 
 import java.util.Collections;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
+import org.junit.Ignore;
 import org.junit.Test;
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
@@ -41,6 +43,8 @@ import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 
+import com.google.common.util.concurrent.SettableFuture;
+
 public class PutAugmentationTest extends AbstractDataServiceTest implements DataChangeListener {
 
     private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
@@ -89,7 +93,7 @@ public class PutAugmentationTest extends AbstractDataServiceTest implements Data
             .augmentation(FlowCapableNode.class) //
             .build();
 
-    private DataChangeEvent<InstanceIdentifier<?>, DataObject> lastReceivedChangeEvent;
+    private SettableFuture<DataChangeEvent<InstanceIdentifier<?>, DataObject>> lastReceivedChangeEvent;
 
     /**
      * Test for Bug 148
@@ -97,8 +101,9 @@ public class PutAugmentationTest extends AbstractDataServiceTest implements Data
      * @throws Exception
      */
     @Test
+    @Ignore
     public void putNodeAndAugmentation() throws Exception {
-
+        lastReceivedChangeEvent = SettableFuture.create();
         baDataService.registerDataChangeListener(ALL_FLOW_CAPABLE_NODES, this);
 
 
@@ -126,11 +131,16 @@ public class PutAugmentationTest extends AbstractDataServiceTest implements Data
         DataModificationTransaction augmentedTransaction = baDataService.beginTransaction();
         augmentedTransaction.putOperationalData(augmentIdentifier, fnu);
 
+
+        lastReceivedChangeEvent = SettableFuture.create();
         result = augmentedTransaction.commit().get();
         assertEquals(TransactionStatus.COMMITED, result.getResult());
 
-        assertNotNull(lastReceivedChangeEvent);
-        assertTrue(lastReceivedChangeEvent.getCreatedOperationalData().containsKey(FLOW_AUGMENTATION_PATH));
+        DataChangeEvent<InstanceIdentifier<?>, DataObject> potential = lastReceivedChangeEvent.get(1000,TimeUnit.MILLISECONDS);
+        assertNotNull(potential);
+        assertTrue(potential.getCreatedOperationalData().containsKey(FLOW_AUGMENTATION_PATH));
+
+        lastReceivedChangeEvent = SettableFuture.create();
 
         Node augmentedNode = (Node) baDataService.readOperationalData(NODE_INSTANCE_ID_BA);
         assertNotNull(node);
@@ -141,12 +151,13 @@ public class PutAugmentationTest extends AbstractDataServiceTest implements Data
         assertEquals(fnu.getDescription(), readedAugmentation.getDescription());
         assertBindingIndependentVersion(NODE_INSTANCE_ID_BI);
         testNodeRemove();
-        assertTrue(lastReceivedChangeEvent.getRemovedOperationalData().contains(FLOW_AUGMENTATION_PATH));
+        assertTrue(lastReceivedChangeEvent.get(1000,TimeUnit.MILLISECONDS).getRemovedOperationalData().contains(FLOW_AUGMENTATION_PATH));
     }
 
     @Test
+    @Ignore
     public void putNodeWithAugmentation() throws Exception {
-
+        lastReceivedChangeEvent = SettableFuture.create();
         baDataService.registerDataChangeListener(ALL_FLOW_CAPABLE_NODES, this);
 
         NodeBuilder nodeBuilder = new NodeBuilder();
@@ -165,9 +176,11 @@ public class PutAugmentationTest extends AbstractDataServiceTest implements Data
         baseTransaction.putOperationalData(NODE_INSTANCE_ID_BA, nodeBuilder.build());
         RpcResult<TransactionStatus> result = baseTransaction.commit().get();
 
-        assertNotNull(lastReceivedChangeEvent);
-        assertTrue(lastReceivedChangeEvent.getCreatedOperationalData().containsKey(FLOW_AUGMENTATION_PATH));
-        lastReceivedChangeEvent = null;
+
+        DataChangeEvent<InstanceIdentifier<?>, DataObject> potential = lastReceivedChangeEvent.get(1000,TimeUnit.MILLISECONDS);
+        assertNotNull(potential);
+        assertTrue(potential.getCreatedOperationalData().containsKey(FLOW_AUGMENTATION_PATH));
+        lastReceivedChangeEvent = SettableFuture.create();
         assertEquals(TransactionStatus.COMMITED, result.getResult());
 
         FlowCapableNode readedAugmentation = (FlowCapableNode) baDataService.readOperationalData(InstanceIdentifier
@@ -177,10 +190,10 @@ public class PutAugmentationTest extends AbstractDataServiceTest implements Data
         assertEquals(fnu.getHardware(), readedAugmentation.getHardware());
 
         testPutNodeConnectorWithAugmentation();
-        lastReceivedChangeEvent = null;
+        lastReceivedChangeEvent = SettableFuture.create();
         testNodeRemove();
 
-        assertTrue(lastReceivedChangeEvent.getRemovedOperationalData().contains(FLOW_AUGMENTATION_PATH));
+        assertTrue(lastReceivedChangeEvent.get(1000,TimeUnit.MILLISECONDS).getRemovedOperationalData().contains(FLOW_AUGMENTATION_PATH));
     }
 
     private void testPutNodeConnectorWithAugmentation() throws Exception {
@@ -218,7 +231,7 @@ public class PutAugmentationTest extends AbstractDataServiceTest implements Data
         assertNull(node);
     }
 
-    private void verifyNodes(Nodes nodes, Node original) {
+    private void verifyNodes(final Nodes nodes, final Node original) {
         assertNotNull(nodes);
         assertNotNull(nodes.getNode());
         assertEquals(1, nodes.getNode().size());
@@ -234,7 +247,7 @@ public class PutAugmentationTest extends AbstractDataServiceTest implements Data
 
     }
 
-    private void assertBindingIndependentVersion(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier nodeId) {
+    private void assertBindingIndependentVersion(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier nodeId) {
         CompositeNode node = biDataService.readOperationalData(nodeId);
         assertNotNull(node);
     }
@@ -244,8 +257,8 @@ public class PutAugmentationTest extends AbstractDataServiceTest implements Data
     }
 
     @Override
-    public void onDataChanged(DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
-        lastReceivedChangeEvent = change;
+    public void onDataChanged(final DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+        lastReceivedChangeEvent.set(change);
     }
 
 }
index 862c6ea269824c16ba3ec7cd5f4c86f48b1f2843..1661ff230d133fc1b2145070fd420fe98bc3dbbd 100644 (file)
@@ -7,12 +7,20 @@
  */
 package org.opendaylight.controller.sal.binding.test.connect.dom;
 
-import com.google.common.collect.ImmutableMap;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
 import org.junit.Test;
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
-import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
-import org.opendaylight.controller.md.sal.common.api.data.DataModification;
-import org.opendaylight.controller.sal.common.util.CommitHandlerTransactions;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
 import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
 import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpVersion;
@@ -44,24 +52,21 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.N
 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.IpMatchBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4Match;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder;
-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.common.QName;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.SettableFuture;
 
 public class ChangeOriginatedInDomBrokerTest extends AbstractDataServiceTest {
 
+    private static final Logger LOG = LoggerFactory.getLogger(ChangeOriginatedInDomBrokerTest.class);
+
     private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
     private static final QName FLOW_ID_QNAME = QName.create(Flow.QNAME, "id");
     private static final QName FLOW_NODE_QNAME = QName.create(Flow.QNAME, "node");
@@ -74,7 +79,7 @@ public class ChangeOriginatedInDomBrokerTest extends AbstractDataServiceTest {
     private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
     private static final FlowKey FLOW_KEY = new FlowKey(FLOW_ID);
 
-    private DataModification<InstanceIdentifier<? extends DataObject>, DataObject> modificationCapture;
+    private final SettableFuture<DataChangeEvent<InstanceIdentifier<?>, DataObject>> modificationCapture = SettableFuture.create();
 
     private static final Map<QName, Object> NODE_KEY_BI = Collections.<QName, Object> singletonMap(NODE_ID_QNAME,
             NODE_ID);
@@ -121,35 +126,32 @@ public class ChangeOriginatedInDomBrokerTest extends AbstractDataServiceTest {
 
         assertNull(biDataService.readConfigurationData(FLOW_INSTANCE_ID_BI));
 
-        registerCommitHandler();
+        registerChangeListener();
 
         CompositeNode domflow = createTestFlow();
         DataModificationTransaction biTransaction = biDataService.beginTransaction();
         biTransaction.putConfigurationData(FLOW_INSTANCE_ID_BI, domflow);
         RpcResult<TransactionStatus> biResult = biTransaction.commit().get();
         assertEquals(TransactionStatus.COMMITED, biResult.getResult());
-        assertNotNull(modificationCapture);
-        Flow flow = (Flow) modificationCapture.getCreatedConfigurationData().get(FLOW_INSTANCE_ID_BA);
+        DataChangeEvent<InstanceIdentifier<?>, DataObject> event = modificationCapture.get(1000,TimeUnit.MILLISECONDS);
+        assertNotNull(event);
+        LOG.info("Created Configuration :{}",event.getCreatedConfigurationData());
+        Flow flow = (Flow) event.getCreatedConfigurationData().get(FLOW_INSTANCE_ID_BA);
         assertNotNull(flow);
         assertNotNull(flow.getMatch());
         assertEquals(TransactionStatus.COMMITED, biResult.getResult());
 
     }
 
-    private void registerCommitHandler() {
-        DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject> flowTestCommitHandler = new DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject>() {
+    private void registerChangeListener() {
+        baDataService.registerDataChangeListener(FLOWS_PATH_BA, new DataChangeListener() {
 
             @Override
-            public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> requestCommit(
-                    DataModification<InstanceIdentifier<? extends DataObject>, DataObject> modification) {
-                modificationCapture = modification;
-                return CommitHandlerTransactions.allwaysSuccessfulTransaction(modification);
+            public void onDataChanged(final DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+                LOG.info("Data Change listener invoked.");
+                modificationCapture.set(change);
             }
-
-        };
-        Registration<DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject>> registration = baDataService
-                .registerCommitHandler(FLOWS_PATH_BA, flowTestCommitHandler);
-        assertNotNull(registration);
+        });
     }
 
     private CompositeNode createTestFlow() {
@@ -192,6 +194,7 @@ public class ChangeOriginatedInDomBrokerTest extends AbstractDataServiceTest {
 
         // Wrap our Apply Action in an Instruction
         InstructionBuilder ib = new InstructionBuilder();
+        ib.setOrder(0);
         ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
 
         // Put our Instruction in a list of Instructions
index 0a71ef5315ebde5338fbe32ab73f1934b2260f01..5b97da368e8634dca3bb86278e8f767c3eecd208 100644 (file)
@@ -45,7 +45,13 @@ public class TestHelper {
                 bindingAwareSalBundles(),
                 mavenBundle("commons-codec", "commons-codec").versionAsInProject(),
 
-                mavenBundle(CONTROLLER, "protocol-framework").versionAsInProject(), //
+                systemProperty("org.osgi.framework.system.packages.extra").value("sun.nio.ch"),
+                mavenBundle("io.netty", "netty-common").versionAsInProject(), //
+                mavenBundle("io.netty", "netty-buffer").versionAsInProject(), //
+                mavenBundle("io.netty", "netty-handler").versionAsInProject(), //
+                mavenBundle("io.netty", "netty-codec").versionAsInProject(), //
+                mavenBundle("io.netty", "netty-transport").versionAsInProject(), //
+
                 mavenBundle(CONTROLLER, "config-manager").versionAsInProject(), // //
                 mavenBundle("commons-io", "commons-io").versionAsInProject(), //
                 mavenBundle(CONTROLLER, "config-manager").versionAsInProject(), //
@@ -57,6 +63,9 @@ public class TestHelper {
                 mavenBundle(CONTROLLER, "ietf-netconf-monitoring-extension").versionAsInProject(), //
                 mavenBundle(CONTROLLER, "netconf-monitoring").versionAsInProject(), //
 
+                mavenBundle(CONTROLLER, "netty-config-api").versionAsInProject(), //
+                mavenBundle(CONTROLLER, "protocol-framework").versionAsInProject(), //
+
                 mavenBundle(CONTROLLER, "netconf-client").versionAsInProject(), //
                 mavenBundle(CONTROLLER, "netconf-util").versionAsInProject(), //
                 mavenBundle(CONTROLLER + ".thirdparty", "ganymed").versionAsInProject(), //
@@ -64,12 +73,6 @@ public class TestHelper {
 
                 mavenBundle(CONTROLLER, "config-persister-impl").versionAsInProject(), //
 
-                mavenBundle("io.netty", "netty-handler").versionAsInProject(), //
-                mavenBundle("io.netty", "netty-codec").versionAsInProject(), //
-                mavenBundle("io.netty", "netty-buffer").versionAsInProject(), //
-                mavenBundle("io.netty", "netty-transport").versionAsInProject(), //
-                mavenBundle("io.netty", "netty-common").versionAsInProject(), //
-
                 mavenBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.xerces", "2.11.0_1"),
                 mavenBundle("org.eclipse.birt.runtime.3_7_1", "org.apache.xml.resolver", "1.2.0"),
 
index 9ac94e7b8904a3148128ca0f01f6b7d6a72fa5d0..019fc0eb73949a55163c8ddd7b9dcc2bee065037 100644 (file)
@@ -25,6 +25,7 @@ import static org.opendaylight.controller.test.sal.binding.it.TestHelper.junitAn
 import static org.opendaylight.controller.test.sal.binding.it.TestHelper.mdSalCoreBundles;
 import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
 import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemPackages;
 import static org.ops4j.pax.exam.CoreOptions.systemProperty;
 
 @RunWith(PaxExam.class)
@@ -70,6 +71,7 @@ public abstract class AbstractTest {
                 mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(), //
                 mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(), //
                 systemProperty("osgi.bundles.defaultStartLevel").value("4"),
+                systemPackages("sun.nio.ch"),
 
                 mdSalCoreBundles(),
 
index 8a2571c727c5e89dbaf8a764fda5bdf4b6199dec..8798897a1def5063a5e9c7cb501c43c14f80566c 100644 (file)
@@ -1,23 +1,23 @@
-<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">
+<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.1-SNAPSHOT</version>
     </parent>
+
     <artifactId>sal-common-api</artifactId>
+    <packaging>bundle</packaging>
+
     <scm>
-      <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
-      <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
-      <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
-      <tag>HEAD</tag>
-  </scm>
+        <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+        <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+        <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+        <tag>HEAD</tag>
+    </scm>
 
     <dependencies>
-        <dependency>
-            <groupId>org.opendaylight.controller</groupId>
-            <artifactId>sal-common</artifactId>
-        </dependency>
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>yang-common</artifactId>
@@ -27,9 +27,8 @@
             <artifactId>concepts</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.core</artifactId>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
         </dependency>
     </dependencies>
-    <packaging>bundle</packaging>
 </project>
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..87bbfd3
--- /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.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Path;
+
+public interface AsyncDataBroker<P extends Path<P>, D, L extends AsyncDataChangeListener<P, D>> extends //
+        AsyncDataTransactionFactory<P, D> {
+
+    /**
+     *
+     * Scope of Data Change
+     *
+     * Represents scope of data change (addition, replacement, deletion).
+     *
+     * The terminology for types is reused from LDAP
+     *
+     * @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 node,
+        * addition or deletion.
+        *
+        */
+       BASE,
+       /**
+        * Represent a change (addition,replacement,deletion)
+        * of the node or one of it's direct childs.
+        *
+        */
+       ONE,
+       /**
+        * Represents a change of the node or any of it's child nodes.
+        *
+        */
+       SUBTREE
+    }
+
+    @Override
+    public AsyncReadTransaction<P, D> newReadOnlyTransaction();
+
+    @Override
+    public AsyncReadWriteTransaction<P,D> newReadWriteTransaction();
+
+    @Override
+    public AsyncWriteTransaction<P, D> newWriteOnlyTransaction();
+
+    /**
+     * Registers {@link DataChangeListener} for Data Change callbacks
+     * which will be triggered on which will be triggered on the store
+     *
+     * @param store Logical store in which listener is registered.
+     * @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 of the listener, call {@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..f612e51
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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;
+
+public interface AsyncDataChangeEvent<P extends Path<P>,D> extends Immutable {
+    /**
+     * Returns a immutable map of paths and newly created objects
+     *
+     * @return map of paths and newly created objects
+     */
+    Map<P, D> getCreatedData();
+
+    /**
+     * Returns a immutable map of paths and respective updated objects after update.
+     *
+     * Original state of the object is in
+     * {@link #getOriginalData()}
+     *
+     * @return map of paths and newly created objects
+     */
+    Map<P, D> getUpdatedData();
+
+    /**
+     * Returns a immutable set of removed paths.
+     *
+     * Original state of the object is in
+     * {@link #getOriginalData()}
+     *
+     * @return set of removed paths
+     */
+    Set<P> getRemovedPaths();
+
+    /**
+     * Return a immutable map of paths and original state of updated and removed objects.
+     *
+     * This map is populated if at changed path was previous object, and captures
+     * state of previous object.
+     *
+     * @return map of paths and original state of updated and removed objects.
+     */
+    Map<P, ? extends D> getOriginalData();
+
+    /**
+     * Returns a  immutable stable view of data state, which
+     * captures state of data store before the reported change.
+     *
+     *
+     * The view is rooted at the point where the listener, to which the event is being delivered, was registered.
+     *
+     * @return Stable view of data before the change happened, rooted at the listener registration path.
+     *
+     */
+    D getOriginalSubtree();
+
+    /**
+     * Returns a immutable stable view of data, which captures state of data store
+     * after the reported change.
+     *
+     * The view is rooted at the point where the listener, to which the event is being delivered, was registered.
+     *
+     * @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..49f07bc
--- /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.common.api.data;
+
+import java.util.EventListener;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+public interface AsyncDataChangeListener<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(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..732fed0
--- /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.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+public interface AsyncDataTransactionFactory<P extends Path<P>, D> {
+
+    AsyncReadTransaction<P, D> newReadOnlyTransaction();
+
+    AsyncReadWriteTransaction<P, D> newReadWriteTransaction();
+
+    AsyncWriteTransaction<P,D> newWriteOnlyTransaction();
+
+}
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..1d1d910
--- /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.common.api.data;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public interface AsyncReadTransaction<P extends Path<P>, D> extends AsyncTransaction<P, D> {
+
+    /**
+     *
+     * Reads data from provided logical data store located at provided path
+     *
+     *
+     * @param store
+     *            Logical data store from which read should occur.
+     * @param path
+     *            Path which uniquely identifies subtree which client want to
+     *            read
+     * @return Listenable Future which contains read result
+     *         <ul>
+     *         <li>If data at supplied path exists the {@link Future#get()}
+     *         returns Optional object containing data
+     *         <li>If data at supplied path does not exists the
+     *         {@link Future#get()} returns {@link Optional#absent()}.
+     *         </ul>
+     */
+    ListenableFuture<Optional<D>> read(LogicalDatastoreType store, P path);
+
+}
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..ce740bf
--- /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.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * Transaction enabling client to have combined transaction,
+ * which provides read and write capabilities.
+ *
+ *
+ * @param <P> Path Type
+ * @param <D> Data Type
+ */
+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..23ca275
--- /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.concepts.Identifiable;
+import org.opendaylight.yangtools.concepts.Path;
+
+
+/**
+ *
+ * @author
+ *
+ * @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>,
+    AutoCloseable {
+
+    @Override
+    public Object getIdentifier();
+
+    /**
+     * Closes 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/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..2ce43b5
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * 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.ListenableFuture;
+
+public interface AsyncWriteTransaction<P extends Path<P>, D>  extends AsyncTransaction<P, D> {
+    /**
+     * Cancels transaction.
+     *
+     * Transaction could be only 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.
+     *
+     * @throws IllegalStateException If transaction status is {@link TransactionStatus#COMMITED}
+     *
+     */
+    public void cancel();
+
+    /**
+     * Store a piece of 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(LogicalDatastoreType, Path, Object)}
+     *
+     * @param store Logical data store which should be modified
+     * @param path Data object path
+     * @param data Data object to be written to specified path
+     * @throws IllegalStateException if the transaction is no longer {@link TransactionStatus#NEW}
+     */
+    public void put(LogicalDatastoreType store, 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, use {@link #put(LogicalDatastoreType, Path, Object)} instead.
+     *
+     * @param store Logical data store which should be modified
+     * @param path Data object path
+     * @param data Data object to be written to specified path
+     * @throws IllegalStateException if the transaction is no longer {@link TransactionStatus#NEW}
+     */
+    public void merge(LogicalDatastoreType store, P path, D data);
+
+    /**
+     * Remove 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}
+     */
+    public void delete(LogicalDatastoreType store, P path);
+
+    /**
+     *
+     * Closes transaction and resources allocated to the transaction.
+     *
+     * This call does not change Transaction status. Client SHOULD
+     * explicitly {@link #commit()} or {@link #cancel()} transaction.
+     *
+     * @throws IllegalStateException if the transaction has not been
+     *         updated by invoking {@link #commit()} or {@link #cancel()}.
+     */
+    @Override
+    public void close();
+
+    /**
+     * Initiates a commit of modification. This call logically seals the
+     * transaction, preventing any the client from interacting with the
+     * data stores. The transaction is marked as {@link TransactionStatus#SUBMITED}
+     * and enqueued into the data store backed for processing.
+     *
+     * <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.
+     * @throws IllegalStateException if the transaction is not {@link TransactionStatus#NEW}
+     */
+    public ListenableFuture<RpcResult<TransactionStatus>> commit();
+
+}
index 8787a3fe8d82de85bc7e064177501e017d041cca..669baa8d9e96a6601bfe7cbe49cb99d2ffb00233 100644 (file)
@@ -12,6 +12,12 @@ import java.util.EventListener;
 import org.opendaylight.yangtools.concepts.Path;
 
 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);
 }
index 30f4fc03cb18b13e0d518c7b237f10e471a190f6..22e95197bb4c2fb9c3886492cf7635cacf18c7d2 100644 (file)
@@ -7,7 +7,6 @@
  */
 package org.opendaylight.controller.md.sal.common.api.data;
 
-import org.opendaylight.controller.sal.common.DataStoreIdentifier;
 import org.opendaylight.yangtools.concepts.Path;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 /**
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
+
+}
index d542935dd6dbf0fdb91ce72b273d6bc9c87b2668..e7e0eb0ff87e424973de4b9c9d6fdf9111ccae4c 100644 (file)
@@ -13,17 +13,34 @@ 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 transactions as if they happened. A chain
  * makes no guarantees of atomicity, in fact transactions are committed as soon as possible.
+ *
  */
-public interface TransactionChain<P extends Path<P>, D> extends AutoCloseable {
+public interface TransactionChain<P extends Path<P>, D> extends AutoCloseable, AsyncDataTransactionFactory<P, D> {
+
     /**
-     * Create a new transaction which will continue the chain. The previous transaction
-     * has to be either COMMITTED or CANCELLED.
+     * Create a new read only transaction which will continue the chain.
+     * The previous read-write transaction has to be either COMMITED or CANCELLED.
      *
      * @return New transaction in the chain.
-     * @throws IllegalStateException if the previous transaction was not COMMITTED or CANCELLED.
+     * @throws IllegalStateException if the previous transaction was not COMMITED
+     *    or CANCELLED.
      * @throws TransactionChainClosedException if the chain has been closed.
      */
-    DataModification<P, D> newTransaction();
+    @Override
+    public AsyncReadTransaction<P, D> newReadOnlyTransaction();
+
+
+    /**
+     * Create a new read write transaction which will continue the chain.
+     * The previous read-write transaction has to be either COMMITED or CANCELLED.
+     *
+     * @return New transaction in the chain.
+     * @throws IllegalStateException if the previous transaction was not COMMITTED
+     *    or CANCELLED.
+     * @throws TransactionChainClosedException if the chain has been closed.
+     */
+    @Override
+    public AsyncReadWriteTransaction<P, D> newReadWriteTransaction();
 
     @Override
     void close();
index 4dac6f557ebda436b29d2e1aaea9df86682fb90a..52b0812736af1f236c292b28eefe9fe51db1245c 100644 (file)
@@ -21,7 +21,7 @@ public interface TransactionChainListener extends EventListener {
      * @param transaction Transaction which caused the chain to fail
      * @param cause The cause of transaction failure
      */
-    void onTransactionChainFailed(TransactionChain<?, ?> chain, DataModification<?, ?> transaction, Throwable cause);
+    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
index d3504bd0180e9d9d34e94115adab1ab0124ac679..03b7020a1c3614e0b5ea978f789c6093a44d3405 100644 (file)
             <groupId>org.eclipse.xtend</groupId>
             <artifactId>org.eclipse.xtend.lib</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>
index 3ceeb7e44db980855d4afad4b05d1489b0135f96..dc3fef15069e6949a0e93e9fa94f6a2418651012 100644 (file)
@@ -10,10 +10,9 @@ package org.opendaylight.controller.md.sal.common.impl;
 import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.NEW;
 
 import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
 
 import org.opendaylight.controller.md.sal.common.api.data.DataModification;
 import org.opendaylight.controller.md.sal.common.api.data.DataReader;
@@ -21,17 +20,17 @@ import org.opendaylight.yangtools.concepts.Path;
 
 public abstract class AbstractDataModification<P extends Path<P>, D> implements DataModification<P, D> {
 
-    private final ConcurrentMap<P, D> operationalOriginal;
-    private final ConcurrentMap<P, D> configurationOriginal;
+    private final Map<P, D> operationalOriginal;
+    private final Map<P, D> configurationOriginal;
 
-    private final ConcurrentMap<P, D> operationalCreated;
-    private final ConcurrentMap<P, D> configurationCreated;
+    private final Map<P, D> operationalCreated;
+    private final Map<P, D> configurationCreated;
 
-    private final ConcurrentMap<P, D> configurationUpdate;
-    private final ConcurrentMap<P, D> operationalUpdate;
+    private final Map<P, D> configurationUpdate;
+    private final Map<P, D> operationalUpdate;
 
-    private final ConcurrentMap<P, P> configurationRemove;
-    private final ConcurrentMap<P, P> operationalRemove;
+    private final Map<P, P> configurationRemove;
+    private final Map<P, P> operationalRemove;
 
     private final Map<P, D> unmodifiable_configurationOriginal;
     private final Map<P, D> unmodifiable_operationalOriginal;
@@ -43,18 +42,18 @@ public abstract class AbstractDataModification<P extends Path<P>, D> implements
     private final Set<P> unmodifiable_OperationalRemove;
     private final DataReader<P, D> reader;
 
-    public AbstractDataModification(DataReader<P, D> reader) {
+    public AbstractDataModification(final DataReader<P, D> reader) {
         this.reader = reader;
-        this.configurationUpdate = new ConcurrentHashMap<>();
-        this.operationalUpdate = new ConcurrentHashMap<>();
-        this.configurationRemove = new ConcurrentHashMap<>();
-        this.operationalRemove = new ConcurrentHashMap<>();
+        this.configurationUpdate = new LinkedHashMap<>();
+        this.operationalUpdate = new LinkedHashMap<>();
+        this.configurationRemove = new LinkedHashMap<>();
+        this.operationalRemove = new LinkedHashMap<>();
 
-        this.configurationOriginal = new ConcurrentHashMap<>();
-        this.operationalOriginal = new ConcurrentHashMap<>();
+        this.configurationOriginal = new LinkedHashMap<>();
+        this.operationalOriginal = new LinkedHashMap<>();
 
-        this.configurationCreated = new ConcurrentHashMap<>();
-        this.operationalCreated = new ConcurrentHashMap<>();
+        this.configurationCreated = new LinkedHashMap<>();
+        this.operationalCreated = new LinkedHashMap<>();
 
         unmodifiable_configurationOriginal = Collections.unmodifiableMap(configurationOriginal);
         unmodifiable_operationalOriginal = Collections.unmodifiableMap(operationalOriginal);
@@ -67,7 +66,7 @@ public abstract class AbstractDataModification<P extends Path<P>, D> implements
     }
 
     @Override
-    public final void putConfigurationData(P path, D data) {
+    public final void putConfigurationData(final P path, final D data) {
         checkMutable();
         D original = null;
         if ((original = getConfigurationOriginal(path)) == null) {
@@ -78,7 +77,7 @@ public abstract class AbstractDataModification<P extends Path<P>, D> implements
     }
 
     @Override
-    public final void putOperationalData(P path, D data) {
+    public final void putOperationalData(final P path, final D data) {
         checkMutable();
         D original = null;
         if ((original = getOperationalOriginal(path)) == null) {
@@ -88,7 +87,7 @@ public abstract class AbstractDataModification<P extends Path<P>, D> implements
     }
 
     @Override
-    public final void removeOperationalData(P path) {
+    public final void removeOperationalData(final P path) {
         checkMutable();
         getOperationalOriginal(path);
         operationalUpdate.remove(path);
@@ -96,7 +95,7 @@ public abstract class AbstractDataModification<P extends Path<P>, D> implements
     }
 
     @Override
-    public final void removeConfigurationData(P path) {
+    public final void removeConfigurationData(final P path) {
         checkMutable();
         getConfigurationOriginal(path);
         configurationUpdate.remove(path);
@@ -150,46 +149,46 @@ public abstract class AbstractDataModification<P extends Path<P>, D> implements
     }
 
     @Override
-    public D readOperationalData(P path) {
+    public D readOperationalData(final P path) {
         return reader.readOperationalData(path);
     }
 
     @Override
-    public D readConfigurationData(P path) {
+    public D readConfigurationData(final P path) {
         return reader.readConfigurationData(path);
     }
 
-    private D getConfigurationOriginal(P path) {
+    private D getConfigurationOriginal(final P path) {
         D data = configurationOriginal.get(path);
         if (data != null) {
             return data;
         }
         data = reader.readConfigurationData(path);
         if (data != null) {
-            configurationOriginal.putIfAbsent(path, data);
+            configurationOriginal.put(path, data);
             return data;
         }
         return null;
     }
 
-    private D getOperationalOriginal(P path) {
+    private D getOperationalOriginal(final P path) {
         D data = operationalOriginal.get(path);
         if (data != null) {
             return data;
         }
         data = reader.readOperationalData(path);
         if (data != null) {
-            operationalOriginal.putIfAbsent(path, data);
+            operationalOriginal.put(path, data);
             return data;
         }
         return null;
     }
 
-    protected D mergeOperationalData(P path,D stored, D modified) {
+    protected D mergeOperationalData(final P path,final D stored, final D modified) {
         return modified;
     }
 
-    protected D mergeConfigurationData(P path,D stored, D modified) {
+    protected D mergeConfigurationData(final P path,final D stored, final D modified) {
         return modified;
     }
 }
diff --git a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/AbstractRegistration.java b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/AbstractRegistration.java
deleted file mode 100644 (file)
index bb8594f..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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 org.opendaylight.yangtools.concepts.Registration;
-
-public abstract class AbstractRegistration<T> implements Registration<T> {
-
-
-    private final T instance;
-
-    public AbstractRegistration(T instance) {
-        super();
-        this.instance = instance;
-    }
-
-    @Override
-    public final T getInstance() {
-        return instance;
-    }
-
-}
index 4ffb87d5d3939e39662833a3ef8b64954ae30965..22c458a507975027136ddf66ad45986ca00d7c1d 100644 (file)
@@ -8,9 +8,10 @@
 package org.opendaylight.controller.md.sal.common.impl;
 
 import org.opendaylight.controller.md.sal.common.api.routing.RoutedRegistration;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
 import org.opendaylight.yangtools.concepts.Path;
 
-public abstract class AbstractRoutedRegistration<C, P extends Path<P>, S> extends AbstractRegistration<S> implements
+public abstract class AbstractRoutedRegistration<C, P extends Path<P>, S> extends AbstractObjectRegistration<S> implements
         RoutedRegistration<C, P, S> {
 
     public AbstractRoutedRegistration(S instance) {
index 776ff7bfb2005219c3fcb4635420647fa1a08c67..a91799d45823a2e2a6c8f90e69cd0931df18584d 100644 (file)
@@ -12,7 +12,7 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
 
-final class ImmutableDataChangeEvent<P extends Path<P>, D> implements DataChangeEvent<P,D> {
+public final class ImmutableDataChangeEvent<P extends Path<P>, D> implements DataChangeEvent<P,D> {
 
     private final D updatedOperationalSubtree;
     private final Map<P, D> updatedOperational;
@@ -28,7 +28,7 @@ final class ImmutableDataChangeEvent<P extends Path<P>, D> implements DataChange
     private final Map<P, D> createdConfiguration;
 
 
-    public ImmutableDataChangeEvent(Builder<P, D> builder) {
+    private ImmutableDataChangeEvent(final Builder<P, D> builder) {
 
         createdConfiguration = builder.getCreatedConfiguration().build();
         createdOperational = builder.getCreatedOperational().build();
@@ -95,11 +95,11 @@ final class ImmutableDataChangeEvent<P extends Path<P>, D> implements DataChange
         return updatedOperationalSubtree;
     }
 
-    static final <P extends Path<P>,D> Builder<P, D> builder() {
+    public static final <P extends Path<P>,D> Builder<P, D> builder() {
         return new Builder<>();
     }
 
-    static final class Builder<P extends Path<P>,D> {
+    public static final class Builder<P extends Path<P>,D> {
 
         private  D updatedOperationalSubtree;
         private  D originalOperationalSubtree;
@@ -117,7 +117,10 @@ final class ImmutableDataChangeEvent<P extends Path<P>, D> implements DataChange
         private final ImmutableMap.Builder<P, D> createdConfiguration = ImmutableMap.builder();
 
 
-        protected Builder<P,D> addTransaction(DataModification<P, D> data, Predicate<P> keyFilter) {
+
+
+
+        protected Builder<P,D> addTransaction(final DataModification<P, D> data, final Predicate<P> keyFilter) {
             updatedOperational.putAll(Maps.filterKeys(data.getUpdatedOperationalData(), keyFilter));
             updatedConfiguration.putAll(Maps.filterKeys(data.getUpdatedConfigurationData(), keyFilter));
             originalConfiguration.putAll(Maps.filterKeys(data.getOriginalConfigurationData(), keyFilter));
@@ -127,7 +130,7 @@ final class ImmutableDataChangeEvent<P extends Path<P>, D> implements DataChange
             return this;
         }
 
-        protected Builder<P, D> addConfigurationChangeSet(RootedChangeSet<P, D> changeSet) {
+        protected Builder<P, D> addConfigurationChangeSet(final RootedChangeSet<P, D> changeSet) {
             if(changeSet == null) {
                 return this;
             }
@@ -139,7 +142,7 @@ final class ImmutableDataChangeEvent<P extends Path<P>, D> implements DataChange
             return this;
         }
 
-        protected Builder<P, D> addOperationalChangeSet(RootedChangeSet<P, D> changeSet) {
+        protected Builder<P, D> addOperationalChangeSet(final RootedChangeSet<P, D> changeSet) {
             if(changeSet == null) {
                 return this;
             }
@@ -150,7 +153,7 @@ final class ImmutableDataChangeEvent<P extends Path<P>, D> implements DataChange
             return this;
         }
 
-        protected ImmutableDataChangeEvent<P, D> build() {
+        public ImmutableDataChangeEvent<P, D> build() {
             return new ImmutableDataChangeEvent<P,D>(this);
         }
 
@@ -158,7 +161,7 @@ final class ImmutableDataChangeEvent<P extends Path<P>, D> implements DataChange
             return updatedOperationalSubtree;
         }
 
-        protected Builder<P, D> setUpdatedOperationalSubtree(D updatedOperationalSubtree) {
+        public Builder<P, D> setUpdatedOperationalSubtree(final D updatedOperationalSubtree) {
             this.updatedOperationalSubtree = updatedOperationalSubtree;
             return this;
         }
@@ -167,7 +170,7 @@ final class ImmutableDataChangeEvent<P extends Path<P>, D> implements DataChange
             return originalOperationalSubtree;
         }
 
-        protected Builder<P,D> setOriginalOperationalSubtree(D originalOperationalSubtree) {
+        public Builder<P,D> setOriginalOperationalSubtree(final D originalOperationalSubtree) {
             this.originalOperationalSubtree = originalOperationalSubtree;
             return this;
         }
@@ -176,7 +179,7 @@ final class ImmutableDataChangeEvent<P extends Path<P>, D> implements DataChange
             return originalConfigurationSubtree;
         }
 
-        protected Builder<P, D> setOriginalConfigurationSubtree(D originalConfigurationSubtree) {
+        public Builder<P, D> setOriginalConfigurationSubtree(final D originalConfigurationSubtree) {
             this.originalConfigurationSubtree = originalConfigurationSubtree;
             return this;
         }
@@ -185,7 +188,7 @@ final class ImmutableDataChangeEvent<P extends Path<P>, D> implements DataChange
             return updatedConfigurationSubtree;
         }
 
-        protected Builder<P,D> setUpdatedConfigurationSubtree(D updatedConfigurationSubtree) {
+        public Builder<P,D> setUpdatedConfigurationSubtree(final D updatedConfigurationSubtree) {
             this.updatedConfigurationSubtree = updatedConfigurationSubtree;
             return this;
         }
@@ -221,6 +224,26 @@ final class ImmutableDataChangeEvent<P extends Path<P>, D> implements DataChange
         protected ImmutableMap.Builder<P, D> getCreatedConfiguration() {
             return createdConfiguration;
         }
+
+        public Builder<P,D> putOriginalOperational(final Map<? extends P, ? extends D> originalData) {
+            originalOperational.putAll(originalData);
+            return this;
+        }
+
+        public Builder<P,D> putCreatedOperational(final Map<? extends P, ? extends D> originalData) {
+            createdOperational.putAll(originalData);
+            return this;
+        }
+
+        public Builder<P,D> putUpdatedOperational(final Map<? extends P, ? extends D> originalData) {
+            updatedOperational.putAll(originalData);
+            return this;
+        }
+
+        public Builder<P,D> putRemovedOperational(final Set<? extends P> originalData) {
+            removedOperational.addAll(originalData);
+            return this;
+        }
     }
 
 }
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..941f2fd
--- /dev/null
@@ -0,0 +1,572 @@
+package org.opendaylight.controller.md.sal.common.impl.util.compat;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+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.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+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.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+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.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 com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public abstract class DataNormalizationOperation<T extends PathArgument> implements Identifiable<T> {
+
+    private final T identifier;
+
+    @Override
+    public T getIdentifier() {
+        return identifier;
+    };
+
+    protected DataNormalizationOperation(final T identifier) {
+        super();
+        this.identifier = identifier;
+    }
+
+    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);
+
+    public abstract DataNormalizationOperation<?> getChild(QName child);
+
+    public abstract NormalizedNode<?, ?> normalize(Node<?> legacyData);
+
+    private static abstract class SimpleTypeNormalization<T extends PathArgument> extends DataNormalizationOperation<T> {
+
+        protected SimpleTypeNormalization(final T identifier) {
+            super(identifier);
+        }
+
+        @Override
+        public NormalizedNode<?, ?> normalize(final Node<?> legacyData) {
+            checkArgument(legacyData != null);
+            checkArgument(legacyData instanceof SimpleNode<?>);
+            return normalizeImpl((SimpleNode<?>) legacyData);
+        }
+
+        protected abstract NormalizedNode<?, ?> normalizeImpl(SimpleNode<?> node);
+
+        @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) {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+    }
+
+    private static final class LeafNormalization extends SimpleTypeNormalization<NodeIdentifier> {
+
+        protected LeafNormalization(final NodeIdentifier identifier) {
+            super(identifier);
+        }
+
+        @Override
+        protected NormalizedNode<?, ?> normalizeImpl(final SimpleNode<?> node) {
+            return ImmutableNodes.leafNode(node.getNodeType(), node.getValue());
+        }
+
+    }
+
+    private static final class LeafListEntryNormalization extends SimpleTypeNormalization<NodeWithValue> {
+
+        public LeafListEntryNormalization(final LeafListSchemaNode potential) {
+            super(new NodeWithValue(potential.getQName(), null));
+        }
+
+        @Override
+        protected NormalizedNode<?, ?> normalizeImpl(final SimpleNode<?> node) {
+            NodeWithValue nodeId = new NodeWithValue(node.getNodeType(), node.getValue());
+            return Builders.leafSetEntryBuilder().withNodeIdentifier(nodeId).withValue(node.getValue()).build();
+        }
+
+
+        @Override
+        public boolean isKeyedEntry() {
+            return true;
+        }
+    }
+
+    private static abstract class CompositeNodeNormalizationOpertation<T extends PathArgument> extends
+            DataNormalizationOperation<T> {
+
+        protected CompositeNodeNormalizationOpertation(final T identifier) {
+            super(identifier);
+        }
+
+        @SuppressWarnings({ "rawtypes", "unchecked" })
+        @Override
+        public final NormalizedNodeContainer<?, ?, ?> normalize(final Node<?> legacyData) {
+            checkArgument(legacyData != null);
+            if (!isMixin() && getIdentifier().getNodeType() != null) {
+                checkArgument(getIdentifier().getNodeType().equals(legacyData.getNodeType()),
+                        "Node QName must be %s was %s", getIdentifier().getNodeType(), legacyData.getNodeType());
+            }
+            checkArgument(legacyData instanceof CompositeNode, "Node %s should be composite", legacyData);
+            CompositeNode compositeNode = (CompositeNode) legacyData;
+            NormalizedNodeContainerBuilder builder = createBuilder(compositeNode);
+
+            Set<DataNormalizationOperation<?>> usedMixins = new HashSet<>();
+            for (Node<?> childLegacy : compositeNode.getValue()) {
+                DataNormalizationOperation childOp = getChild(childLegacy.getNodeType());
+
+                // We skip unknown nodes if this node is mixin since
+                // it's nodes and parent nodes are interleaved
+                if (childOp == null && isMixin()) {
+                    continue;
+                }
+
+                checkArgument(childOp != null, "Node %s is not allowed inside %s", childLegacy.getNodeType(),
+                        getIdentifier());
+                if (childOp.isMixin()) {
+                    if (usedMixins.contains(childOp)) {
+                        // We already run / processed that mixin, so to avoid
+                        // dupliciry we are
+                        // skiping next nodes.
+                        continue;
+                    }
+                    builder.addChild(childOp.normalize(compositeNode));
+                    usedMixins.add(childOp);
+                } else {
+                    builder.addChild(childOp.normalize(childLegacy));
+                }
+            }
+            return (NormalizedNodeContainer<?, ?, ?>) builder.build();
+        }
+
+        @SuppressWarnings("rawtypes")
+        protected abstract NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode);
+
+    }
+
+    private static abstract class DataContainerNormalizationOperation<T extends PathArgument> extends
+            CompositeNodeNormalizationOpertation<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) {
+            super(identifier);
+            this.schema = schema;
+            this.byArg = new ConcurrentHashMap<>();
+            this.byQName = new ConcurrentHashMap<>();
+        }
+
+        @Override
+        public DataNormalizationOperation<?> getChild(final PathArgument child) {
+            DataNormalizationOperation<?> potential = byArg.get(child);
+            if (potential != null) {
+                return potential;
+            }
+            potential = fromSchema(schema, child);
+            return register(potential);
+        }
+
+        @Override
+        public DataNormalizationOperation<?> getChild(final QName child) {
+            DataNormalizationOperation<?> potential = byQName.get(child);
+            if (potential != null) {
+                return potential;
+            }
+            potential = fromSchemaAndPathArgument(schema, child);
+            return register(potential);
+        }
+
+        private DataNormalizationOperation<?> register(final DataNormalizationOperation<?> potential) {
+            if (potential != null) {
+                byArg.put(potential.getIdentifier(), potential);
+                for (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);
+            keyDefinition = schema.getKeyDefinition();
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
+            ImmutableMap.Builder<QName, Object> keys = ImmutableMap.builder();
+            for (QName key : keyDefinition) {
+
+                SimpleNode<?> valueNode = checkNotNull(compositeNode.getFirstSimpleByName(key),
+                        "List node %s MUST contain leaf %s with value.", getIdentifier().getNodeType(), key);
+                keys.put(key, valueNode.getValue());
+            }
+
+            return Builders.mapEntryBuilder().withNodeIdentifier(
+                    new NodeIdentifierWithPredicates(getIdentifier().getNodeType(), keys.build()));
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+            DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = Builders
+                    .mapEntryBuilder().withNodeIdentifier((NodeIdentifierWithPredicates) currentArg);
+            for (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 ContainerNormalization extends DataContainerNormalizationOperation<NodeIdentifier> {
+
+        protected ContainerNormalization(final ContainerSchemaNode schema) {
+            super(new NodeIdentifier(schema.getQName()), schema);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
+            return Builders.containerBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+            return Builders.containerBuilder().withNodeIdentifier((NodeIdentifier) currentArg).build();
+        }
+
+    }
+
+    private static abstract class MixinNormalizationOp<T extends PathArgument> extends
+            CompositeNodeNormalizationOpertation<T> {
+
+        protected MixinNormalizationOp(final T identifier) {
+            super(identifier);
+        }
+
+        @Override
+        public final boolean isMixin() {
+            return true;
+        }
+
+    }
+
+    private static final class LeafListMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
+
+        private final DataNormalizationOperation<?> innerOp;
+
+        public LeafListMixinNormalization(final LeafListSchemaNode potential) {
+            super(new NodeIdentifier(potential.getQName()));
+            innerOp = new LeafListEntryNormalization(potential);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
+            return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @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 MixinNormalizationOp<AugmentationIdentifier> {
+
+        private final Map<QName, DataNormalizationOperation<?>> byQName;
+        private final Map<PathArgument, DataNormalizationOperation<?>> byArg;
+
+        public AugmentationNormalization(final AugmentationSchema augmentation, final DataNodeContainer schema) {
+            super(augmentationIdentifierFrom(augmentation));
+
+            ImmutableMap.Builder<QName, DataNormalizationOperation<?>> byQNameBuilder = ImmutableMap.builder();
+            ImmutableMap.Builder<PathArgument, DataNormalizationOperation<?>> byArgBuilder = ImmutableMap.builder();
+
+            for (DataSchemaNode augNode : augmentation.getChildNodes()) {
+                DataSchemaNode resolvedNode = schema.getDataChildByName(augNode.getQName());
+                DataNormalizationOperation<?> resolvedOp = fromDataSchemaNode(resolvedNode);
+                byArgBuilder.put(resolvedOp.getIdentifier(), resolvedOp);
+                for (QName resQName : resolvedOp.getQNameIdentifiers()) {
+                    byQNameBuilder.put(resQName, resolvedOp);
+                }
+            }
+            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
+        protected Set<QName> getQNameIdentifiers() {
+            return getIdentifier().getPossibleChildNames();
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
+            return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+            return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier()).build();
+        }
+
+    }
+
+    private static final class ListMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
+
+        private final ListItemNormalization innerNode;
+
+        public ListMixinNormalization(final ListSchemaNode list) {
+            super(new NodeIdentifier(list.getQName()));
+            this.innerNode = new ListItemNormalization(new NodeIdentifierWithPredicates(list.getQName(),
+                    Collections.<QName, Object> emptyMap()), list);
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
+            return Builders.mapBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @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 ChoiceNodeNormalization extends MixinNormalizationOp<NodeIdentifier> {
+
+        private final ImmutableMap<QName, DataNormalizationOperation<?>> byQName;
+        private final ImmutableMap<PathArgument, DataNormalizationOperation<?>> byArg;
+
+        protected ChoiceNodeNormalization(final org.opendaylight.yangtools.yang.model.api.ChoiceNode schema) {
+            super(new NodeIdentifier(schema.getQName()));
+            ImmutableMap.Builder<QName, DataNormalizationOperation<?>> byQNameBuilder = ImmutableMap.builder();
+            ImmutableMap.Builder<PathArgument, DataNormalizationOperation<?>> byArgBuilder = ImmutableMap.builder();
+
+            for (ChoiceCaseNode caze : schema.getCases()) {
+                for (DataSchemaNode cazeChild : caze.getChildNodes()) {
+                    DataNormalizationOperation<?> childOp = fromDataSchemaNode(cazeChild);
+                    byArgBuilder.put(childOp.getIdentifier(), childOp);
+                    for (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
+        protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
+            return Builders.choiceBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+            return Builders.choiceBuilder().withNodeIdentifier(getIdentifier()).build();
+        }
+    }
+
+    public static DataNormalizationOperation<?> fromSchemaAndPathArgument(final DataNodeContainer schema,
+            final QName child) {
+        DataSchemaNode potential = schema.getDataChildByName(child);
+        if (potential == null) {
+            Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices = FluentIterable.from(
+                    schema.getChildNodes()).filter(org.opendaylight.yangtools.yang.model.api.ChoiceNode.class);
+            potential = findChoice(choices, child);
+        }
+        checkArgument(potential != null, "Supplied QName %s is not valid according to schema %s", child, schema);
+        if ((schema instanceof DataSchemaNode) && !((DataSchemaNode) schema).isAugmenting() && potential.isAugmenting()) {
+            return fromAugmentation(schema, (AugmentationTarget) schema, potential);
+        }
+        return fromDataSchemaNode(potential);
+    }
+
+    private static org.opendaylight.yangtools.yang.model.api.ChoiceNode findChoice(
+            final Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices, final QName child) {
+        org.opendaylight.yangtools.yang.model.api.ChoiceNode foundChoice = null;
+        choiceLoop: for (org.opendaylight.yangtools.yang.model.api.ChoiceNode choice : choices) {
+            for (ChoiceCaseNode caze : choice.getCases()) {
+                if (caze.getDataChildByName(child) != null) {
+                    foundChoice = choice;
+                    break choiceLoop;
+                }
+            }
+        }
+        return foundChoice;
+    }
+
+    public static AugmentationIdentifier augmentationIdentifierFrom(final AugmentationSchema augmentation) {
+        ImmutableSet.Builder<QName> potentialChildren = ImmutableSet.builder();
+        for (DataSchemaNode child : augmentation.getChildNodes()) {
+            potentialChildren.add(child.getQName());
+        }
+        return new AugmentationIdentifier(null, potentialChildren.build());
+    }
+
+    private static AugmentationNormalization fromAugmentation(final DataNodeContainer schema,
+            final AugmentationTarget augments, final DataSchemaNode potential) {
+        AugmentationSchema augmentation = null;
+        for (AugmentationSchema aug : augments.getAvailableAugmentations()) {
+            DataSchemaNode child = aug.getDataChildByName(potential.getQName());
+            if (child != null) {
+                augmentation = aug;
+                break;
+            }
+
+        }
+        if (augmentation != null) {
+            return new AugmentationNormalization(augmentation, schema);
+        } else {
+            return null;
+        }
+    }
+
+    private static DataNormalizationOperation<?> fromSchema(final DataNodeContainer schema, final PathArgument child) {
+        if (child instanceof AugmentationIdentifier) {
+            return fromSchemaAndPathArgument(schema, ((AugmentationIdentifier) child).getPossibleChildNames()
+                    .iterator().next());
+        }
+        return fromSchemaAndPathArgument(schema, child.getNodeType());
+    }
+
+    public static DataNormalizationOperation<?> fromDataSchemaNode(final DataSchemaNode potential) {
+        if (potential instanceof ContainerSchemaNode) {
+            return new ContainerNormalization((ContainerSchemaNode) potential);
+        } else if (potential instanceof ListSchemaNode) {
+            return new ListMixinNormalization((ListSchemaNode) potential);
+        } else if (potential instanceof LeafSchemaNode) {
+            return new LeafNormalization(new NodeIdentifier(potential.getQName()));
+        } else if (potential instanceof org.opendaylight.yangtools.yang.model.api.ChoiceNode) {
+            return new ChoiceNodeNormalization((org.opendaylight.yangtools.yang.model.api.ChoiceNode) potential);
+        } else if (potential instanceof LeafListSchemaNode) {
+            return new LeafListMixinNormalization((LeafListSchemaNode) potential);
+        }
+        return null;
+    }
+
+    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..28b2bde
--- /dev/null
@@ -0,0 +1,189 @@
+package org.opendaylight.controller.md.sal.common.impl.util.compat;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MixinNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class DataNormalizer {
+
+    private final DataNormalizationOperation<?> operation;
+
+    public DataNormalizer(final SchemaContext ctx) {
+        operation = DataNormalizationOperation.from(ctx);
+    }
+
+    public InstanceIdentifier toNormalized(final InstanceIdentifier legacy) {
+        ImmutableList.Builder<PathArgument> normalizedArgs = ImmutableList.builder();
+
+        DataNormalizationOperation<?> currentOp = operation;
+        Iterator<PathArgument> arguments = legacy.getPath().iterator();
+        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());
+            }
+            if(arguments.hasNext() || (!currentOp.isKeyedEntry() || legacyArg instanceof NodeIdentifierWithPredicates || legacyArg instanceof NodeWithValue)) {
+                normalizedArgs.add(legacyArg);
+            }
+        }
+        return new InstanceIdentifier(normalizedArgs.build());
+    }
+
+    public Map.Entry<InstanceIdentifier,NormalizedNode<?, ?>> toNormalized(final Map.Entry<InstanceIdentifier,CompositeNode> legacy) {
+        return toNormalized(legacy.getKey(), legacy.getValue());
+    }
+
+    public Map.Entry<InstanceIdentifier,NormalizedNode<?, ?>> toNormalized(final InstanceIdentifier legacyPath, final CompositeNode legacyData) {
+
+        InstanceIdentifier normalizedPath = toNormalized(legacyPath);
+
+        DataNormalizationOperation<?> currentOp = operation;
+        for (PathArgument arg : normalizedPath.getPath()) {
+            currentOp = currentOp.getChild(arg);
+        }
+        // Write Augmentaiton data resolution
+        if (legacyData.getChildren().size() == 1) {
+            DataNormalizationOperation<?> potentialOp = currentOp.getChild(legacyData.getChildren().get(0)
+                    .getNodeType());
+            if(potentialOp.getIdentifier() instanceof AugmentationIdentifier) {
+                currentOp = potentialOp;
+                ArrayList<PathArgument> reworkedArgs = new ArrayList<>(normalizedPath.getPath());
+                reworkedArgs.add(potentialOp.getIdentifier());
+                normalizedPath = new InstanceIdentifier(reworkedArgs);
+            }
+        }
+
+        Preconditions.checkArgument(currentOp != null,
+                "Instance Identifier %s does not reference correct schema Node.", normalizedPath);
+        return new AbstractMap.SimpleEntry<InstanceIdentifier,NormalizedNode<?, ?>>(normalizedPath,currentOp.normalize(legacyData));
+    }
+
+    public InstanceIdentifier toLegacy(final InstanceIdentifier normalized) {
+        ImmutableList.Builder<PathArgument> legacyArgs = ImmutableList.builder();
+        PathArgument previous = null;
+        for (PathArgument normalizedArg : normalized.getPath()) {
+            if (normalizedArg instanceof NodeIdentifier) {
+                if (previous != null) {
+                    legacyArgs.add(previous);
+                }
+                previous = normalizedArg;
+            } else if (normalizedArg instanceof NodeIdentifierWithPredicates) {
+                // We skip previous node, which was mixin.
+                previous = normalizedArg;
+            } else if (normalizedArg instanceof AugmentationIdentifier) {
+                // We ignore argument
+            }
+            // FIXME : Add option for reading choice
+        }
+        if (previous != null) {
+            legacyArgs.add(previous);
+        }
+        return new InstanceIdentifier(legacyArgs.build());
+    }
+
+    public CompositeNode toLegacy(final InstanceIdentifier normalizedPath, final NormalizedNode<?, ?> normalizedData) {
+        // Preconditions.checkArgument(normalizedData instanceof
+        // DataContainerNode<?>,"Node object %s, %s should be of type DataContainerNode",normalizedPath,normalizedData);
+        if (normalizedData instanceof DataContainerNode<?>) {
+            return toLegacyFromDataContainer((DataContainerNode<?>) normalizedData);
+        }
+        return null;
+    }
+
+    public static Node<?> toLegacy(final NormalizedNode<?, ?> node) {
+        if (node instanceof MixinNode) {
+            /**
+             * Direct reading of MixinNodes is not supported,
+             * since it is not possible in legacy APIs create pointer
+             * to Mixin Nodes.
+             *
+             */
+            return null;
+        }
+
+        if (node instanceof DataContainerNode<?>) {
+            return toLegacyFromDataContainer((DataContainerNode<?>) node);
+        }
+        return toLegacySimple(node);
+
+    }
+
+    private static SimpleNode<?> toLegacySimple(final NormalizedNode<?, ?> node) {
+        return new SimpleNodeTOImpl<Object>(node.getNodeType(), null, node.getValue());
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private static CompositeNode toLegacyFromDataContainer(final DataContainerNode<?> node) {
+        CompositeNodeBuilder<ImmutableCompositeNode> builder = ImmutableCompositeNode.builder();
+        builder.setQName(node.getNodeType());
+        for (NormalizedNode<?, ?> child : node.getValue()) {
+            if (child instanceof MixinNode && child instanceof NormalizedNodeContainer<?, ?, ?>) {
+                builder.addAll(toLegacyNodesFromMixin((NormalizedNodeContainer) child));
+            } else {
+                addToBuilder(builder, toLegacy(child));
+            }
+        }
+        return builder.toInstance();
+    }
+
+    private static void addToBuilder(final CompositeNodeBuilder<ImmutableCompositeNode> builder, final Node<?> legacy) {
+        if (legacy != null) {
+            builder.add(legacy);
+        }
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    private static Iterable<Node<?>> toLegacyNodesFromMixin(
+            final NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>> mixin) {
+        ArrayList<Node<?>> ret = new ArrayList<>();
+        for (NormalizedNode<?, ?> child : mixin.getValue()) {
+            if(child instanceof MixinNode && child instanceof NormalizedNodeContainer<?, ?, ?>) {
+                Iterables.addAll(ret,toLegacyNodesFromMixin((NormalizedNodeContainer) child));
+            } else {
+                ret.add(toLegacy(child));
+            }
+        }
+        return FluentIterable.from(ret).filter(new Predicate<Node<?>>() {
+
+            @Override
+            public boolean apply(final Node<?> input) {
+                return input != null;
+            }
+        });
+    }
+
+    public DataNormalizationOperation<?> getRootOperation() {
+        return operation;
+    }
+
+}
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..dbaba29
--- /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 org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataBroker extends AsyncDataBroker<InstanceIdentifier, NormalizedNode<?, ?>, DOMDataChangeListener>, BrokerService {
+    @Override
+    DOMDataReadTransaction newReadOnlyTransaction();
+
+    @Override
+    DOMDataReadWriteTransaction newReadWriteTransaction();
+
+    @Override
+    DOMDataWriteTransaction newWriteOnlyTransaction();
+}
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..d1f0176
--- /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.AsyncDataChangeListener;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataChangeListener extends AsyncDataChangeListener<InstanceIdentifier, 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..5baa5e7
--- /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.AsyncReadTransaction;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataReadTransaction extends AsyncReadTransaction<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+}
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..55600b0
--- /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.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataReadWriteTransaction extends DOMDataReadTransaction, DOMDataWriteTransaction, AsyncReadWriteTransaction<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+}
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..9415973
--- /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.AsyncWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataWriteTransaction extends AsyncWriteTransaction<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+}
index 6af06255c7ef3adbbfd526404babf70b4b285a6b..72df8cb55348cb08fec5a4bad8d35daa125e2ecf 100644 (file)
@@ -16,7 +16,7 @@ import org.opendaylight.controller.sal.core.api.data.DataProviderService;
 import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService;
 import org.opendaylight.controller.sal.core.api.notify.NotificationService;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
@@ -237,7 +237,7 @@ public interface Broker {
         ListenerRegistration<RpcRegistrationListener> addRpcRegistrationListener(RpcRegistrationListener listener);
     }
 
-    public interface RpcRegistration extends Registration<RpcImplementation> {
+    public interface RpcRegistration extends ObjectRegistration<RpcImplementation> {
         QName getType();
 
         @Override
index 18c854646cca6aa08596a84ddaeba77959129219..5698b969771bd4296cbcb8d85ebbd03fcbca59ae 100644 (file)
@@ -8,8 +8,6 @@
 
 package org.opendaylight.controller.sal.core.api.mount;
 
-import java.util.concurrent.Future;
-
 import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
 import org.opendaylight.controller.sal.core.api.notify.NotificationService;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -17,11 +15,29 @@ import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Interface representing a single mount instance and represents a way for
+ * clients to access underlying data, RPCs and notifications.
+ */
 public interface MountInstance extends //
         NotificationService, //
         DataBrokerService {
 
-    Future<RpcResult<CompositeNode>> rpc(QName type, CompositeNode input);
+    /**
+     * Invoke an RPC on the system underlying the mount instance.
+     *
+     * @param type RPC type
+     * @param input RPC input arguments
+     * @return Future representing execution of the RPC.
+     */
+    ListenableFuture<RpcResult<CompositeNode>> rpc(QName type, CompositeNode input);
 
+    /**
+     * Get {@link SchemaContext} of the system underlying the mount instance.
+     *
+     * @return A schema context.
+     */
     SchemaContext getSchemaContext();
 }
index c4c7889f579526a8626a59ab6952ddd6bc151a95..1185c4528cee3e60bbc7bba70ab3beddc5eac021 100644 (file)
@@ -16,18 +16,18 @@ public interface MountProvisionService extends MountService {
 
     @Override
     public MountProvisionInstance getMountPoint(InstanceIdentifier path);
-    
+
     MountProvisionInstance createMountPoint(InstanceIdentifier path);
-    
+
     MountProvisionInstance createOrGetMountPoint(InstanceIdentifier path);
-    
+
     ListenerRegistration<MountProvisionListener> registerProvisionListener(MountProvisionListener listener);
-    
-    public  interface MountProvisionListener extends EventListener {
-        
+
+    public interface MountProvisionListener extends EventListener {
+
         void onMountPointCreated(InstanceIdentifier path);
-        
+
         void onMountPointRemoved(InstanceIdentifier path);
-        
+
     }
 }
index 3180271c935311a64bb69a66b7264ab1ef72cb2a..6d1f17255c7defd4a0e61a890d0b6246b4b466a1 100644 (file)
@@ -10,8 +10,16 @@ package org.opendaylight.controller.sal.core.api.mount;
 import org.opendaylight.controller.sal.core.api.BrokerService;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
 
-
+/**
+ * Client-level interface for interacting with mount points. It provides access
+ * to {@link MountInstance} instances based on their path.
+ */
 public interface MountService extends BrokerService {
-
+    /**
+     * Obtain access to a mount instance registered at the specified path.
+     *
+     * @param path Path at which the instance is registered
+     * @return Reference to the instance, or null if no such instance exists.
+     */
     MountInstance getMountPoint(InstanceIdentifier path);
 }
index b0417eb8a0b440332e46e9296169c4eb818b4cf8..8194dee0f3a72fd9cfb2f9666e8b4f92139f1763 100644 (file)
@@ -22,6 +22,11 @@ module opendaylight-md-sal-dom {
         base "config:service-type";
         config:java-class "org.opendaylight.controller.sal.core.api.data.DataProviderService";
     }
+    
+    identity dom-async-data-broker {
+        base "config:service-type";
+        config:java-class "org.opendaylight.controller.md.sal.dom.api.DOMDataBroker";
+    }
 
     identity dom-data-store {
         base "config:service-type";
index d192bea54036ff62d3680c8561e816b330fbebbb..5063e4339b6d013014064ac2057b6e05683423ba 100644 (file)
   </scm>
 
     <dependencies>
+    <dependency>
+        <groupId>junit</groupId>
+        <artifactId>junit</artifactId>
+    </dependency>
         <dependency>
             <groupId>org.opendaylight.controller</groupId>
             <artifactId>sal-core-api</artifactId>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <version>${slf4j.version}</version>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
                         <Private-Package>
                             org.opendaylight.controller.sal.dom.broker,
                             org.opendaylight.controller.sal.dom.broker.impl,
+                            org.opendaylight.controller.sal.dom.broker.impl.*,
                             org.opendaylight.controller.sal.dom.broker.osgi,
                             org.opendaylight.controller.sal.dom.broker.util,
                             org.opendaylight.controller.config.yang.md.sal.dom.impl,
                             org.opendaylight.controller.config.yang.md.sal.dom.statistics,
+                            org.opendaylight.controller.md.sal.dom.broker.impl,
+                            org.opendaylight.controller.md.sal.dom.broker.impl.*,
+                            org.opendaylight.controller.md.sal.dom.store.impl,
+                            org.opendaylight.controller.md.sal.dom.store.impl.*,
                             org.opendaylight.yangtools.yang.util,
                             org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.dom.impl.rev131028.*
                         </Private-Package>
index f4d642f82d1e9cf54bd5c89c71f8640dab7bf148..767785dbf13c1dac1231b46e2339f044ebe0cb97 100644 (file)
@@ -7,14 +7,12 @@
  */
 package org.opendaylight.controller.config.yang.md.sal.dom.impl;
 
-import org.opendaylight.controller.config.yang.md.sal.dom.statistics.DomBrokerRuntimeMXBeanImpl;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.sal.core.api.data.DataStore;
 import org.opendaylight.controller.sal.dom.broker.BrokerConfigActivator;
 import org.opendaylight.controller.sal.dom.broker.BrokerImpl;
 import org.osgi.framework.BundleContext;
 
-import static com.google.common.base.Preconditions.*;
-
 /**
 *
 */
@@ -23,29 +21,30 @@ public final class DomBrokerImplModule extends org.opendaylight.controller.confi
 
     private BundleContext bundleContext;
 
-    public DomBrokerImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+    public DomBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
         super(identifier, dependencyResolver);
     }
 
-    public DomBrokerImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, DomBrokerImplModule oldModule, java.lang.AutoCloseable oldInstance) {
+    public DomBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final DomBrokerImplModule oldModule, final java.lang.AutoCloseable oldInstance) {
         super(identifier, dependencyResolver, oldModule, oldInstance);
     }
 
     @Override
     public void validate(){
         super.validate();
-        checkArgument(getDataStore() != null, "Data Store needs to be provided for DomBroker");
     }
-    
+
     @Override
     public java.lang.AutoCloseable createInstance() {
         final BrokerImpl broker = new BrokerImpl();
         final BrokerConfigActivator activator = new BrokerConfigActivator();
         final DataStore store = getDataStoreDependency();
-        activator.start(broker, store, getBundleContext());
-        
-        final DomBrokerImplRuntimeMXBean domBrokerRuntimeMXBean = new DomBrokerRuntimeMXBeanImpl(activator.getDataService());
-        getRootRuntimeBeanRegistratorWrapper().register(domBrokerRuntimeMXBean);
+        final DOMDataBroker asyncBroker= getAsyncDataBrokerDependency();
+
+        activator.start(broker, store, asyncBroker,getBundleContext());
+
+//        final DomBrokerImplRuntimeMXBean domBrokerRuntimeMXBean = new DomBrokerRuntimeMXBeanImpl(activator.getDataService());
+//        getRootRuntimeBeanRegistratorWrapper().register(domBrokerRuntimeMXBean);
         return broker;
     }
 
@@ -53,7 +52,7 @@ public final class DomBrokerImplModule extends org.opendaylight.controller.confi
         return this.bundleContext;
     }
 
-    public void setBundleContext(BundleContext bundleContext) {
+    public void setBundleContext(final BundleContext bundleContext) {
         this.bundleContext = bundleContext;
     }
 }
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java
new file mode 100644 (file)
index 0000000..696c10e
--- /dev/null
@@ -0,0 +1,77 @@
+/**
+ * Generated file
+
+ * Generated from: yang module name: opendaylight-sal-dom-broker-impl  yang module local name: dom-inmemory-data-broker
+ * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ * Generated at: Fri Mar 28 17:32:48 CET 2014
+ *
+ * Do not modify this file unless it is present under src/main directory
+ */
+package org.opendaylight.controller.config.yang.md.sal.dom.impl;
+
+import java.util.Hashtable;
+import java.util.concurrent.Executors;
+
+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.DOMDataBrokerImpl;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.osgi.framework.BundleContext;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+/**
+*
+*/
+public final class DomInmemoryDataBrokerModule extends
+        org.opendaylight.controller.config.yang.md.sal.dom.impl.AbstractDomInmemoryDataBrokerModule {
+
+    private BundleContext bundleContext;
+
+    public DomInmemoryDataBrokerModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+            final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public DomInmemoryDataBrokerModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+            final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+            final DomInmemoryDataBrokerModule oldModule, final java.lang.AutoCloseable oldInstance) {
+
+        super(identifier, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    protected void customValidation() {
+        // Add custom validation for module attributes here.
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        ListeningExecutorService storeExecutor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(2));
+        InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("DOM-OPER", storeExecutor);
+        InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("DOM-CFG", storeExecutor);
+        ImmutableMap<LogicalDatastoreType, DOMStore> datastores = ImmutableMap
+                .<LogicalDatastoreType, DOMStore> builder().put(LogicalDatastoreType.OPERATIONAL, operStore)
+                .put(LogicalDatastoreType.CONFIGURATION, configStore).build();
+
+        DOMDataBrokerImpl newDataBroker = new DOMDataBrokerImpl(datastores, MoreExecutors.sameThreadExecutor());
+
+        getBundleContext().registerService(DOMDataBroker.class, newDataBroker, new Hashtable<String, String>());
+
+        getSchemaServiceDependency().registerSchemaServiceListener(operStore);
+        getSchemaServiceDependency().registerSchemaServiceListener(configStore);
+
+        return newDataBroker;
+    }
+
+    private BundleContext getBundleContext() {
+        return bundleContext;
+    }
+
+    void setBundleContext(final BundleContext ctx) {
+        bundleContext = ctx;
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModuleFactory.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModuleFactory.java
new file mode 100644 (file)
index 0000000..92d159c
--- /dev/null
@@ -0,0 +1,39 @@
+/**
+* Generated file
+
+* Generated from: yang module name: opendaylight-sal-dom-broker-impl  yang module local name: dom-inmemory-data-broker
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Fri Mar 28 17:32:48 CET 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.md.sal.dom.impl;
+
+import org.opendaylight.controller.config.api.DependencyResolver;
+import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
+import org.opendaylight.controller.config.spi.Module;
+import org.osgi.framework.BundleContext;
+
+/**
+*
+*/
+public class DomInmemoryDataBrokerModuleFactory extends org.opendaylight.controller.config.yang.md.sal.dom.impl.AbstractDomInmemoryDataBrokerModuleFactory
+{
+
+
+
+    @Override
+    public Module createModule(final String instanceName, final DependencyResolver dependencyResolver, final BundleContext bundleContext) {
+        DomInmemoryDataBrokerModule module = (DomInmemoryDataBrokerModule) super.createModule(instanceName, dependencyResolver, bundleContext);
+        module.setBundleContext(bundleContext);
+        return module;
+    }
+
+    @Override
+    public Module createModule(final String instanceName, final DependencyResolver dependencyResolver,
+            final DynamicMBeanWithInstance old, final BundleContext bundleContext) throws Exception {
+        DomInmemoryDataBrokerModule module = (DomInmemoryDataBrokerModule)  super.createModule(instanceName, dependencyResolver, old, bundleContext);
+        module.setBundleContext(bundleContext);
+        return module;
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerImpl.java
new file mode 100644 (file)
index 0000000..fc87a91
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * 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.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicLong;
+
+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.dom.api.DOMDataBroker;
+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.sal.common.util.Rpcs;
+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.DOMStoreTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+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;
+
+public class DOMDataBrokerImpl implements DOMDataBroker, AutoCloseable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(DOMDataBrokerImpl.class);
+    private static final Logger COORDINATOR_LOG = LoggerFactory.getLogger(CommitCoordination.class);
+    private final ImmutableMap<LogicalDatastoreType, DOMStore> datastores;
+    private final ListeningExecutorService executor;
+    private final AtomicLong txNum = new AtomicLong();
+
+    public DOMDataBrokerImpl(final ImmutableMap<LogicalDatastoreType, DOMStore> datastores,
+            final ListeningExecutorService executor) {
+        super();
+        this.datastores = datastores;
+        this.executor = executor;
+    }
+
+    private static final Function<Iterable<Boolean>, Boolean> AND_FUNCTION = new Function<Iterable<Boolean>, Boolean>() {
+
+        @Override
+        public Boolean apply(final Iterable<Boolean> input) {
+
+            for (Boolean value : input) {
+                if (value == false) {
+                    return Boolean.FALSE;
+                }
+            }
+            return Boolean.TRUE;
+        }
+    };
+
+    @Override
+    public DOMDataReadTransaction newReadOnlyTransaction() {
+        ImmutableMap.Builder<LogicalDatastoreType, DOMStoreReadTransaction> builder = ImmutableMap.builder();
+        for (Entry<LogicalDatastoreType, DOMStore> store : datastores.entrySet()) {
+            builder.put(store.getKey(), store.getValue().newReadOnlyTransaction());
+        }
+        return new ReadOnlyTransactionImpl(newTransactionIdentifier(), builder.build());
+    }
+
+    private Object newTransactionIdentifier() {
+        return "DOM-" + txNum.getAndIncrement();
+    }
+
+    @Override
+    public DOMDataReadWriteTransaction newReadWriteTransaction() {
+        ImmutableMap.Builder<LogicalDatastoreType, DOMStoreReadWriteTransaction> builder = ImmutableMap.builder();
+        for (Entry<LogicalDatastoreType, DOMStore> store : datastores.entrySet()) {
+            builder.put(store.getKey(), store.getValue().newReadWriteTransaction());
+        }
+        return new ReadWriteTransactionImpl(newTransactionIdentifier(), builder.build(), this);
+    }
+
+    @Override
+    public DOMDataWriteTransaction newWriteOnlyTransaction() {
+        ImmutableMap.Builder<LogicalDatastoreType, DOMStoreWriteTransaction> builder = ImmutableMap.builder();
+        for (Entry<LogicalDatastoreType, DOMStore> store : datastores.entrySet()) {
+            builder.put(store.getKey(), store.getValue().newWriteOnlyTransaction());
+        }
+        return new WriteTransactionImpl<DOMStoreWriteTransaction>(newTransactionIdentifier(), builder.build(), this);
+    }
+
+    @Override
+    public ListenerRegistration<DOMDataChangeListener> registerDataChangeListener(final LogicalDatastoreType store,
+            final InstanceIdentifier path, final DOMDataChangeListener listener, final DataChangeScope triggeringScope) {
+
+        DOMStore potentialStore = datastores.get(store);
+        checkState(potentialStore != null, "Requested logical data store is not available.");
+        return potentialStore.registerChangeListener(path, listener, triggeringScope);
+    }
+
+    private ListenableFuture<RpcResult<TransactionStatus>> submit(
+            final WriteTransactionImpl<? extends DOMStoreWriteTransaction> transaction) {
+        LOG.debug("Tx: {} is submitted for execution.", transaction.getIdentifier());
+        return executor.submit(new CommitCoordination(transaction));
+    }
+
+    private abstract static class AbstractCompositeTransaction<K, T extends DOMStoreTransaction> implements
+            AsyncTransaction<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+        private final ImmutableMap<K, T> backingTxs;
+        private final Object identifier;
+
+        protected AbstractCompositeTransaction(final Object identifier, final ImmutableMap<K, T> backingTxs) {
+            this.identifier = checkNotNull(identifier, "Identifier should not be null");
+            this.backingTxs = checkNotNull(backingTxs, "Backing transactions should not be null");
+        }
+
+        protected T getSubtransaction(final K key) {
+            return backingTxs.get(key);
+        }
+
+        public Iterable<T> getSubtransactions() {
+            return backingTxs.values();
+        }
+
+        @Override
+        public Object getIdentifier() {
+            return identifier;
+        }
+
+        @Override
+        public void close() {
+            try {
+                for (T subtransaction : backingTxs.values()) {
+                    subtransaction.close();
+                }
+            } catch (Exception e) {
+                throw new IllegalStateException("Uncaught exception occured during closing transaction.", e);
+            }
+        }
+
+    }
+
+    private static class ReadOnlyTransactionImpl extends
+            AbstractCompositeTransaction<LogicalDatastoreType, DOMStoreReadTransaction> implements
+            DOMDataReadTransaction {
+
+        protected ReadOnlyTransactionImpl(final Object identifier,
+                final ImmutableMap<LogicalDatastoreType, DOMStoreReadTransaction> backingTxs) {
+            super(identifier, backingTxs);
+        }
+
+        @Override
+        public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final LogicalDatastoreType store,
+                final InstanceIdentifier path) {
+            return getSubtransaction(store).read(path);
+        }
+
+    }
+
+    private static class WriteTransactionImpl<T extends DOMStoreWriteTransaction> extends
+            AbstractCompositeTransaction<LogicalDatastoreType, T> implements DOMDataWriteTransaction {
+
+        private final DOMDataBrokerImpl broker;
+        private ImmutableList<DOMStoreThreePhaseCommitCohort> cohorts;
+
+        protected WriteTransactionImpl(final Object identifier, final ImmutableMap<LogicalDatastoreType, T> backingTxs,
+                final DOMDataBrokerImpl broker) {
+            super(identifier, backingTxs);
+            this.broker = broker;
+        }
+
+        public Iterable<DOMStoreThreePhaseCommitCohort> ready() {
+            checkState(cohorts == null, "Transaction was already marked as ready.");
+            ImmutableList.Builder<DOMStoreThreePhaseCommitCohort> cohortsBuilder = ImmutableList.builder();
+            for (DOMStoreWriteTransaction subTx : getSubtransactions()) {
+                cohortsBuilder.add(subTx.ready());
+            }
+            cohorts = cohortsBuilder.build();
+            return cohorts;
+        }
+
+        protected ImmutableList<DOMStoreThreePhaseCommitCohort> getCohorts() {
+            return cohorts;
+        }
+
+        @Override
+        public void put(final LogicalDatastoreType store, final InstanceIdentifier path, final NormalizedNode<?, ?> data) {
+            getSubtransaction(store).write(path, data);
+        }
+
+        @Override
+        public void delete(final LogicalDatastoreType store, final InstanceIdentifier path) {
+            getSubtransaction(store).delete(path);
+        }
+
+        @Override
+        public void merge(final LogicalDatastoreType store, final InstanceIdentifier path,
+                final NormalizedNode<?, ?> data) {
+            // TODO Auto-generated method stub
+            throw new UnsupportedOperationException("Not implemented yet.");
+        }
+
+        @Override
+        public void cancel() {
+            // TODO Auto-generated method stub
+
+        }
+
+        @Override
+        public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+
+            ready();
+            return broker.submit(this);
+        }
+
+    }
+
+    private static class ReadWriteTransactionImpl extends WriteTransactionImpl<DOMStoreReadWriteTransaction> implements
+            DOMDataReadWriteTransaction {
+
+        protected ReadWriteTransactionImpl(final Object identifier,
+                final ImmutableMap<LogicalDatastoreType, DOMStoreReadWriteTransaction> backingTxs,
+                final DOMDataBrokerImpl broker) {
+            // super(identifier, backingTxs);
+            super(identifier, backingTxs, broker);
+        }
+
+        @Override
+        public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final LogicalDatastoreType store,
+                final InstanceIdentifier path) {
+            return getSubtransaction(store).read(path);
+        }
+
+        @Override
+        public void merge(final LogicalDatastoreType store, final InstanceIdentifier path,
+                final NormalizedNode<?, ?> data) {
+
+        }
+    }
+
+    private final class CommitCoordination implements Callable<RpcResult<TransactionStatus>> {
+
+        private final WriteTransactionImpl<? extends DOMStoreWriteTransaction> transaction;
+
+        public CommitCoordination(final WriteTransactionImpl<? extends DOMStoreWriteTransaction> transaction) {
+            this.transaction = transaction;
+        }
+
+        @Override
+        public RpcResult<TransactionStatus> call() throws Exception {
+
+            try {
+                Boolean canCommit = canCommit().get();
+
+                if (canCommit) {
+                    try {
+                        preCommit().get();
+                        try {
+                            commit().get();
+                            COORDINATOR_LOG.debug("Tx: {} Is commited.", transaction.getIdentifier());
+                            return Rpcs.getRpcResult(true, TransactionStatus.COMMITED,
+                                    Collections.<RpcError> emptySet());
+
+                        } catch (InterruptedException | ExecutionException e) {
+                            COORDINATOR_LOG.error("Tx: {} Error during commit", transaction.getIdentifier(), e);
+                        }
+
+                    } catch (InterruptedException | ExecutionException e) {
+                        COORDINATOR_LOG.warn("Tx: {} Error during preCommit, starting Abort",
+                                transaction.getIdentifier(), e);
+                    }
+                } else {
+                    COORDINATOR_LOG.info("Tx: {} Did not pass canCommit phase.", transaction.getIdentifier());
+                    abort().get();
+                }
+            } catch (InterruptedException | ExecutionException e) {
+                COORDINATOR_LOG.warn("Tx: {} Error during canCommit, starting Abort", transaction.getIdentifier(), e);
+
+            }
+            try {
+                abort().get();
+            } catch (InterruptedException | ExecutionException e) {
+                COORDINATOR_LOG.error("Tx: {} Error during abort", transaction.getIdentifier(), e);
+            }
+            return Rpcs.getRpcResult(false, TransactionStatus.FAILED, Collections.<RpcError> emptySet());
+        }
+
+        public ListenableFuture<Void> preCommit() {
+            COORDINATOR_LOG.debug("Transaction {}: PreCommit Started ", transaction.getIdentifier());
+            Builder<ListenableFuture<Void>> ops = ImmutableList.builder();
+            for (DOMStoreThreePhaseCommitCohort cohort : transaction.getCohorts()) {
+                ops.add(cohort.preCommit());
+            }
+            return (ListenableFuture) Futures.allAsList(ops.build());
+        }
+
+        public ListenableFuture<Void> commit() {
+            COORDINATOR_LOG.debug("Transaction {}: Commit Started ", transaction.getIdentifier());
+            Builder<ListenableFuture<Void>> ops = ImmutableList.builder();
+            for (DOMStoreThreePhaseCommitCohort cohort : transaction.getCohorts()) {
+                ops.add(cohort.commit());
+            }
+            return (ListenableFuture) Futures.allAsList(ops.build());
+        }
+
+        public ListenableFuture<Boolean> canCommit() {
+            COORDINATOR_LOG.debug("Transaction {}: CanCommit Started ", transaction.getIdentifier());
+            Builder<ListenableFuture<Boolean>> canCommitOperations = ImmutableList.builder();
+            for (DOMStoreThreePhaseCommitCohort cohort : transaction.getCohorts()) {
+                canCommitOperations.add(cohort.canCommit());
+            }
+            ListenableFuture<List<Boolean>> allCanCommits = Futures.allAsList(canCommitOperations.build());
+            return Futures.transform(allCanCommits, AND_FUNCTION);
+        }
+
+        public ListenableFuture<Void> abort() {
+            COORDINATOR_LOG.debug("Transaction {}: Abort Started ", transaction.getIdentifier());
+            Builder<ListenableFuture<Void>> ops = ImmutableList.builder();
+            for (DOMStoreThreePhaseCommitCohort cohort : transaction.getCohorts()) {
+                ops.add(cohort.abort());
+            }
+            return (ListenableFuture) Futures.allAsList(ops.build());
+        };
+
+    }
+
+    @Override
+    public void close() throws Exception {
+
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/BackwardsCompatibleDataBroker.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/BackwardsCompatibleDataBroker.java
new file mode 100644 (file)
index 0000000..b2217a6
--- /dev/null
@@ -0,0 +1,148 @@
+package org.opendaylight.controller.md.sal.dom.broker.impl.compat;
+
+import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
+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.DataCommitHandlerRegistration;
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.sal.common.DataStoreIdentifier;
+import org.opendaylight.controller.sal.core.api.data.DataChangeListener;
+import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
+import org.opendaylight.controller.sal.core.api.data.DataProviderService;
+import org.opendaylight.controller.sal.core.api.data.DataValidator;
+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.concepts.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+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;
+
+public class BackwardsCompatibleDataBroker implements DataProviderService, SchemaContextListener {
+
+    DOMDataBroker backingBroker;
+    DataNormalizer normalizer;
+    private final ListenerRegistry<DataChangeListener> fakeRegistry = ListenerRegistry.create();
+
+
+    public BackwardsCompatibleDataBroker(final DOMDataBroker newBiDataImpl) {
+        backingBroker = newBiDataImpl;
+    }
+
+    @Override
+    public void onGlobalContextUpdated(final SchemaContext ctx) {
+        normalizer = new DataNormalizer(ctx);
+    }
+
+    @Override
+    public CompositeNode readConfigurationData(final InstanceIdentifier legacyPath) {
+        BackwardsCompatibleTransaction<?> tx = BackwardsCompatibleTransaction.readOnlyTransaction(backingBroker.newReadOnlyTransaction(),normalizer);
+        try {
+            return tx.readConfigurationData(legacyPath);
+        } finally {
+            tx.commit();
+        }
+    }
+
+    @Override
+    public CompositeNode readOperationalData(final InstanceIdentifier legacyPath) {
+        BackwardsCompatibleTransaction<?> tx = BackwardsCompatibleTransaction.readOnlyTransaction(backingBroker.newReadOnlyTransaction(),normalizer);
+        try {
+            return tx.readOperationalData(legacyPath);
+        } finally {
+            tx.commit();
+        }
+    }
+
+    @Override
+    public DataModificationTransaction beginTransaction() {
+        return BackwardsCompatibleTransaction.readWriteTransaction(backingBroker.newReadWriteTransaction(), normalizer);
+    }
+
+    @Override
+    public ListenerRegistration<DataChangeListener> registerDataChangeListener(final InstanceIdentifier path,
+            final DataChangeListener listener) {
+        return fakeRegistry .register(listener);
+    }
+
+    @Override
+    public Registration<DataCommitHandler<InstanceIdentifier, CompositeNode>> registerCommitHandler(
+            final InstanceIdentifier path, final DataCommitHandler<InstanceIdentifier, CompositeNode> commitHandler) {
+        // FIXME Do real forwarding
+        return new AbstractObjectRegistration<DataCommitHandler<InstanceIdentifier,CompositeNode>>(commitHandler) {
+            @Override
+            protected void removeRegistration() {
+                // NOOP
+            }
+        };
+    }
+
+    @Override
+    public ListenerRegistration<RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier, CompositeNode>>> registerCommitHandlerListener(
+            final RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier, CompositeNode>> commitHandlerListener) {
+        return null;
+    }
+
+    // Obsolote functionality
+
+    @Override
+    public void addValidator(final DataStoreIdentifier store, final DataValidator validator) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void removeValidator(final DataStoreIdentifier store, final DataValidator validator) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void addRefresher(final DataStoreIdentifier store, final DataRefresher refresher) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void removeRefresher(final DataStoreIdentifier store, final DataRefresher refresher) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Registration<DataReader<InstanceIdentifier, CompositeNode>> registerConfigurationReader(
+            final InstanceIdentifier path, final DataReader<InstanceIdentifier, CompositeNode> reader) {
+        throw new UnsupportedOperationException("Data Reader contract is not supported.");
+    }
+
+    @Override
+    public Registration<DataReader<InstanceIdentifier, CompositeNode>> registerOperationalReader(
+            final InstanceIdentifier path, final DataReader<InstanceIdentifier, CompositeNode> reader) {
+        throw new UnsupportedOperationException("Data Reader contract is not supported.");
+    }
+
+    private final class TranslatingListenerInvoker implements DOMDataChangeListener, Delegator<DataChangeListener> {
+
+
+        private DataChangeListener delegate;
+
+        @Override
+        public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> normalizedChange) {
+
+            DataChangeEvent<InstanceIdentifier, CompositeNode> legacyChange = null;
+            delegate.onDataChanged(legacyChange);
+        }
+
+        @Override
+        public DataChangeListener getDelegate() {
+
+            return delegate;
+        }
+
+
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/BackwardsCompatibleTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/BackwardsCompatibleTransaction.java
new file mode 100644 (file)
index 0000000..fce2494
--- /dev/null
@@ -0,0 +1,304 @@
+package org.opendaylight.controller.md.sal.dom.broker.impl.compat;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+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.impl.util.compat.DataNormalizationOperation;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+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.impl.schema.Builders;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public abstract class BackwardsCompatibleTransaction<T extends DOMDataReadTransaction> implements
+        DataModificationTransaction, Delegator<T> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(BackwardsCompatibleTransaction.class);
+
+    private final T asyncTx;
+    private final DataNormalizer normalizer;
+
+    protected BackwardsCompatibleTransaction(final T asyncTx, final DataNormalizer normalizer) {
+        super();
+        this.asyncTx = asyncTx;
+        this.normalizer = normalizer;
+    }
+
+    public static BackwardsCompatibleTransaction<?> readOnlyTransaction(final DOMDataReadTransaction readTx,
+            final DataNormalizer normalizer) {
+
+        return new BackwardsCompatibleTransaction<DOMDataReadTransaction>(readTx, normalizer) {
+
+            @Override
+            public TransactionStatus getStatus() {
+                return TransactionStatus.NEW;
+            }
+
+            @Override
+            public Future<RpcResult<TransactionStatus>> commit() {
+                getDelegate().close();
+                return null;
+            }
+        };
+    }
+
+    public static BackwardsCompatibleTransaction<?> readWriteTransaction(final DOMDataReadWriteTransaction rwTx,
+            final DataNormalizer normalizer) {
+        return new ReadWriteTransaction(rwTx, normalizer);
+    }
+
+    protected DataNormalizer getNormalizer() {
+        return normalizer;
+    }
+
+    @Override
+    public T getDelegate() {
+        return asyncTx;
+    };
+
+    @Override
+    public CompositeNode readConfigurationData(final InstanceIdentifier legacyPath) {
+
+        InstanceIdentifier normalizedPath = normalizer.toNormalized(legacyPath);
+
+        ListenableFuture<Optional<NormalizedNode<?, ?>>> normalizedData = asyncTx.read(
+                LogicalDatastoreType.CONFIGURATION, normalizedPath);
+
+        try {
+            return normalizer.toLegacy(normalizedPath, normalizedData.get().orNull());
+        } catch (InterruptedException | ExecutionException e) {
+            return null;
+        }
+    }
+
+    @Override
+    public CompositeNode readOperationalData(final InstanceIdentifier legacyPath) {
+        InstanceIdentifier normalizedPath = normalizer.toNormalized(legacyPath);
+
+        ListenableFuture<Optional<NormalizedNode<?, ?>>> normalizedData = asyncTx.read(
+                LogicalDatastoreType.OPERATIONAL, normalizedPath);
+
+        try {
+            return normalizer.toLegacy(normalizedPath, normalizedData.get().orNull());
+        } catch (InterruptedException | ExecutionException e) {
+            return null;
+        }
+    }
+
+    @Override
+    public ListenerRegistration<DataTransactionListener> registerListener(final DataTransactionListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Map<InstanceIdentifier, CompositeNode> getCreatedConfigurationData() {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public Map<InstanceIdentifier, CompositeNode> getCreatedOperationalData() {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public Map<InstanceIdentifier, CompositeNode> getOriginalConfigurationData() {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public Map<InstanceIdentifier, CompositeNode> getOriginalOperationalData() {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public Set<InstanceIdentifier> getRemovedConfigurationData() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public Set<InstanceIdentifier> getRemovedOperationalData() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public Map<InstanceIdentifier, CompositeNode> getUpdatedConfigurationData() {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public Map<InstanceIdentifier, CompositeNode> getUpdatedOperationalData() {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public void putConfigurationData(final InstanceIdentifier path, final CompositeNode data) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void putOperationalData(final InstanceIdentifier path, final CompositeNode data) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void removeConfigurationData(final InstanceIdentifier path) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void removeOperationalData(final InstanceIdentifier path) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Object getIdentifier() {
+        return asyncTx.getIdentifier();
+    }
+
+    private static final class ReadWriteTransaction extends BackwardsCompatibleTransaction<DOMDataReadWriteTransaction> {
+
+        private TransactionStatus status = TransactionStatus.NEW;
+
+        protected ReadWriteTransaction(final DOMDataReadWriteTransaction asyncTx, final DataNormalizer normalizer) {
+            super(asyncTx, normalizer);
+        }
+
+        @Override
+        public TransactionStatus getStatus() {
+            return status;
+        }
+
+        @Override
+        public Future<RpcResult<TransactionStatus>> commit() {
+            Preconditions.checkState(status == TransactionStatus.NEW);
+            status = TransactionStatus.SUBMITED;
+            return getDelegate().commit();
+        }
+
+        @Override
+        public void putConfigurationData(final InstanceIdentifier legacyPath, final CompositeNode legacyData) {
+            checkNotNull(legacyPath, "Path MUST NOT be null.");
+            checkNotNull(legacyData, "Data for path %s MUST NOT be null",legacyData);
+            Entry<InstanceIdentifier, NormalizedNode<?, ?>> normalizedData = getNormalizer().toNormalized(legacyPath, legacyData);
+            putWithEnsuredParents(LogicalDatastoreType.CONFIGURATION, normalizedData.getKey(), normalizedData.getValue());
+        }
+
+        @Override
+        public void putOperationalData(final InstanceIdentifier legacyPath, final CompositeNode legacyData) {
+            checkNotNull(legacyPath, "Path MUST NOT be null.");
+            checkNotNull(legacyData, "Data for path %s MUST NOT be null",legacyData);
+            Entry<InstanceIdentifier, NormalizedNode<?, ?>> normalizedData = getNormalizer().toNormalized(legacyPath, legacyData);
+            putWithEnsuredParents(LogicalDatastoreType.OPERATIONAL, normalizedData.getKey(), normalizedData.getValue());
+        }
+
+        private void putWithEnsuredParents(final LogicalDatastoreType store, final InstanceIdentifier normalizedPath,
+                final NormalizedNode<?, ?> normalizedData) {
+
+            LOG.trace("write {}:{} ",store,normalizedPath);
+            try {
+            List<PathArgument> currentArguments = new ArrayList<>();
+            DataNormalizationOperation<?> currentOp = getNormalizer().getRootOperation();
+            Iterator<PathArgument> iterator = normalizedPath.getPath().iterator();
+            while(iterator.hasNext()) {
+                PathArgument currentArg = iterator.next();
+                currentOp = currentOp.getChild(currentArg);
+                currentArguments.add(currentArg);
+                InstanceIdentifier currentPath = new InstanceIdentifier(currentArguments);
+                boolean isPresent = getDelegate().read(store, currentPath).get().isPresent();
+                if(isPresent == false && iterator.hasNext()) {
+                    getDelegate().put(store, currentPath, currentOp.createDefault(currentArg));
+                }
+            }
+            } catch (InterruptedException | ExecutionException e) {
+                LOG.error("Exception durring read.",e);
+            }
+
+            getDelegate().put(store, normalizedPath, normalizedData);
+        }
+
+        private boolean isAugmentationChild(final InstanceIdentifier normalizedPath) {
+            List<PathArgument> parentArgs = parentPath(normalizedPath).getPath();
+            if(parentArgs.isEmpty()) {
+                return false;
+            }
+            return Iterables.getLast(parentArgs) instanceof AugmentationIdentifier;
+        }
+
+        private void ensureParentNode(final LogicalDatastoreType store, final InstanceIdentifier normalizedPath,
+                final NormalizedNode<?, ?> normalizedData) {
+            InstanceIdentifier parentPath = parentPath(normalizedPath);
+            PathArgument parentType = Iterables.getLast(parentPath.getPath());
+            if(parentType instanceof AugmentationIdentifier) {
+                AugmentationNode node = Builders.augmentationBuilder()
+                        .withNodeIdentifier((AugmentationIdentifier) parentType)
+                        .build();
+                getDelegate().put(store, parentPath, node);
+            }
+            if(normalizedData instanceof MapEntryNode) {
+                MapNode mapNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(normalizedData.getNodeType())).build();
+                getDelegate().put(store, parentPath, mapNode);
+            } else if (normalizedData instanceof LeafSetNode<?>){
+                LeafSetNode<Object> leafNode = Builders.leafSetBuilder().withNodeIdentifier(new NodeIdentifier(normalizedData.getNodeType())).build();
+                getDelegate().put(store, parentPath, leafNode);
+            }
+
+
+        }
+
+        private InstanceIdentifier parentPath(final InstanceIdentifier normalizedPath) {
+            List<PathArgument> childArgs = normalizedPath.getPath();
+            return new InstanceIdentifier(childArgs.subList(0, childArgs.size() -1));
+        }
+
+        private boolean parentNodeDoesNotExists(final LogicalDatastoreType store, final InstanceIdentifier normalizedPath) {
+            try {
+                return !getDelegate().read(store, parentPath(normalizedPath)).get().isPresent();
+            } catch (InterruptedException | ExecutionException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+
+        @Override
+        public void removeConfigurationData(final InstanceIdentifier legacyPath) {
+            checkNotNull(legacyPath, "Path MUST NOT be null.");
+            getDelegate().delete(LogicalDatastoreType.CONFIGURATION, getNormalizer().toNormalized(legacyPath));
+        }
+
+        @Override
+        public void removeOperationalData(final InstanceIdentifier legacyPath) {
+            checkNotNull(legacyPath, "Path MUST NOT be null.");
+            getDelegate().delete(LogicalDatastoreType.OPERATIONAL, getNormalizer().toNormalized(legacyPath));
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChangeListenerNotifyTask.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChangeListenerNotifyTask.java
new file mode 100644 (file)
index 0000000..306dc23
--- /dev/null
@@ -0,0 +1,34 @@
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class ChangeListenerNotifyTask implements Runnable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ChangeListenerNotifyTask.class);
+    private final Iterable<? extends DataChangeListenerRegistration<?>> listeners;
+    private final AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> event;
+
+    public ChangeListenerNotifyTask(final Iterable<? extends DataChangeListenerRegistration<?>> listeners,
+            final AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> event) {
+        this.listeners = listeners;
+        this.event = event;
+    }
+
+    @Override
+    public void run() {
+
+        for (DataChangeListenerRegistration<?> listener : listeners) {
+            try {
+                listener.getInstance().onDataChanged(event);
+            } catch (Exception e) {
+                LOG.error("Unhandled exception during invoking listener {} with event {}", listener, event, e);
+            }
+        }
+
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMImmutableDataChangeEvent.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMImmutableDataChangeEvent.java
new file mode 100644 (file)
index 0000000..3c6a3d6
--- /dev/null
@@ -0,0 +1,129 @@
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public final class DOMImmutableDataChangeEvent implements
+        AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+    private final NormalizedNode<?, ?> original;
+    private final NormalizedNode<?, ?> updated;
+    private final Map<InstanceIdentifier, ? extends NormalizedNode<?, ?>> originalData;
+    private final Map<InstanceIdentifier, NormalizedNode<?, ?>> createdData;
+    private final Map<InstanceIdentifier, NormalizedNode<?, ?>> updatedData;
+    private final Set<InstanceIdentifier> removedPaths;
+
+    private DOMImmutableDataChangeEvent(final Builder change) {
+        original = change.before;
+        updated = change.after;
+        originalData = change.original.build();
+        createdData = change.created.build();
+        updatedData = change.updated.build();
+        removedPaths = change.removed.build();
+    }
+
+    public static final Builder builder() {
+        return new Builder();
+    }
+
+    @Override
+    public NormalizedNode<?, ?> getOriginalSubtree() {
+        return original;
+    }
+
+    @Override
+    public NormalizedNode<?, ?> getUpdatedSubtree() {
+        return updated;
+    }
+
+    @Override
+    public Map<InstanceIdentifier, ? extends NormalizedNode<?, ?>> getOriginalData() {
+        return originalData;
+    }
+
+    @Override
+    public Map<InstanceIdentifier, NormalizedNode<?, ?>> getCreatedData() {
+        return createdData;
+    }
+
+    @Override
+    public Map<InstanceIdentifier, NormalizedNode<?, ?>> getUpdatedData() {
+        return updatedData;
+    }
+
+    @Override
+    public Set<InstanceIdentifier> getRemovedPaths() {
+        return removedPaths;
+    }
+
+    @Override
+    public String toString() {
+        return "DOMImmutableDataChangeEvent [created=" + createdData.keySet() + ", updated=" + updatedData.keySet()
+                + ", removed=" + removedPaths + "]";
+    }
+
+    public static class Builder {
+
+        private NormalizedNode<?, ?> after;
+        private NormalizedNode<?, ?> before;
+
+        private final ImmutableMap.Builder<InstanceIdentifier, NormalizedNode<?, ?>> original = ImmutableMap.builder();
+        private final ImmutableMap.Builder<InstanceIdentifier, NormalizedNode<?, ?>> created = ImmutableMap.builder();
+        private final ImmutableMap.Builder<InstanceIdentifier, NormalizedNode<?, ?>> updated = ImmutableMap.builder();
+        private final ImmutableSet.Builder<InstanceIdentifier> removed = ImmutableSet.builder();
+
+        private Builder() {
+
+        }
+
+        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 InstanceIdentifier path, final NormalizedNode<?, ?> node) {
+            created.put(path, node);
+            return this;
+        }
+
+        public Builder addRemoved(final InstanceIdentifier path, final NormalizedNode<?, ?> node) {
+            original.put(path, node);
+            removed.add(path);
+            return this;
+        }
+
+        public Builder addUpdated(final InstanceIdentifier path, final NormalizedNode<?, ?> before,
+                final NormalizedNode<?, ?> after) {
+            original.put(path, before);
+            updated.put(path, after);
+            return this;
+        }
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DataAndMetadataSnapshot.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DataAndMetadataSnapshot.java
new file mode 100644 (file)
index 0000000..9961fcc
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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.dom.store.impl.tree.StoreMetadataNode;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.TreeNodeUtils;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.base.Optional;
+import com.google.common.primitives.UnsignedLong;
+
+class DataAndMetadataSnapshot {
+
+    private final StoreMetadataNode metadataTree;
+    private final Optional<SchemaContext> schemaContext;
+
+    private DataAndMetadataSnapshot(final StoreMetadataNode metadataTree, final Optional<SchemaContext> schemaCtx) {
+        this.metadataTree = metadataTree;
+        this.schemaContext = schemaCtx;
+    }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static DataAndMetadataSnapshot createEmpty() {
+        return createEmpty(new NodeIdentifier(SchemaContext.NAME));
+    }
+
+
+    public static DataAndMetadataSnapshot createEmpty(final NodeIdentifier rootNode) {
+        NormalizedNode<?, ?> data = Builders.containerBuilder().withNodeIdentifier(rootNode).build();
+        StoreMetadataNode metadata = StoreMetadataNode.builder()
+                .setNodeVersion(UnsignedLong.ZERO)
+                .setSubtreeVersion(UnsignedLong.ZERO)
+                .setData(data)
+                .build();
+        return new DataAndMetadataSnapshot(metadata,Optional.<SchemaContext>absent());
+    }
+
+    public static DataAndMetadataSnapshot createEmpty(final SchemaContext ctx) {
+        NodeIdentifier rootNodeIdentifier = new NodeIdentifier(ctx.getQName());
+        NormalizedNode<?, ?> data = Builders.containerBuilder().withNodeIdentifier(rootNodeIdentifier).build();
+        StoreMetadataNode metadata = StoreMetadataNode.builder()
+                .setData(data)
+                .setNodeVersion(UnsignedLong.ZERO)
+                .setSubtreeVersion(UnsignedLong.ZERO)
+                .build();
+        return new DataAndMetadataSnapshot(metadata, Optional.of(ctx));
+    }
+
+    public Optional<SchemaContext> getSchemaContext() {
+        return schemaContext;
+    }
+
+    public NormalizedNode<?, ?> getDataTree() {
+        return metadataTree.getData();
+    }
+
+    public StoreMetadataNode getMetadataTree() {
+        return metadataTree;
+    }
+
+    public Optional<StoreMetadataNode> read(final InstanceIdentifier path) {
+        return TreeNodeUtils.findNode(metadataTree, path);
+    }
+
+    public static class Builder {
+        private NormalizedNode<?, ?> dataTree;
+        private StoreMetadataNode metadataTree;
+        private SchemaContext schemaContext;
+
+        public NormalizedNode<?, ?> getDataTree() {
+            return dataTree;
+        }
+
+        public Builder setMetadataTree(final StoreMetadataNode metadataTree) {
+            this.metadataTree = metadataTree;
+            return this;
+        }
+
+        public Builder setSchemaContext(final SchemaContext schemaContext) {
+            this.schemaContext = schemaContext;
+            return this;
+        }
+
+        public DataAndMetadataSnapshot build() {
+            return new DataAndMetadataSnapshot(metadataTree, Optional.fromNullable(schemaContext));
+        }
+
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DataChangeEventResolver.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DataChangeEventResolver.java
new file mode 100644 (file)
index 0000000..0948f6d
--- /dev/null
@@ -0,0 +1,238 @@
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.builder;
+import static org.opendaylight.controller.md.sal.dom.store.impl.StoreUtils.append;
+import static org.opendaylight.controller.md.sal.dom.store.impl.tree.TreeNodeUtils.getChild;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.Builder;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerRegistrationNode;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.NodeModification;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+public class DataChangeEventResolver {
+
+
+    private  static final Logger LOG = LoggerFactory.getLogger(DataChangeEventResolver.class);
+
+    private static final DOMImmutableDataChangeEvent NO_CHANGE = builder().build();
+    private InstanceIdentifier rootPath;
+    private ListenerRegistrationNode listenerRoot;
+    private NodeModification modificationRoot;
+    private Optional<StoreMetadataNode> beforeRoot;
+    private Optional<StoreMetadataNode> afterRoot;
+    private final ImmutableList.Builder<ChangeListenerNotifyTask> tasks = ImmutableList.builder();
+
+    protected InstanceIdentifier getRootPath() {
+        return rootPath;
+    }
+
+    protected DataChangeEventResolver setRootPath(final InstanceIdentifier rootPath) {
+        this.rootPath = rootPath;
+        return this;
+    }
+
+    protected ListenerRegistrationNode getListenerRoot() {
+        return listenerRoot;
+    }
+
+    protected DataChangeEventResolver setListenerRoot(final ListenerRegistrationNode listenerRoot) {
+        this.listenerRoot = listenerRoot;
+        return this;
+    }
+
+    protected NodeModification getModificationRoot() {
+        return modificationRoot;
+    }
+
+    protected DataChangeEventResolver setModificationRoot(final NodeModification modificationRoot) {
+        this.modificationRoot = modificationRoot;
+        return this;
+    }
+
+    protected Optional<StoreMetadataNode> getBeforeRoot() {
+        return beforeRoot;
+    }
+
+    protected DataChangeEventResolver setBeforeRoot(final Optional<StoreMetadataNode> beforeRoot) {
+        this.beforeRoot = beforeRoot;
+        return this;
+    }
+
+    protected Optional<StoreMetadataNode> getAfterRoot() {
+        return afterRoot;
+    }
+
+    protected DataChangeEventResolver setAfterRoot(final Optional<StoreMetadataNode> afterRoot) {
+        this.afterRoot = afterRoot;
+        return this;
+    }
+
+    public Iterable<ChangeListenerNotifyTask> resolve() {
+        LOG.trace("Resolving events for {}" ,modificationRoot);
+        resolveAnyChangeEvent(rootPath, Optional.of(listenerRoot), modificationRoot, beforeRoot, afterRoot);
+        return tasks.build();
+    }
+
+    private DOMImmutableDataChangeEvent resolveAnyChangeEvent(final InstanceIdentifier path,
+            final Optional<ListenerRegistrationNode> listeners, final NodeModification modification,
+            final Optional<StoreMetadataNode> before, final Optional<StoreMetadataNode> after) {
+        // No listeners are present in listener registration subtree
+        // no before and after state is present
+        if (!before.isPresent() && !after.isPresent()) {
+            return NO_CHANGE;
+        }
+        switch (modification.getModificationType()) {
+        case SUBTREE_MODIFIED:
+            return resolveSubtreeChangeEvent(path, listeners, modification, before.get(), after.get());
+        case WRITE:
+            if (before.isPresent()) {
+                return resolveReplacedEvent(path, listeners, modification, before.get(), after.get());
+            } else {
+                return resolveCreateEvent(path, listeners, after.get());
+            }
+        case DELETE:
+            return resolveDeleteEvent(path, listeners, before.get());
+        default:
+            return NO_CHANGE;
+        }
+
+    }
+
+    /**
+     * Resolves create events deep down the interest listener tree.
+     *
+     *
+     * @param path
+     * @param listeners
+     * @param afterState
+     * @return
+     */
+    private DOMImmutableDataChangeEvent resolveCreateEvent(final InstanceIdentifier path,
+            final Optional<ListenerRegistrationNode> listeners, final StoreMetadataNode afterState) {
+        final NormalizedNode<?, ?> node = afterState.getData();
+        Builder builder = builder().setAfter(node).addCreated(path, node);
+
+        for (StoreMetadataNode child : afterState.getChildren()) {
+            PathArgument childId = child.getIdentifier();
+            Optional<ListenerRegistrationNode> childListeners = getChild(listeners, childId);
+
+            InstanceIdentifier childPath = StoreUtils.append(path, childId);
+            builder.merge(resolveCreateEvent(childPath, childListeners, child));
+        }
+
+        DOMImmutableDataChangeEvent event = builder.build();
+        if (listeners.isPresent()) {
+            addNotifyTask(listeners.get().getListeners(), event);
+        }
+        return event;
+    }
+
+    private DOMImmutableDataChangeEvent resolveDeleteEvent(final InstanceIdentifier path,
+            final Optional<ListenerRegistrationNode> listeners, final StoreMetadataNode beforeState) {
+        final NormalizedNode<?, ?> node = beforeState.getData();
+        Builder builder = builder().setBefore(node).addRemoved(path, node);
+
+        for (StoreMetadataNode child : beforeState.getChildren()) {
+            PathArgument childId = child.getIdentifier();
+            Optional<ListenerRegistrationNode> childListeners = getChild(listeners, childId);
+            InstanceIdentifier childPath = StoreUtils.append(path, childId);
+            builder.merge(resolveDeleteEvent(childPath, childListeners, child));
+        }
+        DOMImmutableDataChangeEvent event = builder.build();
+        if (listeners.isPresent()) {
+            addNotifyTask(listeners.get().getListeners(), event);
+        }
+        return event;
+
+    }
+
+    private DOMImmutableDataChangeEvent resolveSubtreeChangeEvent(final InstanceIdentifier path,
+            final Optional<ListenerRegistrationNode> listeners, final NodeModification modification,
+            final StoreMetadataNode before, final StoreMetadataNode after) {
+
+        Builder one = builder().setBefore(before.getData()).setAfter(after.getData());
+
+        Builder subtree = builder();
+
+        for (NodeModification childMod : modification.getModifications()) {
+            PathArgument childId = childMod.getIdentifier();
+            InstanceIdentifier childPath = append(path, childId);
+            Optional<ListenerRegistrationNode> childListen = getChild(listeners, childId);
+
+            Optional<StoreMetadataNode> childBefore = before.getChild(childId);
+            Optional<StoreMetadataNode> childAfter = after.getChild(childId);
+
+            switch (childMod.getModificationType()) {
+            case WRITE:
+            case DELETE:
+                one.merge(resolveAnyChangeEvent(childPath, childListen, childMod, childBefore, childAfter));
+                break;
+            case SUBTREE_MODIFIED:
+                subtree.merge(resolveSubtreeChangeEvent(childPath, childListen, childMod, childBefore.get(),
+                        childAfter.get()));
+                break;
+            case UNMODIFIED:
+                // no-op
+                break;
+            }
+        }
+        DOMImmutableDataChangeEvent oneChangeEvent = one.build();
+        subtree.merge(oneChangeEvent);
+        DOMImmutableDataChangeEvent subtreeEvent = subtree.build();
+        if (listeners.isPresent()) {
+            addNotifyTask(listeners.get(), DataChangeScope.ONE, oneChangeEvent);
+            addNotifyTask(listeners.get(), DataChangeScope.SUBTREE, subtreeEvent);
+        }
+        return subtreeEvent;
+    }
+
+    private DOMImmutableDataChangeEvent resolveReplacedEvent(final InstanceIdentifier path,
+            final Optional<ListenerRegistrationNode> listeners, final NodeModification modification,
+            final StoreMetadataNode before, final StoreMetadataNode after) {
+        // FIXME Add task
+        return builder().build();
+    }
+
+    private void addNotifyTask(final ListenerRegistrationNode listenerRegistrationNode, final DataChangeScope scope,
+            final DOMImmutableDataChangeEvent event) {
+        Collection<DataChangeListenerRegistration<?>> potential = listenerRegistrationNode.getListeners();
+        if(potential.isEmpty()) {
+            return;
+        }
+        ArrayList<DataChangeListenerRegistration<?>> toNotify = new ArrayList<>(potential.size());
+        for(DataChangeListenerRegistration<?> listener : potential) {
+            if(scope.equals(listener.getScope())) {
+                toNotify.add(listener);
+            }
+        }
+        addNotifyTask(toNotify, event);
+
+    }
+
+    private void addNotifyTask(final Collection<DataChangeListenerRegistration<?>> listeners,
+            final DOMImmutableDataChangeEvent event) {
+        if(!listeners.isEmpty()) {
+            tasks.add(new ChangeListenerNotifyTask(ImmutableSet.copyOf(listeners),event));
+        }
+    }
+
+    public static DataChangeEventResolver create() {
+        return new DataChangeEventResolver();
+    }
+
+
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DataChangeListenerRegistration.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DataChangeListenerRegistration.java
new file mode 100644 (file)
index 0000000..d3a892a
--- /dev/null
@@ -0,0 +1,22 @@
+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.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DataChangeListenerRegistration<L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>>
+extends ListenerRegistration<L> {
+
+
+    @Override
+    public L getInstance();
+
+    InstanceIdentifier getPath();
+
+    DataChangeScope getScope();
+
+
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java
new file mode 100644 (file)
index 0000000..2091913
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * 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 com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static org.opendaylight.controller.md.sal.dom.store.impl.StoreUtils.increase;
+
+import java.util.Collections;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+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.store.impl.tree.ListenerRegistrationNode;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerRegistrationNode.DataChangeListenerRegistration;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.NodeModification;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
+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.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeUtils;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.primitives.UnsignedLong;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+public class InMemoryDOMDataStore implements DOMStore, Identifiable<String>, SchemaContextListener {
+
+    private static final Logger LOG = LoggerFactory.getLogger(InMemoryDOMDataStore.class);
+    private static final InstanceIdentifier PUBLIC_ROOT_PATH = InstanceIdentifier.builder().build();
+
+
+    private final ListeningExecutorService executor;
+    private final String name;
+    private final AtomicLong txCounter = new AtomicLong(0);
+    private final ListenerRegistrationNode listenerTree;
+    private final AtomicReference<DataAndMetadataSnapshot> snapshot;
+
+    private ModificationApplyOperation operationTree;
+
+    private SchemaContext schemaContext;
+
+    public InMemoryDOMDataStore(final String name, final ListeningExecutorService executor) {
+        this.name = Preconditions.checkNotNull(name);
+        this.executor = Preconditions.checkNotNull(executor);
+        this.listenerTree = ListenerRegistrationNode.createRoot();
+        this.snapshot = new AtomicReference<DataAndMetadataSnapshot>(DataAndMetadataSnapshot.createEmpty());
+        this.operationTree = new AlwaysFailOperation();
+    }
+
+    @Override
+    public final String getIdentifier() {
+        return name;
+    }
+
+    @Override
+    public DOMStoreReadTransaction newReadOnlyTransaction() {
+        return new SnapshotBackedReadTransaction(nextIdentifier(), snapshot.get());
+    }
+
+    @Override
+    public DOMStoreReadWriteTransaction newReadWriteTransaction() {
+        return new SnapshotBackedReadWriteTransaction(nextIdentifier(), snapshot.get(), this, operationTree);
+    }
+
+    @Override
+    public DOMStoreWriteTransaction newWriteOnlyTransaction() {
+        return new SnaphostBackedWriteTransaction(nextIdentifier(), snapshot.get(), this, operationTree);
+    }
+
+    @Override
+    public synchronized void onGlobalContextUpdated(final SchemaContext ctx) {
+        operationTree = SchemaAwareApplyOperationRoot.from(ctx);
+        schemaContext = ctx;
+    }
+
+    @Override
+    public <L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerChangeListener(
+            final InstanceIdentifier path, final L listener, final DataChangeScope scope) {
+        LOG.debug("{}: Registering data change listener {} for {}",name,listener,path);
+        ListenerRegistrationNode listenerNode = listenerTree;
+        for(PathArgument arg : path.getPath()) {
+            listenerNode = listenerNode.ensureChild(arg);
+        }
+
+        /*
+         * 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) {
+            reg = listenerNode.registerDataChangeListener(path, listener, scope);
+
+            Optional<StoreMetadataNode> currentState = snapshot.get().read(path);
+            if (currentState.isPresent()) {
+                final NormalizedNode<?, ?> data = currentState.get().getData();
+
+                final DOMImmutableDataChangeEvent event = DOMImmutableDataChangeEvent.builder() //
+                        .setAfter(data) //
+                        .addCreated(path, data) //
+                        .build();
+                executor.submit(new ChangeListenerNotifyTask(Collections.singletonList(reg), event));
+            }
+        }
+
+        return reg;
+    }
+
+    private synchronized DOMStoreThreePhaseCommitCohort submit(
+            final SnaphostBackedWriteTransaction writeTx) {
+        LOG.debug("Tx: {} is submitted. Modifications: {}",writeTx.getIdentifier(),writeTx.getMutatedView());
+        return new ThreePhaseCommitImpl(writeTx);
+    }
+
+    private Object nextIdentifier() {
+        return name + "-" + txCounter.getAndIncrement();
+    }
+
+    private void commit(final DataAndMetadataSnapshot currentSnapshot,
+            final StoreMetadataNode newDataTree, final Iterable<ChangeListenerNotifyTask> listenerTasks) {
+        LOG.debug("Updating Store snaphot version: {} with version:{}",currentSnapshot.getMetadataTree().getSubtreeVersion(),newDataTree.getSubtreeVersion());
+
+        if(LOG.isTraceEnabled()) {
+            LOG.trace("Data Tree is {}",StoreUtils.toStringTree(newDataTree));
+        }
+
+        final DataAndMetadataSnapshot newSnapshot = DataAndMetadataSnapshot.builder() //
+                .setMetadataTree(newDataTree) //
+                .setSchemaContext(schemaContext) //
+                .build();
+
+        /*
+         * The commit has to occur atomically with regard to listener registrations.
+         */
+        synchronized (this) {
+            final boolean success = snapshot.compareAndSet(currentSnapshot, newSnapshot);
+            checkState(success, "Store snapshot and transaction snapshot differ. This should never happen.");
+
+            for (ChangeListenerNotifyTask task : listenerTasks) {
+                executor.submit(task);
+            }
+        }
+    }
+
+    private static class SnapshotBackedReadTransaction implements DOMStoreReadTransaction {
+
+        private DataAndMetadataSnapshot stableSnapshot;
+        private final Object identifier;
+
+        public SnapshotBackedReadTransaction(final Object identifier, final DataAndMetadataSnapshot snapshot) {
+            this.identifier = identifier;
+            this.stableSnapshot = snapshot;
+            LOG.debug("ReadOnly Tx: {} allocated with snapshot {}",identifier,snapshot.getMetadataTree().getSubtreeVersion());
+
+        }
+
+        @Override
+        public Object getIdentifier() {
+            return identifier;
+        }
+
+        @Override
+        public void close() {
+            stableSnapshot = null;
+        }
+
+        @Override
+        public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final InstanceIdentifier path) {
+            checkNotNull(path, "Path must not be null.");
+            checkState(stableSnapshot != null, "Transaction is closed");
+            return Futures.immediateFuture(NormalizedNodeUtils.findNode(stableSnapshot.getDataTree(), path));
+        }
+
+        @Override
+        public String toString() {
+            return "SnapshotBackedReadTransaction [id =" + identifier + "]";
+        }
+
+    }
+
+    private static class SnaphostBackedWriteTransaction implements DOMStoreWriteTransaction {
+
+        private MutableDataTree mutableTree;
+        private final Object identifier;
+        private InMemoryDOMDataStore store;
+
+        private boolean ready = false;
+
+        public SnaphostBackedWriteTransaction(final Object identifier, final DataAndMetadataSnapshot snapshot,
+                final InMemoryDOMDataStore store, final ModificationApplyOperation applyOper) {
+            this.identifier = identifier;
+            mutableTree = MutableDataTree.from(snapshot, applyOper);
+            this.store = store;
+            LOG.debug("Write Tx: {} allocated with snapshot {}",identifier,snapshot.getMetadataTree().getSubtreeVersion());
+        }
+
+        @Override
+        public Object getIdentifier() {
+            return identifier;
+        }
+
+        @Override
+        public void close() {
+            this.mutableTree = null;
+            this.store = null;
+        }
+
+        @Override
+        public void write(final InstanceIdentifier path, final NormalizedNode<?, ?> data) {
+            checkNotReady();
+            mutableTree.write(path, data);
+        }
+
+        @Override
+        public void delete(final InstanceIdentifier path) {
+            checkNotReady();
+            mutableTree.delete(path);
+        }
+
+        protected boolean isReady() {
+            return ready;
+        }
+
+        protected void checkNotReady() {
+            checkState(!ready, "Transaction is ready. No further modifications allowed.");
+        }
+
+        @Override
+        public synchronized DOMStoreThreePhaseCommitCohort ready() {
+            ready = true;
+            LOG.debug("Store transaction: {} : Ready", getIdentifier());
+            mutableTree.seal();
+            return store.submit(this);
+        }
+
+        protected MutableDataTree getMutatedView() {
+            return mutableTree;
+        }
+
+        @Override
+        public String toString() {
+            return "SnaphostBackedWriteTransaction [id=" + getIdentifier() + ", ready=" + isReady() + "]";
+        }
+
+    }
+
+    private static class SnapshotBackedReadWriteTransaction extends SnaphostBackedWriteTransaction implements
+            DOMStoreReadWriteTransaction {
+
+        protected SnapshotBackedReadWriteTransaction(final Object identifier, final DataAndMetadataSnapshot snapshot,
+                final InMemoryDOMDataStore store, final ModificationApplyOperation applyOper) {
+            super(identifier, snapshot, store, applyOper);
+        }
+
+        @Override
+        public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final InstanceIdentifier path) {
+            return Futures.immediateFuture(getMutatedView().read(path));
+        }
+
+        @Override
+        public String toString() {
+            return "SnapshotBackedReadWriteTransaction [id=" + getIdentifier() + ", ready=" + isReady() + "]";
+        }
+
+    }
+
+    private class ThreePhaseCommitImpl implements DOMStoreThreePhaseCommitCohort {
+
+        private final SnaphostBackedWriteTransaction transaction;
+        private final NodeModification modification;
+
+        private DataAndMetadataSnapshot storeSnapshot;
+        private Optional<StoreMetadataNode> proposedSubtree;
+        private Iterable<ChangeListenerNotifyTask> listenerTasks;
+
+        public ThreePhaseCommitImpl(final SnaphostBackedWriteTransaction writeTransaction) {
+            this.transaction = writeTransaction;
+            this.modification = transaction.getMutatedView().getRootModification();
+        }
+
+        @Override
+        public ListenableFuture<Boolean> canCommit() {
+            final DataAndMetadataSnapshot snapshotCapture = snapshot.get();
+            final ModificationApplyOperation snapshotOperation = operationTree;
+
+            return executor.submit(new Callable<Boolean>() {
+
+                @Override
+                public Boolean call() throws Exception {
+                    boolean applicable = snapshotOperation.isApplicable(modification,
+                            Optional.of(snapshotCapture.getMetadataTree()));
+                    LOG.debug("Store Transcation: {} : canCommit : {}", transaction.getIdentifier(), applicable);
+                    return applicable;
+                }
+            });
+        }
+
+        @Override
+        public ListenableFuture<Void> preCommit() {
+            storeSnapshot = snapshot.get();
+            if(modification.getModificationType() == ModificationType.UNMODIFIED) {
+                return Futures.immediateFuture(null);
+            }
+            return executor.submit(new Callable<Void>() {
+
+
+
+                @Override
+                public Void call() throws Exception {
+                    StoreMetadataNode metadataTree = storeSnapshot.getMetadataTree();
+
+                    proposedSubtree = operationTree.apply(modification, Optional.of(metadataTree),
+                            increase(metadataTree.getSubtreeVersion()));
+
+                    listenerTasks = DataChangeEventResolver.create() //
+                            .setRootPath(PUBLIC_ROOT_PATH) //
+                            .setBeforeRoot(Optional.of(metadataTree)) //
+                            .setAfterRoot(proposedSubtree) //
+                            .setModificationRoot(modification) //
+                            .setListenerRoot(listenerTree) //
+                            .resolve();
+
+                    return null;
+                }
+            });
+        }
+
+        @Override
+        public ListenableFuture<Void> abort() {
+            storeSnapshot = null;
+            proposedSubtree = null;
+            return Futures.<Void> immediateFuture(null);
+        }
+
+        @Override
+        public ListenableFuture<Void> commit() {
+            if(modification.getModificationType() == ModificationType.UNMODIFIED) {
+                return Futures.immediateFuture(null);
+            }
+
+            checkState(proposedSubtree != null,"Proposed subtree must be computed");
+            checkState(storeSnapshot != null,"Proposed subtree must be computed");
+            // return ImmediateFuture<>;
+            InMemoryDOMDataStore.this.commit(storeSnapshot, proposedSubtree.get(),listenerTasks);
+            return Futures.<Void> immediateFuture(null);
+        }
+
+    }
+
+    private static final class AlwaysFailOperation implements ModificationApplyOperation {
+
+        @Override
+        public Optional<StoreMetadataNode> apply(final NodeModification modification,
+                final Optional<StoreMetadataNode> storeMeta, final UnsignedLong subtreeVersion) {
+            throw new IllegalStateException("Schema Context is not available.");
+        }
+
+        @Override
+        public boolean isApplicable(final NodeModification modification, final Optional<StoreMetadataNode> storeMetadata) {
+            throw new IllegalStateException("Schema Context is not available.");
+        }
+
+        @Override
+        public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
+            throw new IllegalStateException("Schema Context is not available.");
+        }
+
+        @Override
+        public void verifyStructure(final NodeModification modification) throws IllegalArgumentException {
+            throw new IllegalStateException("Schema Context is not available.");
+        }
+
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ModificationApplyOperation.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ModificationApplyOperation.java
new file mode 100644 (file)
index 0000000..d02f110
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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.dom.store.impl.tree.NodeModification;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreTreeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+
+import com.google.common.base.Optional;
+import com.google.common.primitives.UnsignedLong;
+
+/**
+ *
+ * Operation responsible for applying {@link NodeModification} on tree.
+ *
+ * Operation is composite - operation on top level node consists of
+ * suboperations on child nodes. This allows to walk operation hierarchy and
+ * invoke suboperations independently.
+ *
+ * <b>Implementation notes</b>
+ * <ul>
+ * <li>
+ * Implementations MUST expose all nested suboperations which operates on child
+ * nodes expose via {@link #getChild(PathArgument)} method.
+ * <li>Same suboperations SHOULD be used when invoked via
+ * {@link #apply(NodeModification, Optional)} if applicable.
+ *
+ *
+ * Hierarchical composite operation which is responsible for applying
+ * modification on particular subtree and creating updated subtree
+ *
+ *
+ */
+public interface ModificationApplyOperation extends StoreTreeNode<ModificationApplyOperation> {
+
+    /**
+     *
+     * Implementation of this operation must be stateless and must not change
+     * state of this object.
+     *
+     * @param modification
+     *            NodeModification to be applied
+     * @param storeMeta
+     *            Store Metadata Node on which NodeModification should be
+     *            applied
+     * @param subtreeVersion New subtree version of parent node
+     * @throws IllegalArgumentException
+     *             If it is not possible to apply Operation on provided Metadata
+     *             node
+     * @return new {@link StoreMetadataNode} if operation resulted in updating
+     *         node, {@link Optional#absent()} if {@link NodeModification}
+     *         resulted in deletion of this node.
+     */
+    Optional<StoreMetadataNode> apply(NodeModification modification, Optional<StoreMetadataNode> storeMeta, UnsignedLong subtreeVersion);
+
+    /**
+     *
+     * Checks if provided node modification could be applied to current metadata node.
+     *
+     * @param modification Modification
+     * @param current Metadata Node to which modification should be applied
+     * @return true if modification is applicable
+     *         false if modification is no applicable
+     */
+    boolean isApplicable(NodeModification modification, Optional<StoreMetadataNode> current);
+
+    /**
+     *
+     * Performs structural verification of NodeModification, such as writen values / types
+     * uses right structural elements.
+     *
+     * @param modification to be verified.
+     * @throws IllegalArgumentException If provided NodeModification does not adhere to the structure.
+     */
+    void verifyStructure(NodeModification modification) throws IllegalArgumentException;
+
+    /**
+     * Returns a suboperation for specified tree node
+     *
+     * @return Reference to suboperation for specified tree node, {@link Optional#absent()}
+     *    if suboperation is not supported for specified tree node.
+     */
+    @Override
+    public Optional<ModificationApplyOperation> getChild(PathArgument child);
+
+
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/MutableDataTree.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/MutableDataTree.java
new file mode 100644 (file)
index 0000000..f252744
--- /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.dom.store.impl;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.Map.Entry;
+
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.NodeModification;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.TreeNodeUtils;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+
+class MutableDataTree {
+
+    private static final Logger log = LoggerFactory.getLogger(MutableDataTree.class);
+
+    final DataAndMetadataSnapshot snapshot;
+    final NodeModification rootModification;
+    final ModificationApplyOperation strategyTree;
+
+    private boolean sealed = false;
+
+    private MutableDataTree(final DataAndMetadataSnapshot snapshot, final ModificationApplyOperation strategyTree) {
+        this.snapshot = snapshot;
+        this.strategyTree = strategyTree;
+        this.rootModification = NodeModification.createUnmodified(snapshot.getMetadataTree());
+    }
+
+    public void write(final InstanceIdentifier path, final NormalizedNode<?, ?> value) {
+        checkSealed();
+        resolveModificationFor(path).write(value);
+    }
+
+    public void delete(final InstanceIdentifier path) {
+        checkSealed();
+        resolveModificationFor(path).delete();
+    }
+
+    public Optional<NormalizedNode<?, ?>> read(final InstanceIdentifier path) {
+        Entry<InstanceIdentifier, NodeModification> modification = TreeNodeUtils.findClosestsOrFirstMatch(rootModification, path, NodeModification.IS_TERMINAL_PREDICATE);
+
+        Optional<StoreMetadataNode> result = resolveSnapshot(modification);
+        if (result.isPresent()) {
+            NormalizedNode<?, ?> data = result.get().getData();
+            return NormalizedNodeUtils.findNode(modification.getKey(), data, path);
+        }
+        return Optional.absent();
+
+    }
+
+    private Optional<StoreMetadataNode> resolveSnapshot(
+            final Entry<InstanceIdentifier, NodeModification> keyModification) {
+        InstanceIdentifier path = keyModification.getKey();
+        NodeModification modification = keyModification.getValue();
+        return resolveSnapshot(path, modification);
+    }
+
+    private Optional<StoreMetadataNode> resolveSnapshot(final InstanceIdentifier path,
+            final NodeModification modification) {
+        try {
+            Optional<Optional<StoreMetadataNode>> potentialSnapshot = modification.getSnapshotCache();
+            if(potentialSnapshot.isPresent()) {
+                return potentialSnapshot.get();
+            }
+            return resolveModificationStrategy(path).apply(modification, modification.getOriginal(),
+                    StoreUtils.increase(snapshot.getMetadataTree().getSubtreeVersion()));
+        } catch (Exception e) {
+            log.error("Could not create snapshot for {}", path,e);
+            throw e;
+        }
+    }
+
+    private ModificationApplyOperation resolveModificationStrategy(final InstanceIdentifier path) {
+        log.trace("Resolving modification apply strategy for {}", path);
+        return TreeNodeUtils.findNodeChecked(strategyTree, path);
+    }
+
+    private OperationWithModification resolveModificationFor(final InstanceIdentifier path) {
+        NodeModification modification = rootModification;
+        // We ensure strategy is present.
+        ModificationApplyOperation operation = resolveModificationStrategy(path);
+        for (PathArgument pathArg : path.getPath()) {
+            modification = modification.modifyChild(pathArg);
+        }
+        return OperationWithModification.from(operation, modification);
+    }
+
+    public static MutableDataTree from(final DataAndMetadataSnapshot snapshot, final ModificationApplyOperation resolver) {
+        return new MutableDataTree(snapshot, resolver);
+    }
+
+    public void seal() {
+        sealed = true;
+        rootModification.seal();
+    }
+
+    private void checkSealed() {
+        checkState(!sealed, "Data Tree is sealed. No further modifications allowed.");
+    }
+
+    protected NodeModification getRootModification() {
+        return rootModification;
+    }
+
+    @Override
+    public String toString() {
+        return "MutableDataTree [modification=" + rootModification + "]";
+    }
+
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/OperationWithModification.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/OperationWithModification.java
new file mode 100644 (file)
index 0000000..eaf01ae
--- /dev/null
@@ -0,0 +1,44 @@
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.NodeModification;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import com.google.common.base.Optional;
+import com.google.common.primitives.UnsignedLong;
+
+public class OperationWithModification {
+
+    private final NodeModification modification;
+    private final ModificationApplyOperation applyOperation;
+
+    private OperationWithModification(final ModificationApplyOperation op, final NodeModification mod) {
+        this.modification = mod;
+        this.applyOperation = op;
+    }
+
+    public OperationWithModification write(final NormalizedNode<?, ?> value) {
+        modification.write(value);
+        applyOperation.verifyStructure(modification);
+        return this;
+    }
+
+    public OperationWithModification delete() {
+        modification.delete();
+        return this;
+    }
+
+    public boolean isApplicable(final Optional<StoreMetadataNode> data) {
+        return applyOperation.isApplicable(modification, data);
+    }
+
+    public Optional<StoreMetadataNode> apply(final Optional<StoreMetadataNode> data, final UnsignedLong subtreeVersion) {
+        return applyOperation.apply(modification, data, subtreeVersion);
+    }
+
+    public static OperationWithModification from(final ModificationApplyOperation operation,
+            final NodeModification modification) {
+        return new OperationWithModification(operation, modification);
+
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SchemaAwareApplyOperation.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SchemaAwareApplyOperation.java
new file mode 100644 (file)
index 0000000..fd85607
--- /dev/null
@@ -0,0 +1,562 @@
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.NodeModification;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreNodeCompositeBuilder;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+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.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableChoiceNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
+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.ChoiceNode;
+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 com.google.common.base.Function;
+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 com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+import com.google.common.primitives.UnsignedLong;
+
+public abstract class SchemaAwareApplyOperation implements ModificationApplyOperation {
+
+    public static SchemaAwareApplyOperation from(final DataSchemaNode schemaNode) {
+        if (schemaNode instanceof ContainerSchemaNode) {
+            return new ContainerModificationStrategy((ContainerSchemaNode) schemaNode);
+        } else if (schemaNode instanceof ListSchemaNode) {
+            return new ListMapModificationStrategy((ListSchemaNode) schemaNode);
+        } else if (schemaNode instanceof ChoiceNode) {
+            return new ChoiceModificationStrategy((ChoiceNode) schemaNode);
+        } else if (schemaNode instanceof LeafListSchemaNode) {
+            return new LeafSetEntryModificationStrategy((LeafListSchemaNode) schemaNode);
+        } else if (schemaNode instanceof LeafSchemaNode) {
+            return new LeafModificationStrategy((LeafSchemaNode) schemaNode);
+        }
+        throw new IllegalArgumentException("Not supported schema node type for " + schemaNode.getClass());
+    }
+
+    public static SchemaAwareApplyOperation from(final DataNodeContainer resolvedTree,
+            final AugmentationTarget augSchemas, final AugmentationIdentifier identifier) {
+        AugmentationSchema augSchema = null;
+        allAugments : for (AugmentationSchema potential : augSchemas.getAvailableAugmentations()) {
+            boolean containsAll = true;
+            for(DataSchemaNode child : potential.getChildNodes()) {
+                if(identifier.getPossibleChildNames().contains(child.getQName())) {
+                    augSchema = potential;
+                    break allAugments;
+                }
+            }
+        }
+        if(augSchema != null) {
+            return new AugmentationModificationStrategy(augSchema,resolvedTree);
+        }
+        return null;
+    }
+
+
+
+    protected final ModificationApplyOperation resolveChildOperation(final PathArgument child) {
+        Optional<ModificationApplyOperation> potential = getChild(child);
+        checkArgument(potential.isPresent(), "Operation for child %s is not defined.", child);
+        return potential.get();
+    }
+
+    @Override
+    public void verifyStructure(final NodeModification modification) throws IllegalArgumentException {
+        if (modification.getModificationType() == ModificationType.WRITE) {
+            verifyWritenStructure(modification.getWritenValue());
+        }
+    }
+
+    protected abstract void verifyWritenStructure(NormalizedNode<?, ?> writenValue);
+
+    @Override
+    public boolean isApplicable(final NodeModification modification, final Optional<StoreMetadataNode> current) {
+        switch (modification.getModificationType()) {
+        case DELETE:
+            return isDeleteApplicable(modification, current);
+        case SUBTREE_MODIFIED:
+            return isSubtreeModificationApplicable(modification, current);
+        case WRITE:
+            return isWriteApplicable(modification, current);
+        case UNMODIFIED:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    protected boolean isWriteApplicable(final NodeModification modification, final Optional<StoreMetadataNode> current) {
+        Optional<StoreMetadataNode> original = modification.getOriginal();
+        if (original.isPresent() && current.isPresent()) {
+            return isNotConflicting(original.get(), current.get());
+        } else if (current.isPresent()) {
+            return false;
+        }
+        return true;
+
+    }
+
+    protected final boolean isNotConflicting(final StoreMetadataNode original, final StoreMetadataNode current) {
+        return original.getNodeVersion().equals(current.getNodeVersion())
+                && original.getSubtreeVersion().equals(current.getSubtreeVersion());
+    }
+
+    protected abstract boolean isSubtreeModificationApplicable(final NodeModification modification,
+            final Optional<StoreMetadataNode> current);
+
+    private boolean isDeleteApplicable(final NodeModification modification, final Optional<StoreMetadataNode> current) {
+        // FiXME: Add delete conflict detection.
+        return true;
+    }
+
+    @Override
+    public final Optional<StoreMetadataNode> apply(final NodeModification modification,
+            final Optional<StoreMetadataNode> currentMeta, final UnsignedLong subtreeVersion) {
+
+        switch (modification.getModificationType()) {
+        case DELETE:
+            return modification.storeSnapshot(Optional.<StoreMetadataNode>absent());
+        case SUBTREE_MODIFIED:
+            return modification.storeSnapshot(Optional.of(applySubtreeChange(modification, currentMeta.get(), subtreeVersion)));
+        case WRITE:
+            return modification.storeSnapshot(Optional.of(applyWrite(modification, currentMeta, subtreeVersion)));
+        case UNMODIFIED:
+            return currentMeta;
+        default:
+            throw new IllegalArgumentException("Provided modification type is not supported.");
+        }
+    }
+
+    protected abstract StoreMetadataNode applyWrite(NodeModification modification,
+            Optional<StoreMetadataNode> currentMeta, UnsignedLong subtreeVersion);
+
+    protected abstract StoreMetadataNode applySubtreeChange(NodeModification modification,
+            StoreMetadataNode currentMeta, UnsignedLong subtreeVersion);
+
+    public static abstract class ValueNodeModificationStrategy<T extends DataSchemaNode> extends
+            SchemaAwareApplyOperation {
+
+        private final T schema;
+        private final Class<? extends NormalizedNode<?, ?>> nodeClass;
+
+        protected ValueNodeModificationStrategy(final T schema, final Class<? extends NormalizedNode<?, ?>> nodeClass) {
+            super();
+            this.schema = schema;
+            this.nodeClass = nodeClass;
+        }
+
+        @Override
+        protected void verifyWritenStructure(final NormalizedNode<?, ?> writenValue) {
+            checkArgument(nodeClass.isInstance(writenValue), "Node should must be of type %s", nodeClass);
+        }
+
+        @Override
+        public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
+            throw new UnsupportedOperationException("Node " + schema.getPath()
+                    + "is leaf type node. Child nodes not allowed");
+        }
+
+        @Override
+        protected StoreMetadataNode applySubtreeChange(final NodeModification modification,
+                final StoreMetadataNode currentMeta, final UnsignedLong subtreeVersion) {
+            throw new UnsupportedOperationException("Node " + schema.getPath()
+                    + "is leaf type node. Subtree change is not allowed.");
+        }
+
+        @Override
+        protected StoreMetadataNode applyWrite(final NodeModification modification,
+                final Optional<StoreMetadataNode> currentMeta, final UnsignedLong subtreeVersion) {
+            UnsignedLong nodeVersion = subtreeVersion;
+            if (currentMeta.isPresent()) {
+                nodeVersion = StoreUtils.increase(currentMeta.get().getNodeVersion());
+            }
+
+            return StoreMetadataNode.builder().setNodeVersion(nodeVersion).setSubtreeVersion(subtreeVersion)
+                    .setData(modification.getWritenValue()).build();
+        }
+
+        @Override
+        protected boolean isSubtreeModificationApplicable(final NodeModification modification,
+                final Optional<StoreMetadataNode> current) {
+            return false;
+        }
+
+    }
+
+    public static class LeafSetEntryModificationStrategy extends ValueNodeModificationStrategy<LeafListSchemaNode> {
+
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        protected LeafSetEntryModificationStrategy(final LeafListSchemaNode schema) {
+            super(schema, (Class) LeafSetEntryNode.class);
+        }
+    }
+
+    public static class LeafModificationStrategy extends ValueNodeModificationStrategy<LeafSchemaNode> {
+
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        protected LeafModificationStrategy(final LeafSchemaNode schema) {
+            super(schema, (Class) LeafNode.class);
+        }
+    }
+
+    public static abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareApplyOperation {
+
+        private final Class<? extends NormalizedNode<?, ?>> nodeClass;
+
+        protected NormalizedNodeContainerModificationStrategy(final Class<? extends NormalizedNode<?, ?>> nodeClass) {
+            this.nodeClass = nodeClass;
+        }
+
+        @Override
+        public void verifyStructure(final NodeModification modification) throws IllegalArgumentException {
+            if (modification.getModificationType() == ModificationType.WRITE) {
+
+            }
+            for (NodeModification childModification : modification.getModifications()) {
+                resolveChildOperation(childModification.getIdentifier()).verifyStructure(childModification);
+            }
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        protected void verifyWritenStructure(final NormalizedNode<?, ?> writenValue) {
+            checkArgument(nodeClass.isInstance(writenValue), "Node should must be of type %s", nodeClass);
+            checkArgument(writenValue instanceof NormalizedNodeContainer);
+            NormalizedNodeContainer writenCont = (NormalizedNodeContainer) writenValue;
+            for (Object child : writenCont.getValue()) {
+                checkArgument(child instanceof NormalizedNode);
+                NormalizedNode childNode = (NormalizedNode) child;
+            }
+        }
+
+        @Override
+        protected StoreMetadataNode applyWrite(final NodeModification modification,
+                final Optional<StoreMetadataNode> currentMeta, final UnsignedLong subtreeVersion) {
+            //
+            NormalizedNode<?, ?> newValue = modification.getWritenValue();
+
+            UnsignedLong nodeVersion = subtreeVersion;
+            if (currentMeta.isPresent()) {
+                nodeVersion = StoreUtils.increase(currentMeta.get().getNodeVersion());
+            }
+            StoreMetadataNode newValueMeta = StoreMetadataNode.createRecursivelly(newValue, nodeVersion, nodeVersion);
+
+            if (!modification.hasAdditionalModifications()) {
+                return newValueMeta;
+            }
+            @SuppressWarnings("rawtypes")
+            NormalizedNodeContainerBuilder dataBuilder = createBuilder(modification.getIdentifier());
+            StoreNodeCompositeBuilder builder = StoreNodeCompositeBuilder.from(dataBuilder) //
+                    .setNodeVersion(nodeVersion) //
+                    .setSubtreeVersion(subtreeVersion);
+
+            Set<PathArgument> processedPreexisting = applyPreexistingChildren(modification, newValueMeta.getChildren(),
+                    builder, nodeVersion);
+            applyNewChildren(modification, processedPreexisting, builder, nodeVersion);
+
+            return builder.build();
+
+        }
+
+        @Override
+        public StoreMetadataNode applySubtreeChange(final NodeModification modification,
+                final StoreMetadataNode currentMeta, final UnsignedLong subtreeVersion) {
+
+            UnsignedLong updatedSubtreeVersion = StoreUtils.increase(currentMeta.getSubtreeVersion());
+            @SuppressWarnings("rawtypes")
+            NormalizedNodeContainerBuilder dataBuilder = createBuilder(modification.getIdentifier());
+            StoreNodeCompositeBuilder builder = StoreNodeCompositeBuilder.from(dataBuilder)
+                    .setIdentifier(modification.getIdentifier()).setNodeVersion(currentMeta.getNodeVersion())
+                    .setSubtreeVersion(updatedSubtreeVersion);
+            // We process preexisting nodes
+            Set<PathArgument> processedPreexisting = applyPreexistingChildren(modification, currentMeta.getChildren(),
+                    builder, updatedSubtreeVersion);
+            applyNewChildren(modification, processedPreexisting, builder, updatedSubtreeVersion);
+            return builder.build();
+        }
+
+        private void applyNewChildren(final NodeModification modification, final Set<PathArgument> ignore,
+                final StoreNodeCompositeBuilder builder, final UnsignedLong subtreeVersion) {
+            for (NodeModification childModification : modification.getModifications()) {
+                PathArgument childIdentifier = childModification.getIdentifier();
+                // We skip allready processed modifications
+                if (ignore.contains(childIdentifier)) {
+                    continue;
+                }
+
+                builder.addIfPresent(resolveChildOperation(childIdentifier) //
+                        .apply(childModification, Optional.<StoreMetadataNode> absent(), subtreeVersion));
+            }
+        }
+
+        private Set<PathArgument> applyPreexistingChildren(final NodeModification modification,
+                final Iterable<StoreMetadataNode> children, final StoreNodeCompositeBuilder nodeBuilder,
+                final UnsignedLong subtreeVersion) {
+            Builder<PathArgument> processedModifications = ImmutableSet.<PathArgument> builder();
+            for (StoreMetadataNode childMeta : children) {
+                PathArgument childIdentifier = childMeta.getIdentifier();
+                // We retrieve Child modification metadata
+                Optional<NodeModification> childModification = modification.getChild(childIdentifier);
+                // Node is modified
+                if (childModification.isPresent()) {
+                    processedModifications.add(childIdentifier);
+                    Optional<StoreMetadataNode> result = resolveChildOperation(childIdentifier) //
+                            .apply(childModification.get(), Optional.of(childMeta), subtreeVersion);
+                    nodeBuilder.addIfPresent(result);
+                } else {
+                    // Child is unmodified - reuse existing metadata and data
+                    // snapshot
+                    nodeBuilder.add(childMeta);
+                }
+            }
+            return processedModifications.build();
+        }
+
+        @Override
+        protected boolean isSubtreeModificationApplicable(final NodeModification modification,
+                final Optional<StoreMetadataNode> current) {
+            if (false == current.isPresent()) {
+                return false;
+            }
+            boolean result = true;
+            StoreMetadataNode currentMeta = current.get();
+            for (NodeModification childMod : modification.getModifications()) {
+                PathArgument childId = childMod.getIdentifier();
+                Optional<StoreMetadataNode> childMeta = currentMeta.getChild(childId);
+                result &= resolveChildOperation(childId).isApplicable(childMod, childMeta);
+            }
+            return result;
+        }
+
+        @SuppressWarnings("rawtypes")
+        protected abstract NormalizedNodeContainerBuilder createBuilder(PathArgument identifier);
+    }
+
+    public static abstract class DataNodeContainerModificationStrategy<T extends DataNodeContainer> extends
+            NormalizedNodeContainerModificationStrategy {
+
+        private final T schema;
+        private final LoadingCache<PathArgument, ModificationApplyOperation> childCache = CacheBuilder.newBuilder().build(
+                CacheLoader.from(new Function<PathArgument, ModificationApplyOperation>() {
+
+                    @Override
+                    public ModificationApplyOperation apply(final PathArgument identifier) {
+                        if (identifier instanceof AugmentationIdentifier && schema instanceof AugmentationTarget) {
+                            return from(schema, (AugmentationTarget) schema, (AugmentationIdentifier) identifier);
+                        }
+
+                        DataSchemaNode child = schema.getDataChildByName(identifier.getNodeType());
+                        if (child == null) {
+                            return null;
+                        }
+                        return from(child);
+                    }
+                }));
+
+        protected DataNodeContainerModificationStrategy(final T schema,
+                final Class<? extends NormalizedNode<?, ?>> nodeClass) {
+            super(nodeClass);
+            this.schema = schema;
+        }
+
+        protected T getSchema() {
+            return schema;
+        }
+
+        @Override
+        public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
+            try {
+                return Optional.<ModificationApplyOperation> fromNullable(childCache.get(identifier));
+            } catch (ExecutionException e) {
+                return Optional.absent();
+            }
+        }
+
+        @Override
+        @SuppressWarnings("rawtypes")
+        protected abstract DataContainerNodeBuilder createBuilder(PathArgument identifier);
+
+        @Override
+        public String toString() {
+            return getClass().getSimpleName() + " [" + schema + "]";
+        }
+
+    }
+
+    public static class ContainerModificationStrategy extends
+            DataNodeContainerModificationStrategy<ContainerSchemaNode> {
+
+        public ContainerModificationStrategy(final ContainerSchemaNode schemaNode) {
+            super(schemaNode, ContainerNode.class);
+        }
+
+        @Override
+        @SuppressWarnings("rawtypes")
+        protected DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
+            // TODO Auto-generated method stub
+            checkArgument(identifier instanceof NodeIdentifier);
+            return ImmutableContainerNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
+        }
+
+    }
+
+    public static class AugmentationModificationStrategy extends
+            DataNodeContainerModificationStrategy<AugmentationSchema> {
+
+        protected AugmentationModificationStrategy(final AugmentationSchema schema, final DataNodeContainer resolved) {
+            super(schema, AugmentationNode.class);
+            // FIXME: Use resolved children instead of unresolved.
+
+        }
+
+
+        @Override
+        protected DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
+            return Builders.augmentationBuilder().withNodeIdentifier((AugmentationIdentifier) identifier);
+        }
+
+    }
+
+    public static class ChoiceModificationStrategy extends NormalizedNodeContainerModificationStrategy {
+
+        private final ChoiceNode schema;
+        private final Map<PathArgument,ModificationApplyOperation> childNodes;
+
+        public ChoiceModificationStrategy(final ChoiceNode schemaNode) {
+            super(org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode.class);
+            this.schema = schemaNode;
+            ImmutableMap.Builder<PathArgument, ModificationApplyOperation> child = ImmutableMap.builder();
+
+            for(ChoiceCaseNode caze : schemaNode.getCases()) {
+                for(DataSchemaNode cazeChild : caze.getChildNodes()) {
+                    SchemaAwareApplyOperation childNode = from(cazeChild);
+                    child.put(new NodeIdentifier(cazeChild.getQName()),childNode);
+                }
+            }
+            childNodes = child.build();
+        }
+
+        @Override
+        public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
+            return Optional.fromNullable(childNodes.get(child));
+        }
+
+        @Override
+        @SuppressWarnings("rawtypes")
+        protected DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
+            checkArgument(identifier instanceof NodeIdentifier);
+            return ImmutableChoiceNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
+        }
+
+    }
+
+    public static class ListEntryModificationStrategy extends DataNodeContainerModificationStrategy<ListSchemaNode> {
+
+        protected ListEntryModificationStrategy(final ListSchemaNode schema) {
+            super(schema, MapEntryNode.class);
+        }
+
+        @Override
+        @SuppressWarnings("rawtypes")
+        protected final DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
+            return ImmutableMapEntryNodeBuilder.create().withNodeIdentifier((NodeIdentifierWithPredicates) identifier);
+        }
+
+    }
+
+    public static class LeafSetModificationStrategy extends NormalizedNodeContainerModificationStrategy {
+
+        private final Optional<ModificationApplyOperation> entryStrategy;
+
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        protected LeafSetModificationStrategy(final LeafListSchemaNode schema) {
+            super((Class) LeafSetNode.class);
+            entryStrategy = Optional.<ModificationApplyOperation> of(new LeafSetEntryModificationStrategy(schema));
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(final PathArgument identifier) {
+            return ImmutableLeafSetNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
+        }
+
+        @Override
+        public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
+            if (identifier instanceof NodeWithValue) {
+                return entryStrategy;
+            }
+            return Optional.absent();
+        }
+
+    }
+
+    public static class ListMapModificationStrategy extends NormalizedNodeContainerModificationStrategy {
+
+        private final Optional<ModificationApplyOperation> entryStrategy;
+
+        protected ListMapModificationStrategy(final ListSchemaNode schema) {
+            super(MapNode.class);
+            entryStrategy = Optional.<ModificationApplyOperation> of(new ListEntryModificationStrategy(schema));
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(final PathArgument identifier) {
+            return ImmutableMapNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
+        }
+
+        @Override
+        public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
+            if (identifier instanceof NodeIdentifierWithPredicates) {
+                return entryStrategy;
+            }
+            return Optional.absent();
+        }
+
+        @Override
+        public String toString() {
+            return "ListMapModificationStrategy [entry=" + entryStrategy + "]";
+        }
+    }
+
+    public void verifyIdentifier(final PathArgument identifier) {
+
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SchemaAwareApplyOperationRoot.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SchemaAwareApplyOperationRoot.java
new file mode 100644 (file)
index 0000000..e7265de
--- /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.dom.store.impl;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class SchemaAwareApplyOperationRoot extends SchemaAwareApplyOperation.DataNodeContainerModificationStrategy<ContainerSchemaNode> {
+
+    private final SchemaContext context;
+
+    public SchemaAwareApplyOperationRoot(final SchemaContext context) {
+        super(context,ContainerNode.class);
+        this.context = context;
+    }
+
+    @Override
+    protected DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
+        return ImmutableContainerNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
+    }
+
+    public SchemaContext getContext() {
+        return context;
+    }
+
+    @Override
+    public String toString() {
+        return "SchemaAwareApplyOperationRoot [context=" + context + "]";
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/StoreUtils.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/StoreUtils.java
new file mode 100644 (file)
index 0000000..02c2a4f
--- /dev/null
@@ -0,0 +1,134 @@
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+
+import com.google.common.base.Function;
+import com.google.common.base.Strings;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.primitives.UnsignedLong;
+
+public final class StoreUtils {
+
+    private final static Function<Identifiable<Object>, Object> EXTRACT_IDENTIFIER = new Function<Identifiable<Object>, Object>() {
+
+        @Override
+        public Object apply(final Identifiable<Object> input) {
+            return input.getIdentifier();
+        }
+    };
+
+    public static final UnsignedLong increase(final UnsignedLong original) {
+        return original.plus(UnsignedLong.ONE);
+    }
+
+    public static final InstanceIdentifier append(final InstanceIdentifier parent, final PathArgument arg) {
+
+        return new InstanceIdentifier(ImmutableList.<PathArgument> builder().addAll(parent.getPath()).add(arg).build());
+    }
+
+    public static AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> initialChangeEvent(
+            final InstanceIdentifier path, final StoreMetadataNode data) {
+        return new InitialDataChangeEvent(path, data.getData());
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static <V> Function<Identifiable<V>, V> identifierExtractor() {
+        return (Function) EXTRACT_IDENTIFIER;
+    }
+
+    private static final class InitialDataChangeEvent implements
+            AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+        private final ImmutableMap<InstanceIdentifier, NormalizedNode<?, ?>> payload;
+        private final NormalizedNode<?, ?> data;
+
+        public InitialDataChangeEvent(final InstanceIdentifier path, final NormalizedNode<?, ?> data) {
+            payload = ImmutableMap.<InstanceIdentifier, NormalizedNode<?, ?>> of(path, data);
+            this.data = data;
+        }
+
+        @Override
+        public Map<InstanceIdentifier, NormalizedNode<?, ?>> getCreatedData() {
+            return payload;
+        }
+
+        @Override
+        public Map<InstanceIdentifier, ? extends NormalizedNode<?, ?>> getOriginalData() {
+            return Collections.emptyMap();
+        }
+
+        @Override
+        public NormalizedNode<?, ?> getOriginalSubtree() {
+            return null;
+        }
+
+        @Override
+        public Set<InstanceIdentifier> getRemovedPaths() {
+            return Collections.emptySet();
+        }
+
+        @Override
+        public Map<InstanceIdentifier, NormalizedNode<?, ?>> getUpdatedData() {
+            return payload;
+        }
+
+        @Override
+        public NormalizedNode<?, ?> getUpdatedSubtree() {
+            return data;
+        }
+
+    }
+
+    public static <V> Set<V> toIdentifierSet(final Iterable<? extends Identifiable<V>> children) {
+        return FluentIterable.from(children).transform(StoreUtils.<V> identifierExtractor()).toSet();
+    }
+
+    public static String toStringTree(final StoreMetadataNode metaNode) {
+        StringBuilder builder = new StringBuilder();
+        toStringTree(builder, metaNode, 0);
+        return builder.toString();
+
+    }
+
+    private static void toStringTree(final StringBuilder builder, final StoreMetadataNode metaNode, final int offset) {
+        String prefix = Strings.repeat(" ", offset);
+        builder.append(prefix).append(toStringTree(metaNode.getIdentifier()));
+        NormalizedNode<?, ?> dataNode = metaNode.getData();
+        if (dataNode instanceof NormalizedNodeContainer<?, ?, ?>) {
+            builder.append(" {").append("\n");
+            for (StoreMetadataNode child : metaNode.getChildren()) {
+                toStringTree(builder, child, offset + 4);
+            }
+            builder.append(prefix).append("}");
+        } else {
+            builder.append(" ").append(dataNode.getValue());
+        }
+        builder.append("\n");
+    }
+
+    private static String toStringTree(final PathArgument identifier) {
+        if (identifier instanceof NodeIdentifierWithPredicates) {
+            StringBuilder builder = new StringBuilder();
+            builder.append(identifier.getNodeType().getLocalName());
+            builder.append(((NodeIdentifierWithPredicates) identifier).getKeyValues().values());
+            return builder.toString();
+        } else if (identifier instanceof AugmentationIdentifier) {
+            return "augmentation";
+        }
+        return identifier.getNodeType().getLocalName();
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/ListenerRegistrationNode.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/ListenerRegistrationNode.java
new file mode 100644 (file)
index 0000000..2528d38
--- /dev/null
@@ -0,0 +1,146 @@
+package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+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.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+
+public class ListenerRegistrationNode implements StoreTreeNode<ListenerRegistrationNode>, Identifiable<PathArgument> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ListenerRegistrationNode.class);
+
+    private final ListenerRegistrationNode parent;
+    private final Map<PathArgument, ListenerRegistrationNode> children;
+    private final PathArgument identifier;
+    private final HashSet<DataChangeListenerRegistration<?>> listeners;
+
+    private ListenerRegistrationNode(final PathArgument identifier) {
+        this(null, identifier);
+    }
+
+    private ListenerRegistrationNode(final ListenerRegistrationNode parent, final PathArgument identifier) {
+        this.parent = parent;
+        this.identifier = identifier;
+        children = new HashMap<>();
+        listeners = new HashSet<>();
+    }
+
+    public final static ListenerRegistrationNode createRoot() {
+        return new ListenerRegistrationNode(null);
+    }
+
+    @Override
+    public PathArgument getIdentifier() {
+        return identifier;
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public Collection<org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration<?>> getListeners() {
+        // FIXME: this is not thread-safe and races with listener (un)registration!
+        return (Collection) listeners;
+    }
+
+    @Override
+    public synchronized Optional<ListenerRegistrationNode> getChild(final PathArgument child) {
+        return Optional.fromNullable(children.get(child));
+    }
+
+    public synchronized ListenerRegistrationNode ensureChild(final PathArgument child) {
+        ListenerRegistrationNode potential = (children.get(child));
+        if (potential == null) {
+            potential = new ListenerRegistrationNode(this, child);
+            children.put(child, potential);
+        }
+        return potential;
+    }
+
+    /**
+     *
+     * Registers listener on this node.
+     *
+     * @param path Full path on which listener is registered.
+     * @param listener Listener
+     * @param scope Scope of triggering event.
+     * @return
+     */
+    public <L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> DataChangeListenerRegistration<L> registerDataChangeListener(final InstanceIdentifier path,
+            final L listener, final DataChangeScope scope) {
+
+        DataChangeListenerRegistration<L> listenerReg = new DataChangeListenerRegistration<L>(path,listener, scope, this);
+        listeners.add(listenerReg);
+        return listenerReg;
+    }
+
+    private void removeListener(final DataChangeListenerRegistration<?> listener) {
+        listeners.remove(listener);
+        removeThisIfUnused();
+    }
+
+    private void removeThisIfUnused() {
+        if (parent != null && listeners.isEmpty() && children.isEmpty()) {
+            parent.removeChildIfUnused(this);
+        }
+    }
+
+    public boolean isUnused() {
+        return (listeners.isEmpty() && children.isEmpty()) || areChildrenUnused();
+    }
+
+    private boolean areChildrenUnused() {
+        for (ListenerRegistrationNode child : children.values()) {
+            if (!child.isUnused()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void removeChildIfUnused(final ListenerRegistrationNode listenerRegistrationNode) {
+        // FIXME Remove unnecessary
+    }
+
+    public static class DataChangeListenerRegistration<T extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>>
+            extends AbstractObjectRegistration<T> implements
+            org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration<T> {
+
+        private final DataChangeScope scope;
+        private ListenerRegistrationNode node;
+        private final InstanceIdentifier path;
+
+        public DataChangeListenerRegistration(final InstanceIdentifier path,final T listener, final DataChangeScope scope,
+                final ListenerRegistrationNode node) {
+            super(listener);
+            this.path = path;
+            this.scope = scope;
+            this.node = node;
+        }
+
+        @Override
+        public DataChangeScope getScope() {
+            return scope;
+        }
+
+        @Override
+        protected void removeRegistration() {
+            node.removeListener(this);
+            node = null;
+        }
+
+        @Override
+        public InstanceIdentifier getPath() {
+            return path;
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/ModificationType.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/ModificationType.java
new file mode 100644 (file)
index 0000000..199d902
--- /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.store.impl.tree;
+
+public enum ModificationType {
+
+    /**
+     *
+     * Node is unmodified
+     *
+     *
+     */
+    UNMODIFIED,
+    /**
+     *
+     * Child of tree node was modified
+     *
+     */
+    SUBTREE_MODIFIED,
+    /**
+     * Tree node was replaced with new value / subtree
+     *
+     */
+    WRITE,
+    /**
+     *
+     * Tree node is to be deleted.
+     *
+     */
+    DELETE
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/NodeModification.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/NodeModification.java
new file mode 100644 (file)
index 0000000..a0c15eb
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * 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 static com.google.common.base.Preconditions.checkState;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.primitives.UnsignedLong;
+
+/**
+ * Node Modification Node and Tree
+ *
+ * Tree which structurally resembles data tree and captures client modifications
+ * to the data store tree.
+ *
+ * This tree is lazily created and populated via {@link #modifyChild(PathArgument)}
+ * and {@link StoreMetadataNode} which represents original state {@link #getOriginal()}.
+ *
+ */
+public class NodeModification implements StoreTreeNode<NodeModification>, Identifiable<PathArgument> {
+
+    public static final Predicate<NodeModification> IS_TERMINAL_PREDICATE = new Predicate<NodeModification>() {
+        @Override
+        public boolean apply(final NodeModification input) {
+            return input.getModificationType() == ModificationType.WRITE || input.getModificationType() == ModificationType.DELETE;
+        }
+    };
+    private final PathArgument identifier;
+    private ModificationType modificationType = ModificationType.UNMODIFIED;
+
+
+    private final Optional<StoreMetadataNode> original;
+
+    private NormalizedNode<?, ?> value;
+
+    private UnsignedLong subtreeVersion;
+    private Optional<StoreMetadataNode> snapshotCache;
+
+    private final Map<PathArgument, NodeModification> childModification;
+
+    private boolean sealed = false;
+
+    protected NodeModification(final PathArgument identifier, final Optional<StoreMetadataNode> original) {
+        this.identifier = identifier;
+        this.original = original;
+        childModification = new LinkedHashMap<>();
+    }
+
+    /**
+     *
+     *
+     * @return
+     */
+    public NormalizedNode<?, ?> getWritenValue() {
+        return value;
+    }
+
+    @Override
+    public PathArgument getIdentifier() {
+        return identifier;
+    }
+
+    /**
+     *
+     * Returns original store metadata
+     * @return original store metadata
+     */
+    public final Optional<StoreMetadataNode> getOriginal() {
+        return original;
+    }
+
+    /**
+     * Returns modification type
+     *
+     * @return modification type
+     */
+    public final ModificationType getModificationType() {
+        return modificationType;
+    }
+
+    /**
+     *
+     * Returns child modification if child was modified
+     *
+     * @return Child modification if direct child or it's subtree
+     *  was modified.
+     *
+     */
+    @Override
+    public Optional<NodeModification> getChild(final PathArgument child) {
+        return Optional.<NodeModification> fromNullable(childModification.get(child));
+    }
+
+    /**
+     *
+     * Returns child modification if child was modified, creates {@link NodeModification}
+     * for child otherwise.
+     *
+     * If this node's {@link ModificationType} is {@link ModificationType#UNMODIFIED}
+     * changes modification type to {@link ModificationType#SUBTREE_MODIFIED}
+     *
+     * @param child
+     * @return {@link NodeModification} for specified child, with {@link #getOriginal()}
+     *  containing child metadata if child was present in original data.
+     */
+    public synchronized NodeModification modifyChild(final PathArgument child) {
+        checkSealed();
+        clearSnapshot();
+        if(modificationType == ModificationType.UNMODIFIED) {
+            updateModificationType(ModificationType.SUBTREE_MODIFIED);
+        }
+        final NodeModification potential = childModification.get(child);
+        if (potential != null) {
+            return potential;
+        }
+        Optional<StoreMetadataNode> currentMetadata = Optional.absent();
+        if(original.isPresent()) {
+            currentMetadata = original.get().getChild(child);
+        }
+        NodeModification newlyCreated = new NodeModification(child,currentMetadata);
+        childModification.put(child, newlyCreated);
+        return newlyCreated;
+    }
+
+    /**
+     *
+     * Returns all recorded direct child modification
+     *
+     * @return all recorded direct child modifications
+     */
+    public Iterable<NodeModification> getModifications() {
+        return childModification.values();
+    }
+
+
+    /**
+     *
+     * Records a delete for associated node.
+     *
+     */
+    public synchronized void delete() {
+        checkSealed();
+        clearSnapshot();
+        updateModificationType(ModificationType.DELETE);
+        childModification.clear();
+        this.value = null;
+    }
+
+    /**
+     *
+     * Records a write for associated node.
+     *
+     * @param value
+     */
+    public synchronized void write(final NormalizedNode<?, ?> value) {
+        checkSealed();
+        clearSnapshot();
+        updateModificationType(ModificationType.WRITE);
+        childModification.clear();
+        this.value = value;
+    }
+
+    private void checkSealed() {
+        checkState(!sealed, "Node Modification is sealed. No further changes allowed.");
+    }
+
+    public synchronized void seal() {
+        sealed = true;
+        clearSnapshot();
+        for(NodeModification child : childModification.values()) {
+            child.seal();
+        }
+    }
+
+    private void clearSnapshot() {
+        snapshotCache = null;
+    }
+
+    public Optional<StoreMetadataNode> storeSnapshot(final Optional<StoreMetadataNode> snapshot) {
+        snapshotCache = snapshot;
+        return snapshot;
+    }
+
+    public Optional<Optional<StoreMetadataNode>> getSnapshotCache() {
+        return Optional.fromNullable(snapshotCache);
+    }
+
+    public boolean hasAdditionalModifications() {
+        return !childModification.isEmpty();
+    }
+
+    public void updateModificationType(final ModificationType type) {
+        modificationType = type;
+        clearSnapshot();
+    }
+
+    @Override
+    public String toString() {
+        return "NodeModification [identifier=" + identifier + ", modificationType="
+                + modificationType + ", childModification=" + childModification + "]";
+    }
+
+    public static NodeModification createUnmodified(final StoreMetadataNode metadataTree) {
+        return new NodeModification(metadataTree.getIdentifier(), Optional.of(metadataTree));
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/StoreMetadataNode.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/StoreMetadataNode.java
new file mode 100644 (file)
index 0000000..8ba0013
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * 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 static com.google.common.base.Preconditions.checkState;
+
+import java.util.Map;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.primitives.UnsignedLong;
+
+public class StoreMetadataNode implements Immutable, Identifiable<PathArgument>, StoreTreeNode<StoreMetadataNode> {
+
+    private final UnsignedLong nodeVersion;
+    private final UnsignedLong subtreeVersion;
+    private final NormalizedNode<?, ?> data;
+
+    private final Map<PathArgument, StoreMetadataNode> children;
+
+    protected StoreMetadataNode(final NormalizedNode<?, ?> data, final UnsignedLong nodeVersion,
+            final UnsignedLong subtreeVersion, final Map<PathArgument, StoreMetadataNode> children) {
+        this.nodeVersion = nodeVersion;
+        this.subtreeVersion = subtreeVersion;
+        this.data = data;
+        this.children = ImmutableMap.copyOf(children);
+
+    }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public UnsignedLong getNodeVersion() {
+        return this.nodeVersion;
+    }
+
+    @Override
+    public PathArgument getIdentifier() {
+        return data.getIdentifier();
+    }
+
+    public UnsignedLong getSubtreeVersion() {
+        return subtreeVersion;
+    }
+
+    public NormalizedNode<?, ?> getData() {
+        return this.data;
+    }
+
+    public Iterable<StoreMetadataNode> getChildren() {
+        return children.values();
+    }
+
+    @Override
+    public Optional<StoreMetadataNode> getChild(final PathArgument key) {
+        return Optional.fromNullable(children.get(key));
+    }
+
+    @Override
+    public String toString() {
+        return "StoreMetadataNode [identifier=" + getIdentifier() + ", nodeVersion=" + nodeVersion + "]";
+    }
+
+    public static Optional<UnsignedLong> getVersion(final Optional<StoreMetadataNode> currentMetadata) {
+        if (currentMetadata.isPresent()) {
+            return Optional.of(currentMetadata.get().getNodeVersion());
+        }
+        return Optional.absent();
+    }
+
+    public static Optional<StoreMetadataNode> getChild(final Optional<StoreMetadataNode> parent,
+            final PathArgument child) {
+        if (parent.isPresent()) {
+            return parent.get().getChild(child);
+        }
+        return Optional.absent();
+    }
+
+    public static final StoreMetadataNode createRecursivelly(final NormalizedNode<?, ?> node,
+            final UnsignedLong nodeVersion, final UnsignedLong subtreeVersion) {
+        Builder builder = builder() //
+                .setNodeVersion(nodeVersion) //
+                .setSubtreeVersion(subtreeVersion) //
+                .setData(node);
+        if (node instanceof NormalizedNodeContainer<?, ?, ?>) {
+
+            @SuppressWarnings("unchecked")
+            NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>> nodeContainer = (NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>>) node;
+            for (NormalizedNode<?, ?> subNode : nodeContainer.getValue()) {
+                builder.add(createRecursivelly(subNode, nodeVersion, subtreeVersion));
+            }
+        }
+        return builder.build();
+    }
+
+    public static class Builder {
+
+        private UnsignedLong nodeVersion;
+        private UnsignedLong subtreeVersion;
+        private NormalizedNode<?, ?> data;
+        private final ImmutableMap.Builder<PathArgument, StoreMetadataNode> children = ImmutableMap.builder();
+
+        private Builder() {}
+
+
+        public UnsignedLong getVersion() {
+            return nodeVersion;
+
+        }
+
+        public Builder setNodeVersion(final UnsignedLong version) {
+            this.nodeVersion = version;
+            return this;
+        }
+
+        public Builder setSubtreeVersion(final UnsignedLong version) {
+            this.subtreeVersion = version;
+            return this;
+        }
+
+        public Builder setData(final NormalizedNode<?, ?> data) {
+            this.data = data;
+            return this;
+        }
+
+        public Builder add(final StoreMetadataNode node) {
+            children.put(node.getIdentifier(), node);
+            return this;
+        }
+
+        public StoreMetadataNode build() {
+            checkState(data != null, "Data node should not be null.");
+            checkState(subtreeVersion.compareTo(nodeVersion) >= 0,
+                    "Subtree version must be equals or greater than node version.");
+            return new StoreMetadataNode(data, nodeVersion, subtreeVersion, children.build());
+        }
+    }
+
+    public static StoreMetadataNode createRecursivelly(final NormalizedNode<?, ?> node, final UnsignedLong version) {
+        return createRecursivelly(node, version, version);
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/StoreNodeCompositeBuilder.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/StoreNodeCompositeBuilder.java
new file mode 100644 (file)
index 0000000..41fc823
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+
+import com.google.common.base.Optional;
+import com.google.common.primitives.UnsignedLong;
+
+/**
+ *
+ * Helper builder
+ *
+ *
+ */
+@SuppressWarnings("rawtypes")
+public class StoreNodeCompositeBuilder {
+
+    private final StoreMetadataNode.Builder metadata;
+
+    private final NormalizedNodeContainerBuilder data;
+
+    private StoreNodeCompositeBuilder(final NormalizedNodeContainerBuilder nodeBuilder) {
+        this.metadata = StoreMetadataNode.builder();
+        this.data = nodeBuilder;
+    }
+
+    @SuppressWarnings("unchecked")
+    public StoreNodeCompositeBuilder add(final StoreMetadataNode node) {
+        metadata.add(node);
+        data.addChild(node.getData());
+        return this;
+    }
+
+    @SuppressWarnings("unchecked")
+    public StoreNodeCompositeBuilder addIfPresent(final Optional<StoreMetadataNode> potential) {
+        if (potential.isPresent()) {
+            StoreMetadataNode node = potential.get();
+            metadata.add(node);
+            data.addChild(node.getData());
+        }
+        return this;
+    }
+
+    public StoreMetadataNode build() {
+        return metadata.setData(data.build()).build();
+    }
+
+    public static StoreNodeCompositeBuilder from(final NormalizedNodeContainerBuilder nodeBuilder) {
+        return new StoreNodeCompositeBuilder(nodeBuilder);
+    }
+
+    @SuppressWarnings("unchecked")
+    public StoreNodeCompositeBuilder setIdentifier(final PathArgument identifier) {
+        data.withNodeIdentifier(identifier);
+        return this;
+    }
+
+    public StoreNodeCompositeBuilder setNodeVersion(final UnsignedLong nodeVersion) {
+        metadata.setNodeVersion(nodeVersion);
+        return this;
+    }
+
+    public StoreNodeCompositeBuilder setSubtreeVersion(final UnsignedLong updatedSubtreeVersion) {
+        metadata.setSubtreeVersion(updatedSubtreeVersion);
+        return this;
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/StoreTreeNode.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/StoreTreeNode.java
new file mode 100644 (file)
index 0000000..52beaa7
--- /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.md.sal.dom.store.impl.tree;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+
+import com.google.common.base.Optional;
+/**
+ *
+ * Tree node which contains references to it's leafs
+ *
+ * @param <C> Final node type
+ */
+public interface StoreTreeNode<C extends StoreTreeNode<C>> {
+
+    /**
+     *
+     * Returns direct child of the node
+     *
+     * @param child Identifier of child
+     * @return Optional with node if the child is existing, {@link Optional#absent()} otherwise.
+     */
+    Optional<C> getChild(PathArgument child);
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/TreeNodeUtils.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/TreeNodeUtils.java
new file mode 100644 (file)
index 0000000..a2a706a
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * 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 java.util.AbstractMap.SimpleEntry;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+
+public class TreeNodeUtils {
+
+    /**
+     * Finds a node in tree
+     *
+     * @param tree Data Tree
+     * @param path Path to the node
+     * @return Optional with node if the node is present in tree, {@link Optional#absent()} otherwise.
+     *
+     */
+    public static <T extends StoreTreeNode<T>> Optional<T> findNode(final T tree, final InstanceIdentifier path) {
+        Optional<T> current = Optional.<T> of(tree);
+        Iterator<PathArgument> pathIter = path.getPath().iterator();
+        while (current.isPresent() && pathIter.hasNext()) {
+            current = current.get().getChild(pathIter.next());
+        }
+        return current;
+    }
+
+
+    public static <T extends StoreTreeNode<T>> T findNodeChecked(final T tree, final InstanceIdentifier path) {
+        T current = tree;
+        List<PathArgument> nested = new ArrayList<>(path.getPath());
+        for(PathArgument pathArg : path.getPath()) {
+            Optional<T> potential = current.getChild(pathArg);
+            nested.add(pathArg);
+            Preconditions.checkArgument(potential.isPresent(),"Child %s is not present in tree.",nested);
+            current = potential.get();
+        }
+        return current;
+    }
+
+    /**
+     * Finds a node or closest parent in  the tree
+     *
+     * @param tree Data Tree
+     * @param path Path to the node
+     * @return Map.Entry Entry with key which is path to closest parent and value is parent node.
+     *
+     */
+    public static <T extends StoreTreeNode<T>> Map.Entry<InstanceIdentifier, T> findClosest(final T tree, final InstanceIdentifier path) {
+        return findClosestsOrFirstMatch(tree, path, Predicates.<T>alwaysFalse());
+    }
+
+    public static <T extends StoreTreeNode<T>> Map.Entry<InstanceIdentifier, T> findClosestsOrFirstMatch(final T tree, final InstanceIdentifier path, final Predicate<T> predicate) {
+        Optional<T> parent = Optional.<T>of(tree);
+        Optional<T> current = Optional.<T> of(tree);
+
+        int nesting = 0;
+        Iterator<PathArgument> pathIter = path.getPath().iterator();
+        while (current.isPresent() && pathIter.hasNext() && !predicate.apply(current.get())) {
+            parent = current;
+            current = current.get().getChild(pathIter.next());
+            nesting++;
+        }
+        if(current.isPresent()) {
+            final InstanceIdentifier currentPath = new InstanceIdentifier(path.getPath().subList(0, nesting));
+            return new SimpleEntry<InstanceIdentifier,T>(currentPath,current.get());
+        }
+        // Nesting minus one is safe, since current is allways present when nesting = 0
+        // so this prat of code is never triggered, in cases nesting == 0;
+        final InstanceIdentifier parentPath = new InstanceIdentifier(path.getPath().subList(0, nesting - 1));
+        return new SimpleEntry<InstanceIdentifier,T>(parentPath,parent.get());
+
+    }
+
+    public static <T extends StoreTreeNode<T>> Optional<T> getChild(final Optional<T> parent,final PathArgument child) {
+        if(parent.isPresent()) {
+            return parent.get().getChild(child);
+        }
+        return Optional.absent();
+    }
+
+}
index 6b5f5acb1945a872d48b925da58283e314014af4..9cbf4282e47649ee42302746d4078d202924c2fe 100644 (file)
@@ -22,13 +22,22 @@ import org.osgi.framework.BundleContext
 import org.osgi.framework.ServiceRegistration
 import org.opendaylight.controller.sal.dom.broker.impl.SchemaContextProviders
 import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry
+import org.opendaylight.controller.md.sal.dom.broker.impl.compat.BackwardsCompatibleDataBroker
+import org.opendaylight.controller.md.sal.dom.broker.impl.DOMDataBrokerImpl
+import com.google.common.util.concurrent.MoreExecutors
+import com.google.common.collect.ImmutableMap
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType
+import org.opendaylight.controller.sal.core.spi.data.DOMStore
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore
+import java.util.concurrent.Executors
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker
 
 class BrokerConfigActivator implements AutoCloseable {
 
     private static val ROOT = InstanceIdentifier.builder().toInstance();
 
     @Property
-    private var DataBrokerImpl dataService;
+    private var DataProviderService dataService;
 
     private var ServiceRegistration<DataBrokerService> dataReg;
     private var ServiceRegistration<DataProviderService> dataProviderReg;
@@ -40,7 +49,7 @@ class BrokerConfigActivator implements AutoCloseable {
 
     SchemaAwareDataStoreAdapter wrappedStore
 
-    public def void start(BrokerImpl broker, DataStore store, BundleContext context) {
+    public def void start(BrokerImpl broker, DataStore store, DOMDataBroker asyncBroker,BundleContext context) {
         val emptyProperties = new Hashtable<String, String>();
         broker.setBundleContext(context);
 
@@ -48,27 +57,32 @@ class BrokerConfigActivator implements AutoCloseable {
         schemaService = context.getService(serviceRef);
 
         broker.setRouter(new SchemaAwareRpcBroker("/", SchemaContextProviders.fromSchemaService(schemaService)));
-
-        dataService = new DataBrokerImpl();
-        //dataService.setExecutor(broker.getExecutor());
-
-        dataReg = context.registerService(DataBrokerService, dataService, emptyProperties);
-        dataProviderReg = context.registerService(DataProviderService, dataService, emptyProperties);
-
-        wrappedStore = new SchemaAwareDataStoreAdapter();
-        wrappedStore.changeDelegate(store);
-        wrappedStore.setValidationEnabled(false);
-
-        context.registerService(SchemaServiceListener, wrappedStore, emptyProperties)
-
-        dataService.registerConfigurationReader(ROOT, wrappedStore);
-        dataService.registerCommitHandler(ROOT, wrappedStore);
-        dataService.registerOperationalReader(ROOT, wrappedStore);
+        
+
+        if(asyncBroker == null) {
+            dataService = new DataBrokerImpl();
+            dataProviderReg = context.registerService(DataProviderService, dataService, emptyProperties);
+    
+            wrappedStore = new SchemaAwareDataStoreAdapter();
+            wrappedStore.changeDelegate(store);
+            wrappedStore.setValidationEnabled(false);
+            context.registerService(SchemaServiceListener, wrappedStore, emptyProperties)
+            
+            dataService.registerConfigurationReader(ROOT, wrappedStore);
+            dataService.registerCommitHandler(ROOT, wrappedStore);
+            dataService.registerOperationalReader(ROOT, wrappedStore);
+        } else {
+            val compatibleDataBroker = new BackwardsCompatibleDataBroker(asyncBroker);
+            context.registerService(SchemaServiceListener,compatibleDataBroker,emptyProperties);
+            dataService = compatibleDataBroker;
+        }
+        
+
+//        
 
         mountService = new MountPointManagerImpl();
-        mountService.setDataBroker(dataService);
-
-        mountReg = context.registerService(MountService, mountService, emptyProperties);
+        dataReg = context.registerService(DataBrokerService, dataService, emptyProperties);
+            mountReg = context.registerService(MountService, mountService, emptyProperties);
         mountProviderReg = context.registerService(MountProvisionService, mountService, emptyProperties);
 
         rpcProvisionRegistryReg = context.registerService(RpcProvisionRegistry, broker.getRouter(), emptyProperties);
index bf35037b224beae2e1f4f0434d22c7e58a920bc0..a60a30d25686cad6013277f21e303e2053ccee94 100644 (file)
@@ -46,7 +46,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements //
     private ListenerRegistry<SchemaServiceListener> listeners;
 
     private BundleContext context;
-    private BundleScanner scanner = new BundleScanner();
+    private final BundleScanner scanner = new BundleScanner();
 
     private BundleTracker<ImmutableSet<Registration<URL>>> bundleTracker;
 
@@ -60,7 +60,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements //
         return listeners;
     }
 
-    public void setListeners(ListenerRegistry<SchemaServiceListener> listeners) {
+    public void setListeners(final ListenerRegistry<SchemaServiceListener> listeners) {
         this.listeners = listeners;
     }
 
@@ -68,7 +68,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements //
         return context;
     }
 
-    public void setContext(BundleContext context) {
+    public void setContext(final BundleContext context) {
         this.context = context;
     }
 
@@ -92,12 +92,13 @@ public class GlobalBundleScanningSchemaServiceImpl implements //
         return getGlobalContext();
     }
 
+    @Override
     public SchemaContext getGlobalContext() {
         return contextResolver.getSchemaContext().orNull();
     }
 
     @Override
-    public void addModule(Module module) {
+    public void addModule(final Module module) {
         throw new UnsupportedOperationException();
     }
 
@@ -107,12 +108,16 @@ public class GlobalBundleScanningSchemaServiceImpl implements //
     }
 
     @Override
-    public void removeModule(Module module) {
+    public void removeModule(final Module module) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(SchemaServiceListener listener) {
+    public ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(final SchemaServiceListener listener) {
+        Optional<SchemaContext> potentialCtx = contextResolver.getSchemaContext();
+        if(potentialCtx.isPresent()) {
+            listener.onGlobalContextUpdated(potentialCtx.get());
+        }
         return listeners.register(listener);
     }
 
@@ -128,7 +133,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements //
     }
 
 
-    private void updateContext(SchemaContext snapshot) {
+    private void updateContext(final SchemaContext snapshot) {
         Object[] services = listenerTracker.getServices();
         if (services != null) {
             for (Object rawListener : services) {
@@ -151,7 +156,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements //
 
     private class BundleScanner implements BundleTrackerCustomizer<ImmutableSet<Registration<URL>>> {
         @Override
-        public ImmutableSet<Registration<URL>> addingBundle(Bundle bundle, BundleEvent event) {
+        public ImmutableSet<Registration<URL>> addingBundle(final Bundle bundle, final BundleEvent event) {
 
             if (bundle.getBundleId() == 0) {
                 return ImmutableSet.of();
@@ -172,7 +177,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements //
         }
 
         @Override
-        public void modifiedBundle(Bundle bundle, BundleEvent event, ImmutableSet<Registration<URL>> object) {
+        public void modifiedBundle(final Bundle bundle, final BundleEvent event, final ImmutableSet<Registration<URL>> object) {
             logger.debug("Modified bundle {} {} {}", bundle, event, object);
         }
 
@@ -183,7 +188,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements //
          */
 
         @Override
-        public synchronized void removedBundle(Bundle bundle, BundleEvent event, ImmutableSet<Registration<URL>> urls) {
+        public synchronized void removedBundle(final Bundle bundle, final BundleEvent event, final ImmutableSet<Registration<URL>> urls) {
             for (Registration<URL> url : urls) {
                 try {
                     url.close();
@@ -196,7 +201,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements //
     }
 
     @Override
-    public SchemaServiceListener addingService(ServiceReference<SchemaServiceListener> reference) {
+    public SchemaServiceListener addingService(final ServiceReference<SchemaServiceListener> reference) {
 
         SchemaServiceListener listener = context.getService(reference);
         SchemaContext _ctxContext = getGlobalContext();
@@ -217,12 +222,12 @@ public class GlobalBundleScanningSchemaServiceImpl implements //
     }
 
     @Override
-    public void modifiedService(ServiceReference<SchemaServiceListener> reference, SchemaServiceListener service) {
+    public void modifiedService(final ServiceReference<SchemaServiceListener> reference, final SchemaServiceListener service) {
         // NOOP
     }
 
     @Override
-    public void removedService(ServiceReference<SchemaServiceListener> reference, SchemaServiceListener service) {
+    public void removedService(final ServiceReference<SchemaServiceListener> reference, final SchemaServiceListener service) {
         context.ungetService(reference);
     }
 }
index dc554a072769ae0ecadf74601aa059cdbfc2d4eb..f9f977e3c24d67abe809ce2f3648df5f63960d83 100644 (file)
@@ -9,7 +9,6 @@ package org.opendaylight.controller.sal.dom.broker;
 
 import java.util.List;
 import java.util.Set;
-import java.util.concurrent.Future;
 
 import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
 import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
@@ -41,6 +40,8 @@ import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
+import com.google.common.util.concurrent.ListenableFuture;
+
 public class MountPointImpl implements MountProvisionInstance, SchemaContextProvider {
 
     private final SchemaAwareRpcBroker rpcs;
@@ -135,7 +136,7 @@ public class MountPointImpl implements MountProvisionInstance, SchemaContextProv
 
 
     @Override
-    public Future<RpcResult<CompositeNode>> rpc(QName type, CompositeNode input) {
+    public ListenableFuture<RpcResult<CompositeNode>> rpc(QName type, CompositeNode input) {
         return null;
     }
 
index 50af3fbfc1f097662ae4f0a8f1e1084cb87aab96..7fba31114f6d53a2c491f91511109a32b6c8efec 100644 (file)
@@ -11,7 +11,7 @@ import java.util.Collection;
 
 import org.opendaylight.controller.sal.core.api.notify.NotificationListener;
 import org.opendaylight.controller.sal.dom.broker.spi.NotificationRouter;
-import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
 import org.opendaylight.yangtools.concepts.Registration;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
@@ -25,12 +25,12 @@ import com.google.common.collect.Multimaps;
 public class NotificationRouterImpl implements NotificationRouter {
     private static Logger log = LoggerFactory.getLogger(NotificationRouterImpl.class);
 
-    private final Multimap<QName, Registration<NotificationListener>> listeners = Multimaps.synchronizedSetMultimap(HashMultimap.<QName, Registration<NotificationListener>>create());
+    private final Multimap<QName, ListenerRegistration> listeners = Multimaps.synchronizedSetMultimap(HashMultimap.<QName, ListenerRegistration>create());
 //    private Registration<NotificationListener> defaultListener;
 
     private void sendNotification(CompositeNode notification) {
         final QName type = notification.getNodeType();
-        final Collection<Registration<NotificationListener>> toNotify = listeners.get(type);
+        final Collection<ListenerRegistration> toNotify = listeners.get(type);
         log.trace("Publishing notification " + type);
 
         if ((toNotify == null) || toNotify.isEmpty()) {
@@ -38,7 +38,7 @@ public class NotificationRouterImpl implements NotificationRouter {
             return;
         }
 
-        for (Registration<NotificationListener> listener : toNotify) {
+        for (ListenerRegistration listener : toNotify) {
             try {
                 // FIXME: ensure that notification is immutable
                 listener.getInstance().onNotification(notification);
@@ -60,7 +60,7 @@ public class NotificationRouterImpl implements NotificationRouter {
         return ret;
     }
 
-    private class ListenerRegistration extends AbstractObjectRegistration<NotificationListener> {
+    private class ListenerRegistration extends AbstractListenerRegistration<NotificationListener> {
 
         final QName type;
 
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..70db71f
--- /dev/null
@@ -0,0 +1,41 @@
+package org.opendaylight.controller.sal.dom.broker.osgi;
+
+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.DOMDataReadTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+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 DOMDataReadTransaction 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 InstanceIdentifier path, final DOMDataChangeListener listener,
+            final org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope triggeringScope) {
+        return getDelegate().registerDataChangeListener(store, path, listener, triggeringScope);
+    }
+
+}
index 5b97443b92e455aafb8bcf54cffd1dbc29d2f1e9..d0afc3f47dbb325b2e9b95fb715b3f8a611a82d9 100644 (file)
@@ -16,6 +16,7 @@ import org.opendaylight.controller.sal.core.api.notify.NotificationService
 import org.opendaylight.controller.sal.core.api.model.SchemaService
 import org.opendaylight.controller.sal.core.api.mount.MountProvisionService
 import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker
 
 class ProxyFactory {
 
@@ -23,6 +24,7 @@ class ProxyFactory {
         return createProxyImpl(serviceRef, service) as T;
     }
 
+
     private static def dispatch createProxyImpl(ServiceReference<?> ref, DataBrokerService service) {
         new DataBrokerServiceProxy(ref as ServiceReference<DataBrokerService>, service);
     }
@@ -51,6 +53,11 @@ class ProxyFactory {
     private static def dispatch createProxyImpl(ServiceReference<?> ref, RpcProvisionRegistry service) {
         new RpcProvisionRegistryProxy(ref as ServiceReference<RpcProvisionRegistry>, service);
     }
+    
+    private static def dispatch createProxyImpl(ServiceReference<?> ref, DOMDataBroker service) {
+        new DOMDataBrokerProxy(ref as ServiceReference<DOMDataBroker>, service)
+    }
+    
 
     private static def dispatch createProxyImpl(ServiceReference<?> reference, BrokerService service) {
         throw new IllegalArgumentException("Not supported class");
index 9ae9c9ce6d4fe65571b4aad1640b3b8782e82930..3c29fe588567dafee48c5f4e628e4eb22a8364fc 100644 (file)
@@ -21,6 +21,12 @@ module opendaylight-sal-dom-broker-impl {
         config:java-name-prefix DomBrokerImpl;
     }    
     
+    
+    identity dom-inmemory-data-broker {
+        base config:module-type;
+        config:provided-service sal:dom-async-data-broker;
+    }
+    
     identity hash-map-data-store {
         base config:module-type;
         config:provided-service sal:dom-data-store;
@@ -39,11 +45,37 @@ module opendaylight-sal-dom-broker-impl {
             container data-store {
                 uses config:service-ref {
                     refine type {
-                        mandatory true;
+                        mandatory false;
                         config:required-identity sal:dom-data-store;
                     }
                 }
             }
+            
+            container async-data-broker {
+                uses config:service-ref {
+                    refine type {
+                        mandatory false;
+                        config:required-identity sal:dom-async-data-broker;
+                    }
+                }
+            
+            }
+        }
+    }
+    
+    augment "/config:modules/config:module/config:configuration" {
+        case dom-inmemory-data-broker {
+            when "/config:modules/config:module/config:type = 'dom-inmemory-data-broker'";
+            
+            container schema-service {
+                uses config:service-ref {
+                    refine type {
+                        mandatory false;
+                        config:required-identity sal:schema-service;
+                    }
+                }
+            
+            }
         }
     }
     
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..66d57f9
--- /dev/null
@@ -0,0 +1,277 @@
+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 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.InstanceIdentifier;
+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;
+
+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;
+
+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 InstanceIdentifier outerListPath(final int i) {
+        return InstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)//
+                .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i) //
+                .build();
+    }
+
+    private SchemaContext schemaContext;
+    private DOMDataBrokerImpl 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.sameThreadExecutor());
+        InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG", MoreExecutors.sameThreadExecutor());
+        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 DOMDataBrokerImpl(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
+                    InstanceIdentifier path = InstanceIdentifier.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++) {
+                        InstanceIdentifier innerPath = InstanceIdentifier.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.commit());
+                                }
+                                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++) {
+                        InstanceIdentifier path = InstanceIdentifier
+                                .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.commit();
+                    }
+                }).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..fec73d6
--- /dev/null
@@ -0,0 +1,119 @@
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+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 java.util.concurrent.ExecutionException;
+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.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+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;
+
+public class DOMBrokerTest {
+
+    private SchemaContext schemaContext;
+    private DOMDataBrokerImpl domBroker;
+
+    @Before
+    public void setupStore() {
+        InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER", MoreExecutors.sameThreadExecutor());
+        InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG", MoreExecutors.sameThreadExecutor());
+        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 DOMDataBrokerImpl(stores, executor);
+    }
+
+    @Test
+    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
+    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.commit().get();
+
+        Optional<NormalizedNode<?, ?>> afterCommitRead = domBroker.newReadOnlyTransaction()
+                .read(OPERATIONAL, TestModel.TEST_PATH).get();
+        assertTrue(afterCommitRead.isPresent());
+    }
+
+
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DataNormalizerTest.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DataNormalizerTest.java
new file mode 100644 (file)
index 0000000..9aa558b
--- /dev/null
@@ -0,0 +1,60 @@
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.controller.md.sal.dom.store.impl.TestModel;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+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 DataNormalizerTest {
+
+    private static final Short OUTER_LIST_ID = (short)10;
+
+    private static final InstanceIdentifier OUTER_LIST_PATH_LEGACY = InstanceIdentifier.builder(TestModel.TEST_QNAME)
+            .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME,OUTER_LIST_ID).build();
+
+    private  static final InstanceIdentifier LEAF_TWO_PATH_LEGACY = InstanceIdentifier.builder(OUTER_LIST_PATH_LEGACY)
+            .node(TestModel.TWO_QNAME).build();
+
+    private static final ChoiceNode OUTER_CHOICE_ITEM = Builders.choiceBuilder()
+            .withNodeIdentifier(new NodeIdentifier(TestModel.OUTER_CHOICE_QNAME))
+            .withChild(ImmutableNodes.leafNode(TestModel.TWO_QNAME, "two"))
+            .withChild(ImmutableNodes.leafNode(TestModel.THREE_QNAME, "three"))
+            .build();
+
+    private static final MapEntryNode OUTER_LIST_WITHOUT_CHOICE = Builders.mapEntryBuilder()
+            .withNodeIdentifier(new NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME,OUTER_LIST_ID))
+            .withChild(ImmutableNodes.leafNode(TestModel.ID_QNAME, OUTER_LIST_ID))
+            .build();
+
+    private static final MapEntryNode OUTER_LIST_WITH_CHOICE = Builders.mapEntryBuilder()
+            .withNodeIdentifier(new NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME,OUTER_LIST_ID))
+            .withChild(ImmutableNodes.leafNode(TestModel.ID_QNAME, OUTER_LIST_ID))
+            .withChild(OUTER_CHOICE_ITEM)
+            .build();
+
+    @Test
+    public void test() {
+        SchemaContext testCtx = TestModel.createTestContext();
+        DataNormalizer normalizer = new DataNormalizer(testCtx);
+
+        InstanceIdentifier normalizedPath = normalizer.toNormalized(LEAF_TWO_PATH_LEGACY);
+
+        Node<?> outerListLegacy = normalizer.toLegacy(OUTER_LIST_WITH_CHOICE);
+        assertNotNull(outerListLegacy);
+
+
+
+
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java
new file mode 100644 (file)
index 0000000..5a43c7b
--- /dev/null
@@ -0,0 +1,167 @@
+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.junit.Before;
+import org.junit.Test;
+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.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+
+public class InMemoryDataStoreTest {
+
+    private SchemaContext schemaContext;
+    private InMemoryDOMDataStore domStore;
+
+
+    @Before
+    public void setupStore() {
+        domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.sameThreadExecutor());
+        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
+         *
+         */
+        writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+        /**
+         *
+         * Reads /test from writeTx
+         * Read should return container.
+         *
+         */
+        ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
+        assertTrue(writeTxContainer.get().isPresent());
+
+        /**
+        *
+        * Reads /test from readTx
+        * Read should return Absent.
+        *
+        */
+        ListenableFuture<Optional<NormalizedNode<?, ?>>> readTxContainer = readTx.read(TestModel.TEST_PATH);
+        assertFalse(readTxContainer.get().isPresent());
+    }
+
+    @Test
+    public void testTransactionCommit() throws InterruptedException, ExecutionException {
+
+        DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+        assertNotNull(writeTx);
+        /**
+         *
+         * Writes /test in writeTx
+         *
+         */
+        writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+        /**
+         *
+         * Reads /test from writeTx
+         * Read should return container.
+         *
+         */
+        ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
+        assertTrue(writeTxContainer.get().isPresent());
+
+        DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
+
+        assertThreePhaseCommit(cohort);
+
+        Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
+        assertTrue(afterCommitRead.isPresent());
+    }
+
+    @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 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));
+
+       /**
+        *
+        * Reads /test from writeTx
+        * Read should return container.
+        *
+        */
+       ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
+       assertTrue(writeTxContainer.get().isPresent());
+       return writeTxContainer.get();
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/ModificationMetadataTreeTest.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/ModificationMetadataTreeTest.java
new file mode 100644 (file)
index 0000000..7adc3c7
--- /dev/null
@@ -0,0 +1,250 @@
+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.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.controller.md.sal.dom.store.impl.TestModel.ID_QNAME;
+import static org.opendaylight.controller.md.sal.dom.store.impl.TestModel.INNER_LIST_QNAME;
+import static org.opendaylight.controller.md.sal.dom.store.impl.TestModel.NAME_QNAME;
+import static org.opendaylight.controller.md.sal.dom.store.impl.TestModel.OUTER_LIST_PATH;
+import static org.opendaylight.controller.md.sal.dom.store.impl.TestModel.OUTER_LIST_QNAME;
+import static org.opendaylight.controller.md.sal.dom.store.impl.TestModel.TEST_PATH;
+import static org.opendaylight.controller.md.sal.dom.store.impl.TestModel.TEST_QNAME;
+import static org.opendaylight.controller.md.sal.dom.store.impl.TestModel.VALUE_QNAME;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntry;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntryBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapNodeBuilder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.base.Optional;
+import com.google.common.primitives.UnsignedLong;
+
+/**
+ *
+ * Schema structure of document is
+ *
+ * <pre>
+ * container root { 
+ *      list list-a {
+ *              key leaf-a;
+ *              leaf leaf-a;
+ *              choice choice-a {
+ *                      case one {
+ *                              leaf one;
+ *                      }
+ *                      case two-three {
+ *                              leaf two;
+ *                              leaf three;
+ *                      }
+ *              }
+ *              list list-b {
+ *                      key leaf-b;
+ *                      leaf leaf-b;
+ *              }
+ *      }
+ * }
+ * </pre>
+ *
+ */
+public class ModificationMetadataTreeTest {
+
+    private static final Short ONE_ID = 1;
+    private static final Short TWO_ID = 2;
+    private static final String TWO_ONE_NAME = "one";
+    private static final String TWO_TWO_NAME = "two";
+
+    private static final InstanceIdentifier OUTER_LIST_1_PATH = InstanceIdentifier.builder(OUTER_LIST_PATH)
+            .nodeWithKey(OUTER_LIST_QNAME, ID_QNAME, ONE_ID) //
+            .build();
+
+    private static final InstanceIdentifier OUTER_LIST_2_PATH = InstanceIdentifier.builder(OUTER_LIST_PATH)
+            .nodeWithKey(OUTER_LIST_QNAME, ID_QNAME, TWO_ID) //
+            .build();
+
+    private static final InstanceIdentifier TWO_TWO_PATH = InstanceIdentifier.builder(OUTER_LIST_2_PATH)
+            .node(INNER_LIST_QNAME) //
+            .nodeWithKey(INNER_LIST_QNAME, NAME_QNAME, TWO_TWO_NAME) //
+            .build();
+
+    private static final InstanceIdentifier TWO_TWO_VALUE_PATH = InstanceIdentifier.builder(TWO_TWO_PATH)
+            .node(VALUE_QNAME) //
+            .build();
+
+    private static final MapEntryNode BAR_NODE = mapEntryBuilder(OUTER_LIST_QNAME, ID_QNAME, TWO_ID) //
+            .withChild(mapNodeBuilder(INNER_LIST_QNAME) //
+                    .withChild(mapEntry(INNER_LIST_QNAME, NAME_QNAME, TWO_ONE_NAME)) //
+                    .withChild(mapEntry(INNER_LIST_QNAME, NAME_QNAME, TWO_TWO_NAME)) //
+                    .build()) //
+            .build();
+
+    private SchemaContext schemaContext;
+
+    @Before
+    public void prepare() {
+        schemaContext = TestModel.createTestContext();
+        assertNotNull("Schema context must not be null.", schemaContext);
+    }
+
+    /**
+     * Returns a test document
+     *
+     * <pre>
+     * test
+     *     outer-list
+     *          id 1
+     *     outer-list
+     *          id 2
+     *          inner-list
+     *                  name "one"
+     *          inner-list
+     *                  name "two"
+     *
+     * </pre>
+     *
+     * @return
+     */
+    public NormalizedNode<?, ?> createDocumentOne() {
+        return ImmutableContainerNodeBuilder
+                .create()
+                .withNodeIdentifier(new NodeIdentifier(schemaContext.getQName()))
+                .withChild(createTestContainer()).build();
+
+    }
+
+    private ContainerNode createTestContainer() {
+        return ImmutableContainerNodeBuilder
+                .create()
+                .withNodeIdentifier(new NodeIdentifier(TEST_QNAME))
+                .withChild(
+                        mapNodeBuilder(OUTER_LIST_QNAME)
+                                .withChild(mapEntry(OUTER_LIST_QNAME, ID_QNAME, ONE_ID))
+                                .withChild(BAR_NODE).build()).build();
+    }
+
+    @Test
+    public void basicReadWrites() {
+        MutableDataTree modificationTree = MutableDataTree.from(
+                DataAndMetadataSnapshot.builder() //
+                        .setMetadataTree(StoreMetadataNode.createRecursivelly(createDocumentOne(), UnsignedLong.valueOf(5))) //
+                        .setSchemaContext(schemaContext) //
+                        .build(), new SchemaAwareApplyOperationRoot(schemaContext));
+        Optional<NormalizedNode<?, ?>> originalBarNode = modificationTree.read(OUTER_LIST_2_PATH);
+        assertTrue(originalBarNode.isPresent());
+        assertSame(BAR_NODE, originalBarNode.get());
+
+        // writes node to /outer-list/1/inner_list/two/value
+        modificationTree.write(TWO_TWO_VALUE_PATH, ImmutableNodes.leafNode(VALUE_QNAME, "test"));
+
+        // reads node to /outer-list/1/inner_list/two/value
+        // and checks if node is already present
+        Optional<NormalizedNode<?, ?>> barTwoCModified = modificationTree.read(TWO_TWO_VALUE_PATH);
+        assertTrue(barTwoCModified.isPresent());
+        assertEquals(ImmutableNodes.leafNode(VALUE_QNAME, "test"), barTwoCModified.get());
+
+        // delete node to /outer-list/1/inner_list/two/value
+        modificationTree.delete(TWO_TWO_VALUE_PATH);
+        Optional<NormalizedNode<?, ?>> barTwoCAfterDelete = modificationTree.read(TWO_TWO_VALUE_PATH);
+        assertFalse(barTwoCAfterDelete.isPresent());
+    }
+
+
+    public MutableDataTree createEmptyModificationTree() {
+        /**
+         * Creates empty Snapshot with associated schema context.
+         */
+        DataAndMetadataSnapshot emptySnapshot = DataAndMetadataSnapshot.createEmpty(schemaContext);
+
+        /**
+         *
+         * Creates Mutable Data Tree based on provided snapshot and schema
+         * context.
+         *
+         */
+        MutableDataTree modificationTree = MutableDataTree.from(emptySnapshot, new SchemaAwareApplyOperationRoot(
+                schemaContext));
+        return modificationTree;
+    }
+
+    @Test
+    public void createFromEmptyState() {
+
+        MutableDataTree modificationTree = createEmptyModificationTree();
+        /**
+         * Writes empty container node to /test
+         *
+         */
+        modificationTree.write(TEST_PATH, ImmutableNodes.containerNode(TEST_QNAME));
+
+        /**
+         * Writes empty list node to /test/outer-list
+         */
+        modificationTree.write(OUTER_LIST_PATH, ImmutableNodes.mapNodeBuilder(OUTER_LIST_QNAME).build());
+
+        /**
+         * Reads list node from /test/outer-list
+         */
+        Optional<NormalizedNode<?, ?>> potentialOuterList = modificationTree.read(OUTER_LIST_PATH);
+        assertTrue(potentialOuterList.isPresent());
+
+        /**
+         * Reads container node from /test and verifies that it contains test
+         * node
+         */
+        Optional<NormalizedNode<?, ?>> potentialTest = modificationTree.read(TEST_PATH);
+        ContainerNode containerTest = assertPresentAndType(potentialTest, ContainerNode.class);
+
+        /**
+         *
+         * Gets list from returned snapshot of /test and verifies it contains
+         * outer-list
+         *
+         */
+        assertPresentAndType(containerTest.getChild(new NodeIdentifier(OUTER_LIST_QNAME)), MapNode.class);
+
+    }
+
+    @Test
+    public void writeSubtreeReadChildren() {
+        MutableDataTree modificationTree = createEmptyModificationTree();
+        modificationTree.write(TEST_PATH, createTestContainer());
+        Optional<NormalizedNode<?, ?>> potential = modificationTree.read(TWO_TWO_PATH);
+        MapEntryNode node = assertPresentAndType(potential, MapEntryNode.class);
+    }
+
+    @Test
+    public void writeSubtreeDeleteChildren() {
+        MutableDataTree modificationTree = createEmptyModificationTree();
+        modificationTree.write(TEST_PATH, createTestContainer());
+
+        // We verify data are present
+        Optional<NormalizedNode<?, ?>> potentialBeforeDelete = modificationTree.read(TWO_TWO_PATH);
+        MapEntryNode node = assertPresentAndType(potentialBeforeDelete, MapEntryNode.class);
+
+        modificationTree.delete(TWO_TWO_PATH);
+        Optional<NormalizedNode<?, ?>> potentialAfterDelete = modificationTree.read(TWO_TWO_PATH);
+        assertFalse(potentialAfterDelete.isPresent());
+
+    }
+
+    private static <T> T assertPresentAndType(final Optional<?> potential, final Class<T> type) {
+        assertNotNull(potential);
+        assertTrue(potential.isPresent());
+        assertTrue(type.isInstance(potential.get()));
+        return type.cast(potential.get());
+    }
+
+}
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..2c96504
--- /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 java.io.InputStream;
+import java.util.Collections;
+import java.util.Set;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+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 InstanceIdentifier TEST_PATH = InstanceIdentifier.of(TEST_QNAME);
+    public static final InstanceIdentifier OUTER_LIST_PATH = InstanceIdentifier.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..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-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..c82a2b8
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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.controller.md.sal.common.api.data.DataChangeListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMStore {
+
+    /**
+     *
+     * Creates a read only transaction
+     *
+     * @return
+     */
+    DOMStoreReadTransaction newReadOnlyTransaction();
+
+    /**
+     * Creates write only transaction
+     *
+     * @return
+     */
+    DOMStoreWriteTransaction newWriteOnlyTransaction();
+
+    /**
+     * Creates Read-Write transaction
+     *
+     * @return
+     */
+    DOMStoreReadWriteTransaction newReadWriteTransaction();
+
+    /**
+     * Registers {@link 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<InstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerChangeListener(
+            InstanceIdentifier path, L listener, DataChangeScope scope);
+
+}
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..733c109
--- /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.core.spi.data;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+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 Listenable Future which contains read result
+     *         <ul>
+     *         <li>If data at supplied path exists the {@link Future#get()}
+     *         returns Optional object containing data
+     *         <li>If data at supplied path does not exists the
+     *         {@link Future#get()} returns {@link Optional#absent()}.
+     *         </ul>
+     */
+    ListenableFuture<Optional<NormalizedNode<?,?>>> read(InstanceIdentifier 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/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..6761bc1
--- /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.sal.core.spi.data;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+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(LogicalDatastoreType, 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(InstanceIdentifier 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(InstanceIdentifier 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/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
index 2105e2e71daa5c1a217bad67b7a2b1a8cb6ec24f..182441d3f5df53135133505448e4979135126989 100644 (file)
             <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-broker-impl</artifactId>
                     </instructions>
                 </configuration>
             </plugin>
+
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
                         </goals>
                         <configuration>
                             <codeGenerators>
+                                <generator>
+                                    <codeGeneratorClass>
+                                        org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl
+                                    </codeGeneratorClass>
+                                    <outputBaseDir>
+                                        ${salGeneratorPath}
+                                    </outputBaseDir>
+                                </generator>
                                 <generator>
                                     <codeGeneratorClass>
                                         org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
                     </dependency>
                 </dependencies>
             </plugin>
+
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>build-helper-maven-plugin</artifactId>
index 451535747697e9c998f012ba2a9b7bf922cd339d..415ae5aa052ef76a6a3379f1480875543004955f 100644 (file)
                     <instructions>
                         <Import-Package>
                             *,
-                            com.google.common.collect,
                             !org.codehaus.enunciate.jaxrs
                         </Import-Package>
                         <Export-Package>
index 3e73f161c28df060f33e9f658b8eb4d60d30f5ac..ffdcda31eb7b4916bc96b3ceca017da177648ab3 100644 (file)
@@ -50,7 +50,7 @@
         </plugins>
         <pluginManagement>
             <plugins>
-                <!--This plugin's configuration is used to store Eclipse 
+                <!--This plugin's configuration is used to store Eclipse
                     m2e settings only. It has no influence on the Maven build itself. -->
                 <plugin>
                     <groupId>org.eclipse.m2e</groupId>
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>yang-data-api</artifactId>
         </dependency>
-        <!--dependency> <groupId>org.opendaylight.yangtools</groupId> <artifactId>yang-data-impl</artifactId> 
+        <!--dependency> <groupId>org.opendaylight.yangtools</groupId> <artifactId>yang-data-impl</artifactId>
             <version>${yangtools.version}</version> </dependency -->
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.controller</groupId>
-            <artifactId>config-persister-file-adapter</artifactId>
+            <artifactId>config-persister-file-xml-adapter</artifactId>
             <version>${config.version}</version>
         </dependency>
         <dependency>
             <groupId>org.ops4j.pax.exam</groupId>
             <artifactId>pax-exam</artifactId>
             <version>${exam.version}</version>
-            <!-- Compile scope here is intentional, it is used in TestHelper 
-                class which could be downloaded via nexus and reused in other integration 
+            <!-- Compile scope here is intentional, it is used in TestHelper
+                class which could be downloaded via nexus and reused in other integration
                 tests. -->
             <scope>compile</scope>
         </dependency>
index cc3b0296ed3ed299ab0ffe5a58b3d2e2775b78bd..4aa3824e615aef1dd528d30a69ae73f39e358de8 100644 (file)
       <groupId>io.netty</groupId>
       <artifactId>netty-codec-http</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-remote</artifactId>
+     </dependency>
     
     <!-- Testing Dependencies -->
     <dependency>
index 8ebf28f35f1771ec778fc6e1a5b16088f6aafe16..fa478ac72e398ae928c619004023b8aae6cc68fe 100644 (file)
@@ -305,7 +305,7 @@ class RestconfImpl implements RestconfService {
         return callRpc(rpc, null)
     }
 
-    def resolveIdentifierInInvokeRpc(String identifier) {
+    private def resolveIdentifierInInvokeRpc(String identifier) {
         if (identifier.indexOf("/") === -1) {
             val identifierDecoded = identifier.urlPathArgDecode
             val rpc = identifierDecoded.rpcDefinition
@@ -314,8 +314,8 @@ class RestconfImpl implements RestconfService {
             }
             throw new ResponseException(NOT_FOUND, "RPC does not exist.");
         }
-        val slashErrorMsg  = String.format("Identifier %n%s%ncan't contain slash character (/). +
-            If slash is part of identifier name then use %2F placeholder.",identifier)
+        val slashErrorMsg = String.format(
+            "Identifier %n%s%ncan't contain slash character (/).%nIf slash is part of identifier name then use %%2F placeholder.", identifier)
         throw new ResponseException(NOT_FOUND, slashErrorMsg);
     }
 
index dff9d65db3f043b3b264ad50bb83687dbb9bba10..320a7f9d08b1f80d3465e7a9570df2044831563a 100644 (file)
@@ -7,20 +7,34 @@
  */
 package org.opendaylight.controller.sal.restconf.impl.test;
 
+import static junit.framework.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 import java.io.FileNotFoundException;
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+
 import javax.ws.rs.core.Application;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+
 import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.test.JerseyTest;
 import org.junit.BeforeClass;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.opendaylight.controller.sal.core.api.mount.MountInstance;
 import org.opendaylight.controller.sal.core.api.mount.MountService;
@@ -33,16 +47,12 @@ import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
 import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
 import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import static junit.framework.Assert.assertNotNull;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 public class RestGetOperationTest extends JerseyTest {
 
@@ -133,6 +143,52 @@ public class RestGetOperationTest extends JerseyTest {
         assertEquals(200, get(uri, MediaType.APPLICATION_XML));
     }
 
+    /**
+     * MountPoint test. URI represents mount point.
+     * 
+     * Slashes in URI behind mount point. lst1 element with key
+     * GigabitEthernet0%2F0%2F0%2F0 (GigabitEthernet0/0/0/0) is requested via
+     * GET HTTP operation. It is tested whether %2F character is replaced with
+     * simple / in InstanceIdentifier parameter in method
+     * {@link BrokerFacade#readConfigurationDataBehindMountPoint(MountInstance, InstanceIdentifier)}
+     * which is called in method {@link RestconfImpl#readConfigurationData}
+     * 
+     * 
+     * @throws ParseException
+     */
+    @Test
+    public void getDataWithSlashesBehindMountPoint() throws UnsupportedEncodingException, URISyntaxException,
+            ParseException {
+        InstanceIdentifier awaitedInstanceIdentifier = prepareInstanceIdentifierForList();
+        when(
+                brokerFacade.readConfigurationDataBehindMountPoint(any(MountInstance.class),
+                        eq(awaitedInstanceIdentifier))).thenReturn(prepareCnDataForMountPointTest());
+        MountInstance mountInstance = mock(MountInstance.class);
+        when(mountInstance.getSchemaContext()).thenReturn(schemaContextTestModule);
+        MountService mockMountService = mock(MountService.class);
+        when(mockMountService.getMountPoint(any(InstanceIdentifier.class))).thenReturn(mountInstance);
+
+        ControllerContext.getInstance().setMountService(mockMountService);
+
+        String uri = "/config/ietf-interfaces:interfaces/interface/0/yang-ext:mount/test-module:cont/lst1/GigabitEthernet0%2F0%2F0%2F0";
+        assertEquals(200, get(uri, MediaType.APPLICATION_XML));
+    }
+
+    private InstanceIdentifier prepareInstanceIdentifierForList() throws URISyntaxException, ParseException {
+        List<PathArgument> parameters = new ArrayList<>();
+
+        Date revision = new SimpleDateFormat("yyyy-MM-dd").parse("2014-01-09");
+        URI uri = new URI("test:module");
+        QName qNameCont = QName.create(uri, revision, "cont");
+        QName qNameList = QName.create(uri, revision, "lst1");
+        QName qNameKeyList = QName.create(uri, revision, "lf11");
+
+        parameters.add(new InstanceIdentifier.NodeIdentifier(qNameCont));
+        parameters.add(new InstanceIdentifier.NodeIdentifierWithPredicates(qNameList, qNameKeyList,
+                "GigabitEthernet0/0/0/0"));
+        return new InstanceIdentifier(parameters);
+    }
+
     @Test
     public void getDataMountPointIntoHighestElement() throws UnsupportedEncodingException, URISyntaxException {
         when(
@@ -162,6 +218,7 @@ public class RestGetOperationTest extends JerseyTest {
         response = target(uri).request("application/yang.api+xml").get();
         validateModulesResponseXml(response);
     }
+
     // /streams/
     @Test
     public void getStreamsTest() throws UnsupportedEncodingException, FileNotFoundException {
@@ -193,7 +250,7 @@ public class RestGetOperationTest extends JerseyTest {
         assertTrue("Module2 in xml wasn't found", prepareXmlRegex("module2", "2014-01-02", "module:2", responseBody)
                 .find());
         String[] split = responseBody.split("<module");
-        assertEquals("<module element is returned more then once",2,split.length);
+        assertEquals("<module element is returned more then once", 2, split.length);
 
         response = target(uri).request("application/yang.api+json").get();
         assertEquals(200, response.getStatus());
@@ -201,7 +258,7 @@ public class RestGetOperationTest extends JerseyTest {
         assertTrue("Module2 in json wasn't found", prepareJsonRegex("module2", "2014-01-02", "module:2", responseBody)
                 .find());
         split = responseBody.split("\"module\"");
-        assertEquals("\"module\" element is returned more then once",2,split.length);
+        assertEquals("\"module\" element is returned more then once", 2, split.length);
 
     }
 
@@ -395,8 +452,7 @@ public class RestGetOperationTest extends JerseyTest {
                 prepareJsonRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point",
                         responseBody).find());
         String[] split = responseBody.split("\"module\"");
-        assertEquals("\"module\" element is returned more then once",2,split.length);
-
+        assertEquals("\"module\" element is returned more then once", 2, split.length);
 
         response = target(uri).request("application/yang.api+xml").get();
         assertEquals(200, response.getStatus());
@@ -406,10 +462,7 @@ public class RestGetOperationTest extends JerseyTest {
                 prepareXmlRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point", responseBody)
                         .find());
         split = responseBody.split("<module");
-        assertEquals("<module element is returned more then once",2,split.length);
-
-
-
+        assertEquals("<module element is returned more then once", 2, split.length);
 
     }
 
index 93342ead54f27a633973a965c3ee48e16c238445..2e533e720e0760f6abca5b84663c52ce170a4899 100644 (file)
@@ -28,7 +28,13 @@ module test-module {
             type string;
         }
     }
-  } 
+    list lst1 {
+        key "lf11";
+        leaf lf11 {
+            type string;
+        }
+    }
+  }   
   
   
   rpc rpc-test {
diff --git a/opendaylight/md-sal/samples/l2switch/implementation/pom.xml b/opendaylight/md-sal/samples/l2switch/implementation/pom.xml
new file mode 100644 (file)
index 0000000..c095eee
--- /dev/null
@@ -0,0 +1,92 @@
+<?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>
+    <artifactId>sal-samples</artifactId>
+    <groupId>org.opendaylight.controller.samples</groupId>
+    <version>1.1-SNAPSHOT</version>
+    <relativePath>../..</relativePath>
+  </parent>
+  <groupId>org.opendaylight.controller.samples.l2switch.md</groupId>
+  <artifactId>l2switch-impl</artifactId>
+  <packaging>bundle</packaging>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+
+          <instructions>
+            <Bundle-Activator>org.opendaylight.controller.sample.l2switch.md.L2SwitchProvider</Bundle-Activator>
+          </instructions>
+          <manifestLocation>${project.build.directory}/META-INF</manifestLocation>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>${junit.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller.samples.l2switch.md</groupId>
+      <artifactId>l2switch-model</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.opendaylight.controller.model</groupId>
+      <artifactId>model-inventory</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-binding-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-common</artifactId>
+      <version>${yangtools.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-binding</artifactId>
+      <version>${yangtools.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-all</artifactId>
+      <version>${mockito.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller.model</groupId>
+      <artifactId>model-flow-service</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller.thirdparty</groupId>
+      <artifactId>net.sf.jung2</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller.model</groupId>
+      <artifactId>model-topology</artifactId>
+      <version>1.1-SNAPSHOT</version>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/L2SwitchProvider.java b/opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/L2SwitchProvider.java
new file mode 100644 (file)
index 0000000..6f31a7e
--- /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.sample.l2switch.md;
+
+import org.opendaylight.controller.sample.l2switch.md.addresstracker.AddressTracker;
+import org.opendaylight.controller.sample.l2switch.md.flow.FlowWriterService;
+import org.opendaylight.controller.sample.l2switch.md.flow.FlowWriterServiceImpl;
+import org.opendaylight.controller.sample.l2switch.md.inventory.InventoryService;
+import org.opendaylight.controller.sample.l2switch.md.packet.PacketHandler;
+import org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphDijkstra;
+import org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService;
+import org.opendaylight.controller.sample.l2switch.md.topology.TopologyLinkDataChangeHandler;
+import org.opendaylight.controller.sal.binding.api.AbstractBindingAwareConsumer;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationService;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * L2SwitchProvider serves as the Activator for our L2Switch OSGI bundle.
+ */
+public class L2SwitchProvider extends AbstractBindingAwareConsumer
+                              implements AutoCloseable {
+
+  private final static Logger _logger = LoggerFactory.getLogger(L2SwitchProvider.class);
+
+  private Registration<NotificationListener> listenerRegistration;
+  private AddressTracker addressTracker;
+  private TopologyLinkDataChangeHandler topologyLinkDataChangeHandler;
+
+
+  /**
+   * Setup the L2Switch.
+   * @param consumerContext  The context of the L2Switch.
+   */
+  @Override
+  public void onSessionInitialized(BindingAwareBroker.ConsumerContext consumerContext) {
+    DataBrokerService dataService = consumerContext.<DataBrokerService>getSALService(DataBrokerService.class);
+    addressTracker = new AddressTracker(dataService);
+
+    NetworkGraphService networkGraphService = new NetworkGraphDijkstra();
+    FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataService, networkGraphService);
+
+    NotificationService notificationService =
+            consumerContext.<NotificationService>getSALService(NotificationService.class);
+    PacketProcessingService packetProcessingService =
+            consumerContext.<PacketProcessingService>getRpcService(PacketProcessingService.class);
+    PacketHandler packetHandler = new PacketHandler();
+    packetHandler.setAddressTracker(addressTracker);
+    packetHandler.setFlowWriterService(flowWriterService);
+    packetHandler.setPacketProcessingService(packetProcessingService);
+    packetHandler.setInventoryService(new InventoryService(dataService));
+
+    this.listenerRegistration = notificationService.registerNotificationListener(packetHandler);
+    this.topologyLinkDataChangeHandler = new TopologyLinkDataChangeHandler(dataService, networkGraphService);
+    topologyLinkDataChangeHandler.registerAsDataChangeListener();
+  }
+
+  /**
+   * Cleanup the L2Switch.
+   * @throws Exception  occurs when the NotificationListener is closed
+   */
+  @Override
+  public void close() throws Exception {
+    if (listenerRegistration != null)
+      listenerRegistration.close();
+  }
+}
diff --git a/opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/addresstracker/AddressTracker.java b/opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/addresstracker/AddressTracker.java
new file mode 100644 (file)
index 0000000..ae5f031
--- /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.sample.l2switch.md.addresstracker;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.address.tracker.rev140402.L2Addresses;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.address.tracker.rev140402.l2.addresses.L2Address;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.address.tracker.rev140402.l2.addresses.L2AddressBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.address.tracker.rev140402.l2.addresses.L2AddressKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.Future;
+
+/**
+ * AddressTracker manages the MD-SAL data tree for L2Address (mac, node connector pairings) information.
+ */
+public class AddressTracker {
+
+  private final static Logger _logger = LoggerFactory.getLogger(AddressTracker.class);
+  private DataBrokerService dataService;
+
+  /**
+   * Construct an AddressTracker with the specified inputs
+   * @param dataService  The DataBrokerService for the AddressTracker
+   */
+  public AddressTracker(DataBrokerService dataService) {
+    this.dataService = dataService;
+  }
+
+  /**
+   * Get all the L2 Addresses in the MD-SAL data tree
+   * @return    All the L2 Addresses in the MD-SAL data tree
+   */
+  public L2Addresses getAddresses() {
+    return (L2Addresses)dataService.readOperationalData(InstanceIdentifier.<L2Addresses>builder(L2Addresses.class).toInstance());
+  }
+
+  /**
+   * Get a specific L2 Address in the MD-SAL data tree
+   * @param macAddress  A MacAddress associated with an L2 Address object
+   * @return    The L2 Address corresponding to the specified macAddress
+   */
+  public L2Address getAddress(MacAddress macAddress) {
+    return (L2Address) dataService.readOperationalData(createPath(macAddress));
+  }
+
+  /**
+   * Add L2 Address into the MD-SAL data tree
+   * @param macAddress  The MacAddress of the new L2Address object
+   * @param nodeConnectorRef  The NodeConnectorRef of the new L2Address object
+   * @return  Future containing the result of the add operation
+   */
+  public Future<RpcResult<TransactionStatus>> addAddress(MacAddress macAddress, NodeConnectorRef nodeConnectorRef) {
+    if(macAddress == null || nodeConnectorRef == null) {
+      return null;
+    }
+
+    // Create L2Address
+    final L2AddressBuilder builder = new L2AddressBuilder();
+    builder.setKey(new L2AddressKey(macAddress))
+            .setMac(macAddress)
+            .setNodeConnectorRef(nodeConnectorRef);
+
+    // Add L2Address to MD-SAL data tree
+    final DataModificationTransaction it = dataService.beginTransaction();
+    it.putOperationalData(createPath(macAddress), builder.build());
+    return it.commit();
+  }
+
+  /**
+   * Remove L2Address from the MD-SAL data tree
+   * @param macAddress  The MacAddress of an L2Address object
+   * @return  Future containing the result of the remove operation
+   */
+  public Future<RpcResult<TransactionStatus>> removeHost(MacAddress macAddress) {
+    final DataModificationTransaction it = dataService.beginTransaction();
+    it.removeOperationalData(createPath(macAddress));
+    return it.commit();
+  }
+
+  /**
+   * Create InstanceIdentifier path for an L2Address in the MD-SAL data tree
+   * @param macAddress  The MacAddress of an L2Address object
+   * @return  InstanceIdentifier of the L2Address corresponding to the specified macAddress
+   */
+  private InstanceIdentifier<L2Address> createPath(MacAddress macAddress) {
+    return InstanceIdentifier.<L2Addresses>builder(L2Addresses.class)
+            .<L2Address, L2AddressKey>child(L2Address.class, new L2AddressKey(macAddress)).toInstance();
+  }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/flow/FlowWriterService.java b/opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/flow/FlowWriterService.java
new file mode 100644 (file)
index 0000000..2d5149e
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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.sample.l2switch.md.flow;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+
+/**
+ * Service that adds packet forwarding flows to configuration data store.
+ */
+public interface FlowWriterService {
+
+  /**
+   * Writes a flow that forwards packets to destPort if destination mac in packet is destMac and
+   * source Mac in packet is sourceMac. If sourceMac is null then flow would not set any source mac,
+   * resulting in all packets with destMac being forwarded to destPort.
+   *
+   * @param sourceMac
+   * @param destMac
+   * @param destNodeConnectorRef
+   */
+  public void addMacToMacFlow(MacAddress sourceMac, MacAddress destMac, NodeConnectorRef destNodeConnectorRef);
+
+  /**
+   * Writes mac-to-mac flow on all ports that are in the path between given source and destination ports.
+   * It uses path provided by NetworkGraphService{@link org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService} to find a links{@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
+   * between given ports. And then writes appropriate flow on each port that is covered in that path.
+   *
+   * @param sourceMac
+   * @param sourceNodeConnectorRef
+   * @param destMac
+   * @param destNodeConnectorRef
+   */
+  public void addMacToMacFlowsUsingShortestPath(MacAddress sourceMac, NodeConnectorRef sourceNodeConnectorRef, MacAddress destMac, NodeConnectorRef destNodeConnectorRef);
+
+
+}
diff --git a/opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/flow/FlowWriterServiceImpl.java b/opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/flow/FlowWriterServiceImpl.java
new file mode 100644 (file)
index 0000000..f49771a
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * 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.sample.l2switch.md.flow;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService;
+import org.opendaylight.controller.sample.l2switch.md.util.InstanceIdentifierUtils;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.output.action._case.OutputActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActions;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetDestinationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetSourceBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.BigInteger;
+import java.util.List;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Implementation of FlowWriterService{@link org.opendaylight.controller.sample.l2switch.md.flow.FlowWriterService},
+ * that builds required flow and writes to configuration data store using provided DataBrokerService
+ * {@link org.opendaylight.controller.sal.binding.api.data.DataBrokerService}
+ */
+public class FlowWriterServiceImpl implements FlowWriterService {
+  private static final Logger _logger = LoggerFactory.getLogger(FlowWriterServiceImpl.class);
+  private final DataBrokerService dataBrokerService;
+  private final NetworkGraphService networkGraphService;
+  private AtomicLong flowIdInc = new AtomicLong();
+  private AtomicLong flowCookieInc = new AtomicLong(0x2a00000000000000L);
+
+
+  public FlowWriterServiceImpl(DataBrokerService dataBrokerService, NetworkGraphService networkGraphService) {
+    Preconditions.checkNotNull(dataBrokerService, "dataBrokerService should not be null.");
+    Preconditions.checkNotNull(networkGraphService, "networkGraphService should not be null.");
+    this.dataBrokerService = dataBrokerService;
+    this.networkGraphService = networkGraphService;
+  }
+
+  /**
+   * Writes a flow that forwards packets to destPort if destination mac in packet is destMac and
+   * source Mac in packet is sourceMac. If sourceMac is null then flow would not set any source mac,
+   * resulting in all packets with destMac being forwarded to destPort.
+   *
+   * @param sourceMac
+   * @param destMac
+   * @param destNodeConnectorRef
+   */
+  @Override
+  public void addMacToMacFlow(MacAddress sourceMac, MacAddress destMac, NodeConnectorRef destNodeConnectorRef) {
+
+    Preconditions.checkNotNull(destMac, "Destination mac address should not be null.");
+    Preconditions.checkNotNull(destNodeConnectorRef, "Destination port should not be null.");
+
+
+    // do not add flow if both macs are same.
+    if(sourceMac != null && destMac.equals(sourceMac)) {
+      _logger.info("In addMacToMacFlow: No flows added. Source and Destination mac are same.");
+      return;
+    }
+
+    // get flow table key
+    TableKey flowTableKey = new TableKey((short) 0); //TODO: Hard coded Table Id 0, need to get it from Configuration data.
+
+    //build a flow path based on node connector to program flow
+    InstanceIdentifier<Flow> flowPath = buildFlowPath(destNodeConnectorRef, flowTableKey);
+
+    // build a flow that target given mac id
+    Flow flowBody = createMacToMacFlow(flowTableKey.getId(), 0, sourceMac, destMac, destNodeConnectorRef);
+
+    // commit the flow in config data
+    writeFlowToConfigData(flowPath, flowBody);
+  }
+
+  /**
+   * Writes mac-to-mac flow on all ports that are in the path between given source and destination ports.
+   * It uses path provided by NetworkGraphService
+   * {@link org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService} to find a links
+   * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
+   * between given ports. And then writes appropriate flow on each port that is covered in that path.
+   *
+   * @param sourceMac
+   * @param sourceNodeConnectorRef
+   * @param destMac
+   * @param destNodeConnectorRef
+   */
+  @Override
+  public void addMacToMacFlowsUsingShortestPath(MacAddress sourceMac,
+                                                NodeConnectorRef sourceNodeConnectorRef,
+                                                MacAddress destMac,
+                                                NodeConnectorRef destNodeConnectorRef) {
+    Preconditions.checkNotNull(sourceMac, "Source mac address should not be null.");
+    Preconditions.checkNotNull(sourceNodeConnectorRef, "Source port should not be null.");
+    Preconditions.checkNotNull(destMac, "Destination mac address should not be null.");
+    Preconditions.checkNotNull(destNodeConnectorRef, "Destination port should not be null.");
+
+    if(sourceNodeConnectorRef.equals(destNodeConnectorRef)) {
+      _logger.info("In addMacToMacFlowsUsingShortestPath: No flows added. Source and Destination ports are same.");
+      return;
+
+    }
+    NodeId sourceNodeId = new NodeId(sourceNodeConnectorRef.getValue().firstKeyOf(Node.class, NodeKey.class).getId().getValue());
+    NodeId destNodeId = new NodeId(destNodeConnectorRef.getValue().firstKeyOf(Node.class, NodeKey.class).getId().getValue());
+
+    // add destMac-To-sourceMac flow on source port
+    addMacToMacFlow(destMac, sourceMac, sourceNodeConnectorRef);
+
+    // add sourceMac-To-destMac flow on destination port
+    addMacToMacFlow(sourceMac, destMac, destNodeConnectorRef);
+
+    if(!sourceNodeId.equals(destNodeId)) {
+      List<Link> linksInBeween = networkGraphService.getPath(sourceNodeId, destNodeId);
+
+      if(linksInBeween != null) {
+        // assumes the list order is maintained and starts with link that has source as source node
+        for(Link link : linksInBeween) {
+          // add sourceMac-To-destMac flow on source port
+          addMacToMacFlow(sourceMac, destMac, getSourceNodeConnectorRef(link));
+
+          // add destMac-To-sourceMac flow on destination port
+          addMacToMacFlow(destMac, sourceMac, getDestNodeConnectorRef(link));
+        }
+      }
+    }
+  }
+
+  private NodeConnectorRef getSourceNodeConnectorRef(Link link) {
+    InstanceIdentifier<NodeConnector> nodeConnectorInstanceIdentifier
+        = InstanceIdentifierUtils.createNodeConnectorIdentifier(
+        link.getSource().getSourceNode().getValue(),
+        link.getSource().getSourceTp().getValue());
+    return new NodeConnectorRef(nodeConnectorInstanceIdentifier);
+  }
+
+  private NodeConnectorRef getDestNodeConnectorRef(Link link) {
+    InstanceIdentifier<NodeConnector> nodeConnectorInstanceIdentifier
+        = InstanceIdentifierUtils.createNodeConnectorIdentifier(
+        link.getDestination().getDestNode().getValue(),
+        link.getDestination().getDestTp().getValue());
+
+    return new NodeConnectorRef(nodeConnectorInstanceIdentifier);
+  }
+
+  /**
+   * @param nodeConnectorRef
+   * @return
+   */
+  private InstanceIdentifier<Flow> buildFlowPath(NodeConnectorRef nodeConnectorRef, TableKey flowTableKey) {
+
+    // generate unique flow key
+    FlowId flowId = new FlowId(String.valueOf(flowIdInc.getAndIncrement()));
+    FlowKey flowKey = new FlowKey(flowId);
+
+    return InstanceIdentifierUtils.generateFlowInstanceIdentifier(nodeConnectorRef, flowTableKey, flowKey);
+  }
+
+  /**
+   * @param tableId
+   * @param priority
+   * @param sourceMac
+   * @param destMac
+   * @param destPort
+   * @return {@link org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder}
+   *         builds flow that forwards all packets with destMac to given port
+   */
+  private Flow createMacToMacFlow(Short tableId, int priority,
+                                  MacAddress sourceMac, MacAddress destMac, NodeConnectorRef destPort) {
+
+    // start building flow
+    FlowBuilder macToMacFlow = new FlowBuilder() //
+        .setTableId(tableId) //
+        .setFlowName("mac2mac");
+
+    // use its own hash code for id.
+    macToMacFlow.setId(new FlowId(Long.toString(macToMacFlow.hashCode())));
+
+    // create a match that has mac to mac ethernet match
+    EthernetMatchBuilder ethernetMatchBuilder = new EthernetMatchBuilder() //
+        .setEthernetDestination(new EthernetDestinationBuilder() //
+            .setAddress(destMac) //
+            .build());
+    // set source in the match only if present
+    if(sourceMac != null) {
+      ethernetMatchBuilder.setEthernetSource(new EthernetSourceBuilder()
+          .setAddress(sourceMac)
+          .build());
+    }
+    EthernetMatch ethernetMatch = ethernetMatchBuilder.build();
+    Match match = new MatchBuilder()
+        .setEthernetMatch(ethernetMatch)
+        .build();
+
+
+    Uri destPortUri = destPort.getValue().firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId();
+
+    Action outputToControllerAction = new ActionBuilder() //
+        .setAction(new OutputActionCaseBuilder() //
+            .setOutputAction(new OutputActionBuilder() //
+                .setMaxLength(new Integer(0xffff)) //
+                .setOutputNodeConnector(destPortUri) //
+                .build()) //
+            .build()) //
+        .build();
+
+    // Create an Apply Action
+    ApplyActions applyActions = new ApplyActionsBuilder().setAction(ImmutableList.of(outputToControllerAction))
+        .build();
+
+    // Wrap our Apply Action in an Instruction
+    Instruction applyActionsInstruction = new InstructionBuilder() //
+        .setInstruction(new ApplyActionsCaseBuilder()//
+            .setApplyActions(applyActions) //
+            .build()) //
+        .build();
+
+    // Put our Instruction in a list of Instructions
+    macToMacFlow
+        .setMatch(match) //
+        .setInstructions(new InstructionsBuilder() //
+            .setInstruction(ImmutableList.of(applyActionsInstruction)) //
+            .build()) //
+        .setPriority(priority) //
+        .setBufferId(0L) //
+        .setHardTimeout(0) //
+        .setIdleTimeout(0) //
+        .setCookie(BigInteger.valueOf(flowCookieInc.getAndIncrement()))
+        .setFlags(new FlowModFlags(false, false, false, false, false));
+
+    return macToMacFlow.build();
+  }
+
+  /**
+   * Starts and commits data change transaction which
+   * modifies provided flow path with supplied body.
+   *
+   * @param flowPath
+   * @param flowBody
+   * @return transaction commit
+   */
+  private Future<RpcResult<TransactionStatus>> writeFlowToConfigData(InstanceIdentifier<Flow> flowPath,
+                                                                     Flow flowBody) {
+    DataModificationTransaction addFlowTransaction = dataBrokerService.beginTransaction();
+    addFlowTransaction.putConfigurationData(flowPath, flowBody);
+    return addFlowTransaction.commit();
+  }
+}
diff --git a/opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/inventory/InventoryService.java b/opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/inventory/InventoryService.java
new file mode 100644 (file)
index 0000000..a12f394
--- /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.sample.l2switch.md.inventory;
+
+import org.opendaylight.controller.sample.l2switch.md.util.InstanceIdentifierUtils;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.*;
+
+/**
+ * InventoryService provides functions related to Nodes & NodeConnectors.
+ */
+public class InventoryService {
+  private DataBrokerService dataService;
+  // Key: SwitchId, Value: NodeConnectorRef that corresponds to NC between controller & switch
+  private HashMap<String, NodeConnectorRef> controllerSwitchConnectors;
+
+  /**
+   * Construct an InventoryService object with the specified inputs.
+   * @param dataService  The DataBrokerService associated with the InventoryService.
+   */
+  public InventoryService(DataBrokerService dataService) {
+    this.dataService = dataService;
+    controllerSwitchConnectors = new HashMap<String, NodeConnectorRef>();
+  }
+
+  public HashMap<String, NodeConnectorRef> getControllerSwitchConnectors() {
+    return controllerSwitchConnectors;
+  }
+
+  // ToDo: Improve performance for thousands of switch ports
+  /**
+   * Get the External NodeConnectors of the network, which are the NodeConnectors connected to hosts.
+   * @return  The list of external node connectors.
+   */
+  public List<NodeConnectorRef> getExternalNodeConnectors() {
+    // External NodeConnectors = All - Internal
+    ArrayList<NodeConnectorRef> externalNodeConnectors = new ArrayList<NodeConnectorRef>();
+    Set<String> internalNodeConnectors = new HashSet<>();
+
+    // Read Topology -- find list of switch-to-switch internal node connectors
+    NetworkTopology networkTopology =
+        (NetworkTopology)dataService.readOperationalData(
+            InstanceIdentifier.<NetworkTopology>builder(NetworkTopology.class).toInstance());
+
+    for (Topology topology : networkTopology.getTopology()) {
+      Topology completeTopology =
+          (Topology)dataService.readOperationalData(
+              InstanceIdentifierUtils.generateTopologyInstanceIdentifier(
+                  topology.getTopologyId().getValue()));
+
+      for (Link link : completeTopology.getLink()) {
+        internalNodeConnectors.add(link.getDestination().getDestTp().getValue());
+        internalNodeConnectors.add(link.getSource().getSourceTp().getValue());
+      }
+    }
+
+    // Read Inventory -- contains list of all nodeConnectors
+    InstanceIdentifier.InstanceIdentifierBuilder<Nodes> nodesInsIdBuilder = InstanceIdentifier.<Nodes>builder(Nodes.class);
+    Nodes nodes = (Nodes)dataService.readOperationalData(nodesInsIdBuilder.toInstance());
+    if (nodes != null) {
+      for (Node node : nodes.getNode()) {
+        Node completeNode = (Node)dataService.readOperationalData(InstanceIdentifierUtils.createNodePath(node.getId()));
+        for (NodeConnector nodeConnector : completeNode.getNodeConnector()) {
+          // NodeConnector isn't switch-to-switch, so it must be controller-to-switch (internal) or external
+          if (!internalNodeConnectors.contains(nodeConnector.getId().getValue())) {
+            NodeConnectorRef ncRef = new NodeConnectorRef(
+                    InstanceIdentifier.<Nodes>builder(Nodes.class).<Node, NodeKey>child(Node.class, node.getKey())
+                            .<NodeConnector, NodeConnectorKey>child(NodeConnector.class, nodeConnector.getKey()).toInstance());
+
+            // External node connectors have "-" in their name for mininet, i.e. "s1-eth1"
+            if (nodeConnector.getAugmentation(FlowCapableNodeConnector.class).getName().contains("-")) {
+              externalNodeConnectors.add(ncRef);
+            }
+            // Controller-to-switch internal node connectors
+            else {
+              controllerSwitchConnectors.put(node.getId().getValue(), ncRef);
+            }
+          }
+        }
+      }
+    }
+
+    return externalNodeConnectors;
+  }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/packet/PacketHandler.java b/opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/packet/PacketHandler.java
new file mode 100644 (file)
index 0000000..753de4a
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * 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.sample.l2switch.md.packet;
+
+import org.opendaylight.controller.sample.l2switch.md.addresstracker.AddressTracker;
+import org.opendaylight.controller.sample.l2switch.md.flow.FlowWriterService;
+import org.opendaylight.controller.sample.l2switch.md.inventory.InventoryService;
+import org.opendaylight.controller.sample.l2switch.md.util.InstanceIdentifierUtils;
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.LLDP;
+import org.opendaylight.controller.sal.packet.LinkEncap;
+import org.opendaylight.controller.sal.packet.Packet;
+import org.opendaylight.controller.sal.packet.RawPacket;
+import org.opendaylight.controller.sal.utils.HexEncode;
+import org.opendaylight.controller.sal.utils.NetUtils;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.*;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.address.tracker.rev140402.l2.addresses.L2Address;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * PacketHandler examines Ethernet packets to find L2Addresses (mac, nodeConnector) pairings
+ * of the sender and learns them.
+ * It also forwards the data packets appropriately dependending upon whether it knows about the
+ * target or not.
+ */
+public class PacketHandler implements PacketProcessingListener {
+
+  private final static Logger _logger = LoggerFactory.getLogger(PacketHandler.class);
+
+  private PacketProcessingService packetProcessingService;
+  private AddressTracker addressTracker;
+  private FlowWriterService flowWriterService;
+  private InventoryService inventoryService;
+
+  public void setAddressTracker(AddressTracker addressTracker) {
+    this.addressTracker = addressTracker;
+  }
+
+  public void setPacketProcessingService(PacketProcessingService packetProcessingService) {
+    this.packetProcessingService = packetProcessingService;
+  }
+
+  public void setFlowWriterService(FlowWriterService flowWriterService) {
+    this.flowWriterService = flowWriterService;
+  }
+
+  public void setInventoryService(InventoryService inventoryService) {
+    this.inventoryService = inventoryService;
+  }
+
+  /**
+   * The handler function for all incoming packets.
+   * @param packetReceived  The incoming packet.
+   */
+  @Override
+  public void onPacketReceived(PacketReceived packetReceived) {
+
+    if(packetReceived == null) return;
+
+    try {
+      byte[] payload = packetReceived.getPayload();
+      RawPacket rawPacket = new RawPacket(payload);
+      NodeConnectorRef ingress = packetReceived.getIngress();
+
+      Packet packet = decodeDataPacket(rawPacket);
+
+      if(!(packet instanceof Ethernet)) return;
+
+      handleEthernetPacket(packet, ingress);
+
+    } catch(Throwable _e) {
+      _e.printStackTrace();
+    }
+  }
+
+  /**
+   * The handler function for Ethernet packets.
+   * @param packet  The incoming Ethernet packet.
+   * @param ingress  The NodeConnector where the Ethernet packet came from.
+   */
+  private void handleEthernetPacket(Packet packet, NodeConnectorRef ingress) {
+    byte[] srcMac = ((Ethernet) packet).getSourceMACAddress();
+    byte[] destMac = ((Ethernet) packet).getDestinationMACAddress();
+
+    if (srcMac  == null || srcMac.length  == 0) return;
+
+    Object enclosedPacket = packet.getPayload();
+
+    if (enclosedPacket instanceof LLDP)
+      return; // LLDP packets are handled by OpenFlowPlugin
+
+    // get l2address by src mac
+    // if unknown, add l2address
+    MacAddress srcMacAddress = toMacAddress(srcMac);
+    L2Address src = addressTracker.getAddress(srcMacAddress);
+    boolean isSrcKnown = (src != null);
+    if (!isSrcKnown) {
+      addressTracker.addAddress(srcMacAddress, ingress);
+    }
+
+    // get host by dest mac
+    // if known set dest known to true
+    MacAddress destMacAddress = toMacAddress(destMac);
+    L2Address dest = addressTracker.getAddress(destMacAddress);
+    boolean isDestKnown = (dest != null);
+
+    byte[] payload = packet.getRawPayload();
+    // if (src and dest known)
+    // sendpacket to dest and add src<->dest flow
+    if(isSrcKnown & isDestKnown) {
+      flowWriterService.addMacToMacFlowsUsingShortestPath(srcMacAddress, src.getNodeConnectorRef(),
+          destMacAddress, dest.getNodeConnectorRef());
+      sendPacketOut(payload, getControllerNodeConnector(dest.getNodeConnectorRef()), dest.getNodeConnectorRef());
+    } else {
+      // if (dest unknown)
+      // sendpacket to external links minus ingress
+      floodExternalPorts(payload, ingress);
+    }
+  }
+
+  /**
+   * Floods the specified payload on external ports, which are ports not connected to switches.
+   * @param payload  The payload to be flooded.
+   * @param ingress  The NodeConnector where the payload came from.
+   */
+  private void floodExternalPorts(byte[] payload, NodeConnectorRef ingress) {
+    List<NodeConnectorRef> externalPorts = inventoryService.getExternalNodeConnectors();
+    externalPorts.remove(ingress);
+
+    for (NodeConnectorRef egress : externalPorts) {
+      sendPacketOut(payload, getControllerNodeConnector(egress), egress);
+    }
+  }
+
+  /**
+   * Sends the specified packet on the specified port.
+   * @param payload  The payload to be sent.
+   * @param ingress  The NodeConnector where the payload came from.
+   * @param egress  The NodeConnector where the payload will go.
+   */
+  private void sendPacketOut(byte[] payload, NodeConnectorRef ingress, NodeConnectorRef egress) {
+    if (ingress == null || egress == null)  return;
+    InstanceIdentifier<Node> egressNodePath = InstanceIdentifierUtils.getNodePath(egress.getValue());
+    TransmitPacketInput input = new TransmitPacketInputBuilder() //
+        .setPayload(payload) //
+        .setNode(new NodeRef(egressNodePath)) //
+        .setEgress(egress) //
+        .setIngress(ingress) //
+        .build();
+    packetProcessingService.transmitPacket(input);
+  }
+
+  /**
+   * Decodes an incoming packet.
+   * @param raw  The raw packet to be decoded.
+   * @return  The decoded form of the raw packet.
+   */
+  private Packet decodeDataPacket(RawPacket raw) {
+    if(raw == null) {
+      return null;
+    }
+    byte[] data = raw.getPacketData();
+    if(data.length <= 0) {
+      return null;
+    }
+    if(raw.getEncap().equals(LinkEncap.ETHERNET)) {
+      Ethernet res = new Ethernet();
+      try {
+        res.deserialize(data, 0, data.length * NetUtils.NumBitsInAByte);
+        res.setRawPayload(raw.getPacketData());
+      } catch(Exception e) {
+        _logger.warn("Failed to decode packet: {}", e.getMessage());
+      }
+      return res;
+    }
+    return null;
+  }
+
+  /**
+   * Creates a MacAddress object out of a byte array.
+   * @param dataLinkAddress  The byte-array form of a MacAddress
+   * @return  MacAddress of the specified dataLinkAddress.
+   */
+  private MacAddress toMacAddress(byte[] dataLinkAddress) {
+    return new MacAddress(HexEncode.bytesToHexStringFormat(dataLinkAddress));
+  }
+
+  /**
+   * Gets the NodeConnector that connects the controller & switch for a specified switch port/node connector.
+   * @param nodeConnectorRef  The nodeConnector of a switch.
+   * @return  The NodeConnector that that connects the controller & switch.
+   */
+  private NodeConnectorRef getControllerNodeConnector(NodeConnectorRef nodeConnectorRef) {
+    NodeConnectorRef controllerSwitchNodeConnector = null;
+    HashMap<String, NodeConnectorRef> controllerSwitchConnectors = inventoryService.getControllerSwitchConnectors();
+    InstanceIdentifier<Node> nodePath = InstanceIdentifierUtils.getNodePath(nodeConnectorRef.getValue());
+    if (nodePath != null) {
+      NodeKey nodeKey = InstanceIdentifierUtils.getNodeKey(nodePath);
+      if (nodeKey != null) {
+        controllerSwitchNodeConnector = controllerSwitchConnectors.get(nodeKey.getId().getValue());
+      }
+    }
+    return controllerSwitchNodeConnector;
+  }
+}
diff --git a/opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/topology/NetworkGraphDijkstra.java b/opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/topology/NetworkGraphDijkstra.java
new file mode 100644 (file)
index 0000000..a90ac5a
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * 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.sample.l2switch.md.topology;
+
+import com.google.common.base.Preconditions;
+import edu.uci.ics.jung.algorithms.shortestpath.DijkstraShortestPath;
+import edu.uci.ics.jung.graph.DirectedSparseGraph;
+import edu.uci.ics.jung.graph.Graph;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+/**
+ * Implementation of NetworkGraphService{@link org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService}.
+ * It uses Jung graph library internally to maintain a graph and optimum way to return shortest path using
+ * Dijkstra algorithm.
+ */
+public class NetworkGraphDijkstra implements NetworkGraphService {
+
+  private static final Logger _logger = LoggerFactory.getLogger(NetworkGraphDijkstra.class);
+
+  DijkstraShortestPath<NodeId, Link> shortestPath = null;
+  Graph<NodeId, Link> networkGraph = null;
+
+  /**
+   * Adds links to existing graph or creates new directed graph with given links if graph was not initialized.
+   * @param links
+   */
+  @Override
+  public synchronized void addLinks(List<Link> links) {
+    if(links == null || links.isEmpty()) {
+      _logger.info("In addLinks: No link added as links is null or empty.");
+      return;
+    }
+
+    if(networkGraph == null) {
+      networkGraph = new DirectedSparseGraph<>();
+    }
+
+    for(Link link : links) {
+      NodeId sourceNodeId = link.getSource().getSourceNode();
+      NodeId destinationNodeId = link.getDestination().getDestNode();
+      networkGraph.addVertex(sourceNodeId);
+      networkGraph.addVertex(destinationNodeId);
+      networkGraph.addEdge(link, sourceNodeId, destinationNodeId);
+    }
+    if(shortestPath == null) {
+      shortestPath = new DijkstraShortestPath<>(networkGraph);
+    } else {
+      shortestPath.reset();
+    }
+  }
+
+  /**
+   * removes links from existing graph.
+   * @param links
+   */
+  @Override
+  public synchronized void removeLinks(List<Link> links) {
+    Preconditions.checkNotNull(networkGraph, "Graph is not initialized, add links first.");
+
+    if(links == null || links.isEmpty()) {
+      _logger.info("In removeLinks: No link removed as links is null or empty.");
+      return;
+    }
+
+    for(Link link : links) {
+      networkGraph.removeEdge(link);
+    }
+
+    if(shortestPath == null) {
+      shortestPath = new DijkstraShortestPath<>(networkGraph);
+    } else {
+      shortestPath.reset();
+    }
+  }
+
+  /**
+   * returns a path between 2 nodes. Uses Dijkstra's algorithm to return shortest path.
+   * @param sourceNodeId
+   * @param destinationNodeId
+   * @return
+   */
+  @Override
+  public synchronized List<Link> getPath(NodeId sourceNodeId, NodeId destinationNodeId) {
+    Preconditions.checkNotNull(shortestPath, "Graph is not initialized, add links first.");
+
+    if(sourceNodeId == null || destinationNodeId == null) {
+      _logger.info("In getPath: returning null, as sourceNodeId or destinationNodeId is null.");
+      return null;
+    }
+
+    return shortestPath.getPath(sourceNodeId, destinationNodeId);
+  }
+
+  /**
+   * Clears the prebuilt graph, in case same service instance is required to process a new graph.
+   */
+  @Override
+  public synchronized void clear() {
+    networkGraph = null;
+    shortestPath = null;
+  }
+}
diff --git a/opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/topology/NetworkGraphService.java b/opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/topology/NetworkGraphService.java
new file mode 100644 (file)
index 0000000..173be34
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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.sample.l2switch.md.topology;
+
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+
+import java.util.List;
+
+/**
+ * Service that allows to build a network graph using Topology links
+ * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
+ * and exposes operation that can be performed on such graph.
+ */
+public interface NetworkGraphService {
+
+  /**
+   * Adds links to existing graph or creates new graph with given links if graph was not initialized.
+   * @param links
+   */
+  public void addLinks(List<Link> links);
+
+  /**
+   * removes links from existing graph.
+   * @param links
+   */
+  public void removeLinks(List<Link> links);
+
+  /**
+   * returns a path between 2 nodes. Implementation should ideally return shortest path.
+   * @param sourceNodeId
+   * @param destinationNodeId
+   * @return
+   */
+  public List<Link> getPath(NodeId sourceNodeId, NodeId destinationNodeId);
+
+  /**
+   * Clears the prebuilt graph, in case same service instance is required to process a new graph.
+   */
+  public void clear();
+}
diff --git a/opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/topology/TopologyLinkDataChangeHandler.java b/opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/topology/TopologyLinkDataChangeHandler.java
new file mode 100644 (file)
index 0000000..254ebf8
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * 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.sample.l2switch.md.topology;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.sample.l2switch.md.util.InstanceIdentifierUtils;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Listens to data change events on topology links
+ * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
+ * and maintains a topology graph using provided NetworkGraphService
+ * {@link org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService}.
+ * It refreshes the graph after a delay(default 10 sec) to accommodate burst of change events if they come in bulk.
+ * This is to avoid continuous refresh of graph on a series of change events in short time.
+ */
+public class TopologyLinkDataChangeHandler implements DataChangeListener {
+  private static final Logger _logger = LoggerFactory.getLogger(TopologyLinkDataChangeHandler.class);
+  private static final String DEFAULT_TOPOLOGY_ID = "flow:1";
+
+  private boolean networkGraphRefreshScheduled = false;
+  private final ScheduledExecutorService networkGraphRefreshScheduler = Executors.newScheduledThreadPool(1);
+  private final long DEFAULT_GRAPH_REFRESH_DELAY = 10;
+  private final long graphRefreshDelayInSec;
+
+  private final NetworkGraphService networkGraphService;
+  private final DataBrokerService dataBrokerService;
+
+  /**
+   * Uses default delay to refresh topology graph if this constructor is used.
+   * @param dataBrokerService
+   * @param networkGraphService
+   */
+  public TopologyLinkDataChangeHandler(DataBrokerService dataBrokerService, NetworkGraphService networkGraphService) {
+    Preconditions.checkNotNull(dataBrokerService, "dataBrokerService should not be null.");
+    Preconditions.checkNotNull(networkGraphService, "networkGraphService should not be null.");
+    this.dataBrokerService = dataBrokerService;
+    this.networkGraphService = networkGraphService;
+    this.graphRefreshDelayInSec = DEFAULT_GRAPH_REFRESH_DELAY;
+  }
+
+  /**
+   *
+   * @param dataBrokerService
+   * @param networkGraphService
+   * @param graphRefreshDelayInSec
+   */
+  public TopologyLinkDataChangeHandler(DataBrokerService dataBrokerService, NetworkGraphService networkGraphService,
+                                       long graphRefreshDelayInSec) {
+    Preconditions.checkNotNull(dataBrokerService, "dataBrokerService should not be null.");
+    Preconditions.checkNotNull(networkGraphService, "networkGraphService should not be null.");
+    this.dataBrokerService = dataBrokerService;
+    this.networkGraphService = networkGraphService;
+    this.graphRefreshDelayInSec = graphRefreshDelayInSec;
+  }
+
+  /**
+   * Based on if links have been added or removed in topology data store, schedules a refresh of network graph.
+   * @param dataChangeEvent
+   */
+  @Override
+  public void onDataChanged(DataChangeEvent<InstanceIdentifier<?>, DataObject> dataChangeEvent) {
+    if(dataChangeEvent == null) {
+      _logger.info("In onDataChanged: No Processing done as dataChangeEvent is null.");
+    }
+    Map<InstanceIdentifier<?>, DataObject> linkOriginalData = dataChangeEvent.getOriginalOperationalData();
+    Map<InstanceIdentifier<?>, DataObject> linkUpdatedData = dataChangeEvent.getUpdatedOperationalData();
+    // change this logic, once MD-SAL start populating DeletedOperationData Set
+    if(linkOriginalData != null && linkUpdatedData != null
+        && (linkOriginalData.size() != 0 || linkUpdatedData.size() != 0)
+        && !networkGraphRefreshScheduled) {
+      networkGraphRefreshScheduled = linkOriginalData.size() != linkUpdatedData.size();
+      if(networkGraphRefreshScheduled) {
+        networkGraphRefreshScheduler.schedule(new NetworkGraphRefresher(), graphRefreshDelayInSec, TimeUnit.SECONDS);
+      }
+    }
+
+  }
+
+  /**
+   * Registers as a data listener to receive changes done to
+   * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
+   * under {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology}
+   * operation data root.
+   */
+
+  public void registerAsDataChangeListener() {
+    InstanceIdentifier<Link> linkInstance = InstanceIdentifier.builder(NetworkTopology.class)
+        .child(Topology.class, new TopologyKey(new TopologyId(DEFAULT_TOPOLOGY_ID))).child(Link.class).toInstance();
+    dataBrokerService.registerDataChangeListener(linkInstance, this);
+  }
+
+  /**
+   *
+   */
+  private class NetworkGraphRefresher implements Runnable {
+    /**
+     *
+     */
+    @Override
+    public void run() {
+      networkGraphRefreshScheduled = false;
+      //TODO: it should refer to changed links only from DataChangeEvent above.
+      List<Link> links = getLinksFromTopology(DEFAULT_TOPOLOGY_ID);
+      networkGraphService.clear();// can remove this once changed links are addressed
+      if(links != null && !links.isEmpty()) {
+        networkGraphService.addLinks(links);
+      }
+    }
+
+    /**
+     * @param topologyId
+     * @return
+     */
+    private List<Link> getLinksFromTopology(String topologyId) {
+      InstanceIdentifier<Topology> topologyInstanceIdentifier = InstanceIdentifierUtils.generateTopologyInstanceIdentifier(topologyId);
+      Topology topology = (Topology) dataBrokerService.readOperationalData(topologyInstanceIdentifier);
+      return topology.getLink();
+    }
+  }
+}
diff --git a/opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/util/InstanceIdentifierUtils.java b/opendaylight/md-sal/samples/l2switch/implementation/src/main/java/org/opendaylight/controller/sample/l2switch/md/util/InstanceIdentifierUtils.java
new file mode 100644 (file)
index 0000000..ea08f94
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * 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.sample.l2switch.md.util;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/* InstanceIdentifierUtils provides utility functions related to InstanceIdentifiers.
+ */
+public class InstanceIdentifierUtils {
+
+  /**
+   * Creates an Instance Identifier (path) for node with specified id
+   *
+   * @param nodeId
+   * @return
+   */
+  public static final InstanceIdentifier<Node> createNodePath(NodeId nodeId) {
+    return InstanceIdentifier.builder(Nodes.class) //
+        .child(Node.class, new NodeKey(nodeId)) //
+        .build();
+  }
+
+  /**
+   * Shorten's node child path to node path.
+   *
+   * @param nodeChild child of node, from which we want node path.
+   * @return
+   */
+  public static final InstanceIdentifier<Node> getNodePath(InstanceIdentifier<?> nodeChild) {
+    return nodeChild.firstIdentifierOf(Node.class);
+  }
+
+
+  /**
+   * Creates a table path by appending table specific location to node path
+   *
+   * @param nodePath
+   * @param tableKey
+   * @return
+   */
+  public static final InstanceIdentifier<Table> createTablePath(InstanceIdentifier<Node> nodePath, TableKey tableKey) {
+    return InstanceIdentifier.builder(nodePath)
+        .augmentation(FlowCapableNode.class)
+        .child(Table.class, tableKey)
+        .build();
+  }
+
+  /**
+   * Creates a path for particular flow, by appending flow-specific information
+   * to table path.
+   *
+   * @param table
+   * @param flowKey
+   * @return
+   */
+  public static InstanceIdentifier<Flow> createFlowPath(InstanceIdentifier<Table> table, FlowKey flowKey) {
+    return InstanceIdentifier.builder(table)
+        .child(Flow.class, flowKey)
+        .build();
+  }
+
+  /**
+   * Extract table id from table path.
+   *
+   * @param tablePath
+   * @return
+   */
+  public static Short getTableId(InstanceIdentifier<Table> tablePath) {
+    return tablePath.firstKeyOf(Table.class, TableKey.class).getId();
+  }
+
+  /**
+   * Extracts NodeConnectorKey from node connector path.
+   */
+  public static NodeConnectorKey getNodeConnectorKey(InstanceIdentifier<?> nodeConnectorPath) {
+    return nodeConnectorPath.firstKeyOf(NodeConnector.class, NodeConnectorKey.class);
+  }
+
+  /**
+   * Extracts NodeKey from node path.
+   */
+  public static NodeKey getNodeKey(InstanceIdentifier<?> nodePath) {
+    return nodePath.firstKeyOf(Node.class, NodeKey.class);
+  }
+
+
+  //
+  public static final InstanceIdentifier<NodeConnector> createNodeConnectorIdentifier(String nodeIdValue,
+                                                                                      String nodeConnectorIdValue) {
+    return InstanceIdentifier.builder(createNodePath(new NodeId(nodeIdValue))) //
+        .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId(nodeConnectorIdValue))) //
+        .build();
+  }
+
+  /**
+   * @param nodeConnectorRef
+   * @return
+   */
+  public static InstanceIdentifier<Node> generateNodeInstanceIdentifier(NodeConnectorRef nodeConnectorRef) {
+    return nodeConnectorRef.getValue().firstIdentifierOf(Node.class);
+  }
+
+  /**
+   * @param nodeConnectorRef
+   * @param flowTableKey
+   * @return
+   */
+  public static InstanceIdentifier<Table> generateFlowTableInstanceIdentifier(NodeConnectorRef nodeConnectorRef, TableKey flowTableKey) {
+    return InstanceIdentifier.builder(generateNodeInstanceIdentifier(nodeConnectorRef))
+        .augmentation(FlowCapableNode.class)
+        .child(Table.class, flowTableKey)
+        .build();
+  }
+
+  /**
+   * @param nodeConnectorRef
+   * @param flowTableKey
+   * @param flowKey
+   * @return
+   */
+  public static InstanceIdentifier<Flow> generateFlowInstanceIdentifier(NodeConnectorRef nodeConnectorRef,
+                                                                        TableKey flowTableKey,
+                                                                        FlowKey flowKey) {
+    return InstanceIdentifier.builder(generateFlowTableInstanceIdentifier(nodeConnectorRef, flowTableKey))
+        .child(Flow.class, flowKey)
+        .build();
+  }
+
+  public static InstanceIdentifier<Topology> generateTopologyInstanceIdentifier(String topologyId) {
+    return InstanceIdentifier.builder(NetworkTopology.class)
+        .child(Topology.class, new TopologyKey(new TopologyId(topologyId)))
+        .build();
+  }
+}
+
diff --git a/opendaylight/md-sal/samples/l2switch/implementation/src/test/java/org/opendaylight/controller/sample/l2switch/md/flow/FlowWriterServiceImplTest.java b/opendaylight/md-sal/samples/l2switch/implementation/src/test/java/org/opendaylight/controller/sample/l2switch/md/flow/FlowWriterServiceImplTest.java
new file mode 100644 (file)
index 0000000..3520d81
--- /dev/null
@@ -0,0 +1,150 @@
+/**
+ * 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.sample.l2switch.md.flow;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ */
+public class FlowWriterServiceImplTest {
+  private DataBrokerService dataBrokerService;
+  private NodeConnectorRef srcNodeConnectorRef;
+  private NodeConnectorRef destNodeConnectorRef;
+  private MacAddress destMacAddress;
+  private MacAddress srcMacAddress;
+  private DataModificationTransaction dataModificationTransaction;
+  private NetworkGraphService networkGraphService;
+
+  @Before
+  public void init() {
+    dataBrokerService = mock(DataBrokerService.class);
+    networkGraphService = mock(NetworkGraphService.class);
+    //build source node connector ref
+    InstanceIdentifier<Nodes> srcNodesInstanceIdentifier
+        = InstanceIdentifier.builder(Nodes.class)
+        .build();
+    InstanceIdentifier<Node> srcNodeInstanceIdentifier
+        = InstanceIdentifier.builder(srcNodesInstanceIdentifier)
+        .child(Node.class, new NodeKey(new NodeId("openflow:1")))
+        .build();
+    InstanceIdentifier<NodeConnector> srcNodeConnectorInstanceIdentifier
+        = InstanceIdentifier.builder(srcNodeInstanceIdentifier)
+        .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId("openflow:1:2")))
+        .build();
+    srcNodeConnectorRef = new NodeConnectorRef(srcNodeConnectorInstanceIdentifier);
+
+    //build dest node connector ref
+    InstanceIdentifier<Nodes> nodesInstanceIdentifier
+        = InstanceIdentifier.builder(Nodes.class)
+        .build();
+    InstanceIdentifier<Node> nodeInstanceIdentifier
+        = InstanceIdentifier.builder(nodesInstanceIdentifier)
+        .child(Node.class, new NodeKey(new NodeId("openflow:2")))
+        .build();
+    InstanceIdentifier<NodeConnector> nodeConnectorInstanceIdentifier
+        = InstanceIdentifier.builder(nodeInstanceIdentifier)
+        .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId("openflow:2:2")))
+        .build();
+    destNodeConnectorRef = new NodeConnectorRef(nodeConnectorInstanceIdentifier);
+    destMacAddress = new MacAddress("00:0a:95:9d:68:16");
+    srcMacAddress = new MacAddress("00:0a:95:8c:97:24");
+    dataModificationTransaction = mock(DataModificationTransaction.class);
+    when(dataBrokerService.beginTransaction()).thenReturn(dataModificationTransaction);
+  }
+
+  @Test
+  public void testFlowWriterServiceImpl_NPEWhenDataBrokerServiceIsNull() throws Exception {
+    try {
+      new FlowWriterServiceImpl(null, networkGraphService);
+      fail("Expected null pointer exception.");
+    } catch(NullPointerException npe) {
+      assertEquals("dataBrokerService should not be null.", npe.getMessage());
+    }
+  }
+
+  @Test
+  public void testAddMacToMacFlow_NPEWhenNullSourceMacDestMacAndNodeConnectorRef() throws Exception {
+    FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+    try {
+      flowWriterService.addMacToMacFlow(null, null, null);
+      fail("Expected null pointer exception.");
+    } catch(NullPointerException npe) {
+      assertEquals("Destination mac address should not be null.", npe.getMessage());
+    }
+  }
+
+  @Test
+  public void testAddMacToMacFlow_NPEWhenSourceMacNullMac() throws Exception {
+    FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+    try {
+      flowWriterService.addMacToMacFlow(null, null, destNodeConnectorRef);
+      fail("Expected null pointer exception.");
+    } catch(NullPointerException npe) {
+      assertEquals("Destination mac address should not be null.", npe.getMessage());
+    }
+  }
+
+  @Test
+  public void testAddMacToMacFlow_NPEWhenNullSourceMacNodeConnectorRef() throws Exception {
+    FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+    try {
+      flowWriterService.addMacToMacFlow(null, destMacAddress, null);
+      fail("Expected null pointer exception.");
+    } catch(NullPointerException npe) {
+      assertEquals("Destination port should not be null.", npe.getMessage());
+    }
+  }
+
+  @Test
+  public void testAddMacToMacFlow_WhenNullSourceMac() throws Exception {
+    FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+    flowWriterService.addMacToMacFlow(null, destMacAddress, destNodeConnectorRef);
+    verify(dataBrokerService, times(1)).beginTransaction();
+    verify(dataModificationTransaction, times(1)).commit();
+  }
+
+  @Test
+  public void testAddMacToMacFlow_WhenSrcAndDestMacAreSame() throws Exception {
+    FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+    flowWriterService.addMacToMacFlow(new MacAddress(destMacAddress.getValue()), destMacAddress, destNodeConnectorRef);
+    verify(dataBrokerService, never()).beginTransaction();
+    verify(dataModificationTransaction, never()).commit();
+
+  }
+
+  @Test
+  public void testAddMacToMacFlow_SunnyDay() throws Exception {
+    FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+    flowWriterService.addMacToMacFlow(srcMacAddress, destMacAddress, destNodeConnectorRef);
+    verify(dataBrokerService, times(1)).beginTransaction();
+    verify(dataModificationTransaction, times(1)).commit();
+  }
+
+}
diff --git a/opendaylight/md-sal/samples/l2switch/implementation/src/test/java/org/opendaylight/controller/sample/l2switch/md/topology/NetworkGraphDijkstraTest.java b/opendaylight/md-sal/samples/l2switch/implementation/src/test/java/org/opendaylight/controller/sample/l2switch/md/topology/NetworkGraphDijkstraTest.java
new file mode 100644 (file)
index 0000000..3669a5c
--- /dev/null
@@ -0,0 +1,158 @@
+/**
+ * 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.sample.l2switch.md.topology;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.Destination;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.Source;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static junit.framework.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ */
+public class NetworkGraphDijkstraTest {
+  Link link1, link2, link3, link4, link5, link6, link7, link8, link9, link10,link11,link12;
+  Destination dest1, dest2, dest3, dest4, dest5, dest6,dest7,dest8,dest9,dest10,dest11,dest12;
+  Source src1, src2, src3, src4, src5, src6,src7,src8,src9,src10,src11,src12;
+  NodeId nodeId1 = new NodeId("openflow:1");
+  NodeId nodeId2 = new NodeId("openflow:2");
+  NodeId nodeId3 = new NodeId("openflow:3");
+  NodeId nodeId4 = new NodeId("openflow:4");
+  NodeId nodeId5 = new NodeId("openflow:5");
+  NodeId nodeId6 = new NodeId("openflow:6");
+  NodeId nodeId7 = new NodeId("openflow:7");
+  List<Link> links = new ArrayList<>();
+
+  @Before
+  public void init() {
+    link1 = mock(Link.class);
+    link2 = mock(Link.class);
+    link3 = mock(Link.class);
+    link4 = mock(Link.class);
+    link5 = mock(Link.class);
+    link6 = mock(Link.class);
+    link7 = mock(Link.class);
+    link8 = mock(Link.class);
+    link9 = mock(Link.class);
+    link10 = mock(Link.class);
+    link11 = mock(Link.class);
+    link12 = mock(Link.class);
+    dest1 = mock(Destination.class);
+    dest2 = mock(Destination.class);
+    dest3 = mock(Destination.class);
+    dest4 = mock(Destination.class);
+    dest5 = mock(Destination.class);
+    dest6 = mock(Destination.class);
+    dest7 = mock(Destination.class);
+    dest8 = mock(Destination.class);
+    dest9 = mock(Destination.class);
+    dest10 = mock(Destination.class);
+    dest11 = mock(Destination.class);
+    dest12 = mock(Destination.class);
+    src1 = mock(Source.class);
+    src2 = mock(Source.class);
+    src3 = mock(Source.class);
+    src4 = mock(Source.class);
+    src5 = mock(Source.class);
+    src6 = mock(Source.class);
+    src7 = mock(Source.class);
+    src8 = mock(Source.class);
+    src9 = mock(Source.class);
+    src10 = mock(Source.class);
+    src11 = mock(Source.class);
+    src12 = mock(Source.class);
+    when(link1.getSource()).thenReturn(src1);
+    when(link2.getSource()).thenReturn(src2);
+    when(link3.getSource()).thenReturn(src3);
+    when(link4.getSource()).thenReturn(src4);
+    when(link5.getSource()).thenReturn(src5);
+    when(link6.getSource()).thenReturn(src6);
+    when(link7.getSource()).thenReturn(src7);
+    when(link8.getSource()).thenReturn(src8);
+    when(link9.getSource()).thenReturn(src9);
+    when(link10.getSource()).thenReturn(src10);
+    when(link11.getSource()).thenReturn(src11);
+    when(link12.getSource()).thenReturn(src12);
+    when(link1.getDestination()).thenReturn(dest1);
+    when(link2.getDestination()).thenReturn(dest2);
+    when(link3.getDestination()).thenReturn(dest3);
+    when(link4.getDestination()).thenReturn(dest4);
+    when(link5.getDestination()).thenReturn(dest5);
+    when(link6.getDestination()).thenReturn(dest6);
+    when(link7.getDestination()).thenReturn(dest7);
+    when(link8.getDestination()).thenReturn(dest8);
+    when(link9.getDestination()).thenReturn(dest9);
+    when(link10.getDestination()).thenReturn(dest10);
+    when(link11.getDestination()).thenReturn(dest11);
+    when(link12.getDestination()).thenReturn(dest12);
+    when(src1.getSourceNode()).thenReturn(nodeId1);
+    when(dest1.getDestNode()).thenReturn(nodeId2);
+    when(src2.getSourceNode()).thenReturn(nodeId2);
+    when(dest2.getDestNode()).thenReturn(nodeId1);
+    when(src3.getSourceNode()).thenReturn(nodeId1);
+    when(dest3.getDestNode()).thenReturn(nodeId3);
+    when(src4.getSourceNode()).thenReturn(nodeId3);
+    when(dest4.getDestNode()).thenReturn(nodeId1);
+    when(src5.getSourceNode()).thenReturn(nodeId2);
+    when(dest5.getDestNode()).thenReturn(nodeId4);
+    when(src6.getSourceNode()).thenReturn(nodeId4);
+    when(dest6.getDestNode()).thenReturn(nodeId2);
+    when(src7.getSourceNode()).thenReturn(nodeId2);
+    when(dest7.getDestNode()).thenReturn(nodeId5);
+    when(src8.getSourceNode()).thenReturn(nodeId5);
+    when(dest8.getDestNode()).thenReturn(nodeId2);
+    when(src9.getSourceNode()).thenReturn(nodeId6);
+    when(dest9.getDestNode()).thenReturn(nodeId3);
+    when(src10.getSourceNode()).thenReturn(nodeId3);
+    when(dest10.getDestNode()).thenReturn(nodeId6);
+    when(src11.getSourceNode()).thenReturn(nodeId7);
+    when(dest11.getDestNode()).thenReturn(nodeId3);
+    when(src12.getSourceNode()).thenReturn(nodeId3);
+    when(dest12.getDestNode()).thenReturn(nodeId7);
+    links.add(link1);
+    links.add(link2);
+    links.add(link3);
+    links.add(link4);
+    links.add(link5);
+    links.add(link6);
+    links.add(link7);
+    links.add(link8);
+    links.add(link9);
+    links.add(link10);
+    links.add(link11);
+    links.add(link12);
+
+  }
+
+  @Test
+  public void testAddLinksAndGetPath() throws Exception {
+    NetworkGraphService networkGraphService = new NetworkGraphDijkstra();
+    networkGraphService.addLinks(links);
+    List<Link> path = networkGraphService.getPath(nodeId2, nodeId3);
+    assertEquals("path size is not as expected.", 2, path.size());
+    assertEquals("link source is not as expected.", nodeId2, path.get(0).getSource().getSourceNode());
+    assertEquals("link destination is not as expected.", nodeId1, path.get(0).getDestination().getDestNode());
+    path = networkGraphService.getPath(nodeId3, nodeId2);
+    assertEquals("path size is not as expected.", 2, path.size());
+    assertEquals("link source is not as expected.", nodeId3, path.get(0).getSource().getSourceNode());
+    assertEquals("link destination is not as expected.", nodeId1, path.get(0).getDestination().getDestNode());
+
+    path = networkGraphService.getPath(nodeId4, nodeId6);
+    assertEquals("path size is not as expected.", 4, path.size());
+    assertEquals("link source is not as expected.", nodeId4, path.get(0).getSource().getSourceNode());
+    assertEquals("link destination is not as expected.", nodeId2, path.get(0).getDestination().getDestNode());
+  }
+}
diff --git a/opendaylight/md-sal/samples/l2switch/implementation/src/test/java/org/opendaylight/controller/sample/l2switch/md/topology/TopologyLinkDataChangeHandlerTest.java b/opendaylight/md-sal/samples/l2switch/implementation/src/test/java/org/opendaylight/controller/sample/l2switch/md/topology/TopologyLinkDataChangeHandlerTest.java
new file mode 100644 (file)
index 0000000..9ecd256
--- /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.sample.l2switch.md.topology;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.sample.l2switch.md.util.InstanceIdentifierUtils;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ */
+public class TopologyLinkDataChangeHandlerTest {
+  NetworkGraphService networkGraphService;
+  DataBrokerService dataBrokerService;
+  DataChangeEvent dataChangeEvent;
+  Topology topology;
+  Link link;
+
+  @Before
+  public void init() {
+    networkGraphService = mock(NetworkGraphService.class);
+    dataBrokerService = mock(DataBrokerService.class);
+    dataChangeEvent = mock(DataChangeEvent.class);
+    link = mock(Link.class);
+    topology = mock(Topology.class);
+  }
+
+  @Test
+  public void testOnDataChange() throws Exception {
+    TopologyLinkDataChangeHandler topologyLinkDataChangeHandler = new TopologyLinkDataChangeHandler(dataBrokerService, networkGraphService, 2);
+    Map<InstanceIdentifier<?>, DataObject> original = new HashMap<InstanceIdentifier<?>, DataObject>();
+    InstanceIdentifier<?> instanceIdentifier = InstanceIdentifierUtils.generateTopologyInstanceIdentifier("flow:1");
+    DataObject dataObject = mock(DataObject.class);
+    Map<InstanceIdentifier<?>, DataObject> updated = new HashMap<InstanceIdentifier<?>, DataObject>();
+    updated.put(instanceIdentifier, dataObject);
+    when(dataChangeEvent.getUpdatedOperationalData()).thenReturn(updated);
+    when(dataChangeEvent.getOriginalOperationalData()).thenReturn(original);
+    List<Link> links = new ArrayList<>();
+    links.add(link);
+    when(dataBrokerService.readOperationalData(instanceIdentifier)).thenReturn(topology);
+    when(topology.getLink()).thenReturn(links);
+
+    topologyLinkDataChangeHandler.onDataChanged(dataChangeEvent);
+    Thread.sleep(2100);
+    verify(networkGraphService, times(1)).addLinks(links);
+  }
+}
diff --git a/opendaylight/md-sal/samples/l2switch/model/pom.xml b/opendaylight/md-sal/samples/l2switch/model/pom.xml
new file mode 100644 (file)
index 0000000..d0ef2e0
--- /dev/null
@@ -0,0 +1,97 @@
+<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>
+    <artifactId>sal-samples</artifactId>
+    <groupId>org.opendaylight.controller.samples</groupId>
+    <version>1.1-SNAPSHOT</version>
+    <relativePath>../..</relativePath>
+  </parent>
+  <groupId>org.opendaylight.controller.samples.l2switch.md</groupId>
+  <artifactId>l2switch-model</artifactId>
+  <packaging>bundle</packaging>
+
+  <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>
+                             <Import-Package>org.opendaylight.yangtools.yang.binding.annotations, *</Import-Package>
+                             <manifestLocation>${project.basedir}/META-INF</manifestLocation>
+                           </instructions>
+                         </configuration>
+                       </plugin>
+                 <plugin>
+        <groupId>org.opendaylight.yangtools</groupId>
+        <artifactId>yang-maven-plugin</artifactId>
+        <version>${yangtools.version}</version>
+        <executions>
+            <execution>
+                <goals>
+                    <goal>generate-sources</goal>
+                </goals>
+                <configuration>
+                    <yangFilesRootDir>src/main/yang</yangFilesRootDir>
+                    <codeGenerators>
+                        <generator>
+                            <codeGeneratorClass>
+                                org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl
+                            </codeGeneratorClass>
+                            <outputBaseDir>
+                                target/generated-sources/sal
+                            </outputBaseDir>
+                        </generator>
+                        <generator>
+                            <codeGeneratorClass>org.opendaylight.yangtools.yang.unified.doc.generator.maven.DocumentationGeneratorImpl</codeGeneratorClass>
+                            <outputBaseDir>target/site/models</outputBaseDir>
+                        </generator>
+                        <generator>
+                            <codeGeneratorClass>org.opendaylight.yangtools.yang.wadl.generator.maven.WadlGenerator</codeGeneratorClass>
+                            <outputBaseDir>target/site/models</outputBaseDir>
+                        </generator>
+                    </codeGenerators>
+                    <inspectDependencies>true</inspectDependencies>
+                </configuration>
+            </execution>
+        </executions>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>maven-sal-api-gen-plugin</artifactId>
+                <version>${yangtools.version}</version>
+                <type>jar</type>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-binding</artifactId>
+                <version>${yangtools.version}</version>
+                <type>jar</type>
+            </dependency>
+        </dependencies>
+                         
+                       </plugin>
+               </plugins>
+       </build>
+       
+       <dependencies>
+      <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.model</groupId>
+          <artifactId>ietf-yang-types</artifactId>
+      </dependency>
+      <dependency>
+        <groupId>org.opendaylight.controller.model</groupId>
+        <artifactId>model-inventory</artifactId>
+      </dependency>
+  </dependencies>
+</project>
diff --git a/opendaylight/md-sal/samples/l2switch/model/src/main/yang/l2-address-tracker.yang b/opendaylight/md-sal/samples/l2switch/model/src/main/yang/l2-address-tracker.yang
new file mode 100644 (file)
index 0000000..d694c68
--- /dev/null
@@ -0,0 +1,45 @@
+module l2-address-tracker {
+  yang-version 1;
+  namespace "urn:opendaylight:l2-address-tracker";
+  prefix l2-address-tracker;
+
+  import ietf-yang-types {
+    prefix yang;
+    revision-date 2010-09-24;
+  }
+  import opendaylight-inventory {
+    prefix inv;
+    revision-date 2013-08-19;
+  }
+
+  organization "Cisco Systems Inc";
+  contact
+    "Alex Fan <alefan@cisco.com>";
+  description
+    "YANG version of the  L2 Address Tracker Data Model";
+
+  revision 2014-04-02 {
+    description
+      "L2 Address Tracker module draft.";
+  }
+
+  grouping l2-address {
+    leaf mac {
+        type yang:mac-address;
+        mandatory true;
+        description
+          "the mac address of the host.";
+    }
+    leaf node-connector-ref {
+      type inv:node-connector-ref;
+    }
+  }
+
+  container l2-addresses {
+    config false;
+    list l2-address {
+      key "mac";
+      uses l2-address;
+    }
+  }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/samples/l2switch/pom.xml b/opendaylight/md-sal/samples/l2switch/pom.xml
new file mode 100644 (file)
index 0000000..2e2100b
--- /dev/null
@@ -0,0 +1,18 @@
+<?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>
+
+
+  <artifactId>l2switch.aggregator</artifactId>
+  <groupId>org.opendaylight.controller.samples.l2switch</groupId>
+  <version>1.0.0-SNAPSHOT</version>
+  <packaging>pom</packaging>
+
+  <modules>
+    <module>model</module>
+    <module>implementation</module>
+  </modules>
+
+</project>
index 54810266a799bc028afd6eeb93eee16730845d30..9f9d9d35c7ea38c9a49f263ea5b4625351d584b4 100644 (file)
@@ -19,6 +19,7 @@
                <module>toaster</module>
                <module>toaster-consumer</module>
                <module>toaster-provider</module>
+    <module>l2switch</module>
   </modules>
 
     <profiles>
index c006a34da7328add85de51d5e2f94a6377478697..486cdcf04e6f4f2a4c4628c1e4d4989288e62c79 100644 (file)
@@ -17,11 +17,15 @@ import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
 import org.opendaylight.yangtools.concepts.Registration;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 /**
 *
 */
 public final class ToasterConsumerModule extends org.opendaylight.controller.config.yang.config.toaster_consumer.impl.AbstractToasterConsumerModule
  {
+    private static final Logger log = LoggerFactory.getLogger(ToasterConsumerModule.class);
 
     public ToasterConsumerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
         super(identifier, dependencyResolver);
@@ -55,6 +59,7 @@ public final class ToasterConsumerModule extends org.opendaylight.controller.con
             public void close() throws Exception {
                 runtimeRegistration.close();
                 notificationRegistration.close();
+                log.info("Toaster consumer (instance {}) torn down.", this);
             }
 
             @Override
@@ -63,6 +68,8 @@ public final class ToasterConsumerModule extends org.opendaylight.controller.con
             }
         }
 
-        return new AutoCloseableToastConsumer();
+        AutoCloseable ret = new AutoCloseableToastConsumer();
+        log.info("Toaster consumer (instance {}) initialized.", ret);
+        return ret;
     }
 }
index 38a4dd46617405c148022d4622a3b200b9182b80..000783bd07e545ee16ae5e306c20c8d72f4d6e35 100644 (file)
@@ -52,6 +52,7 @@ public class ToasterTest {
                 mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(), //
                 mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(), //
                 systemProperty("osgi.bundles.defaultStartLevel").value("4"),
+                systemPackages("sun.nio.ch"),
 
                 toasterBundles(),
                 mdSalCoreBundles(),
index 1029105233311b9310eb68bed09b51c630e4b4be..d9bb36eb8ec6d1ca97032e3328cbb72a9879f1bf 100644 (file)
@@ -14,12 +14,15 @@ import org.opendaylight.controller.sample.toaster.provider.OpendaylightToaster;
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.Toaster;
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterData;
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
 *
 */
 public final class ToasterProviderModule extends org.opendaylight.controller.config.yang.config.toaster_provider.impl.AbstractToasterProviderModule
  {
+    private static final Logger log = LoggerFactory.getLogger(ToasterProviderModule.class);
 
     public ToasterProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
         super(identifier, dependencyResolver);
@@ -43,6 +46,7 @@ public final class ToasterProviderModule extends org.opendaylight.controller.con
 
         // Register to md-sal
         opendaylightToaster.setNotificationProvider(getNotificationServiceDependency());
+        opendaylightToaster.setDataProvider(getDataBrokerDependency());
         final BindingAwareBroker.RpcRegistration<ToasterService> rpcRegistration = getRpcRegistryDependency()
                 .addRpcImplementation(ToasterService.class, opendaylightToaster);
 
@@ -58,6 +62,8 @@ public final class ToasterProviderModule extends org.opendaylight.controller.con
             public void close() throws Exception {
                 rpcRegistration.close();
                 runtimeReg.close();
+                opendaylightToaster.close();
+                log.info("Toaster provider (instance {}) torn down.", this);
             }
 
             @Override
@@ -66,7 +72,8 @@ public final class ToasterProviderModule extends org.opendaylight.controller.con
             }
         }
 
-        return new AutoCloseableToaster();
+        AutoCloseable ret = new AutoCloseableToaster();
+        log.info("Toaster provider (instance {}) initialized.", ret);
+        return ret;
     }
-
 }
index e1d69800cab5688ab46d7df75baefda1c4361998..2dab924e7709dc74fcb3047272d068f9e459c2d5 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.controller.sample.toaster.provider;
 
 import java.util.Collections;
 import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
@@ -16,6 +17,8 @@ import java.util.concurrent.atomic.AtomicLong;
 
 import org.opendaylight.controller.config.yang.config.toaster_provider.impl.ToasterProviderRuntimeMXBean;
 import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
 import org.opendaylight.controller.sal.common.util.Rpcs;
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.DisplayString;
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.MakeToastInput;
@@ -26,6 +29,7 @@ import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterBuilder;
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterData;
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.RpcError;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.slf4j.Logger;
@@ -33,37 +37,37 @@ import org.slf4j.LoggerFactory;
 
 import com.google.common.util.concurrent.Futures;
 
-public class OpendaylightToaster implements ToasterData, ToasterService, ToasterProviderRuntimeMXBean {
+public class OpendaylightToaster implements ToasterData, ToasterService, ToasterProviderRuntimeMXBean, AutoCloseable {
 
     private static final Logger log = LoggerFactory.getLogger(OpendaylightToaster.class);
+    private static final InstanceIdentifier<Toaster>  toasterIID = InstanceIdentifier.builder(Toaster.class).build();
 
     private static final DisplayString toasterManufacturer = new DisplayString("Opendaylight");
     private static final DisplayString toasterModelNumber = new DisplayString("Model 1 - Binding Aware");
-    private final ToasterStatus toasterStatus;
 
     private NotificationProviderService notificationProvider;
+    private DataBrokerService dataProvider;
     private final ExecutorService executor;
 
     private Future<RpcResult<Void>> currentTask;
 
     public OpendaylightToaster() {
-        toasterStatus = ToasterStatus.Down;
         executor = Executors.newFixedThreadPool(1);
     }
 
     @Override
-    public Toaster getToaster() {
+    public synchronized Toaster getToaster() {
         ToasterBuilder tb = new ToasterBuilder();
         tb //
         .setToasterManufacturer(toasterManufacturer) //
-                .setToasterModelNumber(toasterModelNumber) //
-                .setToasterStatus(toasterStatus);
+        .setToasterModelNumber(toasterModelNumber) //
+        .setToasterStatus(currentTask == null ? ToasterStatus.Up : ToasterStatus.Down);
 
         return tb.build();
     }
 
     @Override
-    public Future<RpcResult<Void>> cancelToast() {
+    public synchronized Future<RpcResult<Void>> cancelToast() {
         if (currentTask != null) {
             cancelToastImpl();
         }
@@ -71,14 +75,14 @@ public class OpendaylightToaster implements ToasterData, ToasterService, Toaster
     }
 
     @Override
-    public Future<RpcResult<Void>> makeToast(MakeToastInput input) {
-        // TODO Auto-generated method stub
-        log.trace("makeToast - Received input for toast");
+    public synchronized Future<RpcResult<Void>> makeToast(MakeToastInput input) {
+        log.debug("makeToast - Received input for toast");
         logToastInput(input);
         if (currentTask != null) {
             return inProgressError();
         }
         currentTask = executor.submit(new MakeToastTask(input));
+        updateStatus();
         return currentTask;
     }
 
@@ -98,6 +102,11 @@ public class OpendaylightToaster implements ToasterData, ToasterService, Toaster
         this.notificationProvider = salService;
     }
 
+    public void setDataProvider(DataBrokerService salDataProvider) {
+        this.dataProvider = salDataProvider;
+        updateStatus();
+    }
+
     private void logToastInput(MakeToastInput input) {
         String toastType = input.getToasterToastType().getName();
         String toastDoneness = input.getToasterDoneness().toString();
@@ -111,6 +120,31 @@ public class OpendaylightToaster implements ToasterData, ToasterService, Toaster
         return toastsMade.get();
     }
 
+    private void updateStatus() {
+        if (dataProvider != null) {
+            final DataModificationTransaction t = dataProvider.beginTransaction();
+            t.removeOperationalData(toasterIID);
+            t.putOperationalData(toasterIID, getToaster());
+
+            try {
+                t.commit().get();
+            } catch (InterruptedException | ExecutionException e) {
+                log.warn("Failed to update toaster status, operational otherwise", e);
+            }
+        } else {
+            log.trace("No data provider configured, not updating status");
+        }
+    }
+
+    @Override
+    public void close() throws ExecutionException, InterruptedException {
+        if (dataProvider != null) {
+            final DataModificationTransaction t = dataProvider.beginTransaction();
+            t.removeOperationalData(toasterIID);
+            t.commit().get();
+        }
+    }
+
     private class MakeToastTask implements Callable<RpcResult<Void>> {
 
         final MakeToastInput toastRequest;
@@ -120,17 +154,18 @@ public class OpendaylightToaster implements ToasterData, ToasterService, Toaster
         }
 
         @Override
-        public RpcResult<Void> call() throws Exception {
-            Thread.sleep(1000);
+        public RpcResult<Void> call() throws InterruptedException {
+            Thread.sleep(1000 * toastRequest.getToasterDoneness());
 
             ToastDoneBuilder notifyBuilder = new ToastDoneBuilder();
             notifyBuilder.setToastStatus(ToastStatus.Done);
             notificationProvider.publish(notifyBuilder.build());
-            log.trace("Toast Done");
+            log.debug("Toast Done");
             logToastInput(toastRequest);
-            currentTask = null;
 
+            currentTask = null;
             toastsMade.incrementAndGet();
+            updateStatus();
 
             return Rpcs.<Void> getRpcResult(true, null, Collections.<RpcError> emptySet());
         }
index 0be8874245d602e88f00359d0c95d9750e41ff15..17b0c8d0f0f9b3361ba4f15c8782bf8afb8828aa 100644 (file)
@@ -47,6 +47,14 @@ module toaster-provider-impl {
                 }
             }
 
+            container data-broker {
+                uses config:service-ref {
+                    refine type {
+                        mandatory false;
+                        config:required-identity mdsal:binding-data-broker;
+                    }
+                }
+            }
         }
     }
 
@@ -60,4 +68,4 @@ module toaster-provider-impl {
 
         }
     }
-}
\ No newline at end of file
+}
index a9c1e054136d49f98f885b2b07575f2a33303aac..b829990bd3be3e26cb0b30407fbf7e6d9e5c4101 100644 (file)
             <groupId>org.eclipse.xtend</groupId>
             <artifactId>org.eclipse.xtend.lib</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
         
         <dependency>
             <groupId>junit</groupId>
index 15fee781ef77275a0213e4a842b85ed953ae8eaa..f0a8584730955a76514824494d02de82f66ded54 100644 (file)
@@ -43,7 +43,7 @@
         </plugins>
         <pluginManagement>
             <plugins>
-                <!--This plugin's configuration is used to store Eclipse 
+                <!--This plugin's configuration is used to store Eclipse
                     m2e settings only. It has no influence on the Maven build itself. -->
                 <plugin>
                     <groupId>org.eclipse.m2e</groupId>
         <dependency>
             <groupId>io.netty</groupId>
             <artifactId>netty-all</artifactId>
-            <version>4.0.10.Final</version>
+           <version>${netty.version}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <artifactId>jersey-core</artifactId>
             <version>${jersey.version}</version>
         </dependency>
-        <!-- <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-servlet</artifactId> 
+        <!-- <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-servlet</artifactId>
             <version>${jersey.version}</version> </dependency> -->
         <dependency>
             <groupId>com.sun.jersey</groupId>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.controller</groupId>
-            <artifactId>config-persister-file-adapter</artifactId>
+            <artifactId>config-persister-file-xml-adapter</artifactId>
             <version>${config.version}</version>
         </dependency>
         <dependency>
index 8400bc18353fc765da09cf6f84831085875fbb44..4a43e0cf8266c1ae5c56dbb6a77c9bdc7018bb73 100644 (file)
@@ -75,9 +75,9 @@ public class ServiceProviderController {
         InstanceIdentifier path = InstanceIdentifier.builder(InventoryUtils.INVENTORY_PATH)
                 .nodeWithKey(InventoryUtils.INVENTORY_NODE, InventoryUtils.INVENTORY_ID, "foo").toInstance();
 
-        
+
         InstanceIdentifier mountPointPath = path;
-        
+
         /** We retrive a mountpoint **/
         MountProvisionInstance mountPoint = mountService.getMountPoint(mountPointPath);
         CompositeNode data = mountPoint.readOperationalData(InstanceIdentifier.builder().node(CONFIG_MODULES)
@@ -215,7 +215,7 @@ public class ServiceProviderController {
                 mavenBundle(ODL, "yang-jmx-generator").versionAsInProject(),
                 mavenBundle(ODL, "logback-config").versionAsInProject(),
                 mavenBundle(ODL, "config-persister-api").versionAsInProject(),
-                // mavenBundle(ODL,"config-persister-file-adapter").versionAsInProject(),
+                // mavenBundle(ODL,"config-persister-file-xml-adapter").versionAsInProject(),
                 mavenBundle(ODL, "protocol-framework").versionAsInProject(),
                 mavenBundle(ODL, "netconf-api").versionAsInProject(),
                 mavenBundle(ODL, "netconf-impl").versionAsInProject(),
index 525ab9e4fe1790ac438c73a822a583ad8bb38725..8035f420fb3797396f7abe7bec6753ed4eaa3c2d 100644 (file)
             <groupId>org.eclipse.xtend</groupId>
             <artifactId>org.eclipse.xtend.lib</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
 
     <build>
index 036fe733aa80b56ac7cc2302d9a26cab0d2703ac..73e03d1e7d208287c1bed8505f889fbe470c8fa8 100644 (file)
@@ -14,6 +14,7 @@ import org.opendaylight.yangtools.yang.binding.NotificationListener
 import org.slf4j.LoggerFactory
 import org.opendaylight.controller.sal.binding.api.AbstractBindingAwareProvider
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext
+import org.osgi.framework.BundleContext;
 
 class FlowCapableTopologyProvider extends AbstractBindingAwareProvider implements AutoCloseable {
 
@@ -35,7 +36,11 @@ class FlowCapableTopologyProvider extends AbstractBindingAwareProvider implement
        LOG.info("FlowCapableTopologyProvider stopped.");
         listenerRegistration?.close();
     }
-    
+
+     /**
+       * Gets called on start of a bundle.
+       * @param session
+       */
     override onSessionInitiated(ProviderContext session) {
         dataService = session.getSALService(DataProviderService)
         notificationService = session.getSALService(NotificationProviderService)
@@ -43,6 +48,14 @@ class FlowCapableTopologyProvider extends AbstractBindingAwareProvider implement
         exporter.start();
         listenerRegistration = notificationService.registerNotificationListener(exporter);
     }
+
+    /**
+      * Gets called during stop bundle
+      * @param context The execution context of the bundle being stopped.
+      */
+    override stopImpl(BundleContext context) {
+        close();
+    }
     
 }
 
index b39549ed5b73095f44194a30e15d5b6cdaa182ce..421870ca3611bb6e35a8e7ddd9ec574d0e62e4ab 100644 (file)
@@ -19,6 +19,9 @@ import org.osgi.util.tracker.ServiceTrackerCustomizer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.Dictionary;
+import java.util.Hashtable;
+
 import static com.google.common.base.Preconditions.checkState;
 
 public class Activator implements BundleActivator {
@@ -88,7 +91,9 @@ public class Activator implements BundleActivator {
         public void run() {
             NetconfOperationServiceFactoryImpl factory = new NetconfOperationServiceFactoryImpl(yangStoreService);
             logger.debug("Registering into OSGi");
-            osgiRegistration = context.registerService(NetconfOperationServiceFactory.class, factory, null);
+            Dictionary<String, String> properties = new Hashtable<>();
+            properties.put("name", "config-netconf-connector");
+            osgiRegistration = context.registerService(NetconfOperationServiceFactory.class, factory, properties);
         }
     }
 }
index 4ca71ae2886a143d7322ed67be3a97b6f8a6cfb3..b8b7fcb47f137f5d422c0239f9159cec3c5ad234 100644 (file)
@@ -66,7 +66,7 @@ public class NetconfOperationServiceFactoryImpl implements NetconfOperationServi
     }
 
     @Override
-    public NetconfOperationServiceImpl createService(long netconfSessionId, String netconfSessionIdForReporting) {
+    public NetconfOperationServiceImpl createService(String netconfSessionIdForReporting) {
         try {
             return new NetconfOperationServiceImpl(yangStoreService, jmxClient, netconfSessionIdForReporting);
         } catch (YangStoreException e) {
index b52328f6313f534f522778b8614c22ea1246f76f..8b6b1aefc1971d2af9fbc7f69b4264cc79ed3327 100644 (file)
@@ -52,10 +52,10 @@ import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStore
 import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreSnapshot;
 import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
 import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession;
-import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouterImpl;
 import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshot;
 import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
 import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
 import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
 import org.opendaylight.controller.netconf.util.xml.XmlElement;
 import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
@@ -681,7 +681,7 @@ public class NetconfMappingTest extends AbstractConfigTest {
 
         Preconditions.checkState(priority != HandlingPriority.CANNOT_HANDLE);
 
-        final Document response = op.handle(request, NetconfOperationRouterImpl.EXECUTION_TERMINATION_POINT);
+        final Document response = op.handle(request, NetconfOperationChainedExecution.EXECUTION_TERMINATION_POINT);
         logger.debug("Got response\n{}", XmlUtil.toString(response));
         return response.getDocumentElement();
     }
index c0b9f68814141427098f92304f024021b74dcfb9..7b4511e1f937c3ec7ca63c6643d91ab5d3ba67c1 100644 (file)
             <groupId>${project.groupId}</groupId>
             <artifactId>netconf-api</artifactId>
         </dependency>
-        <dependency>
-            <groupId>${project.groupId}</groupId>
-            <artifactId>netconf-client</artifactId>
-        </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>netconf-util</artifactId>
@@ -61,6 +57,7 @@
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>mockito-configuration</artifactId>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>commons-io</groupId>
                     <instructions>
                         <Bundle-Activator>org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator
                         </Bundle-Activator>
-                        <Require-Capability>org.opendaylight.controller.config.persister.storage.adapter
-                        </Require-Capability>
-                        <Import-Package>
-                            com.google.common.base,
-                            com.google.common.collect,
-                            javax.management,
-                            javax.xml.parsers,
-                            org.opendaylight.controller.config.persist.api,
-                            org.opendaylight.controller.netconf.api,
-                            org.opendaylight.controller.netconf.api.jmx,
-                            org.opendaylight.controller.netconf.client,
-                            org.opendaylight.controller.netconf.util.osgi,
-                            org.opendaylight.controller.netconf.util.xml,
-                            org.opendaylight.controller.netconf.util.messages,
-                            io.netty.channel,
-                            io.netty.channel.nio,
-                            io.netty.util.concurrent,
-                            org.osgi.framework,
-                            org.slf4j,
-                            org.w3c.dom,
-                            org.xml.sax,
-                            javax.xml.namespace,
-                            javax.xml.xpath,
-                            org.opendaylight.controller.config.api,
-                            org.opendaylight.controller.netconf.util
-                        </Import-Package>
+                        <Require-Capability>org.opendaylight.controller.config.persister.storage.adapter</Require-Capability>
                         <Export-Package>
                         </Export-Package>
                     </instructions>
index d9c5dfadede5a02bcd7d6c0f91fffccda499a14f..ab353e349b2908b67ade7f583f3601b7aa16ce3b 100644 (file)
@@ -5,6 +5,7 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
+
 package org.opendaylight.controller.netconf.persist.impl;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -18,54 +19,50 @@ import org.w3c.dom.Element;
 
 import java.util.Collections;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
-import java.util.regex.Pattern;
-
-import static com.google.common.base.Preconditions.checkState;
 
+/**
+ * Inspects snapshot xml to be stored, remove all capabilities that are not referenced by it.
+ * Useful when persisting current configuration.
+ */
 public class CapabilityStrippingConfigSnapshotHolder implements ConfigSnapshotHolder {
     private static final Logger logger = LoggerFactory.getLogger(CapabilityStrippingConfigSnapshotHolder.class);
 
     private final String configSnapshot;
     private final StripCapabilitiesResult stripCapabilitiesResult;
 
-    public CapabilityStrippingConfigSnapshotHolder(Element snapshot, Set<String> capabilities, Pattern ignoredMissingCapabilityRegex) {
+    public CapabilityStrippingConfigSnapshotHolder(Element snapshot, Set<String> capabilities) {
         final XmlElement configElement = XmlElement.fromDomElement(snapshot);
         configSnapshot = XmlUtil.toString(configElement.getDomElement());
-        stripCapabilitiesResult = stripCapabilities(configElement, capabilities, ignoredMissingCapabilityRegex);
+        stripCapabilitiesResult = stripCapabilities(configElement, capabilities);
     }
 
     private static class StripCapabilitiesResult {
-        private final SortedSet<String> requiredCapabilities, missingNamespaces;
+        private final SortedSet<String> requiredCapabilities, obsoleteCapabilities;
 
-        private StripCapabilitiesResult(SortedSet<String> requiredCapabilities, SortedSet<String> missingNamespaces) {
+        private StripCapabilitiesResult(SortedSet<String> requiredCapabilities, SortedSet<String> obsoleteCapabilities) {
             this.requiredCapabilities = Collections.unmodifiableSortedSet(requiredCapabilities);
-            this.missingNamespaces = Collections.unmodifiableSortedSet(missingNamespaces);
+            this.obsoleteCapabilities = Collections.unmodifiableSortedSet(obsoleteCapabilities);
         }
     }
 
 
     @VisibleForTesting
-    static StripCapabilitiesResult stripCapabilities(XmlElement configElement, Set<String> allCapabilitiesFromHello,
-                                                     Pattern ignoredMissingCapabilityRegex) {
+    static StripCapabilitiesResult stripCapabilities(XmlElement configElement, Set<String> allCapabilitiesFromHello) {
         // collect all namespaces
         Set<String> foundNamespacesInXML = getNamespaces(configElement);
         logger.trace("All capabilities {}\nFound namespaces in XML {}", allCapabilitiesFromHello, foundNamespacesInXML);
         // required are referenced both in xml and hello
         SortedSet<String> requiredCapabilities = new TreeSet<>();
         // can be removed
-        Set<String> obsoleteCapabilities = new HashSet<>();
-        // are in xml but not in hello
-        SortedSet<String> missingNamespaces = new TreeSet<>(foundNamespacesInXML);
+        SortedSet<String> obsoleteCapabilities = new TreeSet<>();
         for (String capability : allCapabilitiesFromHello) {
             String namespace = capability.replaceAll("\\?.*","");
             if (foundNamespacesInXML.contains(namespace)) {
                 requiredCapabilities.add(capability);
-                checkState(missingNamespaces.remove(namespace));
             } else {
                 obsoleteCapabilities.add(capability);
             }
@@ -74,17 +71,7 @@ public class CapabilityStrippingConfigSnapshotHolder implements ConfigSnapshotHo
         logger.trace("Required capabilities {}, \nObsolete capabilities {}",
                 requiredCapabilities, obsoleteCapabilities);
 
-        for(Iterator<String> iterator = missingNamespaces.iterator();iterator.hasNext(); ){
-            String capability = iterator.next();
-            if (ignoredMissingCapabilityRegex.matcher(capability).matches()){
-                logger.trace("Ignoring missing capability {}", capability);
-                iterator.remove();
-            }
-        }
-        if (missingNamespaces.size() > 0) {
-            logger.warn("Some capabilities are missing: {}", missingNamespaces);
-        }
-        return new StripCapabilitiesResult(requiredCapabilities, missingNamespaces);
+        return new StripCapabilitiesResult(requiredCapabilities, obsoleteCapabilities);
     }
 
     static Set<String> getNamespaces(XmlElement element){
@@ -94,7 +81,6 @@ public class CapabilityStrippingConfigSnapshotHolder implements ConfigSnapshotHo
                 result.add(attribute.getValue().getValue());
             }
         }
-        //element.getAttributes()
         for(XmlElement child: element.getChildElements()) {
             result.addAll(getNamespaces(child));
         }
@@ -107,8 +93,8 @@ public class CapabilityStrippingConfigSnapshotHolder implements ConfigSnapshotHo
     }
 
     @VisibleForTesting
-    Set<String> getMissingNamespaces(){
-        return stripCapabilitiesResult.missingNamespaces;
+    Set<String> getObsoleteCapabilities(){
+        return stripCapabilitiesResult.obsoleteCapabilities;
     }
 
     @Override
index 2d89bbc55c03d59d3ad7e648426df91562fc6fde..eb6fd2722a4cbdb9860ec4faf57462403ff02fbd 100644 (file)
@@ -23,7 +23,6 @@ import javax.management.NotificationListener;
 import javax.management.ObjectName;
 import java.io.Closeable;
 import java.io.IOException;
-import java.util.regex.Pattern;
 
 /**
  * Responsible for listening for notifications from netconf (via JMX) containing latest
@@ -39,9 +38,9 @@ public class ConfigPersisterNotificationHandler implements Closeable {
 
 
     public ConfigPersisterNotificationHandler(MBeanServerConnection mBeanServerConnection,
-                                              Persister persisterAggregator, Pattern ignoredMissingCapabilityRegex) {
+                                              Persister persisterAggregator) {
         this.mBeanServerConnection = mBeanServerConnection;
-        listener = new ConfigPersisterNotificationListener(persisterAggregator, ignoredMissingCapabilityRegex);
+        listener = new ConfigPersisterNotificationListener(persisterAggregator);
         registerAsJMXListener(mBeanServerConnection, listener);
 
     }
@@ -73,17 +72,16 @@ class ConfigPersisterNotificationListener implements NotificationListener {
     private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterNotificationListener.class);
 
     private final Persister persisterAggregator;
-    private final Pattern ignoredMissingCapabilityRegex;
 
-    ConfigPersisterNotificationListener(Persister persisterAggregator, Pattern ignoredMissingCapabilityRegex) {
+    ConfigPersisterNotificationListener(Persister persisterAggregator) {
         this.persisterAggregator = persisterAggregator;
-        this.ignoredMissingCapabilityRegex = ignoredMissingCapabilityRegex;
     }
 
     @Override
     public void handleNotification(Notification notification, Object handback) {
-        if (notification instanceof NetconfJMXNotification == false)
+        if (notification instanceof NetconfJMXNotification == false) {
             return;
+        }
 
         // Socket should not be closed at this point
         // Activator unregisters this as JMX listener before close is called
@@ -98,14 +96,15 @@ class ConfigPersisterNotificationListener implements NotificationListener {
                 logger.warn("Exception occured during notification handling: ", e);
                 throw e;
             }
-        } else
+        } else {
             throw new IllegalStateException("Unknown config registry notification type " + notification);
+        }
     }
 
     private void handleAfterCommitNotification(final CommitJMXNotification notification) {
         try {
             persisterAggregator.persistConfig(new CapabilityStrippingConfigSnapshotHolder(notification.getConfigSnapshot(),
-                    notification.getCapabilities(), ignoredMissingCapabilityRegex));
+                    notification.getCapabilities()));
             logger.trace("Configuration persisted successfully");
         } catch (IOException e) {
             throw new RuntimeException("Unable to persist configuration snapshot", e);
index ea2a46dba535f825d0baa7b5dfa58d4b6371c297..ed5a3a97a4c67809e75857ba77409eb695e00535 100644 (file)
@@ -8,14 +8,21 @@
 
 package org.opendaylight.controller.netconf.persist.impl;
 
+import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.Collections2;
 import org.opendaylight.controller.config.api.ConflictingVersionException;
 import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
-import org.opendaylight.controller.netconf.client.NetconfClient;
-import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.controller.netconf.mapping.api.Capability;
+import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
 import org.opendaylight.controller.netconf.util.NetconfUtil;
-import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
 import org.opendaylight.controller.netconf.util.xml.XmlElement;
 import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
@@ -28,38 +35,36 @@ import org.xml.sax.SAXException;
 import javax.annotation.concurrent.Immutable;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.Collections;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map.Entry;
 import java.util.Set;
-import java.util.concurrent.ExecutionException;
+import java.util.TreeMap;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 
 @Immutable
 public class ConfigPusher {
     private static final Logger logger = LoggerFactory.getLogger(ConfigPusher.class);
 
-    private final ConfigPusherConfiguration configuration;
+    private final long maxWaitForCapabilitiesMillis;
+    private final long conflictingVersionTimeoutMillis;
+    private final NetconfOperationServiceFactory configNetconfConnector;
 
-    public ConfigPusher(ConfigPusherConfiguration configuration) {
-        this.configuration = configuration;
+    public ConfigPusher(NetconfOperationServiceFactory configNetconfConnector, long maxWaitForCapabilitiesMillis,
+                        long conflictingVersionTimeoutMillis) {
+        this.configNetconfConnector = configNetconfConnector;
+        this.maxWaitForCapabilitiesMillis = maxWaitForCapabilitiesMillis;
+        this.conflictingVersionTimeoutMillis = conflictingVersionTimeoutMillis;
     }
 
-    public synchronized LinkedHashMap<ConfigSnapshotHolder, EditAndCommitResponseWithRetries> pushConfigs(
-            List<ConfigSnapshotHolder> configs) throws InterruptedException {
+    public synchronized LinkedHashMap<ConfigSnapshotHolder, EditAndCommitResponse> pushConfigs(List<ConfigSnapshotHolder> configs) {
         logger.debug("Last config snapshots to be pushed to netconf: {}", configs);
-
-        // first just make sure we can connect to netconf, even if nothing is being pushed
-        {
-            NetconfClient netconfClient = makeNetconfConnection(Collections.<String>emptySet());
-            Util.closeClientAndDispatcher(netconfClient);
-        }
-        LinkedHashMap<ConfigSnapshotHolder, EditAndCommitResponseWithRetries> result = new LinkedHashMap<>();
+        LinkedHashMap<ConfigSnapshotHolder, EditAndCommitResponse> result = new LinkedHashMap<>();
         // start pushing snapshots:
         for (ConfigSnapshotHolder configSnapshotHolder : configs) {
-            EditAndCommitResponseWithRetries editAndCommitResponseWithRetries = pushSnapshotWithRetries(configSnapshotHolder);
+            EditAndCommitResponse editAndCommitResponseWithRetries = pushConfigWithConflictingVersionRetries(configSnapshotHolder);
             logger.debug("Config snapshot pushed successfully: {}, result: {}", configSnapshotHolder, result);
             result.put(configSnapshotHolder, editAndCommitResponseWithRetries);
         }
@@ -68,106 +73,110 @@ public class ConfigPusher {
     }
 
     /**
-     * Checks for ConflictingVersionException and retries until optimistic lock succeeds or maximal
-     * number of attempts is reached.
+     * First calls {@link #getOperationServiceWithRetries(java.util.Set, String)} in order to wait until
+     * expected capabilities are present, then tries to push configuration. If {@link ConflictingVersionException}
+     * is caught, whole process is retried - new service instance need to be obtained from the factory. Closes
+     * {@link NetconfOperationService} after each use.
      */
-    private synchronized EditAndCommitResponseWithRetries pushSnapshotWithRetries(ConfigSnapshotHolder configSnapshotHolder)
-            throws InterruptedException {
-
-        ConflictingVersionException lastException = null;
-        int maxAttempts = configuration.netconfPushConfigAttempts;
-
-        for (int retryAttempt = 1; retryAttempt <= maxAttempts; retryAttempt++) {
-            NetconfClient netconfClient = makeNetconfConnection(configSnapshotHolder.getCapabilities());
-            logger.trace("Pushing following xml to netconf {}", configSnapshotHolder);
-            try {
-                EditAndCommitResponse editAndCommitResponse = pushLastConfig(configSnapshotHolder, netconfClient);
-                return new EditAndCommitResponseWithRetries(editAndCommitResponse, retryAttempt);
+    private synchronized EditAndCommitResponse pushConfigWithConflictingVersionRetries(ConfigSnapshotHolder configSnapshotHolder) {
+        ConflictingVersionException lastException;
+        Stopwatch stopwatch = new Stopwatch().start();
+        do {
+            try (NetconfOperationService operationService = getOperationServiceWithRetries(configSnapshotHolder.getCapabilities(), configSnapshotHolder.toString())) {
+                return pushConfig(configSnapshotHolder, operationService);
             } catch (ConflictingVersionException e) {
-                logger.debug("Conflicting version detected, will retry after timeout");
                 lastException = e;
-                Thread.sleep(configuration.netconfPushConfigDelayMs);
-            } catch (RuntimeException e) {
-                throw new IllegalStateException("Unable to load " + configSnapshotHolder, e);
-            } finally {
-                Util.closeClientAndDispatcher(netconfClient);
+                logger.debug("Conflicting version detected, will retry after timeout");
+                sleep();
             }
-        }
-        throw new IllegalStateException("Maximum attempt count has been reached for pushing " + configSnapshotHolder,
+        } while (stopwatch.elapsed(TimeUnit.MILLISECONDS) < conflictingVersionTimeoutMillis);
+        throw new IllegalStateException("Max wait for conflicting version stabilization timeout after " + stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms",
                 lastException);
     }
 
-    /**
-     * @param expectedCaps capabilities that server hello must contain. Will retry until all are found or throws RuntimeException.
-     *                     If empty set is provided, will only make sure netconf client successfuly connected to the server.
-     * @return NetconfClient that has all required capabilities from server.
-     */
-    private synchronized NetconfClient makeNetconfConnection(Set<String> expectedCaps) throws InterruptedException {
-
-        // TODO think about moving capability subset check to netconf client
-        // could be utilized by integration tests
+    private NetconfOperationService getOperationServiceWithRetries(Set<String> expectedCapabilities, String idForReporting) {
+        Stopwatch stopwatch = new Stopwatch().start();
+        NotEnoughCapabilitiesException lastException;
+        do {
+            try {
+                return getOperationService(expectedCapabilities, idForReporting);
+            } catch (NotEnoughCapabilitiesException e) {
+                logger.debug("Not enough capabilities: " + e.toString());
+                lastException = e;
+                sleep();
+            }
+        } while (stopwatch.elapsed(TimeUnit.MILLISECONDS) < maxWaitForCapabilitiesMillis);
+        throw new IllegalStateException("Max wait for capabilities reached." + lastException.getMessage(), lastException);
+    }
 
-        final long pollingStartNanos = System.nanoTime();
-        final long deadlineNanos = pollingStartNanos + TimeUnit.MILLISECONDS.toNanos(configuration.netconfCapabilitiesWaitTimeoutMs);
-        int attempt = 0;
+    private static class NotEnoughCapabilitiesException extends Exception {
+        private NotEnoughCapabilitiesException(String message, Throwable cause) {
+            super(message, cause);
+        }
 
-        NetconfHelloMessageAdditionalHeader additionalHeader = new NetconfHelloMessageAdditionalHeader("unknown",
-                configuration.netconfAddress.getAddress().getHostAddress(),
-                Integer.toString(configuration.netconfAddress.getPort()), "tcp", "persister");
+        private NotEnoughCapabilitiesException(String message) {
+            super(message);
+        }
+    }
 
-        Set<String> latestCapabilities = null;
-        while (System.nanoTime() < deadlineNanos) {
-            attempt++;
-            NetconfClientDispatcher netconfClientDispatcher = new NetconfClientDispatcher(configuration.eventLoopGroup,
-                    configuration.eventLoopGroup, additionalHeader, configuration.connectionAttemptTimeoutMs);
-            NetconfClient netconfClient;
-            try {
-                netconfClient = new NetconfClient(this.toString(), configuration.netconfAddress, configuration.connectionAttemptDelayMs, netconfClientDispatcher);
-            } catch (IllegalStateException e) {
-                logger.debug("Netconf {} was not initialized or is not stable, attempt {}", configuration.netconfAddress, attempt, e);
-                netconfClientDispatcher.close();
-                Thread.sleep(configuration.connectionAttemptDelayMs);
-                continue;
-            }
-            latestCapabilities = netconfClient.getCapabilities();
-            if (Util.isSubset(netconfClient, expectedCaps)) {
-                logger.debug("Hello from netconf stable with {} capabilities", latestCapabilities);
-                logger.trace("Session id received from netconf server: {}", netconfClient.getClientSession());
-                return netconfClient;
-            }
-            Set<String> allNotFound = computeNotFoundCapabilities(expectedCaps, latestCapabilities);
-            logger.debug("Netconf server did not provide required capabilities. Attempt {}. " +
-                    "Expected but not found: {}, all expected {}, current {}",
-                    attempt, allNotFound, expectedCaps, latestCapabilities);
-            Util.closeClientAndDispatcher(netconfClient);
-            Thread.sleep(configuration.connectionAttemptDelayMs);
+    /**
+     * Get NetconfOperationService iif all required capabilities are present.
+     *
+     * @param expectedCapabilities that must be provided by configNetconfConnector
+     * @param idForReporting
+     * @return service if capabilities are present, otherwise absent value
+     */
+    private NetconfOperationService getOperationService(Set<String> expectedCapabilities, String idForReporting) throws NotEnoughCapabilitiesException {
+        NetconfOperationService serviceCandidate;
+        try {
+            serviceCandidate = configNetconfConnector.createService(idForReporting);
+        } catch(RuntimeException e) {
+            throw new NotEnoughCapabilitiesException("Netconf service not stable for " + idForReporting, e);
         }
-        if (latestCapabilities == null) {
-            logger.error("Could not connect to the server in {} ms", configuration.netconfCapabilitiesWaitTimeoutMs);
-            throw new RuntimeException("Could not connect to netconf server");
+        Set<String> notFoundDiff = computeNotFoundCapabilities(expectedCapabilities, serviceCandidate);
+        if (notFoundDiff.isEmpty()) {
+            return serviceCandidate;
+        } else {
+            serviceCandidate.close();
+            logger.trace("Netconf server did not provide required capabilities for {} " +
+                            "Expected but not found: {}, all expected {}, current {}",
+                    idForReporting, notFoundDiff, expectedCapabilities, serviceCandidate.getCapabilities()
+            );
+            throw new NotEnoughCapabilitiesException("Not enough capabilities for " + idForReporting + ". Expected but not found: " + notFoundDiff);
         }
-        Set<String> allNotFound = computeNotFoundCapabilities(expectedCaps, latestCapabilities);
-        logger.error("Netconf server did not provide required capabilities. Expected but not found: {}, all expected {}, current {}",
-                allNotFound, expectedCaps, latestCapabilities);
-        throw new RuntimeException("Netconf server did not provide required capabilities. Expected but not found:" + allNotFound);
     }
 
-    private static Set<String> computeNotFoundCapabilities(Set<String> expectedCaps, Set<String> latestCapabilities) {
-        Set<String> allNotFound = new HashSet<>(expectedCaps);
-        allNotFound.removeAll(latestCapabilities);
+    private static Set<String> computeNotFoundCapabilities(Set<String> expectedCapabilities, NetconfOperationService serviceCandidate) {
+        Collection<String> actual = Collections2.transform(serviceCandidate.getCapabilities(), new Function<Capability, String>() {
+            @Override
+            public String apply(Capability input) {
+                return input.getCapabilityUri();
+            }
+        });
+        Set<String> allNotFound = new HashSet<>(expectedCapabilities);
+        allNotFound.removeAll(actual);
         return allNotFound;
     }
 
 
+
+    private void sleep() {
+        try {
+            Thread.sleep(100);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new IllegalStateException(e);
+        }
+    }
+
     /**
      * Sends two RPCs to the netconf server: edit-config and commit.
      *
      * @param configSnapshotHolder
-     * @param netconfClient
      * @throws ConflictingVersionException if commit fails on optimistic lock failure inside of config-manager
      * @throws java.lang.RuntimeException  if edit-config or commit fails otherwise
      */
-    private synchronized EditAndCommitResponse pushLastConfig(ConfigSnapshotHolder configSnapshotHolder, NetconfClient netconfClient)
+    private synchronized EditAndCommitResponse pushConfig(ConfigSnapshotHolder configSnapshotHolder, NetconfOperationService operationService)
             throws ConflictingVersionException {
 
         Element xmlToBePersisted;
@@ -177,56 +186,64 @@ public class ConfigPusher {
             throw new IllegalStateException("Cannot parse " + configSnapshotHolder);
         }
         logger.trace("Pushing last configuration to netconf: {}", configSnapshotHolder);
-
+        Stopwatch stopwatch = new Stopwatch().start();
         NetconfMessage editConfigMessage = createEditConfigMessage(xmlToBePersisted);
 
-        // sending message to netconf
-        NetconfMessage editResponseMessage;
-        try {
-            editResponseMessage = sendRequestGetResponseCheckIsOK(editConfigMessage, netconfClient);
-        } catch (IOException e) {
-            throw new IllegalStateException("Edit-config failed on " + configSnapshotHolder, e);
-        }
+        Document editResponseMessage = sendRequestGetResponseCheckIsOK(editConfigMessage, operationService,
+                "edit-config", configSnapshotHolder.toString());
 
-        // commit
-        NetconfMessage commitResponseMessage;
-        try {
-            commitResponseMessage = sendRequestGetResponseCheckIsOK(getCommitMessage(), netconfClient);
-        } catch (IOException e) {
-            throw new IllegalStateException("Edit commit succeeded, but commit failed on " + configSnapshotHolder, e);
-        }
+        Document commitResponseMessage = sendRequestGetResponseCheckIsOK(getCommitMessage(), operationService,
+                "commit", configSnapshotHolder.toString());
 
         if (logger.isTraceEnabled()) {
             StringBuilder response = new StringBuilder("editConfig response = {");
-            response.append(XmlUtil.toString(editResponseMessage.getDocument()));
+            response.append(XmlUtil.toString(editResponseMessage));
             response.append("}");
             response.append("commit response = {");
-            response.append(XmlUtil.toString(commitResponseMessage.getDocument()));
+            response.append(XmlUtil.toString(commitResponseMessage));
             response.append("}");
             logger.trace("Last configuration loaded successfully");
             logger.trace("Detailed message {}", response);
+            logger.trace("Total time spent {} ms", stopwatch.elapsed(TimeUnit.MILLISECONDS));
         }
         return new EditAndCommitResponse(editResponseMessage, commitResponseMessage);
     }
 
+    private NetconfOperation findOperation(NetconfMessage request, NetconfOperationService operationService) {
+        TreeMap<HandlingPriority, NetconfOperation> allOperations = new TreeMap<>();
+        Set<NetconfOperation> netconfOperations = operationService.getNetconfOperations();
+        if (netconfOperations.isEmpty()) {
+            throw new IllegalStateException("Possible code error: no config operations");
+        }
+        for (NetconfOperation netconfOperation : netconfOperations) {
+            HandlingPriority handlingPriority = netconfOperation.canHandle(request.getDocument());
+            allOperations.put(handlingPriority, netconfOperation);
+        }
+        Entry<HandlingPriority, NetconfOperation> highestEntry = allOperations.lastEntry();
+        if (highestEntry.getKey().isCannotHandle()) {
+            throw new IllegalStateException("Possible code error: operation with highest priority is CANNOT_HANDLE");
+        }
+        return highestEntry.getValue();
+    }
+
+    private Document sendRequestGetResponseCheckIsOK(NetconfMessage request, NetconfOperationService operationService,
+                                                     String operationNameForReporting, String configIdForReporting)
+            throws ConflictingVersionException {
 
-    private NetconfMessage sendRequestGetResponseCheckIsOK(NetconfMessage request, NetconfClient netconfClient)
-            throws ConflictingVersionException, IOException {
+        NetconfOperation operation = findOperation(request, operationService);
+        Document response;
         try {
-            NetconfMessage netconfMessage = netconfClient.sendMessage(request,
-                    configuration.netconfSendMessageMaxAttempts, configuration.netconfSendMessageDelayMs);
-            NetconfUtil.checkIsMessageOk(netconfMessage);
-            return netconfMessage;
-        } catch(ConflictingVersionException e) {
-            logger.trace("conflicting version detected: {}", e.toString());
-            throw e;
-        } catch (RuntimeException | ExecutionException | InterruptedException | TimeoutException e) { // TODO: change NetconfClient#sendMessage to throw checked exceptions
-            logger.debug("Error while executing netconf transaction {} to {}", request, netconfClient, e);
-            throw new IOException("Failed to execute netconf transaction", e);
+            response = operation.handle(request.getDocument(), NetconfOperationChainedExecution.EXECUTION_TERMINATION_POINT);
+        } catch (NetconfDocumentedException | RuntimeException e) {
+            if (e instanceof NetconfDocumentedException && e.getCause() instanceof ConflictingVersionException) {
+                throw (ConflictingVersionException) e.getCause();
+            }
+            throw new IllegalStateException("Failed to send " + operationNameForReporting +
+                    " for configuration " + configIdForReporting, e);
         }
+        return NetconfUtil.checkIsMessageOk(response);
     }
 
-
     // load editConfig.xml template, populate /rpc/edit-config/config with parameter
     private static NetconfMessage createEditConfigMessage(Element dataElement) {
         String editConfigResourcePath = "/netconfOp/editConfig.xml";
@@ -246,7 +263,7 @@ public class ConfigPusher {
             return new NetconfMessage(doc);
         } catch (IOException | SAXException e) {
             // error reading the xml file bundled into the jar
-            throw new RuntimeException("Error while opening local resource " + editConfigResourcePath, e);
+            throw new IllegalStateException("Error while opening local resource " + editConfigResourcePath, e);
         }
     }
 
@@ -257,23 +274,23 @@ public class ConfigPusher {
             return new NetconfMessage(XmlUtil.readXmlToDocument(stream));
         } catch (SAXException | IOException e) {
             // error reading the xml file bundled into the jar
-            throw new RuntimeException("Error while opening local resource " + resource, e);
+            throw new IllegalStateException("Error while opening local resource " + resource, e);
         }
     }
 
     static class EditAndCommitResponse {
-        private final NetconfMessage editResponse, commitResponse;
+        private final Document editResponse, commitResponse;
 
-        EditAndCommitResponse(NetconfMessage editResponse, NetconfMessage commitResponse) {
+        EditAndCommitResponse(Document editResponse, Document commitResponse) {
             this.editResponse = editResponse;
             this.commitResponse = commitResponse;
         }
 
-        public NetconfMessage getEditResponse() {
+        public Document getEditResponse() {
             return editResponse;
         }
 
-        public NetconfMessage getCommitResponse() {
+        public Document getCommitResponse() {
             return commitResponse;
         }
 
@@ -285,32 +302,4 @@ public class ConfigPusher {
                     '}';
         }
     }
-
-
-    static class EditAndCommitResponseWithRetries {
-        private final EditAndCommitResponse editAndCommitResponse;
-        private final int retries;
-
-        EditAndCommitResponseWithRetries(EditAndCommitResponse editAndCommitResponse, int retries) {
-            this.editAndCommitResponse = editAndCommitResponse;
-            this.retries = retries;
-        }
-
-        public int getRetries() {
-            return retries;
-        }
-
-        public EditAndCommitResponse getEditAndCommitResponse() {
-            return editAndCommitResponse;
-        }
-
-        @Override
-        public String toString() {
-            return "EditAndCommitResponseWithRetries{" +
-                    "editAndCommitResponse=" + editAndCommitResponse +
-                    ", retries=" + retries +
-                    '}';
-        }
-    }
-
 }
diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherConfiguration.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherConfiguration.java
deleted file mode 100644 (file)
index aa189f0..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.netconf.persist.impl;
-
-import io.netty.channel.EventLoopGroup;
-
-import javax.annotation.concurrent.Immutable;
-import java.net.InetSocketAddress;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Configuration properties for ConfigPusher. Contains delays and timeouts for netconf
- * connection establishment, netconf capabilities stabilization and configuration push.
- */
-@Immutable
-public final class ConfigPusherConfiguration {
-
-    public static final long DEFAULT_CONNECTION_ATTEMPT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5);
-    public static final int DEFAULT_CONNECTION_ATTEMPT_DELAY_MS = 5000;
-
-    public static final int DEFAULT_NETCONF_SEND_MESSAGE_MAX_ATTEMPTS = 20;
-    public static final int DEFAULT_NETCONF_SEND_MESSAGE_DELAY_MS = 1000;
-
-    public static final long DEFAULT_NETCONF_CAPABILITIES_WAIT_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(2);
-
-    public static final int DEFAULT_NETCONF_PUSH_CONFIG_ATTEMPTS = 30;
-    public static final long DEFAULT_NETCONF_PUSH_CONFIG_DELAY_MS = TimeUnit.MINUTES.toMillis(1);
-
-    final InetSocketAddress netconfAddress;
-    final EventLoopGroup eventLoopGroup;
-
-    /**
-     * Total time to wait for capability stabilization
-     */
-    final long netconfCapabilitiesWaitTimeoutMs;
-
-    /**
-     * Delay between message send attempts
-     */
-    final int netconfSendMessageDelayMs;
-    /**
-     * Total number attempts to send a message
-     */
-    final int netconfSendMessageMaxAttempts;
-
-    /**
-     * Delay between connection establishment attempts
-     */
-    final int connectionAttemptDelayMs;
-    /**
-     * Total number of attempts to perform connection establishment
-     */
-    final long connectionAttemptTimeoutMs;
-
-    /**
-     * Total number of attempts to push configuration to netconf
-     */
-    final int netconfPushConfigAttempts;
-    /**
-     * Delay between configuration push attempts
-     */
-    final long netconfPushConfigDelayMs;
-
-    ConfigPusherConfiguration(InetSocketAddress netconfAddress, long netconfCapabilitiesWaitTimeoutMs,
-            int netconfSendMessageDelayMs, int netconfSendMessageMaxAttempts, int connectionAttemptDelayMs,
-            long connectionAttemptTimeoutMs, EventLoopGroup eventLoopGroup, int netconfPushConfigAttempts,
-            long netconfPushConfigDelayMs) {
-        this.netconfAddress = netconfAddress;
-        this.netconfCapabilitiesWaitTimeoutMs = netconfCapabilitiesWaitTimeoutMs;
-        this.netconfSendMessageDelayMs = netconfSendMessageDelayMs;
-        this.netconfSendMessageMaxAttempts = netconfSendMessageMaxAttempts;
-        this.connectionAttemptDelayMs = connectionAttemptDelayMs;
-        this.connectionAttemptTimeoutMs = connectionAttemptTimeoutMs;
-        this.eventLoopGroup = eventLoopGroup;
-        this.netconfPushConfigAttempts = netconfPushConfigAttempts;
-        this.netconfPushConfigDelayMs = netconfPushConfigDelayMs;
-    }
-}
diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherConfigurationBuilder.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherConfigurationBuilder.java
deleted file mode 100644 (file)
index c26dc8d..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.netconf.persist.impl;
-
-import io.netty.channel.EventLoopGroup;
-
-import java.net.InetSocketAddress;
-
-public class ConfigPusherConfigurationBuilder {
-    InetSocketAddress netconfAddress;
-    EventLoopGroup eventLoopGroup;
-
-    long netconfCapabilitiesWaitTimeoutMs = ConfigPusherConfiguration.DEFAULT_NETCONF_CAPABILITIES_WAIT_TIMEOUT_MS;
-    int netconfSendMessageDelayMs = ConfigPusherConfiguration.DEFAULT_NETCONF_SEND_MESSAGE_DELAY_MS;
-    int netconfSendMessageMaxAttempts = ConfigPusherConfiguration.DEFAULT_NETCONF_SEND_MESSAGE_MAX_ATTEMPTS;
-    int connectionAttemptDelayMs = ConfigPusherConfiguration.DEFAULT_CONNECTION_ATTEMPT_DELAY_MS;
-    long connectionAttemptTimeoutMs = ConfigPusherConfiguration.DEFAULT_CONNECTION_ATTEMPT_TIMEOUT_MS;
-    int netconfPushConfigAttempts = ConfigPusherConfiguration.DEFAULT_NETCONF_PUSH_CONFIG_ATTEMPTS;
-    long netconfPushConfigDelayMs = ConfigPusherConfiguration.DEFAULT_NETCONF_PUSH_CONFIG_DELAY_MS;
-
-    private ConfigPusherConfigurationBuilder() {
-    }
-
-    public static ConfigPusherConfigurationBuilder aConfigPusherConfiguration() {
-        return new ConfigPusherConfigurationBuilder();
-    }
-
-    public ConfigPusherConfigurationBuilder withNetconfAddress(InetSocketAddress netconfAddress) {
-        this.netconfAddress = netconfAddress;
-        return this;
-    }
-
-    public ConfigPusherConfigurationBuilder withNetconfCapabilitiesWaitTimeoutMs(long netconfCapabilitiesWaitTimeoutMs) {
-        this.netconfCapabilitiesWaitTimeoutMs = netconfCapabilitiesWaitTimeoutMs;
-        return this;
-    }
-
-    public ConfigPusherConfigurationBuilder withNetconfSendMessageDelayMs(int netconfSendMessageDelayMs) {
-        this.netconfSendMessageDelayMs = netconfSendMessageDelayMs;
-        return this;
-    }
-
-    public ConfigPusherConfigurationBuilder withNetconfSendMessageMaxAttempts(int netconfSendMessageMaxAttempts) {
-        this.netconfSendMessageMaxAttempts = netconfSendMessageMaxAttempts;
-        return this;
-    }
-
-    public ConfigPusherConfigurationBuilder withConnectionAttemptDelayMs(int connectionAttemptDelayMs) {
-        this.connectionAttemptDelayMs = connectionAttemptDelayMs;
-        return this;
-    }
-
-    public ConfigPusherConfigurationBuilder withConnectionAttemptTimeoutMs(long connectionAttemptTimeoutMs) {
-        this.connectionAttemptTimeoutMs = connectionAttemptTimeoutMs;
-        return this;
-    }
-
-    public ConfigPusherConfigurationBuilder withEventLoopGroup(EventLoopGroup eventLoopGroup) {
-        this.eventLoopGroup = eventLoopGroup;
-        return this;
-    }
-
-    public ConfigPusherConfigurationBuilder withNetconfPushConfigAttempts(int netconfPushConfigAttempts) {
-        this.netconfPushConfigAttempts = netconfPushConfigAttempts;
-        return this;
-    }
-
-    public ConfigPusherConfigurationBuilder withNetconfPushConfigDelayMs(long netconfPushConfigDelayMs) {
-        this.netconfPushConfigDelayMs = netconfPushConfigDelayMs;
-        return this;
-    }
-
-    public ConfigPusherConfiguration build() {
-        ConfigPusherConfiguration configPusherConfiguration = new ConfigPusherConfiguration(netconfAddress,
-                netconfCapabilitiesWaitTimeoutMs, netconfSendMessageDelayMs, netconfSendMessageMaxAttempts,
-                connectionAttemptDelayMs, connectionAttemptTimeoutMs, eventLoopGroup, netconfPushConfigAttempts,
-                netconfPushConfigDelayMs);
-        return configPusherConfiguration;
-    }
-}
index c1cad4a8ddaba3750b70c3530d7f8324afde8a95..31a4f080368515c075872173e9ba066c830f5e0e 100644 (file)
@@ -37,12 +37,12 @@ import java.util.ListIterator;
  netconf.config.persister.1.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.xml.XmlDirectoryStorageAdapter
  netconf.config.persister.1.properties.fileStorage=configuration/initial/
 
- netconf.config.persister.2.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter
+ netconf.config.persister.2.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.xml.XmlFileStorageAdapter
  netconf.config.persister.2.readonly=true
- netconf.config.persister.2.properties.fileStorage=configuration/current/controller.config.1.txt
+ netconf.config.persister.2.properties.fileStorage=configuration/current/controller.config.1.xml
 
- netconf.config.persister.3.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter
- netconf.config.persister.3.properties.fileStorage=configuration/current/controller.config.2.txt
+ netconf.config.persister.3.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.xml.XmlFileStorageAdapter
+ netconf.config.persister.3.properties.fileStorage=configuration/current/controller.config.2.xml
  netconf.config.persister.3.properties.numberOfBackups=3
 
  </pre>
diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/Util.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/Util.java
deleted file mode 100644 (file)
index 322a9b7..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.netconf.persist.impl;
-
-import org.opendaylight.controller.netconf.client.NetconfClient;
-import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Set;
-
-public final class Util {
-    private static final Logger logger = LoggerFactory.getLogger(Util.class);
-
-
-    public static boolean isSubset(NetconfClient netconfClient, Set<String> expectedCaps) {
-        return isSubset(netconfClient.getCapabilities(), expectedCaps);
-
-    }
-
-    private static boolean isSubset(Set<String> currentCapabilities, Set<String> expectedCaps) {
-        for (String exCap : expectedCaps) {
-            if (currentCapabilities.contains(exCap) == false)
-                return false;
-        }
-        return true;
-    }
-
-    public static void closeClientAndDispatcher(NetconfClient client) {
-        NetconfClientDispatcher dispatcher = client.getNetconfClientDispatcher();
-        Exception fromClient = null;
-        try {
-            client.close();
-        } catch (Exception e) {
-            fromClient = e;
-        } finally {
-            try {
-                dispatcher.close();
-            } catch (Exception e) {
-                if (fromClient != null) {
-                    e.addSuppressed(fromClient);
-                }
-                throw new RuntimeException("Error closing temporary client ", e);
-            }
-        }
-    }
-}
index 1246c78fbe21539589a23d641e58f4a1846da5c8..76afe8eb39b95f4500fd847cfe5df9a297c656e7 100644 (file)
 package org.opendaylight.controller.netconf.persist.impl.osgi;
 
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Optional;
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.nio.NioEventLoopGroup;
+import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
 import org.opendaylight.controller.netconf.persist.impl.ConfigPersisterNotificationHandler;
 import org.opendaylight.controller.netconf.persist.impl.ConfigPusher;
-import org.opendaylight.controller.netconf.persist.impl.ConfigPusherConfiguration;
-import org.opendaylight.controller.netconf.persist.impl.ConfigPusherConfigurationBuilder;
 import org.opendaylight.controller.netconf.persist.impl.PersisterAggregator;
-import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.management.MBeanServer;
 import java.lang.management.ManagementFactory;
-import java.net.InetSocketAddress;
-import java.util.concurrent.ThreadFactory;
-import java.util.regex.Pattern;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 public class ConfigPersisterActivator implements BundleActivator {
 
     private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterActivator.class);
 
-    public static final String IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX = "ignoredMissingCapabilityRegex";
-
-    public static final String MAX_WAIT_FOR_CAPABILITIES_MILLIS = "maxWaitForCapabilitiesMillis";
+    public static final String MAX_WAIT_FOR_CAPABILITIES_MILLIS_PROPERTY = "maxWaitForCapabilitiesMillis";
+    private static final long MAX_WAIT_FOR_CAPABILITIES_MILLIS_DEFAULT = TimeUnit.MINUTES.toMillis(2);
+    public static final String CONFLICTING_VERSION_TIMEOUT_MILLIS_PROPERTY = "conflictingVersionTimeoutMillis";
+    private static final long CONFLICTING_VERSION_TIMEOUT_MILLIS_DEFAULT = TimeUnit.SECONDS.toMillis(30);
 
     public static final String NETCONF_CONFIG_PERSISTER = "netconf.config.persister";
 
     public static final String STORAGE_ADAPTER_CLASS_PROP_SUFFIX = "storageAdapterClass";
 
-    public static final String DEFAULT_IGNORED_REGEX = "^urn:ietf:params:xml:ns:netconf:base:1.0";
 
-    private final MBeanServer platformMBeanServer;
+    private static final MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
 
-    private final Optional<ConfigPusherConfiguration> initialConfigForPusher;
-    private volatile ConfigPersisterNotificationHandler jmxNotificationHandler;
-    private Thread initializationThread;
-    private ThreadFactory initializationThreadFactory;
-    private EventLoopGroup nettyThreadGroup;
-    private PersisterAggregator persisterAggregator;
+    private List<AutoCloseable> autoCloseables;
 
-    public ConfigPersisterActivator() {
-        this(new ThreadFactory() {
-            @Override
-            public Thread newThread(Runnable initializationRunnable) {
-                return new Thread(initializationRunnable, "ConfigPersister-registrator");
-            }
-        }, ManagementFactory.getPlatformMBeanServer(), null);
-    }
-
-    @VisibleForTesting
-    protected ConfigPersisterActivator(ThreadFactory threadFactory, MBeanServer mBeanServer,
-            ConfigPusherConfiguration initialConfigForPusher) {
-        this.initializationThreadFactory = threadFactory;
-        this.platformMBeanServer = mBeanServer;
-        this.initialConfigForPusher = Optional.fromNullable(initialConfigForPusher);
-    }
 
     @Override
     public void start(final BundleContext context) throws Exception {
         logger.debug("ConfigPersister starting");
-
+        autoCloseables = new ArrayList<>();
         PropertiesProviderBaseImpl propertiesProvider = new PropertiesProviderBaseImpl(context);
 
-        final Pattern ignoredMissingCapabilityRegex = getIgnoredCapabilitiesProperty(propertiesProvider);
 
-        persisterAggregator = PersisterAggregator.createFromProperties(propertiesProvider);
+        final PersisterAggregator persisterAggregator = PersisterAggregator.createFromProperties(propertiesProvider);
+        autoCloseables.add(persisterAggregator);
+        final long maxWaitForCapabilitiesMillis = getMaxWaitForCapabilitiesMillis(propertiesProvider);
+        final List<ConfigSnapshotHolder> configs = persisterAggregator.loadLastConfigs();
+        final long conflictingVersionTimeoutMillis = getConflictingVersionTimeoutMillis(propertiesProvider);
+        logger.trace("Following configs will be pushed: {}", configs);
+        ServiceTrackerCustomizer<NetconfOperationServiceFactory, NetconfOperationServiceFactory> configNetconfCustomizer = new ServiceTrackerCustomizer<NetconfOperationServiceFactory, NetconfOperationServiceFactory>() {
+            @Override
+            public NetconfOperationServiceFactory addingService(ServiceReference<NetconfOperationServiceFactory> reference) {
+                NetconfOperationServiceFactory service = reference.getBundle().getBundleContext().getService(reference);
+                final ConfigPusher configPusher = new ConfigPusher(service, maxWaitForCapabilitiesMillis, conflictingVersionTimeoutMillis);
+                logger.debug("Configuration Persister got %s", service);
+                final Thread pushingThread = new Thread(new Runnable() {
+                    @Override
+                    public void run() {
+                        configPusher.pushConfigs(configs);
+                        logger.info("Configuration Persister initialization completed.");
+                        ConfigPersisterNotificationHandler jmxNotificationHandler = new ConfigPersisterNotificationHandler(platformMBeanServer, persisterAggregator);
+                        synchronized (ConfigPersisterActivator.this) {
+                            autoCloseables.add(jmxNotificationHandler);
+                        }
+                    }
+                }, "config-pusher");
+                synchronized (ConfigPersisterActivator.this){
+                    autoCloseables.add(new AutoCloseable() {
+                        @Override
+                        public void close() throws Exception {
+                            pushingThread.interrupt();
+                        }
+                    });
+                }
+                pushingThread.start();
+                return service;
+            }
 
-        final ConfigPusher configPusher = new ConfigPusher(getConfigurationForPusher(context, propertiesProvider));
+            @Override
+            public void modifiedService(ServiceReference<NetconfOperationServiceFactory> reference, NetconfOperationServiceFactory service) {
+            }
 
-        // offload initialization to another thread in order to stop blocking activator
-        Runnable initializationRunnable = new Runnable() {
             @Override
-            public void run() {
-                try {
-                    configPusher.pushConfigs(persisterAggregator.loadLastConfigs());
-                    jmxNotificationHandler = new ConfigPersisterNotificationHandler(platformMBeanServer, persisterAggregator,
-                            ignoredMissingCapabilityRegex);
-                } catch (InterruptedException e) {
-                    Thread.currentThread().interrupt();
-                    logger.error("Interrupted while waiting for netconf connection");
-                    // uncaught exception handler will deal with this failure
-                    throw new RuntimeException("Interrupted while waiting for netconf connection", e);
-                }
-                logger.info("Configuration Persister initialization completed.");
+            public void removedService(ServiceReference<NetconfOperationServiceFactory> reference, NetconfOperationServiceFactory service) {
             }
         };
 
-        initializationThread = initializationThreadFactory.newThread(initializationRunnable);
-        initializationThread.start();
-    }
-
-    private Pattern getIgnoredCapabilitiesProperty(PropertiesProviderBaseImpl propertiesProvider) {
-        String regexProperty = propertiesProvider.getProperty(IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX);
-        String regex;
-        if (regexProperty != null) {
-            regex = regexProperty;
-        } else {
-            regex = DEFAULT_IGNORED_REGEX;
-        }
-        return Pattern.compile(regex);
-    }
+        Filter filter = context.createFilter(getFilterString());
 
-    private Optional<Long> getMaxWaitForCapabilitiesProperty(PropertiesProviderBaseImpl propertiesProvider) {
-        String timeoutProperty = propertiesProvider.getProperty(MAX_WAIT_FOR_CAPABILITIES_MILLIS);
-        return Optional.fromNullable(timeoutProperty == null ? null : Long.valueOf(timeoutProperty));
+        ServiceTracker<NetconfOperationServiceFactory, NetconfOperationServiceFactory> tracker =
+                new ServiceTracker<>(context, filter, configNetconfCustomizer);
+        tracker.open();
     }
 
-    private ConfigPusherConfiguration getConfigurationForPusher(BundleContext context,
-            PropertiesProviderBaseImpl propertiesProvider) {
-
-        // If configuration was injected via constructor, use it
-        if(initialConfigForPusher.isPresent())
-            return initialConfigForPusher.get();
-
-        Optional<Long> maxWaitForCapabilitiesMillis = getMaxWaitForCapabilitiesProperty(propertiesProvider);
-        final InetSocketAddress address = NetconfConfigUtil.extractTCPNetconfAddress(context,
-                "Netconf is not configured, persister is not operational", true);
-
-        nettyThreadGroup = new NioEventLoopGroup();
 
-        ConfigPusherConfigurationBuilder configPusherConfigurationBuilder = ConfigPusherConfigurationBuilder.aConfigPusherConfiguration();
+    @VisibleForTesting
+    public static String getFilterString() {
+        return "(&" +
+                "(" + Constants.OBJECTCLASS + "=" + NetconfOperationServiceFactory.class.getName() + ")" +
+                "(name" + "=" + "config-netconf-connector" + ")" +
+                ")";
+    }
 
-        if(maxWaitForCapabilitiesMillis.isPresent())
-            configPusherConfigurationBuilder.withNetconfCapabilitiesWaitTimeoutMs(maxWaitForCapabilitiesMillis.get());
+    private long getConflictingVersionTimeoutMillis(PropertiesProviderBaseImpl propertiesProvider) {
+        String timeoutProperty = propertiesProvider.getProperty(CONFLICTING_VERSION_TIMEOUT_MILLIS_PROPERTY);
+        return timeoutProperty == null ? CONFLICTING_VERSION_TIMEOUT_MILLIS_DEFAULT : Long.valueOf(timeoutProperty);
+    }
 
-        return configPusherConfigurationBuilder
-                .withEventLoopGroup(nettyThreadGroup)
-                .withNetconfAddress(address)
-                .build();
+    private long getMaxWaitForCapabilitiesMillis(PropertiesProviderBaseImpl propertiesProvider) {
+        String timeoutProperty = propertiesProvider.getProperty(MAX_WAIT_FOR_CAPABILITIES_MILLIS_PROPERTY);
+        return timeoutProperty == null ? MAX_WAIT_FOR_CAPABILITIES_MILLIS_DEFAULT : Long.valueOf(timeoutProperty);
     }
 
     @Override
-    public void stop(BundleContext context) throws Exception {
-        initializationThread.interrupt();
-        if (jmxNotificationHandler != null) {
-            jmxNotificationHandler.close();
+    public synchronized void stop(BundleContext context) throws Exception {
+        Exception lastException = null;
+        for (AutoCloseable autoCloseable : autoCloseables) {
+            try {
+                autoCloseable.close();
+            } catch (Exception e) {
+                if (lastException == null) {
+                    lastException = e;
+                } else {
+                    lastException.addSuppressed(e);
+                }
+            }
+        }
+        if (lastException != null) {
+            throw lastException;
         }
-        if(nettyThreadGroup!=null)
-            nettyThreadGroup.shutdownGracefully();
-        persisterAggregator.close();
     }
 }
index d91712f3475fe2dde61692368a85e813b67c048c..7e9d80abc0a8814f655fdf964c946f98fe956058 100644 (file)
@@ -10,44 +10,30 @@ package org.opendaylight.controller.netconf.persist.impl;
 import com.google.common.collect.Sets;
 import org.apache.commons.io.IOUtils;
 import org.junit.Test;
-import org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.w3c.dom.Element;
 
 import java.io.IOException;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
-import java.util.regex.Pattern;
 
 import static org.junit.Assert.assertEquals;
 
 public class CapabilityStrippingConfigSnapshotHolderTest {
 
     @Test
-    public void  testCapabilityStripping() throws Exception {
+    public void testCapabilityStripping() throws Exception {
         Set<String> allCapabilities = readLines("/capabilities-all.txt");
         Set<String> expectedCapabilities = readLines("/capabilities-stripped.txt");
         String snapshotAsString = readToString("/snapshot.xml");
         Element element = XmlUtil.readXmlToElement(snapshotAsString);
-        {
-            CapabilityStrippingConfigSnapshotHolder tested = new CapabilityStrippingConfigSnapshotHolder(
-                    element, allCapabilities, Pattern.compile(
-                    ConfigPersisterActivator.DEFAULT_IGNORED_REGEX
-            ));
-            assertEquals(expectedCapabilities, tested.getCapabilities());
-            assertEquals(Collections.emptySet(), tested.getMissingNamespaces());
-        }
-        {
-            // test regex
-            CapabilityStrippingConfigSnapshotHolder tested = new CapabilityStrippingConfigSnapshotHolder(
-                    element, allCapabilities, Pattern.compile(
-                    "^bar"
-            ));
-            assertEquals(expectedCapabilities, tested.getCapabilities());
-            assertEquals(Sets.newHashSet(ConfigPersisterActivator.DEFAULT_IGNORED_REGEX.substring(1)),
-                    tested.getMissingNamespaces());
-        }
+        CapabilityStrippingConfigSnapshotHolder tested = new CapabilityStrippingConfigSnapshotHolder(
+                element, allCapabilities);
+        assertEquals(expectedCapabilities, tested.getCapabilities());
+
+        Set<String> obsoleteCapabilities = Sets.difference(allCapabilities, expectedCapabilities);
+
+        assertEquals(obsoleteCapabilities, tested.getObsoleteCapabilities());
     }
 
     private Set<String> readLines(String fileName) throws IOException {
index 230c74725d0b3c78160bcfecf8c60b29eb42ccff..493ecd9250262959e78523c4ed1dc49c7d177474 100644 (file)
  */
 package org.opendaylight.controller.netconf.persist.impl.osgi;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
-import java.lang.management.ManagementFactory;
-import java.net.InetSocketAddress;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeoutException;
-
-import javax.management.MBeanServer;
-
+import com.google.common.collect.Sets;
 import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
+import org.junit.Before;
 import org.junit.Test;
-import org.junit.matchers.JUnitMatchers;
 import org.opendaylight.controller.config.api.ConflictingVersionException;
-import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
-import org.opendaylight.controller.netconf.persist.impl.ConfigPusherConfiguration;
-import org.opendaylight.controller.netconf.persist.impl.ConfigPusherConfigurationBuilder;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.mapping.api.Capability;
+import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.persist.impl.osgi.MockedBundleContext.DummyAdapterWithInitialSnapshot;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
 
-import com.google.common.collect.Lists;
-import io.netty.channel.nio.NioEventLoopGroup;
+import javax.management.MBeanServer;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
 
 public class ConfigPersisterTest {
+    private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterTest.class);
 
     private MockedBundleContext ctx;
     private ConfigPersisterActivator configPersisterActivator;
     private static final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
+    private TestingExceptionHandler handler;
 
-    private static final String NETCONF_ADDRESS = "localhost";
-    private static final String NETCONF_PORT = "18383";
-    private static NioEventLoopGroup eventLoopGroup;
 
-    private void setUpContextAndStartPersister(Thread.UncaughtExceptionHandler exHandler, String requiredCapability, ConfigPusherConfiguration configuration)
-            throws Exception {
-        MockedBundleContext.DummyAdapterWithInitialSnapshot.expectedCapability = requiredCapability;
-        ctx = new MockedBundleContext(NETCONF_ADDRESS, NETCONF_PORT);
-        configPersisterActivator = new ConfigPersisterActivator(getThreadFactory(exHandler), mBeanServer,
-                configuration);
+    private void setUpContextAndStartPersister(String requiredCapability) throws Exception {
+        DummyAdapterWithInitialSnapshot.expectedCapability = requiredCapability;
+        ctx = new MockedBundleContext(1000, 1000);
+        configPersisterActivator = new ConfigPersisterActivator();
         configPersisterActivator.start(ctx.getBundleContext());
     }
 
-    @BeforeClass
-    public static void setUp() throws Exception {
-        eventLoopGroup = new NioEventLoopGroup();
+    @Before
+    public void setUp() {
+        handler = new TestingExceptionHandler();
+        Thread.setDefaultUncaughtExceptionHandler(handler);
     }
 
     @After
     public void tearDown() throws Exception {
+        Thread.setDefaultUncaughtExceptionHandler(null);
         configPersisterActivator.stop(ctx.getBundleContext());
     }
 
-    @AfterClass
-    public static void closeNettyGroup() throws Exception {
-        eventLoopGroup.shutdownGracefully();
-    }
-
-    @Test
-    public void testPersisterNetconfNotStarting() throws Exception {
-        final TestingExceptionHandler handler = new TestingExceptionHandler();
-
-        setUpContextAndStartPersister(handler, "cap2", getConfiguration(100, 100).build());
-
-        waitTestToFinish(2000);
-
-        handler.assertException("connect to netconf endpoint", RuntimeException.class,
-                "Could not connect to netconf server");
-    }
-
     @Test
     public void testPersisterNotAllCapabilitiesProvided() throws Exception {
-        final TestingExceptionHandler handler = new TestingExceptionHandler();
-        ConfigPusherConfiguration cfg = getConfiguration(500, 1000)
-                .withNetconfCapabilitiesWaitTimeoutMs(1000).build();
-
-        setUpContextAndStartPersister(handler, "required-cap", cfg);
-
-        try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1")) {
-
-            waitTestToFinish(2500);
+        setUpContextAndStartPersister("required-cap");
+        Thread.sleep(2000);
+        handler.assertException(IllegalStateException.class, "Max wait for capabilities reached.Not enough capabilities " +
+                "for <data><config-snapshot/></data>. Expected but not found: [required-cap]");
 
-            handler.assertException("retrieve required capabilities from netconf endpoint", RuntimeException.class,
-                    "Expected but not found:[required-cap]");
-        }
     }
 
     @Test
-    public void testPersisterNoResponseFromNetconfAfterEdit() throws Exception {
-        final TestingExceptionHandler handler = new TestingExceptionHandler();
-        ConfigPusherConfiguration cfg = getConfigurationWithOnePushAttempt();
-
-        setUpContextAndStartPersister(handler, "cap1", cfg);
-
-        try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1")) {
-
-            waitTestToFinish(3000);
-
-            handler.assertException("receive response from netconf endpoint", IllegalStateException.class,
-                    "Unable to load", TimeoutException.class,
-                    null, 3);
-
-            assertEquals(1 + 2, endpoint.getReceivedMessages().size());
-            assertHelloMessage(endpoint.getReceivedMessages().get(1));
-            assertEditMessage(endpoint.getReceivedMessages().get(2));
-        }
+    public void testPersisterSuccessfulPush() throws Exception {
+        setUpContextAndStartPersister("cap1");
+        NetconfOperationService service = getWorkingService(getOKDocument());
+        doReturn(service).when(ctx.serviceFactory).createService(anyString());
+        Thread.sleep(2000);
+        assertCannotRegisterAsJMXListener_pushWasSuccessful();
     }
 
-    private ConfigPusherConfiguration getConfigurationWithOnePushAttempt() {
-        return getConfiguration(500, 1000)
-                    .withNetconfCapabilitiesWaitTimeoutMs(1000)
-                    .withNetconfPushConfigAttempts(1)
-                    .withNetconfPushConfigDelayMs(100)
-                    .withNetconfSendMessageMaxAttempts(3)
-                    .withNetconfSendMessageDelayMs(500).build();
+    // this means pushing of config was successful
+    public void assertCannotRegisterAsJMXListener_pushWasSuccessful() {
+        handler.assertException(RuntimeException.class, "Cannot register as JMX listener to netconf");
     }
 
-    @Test
-    public void testPersisterSuccessfulPush() throws Exception {
-        final TestingExceptionHandler handler = new TestingExceptionHandler();
-        ConfigPusherConfiguration cfg = getConfigurationForSuccess();
-
-        setUpContextAndStartPersister(handler, "cap1", cfg);
-
-        try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1", MockNetconfEndpoint.okMessage,
-                MockNetconfEndpoint.okMessage)) {
+    public NetconfOperationService getWorkingService(Document document) throws SAXException, IOException, NetconfDocumentedException {
+        NetconfOperationService service = mock(NetconfOperationService.class);
+        Capability capability = mock(Capability.class);
+        doReturn(Sets.newHashSet(capability)).when(service).getCapabilities();
+        doReturn("cap1").when(capability).getCapabilityUri();
 
-            waitTestToFinish(4000);
 
-            handler.assertException("register as JMX listener", RuntimeException.class,
-                    "Cannot register as JMX listener to netconf");
-
-            assertEquals(1 + 3, endpoint.getReceivedMessages().size());
-            assertCommitMessage(endpoint.getReceivedMessages().get(3));
-        }
+        NetconfOperation mockedOperation = mock(NetconfOperation.class);
+        doReturn(Sets.newHashSet(mockedOperation)).when(service).getNetconfOperations();
+        doReturn(HandlingPriority.getHandlingPriority(1)).when(mockedOperation).canHandle(any(Document.class));
+        doReturn(document).when(mockedOperation).handle(any(Document.class), any(NetconfOperationChainedExecution.class));
+        doNothing().when(service).close();
+        return service;
     }
 
-    private ConfigPusherConfiguration getConfigurationForSuccess() {
-        return getConfiguration(500, 1000)
-                    .withNetconfCapabilitiesWaitTimeoutMs(1000)
-                    .withNetconfPushConfigAttempts(3)
-                    .withNetconfPushConfigDelayMs(100)
-                    .withNetconfSendMessageMaxAttempts(3)
-                    .withNetconfSendMessageDelayMs(500).build();
+    private Document getOKDocument() throws SAXException, IOException {
+        return XmlUtil.readXmlToDocument(
+                "<rpc-reply message-id=\"1\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
+                        "<ok/>\n" +
+                        "</rpc-reply>"
+        );
     }
 
-    @Test
-    public void testPersisterConflictingVersionException() throws Exception {
-        final TestingExceptionHandler handler = new TestingExceptionHandler();
-        ConfigPusherConfiguration cfg = getConfigurationWithOnePushAttempt();
-
-        setUpContextAndStartPersister(handler, "cap1", cfg);
-
-        try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1", MockNetconfEndpoint.okMessage,
-                MockNetconfEndpoint.conflictingVersionErrorMessage); DefaultCommitNotificationProducer jMXNotifier = startJMXCommitNotifier();) {
-
-            Thread.sleep(4000);
-
-            handler.assertException("register as JMX listener", IllegalStateException.class,
-                    "Maximum attempt count has been reached for pushing", ConflictingVersionException.class, "Optimistic lock failed", 1);
-
-            assertEquals(1 + 3, endpoint.getReceivedMessages().size());
-            assertCommitMessage(endpoint.getReceivedMessages().get(3));
-        }
-    }
 
     @Test
-    public void testPersisterConflictingVersionExceptionThenSuccess() throws Exception {
-        final TestingExceptionHandler handler = new TestingExceptionHandler();
-        ConfigPusherConfiguration cfg = getConfigurationForSuccess();
-
-        setUpContextAndStartPersister(handler, "cap1", cfg);
-
-        MockNetconfEndpoint.MessageSequence conflictingMessageSequence = new MockNetconfEndpoint.MessageSequence(
-                MockNetconfEndpoint.okMessage, MockNetconfEndpoint.conflictingVersionErrorMessage);
-        MockNetconfEndpoint.MessageSequence okMessageSequence = new MockNetconfEndpoint.MessageSequence(
-                MockNetconfEndpoint.okMessage, MockNetconfEndpoint.okMessage);
-
-        try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1",
-                Lists.newArrayList(conflictingMessageSequence, okMessageSequence));
-             DefaultCommitNotificationProducer jMXNotifier = startJMXCommitNotifier()) {
-
-            Thread.sleep(4000);
-
-            handler.assertNoException();
-
-            assertEquals(1 + 3/*Hello + Edit + Commit*/ + 3/*Hello + Edit + Commit*/, endpoint.getReceivedMessages().size());
-            assertCommitMessage(endpoint.getReceivedMessages().get(6));
+    public void testPersisterConflictingVersionException() throws Exception {
+        setUpContextAndStartPersister("cap1");
+
+        doReturn(getConflictingService()).when(ctx.serviceFactory).createService(anyString());
+        Thread.sleep(2000);
+        handler.assertException(IllegalStateException.class, "Max wait for conflicting version stabilization timeout");
+    }
+
+    private NetconfOperationService getConflictingService() throws Exception {
+        NetconfOperationService service =  getWorkingService(getOKDocument());
+        ConflictingVersionException cve = new ConflictingVersionException("");
+        try {
+            NetconfDocumentedException.wrap(cve);
+            throw new AssertionError("Should throw an exception");
+        }catch(NetconfDocumentedException e) {
+            NetconfOperation mockedOperation = service.getNetconfOperations().iterator().next();
+            doThrow(e).when(mockedOperation).handle(any(Document.class), any(NetconfOperationChainedExecution.class));
+            return service;
         }
     }
 
     @Test
-    public void testPersisterSuccessfulPushAndSuccessfulJMXRegistration() throws Exception {
-        final TestingExceptionHandler handler = new TestingExceptionHandler();
-        ConfigPusherConfiguration cfg = getConfigurationForSuccess();
-
-        setUpContextAndStartPersister(handler, "cap1", cfg);
-
-        try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1", MockNetconfEndpoint.okMessage,
-                MockNetconfEndpoint.okMessage); DefaultCommitNotificationProducer jMXNotifier = startJMXCommitNotifier()) {
-
-            Thread.sleep(2000);
-
-            handler.assertNoException();
-
-            assertEquals(1 + 3, endpoint.getReceivedMessages().size());
-        }
+    public void testSuccessConflictingVersionException() throws Exception {
+        setUpContextAndStartPersister("cap1");
+        doReturn(getConflictingService()).when(ctx.serviceFactory).createService(anyString());
+        Thread.sleep(500);
+        // working service:
+        logger.info("Switching to working service **");
+        doReturn(getWorkingService(getOKDocument())).when(ctx.serviceFactory).createService(anyString());
+        Thread.sleep(1000);
+        assertCannotRegisterAsJMXListener_pushWasSuccessful();
     }
 
-    private ConfigPusherConfigurationBuilder getConfiguration(int connectionAttemptDelayMs, int connectionAttemptTimeoutMs) {
-        return ConfigPusherConfigurationBuilder.aConfigPusherConfiguration()
-                .withEventLoopGroup(eventLoopGroup)
-                .withConnectionAttemptDelayMs(connectionAttemptDelayMs)
-                .withConnectionAttemptTimeoutMs(connectionAttemptTimeoutMs)
-                .withNetconfCapabilitiesWaitTimeoutMs(44)
-                .withNetconfAddress(new InetSocketAddress(NETCONF_ADDRESS, Integer.valueOf(NETCONF_PORT)));
-    }
-
-    private void waitTestToFinish(int i) throws InterruptedException {
-        Thread.sleep(i);
-    }
-
-
-    private DefaultCommitNotificationProducer startJMXCommitNotifier() {
-        return new DefaultCommitNotificationProducer(mBeanServer);
-    }
-
-    private void assertEditMessage(String netconfMessage) {
-        assertThat(netconfMessage,
-                JUnitMatchers.containsString(MockedBundleContext.DummyAdapterWithInitialSnapshot.CONFIG_SNAPSHOT));
-    }
-
-    private void assertCommitMessage(String netconfMessage) {
-        assertThat(netconfMessage, JUnitMatchers.containsString("<commit"));
-    }
-
-    private void assertHelloMessage(String netconfMessage) {
-        assertThat(netconfMessage,
-                JUnitMatchers.containsString("<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"));
-        assertThat(netconfMessage, JUnitMatchers.containsString("<capability>"));
-    }
-
-    private MockNetconfEndpoint startMockNetconfEndpoint(String capability, List<MockNetconfEndpoint.MessageSequence> messageSequences) {
-        // Add first empty sequence for testing connection created by config persister at startup
-        messageSequences.add(0, new MockNetconfEndpoint.MessageSequence(Collections.<String>emptyList()));
-        return new MockNetconfEndpoint(capability, NETCONF_PORT, messageSequences);
-    }
-
-    private MockNetconfEndpoint startMockNetconfEndpoint(String capability, String... messages) {
-        return startMockNetconfEndpoint(capability, Lists.newArrayList(new MockNetconfEndpoint.MessageSequence(messages)));
-    }
-
-    public ThreadFactory getThreadFactory(final Thread.UncaughtExceptionHandler exHandler) {
-        return new ThreadFactory() {
-            @Override
-            public Thread newThread(Runnable r) {
-                Thread thread = new Thread(r, "config-persister-testing-activator");
-                thread.setUncaughtExceptionHandler(exHandler);
-                return thread;
-            }
-        };
-    }
 }
diff --git a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/MockNetconfEndpoint.java b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/MockNetconfEndpoint.java
deleted file mode 100644 (file)
index 913db28..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * 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.netconf.persist.impl.osgi;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketTimeoutException;
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
-import org.opendaylight.controller.netconf.util.xml.XmlUtil;
-
-import com.google.common.collect.Lists;
-
-class MockNetconfEndpoint implements AutoCloseable {
-
-    public static final int READ_SOCKET_TIMEOUT = 3000;
-
-    public static final String MSG_SEPARATOR = "]]>]]>\n";
-
-    private final AtomicBoolean stopped = new AtomicBoolean(false);
-    private List<String> receivedMessages = Lists.newCopyOnWriteArrayList();
-    private Thread innerThread;
-
-    MockNetconfEndpoint(String capability, String netconfPort, List<MessageSequence> messageSequence) {
-        helloMessage = helloMessage.replace("capability_place_holder", capability);
-        start(netconfPort, messageSequence);
-    }
-
-    private String helloMessage = "<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
-            "<capabilities>\n" +
-            "<capability>capability_place_holder</capability>\n" +
-            "</capabilities>\n" +
-            "<session-id>1</session-id>\n" +
-            "</hello>\n" +
-            MSG_SEPARATOR;
-
-    public static String conflictingVersionErrorMessage;
-    static {
-        try {
-            conflictingVersionErrorMessage = XmlUtil.toString(XmlFileLoader
-                    .xmlFileToDocument("netconfMessages/conflictingversion/conflictingVersionResponse.xml")) + MSG_SEPARATOR;
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    public static String okMessage = "<rpc-reply message-id=\"1\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
-            "<ok/>\n" +
-            "</rpc-reply>" +
-            MSG_SEPARATOR ;
-
-    private void start(final String port, final List<MessageSequence> messagesToSend) {
-        innerThread = new Thread(new Runnable() {
-            @Override
-            public void run() {
-                int clientCounter = 0;
-
-                while (stopped.get() == false) {
-                    try (ServerSocket s = new ServerSocket(Integer.valueOf(port))) {
-                        s.setSoTimeout(READ_SOCKET_TIMEOUT);
-
-                        Socket clientSocket = s.accept();
-                        clientCounter++;
-                        clientSocket.setSoTimeout(READ_SOCKET_TIMEOUT);
-
-                        PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
-                        BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
-
-                        // Negotiate
-                        sendMessage(out, helloMessage);
-                        receiveMessage(in);
-
-                        // Accept next message (edit-config)
-                        receiveMessage(in);
-
-                        for (String message : getMessageSequenceForClient(messagesToSend, clientCounter)) {
-                            sendMessage(out, message);
-                            receiveMessage(in);
-                        }
-                    } catch (SocketTimeoutException e) {
-                        // No more activity on netconf endpoint, close
-                        return;
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
-                    }
-                }
-            }
-
-            private Iterable<? extends String> getMessageSequenceForClient(List<MessageSequence> messagesToSend,
-                    int clientCounter) {
-                if (messagesToSend.size() <= clientCounter) {
-                    return messagesToSend.get(messagesToSend.size() - 1).getMessages();
-                } else {
-                    return messagesToSend.get(clientCounter - 1).getMessages();
-                }
-            }
-
-            private void receiveMessage(BufferedReader in) throws Exception {
-                String message = readMessage(in);
-                if(message == null || message.equals(""))
-                    return;
-                receivedMessages.add(message);
-            }
-
-            private String readMessage(BufferedReader in) throws IOException {
-                int c;
-                StringBuilder b = new StringBuilder();
-
-                while((c = in.read()) != -1) {
-                    b.append((char)c);
-                    if(b.toString().endsWith("]]>]]>"))
-                        break;
-                }
-
-                return b.toString();
-            }
-
-            private void sendMessage(PrintWriter out, String message) throws InterruptedException {
-                out.print(message);
-                out.flush();
-            }
-
-        });
-        innerThread.setName("Mocked-netconf-endpoint-inner-thread");
-        innerThread.start();
-    }
-
-    public List<String> getReceivedMessages() {
-        return receivedMessages;
-    }
-
-    public void close() throws IOException, InterruptedException {
-        stopped.set(true);
-        innerThread.join();
-    }
-
-    static class MessageSequence {
-        private List<String> messages;
-
-        MessageSequence(List<String> messages) {
-            this.messages = messages;
-        }
-
-        MessageSequence(String... messages) {
-            this(Lists.newArrayList(messages));
-        }
-
-        public Collection<String> getMessages() {
-            return messages;
-        }
-    }
-}
index 97cf7ecfe7e2f36763e1a96ad7f994cc87db9ea5..8bc787bdef39e571fc4cef62add5b0854c491130 100644 (file)
@@ -14,41 +14,71 @@ import org.mockito.MockitoAnnotations;
 import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
 import org.opendaylight.controller.config.persist.api.Persister;
 import org.opendaylight.controller.config.persist.api.PropertiesProvider;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
 import org.opendaylight.controller.netconf.persist.impl.DummyAdapter;
+import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
 
 import java.io.IOException;
+import java.util.Collections;
 import java.util.List;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 
 final class MockedBundleContext {
-
     @Mock
     private BundleContext context;
+    @Mock
+    private Filter filter;
+    @Mock
+    private ServiceReference<?> serviceReference;
+    @Mock
+    private Bundle bundle;
+    @Mock
+    NetconfOperationServiceFactory serviceFactory;
+    @Mock
+    private NetconfOperationService service;
 
-    MockedBundleContext(String netconfAddress, String netconfPort) {
+    MockedBundleContext(long maxWaitForCapabilitiesMillis, long conflictingVersionTimeoutMillis) throws Exception {
         MockitoAnnotations.initMocks(this);
-        initContext(netconfAddress, netconfPort);
+        doReturn(null).when(context).getProperty(anyString());
+        initContext(maxWaitForCapabilitiesMillis, conflictingVersionTimeoutMillis);
+        doReturn(filter).when(context).createFilter(ConfigPersisterActivator.getFilterString());
+        String filterString = "filter";
+        doReturn(filterString).when(filter).toString();
+        doNothing().when(context).addServiceListener(any(ServiceListener.class), eq(filterString));
+        ServiceReference<?>[] toBeReturned = {serviceReference};
+        doReturn(toBeReturned).when(context).getServiceReferences((String) null, filterString);
+        doReturn(bundle).when(serviceReference).getBundle();
+        doReturn(context).when(bundle).getBundleContext();
+        doReturn("").when(serviceReference).toString();
+        doReturn(serviceFactory).when(context).getService(any(ServiceReference.class));
+        doReturn(service).when(serviceFactory).createService(anyString());
+        doReturn(Collections.emptySet()).when(service).getCapabilities();
+        doNothing().when(service).close();
     }
 
     public BundleContext getBundleContext() {
         return context;
     }
 
-    private void initContext(String netconfAddress, String netconfPort) {
-        initProp(context, ConfigPersisterActivator.IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX, null);
-
-        initPropNoPrefix(context, "netconf.tcp.client.address", netconfAddress);
-        initPropNoPrefix(context, "netconf.tcp.client.port", netconfPort);
-
+    private void initContext(long maxWaitForCapabilitiesMillis, long conflictingVersionTimeoutMillis) {
         initProp(context, "active", "1");
         initProp(context, "1." + ConfigPersisterActivator.STORAGE_ADAPTER_CLASS_PROP_SUFFIX, DummyAdapterWithInitialSnapshot.class.getName());
         initProp(context, "1." + "readonly", "false");
         initProp(context, "1." + ".properties.fileStorage", "target/configuration-persister-test/initial/");
-
+        initProp(context, ConfigPersisterActivator.MAX_WAIT_FOR_CAPABILITIES_MILLIS_PROPERTY, String.valueOf(maxWaitForCapabilitiesMillis));
+        initProp(context, ConfigPersisterActivator.CONFLICTING_VERSION_TIMEOUT_MILLIS_PROPERTY, String.valueOf(conflictingVersionTimeoutMillis));
     }
 
     private void initProp(BundleContext context, String key, String value) {
@@ -66,7 +96,7 @@ final class MockedBundleContext {
 
         @Override
         public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
-            return Lists.newArrayList(getConfigSnapshopt());
+            return Lists.newArrayList(getConfigSnapshot());
         }
 
         @Override
@@ -74,7 +104,7 @@ final class MockedBundleContext {
             return this;
         }
 
-        public ConfigSnapshotHolder getConfigSnapshopt() {
+        public ConfigSnapshotHolder getConfigSnapshot() {
             return new ConfigSnapshotHolder() {
                 @Override
                 public String getConfigSnapshot() {
index d42c15b8342736b0a523a7723d2e1d3b7865b15a..6fb231d847f2c5ae8c2022a7951460e19199ff70 100644 (file)
@@ -24,6 +24,10 @@ final class TestingExceptionHandler implements Thread.UncaughtExceptionHandler {
         this.t = e;
     }
 
+    public void assertException(Class<? extends Exception> exType, String exMessageToContain) {
+        assertException(exMessageToContain, exType, exMessageToContain);
+    }
+
     public void assertException(String failMessageSuffix, Class<? extends Exception> exType, String exMessageToContain) {
         if(t == null) {
             fail("Should fail to " + failMessageSuffix);
index 8d532d45e8a3c95153ad37e2fa9546d1fa8788c3..a35851445381f8bdcfd3142d036b9c3c34280d13 100644 (file)
@@ -7,12 +7,9 @@
  */
 package org.opendaylight.controller.netconf.impl.osgi;
 
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
 import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
 import org.opendaylight.controller.netconf.api.NetconfSession;
@@ -33,9 +30,11 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
 
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
 
 public class NetconfOperationRouterImpl implements NetconfOperationRouter {
 
@@ -186,18 +185,6 @@ public class NetconfOperationRouterImpl implements NetconfOperationRouter {
         return sortedPriority;
     }
 
-    public static final NetconfOperationChainedExecution EXECUTION_TERMINATION_POINT = new NetconfOperationChainedExecution() {
-        @Override
-        public boolean isExecutionTermination() {
-            return true;
-        }
-
-        @Override
-        public Document execute(Document requestMessage) throws NetconfDocumentedException {
-            throw new IllegalStateException("This execution represents the termination point in operation execution and cannot be executed itself");
-        }
-    };
-
     private static class NetconfOperationExecution implements NetconfOperationChainedExecution {
         private final NetconfOperation netconfOperation;
         private NetconfOperationChainedExecution subsequentExecution;
index cb4f53257e06f7e29d3989f6e3200232a9b4229e..5c08505091253282bdb638600b0ed656d3f2c319 100644 (file)
@@ -8,15 +8,15 @@
 
 package org.opendaylight.controller.netconf.impl.osgi;
 
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
 public class NetconfOperationServiceSnapshot implements AutoCloseable {
     private static final Logger logger = LoggerFactory.getLogger(NetconfOperationServiceSnapshot.class);
 
@@ -27,7 +27,7 @@ public class NetconfOperationServiceSnapshot implements AutoCloseable {
         Set<NetconfOperationService> services = new HashSet<>();
         netconfSessionIdForReporting = getNetconfSessionIdForReporting(sessionId);
         for (NetconfOperationServiceFactory factory : factories) {
-            services.add(factory.createService(sessionId, netconfSessionIdForReporting));
+            services.add(factory.createService(netconfSessionIdForReporting));
         }
         this.services = Collections.unmodifiableSet(services);
     }
index 07da7f990a974df6dcfe8e1a62ddb68ae4d8987f..c1a7b1478b3edde41ea5f13004afc48df540c6bf 100644 (file)
@@ -111,7 +111,7 @@ public class ConcurrentClientsTest {
     private NetconfOperationServiceFactory mockOpF() {
         return new NetconfOperationServiceFactory() {
             @Override
-            public NetconfOperationService createService(long netconfSessionId, String netconfSessionIdForReporting) {
+            public NetconfOperationService createService(String netconfSessionIdForReporting) {
                 return new NetconfOperationService() {
                     @Override
                     public Set<Capability> getCapabilities() {
index 19007cd0371b6ad5198db5ab8681a8ffff1a7f3c..997cae0f7cf258a8a3074af1f525ea17347b1ce1 100644 (file)
@@ -22,13 +22,13 @@ import org.opendaylight.controller.config.manager.impl.factoriesresolver.Hardcod
 import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
 import org.opendaylight.controller.config.persist.api.Persister;
 import org.opendaylight.controller.config.spi.ModuleFactory;
-import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.api.jmx.CommitJMXNotification;
 import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
 import org.opendaylight.controller.netconf.client.NetconfClient;
 import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
 import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl;
+import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException;
 import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
 import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher;
 import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
@@ -54,7 +54,6 @@ import java.net.InetSocketAddress;
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
-import java.util.regex.Pattern;
 
 import static junit.framework.Assert.assertEquals;
 import static org.mockito.Matchers.any;
@@ -124,7 +123,7 @@ public class NetconfConfigPersisterITTest extends AbstractNetconfConfigTest {
 
         try (NetconfClient persisterClient = new NetconfClient("persister", tcpAddress, 4000, clientDispatcher)) {
             try (ConfigPersisterNotificationHandler configPersisterNotificationHandler = new ConfigPersisterNotificationHandler(
-                    platformMBeanServer, mockedAggregator, Pattern.compile(""))) {
+                    platformMBeanServer, mockedAggregator)) {
 
 
                 try (NetconfClient netconfClient = new NetconfClient("client", tcpAddress, 4000, clientDispatcher)) {
index 677d0dff8ca5092c8e3d726864689bf2580462b9..4ca9690211c87bccd6ee79ddc23985c191024653 100644 (file)
@@ -8,33 +8,13 @@
 
 package org.opendaylight.controller.netconf.it;
 
-import static java.util.Collections.emptyList;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertTrue;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
+import ch.ethz.ssh2.Connection;
+import ch.ethz.ssh2.Session;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 import io.netty.channel.ChannelFuture;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.management.ManagementFactory;
-import java.net.InetSocketAddress;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeoutException;
-
-import javax.management.ObjectName;
-import javax.xml.parsers.ParserConfigurationException;
-
 import junit.framework.Assert;
-
+import org.apache.commons.io.IOUtils;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
@@ -42,7 +22,6 @@ import org.junit.Test;
 import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
 import org.opendaylight.controller.config.spi.ModuleFactory;
 import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
-import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException;
 import org.opendaylight.controller.config.yang.test.impl.DepTestImplModuleFactory;
 import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleFactory;
 import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleMXBean;
@@ -52,6 +31,7 @@ import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.client.NetconfClient;
 import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
 import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl;
+import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException;
 import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
 import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher;
 import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
@@ -72,17 +52,34 @@ import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
 import org.xml.sax.SAXException;
 
-import ch.ethz.ssh2.Connection;
-import ch.ethz.ssh2.Session;
+import javax.management.ObjectName;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
 
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
+import static java.util.Collections.emptyList;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 
 public class NetconfITTest extends AbstractNetconfConfigTest {
 
     // TODO refactor, pull common code up to AbstractNetconfITTest
 
-    private static final Logger logger =  LoggerFactory.getLogger(NetconfITTest.class);
+    private static final Logger logger = LoggerFactory.getLogger(NetconfITTest.class);
 
     private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023);
     private static final InetSocketAddress sshAddress = new InetSocketAddress("127.0.0.1", 10830);
@@ -90,7 +87,7 @@ public class NetconfITTest extends AbstractNetconfConfigTest {
     private static final String PASSWORD = "netconf";
 
     private NetconfMessage getConfig, getConfigCandidate, editConfig,
-    closeSession, startExi, stopExi;
+            closeSession, startExi, stopExi;
     private DefaultCommitNotificationProducer commitNot;
     private NetconfServerDispatcher dispatch;
 
@@ -113,7 +110,7 @@ public class NetconfITTest extends AbstractNetconfConfigTest {
         ChannelFuture s = dispatch.createServer(tcpAddress);
         s.await();
 
-        clientDispatcher = new NetconfClientDispatcher( nettyThreadgroup, nettyThreadgroup, 5000);
+        clientDispatcher = new NetconfClientDispatcher(nettyThreadgroup, nettyThreadgroup, 5000);
     }
 
     private NetconfServerDispatcher createDispatcher(NetconfOperationServiceFactoryListenerImpl factoriesListener) {
@@ -164,13 +161,14 @@ public class NetconfITTest extends AbstractNetconfConfigTest {
                 yangDependencies.add(resourceAsStream);
             }
         }
-        assertEquals("Some yang files were not found",emptyList(), failedToFind);
+        assertEquals("Some yang files were not found", emptyList(), failedToFind);
         return yangDependencies;
     }
 
     protected List<ModuleFactory> getModuleFactories() {
         return getModuleFactoriesS();
     }
+
     static List<ModuleFactory> getModuleFactoriesS() {
         return Lists.newArrayList(new TestImplModuleFactory(), new DepTestImplModuleFactory(),
                 new NetconfTestImplModuleFactory());
@@ -193,7 +191,7 @@ public class NetconfITTest extends AbstractNetconfConfigTest {
 
     @Test
     public void testTwoSessions() throws Exception {
-        try (NetconfClient netconfClient = new NetconfClient("1", tcpAddress, 10000, clientDispatcher))  {
+        try (NetconfClient netconfClient = new NetconfClient("1", tcpAddress, 10000, clientDispatcher)) {
             try (NetconfClient netconfClient2 = new NetconfClient("2", tcpAddress, 10000, clientDispatcher)) {
             }
         }
@@ -258,7 +256,7 @@ public class NetconfITTest extends AbstractNetconfConfigTest {
         NetconfTestImplModuleMXBean proxy = configRegistryClient
                 .newMXBeanProxy(impl, NetconfTestImplModuleMXBean.class);
         proxy.setTestingDep(dep);
-        proxy.setSimpleShort((short)0);
+        proxy.setSimpleShort((short) 0);
 
         transaction.commit();
 
@@ -400,12 +398,15 @@ public class NetconfITTest extends AbstractNetconfConfigTest {
         return netconfClient;
     }
 
-    private void startSSHServer() throws Exception{
+    private void startSSHServer() throws Exception {
         logger.info("Creating SSH server");
-        StubUserManager um = new StubUserManager(USERNAME,PASSWORD);
-        InputStream is = getClass().getResourceAsStream("/RSA.pk");
-        AuthProvider ap = new AuthProvider(um, is);
-        Thread sshServerThread = new Thread(NetconfSSHServer.start(10830,tcpAddress,ap));
+        StubUserManager um = new StubUserManager(USERNAME, PASSWORD);
+        String pem;
+        try (InputStream is = getClass().getResourceAsStream("/RSA.pk")) {
+            pem = IOUtils.toString(is);
+        }
+        AuthProvider ap = new AuthProvider(um, pem);
+        Thread sshServerThread = new Thread(NetconfSSHServer.start(10830, tcpAddress, ap));
         sshServerThread.setDaemon(true);
         sshServerThread.start();
         logger.info("SSH server on");
@@ -415,11 +416,11 @@ public class NetconfITTest extends AbstractNetconfConfigTest {
     public void sshTest() throws Exception {
         startSSHServer();
         logger.info("creating connection");
-        Connection conn = new Connection(sshAddress.getHostName(),sshAddress.getPort());
+        Connection conn = new Connection(sshAddress.getHostName(), sshAddress.getPort());
         Assert.assertNotNull(conn);
         logger.info("connection created");
         conn.connect();
-        boolean isAuthenticated = conn.authenticateWithPassword(USERNAME,PASSWORD);
+        boolean isAuthenticated = conn.authenticateWithPassword(USERNAME, PASSWORD);
         assertTrue(isAuthenticated);
         logger.info("user authenticated");
         final Session sess = conn.openSession();
@@ -427,10 +428,10 @@ public class NetconfITTest extends AbstractNetconfConfigTest {
         logger.info("user authenticated");
         sess.getStdin().write(XmlUtil.toString(this.getConfig.getDocument()).getBytes());
 
-        new Thread(){
+        new Thread() {
             @Override
-            public void run(){
-                while (true){
+            public void run() {
+                while (true) {
                     byte[] bytes = new byte[1024];
                     int c = 0;
                     try {
@@ -438,8 +439,10 @@ public class NetconfITTest extends AbstractNetconfConfigTest {
                     } catch (IOException e) {
                         e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                     }
-                    logger.info("got data:"+bytes);
-                    if (c == 0) break;
+                    logger.info("got data:" + bytes);
+                    if (c == 0) {
+                        break;
+                    }
                 }
             }
         }.join();
index 5fd9f2fcd1ba6f1152c9ca7ef317b574ca47f706..ee971a65ddc882bd11e7c0593f1b3f0168bcce99 100644 (file)
@@ -44,6 +44,7 @@ import static org.opendaylight.controller.test.sal.binding.it.TestHelper.junitAn
 import static org.opendaylight.controller.test.sal.binding.it.TestHelper.mdSalCoreBundles;
 import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
 import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemPackages;
 import static org.ops4j.pax.exam.CoreOptions.systemProperty;
 
 @RunWith(PaxExam.class)
@@ -62,6 +63,7 @@ public class IdentityRefNetconfTest {
                 systemProperty("osgi.console").value("2401"),
                 systemProperty("osgi.bundles.defaultStartLevel").value("4"),
                 systemProperty("pax.exam.osgi.unresolved.fail").value("true"),
+                systemPackages("sun.nio.ch"),
 
                 testingModules(),
                 loggingModules(),
index 1236138e6c6437a181b11425ac0f2f0449930944..05122be4d245a8f09b0c566aab90337c28c513af 100644 (file)
@@ -45,6 +45,10 @@ public class HandlingPriority implements Comparable<HandlingPriority> {
         return getHandlingPriority(priority + priorityIncrease);
     }
 
+    public boolean isCannotHandle() {
+        return this.equals(CANNOT_HANDLE);
+    }
+
     @Override
     public int compareTo(HandlingPriority o) {
         if (this == o)
index 22981530177dc57c2fd90bca03b7ea04880cd2c5..4013d623bd4c1dc1e891cd230fcb1b260c0f1ad6 100644 (file)
@@ -27,4 +27,18 @@ public interface NetconfOperationChainedExecution {
      * Do not execute if this is termination point
      */
     Document execute(Document requestMessage) throws NetconfDocumentedException;
+
+    public static final NetconfOperationChainedExecution EXECUTION_TERMINATION_POINT = new NetconfOperationChainedExecution() {
+        @Override
+        public boolean isExecutionTermination() {
+            return true;
+        }
+
+        @Override
+        public Document execute(Document requestMessage) throws NetconfDocumentedException {
+            throw new IllegalStateException("This execution represents the termination point in operation execution and cannot be executed itself");
+        }
+    };
+
+
 }
index 46b9cd22e06bb35cba2875fa6b05a9ca02b92454..81401f26eecc4796a61f2740b079a7351e33007a 100644 (file)
@@ -15,6 +15,6 @@ package org.opendaylight.controller.netconf.mapping.api;
  */
 public interface NetconfOperationServiceFactory {
 
-    NetconfOperationService createService(long netconfSessionId, String netconfSessionIdForReporting);
+    NetconfOperationService createService(String netconfSessionIdForReporting);
 
 }
index 11432314425e9db752be3bdde5eecb8b6cd4fa8c..de04484d1388f411577c7b1c2537996f2e625e7d 100644 (file)
@@ -46,7 +46,7 @@ public class NetconfMonitoringActivator implements BundleActivator {
         }
 
         @Override
-        public NetconfOperationService createService(long netconfSessionId, String netconfSessionIdForReporting) {
+        public NetconfOperationService createService(String netconfSessionIdForReporting) {
             return operationService;
         }
     }
index 73e0a467c040cc3cc8e7f3518e2a209ebdddb453..23a69b23fc307b7d51f74fba072b39a218d591e1 100644 (file)
@@ -1,4 +1,5 @@
-<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">
+<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>netconf-subsystem</artifactId>
         <groupId>org.opendaylight.controller</groupId>
             <groupId>org.opendaylight.controller</groupId>
             <artifactId>usermanager</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk15on</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcpkix-jdk15on</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>mockito-configuration</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
@@ -58,7 +72,8 @@
                 <artifactId>maven-bundle-plugin</artifactId>
                 <configuration>
                     <instructions>
-                        <Bundle-Activator>org.opendaylight.controller.netconf.osgi.NetconfSSHActivator</Bundle-Activator>
+                        <Bundle-Activator>org.opendaylight.controller.netconf.ssh.osgi.NetconfSSHActivator
+                        </Bundle-Activator>
                         <Import-Package>
                             com.google.common.base,
                             ch.ethz.ssh2,
@@ -71,6 +86,7 @@
                             org.osgi.framework,
                             org.osgi.util.tracker,
                             org.slf4j,
+                            org.bouncycastle.openssl
                         </Import-Package>
                     </instructions>
                 </configuration>
index 3d5318073d8c0e7cc203fd80fa63b6a05ea71e3e..2d380482ba456afd4beef97c77d7a86d1bf79f14 100644 (file)
@@ -7,64 +7,48 @@
  */
 package org.opendaylight.controller.netconf.ssh.authentication;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-import org.apache.commons.io.IOUtils;
 import org.opendaylight.controller.sal.authorization.AuthResultEnum;
 import org.opendaylight.controller.sal.authorization.UserLevel;
 import org.opendaylight.controller.usermanager.IUserManager;
 import org.opendaylight.controller.usermanager.UserConfig;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
 
 public class AuthProvider implements AuthProviderInterface {
 
-    private static IUserManager um;
+    private static IUserManager um; //FIXME static mutable state, no locks
     private static final String DEFAULT_USER = "netconf";
     private static final String DEFAULT_PASSWORD = "netconf";
-    private String PEM;
-
-    private static final Logger logger =  LoggerFactory.getLogger(AuthProvider.class);
-
-    public AuthProvider(IUserManager ium,InputStream privateKeyFileInputStream) throws Exception {
+    private final String pem;
 
+    public AuthProvider(IUserManager ium, String pemCertificate) throws Exception {
+        checkNotNull(pemCertificate, "Parameter 'pemCertificate' is null");
         AuthProvider.um = ium;
-        if (AuthProvider.um  == null){
+        if (AuthProvider.um == null) {
             throw new Exception("No usermanager service available.");
         }
 
         List<String> roles = new ArrayList<String>(1);
         roles.add(UserLevel.SYSTEMADMIN.toString());
-        AuthProvider.um.addLocalUser(new UserConfig(DEFAULT_USER, DEFAULT_PASSWORD, roles));
-
-        try {
-            PEM = IOUtils.toString(privateKeyFileInputStream);
-        } catch (IOException e) {
-            logger.error("Error reading RSA key from file.");
-            throw new IllegalStateException("Error reading RSA key from file.");
-        }
+        AuthProvider.um.addLocalUser(new UserConfig(DEFAULT_USER, DEFAULT_PASSWORD, roles)); //FIXME hardcoded auth
+        pem = pemCertificate;
     }
+
     @Override
-    public boolean authenticated(String username, String password)  throws Exception {
-        if (AuthProvider.um  == null){
+    public boolean authenticated(String username, String password) throws Exception {
+        if (AuthProvider.um == null) {
             throw new Exception("No usermanager service available.");
         }
-        AuthResultEnum authResult = AuthProvider.um.authenticate(username,password);
-        if (authResult.equals(AuthResultEnum.AUTH_ACCEPT) || authResult.equals(AuthResultEnum.AUTH_ACCEPT_LOC)){
-            return true;
-        }
-        return false;
+        AuthResultEnum authResult = AuthProvider.um.authenticate(username, password);
+        return authResult.equals(AuthResultEnum.AUTH_ACCEPT) || authResult.equals(AuthResultEnum.AUTH_ACCEPT_LOC);
     }
 
     @Override
-    public char[] getPEMAsCharArray() throws Exception {
-        if (null == PEM){
-            logger.error("Missing RSA key string.");
-            throw new Exception("Missing RSA key.");
-        }
-        return PEM.toCharArray();
+    public char[] getPEMAsCharArray() {
+        return pem.toCharArray();
     }
 
     @Override
@@ -76,6 +60,4 @@ public class AuthProvider implements AuthProviderInterface {
     public void addUserManagerService(IUserManager userManagerService) {
         AuthProvider.um = userManagerService;
     }
-
-
 }
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/PEMGenerator.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/PEMGenerator.java
new file mode 100644 (file)
index 0000000..73886c4
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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.netconf.ssh.authentication;
+
+import org.apache.commons.io.FileUtils;
+import org.bouncycastle.openssl.PEMWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.SecureRandom;
+
+public class PEMGenerator {
+    private static final Logger logger = LoggerFactory.getLogger(PEMGenerator.class);
+    private static final int KEY_SIZE = 4096;
+
+    public static String generateTo(File privateFile) throws Exception {
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+        SecureRandom sr = new SecureRandom();
+        keyGen.initialize(KEY_SIZE, sr);
+        KeyPair keypair = keyGen.generateKeyPair();
+        logger.info("Generating private key to {}", privateFile.getAbsolutePath());
+        String privatePEM = toString(keypair.getPrivate());
+        FileUtils.write(privateFile, privatePEM);
+        return privatePEM;
+    }
+
+    private static String toString(Key key) throws IOException {
+        try (StringWriter writer = new StringWriter()) {
+            try (PEMWriter pemWriter = new PEMWriter(writer)) {
+                pemWriter.writeObject(key);
+            }
+            return writer.toString();
+        }
+    }
+}
@@ -5,13 +5,13 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-package org.opendaylight.controller.netconf.osgi;
+package org.opendaylight.controller.netconf.ssh.osgi;
 
 import com.google.common.base.Optional;
-import java.io.FileInputStream;
-import java.net.InetSocketAddress;
+import org.apache.commons.io.IOUtils;
 import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
 import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
+import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
 import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
 import org.opendaylight.controller.usermanager.IUserManager;
 import org.osgi.framework.BundleActivator;
@@ -22,6 +22,11 @@ import org.osgi.util.tracker.ServiceTrackerCustomizer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
 /**
  * Activator for netconf SSH bundle which creates SSH bridge between netconf client and netconf server. Activator
  * starts SSH Server in its own thread. This thread is closed when activator calls stop() method. Server opens socket
@@ -60,7 +65,7 @@ public class NetconfSSHActivator implements BundleActivator{
         @Override
         public void removedService(ServiceReference<IUserManager> reference, IUserManager service) {
             logger.trace("Removing service {} from netconf SSH. " +
-                    "SSH won't authenticate users until IUserManeger service will be started.", reference);
+                    "SSH won't authenticate users until IUserManager service will be started.", reference);
             removeUserManagerService();
         }
     };
@@ -87,15 +92,27 @@ public class NetconfSSHActivator implements BundleActivator{
 
         if (sshSocketAddressOptional.isPresent()){
             String path = NetconfConfigUtil.getPrivateKeyPath(context);
-            path = path.replace("\\", "/");
+            path = path.replace("\\", "/");  // FIXME: shouldn't this convert lines to system dependent path separator?
             if (path.equals("")){
                 throw new Exception("Missing netconf.ssh.pk.path key in configuration file.");
             }
 
-            try (FileInputStream fis = new FileInputStream(path)){
-                AuthProvider authProvider = new AuthProvider(iUserManager,fis);
-                this.server = NetconfSSHServer.start(sshSocketAddressOptional.get().getPort(),tcpSocketAddress,authProvider);
+            File privateKeyFile = new File(path);
+            String privateKeyPEMString;
+            if (privateKeyFile.exists() == false) {
+                // generate & save to file
+                privateKeyPEMString = PEMGenerator.generateTo(privateKeyFile);
+            } else {
+                // read from file
+                try (FileInputStream fis = new FileInputStream(path)) {
+                    privateKeyPEMString = IOUtils.toString(fis);
+                } catch (IOException e) {
+                    logger.error("Error reading RSA key from file '{}'", path);
+                    throw new IllegalStateException("Error reading RSA key from file " + path);
+                }
             }
+            AuthProvider authProvider = new AuthProvider(iUserManager, privateKeyPEMString);
+            this.server = NetconfSSHServer.start(sshSocketAddressOptional.get().getPort(),tcpSocketAddress,authProvider);
 
             Thread serverThread = new  Thread(server,"netconf SSH server thread");
             serverThread.setDaemon(true);
index 204bf1d13194800e8a9347e97e97c2959ce9417f..ce26910b97dd449b10d395933e57cd6deccd52ea 100644 (file)
@@ -8,16 +8,6 @@
 package org.opendaylight.controller.netconf.ssh.threads;
 
 
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-
-import javax.annotation.concurrent.ThreadSafe;
-
-import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import ch.ethz.ssh2.AuthenticationResult;
 import ch.ethz.ssh2.PtySettings;
 import ch.ethz.ssh2.ServerAuthenticationCallback;
@@ -26,10 +16,18 @@ import ch.ethz.ssh2.ServerConnectionCallback;
 import ch.ethz.ssh2.ServerSession;
 import ch.ethz.ssh2.ServerSessionCallback;
 import ch.ethz.ssh2.SimpleServerSessionCallback;
+import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.concurrent.ThreadSafe;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
 
 @ThreadSafe
 public class SocketThread implements Runnable, ServerAuthenticationCallback, ServerConnectionCallback {
-    private static final Logger logger =  LoggerFactory.getLogger(SocketThread.class);
+    private static final Logger logger = LoggerFactory.getLogger(SocketThread.class);
 
     private final Socket socket;
     private final InetSocketAddress clientAddress;
@@ -43,11 +41,12 @@ public class SocketThread implements Runnable, ServerAuthenticationCallback, Ser
     public static void start(Socket socket,
                              InetSocketAddress clientAddress,
                              long sessionId,
-                             AuthProvider authProvider) throws IOException{
-        Thread netconf_ssh_socket_thread = new Thread(new SocketThread(socket,clientAddress,sessionId,authProvider));
+                             AuthProvider authProvider) throws IOException {
+        Thread netconf_ssh_socket_thread = new Thread(new SocketThread(socket, clientAddress, sessionId, authProvider));
         netconf_ssh_socket_thread.setDaemon(true);
         netconf_ssh_socket_thread.start();
     }
+
     private SocketThread(Socket socket,
                          InetSocketAddress clientAddress,
                          long sessionId,
@@ -56,7 +55,7 @@ public class SocketThread implements Runnable, ServerAuthenticationCallback, Ser
         this.socket = socket;
         this.clientAddress = clientAddress;
         this.sessionId = sessionId;
-        this.remoteAddressWithPort = socket.getRemoteSocketAddress().toString().replaceFirst("/","");
+        this.remoteAddressWithPort = socket.getRemoteSocketAddress().toString().replaceFirst("/", "");
         this.authProvider = authProvider;
 
     }
@@ -65,7 +64,7 @@ public class SocketThread implements Runnable, ServerAuthenticationCallback, Ser
     public void run() {
         conn = new ServerConnection(socket);
         try {
-            conn.setPEMHostKey(authProvider.getPEMAsCharArray(),"netconf");
+            conn.setPEMHostKey(authProvider.getPEMAsCharArray(), "netconf");
         } catch (Exception e) {
             logger.debug("Server authentication setup failed.");
         }
@@ -74,24 +73,21 @@ public class SocketThread implements Runnable, ServerAuthenticationCallback, Ser
         try {
             conn.connect();
         } catch (IOException e) {
-            logger.error("SocketThread error ",e);
+            logger.error("SocketThread error ", e);
         }
     }
+
     @Override
-    public ServerSessionCallback acceptSession(final ServerSession session)
-    {
-        SimpleServerSessionCallback cb = new SimpleServerSessionCallback()
-        {
+    public ServerSessionCallback acceptSession(final ServerSession session) {
+        SimpleServerSessionCallback cb = new SimpleServerSessionCallback() {
             @Override
-            public Runnable requestSubsystem(final ServerSession ss, final String subsystem) throws IOException
-            {
-                return new Runnable(){
+            public Runnable requestSubsystem(final ServerSession ss, final String subsystem) throws IOException {
+                return new Runnable() {
                     @Override
-                    public void run()
-                    {
-                        if (subsystem.equals("netconf")){
+                    public void run() {
+                        if (subsystem.equals("netconf")) {
                             IOThread netconf_ssh_input = null;
-                            IOThread  netconf_ssh_output = null;
+                            IOThread netconf_ssh_output = null;
                             try {
                                 String hostName = clientAddress.getHostName();
                                 int portNumber = clientAddress.getPort();
@@ -99,71 +95,71 @@ public class SocketThread implements Runnable, ServerAuthenticationCallback, Ser
                                 logger.trace("echo socket created");
 
                                 logger.trace("starting netconf_ssh_input thread");
-                                netconf_ssh_input =  new IOThread(echoSocket.getInputStream(),ss.getStdin(),"input_thread_"+sessionId,ss,conn);
+                                netconf_ssh_input = new IOThread(echoSocket.getInputStream(), ss.getStdin(), "input_thread_" + sessionId, ss, conn);
                                 netconf_ssh_input.setDaemon(false);
                                 netconf_ssh_input.start();
 
                                 logger.trace("starting netconf_ssh_output thread");
-                                final String customHeader = "["+currentUser+";"+remoteAddressWithPort+";ssh;;;;;;]\n";
-                                netconf_ssh_output = new IOThread(ss.getStdout(),echoSocket.getOutputStream(),"output_thread_"+sessionId,ss,conn,customHeader);
+                                final String customHeader = "[" + currentUser + ";" + remoteAddressWithPort + ";ssh;;;;;;]\n";
+                                netconf_ssh_output = new IOThread(ss.getStdout(), echoSocket.getOutputStream(), "output_thread_" + sessionId, ss, conn, customHeader);
                                 netconf_ssh_output.setDaemon(false);
                                 netconf_ssh_output.start();
 
-                            } catch (Throwable t){
-                                logger.error("SSH bridge couldn't create echo socket",t.getMessage(),t);
+                            } catch (Exception t) {
+                                logger.error("SSH bridge could not create echo socket: {}", t.getMessage(), t);
 
                                 try {
-                                    if (netconf_ssh_input!=null){
+                                    if (netconf_ssh_input != null) {
                                         netconf_ssh_input.join();
                                     }
                                 } catch (InterruptedException e) {
                                     Thread.currentThread().interrupt();
-                                   logger.error("netconf_ssh_input join error ",e);
+                                    logger.error("netconf_ssh_input join error ", e);
                                 }
 
                                 try {
-                                    if (netconf_ssh_output!=null){
+                                    if (netconf_ssh_output != null) {
                                         netconf_ssh_output.join();
                                     }
                                 } catch (InterruptedException e) {
                                     Thread.currentThread().interrupt();
-                                    logger.error("netconf_ssh_output join error ",e);
+                                    logger.error("netconf_ssh_output join error ", e);
                                 }
-
                             }
                         } else {
-                            try {
-                                ss.getStdin().write("wrong subsystem requested - closing connection".getBytes());
-                                ss.close();
-                            } catch (IOException e) {
-                                logger.debug("excpetion while sending bad subsystem response",e);
-                            }
+                            String reason = "Only netconf subsystem is supported, requested:" + subsystem;
+                            closeSession(ss, reason);
                         }
                     }
                 };
             }
+
+            public void closeSession(ServerSession ss, String reason) {
+                logger.trace("Closing session - {}", reason);
+                try {
+                    ss.getStdin().write(reason.getBytes());
+                } catch (IOException e) {
+                    logger.debug("Exception while closing session", e);
+                }
+                ss.close();
+            }
+
             @Override
-            public Runnable requestPtyReq(final ServerSession ss, final PtySettings pty) throws IOException
-            {
-                return new Runnable()
-                {
+            public Runnable requestPtyReq(final ServerSession ss, final PtySettings pty) throws IOException {
+                return new Runnable() {
                     @Override
-                    public void run()
-                    {
-                        //noop
+                    public void run() {
+                        closeSession(ss, "PTY request not supported");
                     }
                 };
             }
 
             @Override
-            public Runnable requestShell(final ServerSession ss) throws IOException
-            {
-                return new Runnable()
-                {
+            public Runnable requestShell(final ServerSession ss) throws IOException {
+                return new Runnable() {
                     @Override
-                    public void run()
-                    {
-                        //noop
+                    public void run() {
+                        closeSession(ss, "Shell not supported");
                     }
                 };
             }
@@ -173,35 +169,31 @@ public class SocketThread implements Runnable, ServerAuthenticationCallback, Ser
     }
 
     @Override
-    public String initAuthentication(ServerConnection sc)
-    {
-        logger.trace("Established connection with host {}",remoteAddressWithPort);
-        return "Established connection with host "+remoteAddressWithPort+"\r\n";
+    public String initAuthentication(ServerConnection sc) {
+        logger.trace("Established connection with host {}", remoteAddressWithPort);
+        return "Established connection with host " + remoteAddressWithPort + "\r\n";
     }
 
     @Override
-    public String[] getRemainingAuthMethods(ServerConnection sc)
-    {
-        return new String[] { ServerAuthenticationCallback.METHOD_PASSWORD };
+    public String[] getRemainingAuthMethods(ServerConnection sc) {
+        return new String[]{ServerAuthenticationCallback.METHOD_PASSWORD};
     }
 
     @Override
-    public AuthenticationResult authenticateWithNone(ServerConnection sc, String username)
-    {
+    public AuthenticationResult authenticateWithNone(ServerConnection sc, String username) {
         return AuthenticationResult.FAILURE;
     }
 
     @Override
-    public AuthenticationResult authenticateWithPassword(ServerConnection sc, String username, String password)
-    {
+    public AuthenticationResult authenticateWithPassword(ServerConnection sc, String username, String password) {
 
         try {
-            if (authProvider.authenticated(username,password)){
+            if (authProvider.authenticated(username, password)) {
                 currentUser = username;
-                logger.trace("user {}@{} authenticated",currentUser,remoteAddressWithPort);
+                logger.trace("user {}@{} authenticated", currentUser, remoteAddressWithPort);
                 return AuthenticationResult.SUCCESS;
             }
-        } catch (Exception e){
+        } catch (Exception e) {
             logger.warn("Authentication failed due to :" + e.getLocalizedMessage());
         }
         return AuthenticationResult.FAILURE;
@@ -209,8 +201,7 @@ public class SocketThread implements Runnable, ServerAuthenticationCallback, Ser
 
     @Override
     public AuthenticationResult authenticateWithPublicKey(ServerConnection sc, String username, String algorithm,
-            byte[] publickey, byte[] signature)
-    {
+                                                          byte[] publickey, byte[] signature) {
         return AuthenticationResult.FAILURE;
     }
 
diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/KeyGeneratorTest.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/KeyGeneratorTest.java
new file mode 100644 (file)
index 0000000..298f91c
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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.netconf;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
+import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
+import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
+import org.opendaylight.controller.usermanager.IUserManager;
+import org.opendaylight.controller.usermanager.UserConfig;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.InetSocketAddress;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+
+// This test is intended to be verified using ssh
+@Ignore
+public class KeyGeneratorTest {
+
+    @Mock
+    private IUserManager iUserManager;
+    File tempFile;
+
+    @Before
+    public void setUp() throws IOException {
+        MockitoAnnotations.initMocks(this);
+        doReturn(null).when(iUserManager).addLocalUser(any(UserConfig.class));
+        tempFile = File.createTempFile("odltest", ".tmp");
+        tempFile.deleteOnExit();
+    }
+
+    @After
+    public void tearDown() {
+        assertTrue(tempFile.delete());
+    }
+
+    @Test
+    public void test() throws Exception {
+        String pem = PEMGenerator.generateTo(tempFile);
+
+        AuthProvider authProvider = new AuthProvider(iUserManager, pem);
+        InetSocketAddress inetSocketAddress = new InetSocketAddress(Inet4Address.getLoopbackAddress().getHostAddress(), 8383);
+        NetconfSSHServer server = NetconfSSHServer.start(1830, inetSocketAddress, authProvider);
+
+        Thread serverThread = new  Thread(server,"netconf SSH server thread");
+        serverThread.start();
+        serverThread.join();
+    }
+}
index 91783ff755b5e934af8359c47f25b51d9ed5cd23..663a0b4a82c6ac3d480b7d1d498aa0339344d220 100644 (file)
@@ -8,15 +8,17 @@
 package org.opendaylight.controller.netconf;
 
 import ch.ethz.ssh2.Connection;
-import java.io.InputStream;
-import java.net.InetSocketAddress;
 import junit.framework.Assert;
+import org.apache.commons.io.IOUtils;
 import org.junit.Test;
 import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
 import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+
 
 public class SSHServerTest {
 
@@ -34,8 +36,11 @@ public class SSHServerTest {
     public void startSSHServer() throws Exception{
         logger.info("Creating SSH server");
         StubUserManager um = new StubUserManager(USER,PASSWORD);
-        InputStream is = getClass().getResourceAsStream("/RSA.pk");
-        AuthProvider ap = new AuthProvider(um, is);
+        String pem;
+        try(InputStream is = getClass().getResourceAsStream("/RSA.pk")) {
+            pem = IOUtils.toString(is);
+        }
+        AuthProvider ap = new AuthProvider(um, pem);
         NetconfSSHServer server = NetconfSSHServer.start(PORT,tcpAddress,ap);
         sshServerThread = new Thread(server);
         sshServerThread.setDaemon(true);
index 796ab91a504040e87197f9032d85e4699d4c80b5..29f40ce6242e7fcada70574b774a56ff1a5fab99 100644 (file)
@@ -8,9 +8,7 @@
 package org.opendaylight.controller.netconf.util;
 
 import com.google.common.base.Preconditions;
-import org.opendaylight.controller.config.api.ConflictingVersionException;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
-import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
 import org.opendaylight.controller.netconf.util.xml.XmlElement;
 import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
@@ -19,8 +17,6 @@ import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
 import org.xml.sax.SAXException;
 
-import javax.xml.xpath.XPathConstants;
-import javax.xml.xpath.XPathExpression;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -56,29 +52,19 @@ public final class NetconfUtil {
         return (doc == null) ? null : new NetconfMessage(doc);
     }
 
-    public static void checkIsMessageOk(NetconfMessage responseMessage) throws ConflictingVersionException {
-        XmlElement element = XmlElement.fromDomDocument(responseMessage.getDocument());
+    public static Document checkIsMessageOk(NetconfMessage responseMessage) {
+        return checkIsMessageOk(responseMessage.getDocument());
+    }
+
+    public static Document checkIsMessageOk(Document response) {
+        XmlElement element = XmlElement.fromDomDocument(response);
         Preconditions.checkState(element.getName().equals(XmlNetconfConstants.RPC_REPLY_KEY));
         element = element.getOnlyChildElement();
-
         if (element.getName().equals(XmlNetconfConstants.OK)) {
-            return;
+            return response;
         }
-
-        if (element.getName().equals(XmlNetconfConstants.RPC_ERROR)) {
-            logger.warn("Can not load last configuration, operation failed");
-            // is it ConflictingVersionException ?
-            XPathExpression xPathExpression = XMLNetconfUtil.compileXPath("/netconf:rpc-reply/netconf:rpc-error/netconf:error-info/netconf:error");
-            String error = (String) XmlUtil.evaluateXPath(xPathExpression, element.getDomElement(), XPathConstants.STRING);
-            if (error!=null && error.contains(ConflictingVersionException.class.getCanonicalName())) {
-                throw new ConflictingVersionException(error);
-            }
-            throw new IllegalStateException("Can not load last configuration, operation failed: "
-                    + XmlUtil.toString(responseMessage.getDocument()));
-        }
-
         logger.warn("Can not load last configuration. Operation failed.");
         throw new IllegalStateException("Can not load last configuration. Operation failed: "
-                + XmlUtil.toString(responseMessage.getDocument()));
+                + XmlUtil.toString(response));
     }
 }
index 2e3100f8b22351b1f3736ddf2d6ac1df20e90b32..6b12a1999270608264053c7edbf9ab1cc5bc6b55 100644 (file)
@@ -8,8 +8,6 @@
 
 package org.opendaylight.controller.netconf.util.mapping;
 
-import java.util.Map;
-
 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
 import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
 import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
@@ -22,6 +20,8 @@ import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.NodeList;
 
+import java.util.Map;
+
 public abstract class AbstractNetconfOperation implements NetconfOperation {
     private final String netconfSessionIdForReporting;
 
@@ -118,8 +118,12 @@ public abstract class AbstractNetconfOperation implements NetconfOperation {
 
     @Override
     public String toString() {
-        final StringBuffer sb = new StringBuffer("AbstractConfigNetconfOperation{");
-        sb.append("name=").append(getOperationName());
+        final StringBuffer sb = new StringBuffer(getClass().getName());
+        try {
+            sb.append("{name=").append(getOperationName());
+        } catch(UnsupportedOperationException e) {
+            // no problem
+        }
         sb.append(", namespace=").append(getOperationNamespace());
         sb.append(", session=").append(netconfSessionIdForReporting);
         sb.append('}');
index b4b8aba81fc0513e5d7debf0aca749f1a592129b..85eeb30a07339c3b379bd756328f7ed96926fbc2 100644 (file)
@@ -8,7 +8,6 @@
 package org.opendaylight.controller.netconf.util;
 
 import org.junit.Test;
-import org.opendaylight.controller.config.api.ConflictingVersionException;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.w3c.dom.Document;
@@ -25,7 +24,7 @@ public class NetconfUtilTest {
         try{
             NetconfUtil.checkIsMessageOk(new NetconfMessage(document));
             fail();
-        }catch(ConflictingVersionException e){
+        }catch(IllegalStateException e){
             assertThat(e.getMessage(), containsString("Optimistic lock failed. Expected parent version 21, was 18"));
         }
     }
index e7b9a025520f3d5ab64fa03b2852a9dc1ce61172..2be64a8a9873f138ae29876a2ac1c1d1998213e4 100644 (file)
@@ -47,7 +47,6 @@
         <osgi.version>5.0.0</osgi.version>
         <maven.bundle.version>2.4.0</maven.bundle.version>
         <slf4j.version>1.7.2</slf4j.version>
-        <netconf.netty.version>4.0.10.Final</netconf.netty.version>
         <salGeneratorPath>${project.build.directory}/generated-sources/sal</salGeneratorPath>
     </properties>
 
                 <artifactId>config-persister-api</artifactId>
                 <version>${config.version}</version>
             </dependency>
-            <dependency>
-                <groupId>org.opendaylight.controller</groupId>
-                <artifactId>config-persister-file-adapter</artifactId>
-                <version>${config.version}</version>
-            </dependency>
             <dependency>
                 <groupId>${project.groupId}</groupId>
                 <artifactId>netconf-client</artifactId>
index 6582d8c021d5f61de10ae6b221f1217fdd1d668f..ae84a72bbac84ddda56c102972c894e21873a603 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright IBM Corporation, 2013.  All rights reserved.
+ * Copyright IBM Corporation and others, 2013.  All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
@@ -64,6 +64,12 @@ public class NeutronSubnet extends ConfigurationObject implements Serializable {
     @XmlElement (name="tenant_id")
     String tenantID;
 
+    @XmlElement (name="ipv6_address_mode", nillable=true)
+    String ipV6AddressMode;
+
+    @XmlElement (name="ipv6_ra_mode", nillable=true)
+    String ipV6RaMode;
+
     /* stores the OpenStackPorts associated with an instance
      * used to determine if that instance can be deleted.
      */
@@ -170,6 +176,14 @@ public class NeutronSubnet extends ConfigurationObject implements Serializable {
         this.tenantID = tenantID;
     }
 
+    public String getIpV6AddressMode() { return ipV6AddressMode; }
+
+    public void setIpV6AddressMode(String ipV6AddressMode) { this.ipV6AddressMode = ipV6AddressMode; }
+
+    public String getIpV6RaMode() { return ipV6RaMode; }
+
+    public void setIpV6RaMode(String ipV6RaMode) { this.ipV6RaMode = ipV6RaMode; }
+
     /**
      * This method copies selected fields from the object and returns them
      * as a new object, suitable for marshaling.
@@ -224,6 +238,12 @@ public class NeutronSubnet extends ConfigurationObject implements Serializable {
             if (s.equals("tenant_id")) {
                 ans.setTenantID(this.getTenantID());
             }
+            if (s.equals("ipv6_address_mode")) {
+                ans.setIpV6AddressMode(this.getIpV6AddressMode());
+            }
+            if (s.equals("ipv6_ra_mode")) {
+                ans.setIpV6RaMode(this.getIpV6RaMode());
+            }
         }
         return ans;
     }
@@ -444,6 +464,7 @@ public class NeutronSubnet extends ConfigurationObject implements Serializable {
                 + ", ipVersion=" + ipVersion + ", cidr=" + cidr + ", gatewayIP=" + gatewayIP + ", dnsNameservers="
                 + dnsNameservers + ", allocationPools=" + allocationPools + ", hostRoutes=" + hostRoutes
                 + ", enableDHCP=" + enableDHCP + ", tenantID=" + tenantID + ", myPorts=" + myPorts
-                + ", gatewayIPAssigned=" + gatewayIPAssigned + "]";
+                + ", gatewayIPAssigned=" + gatewayIPAssigned + ", ipv6AddressMode=" + ipV6AddressMode
+                + ", ipv6RaMode=" + ipV6RaMode + "]";
     }
 }
index 805f5be29601dcc8026d980b577e69af6758e7f1..9ddba67e251360996d6c72e50ad81e271c1c0f65 100644 (file)
@@ -29,6 +29,7 @@ import org.opendaylight.controller.northbound.commons.exception.NotAcceptableExc
 import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
 import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
 import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.networkconfig.bridgedomain.BridgeDomainConfigServiceException;
 import org.opendaylight.controller.sal.networkconfig.bridgedomain.ConfigConstants;
 import org.opendaylight.controller.sal.networkconfig.bridgedomain.IBridgeDomainConfigService;
 import org.opendaylight.controller.sal.utils.ServiceHelper;
@@ -114,7 +115,7 @@ public class BridgeDomainNorthbound {
            if (status.getCode().equals(StatusCode.SUCCESS)) {
                return Response.status(Response.Status.CREATED).build();
            }
-       } catch (Throwable t) {
+       } catch (BridgeDomainConfigServiceException e) {
            return Response.status(Response.Status.PRECONDITION_FAILED).build();
        }
        throw new ResourceNotFoundException(status.getDescription());
@@ -161,7 +162,7 @@ public class BridgeDomainNorthbound {
           if (status.getCode().equals(StatusCode.SUCCESS)) {
               return Response.status(Response.Status.OK).build();
           }
-      } catch (Throwable t) {
+      } catch (Exception t) {
           return Response.status(Response.Status.PRECONDITION_FAILED).build();
       }
       throw new ResourceNotFoundException(status.getDescription());
@@ -215,7 +216,7 @@ public class BridgeDomainNorthbound {
            if (status.getCode().equals(StatusCode.SUCCESS)) {
                return Response.status(Response.Status.CREATED).build();
            }
-       } catch (Throwable t) {
+       } catch (Exception t) {
            return Response.status(Response.Status.PRECONDITION_FAILED).build();
        }
        throw new ResourceNotFoundException(status.getDescription());
@@ -264,7 +265,7 @@ public class BridgeDomainNorthbound {
            if (status.getCode().equals(StatusCode.SUCCESS)) {
                return Response.status(Response.Status.OK).build();
            }
-       } catch (Throwable t) {
+       } catch (Exception t) {
            return Response.status(Response.Status.PRECONDITION_FAILED).build();
        }
        throw new ResourceNotFoundException(status.getDescription());
index 224fcb5f01ceaaaa662d2c9bbe0655892da463a2..f397eb3a97b0630002b3fc1b70901a1a25cf0609 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright IBM Corporation, 2013.  All rights reserved.
+ * Copyright IBM Corporation and others, 2013.  All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
@@ -87,6 +87,8 @@ public class NeutronSubnetsNorthbound {
             @QueryParam("gateway_ip") String queryGatewayIP,
             @QueryParam("enable_dhcp") String queryEnableDHCP,
             @QueryParam("tenant_id") String queryTenantID,
+            @QueryParam("ipv6_address_mode") String queryIpV6AddressMode,
+            @QueryParam("ipv6_ra_mode") String queryIpV6RaMode,
             // pagination
             @QueryParam("limit") String limit,
             @QueryParam("marker") String marker,
@@ -110,7 +112,9 @@ public class NeutronSubnetsNorthbound {
                     (queryCIDR == null || queryCIDR.equals(oSS.getCidr())) &&
                     (queryGatewayIP == null || queryGatewayIP.equals(oSS.getGatewayIP())) &&
                     (queryEnableDHCP == null || queryEnableDHCP.equals(oSS.getEnableDHCP())) &&
-                    (queryTenantID == null || queryTenantID.equals(oSS.getTenantID()))) {
+                    (queryTenantID == null || queryTenantID.equals(oSS.getTenantID())) &&
+                    (queryIpV6AddressMode == null || queryIpV6AddressMode.equals(oSS.getIpV6AddressMode())) &&
+                    (queryIpV6RaMode == null || queryIpV6RaMode.equals(oSS.getIpV6RaMode()))){
                 if (fields.size() > 0) {
                     ans.add(extractFields(oSS,fields));
                 } else {
index aa60f9117405270725514dc740f564ad8e2ccd03..f27d30eaae620312a75a7da2d47fa878936f5ccd 100644 (file)
@@ -54,6 +54,12 @@ public class SecureMessageReadWriteService implements IMessageReadWrite {
                                     // switch
     private ByteBuffer peerNetData; // encrypted message from the switch
     private FileInputStream kfd = null, tfd = null;
+    private final String keyStoreFileDefault = "./configuration/tlsKeyStore";
+    private final String trustStoreFileDefault = "./configuration/tlsTrustStore";
+    private final String keyStorePasswordPropName = "controllerKeyStorePassword";
+    private final String trustStorePasswordPropName = "controllerTrustStorePassword";
+    private static String keyStorePassword = null;
+    private static String trustStorePassword = null;
 
     public SecureMessageReadWriteService(SocketChannel socket, Selector selector)
             throws Exception {
@@ -80,32 +86,44 @@ public class SecureMessageReadWriteService implements IMessageReadWrite {
      */
     private void createSecureChannel(SocketChannel socket) throws Exception {
         String keyStoreFile = System.getProperty("controllerKeyStore");
-        String keyStorePassword = System
-                .getProperty("controllerKeyStorePassword");
         String trustStoreFile = System.getProperty("controllerTrustStore");
-        String trustStorePassword = System
-                .getProperty("controllerTrustStorePassword");
+        String keyStorePasswordProp = System.getProperty(keyStorePasswordPropName);
+        String trustStorePasswordProp = System.getProperty(trustStorePasswordPropName);
 
         if (keyStoreFile != null) {
             keyStoreFile = keyStoreFile.trim();
+        } else {
+            keyStoreFile = keyStoreFileDefault;
         }
         if ((keyStoreFile == null) || keyStoreFile.isEmpty()) {
             throw new FileNotFoundException("TLS KeyStore file not found.");
         }
+
+        if ((keyStorePassword == null) || ((keyStorePasswordProp != null) && !keyStorePasswordProp.isEmpty())) {
+            keyStorePassword = keyStorePasswordProp;
+        }
         if (keyStorePassword != null) {
             keyStorePassword = keyStorePassword.trim();
+            System.setProperty(keyStorePasswordPropName, "");
         }
         if ((keyStorePassword == null) || keyStorePassword.isEmpty()) {
             throw new FileNotFoundException("TLS KeyStore Password not provided.");
         }
         if (trustStoreFile != null) {
             trustStoreFile = trustStoreFile.trim();
+        } else {
+            trustStoreFile = trustStoreFileDefault;
         }
         if ((trustStoreFile == null) || trustStoreFile.isEmpty()) {
             throw new FileNotFoundException("TLS TrustStore file not found");
         }
+
+        if ((trustStorePassword == null) || ((trustStorePasswordProp != null) && !trustStorePasswordProp.isEmpty())) {
+            trustStorePassword = trustStorePasswordProp;
+        }
         if (trustStorePassword != null) {
             trustStorePassword = trustStorePassword.trim();
+            System.setProperty(trustStorePasswordPropName, "");
         }
         if ((trustStorePassword == null) || trustStorePassword.isEmpty()) {
             throw new FileNotFoundException("TLS TrustStore Password not provided.");
index 548bfb1f9fb26eb93ab4ca5bcd456d9bdc4082c3..fc668b3f4d0f1a1f01bb3a9b096b6bc7440515c0 100644 (file)
@@ -945,7 +945,7 @@ public class DiscoveryService implements IInventoryShimExternalListener, IDataPa
         if (!src.getType().equals(NodeConnector.NodeConnectorIDType.PRODUCTION)) {
             if (type == UpdateType.ADDED) {
                 edgeMap.put(dst, edge);
-            } else {
+            } else if (type == UpdateType.REMOVED) {
                 edgeMap.remove(dst);
             }
         } else {
@@ -954,7 +954,7 @@ public class DiscoveryService implements IInventoryShimExternalListener, IDataPa
              */
             if (type == UpdateType.ADDED) {
                 prodMap.put(dst, edge);
-            } else {
+            } else if (type == UpdateType.REMOVED) {
                 prodMap.remove(dst);
             }
         }
index 6d1c563aa44729581b5eb46b5e61f8cb0543b47a..8893762c3ca41f52fbc0fcfeb8905c66a1d9eb02 100644 (file)
@@ -806,6 +806,8 @@ public class FlowConverter {
         list.add(SwPath.class);
         list.add(HwPath.class);
         list.add(Drop.class);
+        list.add(Flood.class);
+        list.add(FloodAll.class);
 
         return list;
     }
diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlDst.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlDst.java
new file mode 100644 (file)
index 0000000..381de8e
--- /dev/null
@@ -0,0 +1,106 @@
+package org.opendaylight.controller.sal.match.extensible;
+
+import java.util.Arrays;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.utils.HexEncode;
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class DlDst extends MatchField<byte[]> {
+    private static final long serialVersionUID = 1L;
+    public static final String TYPE = "DL_DST";
+    private byte[] address;
+
+    /**
+     * Creates a Match field for the destination data layer address
+     *
+     * @param address
+     *            the data layer address. The constructor makes a copy of it
+     */
+    public DlDst(byte[] address) {
+        super(TYPE);
+        if (address != null) {
+            this.address = Arrays.copyOf(address, address.length);
+        }
+    }
+
+    // To satisfy JAXB
+    public DlDst() {
+        super(TYPE);
+    }
+
+    @Override
+    public byte[] getValue() {
+        return Arrays.copyOf(address, address.length);
+    }
+
+    @Override
+    @XmlElement(name = "value")
+    protected String getValueString() {
+        return HexEncode.bytesToHexStringFormat(address);
+    }
+
+    @Override
+    public byte[] getMask() {
+        return null;
+    }
+
+    @Override
+    protected String getMaskString() {
+        return null;
+    }
+
+    @Override
+    public boolean isValid() {
+        return address != null && address.length == NetUtils.MACAddrLengthInBytes;
+    }
+
+    @Override
+    public boolean hasReverse() {
+        return true;
+    }
+
+    @Override
+    public DlSrc getReverse() {
+        return new DlSrc(address);
+    }
+
+    @Override
+    public DlDst clone() {
+        return new DlDst(address);
+    }
+
+    @Override
+    public boolean isV6() {
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Arrays.hashCode(address);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof DlDst)) {
+            return false;
+        }
+        DlDst other = (DlDst) obj;
+        return Arrays.equals(address, other.address);
+    }
+}
diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlSrc.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlSrc.java
new file mode 100644 (file)
index 0000000..962c4d3
--- /dev/null
@@ -0,0 +1,106 @@
+package org.opendaylight.controller.sal.match.extensible;
+
+import java.util.Arrays;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.utils.HexEncode;
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class DlSrc extends MatchField<byte[]> {
+    private static final long serialVersionUID = 1L;
+    public static final String TYPE = "DL_SRC";
+    private byte[] address;
+
+    /**
+     * Creates a Match field for the source datalayer address
+     *
+     * @param address
+     *            the datalayer address. The constructor makes a copy of it
+     */
+    public DlSrc(byte[] address) {
+        super(TYPE);
+        if (address != null) {
+            this.address = Arrays.copyOf(address, address.length);
+        }
+    }
+
+    // To satisfy JAXB
+    private DlSrc() {
+        super(TYPE);
+    }
+
+    @Override
+    public byte[] getValue() {
+        return Arrays.copyOf(address, address.length);
+    }
+
+    @Override
+    @XmlElement(name = "value")
+    protected String getValueString() {
+        return HexEncode.bytesToHexStringFormat(address);
+    }
+
+    @Override
+    public byte[] getMask() {
+        return null;
+    }
+
+    @Override
+    protected String getMaskString() {
+        return null;
+    }
+
+    @Override
+    public boolean isValid() {
+        return address != null && address.length == NetUtils.MACAddrLengthInBytes;
+    }
+
+    @Override
+    public boolean hasReverse() {
+        return true;
+    }
+
+    @Override
+    public DlDst getReverse() {
+        return new DlDst(address);
+    }
+
+    @Override
+    public DlSrc clone() {
+        return new DlSrc(address);
+    }
+
+    @Override
+    public boolean isV6() {
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Arrays.hashCode(address);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof DlSrc)) {
+            return false;
+        }
+        DlSrc other = (DlSrc) obj;
+        return Arrays.equals(address, other.address);
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlType.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlType.java
new file mode 100644 (file)
index 0000000..468703d
--- /dev/null
@@ -0,0 +1,105 @@
+package org.opendaylight.controller.sal.match.extensible;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.utils.EtherTypes;
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class DlType extends MatchField<Short> {
+    private static final long serialVersionUID = 1L;
+    public static final String TYPE = "DL_TYPE";
+    private short ethertype;
+
+    /**
+     * Creates a Match field for the data layer type
+     *
+     * @param address
+     *            the data layer type
+     */
+    public DlType(short ethertype) {
+        super(TYPE);
+        this.ethertype = ethertype;
+    }
+
+    // To satisfy JAXB
+    private DlType() {
+        super(TYPE);
+    }
+
+    @Override
+    public Short getValue() {
+        return ethertype;
+    }
+
+    @Override
+    @XmlElement(name = "value")
+    protected String getValueString() {
+        return String.format("0X%s", Integer.toHexString(NetUtils.getUnsignedShort(ethertype)));
+    }
+
+    @Override
+    public Short getMask() {
+        return null;
+    }
+
+    @Override
+    protected String getMaskString() {
+        return null;
+    }
+
+    @Override
+    public boolean isValid() {
+        return true;
+    }
+
+    @Override
+    public boolean hasReverse() {
+        return false;
+    }
+
+    @Override
+    public DlType getReverse() {
+        return this.clone();
+    }
+
+    @Override
+    public DlType clone() {
+        return new DlType(ethertype);
+    }
+
+    @Override
+    public boolean isV6() {
+        return this.ethertype == EtherTypes.IPv6.shortValue();
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ethertype;
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof DlType)) {
+            return false;
+        }
+        DlType other = (DlType) obj;
+        if (ethertype != other.ethertype) {
+            return false;
+        }
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlVlan.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlVlan.java
new file mode 100644 (file)
index 0000000..30657a9
--- /dev/null
@@ -0,0 +1,104 @@
+package org.opendaylight.controller.sal.match.extensible;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class DlVlan extends MatchField<Short> {
+    private static final long serialVersionUID = 1L;
+    public static final String TYPE = "DL_VLAN";
+    private static final short MAX = 4095;
+    private short vlan;
+
+    /**
+     * Creates a Match field for the data layer type
+     *
+     * @param address
+     *            the data layer type
+     */
+    public DlVlan(short vlan) {
+        super(TYPE);
+        this.vlan = vlan;
+    }
+
+    // To satisfy JAXB
+    private DlVlan() {
+        super(TYPE);
+    }
+
+    @Override
+    public Short getValue() {
+        return vlan;
+    }
+
+    @Override
+    @XmlElement(name = "value")
+    protected String getValueString() {
+        return String.valueOf(vlan);
+    }
+
+    @Override
+    public Short getMask() {
+        return null;
+    }
+
+    @Override
+    protected String getMaskString() {
+        return null;
+    }
+
+    @Override
+    public boolean isValid() {
+        return vlan >= 0 && vlan <= MAX;
+    }
+
+    @Override
+    public DlVlan getReverse() {
+        return this.clone();
+    }
+
+    @Override
+    public boolean hasReverse() {
+        return false;
+    }
+
+    @Override
+    public DlVlan clone() {
+        return new DlVlan(vlan);
+    }
+
+    @Override
+    public boolean isV6() {
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + vlan;
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof DlVlan)) {
+            return false;
+        }
+        DlVlan other = (DlVlan) obj;
+        if (vlan != other.vlan) {
+            return false;
+        }
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlVlanPriority.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlVlanPriority.java
new file mode 100644 (file)
index 0000000..58dd563
--- /dev/null
@@ -0,0 +1,105 @@
+package org.opendaylight.controller.sal.match.extensible;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class DlVlanPriority extends MatchField<Byte> {
+    private static final long serialVersionUID = 1L;
+    public static final String TYPE = "DL_VLAN_PR";
+    private static final byte MAX = 7;
+    private byte vlanPriority;
+
+    /**
+     * Creates a Match field for the data layer type
+     *
+     * @param address
+     *            the data layer type
+     */
+    public DlVlanPriority(byte vlanPriority) {
+        super(TYPE);
+        this.vlanPriority = vlanPriority;
+    }
+
+    // To satisfy JAXB
+    private DlVlanPriority() {
+        super(TYPE);
+    }
+
+    @Override
+    public Byte getValue() {
+        return vlanPriority;
+    }
+
+    @Override
+    @XmlElement(name = "mask")
+    protected String getValueString() {
+        return String.format("0X%s", Integer.toHexString(NetUtils.getUnsignedByte(vlanPriority)));
+    }
+
+    @Override
+    public Byte getMask() {
+        return null;
+    }
+
+    @Override
+    protected String getMaskString() {
+        return null;
+    }
+
+    @Override
+    public boolean isValid() {
+        return vlanPriority >= 0 && vlanPriority <= MAX;
+    }
+
+    @Override
+    public boolean hasReverse() {
+        return false;
+    }
+
+    @Override
+    public DlVlanPriority getReverse() {
+        return this.clone();
+    }
+
+    @Override
+    public DlVlanPriority clone() {
+        return new DlVlanPriority(vlanPriority);
+    }
+
+    @Override
+    public boolean isV6() {
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + vlanPriority;
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof DlVlanPriority)) {
+            return false;
+        }
+        DlVlanPriority other = (DlVlanPriority) obj;
+        if (vlanPriority != other.vlanPriority) {
+            return false;
+        }
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/InPort.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/InPort.java
new file mode 100644 (file)
index 0000000..2b5eb5b
--- /dev/null
@@ -0,0 +1,108 @@
+package org.opendaylight.controller.sal.match.extensible;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.core.NodeConnector;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class InPort extends MatchField<NodeConnector> {
+    private static final long serialVersionUID = 1L;
+    public static final String TYPE = "IN_PORT";
+    private NodeConnector port;
+
+    /**
+     * Creates a Match field for the input port
+     *
+     * @param port
+     *            the input port
+     */
+    public InPort(NodeConnector port) {
+        super(TYPE);
+        this.port = port;
+    }
+
+    // To satisfy JAXB
+    private InPort() {
+        super(TYPE);
+    }
+
+    @Override
+    public NodeConnector getValue() {
+        return port;
+    }
+
+    @Override
+    @XmlElement(name = "value")
+    protected String getValueString() {
+        return port.toString();
+    }
+
+    @Override
+    public NodeConnector getMask() {
+        return null;
+    }
+
+    @Override
+    protected String getMaskString() {
+        return null;
+    }
+
+    @Override
+    public boolean isValid() {
+        return true;
+    }
+
+    @Override
+    public boolean hasReverse() {
+        return false;
+    }
+
+    @Override
+    public InPort getReverse() {
+        return this.clone();
+    }
+
+    @Override
+    public InPort clone() {
+        return new InPort(port);
+    }
+
+    @Override
+    public boolean isV6() {
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((port == null) ? 0 : port.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof InPort)) {
+            return false;
+        }
+        InPort other = (InPort) obj;
+        if (port == null) {
+            if (other.port != null) {
+                return false;
+            }
+        } else if (!port.equals(other.port)) {
+            return false;
+        }
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/Match.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/Match.java
new file mode 100644 (file)
index 0000000..b065444
--- /dev/null
@@ -0,0 +1,422 @@
+
+/*
+ * 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.match.extensible;
+
+import java.io.Serializable;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+/**
+ * Represents the generic match criteria for a network frame/packet/message
+ * It contains a collection of individual field match
+ *
+ */
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class Match implements Cloneable, Serializable {
+        private static final long serialVersionUID = 1L;
+    private Map<String, MatchField<?>> fields;
+
+    public Match() {
+        fields = new HashMap<String, MatchField<?>>();
+    }
+
+    public Match(Match match) {
+        fields = new HashMap<String, MatchField<?>>(match.fields);
+    }
+
+    /**
+     * Generic setter for frame/packet/message's header field against which to match
+     *
+     * @param field the fields parameters as MAtchField object
+     */
+    public void setField(MatchField<?> field) {
+        if (field.isValid()) {
+            fields.put(field.getType(), field);
+        }
+    }
+
+    /**
+     * Generic method to clear a field from the match
+     */
+    public void clearField(String type) {
+        fields.remove(type);
+    }
+
+    /**
+     * Generic getter for fields against which the match is programmed
+     *
+     * @param type  frame/packet/message's header field type
+     * @return
+     */
+    public MatchField<?> getField(String type) {
+        return fields.get(type);
+    }
+
+    /**
+     * Returns the list of MatchType fields the match is set for
+     *
+     * @return List of individual MatchType fields.
+     */
+    public List<String> getMatchesList() {
+        return new ArrayList<String>(fields.keySet());
+    }
+
+    /**
+     * Returns the list of MatchFields the match is set for
+     *
+     * @return List of individual MatchField values.
+     */
+    @XmlElement(name="matchField")
+    public List<MatchField<?>> getMatchFields() {
+        return new ArrayList<MatchField<?>>(fields.values());
+    }
+
+    /**
+     * Returns whether this match is for an IPv6 flow
+     */
+    public boolean isIPv6() {
+        if (isPresent(DlType.TYPE)) {
+            for (MatchField<?> field : fields.values()) {
+                if (!field.isV6()) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns whether this match is for an IPv4 flow
+     */
+    public boolean isIPv4() {
+        return !isIPv6();
+    }
+
+    /**
+     * Returns whether for the specified field type the match is to be considered "any"
+     * Equivalent to say this match does not care about the value of the specified field
+     *
+     * @param type
+     * @return
+     */
+    public boolean isAny(String type) {
+        return !fields.containsKey(type);
+    }
+
+    /**
+     * Returns whether a match for the specified field type is configured
+     *
+     * @param type
+     * @return
+     */
+    public boolean isPresent(String type) {
+        return (fields.get(type) != null);
+    }
+
+    public boolean isEmpty() {
+        return fields.isEmpty();
+    }
+
+    @Override
+    public Match clone() {
+        Match cloned = null;
+        try {
+            cloned = (Match) super.clone();
+            cloned.fields = new HashMap<String, MatchField<?>>();
+            for (Entry<String, MatchField<?>> entry : this.fields.entrySet()) {
+                cloned.fields.put(entry.getKey(), entry.getValue().clone());
+            }
+        } catch (CloneNotSupportedException e) {
+            throw new RuntimeException(e);
+        }
+        return cloned;
+    }
+
+    /**
+     * Returns a reversed version of this match
+     * For example, in the reversed version the network source and destination
+     * addresses will be exchanged. Non symmetric match field will not be
+     * copied over into the reversed match version, like input port.
+     *
+     * @return
+     */
+    public Match reverse() {
+        Match reverse = new Match();
+        for (MatchField<?> field : fields.values()) {
+            reverse.setField(field.hasReverse()? field.getReverse() : field.clone());
+        }
+
+        // Reset asymmetric fields
+        reverse.clearField(InPort.TYPE);
+
+        return reverse;
+    }
+
+    /**
+     * Check whether the current match conflicts with the passed filter match
+     * This match conflicts with the filter if for at least a MatchType defined
+     * in the filter match, the respective MatchFields differ or are not
+     * compatible
+     *
+     * In other words the function returns true if the set of packets described
+     * by one match and the set of packets described by the other match are
+     * disjoint. Equivalently, if the intersection of the two sets of packets
+     * described by the two org.opendaylight.controller.sal.matches is an empty.
+     *
+     * For example, Let's suppose the filter has the following MatchFields:
+     * DL_TYPE = 0x800
+     * NW_DST = 172.20.30.110/24
+     *
+     * while this match has the following MatchFields:
+     * DL_TYPE = 0x800
+     * NW_DST = 172.20.30.45/24
+     * TP_DST = 80
+     *
+     * Then the function would return false as the two Match are not
+     * conflicting.
+     *
+     * Note: the mask value is taken into account only for MatchType.NW_SRC and
+     * MatchType.NW_DST
+     *
+     * @param match
+     *            the Match describing the filter
+     * @return true if the set of packets described by one match and the set of
+     *         packets described by the other match are disjoint, false
+     *         otherwise
+     */
+    public boolean conflictWithFilter(Match filter) {
+        return !this.intersetcs(filter);
+    }
+
+    /**
+     * Merge the current Match fields with the fields of the filter Match. A
+     * check is first run to see if this Match is compatible with the filter
+     * Match. If it is not, the merge is not attempted.
+     *
+     * The result is the match object representing the intersection of the set
+     * of packets described by this match with the set of packets described by
+     * the filter match. If the intersection of the two sets is empty, the
+     * return match will be null.
+     *
+     * @param filter
+     *            the match with which attempting the merge
+     * @return a new Match object describing the set of packets represented by
+     *         the intersection of this and the filter org.opendaylight.controller.sal.matches. null if the
+     *         intersection is empty.
+     */
+    public Match mergeWithFilter(Match filter) {
+        return this.getIntersection(filter);
+    }
+
+    /**
+     * Return the match representing the intersection of the set of packets
+     * described by this match with the set of packets described by the other
+     * match. Such as m.getIntersection(m) == m, m.getIntersection(u) == m and
+     * m.getIntersection(o) == o where u is an empty match (universal set, all
+     * packets) and o is the null match (empty set).
+     *
+     * @param other
+     *            the match with which computing the intersection
+     * @return a new Match object representing the intersection of the set of
+     *         packets described by this match with the set of packets described
+     *         by the other match. null when the intersection is the empty set.
+     */
+    public Match getIntersection(Match other) {
+        // If no intersection, return the empty set
+        if (!this.intersetcs(other)) {
+            return null;
+        }
+        // Check if any of the two is the universal match
+        if (this.isEmpty()) {
+            return other.clone();
+        }
+        if (other.isEmpty()) {
+            return this.clone();
+        }
+        // Get all the match types for both filters
+        Set<String> allTypes = new HashSet<String>(this.fields.keySet());
+        allTypes.addAll(new HashSet<String>(other.fields.keySet()));
+        // Derive the intersection
+        Match intersection = new Match();
+        for (String type : allTypes) {
+            if (this.isAny(type) && other.isAny(type)) {
+                continue;
+            }
+            if (this.isAny(type)) {
+                intersection.setField(other.getField(type).clone());
+                continue;
+            } else if (other.isAny(type)) {
+                intersection.setField(this.getField(type).clone());
+                continue;
+            }
+            // Either they are equal or it is about IP address
+            switch (type) {
+            // When it is about IP address, take the wider prefix address
+            // between the twos
+            case NwSrc.TYPE:
+            case NwDst.TYPE:
+                MatchField<?> thisField = this.getField(type);
+                MatchField<?> otherField = other.getField(type);
+                InetAddress thisAddress = (InetAddress) thisField.getValue();
+                InetAddress otherAddress = (InetAddress) otherField.getValue();
+                InetAddress thisMask = (InetAddress) thisField.getMask();
+                InetAddress otherMask = (InetAddress) otherField.getMask();
+
+                int thisMaskLen = (thisMask == null) ? ((thisAddress instanceof Inet4Address) ? 32 : 128) : NetUtils
+                        .getSubnetMaskLength(thisMask);
+                int otherMaskLen = (otherMask == null) ? ((otherAddress instanceof Inet4Address) ? 32 : 128) : NetUtils
+                        .getSubnetMaskLength(otherMask);
+
+                InetAddress subnetPrefix = null;
+                InetAddress subnetMask = null;
+                if (thisMaskLen < otherMaskLen) {
+                    subnetPrefix = NetUtils.getSubnetPrefix(otherAddress, otherMaskLen);
+                    subnetMask = otherMask;
+                } else {
+                    subnetPrefix = NetUtils.getSubnetPrefix(thisAddress, thisMaskLen);
+                    subnetMask = thisMask;
+                }
+                MatchField<?> field = (type.equals(NwSrc.TYPE)) ? new NwSrc(subnetPrefix, subnetMask) : new NwDst(
+                        subnetPrefix, subnetMask);
+                intersection.setField(field);
+                break;
+            default:
+                // this and other match field are equal for this type, pick this
+                // match field
+                intersection.setField(this.getField(type).clone());
+            }
+        }
+        return intersection;
+    }
+
+    /**
+     * Checks whether the intersection of the set of packets described by this
+     * match with the set of packets described by the other match is non empty
+     *
+     * For example, if this match is: DL_SRC = 00:cc:bb:aa:11:22
+     *
+     * and the other match is: DL_TYPE = 0x800 NW_SRC = 1.2.3.4
+     *
+     * then their respective matching packets set intersection is non empty:
+     * DL_SRC = 00:cc:bb:aa:11:22 DL_TYPE = 0x800 NW_SRC = 1.2.3.4
+     *
+     * @param other
+     *            the other match with which testing the intersection
+     * @return true if the intersection of the respective matching packets sets
+     *         is non empty
+     */
+    public boolean intersetcs(Match other) {
+        // No intersection with the empty set
+        if (other == null) {
+            return false;
+        }
+        // Always intersection with the universal set
+        if (this.isEmpty() || other.isEmpty()) {
+            return true;
+        }
+
+        // Get all the match types for both filters
+        Set<String> allTypes = new HashSet<String>(this.fields.keySet());
+        allTypes.addAll(new HashSet<String>(other.fields.keySet()));
+
+        // Iterate through all the match types defined in the two filters
+        for (String type : allTypes) {
+            if (this.isAny(type) || other.isAny(type)) {
+                continue;
+            }
+
+            MatchField<?> thisField = this.getField(type);
+            MatchField<?> otherField = other.getField(type);
+
+            switch (type) {
+            case DlSrc.TYPE:
+            case DlDst.TYPE:
+                if (!Arrays.equals((byte[]) thisField.getValue(), (byte[]) otherField.getValue())) {
+                    return false;
+                }
+                break;
+            case NwSrc.TYPE:
+            case NwDst.TYPE:
+                InetAddress thisAddress = (InetAddress) thisField.getValue();
+                InetAddress otherAddress = (InetAddress) otherField.getValue();
+                // Validity check
+                if (thisAddress instanceof Inet4Address && otherAddress instanceof Inet6Address
+                        || thisAddress instanceof Inet6Address && otherAddress instanceof Inet4Address) {
+                    return false;
+                }
+                InetAddress thisMask = (InetAddress) thisField.getMask();
+                InetAddress otherMask = (InetAddress) otherField.getMask();
+                if (NetUtils.inetAddressConflict(thisAddress, otherAddress, thisMask, otherMask)
+                        && NetUtils.inetAddressConflict(otherAddress, thisAddress, otherMask, thisMask)) {
+                    return false;
+                }
+                break;
+            default:
+                if (!thisField.getValue().equals(otherField.getValue())) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((fields == null) ? 0 : fields.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof Match)) {
+            return false;
+        }
+        Match other = (Match) obj;
+        if (fields == null) {
+            if (other.fields != null) {
+                return false;
+            }
+        } else if (!fields.equals(other.fields)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "Match[" + fields.values() + "]";
+    }
+}
diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/MatchField.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/MatchField.java
new file mode 100644 (file)
index 0000000..e7a5de3
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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.match.extensible;
+
+import java.io.Serializable;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Represents the generic matching field object
+ */
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public abstract class MatchField<T> implements Cloneable, Serializable {
+    private static final long serialVersionUID = 1L;
+    private String type;
+
+    // To satisfy JAXB
+    @SuppressWarnings("unused")
+    private MatchField() {
+    }
+
+    public MatchField(String type) {
+        this.type = type;
+    }
+
+    @XmlElement(name = "type")
+    public String getType() {
+        return type;
+    }
+
+    /**
+     * Returns the value set for this match field
+     *
+     * @return
+     */
+    public abstract T getValue();
+
+    @XmlElement(name = "value")
+    protected abstract String getValueString();
+
+    /**
+     * Returns the mask value set for this field match A null mask means this is
+     * a full match
+     *
+     * @return
+     */
+    public abstract T getMask();
+
+    @XmlElement(name = "mask")
+    protected abstract String getMaskString();
+
+    /**
+     * Returns whether the field match configuration is valid or not
+     *
+     * @return true if valid, false otherwise
+     */
+    public abstract boolean isValid();
+
+    public abstract boolean hasReverse();
+
+    /**
+     * Returns the reverse match field. For example for a MatchField matching on
+     * source ip 1.1.1.1 it will return a MatchField matching on destination IP
+     * 1.1.1.1. For not reversable MatchField, a copy of this MatchField will be
+     * returned
+     *
+     * @return the correspondent reverse MatchField object or a copy of this
+     *         object if the field is not reversable
+     */
+    public abstract MatchField<T> getReverse();
+
+    /**
+     * Returns whether the match field is congruent with IPv6 frames
+     *
+     * @return true if congruent with IPv6 frames
+     */
+    public abstract boolean isV6();
+
+    @Override
+    public abstract MatchField<T> clone();
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((type == null) ? 0 : type.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof MatchField)) {
+            return false;
+        }
+        MatchField<?> other = (MatchField<?>) obj;
+        if (type == null) {
+            if (other.type != null) {
+                return false;
+            }
+        } else if (!type.equals(other.type)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return (getMask() == null) ? String.format("%s(%s)", getType(), getValueString()) :
+            String.format("%s(%s,%s)", getType(), getValueString(), getMaskString());
+    }
+
+}
diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwDst.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwDst.java
new file mode 100644 (file)
index 0000000..42b6ba6
--- /dev/null
@@ -0,0 +1,131 @@
+package org.opendaylight.controller.sal.match.extensible;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class NwDst extends MatchField<InetAddress> {
+    private static final long serialVersionUID = 1L;
+    public static final String TYPE = "NW_DST";
+    private InetAddress address;
+    private InetAddress mask;
+
+    /**
+     * Creates a Match field for the network destination address
+     *
+     * @param address
+     *            the network address
+     * @param mask
+     *            the network mask
+     */
+    public NwDst(InetAddress address, InetAddress mask) {
+        super(TYPE);
+        this.address = address;
+        this.mask = mask;
+    }
+
+    // To satisfy JAXB
+    private NwDst() {
+        super(TYPE);
+    }
+
+    public NwDst(InetAddress address) {
+        super(TYPE);
+        this.address = address;
+        this.mask = null;
+    }
+
+    @Override
+    public InetAddress getValue() {
+        return address;
+    }
+
+    @Override
+    @XmlElement(name = "value")
+    protected String getValueString() {
+        return address.getHostAddress();
+    }
+
+    @Override
+    public InetAddress getMask() {
+        return mask;
+    }
+
+    @Override
+    @XmlElement(name = "mask")
+    protected String getMaskString() {
+        return (mask == null) ? "null" : mask.getHostAddress();
+    }
+
+    @Override
+    public boolean isValid() {
+        if (address != null) {
+            if (mask != null) {
+                return address instanceof Inet4Address && mask instanceof Inet4Address
+                        || address instanceof Inet6Address && mask instanceof Inet6Address;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean hasReverse() {
+        return true;
+    }
+
+    @Override
+    public NwSrc getReverse() {
+        return new NwSrc(address, mask);
+    }
+
+    @Override
+    public NwDst clone() {
+        return new NwDst(address, mask);
+    }
+
+    @Override
+    public boolean isV6() {
+        return address instanceof Inet6Address;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((address == null) ? 0 : address.hashCode());
+        result = prime * result + ((mask == null) ? 0 : mask.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof NwDst)) {
+            return false;
+        }
+        NwDst other = (NwDst) obj;
+        // Equality to be checked against prefix addresses
+        int thisMaskLen = (this.mask == null) ? ((this.address instanceof Inet4Address) ? 32 : 128) : NetUtils
+                .getSubnetMaskLength(this.mask);
+        int otherMaskLen = (other.mask == null) ? ((other.address instanceof Inet4Address) ? 32 : 128) : NetUtils
+                .getSubnetMaskLength(other.mask);
+
+        return NetUtils.getSubnetPrefix(address, thisMaskLen).equals(
+                NetUtils.getSubnetPrefix(other.address, otherMaskLen));
+    }
+}
diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwProtocol.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwProtocol.java
new file mode 100644 (file)
index 0000000..c5b5315
--- /dev/null
@@ -0,0 +1,116 @@
+package org.opendaylight.controller.sal.match.extensible;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class NwProtocol extends MatchField<Byte> {
+    private static final long serialVersionUID = 1L;
+    public static final String TYPE = "NW_PROTO";
+    private static final short MAX = 255;
+    private byte protocol;
+
+    /**
+     * Creates a Match field for the network protocol
+     *
+     * @param protocol
+     *            the protocol number
+     */
+    public NwProtocol(byte protocol) {
+        super(TYPE);
+        this.protocol = protocol;
+    }
+
+    public NwProtocol(int protocol) {
+        super(TYPE);
+        this.protocol = (byte) protocol;
+    }
+
+    public NwProtocol(short protocol) {
+        super(TYPE);
+        this.protocol = (byte) protocol;
+    }
+
+    // To satisfy JAXB
+    private NwProtocol() {
+        super(TYPE);
+    }
+
+    @Override
+    public Byte getValue() {
+        return protocol;
+    }
+
+    @Override
+    @XmlElement(name = "value")
+    protected String getValueString() {
+        return String.format("0X%s", Integer.toHexString(NetUtils.getUnsignedByte(protocol)));
+    }
+
+    @Override
+    public Byte getMask() {
+        return null;
+    }
+
+    @Override
+    protected String getMaskString() {
+        return null;
+    }
+
+    @Override
+    public boolean isValid() {
+        int intProtocol = NetUtils.getUnsignedByte(protocol);
+        return intProtocol >= 0 && intProtocol <= MAX;
+    }
+
+    @Override
+    public boolean hasReverse() {
+        return false;
+    }
+
+    @Override
+    public NwProtocol getReverse() {
+        return this.clone();
+    }
+
+    @Override
+    public NwProtocol clone() {
+        return new NwProtocol(protocol);
+    }
+
+    @Override
+    public boolean isV6() {
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + protocol;
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof NwProtocol)) {
+            return false;
+        }
+        NwProtocol other = (NwProtocol) obj;
+        if (protocol != other.protocol) {
+            return false;
+        }
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwSrc.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwSrc.java
new file mode 100644 (file)
index 0000000..4da43f5
--- /dev/null
@@ -0,0 +1,131 @@
+package org.opendaylight.controller.sal.match.extensible;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class NwSrc extends MatchField<InetAddress> {
+    private static final long serialVersionUID = 1L;
+    public static final String TYPE = "NW_SRC";
+    private InetAddress address;
+    private InetAddress mask;
+
+    /**
+     * Creates a Match field for the network source address
+     *
+     * @param address
+     *            the network address
+     * @param mask
+     *            the network mask
+     */
+    public NwSrc(InetAddress address, InetAddress mask) {
+        super(TYPE);
+        this.address = address;
+        this.mask = mask;
+    }
+
+    // To satisfy JAXB
+    private NwSrc() {
+        super(TYPE);
+    }
+
+    public NwSrc(InetAddress address) {
+        super(TYPE);
+        this.address = address;
+        this.mask = null;
+    }
+
+    @Override
+    public InetAddress getValue() {
+        return address;
+    }
+
+    @Override
+    @XmlElement(name = "value")
+    protected String getValueString() {
+        return address.toString();
+    }
+
+    @Override
+    public InetAddress getMask() {
+        return mask;
+    }
+
+    @Override
+    @XmlElement(name = "mask")
+    protected String getMaskString() {
+        return mask == null ? "null" : mask.toString();
+    }
+
+    @Override
+    public boolean isValid() {
+        if (address != null) {
+            if (mask != null) {
+                return address instanceof Inet4Address && mask instanceof Inet4Address
+                        || address instanceof Inet6Address && mask instanceof Inet6Address;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean hasReverse() {
+        return true;
+    }
+
+    @Override
+    public NwDst getReverse() {
+        return new NwDst(address, mask);
+    }
+
+    @Override
+    public NwSrc clone() {
+        return new NwSrc(address, mask);
+    }
+
+    @Override
+    public boolean isV6() {
+        return address instanceof Inet6Address;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((address == null) ? 0 : address.hashCode());
+        result = prime * result + ((mask == null) ? 0 : mask.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof NwSrc)) {
+            return false;
+        }
+        NwSrc other = (NwSrc) obj;
+        // Equality to be checked against prefix addresses
+        int thisMaskLen = (this.mask == null) ? ((this.address instanceof Inet4Address) ? 32 : 128) : NetUtils
+                .getSubnetMaskLength(this.mask);
+        int otherMaskLen = (other.mask == null) ? ((other.address instanceof Inet4Address) ? 32 : 128) : NetUtils
+                .getSubnetMaskLength(other.mask);
+
+        return NetUtils.getSubnetPrefix(address, thisMaskLen).equals(
+                NetUtils.getSubnetPrefix(other.address, otherMaskLen));
+    }
+}
diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwTos.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwTos.java
new file mode 100644 (file)
index 0000000..ba5b562
--- /dev/null
@@ -0,0 +1,115 @@
+package org.opendaylight.controller.sal.match.extensible;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class NwTos extends MatchField<Byte> {
+    private static final long serialVersionUID = 1L;
+    public static final String TYPE = "NW_TOS";
+    private static final short MAX = 63;
+    private byte tos;
+
+    /**
+     * Creates a Match field for the network TOS
+     *
+     * @param address
+     *            the network TOS
+     */
+    public NwTos(byte tos) {
+        super(TYPE);
+        this.tos = tos;
+    }
+
+    public NwTos(int tos) {
+        super(TYPE);
+        this.tos = (byte) tos;
+    }
+
+    public NwTos(short tos) {
+        super(TYPE);
+        this.tos = (byte) tos;
+    }
+
+    // To satisfy JAXB
+    private NwTos() {
+        super(TYPE);
+    }
+
+    @Override
+    public Byte getValue() {
+        return tos;
+    }
+
+    @Override
+    @XmlElement(name = "value")
+    protected String getValueString() {
+        return String.format("0X%s", Integer.toHexString(NetUtils.getUnsignedByte(tos)));
+    }
+
+    @Override
+    public Byte getMask() {
+        return null;
+    }
+
+    @Override
+    protected String getMaskString() {
+        return null;
+    }
+
+    @Override
+    public boolean isValid() {
+        return tos >= 0 && tos <= MAX;
+    }
+
+    @Override
+    public boolean hasReverse() {
+        return false;
+    }
+
+    @Override
+    public NwTos getReverse() {
+        return this.clone();
+    }
+
+    @Override
+    public NwTos clone() {
+        return new NwTos(tos);
+    }
+
+    @Override
+    public boolean isV6() {
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + tos;
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof NwTos)) {
+            return false;
+        }
+        NwTos other = (NwTos) obj;
+        if (tos != other.tos) {
+            return false;
+        }
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/TpDst.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/TpDst.java
new file mode 100644 (file)
index 0000000..022db4c
--- /dev/null
@@ -0,0 +1,104 @@
+package org.opendaylight.controller.sal.match.extensible;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class TpDst extends MatchField<Short> {
+    private static final long serialVersionUID = 1L;
+    public static final String TYPE = "TP_DST";
+    private short port;
+
+    /**
+     * Creates a Match field for the transport destination port
+     *
+     * @param port
+     *            the transport port
+     */
+    public TpDst(short port) {
+        super(TYPE);
+        this.port = port;
+    }
+
+    // To satisfy JAXB
+    private TpDst() {
+        super(TYPE);
+    }
+
+    @Override
+    public Short getValue() {
+        return port;
+    }
+
+    @Override
+    @XmlElement(name = "value")
+    protected String getValueString() {
+        return String.valueOf(NetUtils.getUnsignedShort(port));
+    }
+
+    @Override
+    public Short getMask() {
+        return null;
+    }
+
+    @Override
+    protected String getMaskString() {
+        return null;
+    }
+
+    @Override
+    public boolean isValid() {
+        return true;
+    }
+
+    @Override
+    public TpSrc getReverse() {
+        return new TpSrc(port);
+    }
+
+    @Override
+    public boolean hasReverse() {
+        return true;
+    }
+
+    @Override
+    public TpDst clone() {
+        return new TpDst(port);
+    }
+
+    @Override
+    public boolean isV6() {
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = super.hashCode();
+        result = prime * result + port;
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!super.equals(obj)) {
+            return false;
+        }
+        if (!(obj instanceof TpDst)) {
+            return false;
+        }
+        TpDst other = (TpDst) obj;
+        if (port != other.port) {
+            return false;
+        }
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/TpSrc.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/TpSrc.java
new file mode 100644 (file)
index 0000000..1c93d1e
--- /dev/null
@@ -0,0 +1,104 @@
+package org.opendaylight.controller.sal.match.extensible;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class TpSrc extends MatchField<Short> {
+    private static final long serialVersionUID = 1L;
+    public static final String TYPE = "TP_SRC";
+    private short port;
+
+    /**
+     * Creates a Match field for the Transport source port
+     *
+     * @param port
+     *            the transport port
+     */
+    public TpSrc(short port) {
+        super(TYPE);
+        this.port = port;
+    }
+
+    // To satisfy JAXB
+    private TpSrc() {
+        super(TYPE);
+    }
+
+    @Override
+    public Short getValue() {
+        return port;
+    }
+
+    @Override
+    @XmlElement(name = "value")
+    protected String getValueString() {
+        return String.valueOf(NetUtils.getUnsignedShort(port));
+    }
+
+    @Override
+    public Short getMask() {
+        return null;
+    }
+
+    @Override
+    protected String getMaskString() {
+        return null;
+    }
+
+    @Override
+    public boolean isValid() {
+        return true;
+    }
+
+    @Override
+    public boolean hasReverse() {
+        return true;
+    }
+
+    @Override
+    public TpDst getReverse() {
+        return new TpDst(port);
+    }
+
+    @Override
+    public TpSrc clone() {
+        return new TpSrc(port);
+    }
+
+    @Override
+    public boolean isV6() {
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = super.hashCode();
+        result = prime * result + port;
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!super.equals(obj)) {
+            return false;
+        }
+        if (!(obj instanceof TpSrc)) {
+            return false;
+        }
+        TpSrc other = (TpSrc) obj;
+        if (port != other.port) {
+            return false;
+        }
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/match/MatchExtensibleTest.java b/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/match/MatchExtensibleTest.java
new file mode 100644 (file)
index 0000000..0f49f42
--- /dev/null
@@ -0,0 +1,589 @@
+/*
+ * 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.match;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.match.extensible.DlDst;
+import org.opendaylight.controller.sal.match.extensible.DlSrc;
+import org.opendaylight.controller.sal.match.extensible.DlType;
+import org.opendaylight.controller.sal.match.extensible.DlVlan;
+import org.opendaylight.controller.sal.match.extensible.DlVlanPriority;
+import org.opendaylight.controller.sal.match.extensible.InPort;
+import org.opendaylight.controller.sal.match.extensible.Match;
+import org.opendaylight.controller.sal.match.extensible.MatchField;
+import org.opendaylight.controller.sal.match.extensible.NwDst;
+import org.opendaylight.controller.sal.match.extensible.NwProtocol;
+import org.opendaylight.controller.sal.match.extensible.NwSrc;
+import org.opendaylight.controller.sal.match.extensible.NwTos;
+import org.opendaylight.controller.sal.match.extensible.TpDst;
+import org.opendaylight.controller.sal.match.extensible.TpSrc;
+import org.opendaylight.controller.sal.utils.EtherTypes;
+import org.opendaylight.controller.sal.utils.IPProtocols;
+import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
+import org.opendaylight.controller.sal.utils.NodeCreator;
+
+public class MatchExtensibleTest {
+    @Test
+    public void testMatchCreation() {
+        Node node = NodeCreator.createOFNode(7L);
+        NodeConnector port = NodeConnectorCreator.createOFNodeConnector((short) 6, node);
+        MatchField<?> field = new InPort(port);
+
+        Assert.assertTrue(field != null);
+        Assert.assertEquals(field.getType(), InPort.TYPE);
+        Assert.assertEquals(field.getValue(), port);
+        Assert.assertTrue(field.isValid());
+
+
+        byte mac[] = { (byte) 0xaa, (byte) 0xbb, (byte) 0xcc, (byte) 0xdd, (byte) 11, (byte) 22 };
+        field = null;
+        field = new DlSrc(mac);
+        Assert.assertNotNull(field.getValue());
+
+        field = null;
+        field = new NwTos((byte) 0x22);
+        Assert.assertNotNull(field.getValue());
+    }
+
+    @Test
+    public void testMatchSetGet() {
+        Match x = new Match();
+        short val = 2346;
+        NodeConnector inPort = NodeConnectorCreator.createOFNodeConnector(val, NodeCreator.createOFNode(1L));
+        x.setField(new InPort(inPort));
+        Assert.assertEquals(x.getField(InPort.TYPE).getValue(), inPort);
+        Assert.assertTrue((Short) ((NodeConnector) x.getField(InPort.TYPE).getValue()).getID() == val);
+    }
+
+    @Test
+    public void testMatchSetGetMAC() {
+        Match x = new Match();
+        byte mac[] = { (byte) 0xaa, (byte) 0xbb, (byte) 0xcc, (byte) 0xdd, (byte) 11, (byte) 22 };
+        byte mac2[] = { (byte) 0xaa, (byte) 0xbb, 0, 0, 0, (byte) 0xbb };
+
+        x.setField(new DlSrc(mac));
+        x.setField(new DlDst(mac2));
+        Assert.assertArrayEquals(mac, (byte[]) x.getField(DlSrc.TYPE).getValue());
+        Assert.assertFalse(Arrays.equals((byte[]) x.getField(DlSrc.TYPE).getValue(), (byte[]) x.getField(DlDst.TYPE)
+                .getValue()));
+
+        x.setField(new DlDst(mac.clone()));
+        Assert.assertArrayEquals((byte[]) x.getField(DlSrc.TYPE).getValue(), (byte[]) x.getField(DlDst.TYPE).getValue());
+    }
+
+    @Test
+    public void testMatchSetGetNWAddr() throws UnknownHostException {
+        Match x = new Match();
+        String ip = "172.20.231.23";
+        InetAddress address = InetAddress.getByName(ip);
+        InetAddress mask = InetAddress.getByName("255.255.0.0");
+
+        x.setField(new NwSrc(address, mask));
+        Assert.assertEquals(address, x.getField(NwSrc.TYPE).getValue());
+        Assert.assertEquals(x.getField(NwSrc.TYPE).getMask(), mask);
+    }
+
+    @Test
+    public void testMatchSetGetEtherType() throws UnknownHostException {
+        Match x = new Match();
+
+        x.setField(new DlType(EtherTypes.QINQ.shortValue()));
+        Assert.assertEquals(x.getField(DlType.TYPE).getValue(), EtherTypes.QINQ.shortValue());
+
+        x.setField(new DlType(EtherTypes.LLDP.shortValue()));
+        Assert.assertEquals(x.getField(DlType.TYPE).getValue(), EtherTypes.LLDP.shortValue());
+        Assert.assertFalse(x.getField(DlType.TYPE).equals(EtherTypes.LLDP.intValue()));
+    }
+
+    @Test
+    public void testSetGetNwTos() {
+        Match x = new Match();
+        x.setField(new NwTos((byte) 0xb));
+
+        Byte t = new Byte((byte) 0xb);
+
+        Object o = x.getField(NwTos.TYPE).getValue();
+        Assert.assertEquals(o, t);
+        Assert.assertEquals(o, Byte.valueOf((byte)0xb));
+    }
+
+    @Test
+    public void testSetGetNwProto() {
+        Match x = new Match();
+        Byte proto = (byte) 199;
+        x.setField(new NwProtocol(proto));
+
+        Byte o = (Byte) x.getField(NwProtocol.TYPE).getValue();
+        Assert.assertEquals(o, proto);
+    }
+
+    @Test
+    public void testSetTpSrc() {
+        // Minimum value validation.
+        Match match = new Match();
+        short tp_src = 0;
+        match.setField(new TpSrc(tp_src));
+
+        Object o = match.getField(TpSrc.TYPE).getValue();
+        Assert.assertEquals(o, tp_src);
+
+        // Maximum value validation.
+        match = new Match();
+        tp_src = (short) 0xffff;
+        match.setField(new TpSrc(tp_src));
+
+        o = match.getField(TpSrc.TYPE).getValue();
+        Assert.assertEquals(o, tp_src);
+    }
+
+    @Test
+    public void testSetTpDst() {
+        // Minimum value validation.
+        Match match = new Match();
+        short tp_dst = 0;
+        match.setField(new TpDst(tp_dst));
+
+        Object o = match.getField(TpDst.TYPE).getValue();
+        Assert.assertTrue(o.equals(tp_dst));
+
+        // Maximum value validation.
+        match = new Match();
+        tp_dst = (short) 0xffff;
+        match.setField(new TpDst(tp_dst));
+
+        o = match.getField(TpDst.TYPE).getValue();
+        Assert.assertEquals(o, tp_dst);
+    }
+
+    @Test
+    public void testEquality() throws Exception {
+        Node node = NodeCreator.createOFNode(7L);
+        NodeConnector port = NodeConnectorCreator.createOFNodeConnector((short) 24, node);
+        NodeConnector port2 = NodeConnectorCreator.createOFNodeConnector((short) 24, node);
+        byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, (byte) 0x9a, (byte) 0xbc };
+        byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d, (byte) 0x5e, (byte) 0x6f };
+        byte srcMac2[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, (byte) 0x9a, (byte) 0xbc };
+        byte dstMac2[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d, (byte) 0x5e, (byte) 0x6f };
+        InetAddress srcIP = InetAddress.getByName("2001:420:281:1004:407a:57f4:4d15:c355");
+        InetAddress dstIP = InetAddress.getByName("2001:420:281:1004:e123:e688:d655:a1b0");
+        InetAddress ipMask = InetAddress.getByName("ffff:ffff:ffff:ffff:0:0:0:0");
+        InetAddress ipMaskd = InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0");
+        InetAddress ipMask2 = InetAddress.getByName("ffff:ffff:ffff:ffff:0:0:0:0");
+        InetAddress ipMaskd2 = InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0");
+        short ethertype = EtherTypes.IPv6.shortValue();
+        short ethertype2 = EtherTypes.IPv6.shortValue();
+        short vlan = (short) 27, vlan2 = (short) 27;
+        byte vlanPr = (byte) 3, vlanPr2 = (byte) 3;
+        Byte tos = 4, tos2 = 4;
+        byte proto = IPProtocols.UDP.byteValue(), proto2 = IPProtocols.UDP.byteValue();
+        short src = (short) 5500, src2 = (short) 5500;
+        short dst = 80, dst2 = 80;
+
+        /*
+         * Create a SAL Flow aFlow
+         */
+        Match match1 = new Match();
+        Match match2 = new Match();
+        match1.setField(new InPort(port));
+        match1.setField(new DlSrc(srcMac));
+        match1.setField(new DlDst(dstMac));
+        match1.setField(new DlType(ethertype));
+        match1.setField(new DlVlan(vlan));
+        match1.setField(new DlVlanPriority(vlanPr));
+        match1.setField(new NwSrc(srcIP, ipMask));
+        match1.setField(new NwDst(dstIP, ipMaskd));
+        match1.setField(new NwTos(tos));
+        match1.setField(new NwProtocol(proto));
+        match1.setField(new TpSrc(src));
+        match1.setField(new TpDst(dst));
+
+        match2.setField(new InPort(port2));
+        match2.setField(new DlSrc(srcMac2));
+        match2.setField(new DlDst(dstMac2));
+        match2.setField(new DlType(ethertype2));
+        match2.setField(new DlVlan(vlan2));
+        match2.setField(new DlVlanPriority(vlanPr2));
+        match2.setField(new NwSrc(srcIP, ipMask2));
+        match2.setField(new NwDst(dstIP, ipMaskd2));
+        match2.setField(new NwTos(tos2));
+        match2.setField(new NwProtocol(proto2));
+        match2.setField(new TpSrc(src2));
+        match2.setField(new TpDst(dst2));
+
+        Assert.assertTrue(match1.equals(match2));
+
+        Set<String> allFields = new HashSet<String>(match1.getMatchesList());
+        allFields.addAll(match2.getMatchesList());
+        // Make sure all values are equals
+        for (String type : allFields) {
+            if (match1.isPresent(type)) {
+                Assert.assertEquals(match1.getField(type), match2.getField(type));
+            }
+        }
+
+        // Make none of the fields couples are pointing to the same reference
+        MatchField<?> a = null, b = null;
+        for (String type : allFields) {
+            a = match1.getField(type);
+            b = match2.getField(type);
+            if (a != null && b != null) {
+                Assert.assertFalse(a == b);
+            }
+        }
+    }
+
+    @Test
+    public void testEqualityNetMask() throws Exception {
+
+        InetAddress srcIP = InetAddress.getByName("1.1.1.1");
+        InetAddress ipMask = InetAddress.getByName("255.255.255.255");
+        InetAddress srcIP2 = InetAddress.getByName("1.1.1.1");
+        InetAddress ipMask2 = null;
+        short ethertype = EtherTypes.IPv4.shortValue();
+        short ethertype2 = EtherTypes.IPv4.shortValue();
+
+        /*
+         * Create a SAL Flow aFlow
+         */
+        Match match1 = new Match();
+        Match match2 = new Match();
+
+        match1.setField(new DlType(ethertype));
+        match1.setField(new NwSrc(srcIP, ipMask));
+
+        match2.setField(new DlType(ethertype2));
+        match2.setField(new NwSrc(srcIP2, ipMask2));
+
+        Assert.assertTrue(match1.equals(match2));
+
+        ipMask2 = InetAddress.getByName("255.255.255.255");
+        match2.setField(new NwSrc(srcIP2, ipMask2));
+
+        srcIP = InetAddress.getByName("2001:420:281:1004:407a:57f4:4d15:c355");
+        srcIP2 = InetAddress.getByName("2001:420:281:1004:407a:57f4:4d15:c355");
+        ipMask = null;
+        ipMask2 = InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+        ethertype = EtherTypes.IPv6.shortValue();
+        ethertype2 = EtherTypes.IPv6.shortValue();
+
+        match1.setField(new DlType(ethertype));
+        match1.setField(new NwSrc(srcIP, ipMask));
+
+        match2.setField(new DlType(ethertype2));
+        match2.setField(new NwSrc(srcIP2, ipMask2));
+
+        Assert.assertEquals(match1, match2);
+    }
+
+    @Test
+    public void testHashCodeWithReverseMatch() throws Exception {
+        InetAddress srcIP1 = InetAddress.getByName("1.1.1.1");
+        InetAddress ipMask1 = InetAddress.getByName("255.255.255.255");
+        InetAddress srcIP2 = InetAddress.getByName("2.2.2.2");
+        InetAddress ipMask2 = InetAddress.getByName("255.255.255.255");
+        MatchField<?> field1 = new NwSrc(srcIP1, ipMask1);
+        MatchField<?> field2 = new NwDst(srcIP2, ipMask2);
+        Match match1 = new Match();
+        match1.setField(field1);
+        match1.setField(field2);
+        Match match2 = match1.reverse();
+        Assert.assertFalse(match1.hashCode() == match2.hashCode());
+    }
+
+    @Test
+    public void testHashCode() throws Exception {
+        byte srcMac1[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, (byte) 0x9a, (byte) 0xbc };
+        byte srcMac2[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, (byte) 0x9a, (byte) 0xbc };
+        byte dstMac1[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d, (byte) 0x5e, (byte) 0x6f };
+        byte dstMac2[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d, (byte) 0x5e, (byte) 0x6f };
+        short ethertype = EtherTypes.IPv4.shortValue();
+        short ethertype2 = EtherTypes.IPv4.shortValue();
+        InetAddress srcIP1 = InetAddress.getByName("1.1.1.1");
+        InetAddress ipMask1 = InetAddress.getByName("255.255.255.255");
+        InetAddress srcIP2 = InetAddress.getByName("1.1.1.1");
+        InetAddress ipMask2 = InetAddress.getByName("255.255.255.255");
+
+        Match match1 = new Match();
+        Match match2 = new Match();
+
+        MatchField<?> field1 = new DlSrc(srcMac1);
+        MatchField<?> field2 = new DlSrc(srcMac2);
+        Assert.assertTrue(field1.hashCode() == field2.hashCode());
+
+        match1.setField(field1);
+        match2.setField(field2);
+        Assert.assertTrue(match1.hashCode() == match2.hashCode());
+
+        MatchField<?> field3 = new DlDst(dstMac1);
+        MatchField<?> field4 = new DlDst(dstMac2);
+        Assert.assertTrue(field3.hashCode() == field4.hashCode());
+
+        match1.setField(field3);
+        match2.setField(field4);
+        Assert.assertTrue(match1.hashCode() == match2.hashCode());
+
+        MatchField<?> field5 = new DlType(ethertype);
+        MatchField<?> field6 = new DlType(ethertype2);
+        Assert.assertTrue(field5.hashCode() == field6.hashCode());
+
+        match1.setField(field5);
+        match2.setField(field6);
+        Assert.assertTrue(match1.hashCode() == match2 .hashCode());
+
+        MatchField<?> field7 = new NwSrc(srcIP1, ipMask1);
+        MatchField<?> field8 = new NwSrc(srcIP2, ipMask2);
+        Assert.assertTrue(field7.hashCode() == field8.hashCode());
+
+        match1.setField(field7);
+        match2.setField(field8);
+        Assert.assertTrue(match1.hashCode() == match2.hashCode());
+
+    }
+
+    @Test
+    public void testCloning() throws Exception {
+        Node node = NodeCreator.createOFNode(7L);
+        NodeConnector port = NodeConnectorCreator.createOFNodeConnector((short) 24, node);
+        byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, (byte) 0x9a, (byte) 0xbc };
+        byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d, (byte) 0x5e, (byte) 0x6f };
+        InetAddress srcIP = InetAddress.getByName("2001:420:281:1004:407a:57f4:4d15:c355");
+        InetAddress dstIP = InetAddress.getByName("2001:420:281:1004:e123:e688:d655:a1b0");
+        InetAddress ipMasks = InetAddress.getByName("ffff:ffff:ffff:ffff:0:0:0:0");
+        InetAddress ipMaskd = InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0");
+        short ethertype = EtherTypes.IPv6.shortValue();
+        short vlan = (short) 27;
+        byte vlanPr = (byte) 3;
+        Byte tos = 4;
+        byte proto = IPProtocols.UDP.byteValue();
+        short src = (short) 5500;
+        short dst = 80;
+
+        /*
+         * Create a SAL Flow aFlow
+         */
+        Match match = new Match();
+        match.setField(new InPort(port));
+        match.setField(new DlSrc(srcMac));
+        match.setField(new DlDst(dstMac));
+        match.setField(new DlType(ethertype));
+        match.setField(new DlVlan(vlan));
+        match.setField(new DlVlanPriority(vlanPr));
+        match.setField(new NwSrc(srcIP, ipMasks));
+        match.setField(new NwDst(dstIP, ipMaskd));
+        match.setField(new NwTos(tos));
+        match.setField(new NwProtocol(proto));
+        match.setField(new TpSrc(src));
+        match.setField(new TpDst(dst));
+
+        Match cloned = match.clone();
+
+        // Make sure all values are equals
+        for (String type : match.getMatchesList()) {
+            if (match.isPresent(type)) {
+                if (!match.getField(type).equals(cloned.getField(type))) {
+                    Assert.assertEquals(match.getField(type), cloned.getField(type));
+                }
+            }
+        }
+
+        // Make sure none of the fields couples are pointing to the same
+        // reference
+        MatchField<?> a = null, b = null;
+        for (String type : match.getMatchesList()) {
+            a = match.getField(type);
+            b = cloned.getField(type);
+            if (a != null && b != null) {
+                Assert.assertFalse(a == b);
+            }
+        }
+
+        Assert.assertTrue(match.equals(cloned));
+
+        Assert.assertEquals(match.getField(DlSrc.TYPE), cloned.getField(DlSrc.TYPE));
+        Assert.assertEquals(match.getField(NwDst.TYPE), cloned.getField(NwDst.TYPE));
+        Assert.assertEquals(match.getField(NwDst.TYPE).getMask(), cloned.getField(NwDst.TYPE).getMask());
+        Assert.assertEquals(match.hashCode(), cloned.hashCode());
+    }
+
+    @Test
+    public void testFlip() throws Exception {
+        Node node = NodeCreator.createOFNode(7L);
+        NodeConnector port = NodeConnectorCreator.createOFNodeConnector((short) 24, node);
+        byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, (byte) 0x9a, (byte) 0xbc };
+        byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d, (byte) 0x5e, (byte) 0x6f };
+        InetAddress srcIP = InetAddress.getByName("2001:420:281:1004:407a:57f4:4d15:c355");
+        InetAddress dstIP = InetAddress.getByName("2001:420:281:1004:e123:e688:d655:a1b0");
+        InetAddress ipMasks = InetAddress.getByName("ffff:ffff:ffff:ffff:0:0:0:0");
+        InetAddress ipMaskd = InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0");
+        short ethertype = EtherTypes.IPv6.shortValue();
+        short vlan = (short) 27;
+        byte vlanPr = (byte) 3;
+        Byte tos = 4;
+        byte proto = IPProtocols.UDP.byteValue();
+        short src = (short) 5500;
+        short dst = 80;
+
+        /*
+         * Create a SAL Flow aFlow
+         */
+        Match match = new Match();
+        match.setField(new InPort(port));
+        match.setField(new DlSrc(srcMac));
+        match.setField(new DlDst(dstMac));
+        match.setField(new DlType(ethertype));
+        match.setField(new DlVlan(vlan));
+        match.setField(new DlVlanPriority(vlanPr));
+        match.setField(new NwSrc(srcIP, ipMasks));
+        match.setField(new NwDst(dstIP, ipMaskd));
+        match.setField(new NwTos(tos));
+        match.setField(new NwProtocol(proto));
+        match.setField(new TpSrc(src));
+        match.setField(new TpDst(dst));
+
+        Match flipped = match.reverse();
+
+        Assert.assertEquals(match.getField(DlType.TYPE), flipped.getField(DlType.TYPE));
+        Assert.assertEquals(match.getField(DlVlan.TYPE), flipped.getField(DlVlan.TYPE));
+
+        Assert.assertArrayEquals((byte[]) match.getField(DlDst.TYPE).getValue(), (byte[]) flipped.getField(DlSrc.TYPE)
+                .getValue());
+
+        Assert.assertEquals(match.getField(NwDst.TYPE).getValue(), flipped.getField(NwSrc.TYPE).getValue());
+
+        Assert.assertEquals(match.getField(TpDst.TYPE).getValue(), flipped.getField(TpSrc.TYPE).getValue());
+
+        Match flipflip = flipped.reverse().reverse();
+        Assert.assertEquals(flipflip, flipped);
+
+    }
+
+    @Test
+    public void testVlanNone() throws Exception {
+        // The value 0 is used to indicate that no VLAN ID is set
+        short vlan = (short) 0;
+        MatchField<?> field = new DlVlan(vlan);
+
+        Assert.assertTrue(field != null);
+        Assert.assertEquals(field.getValue(), new Short(vlan));
+        Assert.assertTrue(field.isValid());
+    }
+
+    @Test
+    public void testIntersection() throws UnknownHostException {
+        Short ethType = Short.valueOf((short)0x800);
+        InetAddress ip1 = InetAddress.getByName("1.1.1.1");
+        InetAddress ip2 = InetAddress.getByName("1.1.1.0");
+        InetAddress ipm2 = InetAddress.getByName("255.255.255.0");
+        InetAddress ip3 = InetAddress.getByName("1.3.0.0");
+        InetAddress ipm3 = InetAddress.getByName("255.255.0.0");
+        InetAddress ip4 = InetAddress.getByName("1.3.4.4");
+        InetAddress ipm4 = InetAddress.getByName("255.255.255.0");
+
+        Match m1 = new Match();
+        m1.setField(new DlType(ethType));
+        m1.setField(new NwSrc(ip1));
+
+        Match m2 = new Match();
+        m2.setField(new DlType(ethType));
+        m2.setField(new NwSrc(ip2, ipm2));
+
+        Match m3 = new Match();
+        m3.setField(new DlType(ethType));
+        m3.setField(new NwSrc(ip3, ipm3));
+        m3.setField(new NwProtocol(IPProtocols.TCP.byteValue()));
+
+        Match m3r = m3.reverse();
+        Assert.assertTrue(m3.intersetcs(m3r));
+
+        Assert.assertTrue(m1.intersetcs(m2));
+        Assert.assertTrue(m2.intersetcs(m1));
+        Assert.assertFalse(m1.intersetcs(m3));
+        Assert.assertTrue(m1.intersetcs(m3r));
+        Assert.assertFalse(m3.intersetcs(m1));
+        Assert.assertTrue(m3.intersetcs(m1.reverse()));
+        Assert.assertFalse(m2.intersetcs(m3));
+        Assert.assertFalse(m3.intersetcs(m2));
+        Assert.assertTrue(m2.intersetcs(m3r));
+
+
+        Match i = m1.getIntersection(m2);
+        Assert.assertTrue(((Short)i.getField(DlType.TYPE).getValue()).equals(ethType));
+        // Verify intersection of IP addresses is correct
+        Assert.assertTrue(((InetAddress)i.getField(NwSrc.TYPE).getValue()).equals(ip1));
+        Assert.assertNull(i.getField(NwSrc.TYPE).getMask());
+
+        // Empty set
+        i = m2.getIntersection(m3);
+        Assert.assertNull(i);
+
+        Match m4 = new Match();
+        m4.setField(new DlType(ethType));
+        m4.setField(new NwProtocol(IPProtocols.TCP.byteValue()));
+        m3.setField(new NwSrc(ip4, ipm4));
+        Assert.assertTrue(m4.intersetcs(m3));
+
+        // Verify intersection of IP and IP mask addresses is correct
+        Match ii = m3.getIntersection(m4);
+        Assert.assertTrue(((InetAddress)ii.getField(NwSrc.TYPE).getValue()).equals(ip4));
+        Assert.assertTrue(((InetAddress)ii.getField(NwSrc.TYPE).getMask()).equals(ipm4));
+
+        Match m5 = new Match();
+        m5.setField(new DlType(ethType));
+        m3.setField(new NwSrc(ip3, ipm3));
+        m5.setField(new NwProtocol(IPProtocols.UDP.byteValue()));
+        Assert.assertFalse(m5.intersetcs(m3));
+        Assert.assertFalse(m5.intersetcs(m4));
+        Assert.assertTrue(m5.intersetcs(m5));
+        Assert.assertFalse(m3.intersetcs(m5));
+        Assert.assertFalse(m4.intersetcs(m5));
+
+
+        Match i2 = m4.getIntersection(m3);
+        Assert.assertFalse(i2.isEmpty());
+        Assert.assertFalse(i2.getMatchesList().isEmpty());
+        Assert.assertTrue(((InetAddress)i2.getField(NwSrc.TYPE).getValue()).equals(ip3));
+        Assert.assertTrue(((InetAddress)i2.getField(NwSrc.TYPE).getMask()).equals(ipm3));
+        Assert.assertTrue(((Byte)i2.getField(NwProtocol.TYPE).getValue()).equals(IPProtocols.TCP.byteValue()));
+
+        byte src[] = {(byte)0, (byte)0xab,(byte)0xbc,(byte)0xcd,(byte)0xde,(byte)0xef};
+        byte dst[] = {(byte)0x10, (byte)0x11,(byte)0x12,(byte)0x13,(byte)0x14,(byte)0x15};
+        Short srcPort = (short)1024;
+        Short dstPort = (short)80;
+
+        // Check identity
+        Match m6 = new Match();
+        m6.setField(new DlSrc(src));
+        m6.setField(new DlDst(dst));
+        m6.setField(new NwSrc(ip2, ipm2));
+        m6.setField(new NwDst(ip3, ipm3));
+        m6.setField(new NwProtocol(IPProtocols.UDP.byteValue()));
+        m6.setField(new TpSrc(srcPort));
+        m6.setField(new TpDst(dstPort));
+        Assert.assertTrue(m6.intersetcs(m6));
+        Assert.assertTrue(m6.getIntersection(m6).equals(m6));
+
+        // Empty match, represents the universal set (all packets)
+        Match u = new Match();
+        Assert.assertEquals(m6.getIntersection(u), m6);
+        Assert.assertEquals(u.getIntersection(m6), m6);
+
+        // No intersection with null match, empty set
+        Assert.assertNull(m6.getIntersection(null));
+    }
+}
diff --git a/opendaylight/sal/networkconfiguration/api/src/main/java/org/opendaylight/controller/sal/networkconfig/bridgedomain/BridgeDomainConfigServiceException.java b/opendaylight/sal/networkconfiguration/api/src/main/java/org/opendaylight/controller/sal/networkconfig/bridgedomain/BridgeDomainConfigServiceException.java
new file mode 100644 (file)
index 0000000..19f467e
--- /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.sal.networkconfig.bridgedomain;
+
+/**
+ * Exception thrown by IPluginInBridgeDomainConfigService implementations.
+ */
+public class BridgeDomainConfigServiceException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public BridgeDomainConfigServiceException(String message) {
+        super(message);
+    }
+
+    public BridgeDomainConfigServiceException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
+
index f8696b1cec347372d7cb27b8b28d43ee22eb38cb..c84136115c434c0a26367afcf1a438670e37f119 100644 (file)
@@ -29,7 +29,7 @@ public interface IPluginInBridgeDomainConfigService {
      * @note This method will return false if one or more of the supplied params is not supported by the
      * protocol plugin that serves the Node.
      */
-    public Status createBridgeDomain(Node node, String bridgeIdentifier, Map<ConfigConstants, Object> params) throws Throwable;
+    public Status createBridgeDomain(Node node, String bridgeIdentifier, Map<ConfigConstants, Object> params) throws BridgeDomainConfigServiceException;
 
     /**
      * Delete a Bridge Domain
index 64c72115f61519895ea3d8e3e44121b646319b77..14c5e0d9e72ada56a7d8d4c2f55e477e2a5f95f1 100644 (file)
@@ -14,6 +14,7 @@ import java.util.concurrent.ConcurrentMap;
 
 import org.opendaylight.controller.sal.core.Node;
 import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.networkconfig.bridgedomain.BridgeDomainConfigServiceException;
 import org.opendaylight.controller.sal.networkconfig.bridgedomain.ConfigConstants;
 import org.opendaylight.controller.sal.networkconfig.bridgedomain.IBridgeDomainConfigService;
 import org.opendaylight.controller.sal.networkconfig.bridgedomain.IPluginInBridgeDomainConfigService;
@@ -26,7 +27,7 @@ import org.slf4j.LoggerFactory;
 public class BridgeDomainConfigService implements IBridgeDomainConfigService {
     protected static final Logger logger = LoggerFactory
             .getLogger(BridgeDomainConfigService.class);
-    private ConcurrentMap<String, IPluginInBridgeDomainConfigService> pluginService =
+    private final ConcurrentMap<String, IPluginInBridgeDomainConfigService> pluginService =
             new ConcurrentHashMap<String, IPluginInBridgeDomainConfigService>();
 
     void setPluginInService (Map props, IPluginInBridgeDomainConfigService s) {
@@ -80,7 +81,7 @@ public class BridgeDomainConfigService implements IBridgeDomainConfigService {
 
     @Override
     public Status createBridgeDomain(Node node, String bridgeIdentifier, Map<ConfigConstants, Object> params)
-            throws Throwable {
+            throws BridgeDomainConfigServiceException {
         if (pluginService != null) {
             IPluginInBridgeDomainConfigService plugin = this.pluginService.get(node.getType());
             if (plugin != null) {
index 1a71511ef5bd0435fea671c08d10998dd9e18a00..ed9374b6faf22fbbd491126fac3d63f6a3c19ceb 100644 (file)
@@ -13,9 +13,6 @@ import java.util.Dictionary;
 import java.util.Hashtable;
 
 import org.apache.felix.dm.Component;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
 import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
 import org.opendaylight.controller.hosttracker.IfIptoHost;
@@ -30,6 +27,8 @@ import org.opendaylight.controller.samples.simpleforwarding.IBroadcastPortSelect
 import org.opendaylight.controller.switchmanager.IInventoryListener;
 import org.opendaylight.controller.switchmanager.ISwitchManager;
 import org.opendaylight.controller.topologymanager.ITopologyManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class Activator extends ComponentActivatorAbstractBase {
     protected static final Logger logger = LoggerFactory
@@ -45,6 +44,7 @@ public class Activator extends ComponentActivatorAbstractBase {
      * instantiated in order to get an fully working implementation
      * Object
      */
+    @Override
     public Object[] getImplementations() {
         Object[] res = { SimpleForwardingImpl.class,
                          SimpleBroadcastHandlerImpl.class };
@@ -64,12 +64,17 @@ public class Activator extends ComponentActivatorAbstractBase {
      * also optional per-container different behavior if needed, usually
      * should not be the case though.
      */
+    @Override
     public void configureInstance(Component c, Object imp, String containerName) {
         if (imp.equals(SimpleForwardingImpl.class)) {
+            Dictionary<String, Object> props = new Hashtable<String, Object>();
+            props.put("salListenerName", "simpleforwarding");
+
             // export the service
             c.setInterface(new String[] { IInventoryListener.class.getName(),
                     IfNewHostNotify.class.getName(),
-                    IListenRoutingUpdates.class.getName() }, null);
+                    IListenRoutingUpdates.class.getName(),
+                    IListenDataPacket.class.getName() }, props);
 
             c.add(createContainerServiceDependency(containerName).setService(
                     IClusterContainerServices.class).setCallbacks(
@@ -96,7 +101,11 @@ public class Activator extends ComponentActivatorAbstractBase {
             c.add(createContainerServiceDependency(containerName).setService(
                     IRouting.class).setCallbacks("setRouting", "unsetRouting")
                     .setRequired(false));
-        }else if (imp.equals(SimpleBroadcastHandlerImpl.class)) {
+            c.add(createContainerServiceDependency(containerName).setService(
+                    IDataPacketService.class).setCallbacks("setDataPacketService",
+                   "unsetDataPacketService").setRequired(false));
+
+        } else if (imp.equals(SimpleBroadcastHandlerImpl.class)) {
             Dictionary<String, String> props = new Hashtable<String, String>();
             props.put("salListenerName", "simplebroadcasthandler");
 
index d2016b1f6337b6923662268ae7d8029d34ae85b0..e39ae6569350e69d47fcc20fa15e20c1caa400bb 100644 (file)
@@ -12,12 +12,12 @@ import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
-import org.opendaylight.controller.sal.packet.Ethernet;
-import org.opendaylight.controller.sal.packet.IDataPacketService;
-import org.opendaylight.controller.sal.packet.IListenDataPacket;
 import org.opendaylight.controller.sal.core.ConstructionException;
 import org.opendaylight.controller.sal.core.Node;
 import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.IDataPacketService;
+import org.opendaylight.controller.sal.packet.IListenDataPacket;
 import org.opendaylight.controller.sal.packet.Packet;
 import org.opendaylight.controller.sal.packet.PacketResult;
 import org.opendaylight.controller.sal.packet.RawPacket;
@@ -54,7 +54,7 @@ public class SimpleBroadcastHandlerImpl implements IBroadcastHandler, IListenDat
     public PacketResult receiveDataPacket(RawPacket inPkt) {
         /*
          * note that this assumes that the protocol plugin will do appropriate
-         * filtering to ensure that this only receives packets for it's
+         * filtering to ensure that this only receives packets for its
          * container.
          */
 
@@ -216,6 +216,7 @@ public class SimpleBroadcastHandlerImpl implements IBroadcastHandler, IListenDat
         lock.writeLock().unlock();
     }
 
+    @Override
     public void setMode(BroadcastMode m) {
         lock.writeLock().lock();
         mode = m;
index 4b3363b832d72ebbd12215d7236bba43009f4af3..b9d8fee986131328ea00be70394eb66f61b246ce 100644 (file)
@@ -9,6 +9,7 @@
 
 package org.opendaylight.controller.samples.simpleforwarding.internal;
 
+import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.HashMap;
@@ -45,9 +46,17 @@ import org.opendaylight.controller.sal.core.UpdateType;
 import org.opendaylight.controller.sal.flowprogrammer.Flow;
 import org.opendaylight.controller.sal.match.Match;
 import org.opendaylight.controller.sal.match.MatchType;
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.IDataPacketService;
+import org.opendaylight.controller.sal.packet.IListenDataPacket;
+import org.opendaylight.controller.sal.packet.IPv4;
+import org.opendaylight.controller.sal.packet.Packet;
+import org.opendaylight.controller.sal.packet.PacketResult;
+import org.opendaylight.controller.sal.packet.RawPacket;
 import org.opendaylight.controller.sal.routing.IListenRoutingUpdates;
 import org.opendaylight.controller.sal.routing.IRouting;
 import org.opendaylight.controller.sal.utils.EtherTypes;
+import org.opendaylight.controller.sal.utils.NetUtils;
 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
 import org.opendaylight.controller.sal.utils.Status;
 import org.opendaylight.controller.samples.simpleforwarding.HostNodePair;
@@ -70,11 +79,10 @@ import org.slf4j.LoggerFactory;
  * installs those rules using <tt>installPerHostRules()</tt>.
  */
 public class SimpleForwardingImpl implements IfNewHostNotify,
-        IListenRoutingUpdates, IInventoryListener {
-    private static Logger log = LoggerFactory
-            .getLogger(SimpleForwardingImpl.class);
+        IListenRoutingUpdates, IInventoryListener, IListenDataPacket {
+    private static Logger log = LoggerFactory.getLogger(SimpleForwardingImpl.class);
     private static short DEFAULT_IPSWITCH_PRIORITY = 1;
-    private static String FORWARDING_RULES_CACHE_NAME = "forwarding.ipswitch.rules";
+    static final String FORWARDING_RULES_CACHE_NAME = "forwarding.ipswitch.rules";
     private IfIptoHost hostTracker;
     private IForwardingRulesManager frm;
     private ITopologyManager topologyManager;
@@ -90,6 +98,7 @@ public class SimpleForwardingImpl implements IfNewHostNotify,
     private Map<Node, List<FlowEntry>> tobePrunedPos = new HashMap<Node, List<FlowEntry>>();
     private IClusterContainerServices clusterContainerService = null;
     private ISwitchManager switchManager;
+    private IDataPacketService dataPacketService;
 
     /**
      * Return codes from the programming of the perHost rules in HW
@@ -97,8 +106,19 @@ public class SimpleForwardingImpl implements IfNewHostNotify,
     public enum RulesProgrammingReturnCode {
         SUCCESS, FAILED_FEW_SWITCHES, FAILED_ALL_SWITCHES, FAILED_WRONG_PARAMS
     }
+    public void setDataPacketService(IDataPacketService s) {
+        log.debug("Setting dataPacketService");
+        this.dataPacketService = s;
+    }
+
+    public void unsetDataPacketService(IDataPacketService s) {
+        if (this.dataPacketService == s) {
+            this.dataPacketService = null;
+        }
+    }
 
     public void setRouting(IRouting routing) {
+        log.debug("Setting routing");
         this.routing = routing;
     }
 
@@ -108,10 +128,6 @@ public class SimpleForwardingImpl implements IfNewHostNotify,
         }
     }
 
-    public ITopologyManager getTopologyManager() {
-        return topologyManager;
-    }
-
     public void setTopologyManager(ITopologyManager topologyManager) {
         log.debug("Setting topologyManager");
         this.topologyManager = topologyManager;
@@ -670,8 +686,7 @@ public class SimpleForwardingImpl implements IfNewHostNotify,
      *
      * @return a return code that convey the programming status of the HW
      */
-    private RulesProgrammingReturnCode uninstallPerHostRules(
-            HostNodeConnector host) {
+    private RulesProgrammingReturnCode uninstallPerHostRules(HostNodeConnector host) {
         RulesProgrammingReturnCode retCode = RulesProgrammingReturnCode.SUCCESS;
         Map<NodeConnector, FlowEntry> pos;
         FlowEntry po;
@@ -768,16 +783,12 @@ public class SimpleForwardingImpl implements IfNewHostNotify,
         for (Node swId : switches) {
             List<FlowEntry> pl = tobePrunedPos.get(swId);
             if (pl != null) {
-                log
-                        .debug(
-                                "Policies for Switch: {} in the list to be deleted: {}",
-                                swId, pl);
+                log.debug("Policies for Switch: {} in the list to be deleted: {}", swId, pl);
                 Iterator<FlowEntry> plIter = pl.iterator();
                 //for (Policy po: pl) {
                 while (plIter.hasNext()) {
                     FlowEntry po = plIter.next();
-                    log.error("Removing Policy, Switch: {} Policy: {}", swId,
-                            po);
+                    log.error("Removing Policy, Switch: {} Policy: {}", swId, po);
                     this.frm.uninstallFlowEntry(po);
                     plIter.remove();
                 }
@@ -962,4 +973,56 @@ public class SimpleForwardingImpl implements IfNewHostNotify,
             this.switchManager = null;
         }
     }
+
+    @Override
+    public PacketResult receiveDataPacket(RawPacket inPkt) {
+        if (inPkt == null) {
+            return PacketResult.IGNORED;
+        }
+        log.trace("Received a frame of size: {}", inPkt.getPacketData().length);
+        Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
+        if (formattedPak instanceof Ethernet) {
+            Object nextPak = formattedPak.getPayload();
+            if (nextPak instanceof IPv4) {
+                log.trace("Handle punted IP packet: {}", formattedPak);
+                handlePuntedIPPacket((IPv4) nextPak, inPkt.getIncomingNodeConnector());
+            }
+        }
+        return PacketResult.IGNORED;
+
+    }
+
+    private void handlePuntedIPPacket(IPv4 pkt, NodeConnector incomingNodeConnector) {
+        InetAddress dIP = NetUtils.getInetAddress(pkt.getDestinationAddress());
+        if (dIP == null || hostTracker == null) {
+            log.debug("Invalid param(s) in handlePuntedIPPacket.. DestIP: {}. hostTracker: {}", dIP, hostTracker);
+            return;
+        }
+        HostNodeConnector destHost = hostTracker.hostFind(dIP);
+        if (destHost != null
+                && (routing == null ||
+                    routing.getRoute(incomingNodeConnector.getNode(), destHost.getnodeconnectorNode()) != null)) {
+
+            log.trace("Host {} is at {}", dIP, destHost.getnodeConnector());
+            HostNodePair key = new HostNodePair(destHost, incomingNodeConnector.getNode());
+
+            // If SimpleForwarding is aware of this host, it will try to install
+            // a path. Forward packet until it's done.
+            if (dataPacketService != null && this.rulesDB.containsKey(key)) {
+
+
+                /*
+                 * if we know where the host is and there's a path from where this
+                 * packet was punted to where the host is, then attempt best effort delivery to the host
+                 */
+                NodeConnector nc = destHost.getnodeConnector();
+                log.trace("Forwarding punted IP received at {} to {}", incomingNodeConnector, nc);
+                // re-encode the Ethernet packet (the parent of the IPv4 packet)
+                RawPacket rp = this.dataPacketService.encodeDataPacket(pkt.getParent());
+                rp.setOutgoingNodeConnector(nc);
+                this.dataPacketService.transmitDataPacket(rp);
+            }
+
+        }
+    }
 }
index e4bb790676379bfb508ea6b6eec4051a38286636..a9f11fafb5c2ae2e9434dc24f232f21b1a3b59d1 100644 (file)
@@ -774,7 +774,7 @@ public class Devices implements IDaylightWeb {
         return result;
     }
 
-    @RequestMapping(value = "/connect/{nodeId}", method = RequestMethod.POST)
+    @RequestMapping(value = "/connect/{nodeId:.+}", method = RequestMethod.POST)
     @ResponseBody
     public Status addNode(HttpServletRequest request, @PathVariable("nodeId") String nodeId,
             @RequestParam(required = true) String ipAddress, @RequestParam(required = true) String port,
@@ -811,7 +811,7 @@ public class Devices implements IDaylightWeb {
         return new Status(StatusCode.SUCCESS);
     }
 
-    @RequestMapping(value = "/disconnect/{nodeId}", method = RequestMethod.POST)
+    @RequestMapping(value = "/disconnect/{nodeId:.+}", method = RequestMethod.POST)
     @ResponseBody
     public Status removeNode(HttpServletRequest request, @PathVariable("nodeId") String nodeId,
             @RequestParam(required = true) String nodeType) {
index 259ffde4b447e1d5d8c916ff96a26ee38198b3bf..9c3c895916b03f88c579996d91d27c15b842776c 100644 (file)
@@ -14,11 +14,15 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeMap;
 
 import javax.servlet.http.HttpServletRequest;
 
 import org.opendaylight.controller.forwardingrulesmanager.FlowConfig;
 import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
+import org.opendaylight.controller.sal.action.Action;
+import org.opendaylight.controller.sal.action.ActionType;
+import org.opendaylight.controller.sal.action.SupportedFlowActions;
 import org.opendaylight.controller.sal.authorization.Privilege;
 import org.opendaylight.controller.sal.authorization.UserLevel;
 import org.opendaylight.controller.sal.core.Description;
@@ -212,7 +216,6 @@ public class Flows implements IDaylightWeb {
         return nodes;
     }
 
-
     @RequestMapping(value = "/flow", method = RequestMethod.POST)
     @ResponseBody
     public String actionFlow(@RequestParam(required = true) String action, @RequestParam(required = false) String body,
@@ -337,6 +340,83 @@ public class Flows implements IDaylightWeb {
         }
     }
 
+    @RequestMapping(value = "/valid-flows/{nodeId}")
+    @ResponseBody
+    public Object getValidActions(HttpServletRequest request, @RequestParam(required = false) String container,
+            @PathVariable("nodeId") String nodeId) {
+        String containerName = (container == null) ? GlobalConstants.DEFAULT.toString() : container;
+
+        // Authorization check
+        String userName = request.getUserPrincipal().getName();
+        if (DaylightWebUtil.getContainerPrivilege(userName, containerName, this) != Privilege.WRITE) {
+            return "Operation not authorized";
+        }
+
+        ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
+        if (switchManager == null) {
+            return null;
+        }
+
+        Map<String, String> result = new TreeMap<String, String>();
+
+        Node node = Node.fromString(nodeId);
+        SupportedFlowActions supportedFlows = (SupportedFlowActions) switchManager.getNodeProp(node, "supportedFlowActions");
+        List<Class<? extends Action>> actions = supportedFlows.getActions();
+        for (Class<? extends Action> action : actions) {
+            if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Drop.class)) {
+                result.put(ActionType.DROP.toString(), "Drop");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Loopback.class)) {
+                result.put(ActionType.LOOPBACK.toString(), "Loopback");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Flood.class)) {
+                result.put(ActionType.FLOOD.toString(), "Flood");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.FloodAll.class)) {
+                result.put(ActionType.FLOOD_ALL.toString(), "Flood All");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Controller.class)) {
+                result.put(ActionType.CONTROLLER.toString(), "Controller");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SwPath.class)) {
+                result.put(ActionType.SW_PATH.toString(), "Software Path");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.HwPath.class)) {
+                result.put(ActionType.HW_PATH.toString(), "Hardware Path");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Output.class)) {
+                result.put(ActionType.OUTPUT.toString(), "Output");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Enqueue.class)) {
+                result.put(ActionType.ENQUEUE.toString(), "Enqueue");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetDlSrc.class)) {
+                result.put(ActionType.SET_DL_SRC.toString(), "Set Datalayer Source Address");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetDlDst.class)) {
+                result.put(ActionType.SET_DL_DST.toString(), "Set Datalayer Destination Address");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetVlanId.class)) {
+                result.put(ActionType.SET_VLAN_ID.toString(), "Set VLAN ID");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetVlanPcp.class)) {
+                result.put(ActionType.SET_VLAN_PCP.toString(), "Set VLAN Priority");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetVlanCfi.class)) {
+                result.put(ActionType.SET_VLAN_CFI.toString(), "Set VLAN CFI");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.PopVlan.class)) {
+                result.put(ActionType.POP_VLAN.toString(), "Pop VLAN");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.PushVlan.class)) {
+                result.put(ActionType.PUSH_VLAN.toString(), "Push VLAN");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetDlType.class)) {
+                result.put(ActionType.SET_DL_TYPE.toString(), "Set EtherType");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetNwSrc.class)) {
+                result.put(ActionType.SET_NW_SRC.toString(), "Set Network Source Address");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetNwDst.class)) {
+                result.put(ActionType.SET_NW_DST.toString(), "Set Network Destination Address");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetNwTos.class)) {
+                result.put(ActionType.SET_NW_TOS.toString(), "Modify ToS Bits");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetTpSrc.class)) {
+                result.put(ActionType.SET_TP_SRC.toString(), "Modify Transport Source Port");
+            } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetTpDst.class)) {
+                result.put(ActionType.SET_TP_DST.toString(), "Modify Transport Destination Port");
+            }
+        }
+
+        return result;
+    }
+
+    private boolean actionCompare(String name, ActionType type) {
+        return name.equals(type.getId().toLowerCase());
+    }
+
     private String getNodeDesc(Node node, ISwitchManager switchManager) {
         Description desc = (Description) switchManager.getNodeProp(node, Description.propertyName);
         String description = (desc == null) ? "" : desc.getValue();
index ab8301bf73dd21fb842aeee16b3a80dc647039a2..6e7fd25e049a17fc997b30c5d2003227e1b9a61f 100644 (file)
@@ -11,1593 +11,1723 @@ one.f = {};
 
 // specify dashlets and layouts
 one.f.dashlet = {
-    flows : {
-        id : 'flows',
-        name : 'Flow Entries'
-    },
-    nodes : {
-        id : 'nodes',
-        name : 'Nodes'
-    },
-    detail : {
-        id : 'detail',
-        name : 'Flow Detail'
-    }
+  flows : {
+    id : 'flows',
+    name : 'Flow Entries'
+  },
+  nodes : {
+    id : 'nodes',
+    name : 'Nodes'
+  },
+  detail : {
+    id : 'detail',
+    name : 'Flow Detail'
+  }
 };
 
 one.f.menu = {
-    left : {
-        top : [
-            one.f.dashlet.flows
-        ],
-        bottom : [
-            one.f.dashlet.nodes
-        ]
-    },
-    right : {
-        top : [],
-        bottom : [
-            one.f.dashlet.detail
-        ]
-    }
+  left : {
+    top : [
+      one.f.dashlet.flows
+      ],
+    bottom : [
+      one.f.dashlet.nodes
+      ]
+  },
+  right : {
+    top : [],
+    bottom : [
+      one.f.dashlet.detail
+      ]
+  }
 };
 
 one.f.address = {
-    root : "/controller/web/flows",
-    flows : {
-        main : "/main",
-        flows : "/node-flows",
-        nodes : "/node-ports",
-        flow : "/flow",
-        modifyFlow : "/modifyFlow",
-        deleteFlows:"/flow/deleteFlows"
-    }
+  root : "/controller/web/flows",
+  flows : {
+    main : "/main",
+    flows : "/node-flows",
+    nodes : "/node-ports",
+    flow : "/flow",
+    modifyFlow : "/modifyFlow",
+    deleteFlows:"/flow/deleteFlows"
+  }
 }
 
 /** NODES **/
 one.f.nodes = {
-    id : {
-        dashlet: {
-            datagrid: "one_f_nodes_id_dashlet_datagrid"
-        }
-    },
-    registry : {},
-    dashlet : function($dashlet) {
-        var $h4 = one.lib.dashlet.header("Nodes");
-        $dashlet.append($h4);
-
-        one.f.nodes.ajax.dashlet(function(data) {
-            var $gridHTML = one.lib.dashlet.datagrid.init(one.f.nodes.id.dashlet.datagrid, {
-                searchable: true,
-                filterable: false,
-                pagination: true,
-                flexibleRowsPerPage: true
-                }, "table-striped table-condensed");
-            $dashlet.append($gridHTML);
-            var dataSource = one.f.nodes.data.nodesDataGrid(data);
-            $("#" + one.f.nodes.id.dashlet.datagrid).datagrid({dataSource: dataSource});
-        });
-    },
-    ajax : {
-        dashlet : function(callback) {
-            $.getJSON(one.f.address.root+one.f.address.flows.flows, function(data) {
-                callback(data);
-            });
-        }
-    },
-    data : {
-        nodesDataGrid: function(data) {
-            var gridData = [];
-            $.each(data, function(nodeName, flow) {
-                var nodeFlowObject = {};
-                nodeFlowObject["nodeName"] = nodeName;
-                nodeFlowObject["flows"] = flow;
-                nodeFlowObject["rowData"] = nodeName + flow + "-foobar";
-                gridData.push(nodeFlowObject);
-            });
+  id : {
+    dashlet: {
+      datagrid: "one_f_nodes_id_dashlet_datagrid"
+    }
+  },
+  registry : {},
+  dashlet : function($dashlet) {
+    var $h4 = one.lib.dashlet.header("Nodes");
+    $dashlet.append($h4);
 
-            var source = new StaticDataSource({
-                    columns: [
-                        {
-                            property: 'nodeName',
-                            label: 'Node',
-                            sortable: true
-                        },
-                        {
-                            property: 'flows',
-                            label: 'Flows',
-                            sortable: true
-                        }
-                    ],
-                    data: gridData,
-                    delay: 0
-                });
-            return source;
-        }
-    },
-    body : {
-        dashlet : function(body, callback) {
-            var attributes = ['table-striped', 'table-bordered', 'table-hover', 'table-condensed'];
-            var $table = one.lib.dashlet.table.table(attributes);
+    one.f.nodes.ajax.dashlet(function(data) {
+      var $gridHTML = one.lib.dashlet.datagrid.init(one.f.nodes.id.dashlet.datagrid, {
+        searchable: true,
+          filterable: false,
+          pagination: true,
+          flexibleRowsPerPage: true
+      }, "table-striped table-condensed");
+      $dashlet.append($gridHTML);
+      var dataSource = one.f.nodes.data.nodesDataGrid(data);
+      $("#" + one.f.nodes.id.dashlet.datagrid).datagrid({dataSource: dataSource});
+    });
+  },
+  ajax : {
+    dashlet : function(callback) {
+      $.getJSON(one.f.address.root+one.f.address.flows.flows, function(data) {
+        callback(data);
+      });
+    }
+  },
+  data : {
+    nodesDataGrid: function(data) {
+      var gridData = [];
+      $.each(data, function(nodeName, flow) {
+        var nodeFlowObject = {};
+        nodeFlowObject["nodeName"] = nodeName;
+        nodeFlowObject["flows"] = flow;
+        nodeFlowObject["rowData"] = nodeName + flow + "-foobar";
+        gridData.push(nodeFlowObject);
+      });
+
+      var source = new StaticDataSource({
+        columns: [
+      {
+        property: 'nodeName',
+          label: 'Node',
+          sortable: true
+      },
+          {
+            property: 'flows',
+          label: 'Flows',
+          sortable: true
+          }
+      ],
+          data: gridData,
+          delay: 0
+      });
+      return source;
+    }
+  },
+  body : {
+    dashlet : function(body, callback) {
+      var attributes = ['table-striped', 'table-bordered', 'table-hover', 'table-condensed'];
+      var $table = one.lib.dashlet.table.table(attributes);
 
-            var headers = ['Node', 'Flows'];
-            var $thead = one.lib.dashlet.table.header(headers);
-            $table.append($thead);
+      var headers = ['Node', 'Flows'];
+      var $thead = one.lib.dashlet.table.header(headers);
+      $table.append($thead);
 
-            var $tbody = one.lib.dashlet.table.body(body);
-            $table.append($tbody);
+      var $tbody = one.lib.dashlet.table.body(body);
+      $table.append($tbody);
 
-            return $table;
-        }
+      return $table;
     }
+  }
 }
 
 /** FLOW DETAIL **/
 one.f.detail = {
-    id : {},
-    registry : {},
-    dashlet : function($dashlet, details) {
-        var $h4 = one.lib.dashlet.header("Flow Details");
-        $dashlet.append($h4);
-
-        // details
-        if (details == undefined) {
-            var $none = $(document.createElement('div'));
-            $none.addClass('none');
-            var $p = $(document.createElement('p'));
-            $p.text('Please select a flow');
-            $p.addClass('text-center').addClass('text-info');
+  id : {},
+  registry : {},
+  dashlet : function($dashlet, details) {
+    var $h4 = one.lib.dashlet.header("Flow Details");
+    $dashlet.append($h4);
 
-            $dashlet.append($none)
-                .append($p);
-        }
-    },
-    data : {
-        dashlet : function(data) {
-            var body = [];
-            var tr = {};
-            var entry = [];
-
-            entry.push(data['name']);
-            entry.push(data['node']);
-            entry.push(data['flow']['priority']);
-            entry.push(data['flow']['hardTimeout']);
-            entry.push(data['flow']['idleTimeout']);
-
-            tr.entry = entry;
-            body.push(tr);
-            return body;
-        },
-        description : function(data) {
-            var body = [];
-            var tr = {};
-            var entry = [];
-            entry.push(data['flow']['ingressPort']);
-            entry.push(data['flow']['etherType']);
-            entry.push(data['flow']['vlanId']);
-            entry.push(data['flow']['vlanPriority']);
-            entry.push(data['flow']['srcMac']);
-            entry.push(data['flow']['dstMac']);
-            entry.push(data['flow']['srcIp']);
-            entry.push(data['flow']['dstIp']);
-            entry.push(data['flow']['tosBits']);
-            entry.push(data['flow']['srcPort']);
-            entry.push(data['flow']['dstPort']);
-            entry.push(data['flow']['protocol']);
-            entry.push(data['flow']['cookie']);
-
-            tr.entry = entry;
-            body.push(tr);
-            return body;
-        },
-        actions : function(data) {
-            var body = [];
-            var tr = {};
-            var entry = [];
-            var actions = '';
-
-            $(data['flow']['actions']).each(function(index, value) {
-                var locEqualTo = value.indexOf("=");
-                if ( locEqualTo == -1 ) {
-                    actions += value + ', ';
-                } else {
-                    var action = value.substr(0,locEqualTo);
-                    if( action == "OUTPUT") {
-                        var portIds = value.substr(locEqualTo+1).split(",");
-                        actions += action + "=";
-                        var allPorts = one.f.flows.registry.nodeports[one.f.flows.registry.selectedNode]['ports'];
-                        for(var i =0; i < portIds.length ; i++) {
-                            var portName = allPorts[portIds[i]];
-                            actions += portName + ", ";
-                        }
-                    } else {
-                        actions += value + ', ';
-                    }
-                }
-            });
-            actions = actions.slice(0,-2);
-            entry.push(actions);
+    // details
+    if (details == undefined) {
+      var $none = $(document.createElement('div'));
+      $none.addClass('none');
+      var $p = $(document.createElement('p'));
+      $p.text('Please select a flow');
+      $p.addClass('text-center').addClass('text-info');
 
-            tr.entry = entry;
-            body.push(tr);
-            return body;
+      $dashlet.append($none)
+        .append($p);
+    }
+  },
+  data : {
+    dashlet : function(data) {
+      var body = [];
+      var tr = {};
+      var entry = [];
+
+      entry.push(data['name']);
+      entry.push(data['node']);
+      entry.push(data['flow']['priority']);
+      entry.push(data['flow']['hardTimeout']);
+      entry.push(data['flow']['idleTimeout']);
+
+      tr.entry = entry;
+      body.push(tr);
+      return body;
+    },
+    description : function(data) {
+      var body = [];
+      var tr = {};
+      var entry = [];
+      entry.push(data['flow']['ingressPort']);
+      entry.push(data['flow']['etherType']);
+      entry.push(data['flow']['vlanId']);
+      entry.push(data['flow']['vlanPriority']);
+      entry.push(data['flow']['srcMac']);
+      entry.push(data['flow']['dstMac']);
+      entry.push(data['flow']['srcIp']);
+      entry.push(data['flow']['dstIp']);
+      entry.push(data['flow']['tosBits']);
+      entry.push(data['flow']['srcPort']);
+      entry.push(data['flow']['dstPort']);
+      entry.push(data['flow']['protocol']);
+      entry.push(data['flow']['cookie']);
+
+      tr.entry = entry;
+      body.push(tr);
+      return body;
+    },
+    actions : function(data) {
+      var body = [];
+      var tr = {};
+      var entry = [];
+      var actions = '';
+
+      $(data['flow']['actions']).each(function(index, value) {
+        var locEqualTo = value.indexOf("=");
+        if ( locEqualTo == -1 ) {
+          actions += value + ', ';
+        } else {
+          var action = value.substr(0,locEqualTo);
+          if( action == "OUTPUT") {
+            var portIds = value.substr(locEqualTo+1).split(",");
+            actions += action + "=";
+            var allPorts = one.f.flows.registry.nodeports[one.f.flows.registry.selectedNode]['ports'];
+            for(var i =0; i < portIds.length ; i++) {
+              var portName = allPorts[portIds[i]];
+              actions += portName + ", ";
+            }
+          } else {
+            actions += value + ', ';
+          }
         }
+      });
+      actions = actions.slice(0,-2);
+      entry.push(actions);
+
+      tr.entry = entry;
+      body.push(tr);
+      return body;
+    }
+  },
+  body : {
+    dashlet : function(body) {
+      // create table
+      var header = ['Flow Name', 'Node', 'Priority', 'Hard Timeout', 'Idle Timeout'];
+      var $thead = one.lib.dashlet.table.header(header);
+      var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
+      var $table = one.lib.dashlet.table.table(attributes);
+      $table.append($thead);
+
+      var $tbody = one.lib.dashlet.table.body(body);
+      $table.append($tbody);
+
+      return $table;
     },
-    body : {
-        dashlet : function(body) {
-            // create table
-            var header = ['Flow Name', 'Node', 'Priority', 'Hard Timeout', 'Idle Timeout'];
-            var $thead = one.lib.dashlet.table.header(header);
-            var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
-            var $table = one.lib.dashlet.table.table(attributes);
-            $table.append($thead);
-
-            var $tbody = one.lib.dashlet.table.body(body);
-            $table.append($tbody);
-
-            return $table;
-        },
-        description : function(body) {
-            var header = ['Input Port', 'Ethernet Type', 'VLAN ID', 'VLAN Priority', 'Source MAC', 'Dest MAC', 'Source IP', 'Dest IP', 'ToS', 'Source Port', 'Dest Port', 'Protocol', 'Cookie'];
-            var $thead = one.lib.dashlet.table.header(header);
-            var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
-            var $table = one.lib.dashlet.table.table(attributes);
-            $table.append($thead);
+    description : function(body) {
+      var header = ['Input Port', 'Ethernet Type', 'VLAN ID', 'VLAN Priority', 'Source MAC', 'Dest MAC', 'Source IP', 'Dest IP', 'ToS', 'Source Port', 'Dest Port', 'Protocol', 'Cookie'];
+      var $thead = one.lib.dashlet.table.header(header);
+      var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
+      var $table = one.lib.dashlet.table.table(attributes);
+      $table.append($thead);
 
-            var $tbody = one.lib.dashlet.table.body(body);
-            $table.append($tbody);
+      var $tbody = one.lib.dashlet.table.body(body);
+      $table.append($tbody);
 
-            return $table;
-        },
-        actions : function(body) {
-            var header = ['Actions'];
-            var $thead = one.lib.dashlet.table.header(header);
-            var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
-            var $table = one.lib.dashlet.table.table(attributes);
-            $table.append($thead);
+      return $table;
+    },
+    actions : function(body) {
+      var header = ['Actions'];
+      var $thead = one.lib.dashlet.table.header(header);
+      var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
+      var $table = one.lib.dashlet.table.table(attributes);
+      $table.append($thead);
 
-            var $tbody = one.lib.dashlet.table.body(body);
-            $table.append($tbody);
+      var $tbody = one.lib.dashlet.table.body(body);
+      $table.append($tbody);
 
-            return $table;
-        }
+      return $table;
     }
+  }
 }
 
 /** FLOW ENTRIES **/
 one.f.flows = {
-    id : {
-        dashlet : {
-            add : "one_f_flows_id_dashlet_add",
-            removeMultiple : "one_f_flows_id_dashlet_removeMultiple",
-            remove : "one_f_flows_id_dashlet_remove",
-            toggle : "one_f_flows_id_dashlet_toggle",
-            edit : "one_f_flows_id_dashlet_edit",
-            datagrid : "one_f_flows_id_dashlet_datagrid",
-            selectAllFlows : "one_f_flows_id_dashlet_selectAllFlows"
-        },
+  id : {
+    dashlet : {
+      add : "one_f_flows_id_dashlet_add",
+      removeMultiple : "one_f_flows_id_dashlet_removeMultiple",
+      remove : "one_f_flows_id_dashlet_remove",
+      toggle : "one_f_flows_id_dashlet_toggle",
+      edit : "one_f_flows_id_dashlet_edit",
+      datagrid : "one_f_flows_id_dashlet_datagrid",
+      selectAllFlows : "one_f_flows_id_dashlet_selectAllFlows"
+    },
+    modal : {
+      install : "one_f_flows_id_modal_install",
+      edit : "one_f_flows_id_modal_edit",
+      add : "one_f_flows_id_modal_add",
+      close : "one_f_flows_id_modal_close",
+      modal : "one_f_flows_id_modal_modal",
+      dialog : {
+        modal : "one_f_flows_id_modal_dialog_modal",
+        remove : "one_f_flows_id_modal_dialog_remove",
+        close : "one_f_flows_id_modal_dialog_close"
+      },
+      action : {
+        button : "one_f_flows_id_modal_action_button",
+        modal : "one_f_flows_id_modal_action_modal",
+        add : "one_f_flows_id_modal_action_add",
+        close : "one_f_flows_id_modal_action_close",
+        table : "one_f_flows_id_modal_action_table",
+        addOutputPorts : "one_f_flows_id_modal_action_addOutputPorts",
+        setVlanId : "one_f_flows_id_modal_action_setVlanId",
+        setVlanPriority : "one_f_flows_id_modal_action_setVlanPriority",
+        modifyDatalayerSourceAddress : "one_f_flows_id_modal_action_modifyDatalayerSourceAddress",
+        modifyDatalayerDestinationAddress : "one_f_flows_id_modal_action_modifyDatalayerDestinationAddress",
+        modifyNetworkSourceAddress : "one_f_flows_modal_action_modifyNetworkSourceAddress",
+        modifyNetworkDestinationAddress : "one_f_flows_modal_action_modifyNetworkDestinationAddress",
+        modifyTosBits : "one_f_flows_modal_action_modifyTosBits",
+        modifyTransportSourcePort : "one_f_flows_modal_action_modifyTransportSourcePort",
+        modifyTransportDestinationPort : "one_f_flows_modal_action_modifyTransportDestinationPort",
+        enqueue : 'one-f-flows-modal-action-enqueue',
+        queue : 'one-f-flows-modal-action-queue',
+        setEthertype : 'one-f-flows-modal-action-setEthertype',
+        pushVlan : 'one-f-flows-modal-action-pushVlan',
+        setVlanCfi : 'one-f-flows-modal-action-setVlanCfi',
         modal : {
-            install : "one_f_flows_id_modal_install",
-            edit : "one_f_flows_id_modal_edit",
-            add : "one_f_flows_id_modal_add",
-            close : "one_f_flows_id_modal_close",
-            modal : "one_f_flows_id_modal_modal",
-            dialog : {
-                modal : "one_f_flows_id_modal_dialog_modal",
-                remove : "one_f_flows_id_modal_dialog_remove",
-                close : "one_f_flows_id_modal_dialog_close"
-            },
-            action : {
-                button : "one_f_flows_id_modal_action_button",
-                modal : "one_f_flows_id_modal_action_modal",
-                add : "one_f_flows_id_modal_action_add",
-                close : "one_f_flows_id_modal_action_close",
-                table : "one_f_flows_id_modal_action_table",
-                addOutputPorts : "one_f_flows_id_modal_action_addOutputPorts",
-                setVlanId : "one_f_flows_id_modal_action_setVlanId",
-                setVlanPriority : "one_f_flows_id_modal_action_setVlanPriority",
-                modifyDatalayerSourceAddress : "one_f_flows_id_modal_action_modifyDatalayerSourceAddress",
-                modifyDatalayerDestinationAddress : "one_f_flows_id_modal_action_modifyDatalayerDestinationAddress",
-                modifyNetworkSourceAddress : "one_f_flows_modal_action_modifyNetworkSourceAddress",
-                modifyNetworkDestinationAddress : "one_f_flows_modal_action_modifyNetworkDestinationAddress",
-                modifyTosBits : "one_f_flows_modal_action_modifyTosBits",
-                modifyTransportSourcePort : "one_f_flows_modal_action_modifyTransportSourcePort",
-                modifyTransportDestinationPort : "one_f_flows_modal_action_modifyTransportDestinationPort",
-                modal : {
-                    modal : "one_f_flows_modal_action_modal_modal",
-                    remove : "one_f_flows_modal_action_modal_remove",
-                    cancel : "one_f_flows_modal_action_modal_cancel"
-                }
-            },
-            form : {
-                name : "one_f_flows_id_modal_form_name",
-                nodes : "one_f_flows_id_modal_form_nodes",
-                port : "one_f_flows_id_modal_form_port",
-                priority : "one_f_flows_id_modal_form_priority",
-                hardTimeout : "one_f_flows_id_modal_form_hardTimeout",
-                idleTimeout : "one_f_flows_id_modal_form_idleTimeout",
-                cookie : "one_f_flows_id_modal_form_cookie",
-                etherType : "one_f_flows_id_modal_form_etherType",
-                vlanId : "one_f_flows_id_modal_form_vlanId",
-                vlanPriority : "one_f_flows_id_modal_form_vlanPriority",
-                srcMac : "one_f_flows_id_modal_form_srcMac",
-                dstMac : "one_f_flows_id_modal_form_dstMac",
-                srcIp : "one_f_flows_id_modal_form_srcIp",
-                dstIp : "one_f_flows_id_modal_form_dstIp",
-                tosBits : "one_f_flows_id_modal_form_tosBits",
-                srcPort : "one_f_flows_id_modal_form_srcPort",
-                dstPort : "one_f_flows_id_modal_form_dstPort",
-                protocol : "one_f_flows_id_modal_form_protocol"
-            }
+          modal : "one_f_flows_modal_action_modal_modal",
+          remove : "one_f_flows_modal_action_modal_remove",
+          cancel : "one_f_flows_modal_action_modal_cancel"
         }
-    },
-    registry : {},
-    dashlet : function($dashlet, callback) {
-
-        // load body
-        one.f.flows.ajax.dashlet(function(data) {
-
-            var $h4 = one.lib.dashlet.header("Flow Entries");
-
-            $dashlet.append($h4);
-            if (one.f.flows.registry.privilege === 'WRITE') {
-                var button = one.lib.dashlet.button.single("Add Flow Entry", one.f.flows.id.dashlet.add, "btn-primary", "btn-mini");
-                var $button = one.lib.dashlet.button.button(button);
-
-                $button.click(function() {
-                    var $modal = one.f.flows.modal.initialize();
-                    $modal.modal();
-                });
-                $dashlet.append($button);
-                var button = one.lib.dashlet.button.single("Remove Flow Entry", one.f.flows.id.dashlet.removeMultiple, "btn-danger", "btn-mini");
-                var $button = one.lib.dashlet.button.button(button);
-
-                $button.click(function() {
-                    var checkedCheckBoxes = $('.flowEntry[type=checkbox]:checked');
-                    if (checkedCheckBoxes.size() === 0) {
-                        return false;
-                    }
-                    
-                    var requestData = [];
-                    
-                    checkedCheckBoxes.each(function(index, value) {
-                        var flowEntry = {};
-                        flowEntry['name'] = checkedCheckBoxes[index].name;
-                        flowEntry['node'] = checkedCheckBoxes[index].getAttribute("node");
-                        requestData.push(flowEntry);  
-                    });
-                    one.f.flows.modal.removeMultiple.dialog(requestData);
-                });
-                $dashlet.append($button);
+      },
+      form : {
+        name : "one_f_flows_id_modal_form_name",
+        nodes : "one_f_flows_id_modal_form_nodes",
+        port : "one_f_flows_id_modal_form_port",
+        priority : "one_f_flows_id_modal_form_priority",
+        hardTimeout : "one_f_flows_id_modal_form_hardTimeout",
+        idleTimeout : "one_f_flows_id_modal_form_idleTimeout",
+        cookie : "one_f_flows_id_modal_form_cookie",
+        etherType : "one_f_flows_id_modal_form_etherType",
+        vlanId : "one_f_flows_id_modal_form_vlanId",
+        vlanPriority : "one_f_flows_id_modal_form_vlanPriority",
+        srcMac : "one_f_flows_id_modal_form_srcMac",
+        dstMac : "one_f_flows_id_modal_form_dstMac",
+        srcIp : "one_f_flows_id_modal_form_srcIp",
+        dstIp : "one_f_flows_id_modal_form_dstIp",
+        tosBits : "one_f_flows_id_modal_form_tosBits",
+        srcPort : "one_f_flows_id_modal_form_srcPort",
+        dstPort : "one_f_flows_id_modal_form_dstPort",
+        protocol : "one_f_flows_id_modal_form_protocol",
+        action : 'one-f-flows-id-modal-form-action'
+      }
+    }
+  },
+  registry : {},
+  dashlet : function($dashlet, callback) {
+    // load body
+    one.f.flows.ajax.dashlet(function(data) {
+      var $h4 = one.lib.dashlet.header("Flow Entries");
+      $dashlet.append($h4);
+      if (one.f.flows.registry.privilege === 'WRITE') {
+        var button = one.lib.dashlet.button.single("Add Flow Entry", one.f.flows.id.dashlet.add, "btn-primary", "btn-mini");
+        var $button = one.lib.dashlet.button.button(button);
+
+        $button.click(function() {
+          var $modal = one.f.flows.modal.initialize();
+          $modal.modal();
+        });
+        $dashlet.append($button);
+        var button = one.lib.dashlet.button.single("Remove Flow Entry", one.f.flows.id.dashlet.removeMultiple, "btn-danger", "btn-mini");
+        var $button = one.lib.dashlet.button.button(button);
+
+        $button.click(function() {
+          var checkedCheckBoxes = $('.flowEntry[type=checkbox]:checked');
+          if (checkedCheckBoxes.size() === 0) {
+            return false;
+          }
 
-            }
+          var requestData = [];
 
-            var $gridHTML = one.lib.dashlet.datagrid.init(one.f.flows.id.dashlet.datagrid, {
-                searchable: true,
-                filterable: false,
-                pagination: true,
-                flexibleRowsPerPage: true
-                }, "table-striped table-condensed");
-            $dashlet.append($gridHTML);
-            var dataSource = one.f.flows.data.flowsDataGrid(data);
-            $("#" + one.f.flows.id.dashlet.datagrid).datagrid({dataSource: dataSource}).on("loaded", function() {
-                    $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).click(function() {
-                                $("#" + one.f.flows.id.dashlet.datagrid).find(':checkbox').prop('checked',
-                                        $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).is(':checked'));
-                    });
-                    
-                    $("#" + one.f.flows.id.dashlet.datagrid).find("tbody tr").each(function(index, tr) {
-                    $tr = $(tr);
-                    $span = $("td span", $tr);
-                    var flowstatus = $span.data("flowstatus");
-                    if($span.data("installinhw") != null) {
-                        var installInHw = $span.data("installinhw").toString();
-                        if(installInHw == "true" && flowstatus == "Success") {
-                            $tr.addClass("success");
-                        } else {
-                            $tr.addClass("warning");
-                        }
-                    }
-                    // attach mouseover to show pointer cursor
-                    $tr.mouseover(function() {
-                        $(this).css("cursor", "pointer");
-                    });
-                    // attach click event
-                    $tr.click(function() {
-                        var $td = $($(this).find("td")[1]);
-                        var id = $td.text();
-                        var node = $td.find("span").data("nodeid");
-                        one.f.flows.detail(id, node);
-                    });
-                    $(".flowEntry").click(function(e){
-                                if (!$('.flowEntry[type=checkbox]:not(:checked)').length) {
-                            $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows)
-                                .prop("checked",
-                              true);
-                        } else {
-                            $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows)
-                                .prop("checked",
-                             false);
-                        }
-                        e.stopPropagation();
-                    });
-                });
-            });
-            
-            // details callback
-            if(callback != undefined) callback();
+          checkedCheckBoxes.each(function(index, value) {
+            var flowEntry = {};
+            flowEntry['name'] = checkedCheckBoxes[index].name;
+            flowEntry['node'] = checkedCheckBoxes[index].getAttribute("node");
+            requestData.push(flowEntry);  
+          });
+          one.f.flows.modal.removeMultiple.dialog(requestData);
         });
-    },
-    detail : function(id, node) {
-        // clear flow details
-        var $detailDashlet = one.main.dashlet.right.bottom;
-        $detailDashlet.empty();
-        var $h4 = one.lib.dashlet.header("Flow Overview");
-        $detailDashlet.append($h4);
-
-        // details
-        var flows = one.f.flows.registry.flows;
-        one.f.flows.registry['selectedId'] = id;
-        one.f.flows.registry['selectedNode'] = node;
-        var flow;
-        $(flows).each(function(index, value) {
-          if (value.name == id && value.nodeId == node) {
-            flow = value;
-          }
+        $dashlet.append($button);
+
+      }
+      var $gridHTML = one.lib.dashlet.datagrid.init(one.f.flows.id.dashlet.datagrid, {
+        searchable: true,
+          filterable: false,
+          pagination: true,
+          flexibleRowsPerPage: true
+      }, "table-striped table-condensed");
+      $dashlet.append($gridHTML);
+      var dataSource = one.f.flows.data.flowsDataGrid(data);
+      $("#" + one.f.flows.id.dashlet.datagrid).datagrid({dataSource: dataSource}).on("loaded", function() {
+        $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).click(function() {
+          $("#" + one.f.flows.id.dashlet.datagrid).find(':checkbox').prop('checked',
+            $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).is(':checked'));
         });
-        if (one.f.flows.registry.privilege === 'WRITE') {
-            // remove button
-            var button = one.lib.dashlet.button.single("Remove Flow", one.f.flows.id.dashlet.remove, "btn-danger", "btn-mini");
-            var $button = one.lib.dashlet.button.button(button);
-            $button.click(function() {
-                var $modal = one.f.flows.modal.dialog.initialize(id, node);
-                $modal.modal();
-            });
-            // edit button
-            var editButton = one.lib.dashlet.button.single("Edit Flow", one.f.flows.id.dashlet.edit, "btn-primary", "btn-mini");
-            var $editButton = one.lib.dashlet.button.button(editButton);
-            $editButton.click(function() {
-               var install = flow['flow']['installInHw'];
-                var $modal = one.f.flows.modal.initialize(true,install);
-                $modal.modal().on('shown',function(){
-                    var $port = $('#'+one.f.flows.id.modal.form.port);
-                    $('#'+one.f.flows.id.modal.form.nodes).trigger("change");
-                });
-            });
-            // toggle button
-            var toggle;
-            if (flow['flow']['installInHw'] == 'true' && flow['flow']['status'] == 'Success') {
-                toggle = one.lib.dashlet.button.single("Uninstall Flow", one.f.flows.id.dashlet.toggle, "btn-warning", "btn-mini");
-            } else {
-                toggle = one.lib.dashlet.button.single("Install Flow", one.f.flows.id.dashlet.toggle, "btn-success", "btn-mini");
-            }
-            var $toggle = one.lib.dashlet.button.button(toggle);
-            $toggle.click(function() {
-                one.f.flows.modal.ajax.toggleflow(id, node, function(data) {
-                    if(data == "Success") {
-                        one.main.dashlet.right.bottom.empty();
-                        one.f.detail.dashlet(one.main.dashlet.right.bottom);
-                        one.main.dashlet.left.top.empty();
-                        one.f.flows.dashlet(one.main.dashlet.left.top, function() {
-                           // checks are backwards due to stale registry
-                           if(flow['flow']['installInHw'] == 'true') {
-                               one.lib.alert('Uninstalled Flow');
-                           } else {
-                               one.lib.alert('Installed Flow');
-                           }
-                           one.f.flows.detail(id, node)
-                        });
-                    } else {
-                        one.lib.alert('Cannot toggle flow: '+data);
-                    }
-                });
-            });
 
-            $detailDashlet.append($button).append($editButton).append($toggle);
-        }
-        // append details
-        var body = one.f.detail.data.dashlet(flow);
-        var $body = one.f.detail.body.dashlet(body);
-        $detailDashlet.append($body);
-        var body = one.f.detail.data.description(flow);
-        var $body = one.f.detail.body.description(body);
-        $detailDashlet.append($body);
-        var body = one.f.detail.data.actions(flow);
-        var $body = one.f.detail.body.actions(body);
-        $detailDashlet.append($body);
-    },
-    modal : {
-        dialog : {
-            initialize : function(id, node) {
-                var h3 = "Remove Flow";
-                var $p = one.f.flows.modal.dialog.body(id);
-                var footer = one.f.flows.modal.dialog.footer();
-                var $modal = one.lib.modal.spawn(one.f.flows.id.modal.dialog.modal, h3, $p, footer);
-                $('#'+one.f.flows.id.modal.dialog.close, $modal).click(function() {
-                    $modal.modal('hide');
-                });
-                $('#'+one.f.flows.id.modal.dialog.remove, $modal).click(function() {
-                    one.f.flows.modal.ajax.removeflow(id, node, function(data) {
-                        if (data == "Success") {
-                            $modal.modal('hide');
-                            one.main.dashlet.right.bottom.empty();
-                            one.f.detail.dashlet(one.main.dashlet.right.bottom);
-                            one.main.dashlet.left.top.empty();
-                            one.f.flows.dashlet(one.main.dashlet.left.top);
-                            one.lib.alert('Flow removed');
-                        } else {
-                            one.lib.alert('Cannot remove flow: '+data);
-                        }
-                    });
-                });
-                return $modal;
-            },
-            footer : function() {
-                var footer = [];
-
-                var removeButton = one.lib.dashlet.button.single("Remove Flow", one.f.flows.id.modal.dialog.remove, "btn-danger", "");
-                var $removeButton = one.lib.dashlet.button.button(removeButton);
-                footer.push($removeButton);
-
-                var closeButton = one.lib.dashlet.button.single("Cancel", one.f.flows.id.modal.dialog.close, "", "");
-                var $closeButton = one.lib.dashlet.button.button(closeButton);
-                footer.push($closeButton);
-
-                return footer;
-            },
-            body : function(id) {
-                var $p = $(document.createElement('p'));
-                $p.append('Remove flow '+id+'?');
-                return $p;
+        $("#" + one.f.flows.id.dashlet.datagrid).find("tbody tr").each(function(index, tr) {
+          $tr = $(tr);
+          $span = $("td span", $tr);
+          var flowstatus = $span.data("flowstatus");
+          if($span.data("installinhw") != null) {
+            var installInHw = $span.data("installinhw").toString();
+            if(installInHw == "true" && flowstatus == "Success") {
+              $tr.addClass("success");
+            } else {
+              $tr.addClass("warning");
             }
-        },
-        initialize : function(edit,install) {
-            var h3;
-            if(edit) {
-                h3 = "Edit Flow Entry";
-                var footer = one.f.flows.modal.footerEdit();
-
+          }
+          // attach mouseover to show pointer cursor
+          $tr.mouseover(function() {
+            $(this).css("cursor", "pointer");
+          });
+          // attach click event
+          $tr.click(function() {
+            var $td = $($(this).find("td")[1]);
+            var id = $td.text();
+            var node = $td.find("span").data("nodeid");
+            one.f.flows.detail(id, node);
+          });
+          $(".flowEntry").click(function(e){
+            if (!$('.flowEntry[type=checkbox]:not(:checked)').length) {
+              $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows)
+            .prop("checked",
+              true);
             } else {
-                h3 = "Add Flow Entry";
-                var footer = one.f.flows.modal.footer();
+              $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows)
+            .prop("checked",
+              false);
             }
-
-            var $modal = one.lib.modal.spawn(one.f.flows.id.modal.modal, h3, "", footer);
-
-            // bind close button
-            $('#'+one.f.flows.id.modal.close, $modal).click(function() {
-                $modal.modal('hide');
+            e.stopPropagation();
+          });
+        });
+      });
+
+      // details callback
+      if(callback != undefined) callback();
+    });
+  },
+  detail : function(id, node) {
+    // clear flow details
+    var $detailDashlet = one.main.dashlet.right.bottom;
+    $detailDashlet.empty();
+    var $h4 = one.lib.dashlet.header("Flow Overview");
+    $detailDashlet.append($h4);
+
+    // details
+    var flows = one.f.flows.registry.flows;
+    one.f.flows.registry['selectedId'] = id;
+    one.f.flows.registry['selectedNode'] = node;
+    var flow;
+    $(flows).each(function(index, value) {
+      if (value.name == id && value.nodeId == node) {
+        flow = value;
+      }
+    });
+    if (one.f.flows.registry.privilege === 'WRITE') {
+      // remove button
+      var button = one.lib.dashlet.button.single("Remove Flow", one.f.flows.id.dashlet.remove, "btn-danger", "btn-mini");
+      var $button = one.lib.dashlet.button.button(button);
+      $button.click(function() {
+        var $modal = one.f.flows.modal.dialog.initialize(id, node);
+        $modal.modal();
+      });
+      // edit button
+      var editButton = one.lib.dashlet.button.single("Edit Flow", one.f.flows.id.dashlet.edit, "btn-primary", "btn-mini");
+      var $editButton = one.lib.dashlet.button.button(editButton);
+      $editButton.click(function() {
+        var install = flow['flow']['installInHw'];
+        var $modal = one.f.flows.modal.initialize(true,install);
+        $modal.modal().on('shown',function(){
+          var $port = $('#'+one.f.flows.id.modal.form.port);
+          $('#'+one.f.flows.id.modal.form.nodes).trigger("change");
+        });
+      });
+      // toggle button
+      var toggle;
+      if (flow['flow']['installInHw'] == 'true' && flow['flow']['status'] == 'Success') {
+        toggle = one.lib.dashlet.button.single("Uninstall Flow", one.f.flows.id.dashlet.toggle, "btn-warning", "btn-mini");
+      } else {
+        toggle = one.lib.dashlet.button.single("Install Flow", one.f.flows.id.dashlet.toggle, "btn-success", "btn-mini");
+      }
+      var $toggle = one.lib.dashlet.button.button(toggle);
+      $toggle.click(function() {
+        one.f.flows.modal.ajax.toggleflow(id, node, function(data) {
+          if(data == "Success") {
+            one.main.dashlet.right.bottom.empty();
+            one.f.detail.dashlet(one.main.dashlet.right.bottom);
+            one.main.dashlet.left.top.empty();
+            one.f.flows.dashlet(one.main.dashlet.left.top, function() {
+              // checks are backwards due to stale registry
+              if(flow['flow']['installInHw'] == 'true') {
+                one.lib.alert('Uninstalled Flow');
+              } else {
+                one.lib.alert('Installed Flow');
+              }
+              one.f.flows.detail(id, node)
             });
+          } else {
+            one.lib.alert('Cannot toggle flow: '+data);
+          }
+        });
+      });
 
-            if (edit) {
-                // bind edit flow button
-                $('#'+one.f.flows.id.modal.edit, $modal).click(function() {
-                    one.f.flows.modal.save($modal, install, true);
-                });
+      $detailDashlet.append($button).append($editButton).append($toggle);
+    }
+    // append details
+    var body = one.f.detail.data.dashlet(flow);
+    var $body = one.f.detail.body.dashlet(body);
+    $detailDashlet.append($body);
+    var body = one.f.detail.data.description(flow);
+    var $body = one.f.detail.body.description(body);
+    $detailDashlet.append($body);
+    var body = one.f.detail.data.actions(flow);
+    var $body = one.f.detail.body.actions(body);
+    $detailDashlet.append($body);
+  },
+  modal : {
+    dialog : {
+      initialize : function(id, node) {
+        var h3 = "Remove Flow";
+        var $p = one.f.flows.modal.dialog.body(id);
+        var footer = one.f.flows.modal.dialog.footer();
+        var $modal = one.lib.modal.spawn(one.f.flows.id.modal.dialog.modal, h3, $p, footer);
+        $('#'+one.f.flows.id.modal.dialog.close, $modal).click(function() {
+          $modal.modal('hide');
+        });
+        $('#'+one.f.flows.id.modal.dialog.remove, $modal).click(function() {
+          one.f.flows.modal.ajax.removeflow(id, node, function(data) {
+            if (data == "Success") {
+              $modal.modal('hide');
+              one.main.dashlet.right.bottom.empty();
+              one.f.detail.dashlet(one.main.dashlet.right.bottom);
+              one.main.dashlet.left.top.empty();
+              one.f.flows.dashlet(one.main.dashlet.left.top);
+              one.lib.alert('Flow removed');
             } else {
-                // bind add flow button
-                $('#'+one.f.flows.id.modal.add, $modal).click(function() {
-                    one.f.flows.modal.save($modal, 'false');
-                });
-
-                // bind install flow button
-                $('#'+one.f.flows.id.modal.install, $modal).click(function() {
-                    one.f.flows.modal.save($modal, 'true');
-                });
+              one.lib.alert('Cannot remove flow: '+data);
             }
+          });
+        });
+        return $modal;
+      },
+      footer : function() {
+        var footer = [];
+
+        var removeButton = one.lib.dashlet.button.single("Remove Flow", one.f.flows.id.modal.dialog.remove, "btn-danger", "");
+        var $removeButton = one.lib.dashlet.button.button(removeButton);
+        footer.push($removeButton);
+
+        var closeButton = one.lib.dashlet.button.single("Cancel", one.f.flows.id.modal.dialog.close, "", "");
+        var $closeButton = one.lib.dashlet.button.button(closeButton);
+        footer.push($closeButton);
+
+        return footer;
+      },
+      body : function(id) {
+        var $p = $(document.createElement('p'));
+        $p.append('Remove flow '+id+'?');
+        return $p;
+      }
+    },
+    initialize : function(edit,install) {
+      var h3;
+      if(edit) {
+        h3 = "Edit Flow Entry";
+        var footer = one.f.flows.modal.footerEdit();
+
+      } else {
+        h3 = "Add Flow Entry";
+        var footer = one.f.flows.modal.footer();
+      }
+
+      var $modal = one.lib.modal.spawn(one.f.flows.id.modal.modal, h3, "", footer);
+
+      // bind close button
+      $('#'+one.f.flows.id.modal.close, $modal).click(function() {
+        $modal.modal('hide');
+      });
+
+      if (edit) {
+        // bind edit flow button
+        $('#'+one.f.flows.id.modal.edit, $modal).click(function() {
+          one.f.flows.modal.save($modal, install, true);
+        });
+      } else {
+        // bind add flow button
+        $('#'+one.f.flows.id.modal.add, $modal).click(function() {
+          one.f.flows.modal.save($modal, 'false');
+        });
 
+        // bind install flow button
+        $('#'+one.f.flows.id.modal.install, $modal).click(function() {
+          one.f.flows.modal.save($modal, 'true');
+        });
+      }
 
-            var nodes = one.f.flows.registry.nodes;
-            var nodeports = one.f.flows.registry.nodeports;
-            var $body = one.f.flows.modal.body(nodes, nodeports, edit);
-            one.lib.modal.inject.body($modal, $body,edit);
-
-            return $modal;
-        },
-        save : function($modal, install, edit) {
-            var result = {};
-
-            result['name'] = $('#'+one.f.flows.id.modal.form.name, $modal).val();
-            result['ingressPort'] = $('#'+one.f.flows.id.modal.form.port, $modal).val();
-            result['priority'] = $('#'+one.f.flows.id.modal.form.priority, $modal).val();
-            result['hardTimeout'] = $('#'+one.f.flows.id.modal.form.hardTimeout, $modal).val();
-            result['idleTimeout'] = $('#'+one.f.flows.id.modal.form.idleTimeout, $modal).val();
-            result['cookie'] = $('#'+one.f.flows.id.modal.form.cookie, $modal).val();
-            result['etherType'] = $('#'+one.f.flows.id.modal.form.etherType, $modal).val();
-            result['vlanId'] = $('#'+one.f.flows.id.modal.form.vlanId, $modal).val();
-            result['vlanPriority'] = $('#'+one.f.flows.id.modal.form.vlanPriority, $modal).val();
-            result['dlSrc'] = $('#'+one.f.flows.id.modal.form.srcMac, $modal).val();
-            result['dlDst'] = $('#'+one.f.flows.id.modal.form.dstMac, $modal).val();
-            result['nwSrc'] = $('#'+one.f.flows.id.modal.form.srcIp, $modal).val();
-            result['nwDst'] = $('#'+one.f.flows.id.modal.form.dstIp, $modal).val();
-            result['tosBits'] = $('#'+one.f.flows.id.modal.form.tosBits, $modal).val();
-            result['tpSrc'] = $('#'+one.f.flows.id.modal.form.srcPort, $modal).val();
-            result['tpDst'] = $('#'+one.f.flows.id.modal.form.dstPort, $modal).val();
-            result['protocol'] = $('#'+one.f.flows.id.modal.form.protocol, $modal).val();
-            result['installInHw'] = install;
-
-            var nodeId = $('#'+one.f.flows.id.modal.form.nodes, $modal).val();
-
-            $.each(result, function(key, value) {
-                if (value == "") delete result[key];
-            });
+      var nodes = one.f.flows.registry.nodes;
+      var nodeports = one.f.flows.registry.nodeports;
+      var $body = one.f.flows.modal.body(nodes, nodeports, edit);
+      one.lib.modal.inject.body($modal, $body,edit);
 
-            var action = [];
-            var $table = $('#'+one.f.flows.id.modal.action.table, $modal);
-            $($table.find('tbody').find('tr')).each(function(index, value) {
-                if (!$(this).find('td').hasClass('empty')) {
-                    action.push($(value).data('action'));
-                }
+      return $modal;
+    },
+    save : function($modal, install, edit) {
+      var result = {};
+
+      result['name'] = $('#'+one.f.flows.id.modal.form.name, $modal).val();
+      result['ingressPort'] = $('#'+one.f.flows.id.modal.form.port, $modal).val();
+      result['priority'] = $('#'+one.f.flows.id.modal.form.priority, $modal).val();
+      result['hardTimeout'] = $('#'+one.f.flows.id.modal.form.hardTimeout, $modal).val();
+      result['idleTimeout'] = $('#'+one.f.flows.id.modal.form.idleTimeout, $modal).val();
+      result['cookie'] = $('#'+one.f.flows.id.modal.form.cookie, $modal).val();
+      result['etherType'] = $('#'+one.f.flows.id.modal.form.etherType, $modal).val();
+      result['vlanId'] = $('#'+one.f.flows.id.modal.form.vlanId, $modal).val();
+      result['vlanPriority'] = $('#'+one.f.flows.id.modal.form.vlanPriority, $modal).val();
+      result['dlSrc'] = $('#'+one.f.flows.id.modal.form.srcMac, $modal).val();
+      result['dlDst'] = $('#'+one.f.flows.id.modal.form.dstMac, $modal).val();
+      result['nwSrc'] = $('#'+one.f.flows.id.modal.form.srcIp, $modal).val();
+      result['nwDst'] = $('#'+one.f.flows.id.modal.form.dstIp, $modal).val();
+      result['tosBits'] = $('#'+one.f.flows.id.modal.form.tosBits, $modal).val();
+      result['tpSrc'] = $('#'+one.f.flows.id.modal.form.srcPort, $modal).val();
+      result['tpDst'] = $('#'+one.f.flows.id.modal.form.dstPort, $modal).val();
+      result['protocol'] = $('#'+one.f.flows.id.modal.form.protocol, $modal).val();
+      result['installInHw'] = install;
+
+      var nodeId = $('#'+one.f.flows.id.modal.form.nodes, $modal).val();
+
+      $.each(result, function(key, value) {
+        if (value == "") delete result[key];
+      });
+
+      var action = [];
+      var $table = $('#'+one.f.flows.id.modal.action.table, $modal);
+      $($table.find('tbody').find('tr')).each(function(index, value) {
+        if (!$(this).find('td').hasClass('empty')) {
+          action.push($(value).data('action'));
+        }
+      });
+      result['actions'] = action;
+
+      // frontend validation
+      if (result['name'] == undefined) {
+        alert('Need flow name');
+        return;
+      }
+      if (nodeId == '') {
+        alert('Select node');
+        return;
+      }
+      if (action.length == 0) {
+        alert('Please specify an action');
+        return;
+      }
+
+      // package for ajax call
+      var resource = {};
+      resource['body'] = JSON.stringify(result);
+      if(edit){
+        resource['action'] = 'edit';
+      } else {
+        resource['action'] = 'add';
+      }
+
+      resource['nodeId'] = nodeId;
+
+      if (edit) {
+        one.f.flows.modal.ajax.saveflow(resource, function(data) {
+          if (data == "Success") {
+            $modal.modal('hide').on('hidden', function () {
+              one.f.flows.detail(result['name'], nodeId);
             });
-            result['actions'] = action;
-
-            // frontend validation
-            if (result['name'] == undefined) {
-                alert('Need flow name');
-                return;
-            }
-            if (nodeId == '') {
-                alert('Select node');
-                return;
-            }
-            if (action.length == 0) {
-                alert('Please specify an action');
-                return;
-            }
-
-            // package for ajax call
-            var resource = {};
-            resource['body'] = JSON.stringify(result);
-            if(edit){
-                resource['action'] = 'edit';
+            one.lib.alert('Flow Entry edited');
+            one.main.dashlet.left.top.empty();
+            one.f.flows.dashlet(one.main.dashlet.left.top);
+          } else {
+            alert('Could not edit flow: '+data);
+          }
+        });
+      } else {
+        one.f.flows.modal.ajax.saveflow(resource, function(data) {
+          if (data == "Success") {
+            $modal.modal('hide');
+            one.lib.alert('Flow Entry added');
+            one.main.dashlet.left.top.empty();
+            one.f.flows.dashlet(one.main.dashlet.left.top);
+          } else {
+            alert('Could not add flow: '+data);
+          }
+        });
+      }
+    },
+    ajax : {
+      nodes : function(successCallback) {
+        $.getJSON(one.f.address.root+one.f.address.flows.nodes, function(data) {
+          var nodes = one.f.flows.modal.data.nodes(data);
+          var nodeports = data;
+          one.f.flows.registry['nodes'] = nodes;
+          one.f.flows.registry['nodeports'] = nodeports;
+
+          successCallback(nodes, nodeports);
+        });
+      },
+      saveflow : function(resource, callback) {
+        $.post(one.f.address.root+one.f.address.flows.flow, resource, function(data) {
+          callback(data);
+        });
+      },
+      removeflow : function(id, node, callback) {
+        resource = {};
+        resource['action'] = 'remove';
+        $.post(one.f.address.root+one.f.address.flows.flow+'/'+node+'/'+id, resource, function(data) {
+          callback(data);
+        });
+      },
+      toggleflow : function(id, node, callback) {
+        resource = {};
+        resource['action'] = 'toggle';
+        $.post(one.f.address.root+one.f.address.flows.flow+'/'+node+'/'+id, resource, function(data) {
+          callback(data);
+        });
+      }
+    },
+    data : {
+      nodes : function(data) {
+        result = {};
+        $.each(data, function(key, value) {
+          result[key] = value['name'];
+        });
+        return result;
+      }
+    },
+    body : function(nodes, nodeports, edit) {
+      var $form = $(document.createElement('form'));
+      var $fieldset = $(document.createElement('fieldset'));
+      var existingFlow;
+      // flow description
+      var $legend = one.lib.form.legend("");
+      $legend.css('visibility', 'hidden');
+      $fieldset.append($legend);
+      // name
+      var $label = one.lib.form.label("Name");
+      var $input = one.lib.form.input("Flow Name");
+      $input.attr('id', one.f.flows.id.modal.form.name);
+      if(edit) {
+        $input.attr('disabled', 'disabled');
+        var flows = one.f.flows.registry.flows;
+        $(flows).each(function(index, value) {
+          if (value.name == one.f.flows.registry.selectedId && value.nodeId == one.f.flows.registry.selectedNode) {
+            existingFlow = value.flow;
+          }
+        });
+        $input.val(existingFlow.name);
+      }
+
+      $fieldset.append($label).append($input);
+      // node
+      var $label = one.lib.form.label("Node");
+      var $select = one.lib.form.select.create(nodes);
+      one.lib.form.select.prepend($select, { '' : 'Please Select a Node' });
+      $select.val($select.find("option:first").val());
+      $select.attr('id', one.f.flows.id.modal.form.nodes);
+      if(edit) {
+        $select.attr('disabled', 'disabled');
+        $select.val(existingFlow.node.type + "|"+ existingFlow.node.nodeIDString);
+      }
+
+      // bind onchange
+      $select.change(function() {
+        // retrieve port value
+        var node = $(this).find('option:selected').attr('value');
+        var $ports = $('#'+one.f.flows.id.modal.form.port);
+        if (node == '') {
+          one.lib.form.select.inject($ports, {});
+          return;
+        }
+        one.f.flows.registry['currentNode'] = node;
+        var ports = nodeports[node]['ports'];
+        one.lib.form.select.inject($ports, ports);
+        one.lib.form.select.prepend($ports, { '' : 'Please Select a Port' });
+        $ports.val($ports.find("option:first").val());
+        if(edit) {
+          $ports.val( existingFlow.ingressPort );
+        }
+        $.getJSON(one.f.address.root+'/valid-flows/'+node, function(response) {
+          var $select = $('#'+one.f.flows.id.modal.form.action, $fieldset);
+          one.lib.form.select.inject($select, response);
+          one.lib.form.select.prepend($select, {'' : 'Please Select an Action'});
+          // when selecting an action
+          $select.change(function() {
+            var action = $(this).find('option:selected');
+            one.f.flows.modal.action.parse(action.attr('value'));
+            $select[0].selectedIndex = 0;
+          });
+        });
+      });
+
+      $fieldset.append($label).append($select);
+      // input port
+      var $label = one.lib.form.label("Input Port");
+      var $select = one.lib.form.select.create();
+
+      $select.attr('id', one.f.flows.id.modal.form.port);
+      $fieldset.append($label).append($select);
+      // priority
+      var $label = one.lib.form.label("Priority");
+      var $input = one.lib.form.input("Priority");
+      $input.attr('id', one.f.flows.id.modal.form.priority);
+      $input.val('500');
+      $fieldset.append($label).append($input);
+      if(edit) {
+        $input.val(existingFlow.priority);
+      }
+      // hardTimeout
+      var $label = one.lib.form.label("Hard Timeout");
+      var $input = one.lib.form.input("Hard Timeout");
+      $input.attr('id', one.f.flows.id.modal.form.hardTimeout);
+      if(edit) {
+        $input.val(existingFlow.hardTimeout);
+      }
+      $fieldset.append($label).append($input);
+
+      // idleTimeout
+      var $label = one.lib.form.label("Idle Timeout");
+      var $input = one.lib.form.input("Idle Timeout");
+      $input.attr('id', one.f.flows.id.modal.form.idleTimeout);
+      $fieldset.append($label).append($input);
+      if(edit) {
+        $input.val(existingFlow.idleTimeout);
+      }
+      // cookie
+      var $label = one.lib.form.label("Cookie");
+      var $input = one.lib.form.input("Cookie");
+      $input.attr('id', one.f.flows.id.modal.form.cookie);
+      $fieldset.append($label).append($input);
+      if(edit) {
+        $input.val(existingFlow.cookie);
+      }
+
+      // layer 2
+      var $legend = one.lib.form.legend("Layer 2");
+      $fieldset.append($legend);
+      // etherType
+      var $label = one.lib.form.label("Ethernet Type");
+      var $input = one.lib.form.input("Ethernet Type");
+      $input.attr('id', one.f.flows.id.modal.form.etherType);
+      $input.val('0x800');
+      $fieldset.append($label).append($input);
+      if(edit) {
+        $input.val(existingFlow.etherType);
+      }
+      // vlanId
+      var $label = one.lib.form.label("VLAN Identification Number");
+      var $input = one.lib.form.input("VLAN Identification Number");
+      $input.attr('id', one.f.flows.id.modal.form.vlanId);
+      var $help = one.lib.form.help("Range: 0 - 4095");
+      $fieldset.append($label).append($input).append($help);
+      if(edit) {
+        $input.val(existingFlow.vlanId);
+      }
+
+      // vlanPriority
+      var $label = one.lib.form.label("VLAN Priority");
+      var $input = one.lib.form.input("VLAN Priority");
+      $input.attr('id', one.f.flows.id.modal.form.vlanPriority);
+      var $help = one.lib.form.help("Range: 0 - 7");
+      $fieldset.append($label).append($input).append($help);
+      if(edit) {
+        $input.val(existingFlow.vlanPriority);
+      }
+
+      // srcMac
+      var $label = one.lib.form.label("Source MAC Address");
+      var $input = one.lib.form.input("3c:97:0e:75:c3:f7");
+      $input.attr('id', one.f.flows.id.modal.form.srcMac);
+      $fieldset.append($label).append($input);
+      if(edit) {
+        $input.val(existingFlow.srcMac);
+      }
+      // dstMac
+      var $label = one.lib.form.label("Destination MAC Address");
+      var $input = one.lib.form.input("7c:d1:c3:e8:e6:99");
+      $input.attr('id', one.f.flows.id.modal.form.dstMac);
+      $fieldset.append($label).append($input);
+      if(edit) {
+        $input.val(existingFlow.dstMac);
+      }
+      // layer 3
+      var $legend = one.lib.form.legend("Layer 3");
+      $fieldset.append($legend);
+
+      // srcIp
+      var $label = one.lib.form.label("Source IP Address");
+      var $input = one.lib.form.input("192.168.3.128");
+      $input.attr('id', one.f.flows.id.modal.form.srcIp);
+      $fieldset.append($label).append($input);
+      if(edit) {
+        $input.val(existingFlow.srcIp);
+      }
+      // dstIp
+      var $label = one.lib.form.label("Destination IP Address");
+      var $input = one.lib.form.input("2001:2334::0/32");
+      $input.attr('id', one.f.flows.id.modal.form.dstIp);
+      $fieldset.append($label).append($input);
+      if(edit) {
+        $input.val(existingFlow.dstIp);
+      }
+      // tosBits
+      var $label = one.lib.form.label("ToS Bits");
+      var $input = one.lib.form.input("ToS Bits");
+      $input.attr('id', one.f.flows.id.modal.form.tosBits);
+      var $help = one.lib.form.help("Range: 0 - 63");
+      $fieldset.append($label).append($input).append($help);
+      if(edit) {
+        $input.val(existingFlow.tosBits);
+      }
+
+      // layer 4
+      var $legend = one.lib.form.legend("Layer 4");
+      $fieldset.append($legend);
+      // srcPort
+      var $label = one.lib.form.label("Source Port");
+      var $input = one.lib.form.input("Source Port");
+      $input.attr('id', one.f.flows.id.modal.form.srcPort);
+      var $help = one.lib.form.help("Range: 0 - 65535");
+      $fieldset.append($label).append($input).append($help);
+      if(edit) {
+        $input.val(existingFlow.srcPort);
+      }
+      // dstPort
+      var $label = one.lib.form.label("Destination Port");
+      var $input = one.lib.form.input("Destination Port");
+      $input.attr('id', one.f.flows.id.modal.form.dstPort);
+      var $help = one.lib.form.help("Range: 0 - 65535");
+      $fieldset.append($label).append($input).append($help);
+      if(edit) {
+        $input.val(existingFlow.dstPort);
+      }
+      // protocol
+      var $label = one.lib.form.label("Protocol");
+      var $input = one.lib.form.input("Protocol");
+      $input.attr('id', one.f.flows.id.modal.form.protocol);
+      $fieldset.append($label).append($input);
+      if(edit) {
+        $input.val(existingFlow.protocol);
+      }
+      // actions
+      var $legend = one.lib.form.label("Actions");
+      $fieldset.append($legend);
+      // actions table
+      var tableAttributes = ["table-striped", "table-bordered", "table-condensed", "table-hover", "table-cursor"];
+      var $table = one.lib.dashlet.table.table(tableAttributes);
+      $table.attr('id', one.f.flows.id.modal.action.table);
+      var tableHeaders = ["Action", "Data"];
+      var $thead = one.lib.dashlet.table.header(tableHeaders);
+      var $tbody = one.lib.dashlet.table.body("", tableHeaders);
+      $table.append($thead).append($tbody);
+      // actions
+      var actions = {
+        "" : "Please Select an Action",
+        "DROP" : "Drop",
+        "LOOPBACK" : "Loopback",
+        "FLOOD" : "Flood",
+        "FLOOD_ALL" : "Flood All",
+        "CONTROLLER" : "Controller",
+        "SW_PATH" : "Software Path",
+        "HW_PATH" : "Hardware Path",
+        "OUTPUT" : "Add Output Ports",
+        "ENQUEUE" : "Enqueue",
+        "SET_VLAN_ID" : "Set VLAN ID",
+        "SET_VLAN_PCP" : "Set VLAN Priority",
+        "SET_VLAN_CFI" : "Set VLAN CFI",
+        "POP_VLAN" : "Strip VLAN Header",
+        "PUSH_VLAN" : "Push VLAN",
+        "SET_DL_SRC" : "Modify Datalayer Source Address",
+        "SET_DL_DST" : "Modify Datalayer Destination Address",
+        "SET_DL_TYPE" : "Set Ethertype",
+        "SET_NW_SRC" : "Modify Network Source Address",
+        "SET_NW_DST" :"Modify Network Destination Address",
+        "SET_NW_TOS" : "Modify ToS Bits",
+        "SET_TP_SRC" : "Modify Transport Source Port",
+        "SET_TP_DST" : "Modify Transport Destination Port"
+      };
+      var $select = one.lib.form.select.create(actions);
+      $select.attr('id', one.f.flows.id.modal.form.action);
+      // when selecting an action
+      $select.change(function() {
+        var action = $(this).find('option:selected');
+        one.f.flows.modal.action.parse(action.attr('value'));
+        $select[0].selectedIndex = 0;
+      });
+
+      if(edit) {
+        $(existingFlow.actions).each(function(index, value){
+          setTimeout(function(){
+            var locEqualTo = value.indexOf("=");
+            if ( locEqualTo == -1 ) {
+              one.f.flows.modal.action.add.add(actions[value], value);
             } else {
-                resource['action'] = 'add';
+              var action = value.substr(0,locEqualTo);
+              if( action == "OUTPUT") {
+                var portIds = value.substr(locEqualTo+1).split(",");
+                var ports = [];
+                var allPorts = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports'];
+                for(var i =0; i < portIds.length ; i++) {
+                  var portName = allPorts[portIds[i]];
+                  ports.push(portName);
+                }
+                one.f.flows.modal.action.add.addPortsToTable(ports.join(", "), portIds.join(","));
+              } else {
+                var val = value.substr(locEqualTo+1);
+                one.f.flows.modal.action.add.addDataToTable(actions[action], val, action)
+              }
             }
+          }, 1000)
+        });
+      }
+      $fieldset.append($select).append($table);
 
-            resource['nodeId'] = nodeId;
-
-            if (edit) {
-                    one.f.flows.modal.ajax.saveflow(resource, function(data) {
-                    if (data == "Success") {
-                        $modal.modal('hide').on('hidden', function () {
-                            one.f.flows.detail(result['name'], nodeId);
-                        });
-                        one.lib.alert('Flow Entry edited');
-                        one.main.dashlet.left.top.empty();
-                        one.f.flows.dashlet(one.main.dashlet.left.top);
-                    } else {
-                        alert('Could not edit flow: '+data);
-                    }
-                });
-            } else {
-                    one.f.flows.modal.ajax.saveflow(resource, function(data) {
-                    if (data == "Success") {
-                        $modal.modal('hide');
-                        one.lib.alert('Flow Entry added');
-                        one.main.dashlet.left.top.empty();
-                        one.f.flows.dashlet(one.main.dashlet.left.top);
-                    } else {
-                        alert('Could not add flow: '+data);
-                    }
-                });
-            }
+      // return
+      $form.append($fieldset);
+      return $form;
+    },
+    action : {
+      parse : function(option) {
+        switch (option) {
+          case "OUTPUT" :
+            var h3 = "Add Output Port";
+            var $modal = one.f.flows.modal.action.initialize(h3, one.f.flows.modal.action.body.addOutputPorts, one.f.flows.modal.action.add.addOutputPorts);
+            $modal.modal();
+            break;
+          case "SET_VLAN_ID" :
+            var h3 = "Set VLAN ID";
+            var placeholder = "VLAN Identification Number";
+            var id = one.f.flows.id.modal.action.setVlanId;
+            var help = "Range: 0 - 4095";
+            var action = 'SET_VLAN_ID';
+            var name = "VLAN ID";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "SET_VLAN_PCP" :
+            var h3 = "Set VLAN Priority";
+            var placeholder = "VLAN Priority";
+            var id = one.f.flows.id.modal.action.setVlanPriority;
+            var help = "Range: 0 - 7";
+            var action = 'SET_VLAN_PCP';
+            var name = "VLAN Priority";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "SET_VLAN_CFI" :
+            var h3 = "Set VLAN CFI";
+            var placeholder = "VLAN CFI";
+            var id = one.f.flows.id.modal.action.setVlanCfi;
+            var help = "Range: 0 - 1";
+            var action = 'SET_VLAN_CFI';
+            var name = "VLAN CFI";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "POP_VLAN" :
+            var name = "Strip VLAN Header";
+            var action = 'POP_VLAN';
+            one.f.flows.modal.action.add.add(name, action);
+            break;
+          case "PUSH_VLAN" :
+            var h3 = "Push VLAN";
+            var placeholder = "VLAN";
+            var id = one.f.flows.id.modal.action.pushVlan;
+            var help = "Range: 0 - 4095";
+            var action = 'PUSH_VLAN';
+            var name = "VLAN";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "SET_DL_SRC" :
+            var h3 = "Set Source MAC Address";
+            var placeholder = "Source MAC Address";
+            var id = one.f.flows.id.modal.action.modifyDatalayerSourceAddress;
+            var help = "Example: 00:11:22:aa:bb:cc";
+            var action = 'SET_DL_SRC';
+            var name = "Source MAC";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "SET_DL_DST" :
+            var h3 = "Set Destination MAC Address";
+            var placeholder = "Destination MAC Address";
+            var id = one.f.flows.id.modal.action.modifyDatalayerDestinationAddress;
+            var help = "Example: 00:11:22:aa:bb:cc";
+            var action = 'SET_DL_DST';
+            var name = "Destination MAC";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "SET_DL_TYPE" :
+            var h3 = "Set Ethertype";
+            var placeholder = "Ethertype";
+            var id = one.f.flows.id.modal.action.setEthertype;
+            var help = "Range: 0 - 65535";
+            var action = 'SET_DL_TYPE';
+            var name = "Ethertype";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "SET_NW_SRC" :
+            var h3 = "Set IP Source Address";
+            var placeholder = "Source IP Address";
+            var id = one.f.flows.id.modal.action.modifyNetworkSourceAddress;
+            var help = "Example: 127.0.0.1";
+            var action = 'SET_NW_SRC';
+            var name = "Source IP";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "SET_NW_DST" :
+            var h3 = "Set IP Destination Address";
+            var placeholder = "Destination IP Address";
+            var id = one.f.flows.id.modal.action.modifyNetworkDestinationAddress;
+            var help = "Example: 127.0.0.1";
+            var action = 'SET_NW_DST';
+            var name = "Destination IP";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "SET_NW_TOS" :
+            var h3 = "Set IPv4 ToS";
+            var placeholder = "IPv4 ToS";
+            var id = one.f.flows.id.modal.action.modifyTosBits;
+            var help = "Range: 0 - 63";
+            var action = 'SET_NW_TOS';
+            var name = "ToS Bits";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "SET_TP_SRC" :
+            var h3 = "Set Transport Source Port";
+            var placeholder = "Transport Source Port";
+            var id = one.f.flows.id.modal.action.modifyTransportSourcePort;
+            var help = "Range: 1 - 65535";
+            var action = 'SET_TP_SRC';
+            var name = "Source Port";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "SET_TP_DST" :
+            var h3 = "Set Transport Destination Port";
+            var placeholder = "Transport Destination Port";
+            var id = one.f.flows.id.modal.action.modifyTransportDestinationPort;
+            var help = "Range: 1 - 65535";
+            var action = 'SET_TP_DST';
+            var name = "Destination Port";
+            var body = function() {
+              return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+            };
+            var add = function($modal) {
+              one.f.flows.modal.action.add.set(name, id, action, $modal);
+            };
+            var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+            $modal.modal();
+            break;
+          case "ENQUEUE" :
+            var h3 = "Enqueue";
+            var placeholder = "Enqueue";
+            var id = one.f.flows.id.modal.action.enqueue;
+            var $modal = one.f.flows.modal.action.initialize(h3, one.f.flows.modal.action.body.addEnqueue, one.f.flows.modal.action.add.addEnqueue);
+            $modal.modal();
+            break;
+          case "DROP" :
+            var name = "Drop";
+            var action = 'DROP';
+            one.f.flows.modal.action.add.add(name, action);
+            break;
+          case "LOOPBACK" :
+            var name = "Loopback";
+            var action = 'LOOPBACK';
+            one.f.flows.modal.action.add.add(name, action);
+            break;
+          case "FLOOD" :
+            var name = "Flood";
+            var action = 'FLOOD';
+            one.f.flows.modal.action.add.add(name, action);
+            break;
+          case "FLOOD_ALL" :
+            var name = "Flood All";
+            var action = 'FLOOD_ALL';
+            one.f.flows.modal.action.add.add(name, action);
+            break;
+          case "SW_PATH" :
+            var name = "Software Path";
+            var action = 'SW_PATH';
+            one.f.flows.modal.action.add.add(name, action);
+            break;
+          case "HW_PATH" :
+            var name = "Hardware Path";
+            var action = 'HW_PATH';
+            one.f.flows.modal.action.add.add(name, action);
+            break;
+          case "CONTROLLER" :
+            var name = "Controller";
+            var action = 'CONTROLLER';
+            one.f.flows.modal.action.add.add(name, action);
+            break;
+        }
+      },
+      initialize : function(h3, bodyCallback, addCallback) {
+        var footer = one.f.flows.modal.action.footer();
+        var $body = bodyCallback();
+        var $modal = one.lib.modal.spawn(one.f.flows.id.modal.action.modal, h3, $body, footer);
+        // bind close button
+        $('#'+one.f.flows.id.modal.action.close, $modal).click(function() {
+          $modal.modal('hide');
+        });
+        // bind add flow button
+        $('#'+one.f.flows.id.modal.action.add, $modal).click(function() {
+          addCallback($modal);
+        });
+        return $modal;
+      },
+      add : {
+        addOutputPorts : function($modal) {
+          var $options = $('#'+one.f.flows.id.modal.action.addOutputPorts).find('option:selected');
+          var ports = '';
+          var pid = '';
+          $options.each(function(index, value) {
+            ports = ports+$(value).text()+", ";
+            pid = pid+$(value).attr('value')+",";
+          });
+          ports = ports.slice(0,-2);
+          pid = pid.slice(0,-1);
+          one.f.flows.modal.action.add.addPortsToTable(ports, pid);
+          $modal.modal('hide');
         },
-        ajax : {
-            nodes : function(successCallback) {
-                $.getJSON(one.f.address.root+one.f.address.flows.nodes, function(data) {
-                    var nodes = one.f.flows.modal.data.nodes(data);
-                    var nodeports = data;
-                    one.f.flows.registry['nodes'] = nodes;
-                    one.f.flows.registry['nodeports'] = nodeports;
-
-                    successCallback(nodes, nodeports);
-                });
-            },
-            saveflow : function(resource, callback) {
-                $.post(one.f.address.root+one.f.address.flows.flow, resource, function(data) {
-                    callback(data);
-                });
-            },
-            removeflow : function(id, node, callback) {
-                resource = {};
-                resource['action'] = 'remove';
-                $.post(one.f.address.root+one.f.address.flows.flow+'/'+node+'/'+id, resource, function(data) {
-                    callback(data);
-                });
-            },
-            toggleflow : function(id, node, callback) {
-                resource = {};
-                resource['action'] = 'toggle';
-                $.post(one.f.address.root+one.f.address.flows.flow+'/'+node+'/'+id, resource, function(data) {
-                    callback(data);
-                });
-            }
+        addEnqueue : function($modal) {
+          var $options = $('#'+one.f.flows.id.modal.action.addOutputPorts).find('option:selected');
+          var ports = '';
+          var pid = '';
+          $options.each(function(index, value) {
+            ports = ports+$(value).text()+", ";
+            pid = pid+$(value).attr('value')+",";
+          });
+          var $input = $('#'+one.f.flows.id.modal.action.queue);
+          var queue = $input.val();
+          ports = ports.slice(0,-2);
+          pid = pid.slice(0,-1);
+          one.f.flows.modal.action.add.addEnqueueToTable(ports, pid, queue);
+          $modal.modal('hide');
         },
-        data : {
-            nodes : function(data) {
-                result = {};
-                $.each(data, function(key, value) {
-                    result[key] = value['name'];
-                });
-                return result;
-            }
+        addEnqueueToTable : function(ports, pid, queue) {
+          if (queue !== '' && queue >= 0) {
+            ports += ':'+queue;
+          }
+          var $tr = one.f.flows.modal.action.table.add("Enqueue", ports);
+          $tr.attr('id', 'ENQUEUE');
+          if (queue !== '' && queue >= 0) {
+            $tr.data('action', 'ENQUEUE='+pid+':'+queue);
+          } else {
+            $tr.data('action', 'ENQUEUE='+pid+':0'); // default queue to 0
+          }
+          $tr.click(function() {
+            one.f.flows.modal.action.add.modal.initialize(this);
+          });
+          one.f.flows.modal.action.table.append($tr);
         },
-        body : function(nodes, nodeports, edit) {
-            var $form = $(document.createElement('form'));
-            var $fieldset = $(document.createElement('fieldset'));
-            var existingFlow;
-            // flow description
-            var $legend = one.lib.form.legend("");
-            $legend.css('visibility', 'hidden');
-            $fieldset.append($legend);
-            // name
-            var $label = one.lib.form.label("Name");
-            var $input = one.lib.form.input("Flow Name");
-            $input.attr('id', one.f.flows.id.modal.form.name);
-            if(edit) {
-                $input.attr('disabled', 'disabled');
-                var flows = one.f.flows.registry.flows;
-                $(flows).each(function(index, value) {
-                  if (value.name == one.f.flows.registry.selectedId && value.nodeId == one.f.flows.registry.selectedNode) {
-                    existingFlow = value.flow;
-                  }
-                });
-                $input.val(existingFlow.name);
-            }
-
-            $fieldset.append($label).append($input);
-            // node
-            var $label = one.lib.form.label("Node");
-            var $select = one.lib.form.select.create(nodes);
-            one.lib.form.select.prepend($select, { '' : 'Please Select a Node' });
-            $select.val($select.find("option:first").val());
-            $select.attr('id', one.f.flows.id.modal.form.nodes);
-            if(edit) {
-                $select.attr('disabled', 'disabled');
-                $select.val(existingFlow.node.type + "|"+ existingFlow.node.nodeIDString);
-            }
+        addPortsToTable : function(ports, pid){
+          var $tr = one.f.flows.modal.action.table.add("Add Output Ports", ports);
+          $tr.attr('id', 'OUTPUT');
+          $tr.data('action', 'OUTPUT='+pid);
+          $tr.click(function() {
+            one.f.flows.modal.action.add.modal.initialize(this);
+          });
+          one.f.flows.modal.action.table.append($tr);
+        },
+        add : function(name, action) {
+          var $tr = one.f.flows.modal.action.table.add(name);
+          $tr.attr('id', action);
+          $tr.data('action', action);
+          $tr.click(function() {
+            one.f.flows.modal.action.add.modal.initialize(this);
+          });
+          one.f.flows.modal.action.table.append($tr);
+        },
+        set : function(name, id, action, $modal) {
+          var $input = $('#'+id);
+          var value = $input.val();
+          one.f.flows.modal.action.add.addDataToTable(name,value,action)
+            $modal.modal('hide');
+        },
+        addDataToTable : function(name,value,action) {
+          var $tr = one.f.flows.modal.action.table.add(name, value);
+          $tr.attr('id', action);
+          $tr.data('action', action+'='+value);
+          $tr.click(function() {
+            one.f.flows.modal.action.add.modal.initialize(this);
+          });
+          one.f.flows.modal.action.table.append($tr);
+        },
+        remove : function(that) {
+          $(that).remove();
+          var $table = $('#'+one.f.flows.id.modal.action.table);
+          if ($table.find('tbody').find('tr').size() == 0) {
+            var $tr = $(document.createElement('tr'));
+            var $td = $(document.createElement('td'));
+            $td.attr('colspan', '3');
+            $tr.addClass('empty');
+            $td.text('No data available');
+            $tr.append($td);
+            $table.find('tbody').append($tr);
+          }
+        },
+        modal : {
+          initialize : function(that) {
+            var h3 = "Remove Action";
+            var footer = one.f.flows.modal.action.add.modal.footer();
+            var $body = one.f.flows.modal.action.add.modal.body();
+            var $modal = one.lib.modal.spawn(one.f.flows.id.modal.action.modal.modal, h3, $body, footer);
+
+            // bind cancel button
+            $('#'+one.f.flows.id.modal.action.modal.cancel, $modal).click(function() {
+              $modal.modal('hide');
+            });
 
-            // bind onchange
-            $select.change(function() {
-                // retrieve port value
-                var node = $(this).find('option:selected').attr('value');
-                var $ports = $('#'+one.f.flows.id.modal.form.port);
-                if (node == '') {
-                    one.lib.form.select.inject($ports, {});
-                    return;
-                }
-                one.f.flows.registry['currentNode'] = node;
-                var ports = nodeports[node]['ports'];
-                one.lib.form.select.inject($ports, ports);
-                one.lib.form.select.prepend($ports, { '' : 'Please Select a Port' });
-                $ports.val($ports.find("option:first").val());
-                if(edit) {
-                    $ports.val( existingFlow.ingressPort );
-                }
+            // bind remove button
+            $('#'+one.f.flows.id.modal.action.modal.remove, $modal).click(function() {
+              one.f.flows.modal.action.add.remove(that);
+              $modal.modal('hide');
             });
 
-            $fieldset.append($label).append($select);
-            // input port
-            var $label = one.lib.form.label("Input Port");
-            var $select = one.lib.form.select.create();
-
-            $select.attr('id', one.f.flows.id.modal.form.port);
-            $fieldset.append($label).append($select);
-            // priority
-            var $label = one.lib.form.label("Priority");
-            var $input = one.lib.form.input("Priority");
-            $input.attr('id', one.f.flows.id.modal.form.priority);
-            $input.val('500');
-            $fieldset.append($label).append($input);
-            if(edit) {
-                $input.val(existingFlow.priority);
-            }
-            // hardTimeout
-            var $label = one.lib.form.label("Hard Timeout");
-            var $input = one.lib.form.input("Hard Timeout");
-            $input.attr('id', one.f.flows.id.modal.form.hardTimeout);
-            if(edit) {
-                $input.val(existingFlow.hardTimeout);
-            }
-            $fieldset.append($label).append($input);
-
-            // idleTimeout
-            var $label = one.lib.form.label("Idle Timeout");
-            var $input = one.lib.form.input("Idle Timeout");
-            $input.attr('id', one.f.flows.id.modal.form.idleTimeout);
-            $fieldset.append($label).append($input);
-            if(edit) {
-                $input.val(existingFlow.idleTimeout);
-            }
-            // cookie
-            var $label = one.lib.form.label("Cookie");
-            var $input = one.lib.form.input("Cookie");
-            $input.attr('id', one.f.flows.id.modal.form.cookie);
-            $fieldset.append($label).append($input);
-            if(edit) {
-                $input.val(existingFlow.cookie);
-            }
+            $modal.modal();
+          },
+          body : function() {
+            var $p = $(document.createElement('p'));
+            $p.append("Remove this action?");
+            return $p;
+          },
+          footer : function() {
+            var footer = [];
 
-            // layer 2
-            var $legend = one.lib.form.legend("Layer 2");
-            $fieldset.append($legend);
-            // etherType
-            var $label = one.lib.form.label("Ethernet Type");
-            var $input = one.lib.form.input("Ethernet Type");
-            $input.attr('id', one.f.flows.id.modal.form.etherType);
-            $input.val('0x800');
-            $fieldset.append($label).append($input);
-            if(edit) {
-                $input.val(existingFlow.etherType);
-            }
-            // vlanId
-            var $label = one.lib.form.label("VLAN Identification Number");
-            var $input = one.lib.form.input("VLAN Identification Number");
-            $input.attr('id', one.f.flows.id.modal.form.vlanId);
-            var $help = one.lib.form.help("Range: 0 - 4095");
-            $fieldset.append($label).append($input).append($help);
-            if(edit) {
-                $input.val(existingFlow.vlanId);
-            }
+            var removeButton = one.lib.dashlet.button.single("Remove Action", one.f.flows.id.modal.action.modal.remove, "btn-danger", "");
+            var $removeButton = one.lib.dashlet.button.button(removeButton);
+            footer.push($removeButton);
 
-            // vlanPriority
-            var $label = one.lib.form.label("VLAN Priority");
-            var $input = one.lib.form.input("VLAN Priority");
-            $input.attr('id', one.f.flows.id.modal.form.vlanPriority);
-            var $help = one.lib.form.help("Range: 0 - 7");
-            $fieldset.append($label).append($input).append($help);
-            if(edit) {
-                $input.val(existingFlow.vlanPriority);
-            }
+            var cancelButton = one.lib.dashlet.button.single("Cancel", one.f.flows.id.modal.action.modal.cancel, "", "");
+            var $cancelButton = one.lib.dashlet.button.button(cancelButton);
+            footer.push($cancelButton);
 
-            // srcMac
-            var $label = one.lib.form.label("Source MAC Address");
-            var $input = one.lib.form.input("3c:97:0e:75:c3:f7");
-            $input.attr('id', one.f.flows.id.modal.form.srcMac);
-            $fieldset.append($label).append($input);
-            if(edit) {
-                $input.val(existingFlow.srcMac);
-            }
-            // dstMac
-            var $label = one.lib.form.label("Destination MAC Address");
-            var $input = one.lib.form.input("7c:d1:c3:e8:e6:99");
-            $input.attr('id', one.f.flows.id.modal.form.dstMac);
-            $fieldset.append($label).append($input);
-            if(edit) {
-                $input.val(existingFlow.dstMac);
-            }
-            // layer 3
-            var $legend = one.lib.form.legend("Layer 3");
-            $fieldset.append($legend);
-
-            // srcIp
-            var $label = one.lib.form.label("Source IP Address");
-            var $input = one.lib.form.input("192.168.3.128");
-            $input.attr('id', one.f.flows.id.modal.form.srcIp);
-            $fieldset.append($label).append($input);
-            if(edit) {
-                $input.val(existingFlow.srcIp);
-            }
-            // dstIp
-            var $label = one.lib.form.label("Destination IP Address");
-            var $input = one.lib.form.input("2001:2334::0/32");
-            $input.attr('id', one.f.flows.id.modal.form.dstIp);
-            $fieldset.append($label).append($input);
-            if(edit) {
-                $input.val(existingFlow.dstIp);
-            }
-            // tosBits
-            var $label = one.lib.form.label("ToS Bits");
-            var $input = one.lib.form.input("ToS Bits");
-            $input.attr('id', one.f.flows.id.modal.form.tosBits);
-            var $help = one.lib.form.help("Range: 0 - 63");
-            $fieldset.append($label).append($input).append($help);
-            if(edit) {
-                $input.val(existingFlow.tosBits);
-            }
+            return footer;
+          }
+        }
+      },
+      table : {
+        add : function(action, data) {
+          var $tr = $(document.createElement('tr'));
+          var $td = $(document.createElement('td'));
+          $td.append(action);
+          $tr.append($td);
+          var $td = $(document.createElement('td'));
+          if (data != undefined) $td.append(data);
+          $tr.append($td);
+          return $tr;
+        },
+        append : function($tr) {
+          var $table = $('#'+one.f.flows.id.modal.action.table);
+          var $empty = $table.find('.empty').parent();
+          if ($empty.size() > 0) $empty.remove();
+          $table.append($tr);
+        }
+      },
+      body : {
+        common : function() {
+          var $form = $(document.createElement('form'));
+          var $fieldset = $(document.createElement('fieldset'));
+          return [$form, $fieldset];
+        },
+        addOutputPorts : function() {
+          var common = one.f.flows.modal.action.body.common();
+          var $form = common[0];
+          var $fieldset = common[1];
+          // output port
+          $label = one.lib.form.label("Select Output Ports");
+          if (one.f.flows.registry.currentNode == undefined){
+            return; //Selecting Output ports without selecting node throws an exception
+          }
+          var ports = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports'];
+          $select = one.lib.form.select.create(ports, true);
+          $select.attr('id', one.f.flows.id.modal.action.addOutputPorts);
+          $fieldset.append($label).append($select);
+          $form.append($fieldset);
+          return $form;
+        },
+        addEnqueue : function() {
+          var common = one.f.flows.modal.action.body.common();
+          var $form = common[0];
+          var $fieldset = common[1];
+          // output port
+          $label = one.lib.form.label("Select Output Ports");
+          if (one.f.flows.registry.currentNode == undefined){
+            return; //Selecting Output ports without selecting node throws an exception
+          }
+          var ports = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports'];
+          $select = one.lib.form.select.create(ports);
+          $select.attr('id', one.f.flows.id.modal.action.addOutputPorts);
+          $fieldset.append($label).append($select);
+          $label = one.lib.form.label('Queue (Optional)');
+          $input = one.lib.form.input('Queue')
+          .attr('id', one.f.flows.id.modal.action.queue);
+          $help = one.lib.form.help('Range: 1 - 2147483647');
+          $fieldset.append($label).append($input).append($help);
+          $form.append($fieldset);
+          return $form;
+        },
+        set : function(label, placeholder, id, help) {
+          var common = one.f.flows.modal.action.body.common();
+          var $form = common[0];
+          var $fieldset = common[1];
+          // input
+          $label = one.lib.form.label(label);
+          $input = one.lib.form.input(placeholder);
+          $input.attr('id', id);
+          $help = one.lib.form.help(help);
+          // append
+          $fieldset.append($label).append($input).append($help);
+          $form.append($fieldset);
+          return $form;
+        }
+      },
+      footer : function() {
+        var footer = [];
+        var addButton = one.lib.dashlet.button.single("Add Action", one.f.flows.id.modal.action.add, "btn-primary", "");
+        var $addButton = one.lib.dashlet.button.button(addButton);
+        footer.push($addButton);
+
+        var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.action.close, "", "");
+        var $closeButton = one.lib.dashlet.button.button(closeButton);
+        footer.push($closeButton);
+
+        return footer;
+      }
+    },
+    footer : function() {
+      var footer = [];
 
-            // layer 4
-            var $legend = one.lib.form.legend("Layer 4");
-            $fieldset.append($legend);
-            // srcPort
-            var $label = one.lib.form.label("Source Port");
-            var $input = one.lib.form.input("Source Port");
-            $input.attr('id', one.f.flows.id.modal.form.srcPort);
-            var $help = one.lib.form.help("Range: 0 - 65535");
-            $fieldset.append($label).append($input).append($help);
-            if(edit) {
-                $input.val(existingFlow.srcPort);
-            }
-            // dstPort
-            var $label = one.lib.form.label("Destination Port");
-            var $input = one.lib.form.input("Destination Port");
-            $input.attr('id', one.f.flows.id.modal.form.dstPort);
-            var $help = one.lib.form.help("Range: 0 - 65535");
-            $fieldset.append($label).append($input).append($help);
-            if(edit) {
-                $input.val(existingFlow.dstPort);
-            }
-            // protocol
-            var $label = one.lib.form.label("Protocol");
-            var $input = one.lib.form.input("Protocol");
-            $input.attr('id', one.f.flows.id.modal.form.protocol);
-            $fieldset.append($label).append($input);
-            if(edit) {
-                $input.val(existingFlow.protocol);
-            }
-            // actions
-            var $legend = one.lib.form.label("Actions");
-            $fieldset.append($legend);
-            // actions table
-            var tableAttributes = ["table-striped", "table-bordered", "table-condensed", "table-hover", "table-cursor"];
-            var $table = one.lib.dashlet.table.table(tableAttributes);
-            $table.attr('id', one.f.flows.id.modal.action.table);
-            var tableHeaders = ["Action", "Data"];
-            var $thead = one.lib.dashlet.table.header(tableHeaders);
-            var $tbody = one.lib.dashlet.table.body("", tableHeaders);
-            $table.append($thead).append($tbody);
-            // actions
-            var actions = {
-                "" : "Please Select an Action",
-                "DROP" : "Drop",
-                "LOOPBACK" : "Loopback",
-                "FLOOD" : "Flood",
-                "SW_PATH" : "Software Path",
-                "HW_PATH" : "Hardware Path",
-                "CONTROLLER" : "Controller",
-                "OUTPUT" : "Add Output Ports",
-                "SET_VLAN_ID" : "Set VLAN ID",
-                "SET_VLAN_PCP" : "Set VLAN Priority",
-                "POP_VLAN" : "Strip VLAN Header",
-                "SET_DL_SRC" : "Modify Datalayer Source Address",
-                "SET_DL_DST" : "Modify Datalayer Destination Address",
-                "SET_NW_SRC" : "Modify Network Source Address",
-                "SET_NW_DST" :"Modify Network Destination Address",
-                "SET_NW_TOS" : "Modify ToS Bits",
-                "SET_TP_SRC" : "Modify Transport Source Port",
-                "SET_TP_DST" : "Modify Transport Destination Port"
-            };
-            var $select = one.lib.form.select.create(actions);
-            // when selecting an action
-            $select.change(function() {
-                var action = $(this).find('option:selected');
-                one.f.flows.modal.action.parse(action.attr('value'));
-                $select[0].selectedIndex = 0;
-            });
+      var installButton = one.lib.dashlet.button.single("Install Flow", one.f.flows.id.modal.install, "btn-success", "");
+      var $installButton = one.lib.dashlet.button.button(installButton);
+      footer.push($installButton);
 
-            if(edit) {
-                $(existingFlow.actions).each(function(index, value){
-                    setTimeout(function(){
-                        var locEqualTo = value.indexOf("=");
-                        if ( locEqualTo == -1 ) {
-                            one.f.flows.modal.action.add.add(actions[value], value);
-                        } else {
-                            var action = value.substr(0,locEqualTo);
-                            if( action == "OUTPUT") {
-                                var portIds = value.substr(locEqualTo+1).split(",");
-                                var ports = [];
-                                var allPorts = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports'];
-                                for(var i =0; i < portIds.length ; i++) {
-                                    var portName = allPorts[portIds[i]];
-                                    ports.push(portName);
-                                }
-                                one.f.flows.modal.action.add.addPortsToTable(ports.join(", "), portIds.join(","));
-                            } else {
-                                var val = value.substr(locEqualTo+1);
-                                one.f.flows.modal.action.add.addDataToTable(actions[action], val, action)
-                            }
-                        }
-                    }, 1000)
-                });
-            }
-            $fieldset.append($select).append($table);
+      var addButton = one.lib.dashlet.button.single("Save Flow", one.f.flows.id.modal.add, "btn-primary", "");
+      var $addButton = one.lib.dashlet.button.button(addButton);
+      footer.push($addButton);
 
-            // return
-            $form.append($fieldset);
-            return $form;
-        },
-        action : {
-            parse : function(option) {
-                switch (option) {
-                    case "OUTPUT" :
-                        var h3 = "Add Output Port";
-                        var $modal = one.f.flows.modal.action.initialize(h3, one.f.flows.modal.action.body.addOutputPorts, one.f.flows.modal.action.add.addOutputPorts);
-                        $modal.modal();
-                        break;
-                    case "SET_VLAN_ID" :
-                        var h3 = "Set VLAN ID";
-                        var placeholder = "VLAN Identification Number";
-                        var id = one.f.flows.id.modal.action.setVlanId;
-                        var help = "Range: 0 - 4095";
-                        var action = 'SET_VLAN_ID';
-                        var name = "VLAN ID";
-                        var body = function() {
-                            return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
-                        };
-                        var add = function($modal) {
-                            one.f.flows.modal.action.add.set(name, id, action, $modal);
-                        };
-                        var $modal = one.f.flows.modal.action.initialize(h3, body, add);
-                        $modal.modal();
-                        break;
-                    case "SET_VLAN_PCP" :
-                        var h3 = "Set VLAN Priority";
-                        var placeholder = "VLAN Priority";
-                        var id = one.f.flows.id.modal.action.setVlanPriority;
-                        var help = "Range: 0 - 7";
-                        var action = 'SET_VLAN_PCP';
-                        var name = "VLAN Priority";
-                        var body = function() {
-                            return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
-                        };
-                        var add = function($modal) {
-                            one.f.flows.modal.action.add.set(name, id, action, $modal);
-                        };
-                        var $modal = one.f.flows.modal.action.initialize(h3, body, add);
-                        $modal.modal();
-                        break;
-                    case "POP_VLAN" :
-                        var name = "Strip VLAN Header";
-                        var action = 'POP_VLAN';
-                        one.f.flows.modal.action.add.add(name, action);
-                        break;
-                    case "SET_DL_SRC" :
-                        var h3 = "Set Source MAC Address";
-                        var placeholder = "Source MAC Address";
-                        var id = one.f.flows.id.modal.action.modifyDatalayerSourceAddress;
-                        var help = "Example: 00:11:22:aa:bb:cc";
-                        var action = 'SET_DL_SRC';
-                        var name = "Source MAC";
-                        var body = function() {
-                            return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
-                        };
-                        var add = function($modal) {
-                            one.f.flows.modal.action.add.set(name, id, action, $modal);
-                        };
-                        var $modal = one.f.flows.modal.action.initialize(h3, body, add);
-                        $modal.modal();
-                        break;
-                    case "SET_DL_DST" :
-                        var h3 = "Set Destination MAC Address";
-                        var placeholder = "Destination MAC Address";
-                        var id = one.f.flows.id.modal.action.modifyDatalayerDestinationAddress;
-                        var help = "Example: 00:11:22:aa:bb:cc";
-                        var action = 'SET_DL_DST';
-                        var name = "Destination MAC";
-                        var body = function() {
-                            return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
-                        };
-                        var add = function($modal) {
-                            one.f.flows.modal.action.add.set(name, id, action, $modal);
-                        };
-                        var $modal = one.f.flows.modal.action.initialize(h3, body, add);
-                        $modal.modal();
-                        break;
-                    case "SET_NW_SRC" :
-                        var h3 = "Set IP Source Address";
-                        var placeholder = "Source IP Address";
-                        var id = one.f.flows.id.modal.action.modifyNetworkSourceAddress;
-                        var help = "Example: 127.0.0.1";
-                        var action = 'SET_NW_SRC';
-                        var name = "Source IP";
-                        var body = function() {
-                            return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
-                        };
-                        var add = function($modal) {
-                            one.f.flows.modal.action.add.set(name, id, action, $modal);
-                        };
-                        var $modal = one.f.flows.modal.action.initialize(h3, body, add);
-                        $modal.modal();
-                        break;
-                    case "SET_NW_DST" :
-                        var h3 = "Set IP Destination Address";
-                        var placeholder = "Destination IP Address";
-                        var id = one.f.flows.id.modal.action.modifyNetworkDestinationAddress;
-                        var help = "Example: 127.0.0.1";
-                        var action = 'SET_NW_DST';
-                        var name = "Destination IP";
-                        var body = function() {
-                            return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
-                        };
-                        var add = function($modal) {
-                            one.f.flows.modal.action.add.set(name, id, action, $modal);
-                        };
-                        var $modal = one.f.flows.modal.action.initialize(h3, body, add);
-                        $modal.modal();
-                        break;
-                    case "SET_NW_TOS" :
-                        var h3 = "Set IPv4 ToS";
-                        var placeholder = "IPv4 ToS";
-                        var id = one.f.flows.id.modal.action.modifyTosBits;
-                        var help = "Range: 0 - 63";
-                        var action = 'SET_NW_TOS';
-                        var name = "ToS Bits";
-                        var body = function() {
-                            return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
-                        };
-                        var add = function($modal) {
-                            one.f.flows.modal.action.add.set(name, id, action, $modal);
-                        };
-                        var $modal = one.f.flows.modal.action.initialize(h3, body, add);
-                        $modal.modal();
-                        break;
-                    case "SET_TP_SRC" :
-                        var h3 = "Set Transport Source Port";
-                        var placeholder = "Transport Source Port";
-                        var id = one.f.flows.id.modal.action.modifyTransportSourcePort;
-                        var help = "Range: 1 - 65535";
-                        var action = 'SET_TP_SRC';
-                        var name = "Source Port";
-                        var body = function() {
-                            return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
-                        };
-                        var add = function($modal) {
-                            one.f.flows.modal.action.add.set(name, id, action, $modal);
-                        };
-                        var $modal = one.f.flows.modal.action.initialize(h3, body, add);
-                        $modal.modal();
-                        break;
-                    case "SET_TP_DST" :
-                        var h3 = "Set Transport Destination Port";
-                        var placeholder = "Transport Destination Port";
-                        var id = one.f.flows.id.modal.action.modifyTransportDestinationPort;
-                        var help = "Range: 1 - 65535";
-                        var action = 'SET_TP_DST';
-                        var name = "Destination Port";
-                        var body = function() {
-                            return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
-                        };
-                        var add = function($modal) {
-                            one.f.flows.modal.action.add.set(name, id, action, $modal);
-                        };
-                        var $modal = one.f.flows.modal.action.initialize(h3, body, add);
-                        $modal.modal();
-                        break;
-                    case "DROP" :
-                        var name = "Drop";
-                        var action = 'DROP';
-                        one.f.flows.modal.action.add.add(name, action);
-                        break;
-                    case "LOOPBACK" :
-                        var name = "Loopback";
-                        var action = 'LOOPBACK';
-                        one.f.flows.modal.action.add.add(name, action);
-                        break;
-                    case "FLOOD" :
-                        var name = "Flood";
-                        var action = 'FLOOD';
-                        one.f.flows.modal.action.add.add(name, action);
-                        break;
-                    case "SW_PATH" :
-                        var name = "Software Path";
-                        var action = 'SW_PATH';
-                        one.f.flows.modal.action.add.add(name, action);
-                        break;
-                    case "HW_PATH" :
-                        var name = "Hardware Path";
-                        var action = 'HW_PATH';
-                        one.f.flows.modal.action.add.add(name, action);
-                        break;
-                    case "CONTROLLER" :
-                        var name = "Controller";
-                        var action = 'CONTROLLER';
-                        one.f.flows.modal.action.add.add(name, action);
-                        break;
-                }
-            },
-            initialize : function(h3, bodyCallback, addCallback) {
-                var footer = one.f.flows.modal.action.footer();
-                var $body = bodyCallback();
-                var $modal = one.lib.modal.spawn(one.f.flows.id.modal.action.modal, h3, $body, footer);
-                // bind close button
-                $('#'+one.f.flows.id.modal.action.close, $modal).click(function() {
-                    $modal.modal('hide');
-                });
-                // bind add flow button
-                $('#'+one.f.flows.id.modal.action.add, $modal).click(function() {
-                    addCallback($modal);
-                });
-                return $modal;
-            },
-            add : {
-                addOutputPorts : function($modal) {
-                    var $options = $('#'+one.f.flows.id.modal.action.addOutputPorts).find('option:selected');
-                    var ports = '';
-                    var pid = '';
-                    $options.each(function(index, value) {
-                        ports = ports+$(value).text()+", ";
-                        pid = pid+$(value).attr('value')+",";
-                    });
-                    ports = ports.slice(0,-2);
-                    pid = pid.slice(0,-1);
-                    one.f.flows.modal.action.add.addPortsToTable(ports, pid);
-                    $modal.modal('hide');
-                },
-                addPortsToTable : function(ports, pid){
-                    var $tr = one.f.flows.modal.action.table.add("Add Output Ports", ports);
-                    $tr.attr('id', 'OUTPUT');
-                    $tr.data('action', 'OUTPUT='+pid);
-                    $tr.click(function() {
-                        one.f.flows.modal.action.add.modal.initialize(this);
-                    });
-                    one.f.flows.modal.action.table.append($tr);
-                },
-                add : function(name, action) {
-                    var $tr = one.f.flows.modal.action.table.add(name);
-                    $tr.attr('id', action);
-                    $tr.data('action', action);
-                    $tr.click(function() {
-                        one.f.flows.modal.action.add.modal.initialize(this);
-                    });
-                    one.f.flows.modal.action.table.append($tr);
-                },
-                set : function(name, id, action, $modal) {
-                    var $input = $('#'+id);
-                    var value = $input.val();
-                    one.f.flows.modal.action.add.addDataToTable(name,value,action)
-                    $modal.modal('hide');
-                },
-                addDataToTable : function(name,value,action) {
-                    var $tr = one.f.flows.modal.action.table.add(name, value);
-                    $tr.attr('id', action);
-                    $tr.data('action', action+'='+value);
-                    $tr.click(function() {
-                        one.f.flows.modal.action.add.modal.initialize(this);
-                    });
-                    one.f.flows.modal.action.table.append($tr);
-                },
-                remove : function(that) {
-                    $(that).remove();
-                    var $table = $('#'+one.f.flows.id.modal.action.table);
-                    if ($table.find('tbody').find('tr').size() == 0) {
-                        var $tr = $(document.createElement('tr'));
-                        var $td = $(document.createElement('td'));
-                        $td.attr('colspan', '3');
-                        $tr.addClass('empty');
-                        $td.text('No data available');
-                        $tr.append($td);
-                        $table.find('tbody').append($tr);
-                    }
-                },
-                modal : {
-                    initialize : function(that) {
-                        var h3 = "Remove Action";
-                        var footer = one.f.flows.modal.action.add.modal.footer();
-                        var $body = one.f.flows.modal.action.add.modal.body();
-                        var $modal = one.lib.modal.spawn(one.f.flows.id.modal.action.modal.modal, h3, $body, footer);
-
-                        // bind cancel button
-                        $('#'+one.f.flows.id.modal.action.modal.cancel, $modal).click(function() {
-                            $modal.modal('hide');
-                        });
-
-                        // bind remove button
-                        $('#'+one.f.flows.id.modal.action.modal.remove, $modal).click(function() {
-                            one.f.flows.modal.action.add.remove(that);
-                            $modal.modal('hide');
-                        });
-
-                        $modal.modal();
-                    },
-                    body : function() {
-                        var $p = $(document.createElement('p'));
-                        $p.append("Remove this action?");
-                        return $p;
-                    },
-                    footer : function() {
-                        var footer = [];
-
-                        var removeButton = one.lib.dashlet.button.single("Remove Action", one.f.flows.id.modal.action.modal.remove, "btn-danger", "");
-                        var $removeButton = one.lib.dashlet.button.button(removeButton);
-                        footer.push($removeButton);
-
-                        var cancelButton = one.lib.dashlet.button.single("Cancel", one.f.flows.id.modal.action.modal.cancel, "", "");
-                        var $cancelButton = one.lib.dashlet.button.button(cancelButton);
-                        footer.push($cancelButton);
-
-                        return footer;
-                    }
-                }
-            },
-            table : {
-                add : function(action, data) {
-                    var $tr = $(document.createElement('tr'));
-                    var $td = $(document.createElement('td'));
-                    $td.append(action);
-                    $tr.append($td);
-                    var $td = $(document.createElement('td'));
-                    if (data != undefined) $td.append(data);
-                    $tr.append($td);
-                    return $tr;
-                },
-                append : function($tr) {
-                    var $table = $('#'+one.f.flows.id.modal.action.table);
-                    var $empty = $table.find('.empty').parent();
-                    if ($empty.size() > 0) $empty.remove();
-                    $table.append($tr);
-                }
-            },
-            body : {
-                common : function() {
-                    var $form = $(document.createElement('form'));
-                    var $fieldset = $(document.createElement('fieldset'));
-                    return [$form, $fieldset];
-                },
-                addOutputPorts : function() {
-                    var common = one.f.flows.modal.action.body.common();
-                    var $form = common[0];
-                    var $fieldset = common[1];
-                    // output port
-                    $label = one.lib.form.label("Select Output Ports");
-                    if (one.f.flows.registry.currentNode == undefined){
-                        return; //Selecting Output ports without selecting node throws an exception
-                    }
-                    var ports = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports'];
-                    $select = one.lib.form.select.create(ports, true);
-                    $select.attr('id', one.f.flows.id.modal.action.addOutputPorts);
-                    $fieldset.append($label).append($select);
-                    $form.append($fieldset);
-                    return $form;
-                },
-                set : function(label, placeholder, id, help) {
-                    var common = one.f.flows.modal.action.body.common();
-                    var $form = common[0];
-                    var $fieldset = common[1];
-                    // input
-                    $label = one.lib.form.label(label);
-                    $input = one.lib.form.input(placeholder);
-                    $input.attr('id', id);
-                    $help = one.lib.form.help(help);
-                    // append
-                    $fieldset.append($label).append($input).append($help);
-                    $form.append($fieldset);
-                    return $form;
-                }
-            },
-            footer : function() {
-                var footer = [];
-                var addButton = one.lib.dashlet.button.single("Add Action", one.f.flows.id.modal.action.add, "btn-primary", "");
-                var $addButton = one.lib.dashlet.button.button(addButton);
-                footer.push($addButton);
-
-                var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.action.close, "", "");
-                var $closeButton = one.lib.dashlet.button.button(closeButton);
-                footer.push($closeButton);
-
-                return footer;
-            }
-        },
-        footer : function() {
-            var footer = [];
+      var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.close, "", "");
+      var $closeButton = one.lib.dashlet.button.button(closeButton);
+      footer.push($closeButton);
 
-            var installButton = one.lib.dashlet.button.single("Install Flow", one.f.flows.id.modal.install, "btn-success", "");
-            var $installButton = one.lib.dashlet.button.button(installButton);
-            footer.push($installButton);
+      return footer;
+    },
+    footerEdit : function() {
+      var footer = [];
 
-            var addButton = one.lib.dashlet.button.single("Save Flow", one.f.flows.id.modal.add, "btn-primary", "");
-            var $addButton = one.lib.dashlet.button.button(addButton);
-            footer.push($addButton);
+      var editButton = one.lib.dashlet.button.single("Save Flow", one.f.flows.id.modal.edit, "btn-success", "");
+      var $editButton = one.lib.dashlet.button.button(editButton);
+      footer.push($editButton);
 
-            var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.close, "", "");
-            var $closeButton = one.lib.dashlet.button.button(closeButton);
-            footer.push($closeButton);
+      var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.close, "", "");
+      var $closeButton = one.lib.dashlet.button.button(closeButton);
+      footer.push($closeButton);
 
-            return footer;
-        },
-        footerEdit : function() {
-            var footer = [];
+      return footer;
+    },
+    removeMultiple: {
+      dialog: function(flows) {
+        var h3 = 'Remove Flow Entry';
+        var flowList = [];
+        for (var i = 0; i < flows.length; i++) {
+          flowList.push(flows[i]["name"]);
+        }
+        var footer = one.f.flows.modal.removeMultiple.footer();
+        var $body = one.f.flows.modal.removeMultiple.body(flowList);
+        var $modal = one.lib.modal.spawn(one.f.flows.id.modal.dialog.modal, h3, $body, footer);
 
-            var editButton = one.lib.dashlet.button.single("Save Flow", one.f.flows.id.modal.edit, "btn-success", "");
-            var $editButton = one.lib.dashlet.button.button(editButton);
-            footer.push($editButton);
+        // bind close button
+        $('#'+one.f.flows.id.modal.dialog.close, $modal).click(function() {
+          $modal.modal('hide');
+        });
 
-            var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.close, "", "");
-            var $closeButton = one.lib.dashlet.button.button(closeButton);
-            footer.push($closeButton);
+        // bind remove rule button
+        $('#'+one.f.flows.id.modal.dialog.remove, $modal).click(this, function(e) {
+          var resource = {};
+          resource['body'] = JSON.stringify(flows);
 
-            return footer;
-        },
-        removeMultiple: {
-            dialog: function(flows) {
-                var h3 = 'Remove Flow Entry';
-                var flowList = [];
-                for (var i = 0; i < flows.length; i++) {
-                    flowList.push(flows[i]["name"]);
-                }
-                var footer = one.f.flows.modal.removeMultiple.footer();
-                var $body = one.f.flows.modal.removeMultiple.body(flowList);
-                var $modal = one.lib.modal.spawn(one.f.flows.id.modal.dialog.modal, h3, $body, footer);
-
-                // bind close button
-                $('#'+one.f.flows.id.modal.dialog.close, $modal).click(function() {
-                    $modal.modal('hide');
-                });
-
-                // bind remove rule button
-                $('#'+one.f.flows.id.modal.dialog.remove, $modal).click(this, function(e) {
-                    var resource = {};
-                    resource['body'] = JSON.stringify(flows);
-
-                    $.post(one.f.address.root+one.f.address.flows.deleteFlows, resource, function(response) {
-                        $modal.modal('hide');
-                        if(response == "Success") {
-                            one.lib.alert("Flow Entry(s) successfully removed");
-                        } else {
-                            one.lib.alert(response);
-                        }
-                        one.main.dashlet.right.bottom.empty();
-                        one.f.detail.dashlet(one.main.dashlet.right.bottom);
-                        one.main.dashlet.left.top.empty();
-                        one.f.flows.dashlet(one.main.dashlet.left.top);
-                    });
-                });
-                $modal.modal();
-            },
-            footer : function() {
-                var footer = [];
-                var remove = one.lib.dashlet.button.single('Remove Flow Entry',one.f.flows.id.modal.dialog.remove, 'btn-danger', '');
-                var $remove = one.lib.dashlet.button.button(remove);
-                footer.push($remove);
-
-                var cancel = one.lib.dashlet.button.single('Cancel', one.f.flows.id.modal.dialog.close, '', '');
-                var $cancel = one.lib.dashlet.button.button(cancel);
-                footer.push($cancel);
-
-                return footer;
-            },
-            body : function (flows) {
-                var $p = $(document.createElement('p'));
-                var p = 'Remove the following Flow Entry(s)?';
-                //creata a BS label for each rule and append to list
-                $(flows).each(function(){
-                    var $span = $(document.createElement('span'));
-                    $span.append(this);
-                    p += '<br/>' + $span[0].outerHTML;
-                });
-                $p.append(p);
-                return $p;
+          $.post(one.f.address.root+one.f.address.flows.deleteFlows, resource, function(response) {
+            $modal.modal('hide');
+            if(response == "Success") {
+              one.lib.alert("Flow Entry(s) successfully removed");
+            } else {
+              one.lib.alert(response);
             }
-        }
-    },
-    ajax : {
-        dashlet : function(callback) {
-            $.getJSON(one.f.address.root+one.f.address.flows.main, function(data) {
-                one.f.flows.registry['flows'] = data.flows;
-                one.f.flows.registry['privilege'] = data.privilege;
-                one.f.flows.modal.ajax.nodes(function(){/*Empty function. Do nothing. */})
+            one.main.dashlet.right.bottom.empty();
+            one.f.detail.dashlet(one.main.dashlet.right.bottom);
+            one.main.dashlet.left.top.empty();
+            one.f.flows.dashlet(one.main.dashlet.left.top);
+          });
+        });
+        $modal.modal();
+      },
+      footer : function() {
+        var footer = [];
+        var remove = one.lib.dashlet.button.single('Remove Flow Entry',one.f.flows.id.modal.dialog.remove, 'btn-danger', '');
+        var $remove = one.lib.dashlet.button.button(remove);
+        footer.push($remove);
+
+        var cancel = one.lib.dashlet.button.single('Cancel', one.f.flows.id.modal.dialog.close, '', '');
+        var $cancel = one.lib.dashlet.button.button(cancel);
+        footer.push($cancel);
+
+        return footer;
+      },
+      body : function (flows) {
+        var $p = $(document.createElement('p'));
+        var p = 'Remove the following Flow Entry(s)?';
+        //creata a BS label for each rule and append to list
+        $(flows).each(function(){
+          var $span = $(document.createElement('span'));
+          $span.append(this);
+          p += '<br/>' + $span[0].outerHTML;
+        });
+        $p.append(p);
+        return $p;
+      }
+    }
+  },
+  ajax : {
+    dashlet : function(callback) {
+      $.getJSON(one.f.address.root+one.f.address.flows.main, function(data) {
+        one.f.flows.registry['flows'] = data.flows;
+        one.f.flows.registry['privilege'] = data.privilege;
+        one.f.flows.modal.ajax.nodes(function(){/*Empty function. Do nothing. */})
+
+        callback(data);
+      });
+    }
+  },
+  data : {
+    flowsDataGrid: function(data) {
+      var source = new StaticDataSource({
+        columns: [
+      {
+        property: 'selector',
+      label: "<input type='checkbox' id='"+one.f.flows.id.dashlet.datagrid.selectAllFlows+"'/>",
+      sortable: false
+      },
+      {
+        property: 'name',
+      label: 'Flow Name',
+      sortable: true
+      },
+      {
+        property: 'node',
+      label: 'Node',
+      sortable: true
+      }
+      ],
+      data: data.flows,
+      formatter: function(items) {
+        $.each(items, function(index, item) {
+          var $checkbox = document.createElement("input");
+          $checkbox.setAttribute("type", "checkbox");
+          $checkbox.setAttribute("name", item.name);
+          $checkbox.setAttribute("node", item.nodeId);
+          $checkbox.setAttribute('class','flowEntry')
+          item.selector = $checkbox.outerHTML;
+        item["name"] = '<span data-installInHw=' + item["flow"]["installInHw"] + 
+          ' data-flowstatus=' + item["flow"]["status"] + 
+          ' data-nodeId=' + item["nodeId"] + '>' + item["name"] + '</span>';
+        });
 
-                callback(data);
-            });
-        }
-    },
-    data : {
-        flowsDataGrid: function(data) {
-            var source = new StaticDataSource({
-                    columns: [
-                        {
-                            property: 'selector',
-                            label: "<input type='checkbox' id='"+one.f.flows.id.dashlet.datagrid.selectAllFlows+"'/>",
-                            sortable: false
-                        },
-                        {
-                            property: 'name',
-                            label: 'Flow Name',
-                            sortable: true
-                        },
-                        {
-                            property: 'node',
-                            label: 'Node',
-                            sortable: true
-                        }
-                    ],
-                    data: data.flows,
-                    formatter: function(items) {
-                        $.each(items, function(index, item) {
-                            var $checkbox = document.createElement("input");
-                            $checkbox.setAttribute("type", "checkbox");
-                            $checkbox.setAttribute("name", item.name);
-                            $checkbox.setAttribute("node", item.nodeId);
-                            $checkbox.setAttribute('class','flowEntry')
-                            item.selector = $checkbox.outerHTML;
-                                  item["name"] = '<span data-installInHw=' + item["flow"]["installInHw"] + 
-                                ' data-flowstatus=' + item["flow"]["status"] + 
-                                ' data-nodeId=' + item["nodeId"] + '>' + item["name"] + '</span>';
-                        });
-
-                    },
-                    delay: 0
-                });
-            return source;
-        },
-        dashlet : function(data) {
-            var body = [];
-            $(data).each(function(index, value) {
-                var tr = {};
-                var entry = [];
-
-                
-                entry.push(value['name']);
-                entry.push(value['node']);
-                if (value['flow']['installInHw'] == 'true' && value['flow']['status'] == 'Success')
-                    tr['type'] = ['success'];
-                else if (value['flow']['installInHw'] == 'false' && value['flow']['status'] == 'Success')
-                    tr['type'] = ['warning'];
-                else 
-                    tr['type'] = ['warning'];
-                tr['entry'] = entry;
-                tr['id'] = value['nodeId'];
-
-                body.push(tr);
-            });
-            return body;
-        }
+      },
+      delay: 0
+      });
+      return source;
     },
-    body : {
-        dashlet : function(body, callback) {
-            var attributes = ['table-striped', 'table-bordered', 'table-hover', 'table-condensed', 'table-cursor'];
-            var $table = one.lib.dashlet.table.table(attributes);
-
-            var headers = ['Flow Name', 'Node'];
-                
-            var $thead = one.lib.dashlet.table.header(headers);
-            $table.append($thead);
-
-            var $tbody = one.lib.dashlet.table.body(body);
-            $table.append($tbody);
-            return $table;
-        }
+    dashlet : function(data) {
+      var body = [];
+      $(data).each(function(index, value) {
+        var tr = {};
+        var entry = [];
+
+
+        entry.push(value['name']);
+        entry.push(value['node']);
+        if (value['flow']['installInHw'] == 'true' && value['flow']['status'] == 'Success')
+        tr['type'] = ['success'];
+        else if (value['flow']['installInHw'] == 'false' && value['flow']['status'] == 'Success')
+        tr['type'] = ['warning'];
+        else 
+        tr['type'] = ['warning'];
+      tr['entry'] = entry;
+      tr['id'] = value['nodeId'];
+
+      body.push(tr);
+      });
+      return body;
     }
+  },
+  body : {
+    dashlet : function(body, callback) {
+      var attributes = ['table-striped', 'table-bordered', 'table-hover', 'table-condensed', 'table-cursor'];
+      var $table = one.lib.dashlet.table.table(attributes);
+
+      var headers = ['Flow Name', 'Node'];
+
+      var $thead = one.lib.dashlet.table.header(headers);
+      $table.append($thead);
+
+      var $tbody = one.lib.dashlet.table.body(body);
+      $table.append($tbody);
+      return $table;
+    }
+  }
 }
 
 /** INIT **/
 // populate nav tabs
 $(one.f.menu.left.top).each(function(index, value) {
-    var $nav = $(".nav", "#left-top");
-    one.main.page.dashlet($nav, value);
+  var $nav = $(".nav", "#left-top");
+  one.main.page.dashlet($nav, value);
 });
 
 $(one.f.menu.left.bottom).each(function(index, value) {
-    var $nav = $(".nav", "#left-bottom");
-    one.main.page.dashlet($nav, value);
+  var $nav = $(".nav", "#left-bottom");
+  one.main.page.dashlet($nav, value);
 });
 
 $(one.f.menu.right.bottom).each(function(index, value) {
-    var $nav = $(".nav", "#right-bottom");
-    one.main.page.dashlet($nav, value);
+  var $nav = $(".nav", "#right-bottom");
+  one.main.page.dashlet($nav, value);
 });
 
 one.f.populate = function($dashlet, header) {
-    var $h4 = one.lib.dashlet.header(header);
-    $dashlet.append($h4);
+  var $h4 = one.lib.dashlet.header(header);
+  $dashlet.append($h4);
 };
 
 // bind dashlet nav
 $('.dash .nav a', '#main').click(function() {
-    // de/activation
-    var $li = $(this).parent();
-    var $ul = $li.parent();
-    one.lib.nav.unfocus($ul);
-    $li.addClass('active');
-    // clear respective dashlet
-    var $dashlet = $ul.parent().find('.dashlet');
-    one.lib.dashlet.empty($dashlet);
-    // callback based on menu
-    var id = $(this).attr('id');
-    var menu = one.f.dashlet;
-    switch (id) {
-        case menu.flows.id:
-            one.f.flows.dashlet($dashlet);
-            break;
-        case menu.nodes.id:
-            one.f.nodes.dashlet($dashlet);
-            break;
-        case menu.detail.id:
-            one.f.detail.dashlet($dashlet);
-            break;
-    };
+  // de/activation
+  var $li = $(this).parent();
+  var $ul = $li.parent();
+  one.lib.nav.unfocus($ul);
+  $li.addClass('active');
+  // clear respective dashlet
+  var $dashlet = $ul.parent().find('.dashlet');
+  one.lib.dashlet.empty($dashlet);
+  // callback based on menu
+  var id = $(this).attr('id');
+  var menu = one.f.dashlet;
+  switch (id) {
+    case menu.flows.id:
+      one.f.flows.dashlet($dashlet);
+      break;
+    case menu.nodes.id:
+      one.f.nodes.dashlet($dashlet);
+      break;
+    case menu.detail.id:
+      one.f.detail.dashlet($dashlet);
+      break;
+  };
 });
 
 // activate first tab on each dashlet
 $('.dash .nav').each(function(index, value) {
-    $($(value).find('li')[0]).find('a').click();
+  $($(value).find('li')[0]).find('a').click();
 });