Merge "Bug 1165: ContainerFlow boundary is not always enforced for vlans"
authorGiovanni Meo <gmeo@cisco.com>
Mon, 9 Jun 2014 06:41:31 +0000 (06:41 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Mon, 9 Jun 2014 06:41:31 +0000 (06:41 +0000)
166 files changed:
features/base/src/main/resources/features.xml
opendaylight/commons/opendaylight/pom.xml
opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/AbstractDispatcher.java
opendaylight/config/logback-config-loader/pom.xml [new file with mode: 0644]
opendaylight/config/logback-config-loader/src/main/java/org/opendaylight/controller/logback/config/loader/Activator.java [new file with mode: 0644]
opendaylight/config/logback-config-loader/src/main/java/org/opendaylight/controller/logback/config/loader/impl/LogbackConfigUtil.java [new file with mode: 0644]
opendaylight/config/logback-config-loader/src/main/java/org/opendaylight/controller/logback/config/loader/impl/LogbackConfigurationLoader.java [new file with mode: 0644]
opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/LogbackConfigurationLoaderTest.java [new file with mode: 0644]
opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/TestAppender.java [new file with mode: 0644]
opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Debugger.java [new file with mode: 0644]
opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Errorer.java [new file with mode: 0644]
opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Informer.java [new file with mode: 0644]
opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Tracer.java [new file with mode: 0644]
opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Warner.java [new file with mode: 0644]
opendaylight/config/logback-config-loader/src/test/resources/logback-test.xml [new file with mode: 0755]
opendaylight/config/logback-config-loader/src/test/resources/logback.d/logback-alt.xml [new file with mode: 0755]
opendaylight/config/logback-config-loader/src/test/resources/logback.d/logback-alt2.xml [new file with mode: 0755]
opendaylight/config/logback-config-loader/src/test/resources/logback.d/logback-alt3.xml [new file with mode: 0755]
opendaylight/config/logback-config/src/test/java/org/opendaylight/controller/config/yang/logback/config/LogbackModuleTest.java
opendaylight/config/pom.xml
opendaylight/config/threadpool-config-impl/src/test/java/org/opendaylight/controller/config/threadpool/fixed/FixedThreadPoolConfigBeanTest.java
opendaylight/config/threadpool-config-impl/src/test/java/org/opendaylight/controller/config/threadpool/scheduled/ScheduledThreadPoolConfigBeanTest.java
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/gofactory/AbsFactoryGeneratedObjectFactory.java
opendaylight/distribution/opendaylight-karaf/src/main/resources/etc/jre.properties [new file with mode: 0644]
opendaylight/distribution/opendaylight-karaf/src/main/resources/etc/startup.properties [new file with mode: 0644]
opendaylight/distribution/opendaylight/pom.xml
opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini
opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/99-netconf-connector.xml
opendaylight/md-sal/compatibility/inventory-topology-compatibility/pom.xml
opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/inventory/InventoryReader.java [new file with mode: 0644]
opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/inventory/InventoryReader.xtend [deleted file]
opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/switchmanager/CompatibleSwitchManager.java [new file with mode: 0644]
opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/switchmanager/CompatibleSwitchManager.xtend [deleted file]
opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/switchmanager/ConfigurableSwitchManager.java [new file with mode: 0644]
opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/switchmanager/ConfigurableSwitchManager.xtend [deleted file]
opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyMapping.java [new file with mode: 0644]
opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyMapping.xtend [deleted file]
opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyReader.java [new file with mode: 0644]
opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyReader.xtend [deleted file]
opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/AdSalTopologyMapping.java [new file with mode: 0644]
opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/AdSalTopologyMapping.xtend [deleted file]
opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/CompatibleTopologyManager.java [new file with mode: 0644]
opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/CompatibleTopologyManager.xtend [deleted file]
opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/ConfigurableLinkManager.java [moved from opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/ConfigurableLinkManager.xtend with 57% similarity]
opendaylight/md-sal/compatibility/pom.xml
opendaylight/md-sal/compatibility/sal-compatibility/pom.xml
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryAndReadAdapter.java [new file with mode: 0644]
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryAndReadAdapter.xtend [deleted file]
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/NodeMapping.java
opendaylight/md-sal/model/model-flow-base/src/main/yang/opendaylight-match-types.yang
opendaylight/md-sal/model/model-flow-base/src/main/yang/opendaylight-table-types.yang
opendaylight/md-sal/pom.xml
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/BrokerImplClassLoader.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcServiceMetadata.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeCodeGenerator.xtend
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeGeneratedInvokerPrototype.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolder.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/AbstractNotificationListenerRegistration.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/AggregatedNotificationListenerRegistration.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/ListenerMapGeneration.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/MountPointManagerImpl.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationBrokerImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationBrokerImpl.xtend [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationListenerRegistration.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotifyTask.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-impl/pom.xml
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/AbstractDOMStoreTransaction.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
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/data/InMemoryDataTreeModification.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/DataReaderRouter.java
opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java
opendaylight/md-sal/sal-netconf-connector/pom.xml
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/MessageTransformer.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDevice.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceCommunicator.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceHandler.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/SchemaContextProviderFactory.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/SchemaSourceProviderFactory.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/package-info.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/InventoryUtils.java [deleted file]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceListener.java [deleted file]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTwoPhaseCommitTransaction.java [deleted file]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfInventoryUtils.java [deleted file]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfRemoteSchemaSourceProvider.java [deleted file]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/UncancellableFuture.java [moved from opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/UncancellableFuture.java with 67% similarity]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/package-info.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceCommitHandler.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataReader.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDatastoreAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceRpc.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalFacade.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalProvider.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceTwoPhaseCommitTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfDeviceSchemaProviderFactory.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaSourceProvider.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java [moved from opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.java with 50% similarity]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/FailedRpcResult.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/MessageCounter.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/RemoteDeviceId.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/package-info.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/yang/odl-sal-netconf-connector-cfg.yang
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/test-module.yang [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonReader.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlReader.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonBasicDataTypesTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/xml/test/CnSnToXmlTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/json/to/cnsn/test/JsonToCnSnTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/CodecsExceptionsCatchingTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPostOperationTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPutOperationTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/xml/to/cnsn/test/XmlToCnSnTest.java
opendaylight/md-sal/sal-rest-connector/src/test/resources/cnsn-to-json/simple-data-types/xml/bad-data.xml [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/test/resources/cnsn-to-json/simple-data-types/xml/data.xml
opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/NodeStatisticsHandler.java
opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsRequestScheduler.java
opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/ReplaceEditConfigStrategyTest.java
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/SshClientChannelInitializer.java
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/TcpClientChannelInitializer.java
opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/test/TestingNetconfClient.java
opendaylight/netconf/netconf-impl/pom.xml
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerDispatcher.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java
opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java
opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java
opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/AbstractChannelInitializer.java
opendaylight/netconf/netconf-ssh/pom.xml
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java
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/AuthProviderInterface.java [deleted file]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/PEMGenerator.java
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/osgi/NetconfSSHActivator.java
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/IOThread.java [deleted file]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/SocketThread.java [deleted file]
opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/KeyGeneratorTest.java [deleted file]
opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/SSHServerTest.java [deleted file]
opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoClient.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoClientHandler.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoServer.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoServerHandler.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/ProxyServer.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/ProxyServerHandler.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/SSHTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/ssh/authentication/SSHServerTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/test/resources/logback-test.xml [new file with mode: 0644]
opendaylight/netconf/netconf-tcp/pom.xml [new file with mode: 0644]
opendaylight/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/netconf/tcp/netty/ProxyServer.java [new file with mode: 0644]
opendaylight/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/netconf/tcp/netty/ProxyServerHandler.java [new file with mode: 0644]
opendaylight/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/netconf/tcp/osgi/NetconfTCPActivator.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/pom.xml
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java
opendaylight/netconf/pom.xml
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/packet/TCP.java
opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/packet/TCPTest.java

index cc112052cc4f9acfab09a030b30caaebd7e878ef..23051f5a9ab88ba7a7f82dc75b9ad2abfbf0355c 100644 (file)
@@ -1,10 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<features name="base-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
-   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-   xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0">
+<features name="base-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0">
 
-   <feature name="base-all" description="OpenDaylight Controller"
-      version="${project.version}">
+   <feature name="base-all" description="OpenDaylight Controller" version="${project.version}">
       <feature>http</feature>
       <feature>transaction</feature>
       <feature>base-felix-dm</feature>
    <feature name="base-dummy-console" description="Temporary Dummy Console" version="1.1.0-SNAPSHOT">
       <bundle>mvn:org.opendaylight.controller/dummy-console/1.1.0-SNAPSHOT</bundle>
    </feature>
-   <feature name="base-felix-dm" description="Felix Dependency Manager"
-      version="${felix.dependencymanager.version}">
+   <feature name="base-felix-dm" description="Felix Dependency Manager" version="${felix.dependencymanager.version}">
       <bundle start-level="35">mvn:org.osgi/org.osgi.compendium/${osgi.compendium.version}</bundle>
       <bundle start-level="35">mvn:org.apache.felix/org.apache.felix.dependencymanager/${felix.dependencymanager.version}</bundle>
       <bundle start-level="35">mvn:org.apache.felix/org.apache.felix.dependencymanager.shell/${felix.dependencymanager.shell.version}</bundle>
    </feature>
-   <feature name="base-aries-spi-fly" description="Aries SPI Fly"
-      version="${spifly.version}">
+   <feature name="base-aries-spi-fly" description="Aries SPI Fly" version="${spifly.version}">
       <bundle start-level="35">mvn:org.apache.aries/org.apache.aries.util/1.1.0</bundle>
       <bundle start-level="35">mvn:org.apache.aries.spifly/org.apache.aries.spifly.dynamic.bundle/${spifly.version}</bundle>
       <bundle start-level="35">mvn:org.ow2.asm/asm-all/4.0</bundle>
    </feature>
-     <feature name='base-netty' version='${netty.version}'>
-        <bundle>wrap:mvn:io.netty/netty-buffer/${netty.version}</bundle>
-        <bundle>wrap:mvn:io.netty/netty-codec/${netty.version}</bundle>
-        <bundle>wrap:mvn:io.netty/netty-transport/${netty.version}</bundle>
-        <bundle>wrap:mvn:io.netty/netty-common/${netty.version}</bundle>
-        <bundle>wrap:mvn:io.netty/netty-handler/${netty.version}</bundle>
-        <bundle>wrap:mvn:io.netty/netty-codec-http/${netty.version}</bundle>
-        <bundle>mvn:org.opendaylight.controller.thirdparty/ganymed/1.1-SNAPSHOT</bundle>
-    </feature>
-    <feature name="base-jersey" description="Jersey" version="${jersey.version}">
-        <feature>base-gemini-web</feature>
-        <bundle>mvn:org.opendaylight.controller.thirdparty/com.sun.jersey.jersey-servlet/${jersey.version}</bundle>
-        <bundle>mvn:com.sun.jersey/jersey-server/${jersey.version}</bundle>
-        <bundle>mvn:com.sun.jersey/jersey-core/${jersey.version}</bundle>
-        <bundle>mvn:com.sun.jersey/jersey-client/${jersey.version}</bundle>
-        <bundle>mvn:com.sun.jersey/jersey-servlet/${jersey.version}</bundle>
+   <feature name='base-netty' version='${netty.version}'>
+      <bundle>wrap:mvn:io.netty/netty-buffer/${netty.version}</bundle>
+      <bundle>wrap:mvn:io.netty/netty-codec/${netty.version}</bundle>
+      <bundle>wrap:mvn:io.netty/netty-transport/${netty.version}</bundle>
+      <bundle>wrap:mvn:io.netty/netty-common/${netty.version}</bundle>
+      <bundle>wrap:mvn:io.netty/netty-handler/${netty.version}</bundle>
+      <bundle>wrap:mvn:io.netty/netty-codec-http/${netty.version}</bundle>
+      <bundle>mvn:org.opendaylight.controller.thirdparty/ganymed/1.1-SNAPSHOT</bundle>
+   </feature>
+   <feature name="base-jersey" description="Jersey" version="${jersey.version}">
+      <feature>base-gemini-web</feature>
+      <bundle>mvn:org.opendaylight.controller.thirdparty/com.sun.jersey.jersey-servlet/${jersey.version}</bundle>
+      <bundle>mvn:com.sun.jersey/jersey-server/${jersey.version}</bundle>
+      <bundle>mvn:com.sun.jersey/jersey-core/${jersey.version}</bundle>
+      <bundle>mvn:com.sun.jersey/jersey-client/${jersey.version}</bundle>
+      <bundle>mvn:com.sun.jersey/jersey-servlet/${jersey.version}</bundle>
+      <bundle start="true" start-level="35">mvn:javax.ws.rs/javax.ws.rs-api/2.0</bundle>
    </feature>
+   <feature name="base-jersey2-osgi" description="OSGi friendly Jersey" version="${jersey2.publisher.version}">
+      <feature>http</feature>
+      <bundle>mvn:com.eclipsesource.jaxrs/jersey-all/${jersey2.version}</bundle>
+      <bundle>mvn:com.eclipsesource.jaxrs/publisher/${jersey2.publisher.version}</bundle>
+      <bundle start="true" start-level="35">mvn:javax.ws.rs/javax.ws.rs-api/${jsr311.v2.api.version}</bundle>
+      <bundle>mvn:javax.annotation/javax.annotation-api/${javax.annotation.version}</bundle>
+    </feature>
    <feature name="base-jackson" description="Jackson JAX-RS" version="${jackson.version}">
       <bundle start="true" start-level="35">mvn:com.fasterxml.jackson.core/jackson-annotations/${jackson.version}</bundle>
       <bundle start="true" start-level="35">mvn:com.fasterxml.jackson.core/jackson-core/${jackson.version}</bundle>
       <bundle start="true" start-level="35">mvn:com.fasterxml.jackson.core/jackson-databind/${jackson.version}</bundle>
       <bundle start="true" start-level="35">mvn:org.codehaus.jettison/jettison/${jettison.version}</bundle>
-      <bundle start="true" start-level="35">mvn:javax.ws.rs/jsr311-api/${jsr311.api.version}</bundle>
       <bundle start="true" start-level="35">mvn:com.fasterxml.jackson.module/jackson-module-jaxb-annotations/${jackson.version}</bundle>
       <bundle start="true" start-level="35">mvn:com.fasterxml.jackson.jaxrs/jackson-jaxrs-base/${jackson.version}</bundle>
       <bundle start="true" start-level="35">mvn:com.fasterxml.jackson.jaxrs/jackson-jaxrs-json-provider/${jackson.version}</bundle>
@@ -66,8 +68,7 @@
       <bundle start-level="35">mvn:org.slf4j/slf4j-simple/1.7.2</bundle>
       <bundle start="true" start-level="35">mvn:org.slf4j/slf4j-api/1.7.2</bundle>
    </feature>
-   <feature name="base-apache-commons" description="Apache Commons Libraries"
-      version="${project.version}">
+   <feature name="base-apache-commons" description="Apache Commons Libraries" version="${project.version}">
       <bundle start="true" start-level="35">mvn:com.google.guava/guava/${guava.version}</bundle>
       <bundle start="true" start-level="35">mvn:org.javassist/javassist/${javassist.version}</bundle>
       <bundle start="true" start-level="35">mvn:commons-io/commons-io/${commons.io.version}</bundle>
index c166f668ccd48e9419e79536c7b39523b7f04238..077b452f0ac96623c4b9f445d91445f9cae57cd1 100644 (file)
     <java.version.source>1.7</java.version.source>
     <java.version.target>1.7</java.version.target>
     <javassist.version>3.17.1-GA</javassist.version>
+    <javax.annotation.version>1.2</javax.annotation.version>
     <!-- Third party version -->
     <jersey-servlet.version>1.17</jersey-servlet.version>
     <jersey.version>1.17</jersey.version>
+    <jersey2.publisher.version>4.0</jersey2.publisher.version>
+    <jersey2.version>2.8</jersey2.version>
     <jettison.version>1.3.3</jettison.version>
     <jmxGeneratorPath>src/main/yang-gen-config</jmxGeneratorPath>
     <jolokia.version>1.1.4</jolokia.version>
     <jsr305.api.version>2.0.1</jsr305.api.version>
     <jsr311.api.version>1.1.1</jsr311.api.version>
+    <jsr311.v2.api.version>2.0</jsr311.v2.api.version>
     <junit.version>4.8.1</junit.version>
     <karaf.version>3.0.1</karaf.version>
     <logback.version>1.0.9</logback.version>
     <mdsal.version>1.1-SNAPSHOT</mdsal.version>
     <mockito.version>1.9.5</mockito.version>
     <netconf.version>0.2.5-SNAPSHOT</netconf.version>
-    <netty.version>4.0.17.Final</netty.version>
+    <netty.version>4.0.19.Final</netty.version>
     <networkconfig.bridgedomain.northbound.version>0.0.3-SNAPSHOT</networkconfig.bridgedomain.northbound.version>
     <networkconfig.neutron.implementation.version>0.4.2-SNAPSHOT</networkconfig.neutron.implementation.version>
     <networkconfig.neutron.northbound.version>0.4.2-SNAPSHOT</networkconfig.neutron.northbound.version>
         <version>${netconf.version}</version>
         <type>test-jar</type>
       </dependency>
+      <dependency>
+        <groupId>org.opendaylight.controller</groupId>
+        <artifactId>netconf-tcp</artifactId>
+        <version>${netconf.version}</version>
+      </dependency>
       <dependency>
         <groupId>org.opendaylight.controller</groupId>
         <artifactId>netconf-util</artifactId>
index 916ef9a88befa87ac8b5c17902ec6d970f23807d..fef2c7196948c007705b4444a5c9445065618648 100644 (file)
@@ -7,12 +7,16 @@
  */
 package org.opendaylight.protocol.framework;
 
+import com.google.common.base.Preconditions;
 import io.netty.bootstrap.Bootstrap;
 import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelInitializer;
 import io.netty.channel.ChannelOption;
 import io.netty.channel.EventLoopGroup;
+import io.netty.channel.ServerChannel;
+import io.netty.channel.local.LocalServerChannel;
 import io.netty.channel.socket.SocketChannel;
 import io.netty.channel.socket.nio.NioServerSocketChannel;
 import io.netty.channel.socket.nio.NioSocketChannel;
@@ -21,22 +25,20 @@ import io.netty.util.concurrent.EventExecutor;
 import io.netty.util.concurrent.Future;
 import io.netty.util.concurrent.GlobalEventExecutor;
 import io.netty.util.concurrent.Promise;
-
 import java.io.Closeable;
 import java.net.InetSocketAddress;
-
+import java.net.SocketAddress;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Preconditions;
-
 /**
  * Dispatcher class for creating servers and clients. The idea is to first create servers and clients and the run the
  * start method that will handle sockets in different thread.
  */
 public abstract class AbstractDispatcher<S extends ProtocolSession<?>, L extends SessionListener<?, ?, ?>> implements Closeable {
 
-    protected interface PipelineInitializer<S extends ProtocolSession<?>> {
+
+    protected interface ChannelPipelineInitializer<CH extends Channel, S extends ProtocolSession<?>> {
         /**
          * Initializes channel by specifying the handlers in its pipeline. Handlers are protocol specific, therefore this
          * method needs to be implemented in protocol specific Dispatchers.
@@ -44,7 +46,11 @@ public abstract class AbstractDispatcher<S extends ProtocolSession<?>, L extends
          * @param channel whose pipeline should be defined, also to be passed to {@link SessionNegotiatorFactory}
          * @param promise to be passed to {@link SessionNegotiatorFactory}
          */
-        void initializeChannel(SocketChannel channel, Promise<S> promise);
+        void initializeChannel(CH channel, Promise<S> promise);
+    }
+
+    protected interface PipelineInitializer<S extends ProtocolSession<?>> extends ChannelPipelineInitializer<SocketChannel, S> {
+
     }
 
 
@@ -76,25 +82,43 @@ public abstract class AbstractDispatcher<S extends ProtocolSession<?>, L extends
      * @return ChannelFuture representing the binding process
      */
     protected ChannelFuture createServer(final InetSocketAddress address, final PipelineInitializer<S> initializer) {
+        return createServer(address, NioServerSocketChannel.class, initializer);
+    }
+
+    /**
+     * Creates server. Each server needs factories to pass their instances to client sessions.
+     *
+     * @param address address to which the server should be bound
+     * @param channelClass The {@link Class} which is used to create {@link Channel} instances from.
+     * @param initializer instance of PipelineInitializer used to initialize the channel pipeline
+     *
+     * @return ChannelFuture representing the binding process
+     */
+    protected <CH extends Channel> ChannelFuture createServer(SocketAddress address, Class<? extends ServerChannel> channelClass,
+                                                              final ChannelPipelineInitializer<CH, S> initializer) {
         final ServerBootstrap b = new ServerBootstrap();
-        b.childHandler(new ChannelInitializer<SocketChannel>() {
+        b.childHandler(new ChannelInitializer<CH>() {
 
             @Override
-            protected void initChannel(final SocketChannel ch) {
+            protected void initChannel(final CH ch) {
                 initializer.initializeChannel(ch, new DefaultPromise<S>(executor));
             }
         });
 
         b.option(ChannelOption.SO_BACKLOG, 128);
-        b.childOption(ChannelOption.SO_KEEPALIVE, true);
+        if (LocalServerChannel.class.equals(channelClass) == false) {
+            // makes no sense for LocalServer and produces warning
+            b.childOption(ChannelOption.SO_KEEPALIVE, true);
+        }
         customizeBootstrap(b);
 
         if (b.group() == null) {
             b.group(bossGroup, workerGroup);
         }
         try {
-            b.channel(NioServerSocketChannel.class);
+            b.channel(channelClass);
         } catch (IllegalStateException e) {
+            // FIXME: if this is ok, document why
             LOG.trace("Not overriding channelFactory on bootstrap {}", b, e);
         }
 
diff --git a/opendaylight/config/logback-config-loader/pom.xml b/opendaylight/config/logback-config-loader/pom.xml
new file mode 100644 (file)
index 0000000..03ff65f
--- /dev/null
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<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>config-plugin-parent</artifactId>
+    <version>0.2.5-SNAPSHOT</version>
+    <relativePath>../config-plugin-parent</relativePath>
+  </parent>
+  <artifactId>logback-config-loader</artifactId>
+  <packaging>bundle</packaging>
+  <name>${project.artifactId}</name>
+  <prerequisites>
+    <maven>3.0.4</maven>
+  </prerequisites>
+
+  <dependencies>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+
+    <!-- test dependencies -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <configuration>
+          <instructions>
+            <Bundle-Activator>org.opendaylight.controller.logback.config.loader.Activator</Bundle-Activator>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/opendaylight/config/logback-config-loader/src/main/java/org/opendaylight/controller/logback/config/loader/Activator.java b/opendaylight/config/logback-config-loader/src/main/java/org/opendaylight/controller/logback/config/loader/Activator.java
new file mode 100644 (file)
index 0000000..99866d5
--- /dev/null
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.logback.config.loader;
+
+import java.io.File;
+import java.util.List;
+
+import org.opendaylight.controller.logback.config.loader.impl.LogbackConfigUtil;
+import org.opendaylight.controller.logback.config.loader.impl.LogbackConfigurationLoader;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * default activator for loading multiple logback configuration files
+ */
+public class Activator implements BundleActivator {
+
+    /**
+     * expected environment variable name, containing the root folder containing
+     * logback configurations
+     */
+    private static final String LOGBACK_CONFIG_D = "logback.config.d";
+    private static Logger LOG = LoggerFactory.getLogger(Activator.class);
+
+    @Override
+    public void start(BundleContext context) {
+        LOG.info("Starting logback configuration loader");
+        String logbackConfigRoot = System.getProperty(LOGBACK_CONFIG_D);
+        LOG.debug("configRoot: {}", logbackConfigRoot);
+        if (logbackConfigRoot != null) {
+            File logbackConfigRootFile = new File(logbackConfigRoot);
+            List<File> sortedConfigFiles = LogbackConfigUtil.harvestSortedConfigFiles(logbackConfigRootFile);
+            LogbackConfigurationLoader.load(true, sortedConfigFiles.toArray());
+        }
+    }
+
+    @Override
+    public void stop(BundleContext context) {
+        LOG.info("Stopping logback configuration loader");
+        // TODO: need reset/reload default config?
+    }
+
+}
diff --git a/opendaylight/config/logback-config-loader/src/main/java/org/opendaylight/controller/logback/config/loader/impl/LogbackConfigUtil.java b/opendaylight/config/logback-config-loader/src/main/java/org/opendaylight/controller/logback/config/loader/impl/LogbackConfigUtil.java
new file mode 100644 (file)
index 0000000..ddf14d7
--- /dev/null
@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.logback.config.loader.impl;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * logback config utils
+ */
+public final class LogbackConfigUtil {
+
+    /** logback config file pattern (*.xml) */
+    protected static final String LOGBACK_CONFIG_FILE_REGEX_SEED = ".+\\.xml";
+    private static final Logger LOG = LoggerFactory
+            .getLogger(LogbackConfigUtil.class);
+
+    /**
+     *  forbidden ctor
+     */
+    private LogbackConfigUtil() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @param logConfigRoot folder containing configuration files
+     * @return sorted list of found files
+     */
+    public static List<File> harvestSortedConfigFiles(File logConfigRoot) {
+        final Pattern xmlFilePattern = Pattern.compile(LOGBACK_CONFIG_FILE_REGEX_SEED);
+        File[] configs = logConfigRoot.listFiles(new FileFilter() {
+            @Override
+            public boolean accept(File pathname) {
+                return pathname.isFile()
+                        && xmlFilePattern.matcher(pathname.getName()).find();
+            }
+        });
+
+        List<File> sortedConfigFiles = new ArrayList<File>(configs.length);
+        for (File cfgItem : configs) {
+            LOG.trace("config: {}", cfgItem.toURI());
+            sortedConfigFiles.add(cfgItem);
+        }
+        Collections.sort(sortedConfigFiles);
+
+        return sortedConfigFiles;
+    }
+
+}
diff --git a/opendaylight/config/logback-config-loader/src/main/java/org/opendaylight/controller/logback/config/loader/impl/LogbackConfigurationLoader.java b/opendaylight/config/logback-config-loader/src/main/java/org/opendaylight/controller/logback/config/loader/impl/LogbackConfigurationLoader.java
new file mode 100644 (file)
index 0000000..2aa6b1a
--- /dev/null
@@ -0,0 +1,109 @@
+/**
+ * Copyright (c) 201 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.logback.config.loader.impl;
+
+import java.io.File;
+import java.net.URL;
+
+import org.slf4j.ILoggerFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.joran.JoranConfigurator;
+import ch.qos.logback.core.joran.spi.JoranException;
+import ch.qos.logback.core.util.StatusPrinter;
+
+/**
+ * Logback configuration loader.
+ * Strategy:
+ * <ol>
+ * <li>reset actual configuration (probably default configuration)</li>
+ * <li>load all given logback config xml files in given order</li>
+ * </ol>
+ */
+public final class LogbackConfigurationLoader {
+
+    private static final Logger LOG = LoggerFactory
+            .getLogger(LogbackConfigurationLoader.class);
+
+    /**
+     *  forbidden ctor
+     */
+    private LogbackConfigurationLoader() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * load given logback configurations in given order, reset existing configuration before applying first one
+     * @param purgeBefore require reset before loading first config
+     * @param args
+     */
+    public static void load(boolean purgeBefore, Object...args) {
+        try {
+            if (purgeBefore) {
+                resetExistingConfiguration();
+            }
+            for (Object logbackConfig : args) {
+                load(logbackConfig);
+            }
+        } catch (IllegalStateException e) {
+            LOG.warn("loading of multiple logback configurations failed", e);
+        }
+    }
+
+    /**
+     * purge existing logback configuration
+     */
+    public static void resetExistingConfiguration() {
+        LOG.trace("resetting existing logback configuration");
+        LoggerContext context = getLoggerContext();
+        JoranConfigurator configurator = new JoranConfigurator();
+        configurator.setContext(context);
+        context.reset();
+    }
+
+    /**
+     * @return logback context
+     */
+    private static LoggerContext getLoggerContext() {
+        ILoggerFactory context = LoggerFactory.getILoggerFactory();
+        if (context != null && context instanceof LoggerContext) {
+            // now SLF4J is bound to logback in the current environment
+            return (LoggerContext) context;
+        }
+        throw new IllegalStateException("current logger factory is not supported: " + context);
+    }
+
+    /**
+     * @param logbackConfig
+     * @param reset true if previous configuration needs to get purged
+     */
+    public static void load(Object logbackConfig) {
+        LOG.trace("BEFORE logback reconfig");
+        try {
+            LoggerContext context = getLoggerContext();
+            JoranConfigurator configurator = new JoranConfigurator();
+            configurator.setContext(context);
+            if (logbackConfig instanceof String) {
+                configurator.doConfigure((String) logbackConfig);
+            } else if (logbackConfig instanceof URL) {
+                configurator.doConfigure((URL) logbackConfig);
+            } else if (logbackConfig instanceof File) {
+                configurator.doConfigure((File) logbackConfig);
+            }
+
+            LOG.trace("applied {}", logbackConfig);
+            StatusPrinter.printInCaseOfErrorsOrWarnings(context);
+        } catch (IllegalStateException | JoranException je) {
+            LOG.warn("Logback configuration loading failed: {}", logbackConfig);
+        }
+        LOG.trace("AFTER logback reconfig");
+    }
+
+}
diff --git a/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/LogbackConfigurationLoaderTest.java b/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/LogbackConfigurationLoaderTest.java
new file mode 100644 (file)
index 0000000..2e9bf1d
--- /dev/null
@@ -0,0 +1,87 @@
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.logback.config.loader.test;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.opendaylight.controller.logback.config.loader.impl.LogbackConfigUtil;
+import org.opendaylight.controller.logback.config.loader.impl.LogbackConfigurationLoader;
+import org.opendaylight.controller.logback.config.loader.test.logwork.Debugger;
+import org.opendaylight.controller.logback.config.loader.test.logwork.Errorer;
+import org.opendaylight.controller.logback.config.loader.test.logwork.Informer;
+import org.opendaylight.controller.logback.config.loader.test.logwork.Tracer;
+import org.opendaylight.controller.logback.config.loader.test.logwork.Warner;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * test of logging config loader - {@link LogbackConfigurationLoader}
+ */
+@RunWith(JUnit4.class)
+public class LogbackConfigurationLoaderTest {
+
+    /** logback config root */
+    private static final String LOGBACK_D = "/logback.d";
+    private static Logger LOG = LoggerFactory
+            .getLogger(LogbackConfigurationLoaderTest.class);
+
+    /**
+     * Test of method {@link LogbackConfigurationLoader#load(boolean, Object[])}
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testLoad() throws Exception {
+        File logConfigRoot = new File(LogbackConfigurationLoaderTest.class
+                .getResource(LOGBACK_D).getFile());
+        List<File> sortedConfigFiles = LogbackConfigUtil.harvestSortedConfigFiles(logConfigRoot);
+        LogbackConfigurationLoader.load(true, sortedConfigFiles.toArray());
+
+        LOG.info("LOGBACK ready -> about to use it");
+
+        Tracer.doSomeAction();
+        Debugger.doSomeAction();
+        Informer.doSomeAction();
+        Warner.doSomeAction();
+        Errorer.doSomeAction();
+
+        // check logs
+        String[] expectedLogs = new String[] {
+                "LoggingEvent -> [INFO] org.opendaylight.controller.logback.config.loader.test.LogbackConfigurationLoaderTest: LOGBACK ready -> about to use it",
+                "LoggingEvent -> [TRACE] org.opendaylight.controller.logback.config.loader.test.logwork.Tracer: tracing",
+                "LoggingEvent -> [DEBUG] org.opendaylight.controller.logback.config.loader.test.logwork.Tracer: debugging",
+                "LoggingEvent -> [INFO] org.opendaylight.controller.logback.config.loader.test.logwork.Tracer: infoing",
+                "LoggingEvent -> [WARN] org.opendaylight.controller.logback.config.loader.test.logwork.Tracer: warning",
+                "LoggingEvent -> [ERROR] org.opendaylight.controller.logback.config.loader.test.logwork.Tracer: erroring",
+                "LoggingEvent -> [DEBUG] org.opendaylight.controller.logback.config.loader.test.logwork.Debugger: debugging",
+                "LoggingEvent -> [INFO] org.opendaylight.controller.logback.config.loader.test.logwork.Debugger: infoing",
+                "LoggingEvent -> [WARN] org.opendaylight.controller.logback.config.loader.test.logwork.Debugger: warning",
+                "LoggingEvent -> [ERROR] org.opendaylight.controller.logback.config.loader.test.logwork.Debugger: erroring",
+                "LoggingEvent -> [INFO] org.opendaylight.controller.logback.config.loader.test.logwork.Informer: infoing",
+                "LoggingEvent -> [WARN] org.opendaylight.controller.logback.config.loader.test.logwork.Informer: warning",
+                "LoggingEvent -> [ERROR] org.opendaylight.controller.logback.config.loader.test.logwork.Informer: erroring",
+                "LoggingEvent -> [WARN] org.opendaylight.controller.logback.config.loader.test.logwork.Warner: warning",
+                "LoggingEvent -> [ERROR] org.opendaylight.controller.logback.config.loader.test.logwork.Warner: erroring",
+                "LoggingEvent -> [ERROR] org.opendaylight.controller.logback.config.loader.test.logwork.Errorer: erroring"
+
+        };
+
+        List<String> logSnapshot = new ArrayList<>(TestAppender.getLogRecord());
+        for (String logLine : logSnapshot) {
+            LOG.info("\"{}\",", logLine);
+        }
+
+        Assert.assertArrayEquals(expectedLogs, logSnapshot.toArray());
+    }
+}
diff --git a/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/TestAppender.java b/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/TestAppender.java
new file mode 100644 (file)
index 0000000..b273d27
--- /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.logback.config.loader.test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ch.qos.logback.classic.spi.LoggingEvent;
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.Context;
+import ch.qos.logback.core.LogbackException;
+import ch.qos.logback.core.filter.Filter;
+import ch.qos.logback.core.spi.FilterReply;
+import ch.qos.logback.core.status.Status;
+
+/**
+ * dummy appender for collecting log messages
+ *
+ * @param <E>
+ */
+public class TestAppender<E> implements Appender<E> {
+
+    private boolean started;
+    private Context context;
+    private String name;
+
+    private static List<String> logRecord = new ArrayList<>();
+
+    @Override
+    public void start() {
+        started = true;
+    }
+
+    @Override
+    public void stop() {
+        started = false;
+    }
+
+    @Override
+    public boolean isStarted() {
+        return started;
+    }
+
+    @Override
+    public void setContext(Context context) {
+        this.context = context;
+    }
+
+    @Override
+    public Context getContext() {
+        return context;
+    }
+
+    @Override
+    public void addStatus(Status status) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void addInfo(String msg) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void addInfo(String msg, Throwable ex) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void addWarn(String msg) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void addWarn(String msg, Throwable ex) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void addError(String msg) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void addError(String msg, Throwable ex) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void addFilter(Filter<E> newFilter) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void clearAllFilters() {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public List<Filter<E>> getCopyOfAttachedFiltersList() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public FilterReply getFilterChainDecision(E event) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public void doAppend(E event) throws LogbackException {
+        if (event instanceof LoggingEvent) {
+            LoggingEvent lEvent = (LoggingEvent) event;
+            logRecord.add(String.format("%s -> [%s] %s: %s", event.getClass()
+                    .getSimpleName(), lEvent.getLevel(),
+                    lEvent.getLoggerName(), lEvent.getMessage()));
+        } else {
+            logRecord.add(event.getClass() + " -> " + event.toString());
+        }
+    }
+
+    @Override
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * @return the logRecord
+     */
+    public static List<String> getLogRecord() {
+        return logRecord;
+    }
+
+}
diff --git a/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Debugger.java b/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Debugger.java
new file mode 100644 (file)
index 0000000..a8052f7
--- /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.logback.config.loader.test.logwork;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * dummy logging guy
+ */
+public class Debugger {
+
+    private static Logger LOG = LoggerFactory.getLogger(Debugger.class);
+
+    /**
+     * all logging
+     */
+    public static void doSomeAction() {
+        LOG.trace("tracing");
+        LOG.debug("debugging");
+        LOG.info("infoing");
+        LOG.warn("warning");
+        LOG.error("erroring");
+    }
+
+}
diff --git a/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Errorer.java b/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Errorer.java
new file mode 100644 (file)
index 0000000..0bcd830
--- /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.logback.config.loader.test.logwork;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * dummy logging guy
+ */
+public class Errorer {
+
+    private static Logger LOG = LoggerFactory.getLogger(Errorer.class);
+
+    /**
+     * all logging
+     */
+    public static void doSomeAction() {
+        LOG.trace("tracing");
+        LOG.debug("debugging");
+        LOG.info("infoing");
+        LOG.warn("warning");
+        LOG.error("erroring");
+    }
+
+}
diff --git a/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Informer.java b/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Informer.java
new file mode 100644 (file)
index 0000000..44f0931
--- /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.logback.config.loader.test.logwork;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * dummy logging guy
+ */
+public class Informer {
+
+    private static Logger LOG = LoggerFactory.getLogger(Informer.class);
+
+    /**
+     * all logging
+     */
+    public static void doSomeAction() {
+        LOG.trace("tracing");
+        LOG.debug("debugging");
+        LOG.info("infoing");
+        LOG.warn("warning");
+        LOG.error("erroring");
+    }
+
+}
diff --git a/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Tracer.java b/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Tracer.java
new file mode 100644 (file)
index 0000000..70df607
--- /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.logback.config.loader.test.logwork;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * dummy logging guy
+ */
+public class Tracer {
+
+    private static Logger LOG = LoggerFactory.getLogger(Tracer.class);
+
+    /**
+     * all logging
+     */
+    public static void doSomeAction() {
+        LOG.trace("tracing");
+        LOG.debug("debugging");
+        LOG.info("infoing");
+        LOG.warn("warning");
+        LOG.error("erroring");
+    }
+
+}
diff --git a/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Warner.java b/opendaylight/config/logback-config-loader/src/test/java/org/opendaylight/controller/logback/config/loader/test/logwork/Warner.java
new file mode 100644 (file)
index 0000000..8093180
--- /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.logback.config.loader.test.logwork;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * dummy logging guy
+ */
+public class Warner {
+
+    private static Logger LOG = LoggerFactory.getLogger(Warner.class);
+
+    /**
+     * all logging
+     */
+    public static void doSomeAction() {
+        LOG.trace("tracing");
+        LOG.debug("debugging");
+        LOG.info("infoing");
+        LOG.warn("warning");
+        LOG.error("erroring");
+    }
+
+}
diff --git a/opendaylight/config/logback-config-loader/src/test/resources/logback-test.xml b/opendaylight/config/logback-config-loader/src/test/resources/logback-test.xml
new file mode 100755 (executable)
index 0000000..7fb760a
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<configuration debug="true">\r
+\r
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">\r
+    <encoder>\r
+      <pattern>%date{"yyyy-MM-dd HH:mm:ss.SSS z"} [%thread] %-5level %logger{36} - %msg%n</pattern>\r
+    </encoder>\r
+  </appender>\r
+\r
+  <root level="INFO">\r
+    <appender-ref ref="STDOUT" />\r
+  </root>\r
+\r
+  <!--  Base log level  -->\r
+  <logger name="org.opendaylight.controller.logback.config.loader" level="DEBUG"/>\r
+\r
+</configuration>\r
diff --git a/opendaylight/config/logback-config-loader/src/test/resources/logback.d/logback-alt.xml b/opendaylight/config/logback-config-loader/src/test/resources/logback.d/logback-alt.xml
new file mode 100755 (executable)
index 0000000..ca489d5
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<configuration debug="false">\r
+\r
+  <appender name="TEST" class="org.opendaylight.controller.logback.config.loader.test.TestAppender"/>\r
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">\r
+    <encoder>\r
+      <pattern>%date{"yyyy-MM-dd HH:mm:ss.SSS z"} [%thread] %-5level %logger{36} - %msg%n</pattern>\r
+    </encoder>\r
+  </appender>\r
+\r
+  <root level="INFO">\r
+    <appender-ref ref="TEST" />\r
+    <appender-ref ref="STDOUT" />\r
+  </root>\r
+\r
+  <!--  Base log level  -->\r
+  <logger name="org.opendaylight.controller.logback.config.loader" level="INFO"/>\r
+\r
+</configuration>\r
diff --git a/opendaylight/config/logback-config-loader/src/test/resources/logback.d/logback-alt2.xml b/opendaylight/config/logback-config-loader/src/test/resources/logback.d/logback-alt2.xml
new file mode 100755 (executable)
index 0000000..89f82c5
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<configuration debug="false">\r
+\r
+  <!--  Base log level  -->\r
+  <logger name="org.opendaylight.controller.logback.config.loader" level="DEBUG"/>\r
+  <logger name="org.opendaylight.controller.logback.config.loader.test.logwork.Tracer" level="TRACE"/>\r
+<!--   <logger name="org.opendaylight.controller.logback.config.loader.test.logwork.Debugger" level="DEBUG"/> -->\r
+  <logger name="org.opendaylight.controller.logback.config.loader.test.logwork.Informer" level="DEBUG"/>\r
+  <logger name="org.opendaylight.controller.logback.config.loader.test.logwork.Warner" level="ERROR"/>\r
+  <logger name="org.opendaylight.controller.logback.config.loader.test.logwork.Errorer" level="ERROR"/>\r
+\r
+</configuration>\r
diff --git a/opendaylight/config/logback-config-loader/src/test/resources/logback.d/logback-alt3.xml b/opendaylight/config/logback-config-loader/src/test/resources/logback.d/logback-alt3.xml
new file mode 100755 (executable)
index 0000000..a37b6f7
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<configuration debug="false">\r
+  <root level="INFO">\r
+    <appender-ref ref="TEST" />\r
+  </root>\r
+\r
+  <logger name="org.opendaylight.controller.logback.config.loader.test.logwork.Informer" level="INFO"/>\r
+  <logger name="org.opendaylight.controller.logback.config.loader.test.logwork.Warner" level="WARN"/>\r
+\r
+  <logger name="org.opendaylight.controller.logback.config.loader.test.LogbackConfigurationLoaderTest" level="TRACE"/>\r
+</configuration>\r
index d9c9dada6202be0a4eac69d138329cb5f69c745f..75323d256e73de143a35c75bbfe9df1faf806d24 100644 (file)
@@ -7,6 +7,14 @@
  */
 package org.opendaylight.controller.config.yang.logback.config;
 
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import javax.management.ObjectName;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -16,15 +24,6 @@ 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 javax.management.ObjectName;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-import static org.junit.matchers.JUnitMatchers.containsString;
-
 public class LogbackModuleTest extends AbstractConfigTest {
 
     private static final String INSTANCE_NAME = "singleton";
@@ -89,7 +88,7 @@ public class LogbackModuleTest extends AbstractConfigTest {
         assertBeanCount(1, factory.getImplementationName());
 
         ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
-        transaction.destroyConfigBean(factory.getImplementationName(), INSTANCE_NAME);
+        transaction.destroyModule(factory.getImplementationName(), INSTANCE_NAME);
         CommitStatus status = transaction.commit();
 
         assertBeanCount(0, factory.getImplementationName());
index 613940144bed213bc7ad76c70a288d9700844d70..5f34a9aa82d0ab35780b04862882710b7ae47fe8 100644 (file)
       </plugins>
     </pluginManagement>
     <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+        <version>2.12</version>
+        <configuration>
+          <failsOnError>false</failsOnError>
+          <failOnViolation>false</failOnViolation>
+          <configLocation>checkstyle-logging.xml</configLocation>
+          <consoleOutput>true</consoleOutput>
+          <includeTestSourceDirectory>true</includeTestSourceDirectory>
+          <sourceDirectory>${project.basedir}</sourceDirectory>
+          <includes>**\/*.java,**\/*.xml,**\/*.ini,**\/*.sh,**\/*.bat,**\/*.yang</includes>
+          <excludes>**\/target\/,**\/bin\/,**\/target-ide\/,**\/${jmxGeneratorPath}\/,**\/${salGeneratorPath}\/</excludes>
+        </configuration>
+        <dependencies>
+          <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>checkstyle-logging</artifactId>
+            <version>${yangtools.version}</version>
+          </dependency>
+        </dependencies>
+        <executions>
+          <execution>
+            <goals>
+              <goal>check</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-compiler-plugin</artifactId>
index c95661d9c95b7470ca45156b56c6bc2df3fe7fe6..62b295be8d1c53e8b648f7c352ab9b0c5244e875 100644 (file)
@@ -7,6 +7,19 @@
  */
 package org.opendaylight.controller.config.threadpool.fixed;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
+import java.util.ArrayList;
+import java.util.List;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
 import org.junit.Before;
 import org.junit.Test;
 import org.opendaylight.controller.config.api.ConflictingVersionException;
@@ -19,16 +32,11 @@ import org.opendaylight.controller.config.yang.threadpool.impl.NamingThreadFacto
 import org.opendaylight.controller.config.yang.threadpool.impl.NamingThreadFactoryModuleMXBean;
 import org.opendaylight.controller.config.yang.threadpool.impl.fixed.FixedThreadPoolModuleFactory;
 import org.opendaylight.controller.config.yang.threadpool.impl.fixed.FixedThreadPoolModuleMXBean;
-
-import javax.management.InstanceAlreadyExistsException;
-import javax.management.InstanceNotFoundException;
-import javax.management.ObjectName;
-
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-import static org.junit.matchers.JUnitMatchers.containsString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class FixedThreadPoolConfigBeanTest extends AbstractConfigTest {
+    private static final Logger logger = LoggerFactory.getLogger(FixedThreadPoolConfigBeanTest.class);
 
     private FixedThreadPoolModuleFactory factory;
     private final String nameInstance = "fixedInstance";
@@ -36,7 +44,7 @@ public class FixedThreadPoolConfigBeanTest extends AbstractConfigTest {
     @Before
     public void setUp() {
         factory = new FixedThreadPoolModuleFactory();
-        super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(mockedContext,factory,
+        super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(mockedContext, factory,
                 new NamingThreadFactoryModuleFactory()));
     }
 
@@ -44,7 +52,7 @@ public class FixedThreadPoolConfigBeanTest extends AbstractConfigTest {
     public void testCreateBean() throws InstanceAlreadyExistsException, ValidationException,
             ConflictingVersionException {
         ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
-        createFixed(transaction, nameInstance, 2);
+        createFixed(transaction, nameInstance, 2, nameInstance);
 
         transaction.validateConfig();
         CommitStatus status = transaction.commit();
@@ -57,7 +65,7 @@ public class FixedThreadPoolConfigBeanTest extends AbstractConfigTest {
     public void testReusingOldInstance() throws InstanceAlreadyExistsException, ConflictingVersionException,
             ValidationException {
         ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
-        createFixed(transaction, nameInstance, 4);
+        createFixed(transaction, nameInstance, 4, nameInstance);
 
         transaction.validateConfig();
         transaction.commit();
@@ -75,12 +83,12 @@ public class FixedThreadPoolConfigBeanTest extends AbstractConfigTest {
     public void testNegative() throws ConflictingVersionException, ValidationException, InstanceAlreadyExistsException {
         ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
 
-        createFixed(transaction, nameInstance, 5);
+        createFixed(transaction, nameInstance, 5, nameInstance);
         transaction.commit();
 
         transaction = configRegistryClient.createTransaction();
         try {
-            createFixed(transaction, nameInstance, 0);
+            createFixed(transaction, nameInstance, 0, nameInstance);
             fail();
         } catch (InstanceAlreadyExistsException e) {
             assertThat(
@@ -89,26 +97,56 @@ public class FixedThreadPoolConfigBeanTest extends AbstractConfigTest {
         }
     }
 
+    private int countThreadsByPrefix(String prefix) {
+        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
+        int result = 0;
+        List<String> names = new ArrayList<>();
+        for (ThreadInfo threadInfo : threadMXBean.dumpAllThreads(false, false)) {
+            names.add(threadInfo.getThreadName());
+            if (threadInfo.getThreadName().startsWith(prefix)) {
+                result++;
+            }
+        }
+        logger.info("Current threads {}", names);
+        return result;
+    }
+
     @Test
     public void testDestroy() throws InstanceAlreadyExistsException, ValidationException, ConflictingVersionException,
-            InstanceNotFoundException {
+            InstanceNotFoundException, InterruptedException {
+
+        String prefix = org.apache.commons.lang3.RandomStringUtils.randomAlphabetic(10);
+
+        int numberOfThreads = 100;
+        int threadCount1 = countThreadsByPrefix(prefix);
+        assertEquals(0, threadCount1);
         ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
-        createFixed(transaction, nameInstance, 1);
 
+        createFixed(transaction, nameInstance, numberOfThreads, prefix);
         transaction.commit();
+        int threadCount2 = countThreadsByPrefix(prefix);
+        assertEquals(numberOfThreads, threadCount2);
 
         transaction = configRegistryClient.createTransaction();
-        transaction.destroyConfigBean(factory.getImplementationName(), nameInstance);
+        transaction.destroyModule(factory.getImplementationName(), nameInstance);
         CommitStatus status = transaction.commit();
 
         assertBeanCount(0, factory.getImplementationName());
         assertStatus(status, 0, 0, 1);
+
+        for (int i = 0; i < 60; i++) {
+            if (countThreadsByPrefix(prefix) == 0) {
+                return;
+            }
+            Thread.sleep(1000);
+        }
+        assertEquals(0, countThreadsByPrefix(prefix));
     }
 
     @Test
     public void testValidationException() throws InstanceAlreadyExistsException {
         ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
-        createFixed(transaction, nameInstance, -1);
+        createFixed(transaction, nameInstance, -1, nameInstance);
         try {
             transaction.validateConfig();
             fail();
@@ -117,7 +155,7 @@ public class FixedThreadPoolConfigBeanTest extends AbstractConfigTest {
         }
     }
 
-    private ObjectName createFixed(ConfigTransactionJMXClient transaction, String name, int numberOfThreads)
+    private ObjectName createFixed(ConfigTransactionJMXClient transaction, String name, int numberOfThreads, String prefix)
             throws InstanceAlreadyExistsException {
         ObjectName nameCreated = transaction.createModule(factory.getImplementationName(), name);
         FixedThreadPoolModuleMXBean mxBean = transaction.newMXBeanProxy(nameCreated, FixedThreadPoolModuleMXBean.class);
@@ -126,7 +164,7 @@ public class FixedThreadPoolConfigBeanTest extends AbstractConfigTest {
         ObjectName threadFactoryON = transaction.createModule(NamingThreadFactoryModuleFactory.NAME, "naming");
         NamingThreadFactoryModuleMXBean namingThreadFactoryModuleMXBean = transaction.newMXBeanProxy(threadFactoryON,
                 NamingThreadFactoryModuleMXBean.class);
-        namingThreadFactoryModuleMXBean.setNamePrefix("prefix");
+        namingThreadFactoryModuleMXBean.setNamePrefix(prefix);
 
         mxBean.setThreadFactory(threadFactoryON);
 
index ef06e43d2f291b87f994d04612c0f9f5488ea86b..0fc2fd6eb3d0c5aa9c0bd4d0c4d45611417f4887 100644 (file)
@@ -7,6 +7,15 @@
  */
 package org.opendaylight.controller.config.threadpool.scheduled;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
 import org.junit.Before;
 import org.junit.Test;
 import org.opendaylight.controller.config.api.ConflictingVersionException;
@@ -20,16 +29,6 @@ import org.opendaylight.controller.config.yang.threadpool.impl.NamingThreadFacto
 import org.opendaylight.controller.config.yang.threadpool.impl.scheduled.ScheduledThreadPoolModuleFactory;
 import org.opendaylight.controller.config.yang.threadpool.impl.scheduled.ScheduledThreadPoolModuleMXBean;
 
-import javax.management.InstanceAlreadyExistsException;
-import javax.management.InstanceNotFoundException;
-import javax.management.ObjectName;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.junit.matchers.JUnitMatchers.containsString;
-
 public class ScheduledThreadPoolConfigBeanTest extends AbstractConfigTest {
 
     private ScheduledThreadPoolModuleFactory factory;
@@ -103,7 +102,7 @@ public class ScheduledThreadPoolConfigBeanTest extends AbstractConfigTest {
         transaction.commit();
 
         transaction = configRegistryClient.createTransaction();
-        transaction.destroyConfigBean(factory.getImplementationName(), instanceName);
+        transaction.destroyModule(factory.getImplementationName(), instanceName);
         CommitStatus status = transaction.commit();
 
         assertBeanCount(0, factory.getImplementationName());
index 48a6c15706a209fe5001b60c0c17e1bdf80d1c96..8c65eaad14247a8be963c1dbc1d3095c7427d4d9 100644 (file)
@@ -31,9 +31,9 @@ import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.Generated
 import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.JavaFileInputBuilder;
 import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.TypeName;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.osgi.framework.BundleContext;
 
 public class AbsFactoryGeneratedObjectFactory {
+    private static final String BUNDLE_CONTEXT = "org.osgi.framework.BundleContext";
 
     public GeneratedObject toGeneratedObject(ModuleMXBeanEntry mbe, Optional<String> copyright) {
         FullyQualifiedName absFactoryFQN = new FullyQualifiedName(mbe.getPackageName(), mbe.getAbstractFactoryName());
@@ -91,7 +91,7 @@ public class AbsFactoryGeneratedObjectFactory {
             "public %s createModule(String instanceName, %s dependencyResolver, %s bundleContext) {\n"+
                 "return instantiateModule(instanceName, dependencyResolver, bundleContext);\n"+
             "}\n",
-                Module.class.getCanonicalName(), DependencyResolver.class.getCanonicalName(), BundleContext.class.getCanonicalName()));
+                Module.class.getCanonicalName(), DependencyResolver.class.getCanonicalName(), BUNDLE_CONTEXT));
 
         b.addToBody(getCreateModule(moduleFQN, moduleFields));
 
@@ -100,12 +100,12 @@ public class AbsFactoryGeneratedObjectFactory {
                 "return new %s(new %s(NAME, instanceName), dependencyResolver, oldModule, oldInstance);\n"+
             "}\n",
                 moduleFQN, DependencyResolver.class.getCanonicalName(), moduleFQN, AutoCloseable.class.getCanonicalName(),
-                BundleContext.class.getCanonicalName(), moduleFQN, ModuleIdentifier.class.getCanonicalName()));
+                BUNDLE_CONTEXT, moduleFQN, ModuleIdentifier.class.getCanonicalName()));
 
         b.addToBody(format("\n"+
             "public %s instantiateModule(String instanceName, %s dependencyResolver, %s bundleContext) {\n"+
                 "return new %s(new %s(NAME, instanceName), dependencyResolver);\n"+
-            "}\n", moduleFQN, DependencyResolver.class.getCanonicalName(), BundleContext.class.getCanonicalName(),
+            "}\n", moduleFQN, DependencyResolver.class.getCanonicalName(), BUNDLE_CONTEXT,
                 moduleFQN, ModuleIdentifier.class.getCanonicalName()
         ));
 
@@ -118,7 +118,7 @@ public class AbsFactoryGeneratedObjectFactory {
             "@Override\n"+
             "public java.util.Set<%s> getDefaultModules(org.opendaylight.controller.config.api.DependencyResolverFactory dependencyResolverFactory, %s bundleContext) {\n"+
                 "return new java.util.HashSet<%s>();\n"+
-            "}\n", moduleFQN, BundleContext.class.getCanonicalName(), moduleFQN));
+            "}\n", moduleFQN, BUNDLE_CONTEXT, moduleFQN));
 
         return new GeneratedObjectBuilder(b.build()).toGeneratedObject();
     }
@@ -127,8 +127,8 @@ public class AbsFactoryGeneratedObjectFactory {
         String result = "\n"+
             "@Override\n"+
             format("public %s createModule(String instanceName, %s dependencyResolver, %s old, %s bundleContext) throws Exception {\n",
-                                Module.class.getCanonicalName(),DependencyResolver.class.getCanonicalName(),
-                                DynamicMBeanWithInstance.class.getCanonicalName(),BundleContext.class.getCanonicalName())+
+                                Module.class.getCanonicalName(), DependencyResolver.class.getCanonicalName(),
+                                DynamicMBeanWithInstance.class.getCanonicalName(), BUNDLE_CONTEXT)+
                 format("%s oldModule = null;\n",moduleFQN)+
                 "try {\n"+
                     format("oldModule = (%s) old.getModule();\n",moduleFQN)+
diff --git a/opendaylight/distribution/opendaylight-karaf/src/main/resources/etc/jre.properties b/opendaylight/distribution/opendaylight-karaf/src/main/resources/etc/jre.properties
new file mode 100644 (file)
index 0000000..e91da89
--- /dev/null
@@ -0,0 +1,503 @@
+################################################################################
+#
+#    Licensed to the Apache Software Foundation (ASF) under one or more
+#    contributor license agreements.  See the NOTICE file distributed with
+#    this work for additional information regarding copyright ownership.
+#    The ASF licenses this file to You under the Apache License, Version 2.0
+#    (the "License"); you may not use this file except in compliance with
+#    the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+#
+################################################################################
+
+#
+# Java platform package export properties.
+#
+
+# Standard package set.  Note that:
+#   - javax.transaction* is exported with a mandatory attribute
+jre-1.6= \
+ javax.accessibility, \
+ javax.activation;version="1.1", \
+ javax.activity, \
+ javax.crypto, \
+ javax.crypto.interfaces, \
+ javax.crypto.spec, \
+ javax.imageio, \
+ javax.imageio.event, \
+ javax.imageio.metadata, \
+ javax.imageio.plugins.bmp, \
+ javax.imageio.plugins.jpeg, \
+ javax.imageio.spi, \
+ javax.imageio.stream, \
+ javax.jws, \
+ javax.jws.soap, \
+ javax.lang.model, \
+ javax.lang.model.element, \
+ javax.lang.model.type, \
+ javax.lang.model.util, \
+ javax.management, \
+ javax.management.loading, \
+ javax.management.modelmbean, \
+ javax.management.monitor, \
+ javax.management.openmbean, \
+ javax.management.relation, \
+ javax.management.remote, \
+ javax.management.remote.rmi, \
+ javax.management.timer, \
+ javax.naming, \
+ javax.naming.directory, \
+ javax.naming.event, \
+ javax.naming.ldap, \
+ javax.naming.spi, \
+ javax.net, \
+ javax.net.ssl, \
+ javax.print, \
+ javax.print.attribute, \
+ javax.print.attribute.standard, \
+ javax.print.event, \
+ javax.rmi, \
+ javax.rmi.CORBA, \
+ javax.rmi.ssl, \
+ javax.script, \
+ javax.security.auth, \
+ javax.security.auth.callback, \
+ javax.security.auth.kerberos, \
+ javax.security.auth.login, \
+ javax.security.auth.spi, \
+ javax.security.auth.x500, \
+ javax.security.cert, \
+ javax.security.sasl, \
+ javax.sound.midi, \
+ javax.sound.midi.spi, \
+ javax.sound.sampled, \
+ javax.sound.sampled.spi, \
+ javax.sql, \
+ javax.sql.rowset, \
+ javax.sql.rowset.serial, \
+ javax.sql.rowset.spi, \
+ javax.swing, \
+ javax.swing.border, \
+ javax.swing.colorchooser, \
+ javax.swing.event, \
+ javax.swing.filechooser, \
+ javax.swing.plaf, \
+ javax.swing.plaf.basic, \
+ javax.swing.plaf.metal, \
+ javax.swing.plaf.multi, \
+ javax.swing.plaf.synth, \
+ javax.swing.table, \
+ javax.swing.text, \
+ javax.swing.text.html, \
+ javax.swing.text.html.parser, \
+ javax.swing.text.rtf, \
+ javax.swing.tree, \
+ javax.swing.undo, \
+ javax.tools, \
+ javax.transaction; javax.transaction.xa; partial=true; mandatory:=partial, \
+ javax.xml, \
+ javax.xml.bind;version="2.2.1", \
+ javax.xml.bind.annotation;version="2.2.1", \
+ javax.xml.bind.annotation.adapters;version="2.2.1", \
+ javax.xml.bind.attachment;version="2.2.1", \
+ javax.xml.bind.helpers;version="2.2.1", \
+ javax.xml.bind.util;version="2.2.1", \
+ javax.xml.crypto, \
+ javax.xml.crypto.dom, \
+ javax.xml.crypto.dsig, \
+ javax.xml.crypto.dsig.dom, \
+ javax.xml.crypto.dsig.keyinfo, \
+ javax.xml.crypto.dsig.spec, \
+ javax.xml.datatype, \
+ javax.xml.namespace, \
+ javax.xml.parsers, \
+ javax.xml.soap;version="1.3", \
+ javax.xml.stream;version="1.2", \
+ javax.xml.stream.events;version="1.2", \
+ javax.xml.stream.util;version="1.2", \
+ javax.xml.transform, \
+ javax.xml.transform.dom, \
+ javax.xml.transform.sax, \
+ javax.xml.transform.stax, \
+ javax.xml.transform.stream, \
+ javax.xml.validation, \
+ javax.xml.ws;version="2.2", \
+ javax.xml.ws.handler;version="2.2", \
+ javax.xml.ws.handler.soap;version="2.2", \
+ javax.xml.ws.http;version="2.2", \
+ javax.xml.ws.soap;version="2.2", \
+ javax.xml.ws.spi;version="2.2", \
+ javax.xml.ws.wsaddressing;version="2.2", \
+ javax.xml.ws.spi.http;version="2.2", \
+ javax.xml.xpath, \
+ org.ietf.jgss, \
+ org.omg.CORBA, \
+ org.omg.CORBA_2_3, \
+ org.omg.CORBA_2_3.portable, \
+ org.omg.CORBA.DynAnyPackage, \
+ org.omg.CORBA.ORBPackage, \
+ org.omg.CORBA.portable, \
+ org.omg.CORBA.TypeCodePackage, \
+ org.omg.CosNaming, \
+ org.omg.CosNaming.NamingContextExtPackage, \
+ org.omg.CosNaming.NamingContextPackage, \
+ org.omg.Dynamic, \
+ org.omg.DynamicAny, \
+ org.omg.DynamicAny.DynAnyFactoryPackage, \
+ org.omg.DynamicAny.DynAnyPackage, \
+ org.omg.IOP, \
+ org.omg.IOP.CodecFactoryPackage, \
+ org.omg.IOP.CodecPackage, \
+ org.omg.Messaging, \
+ org.omg.PortableInterceptor, \
+ org.omg.PortableInterceptor.ORBInitInfoPackage, \
+ org.omg.PortableServer, \
+ org.omg.PortableServer.CurrentPackage, \
+ org.omg.PortableServer.POAManagerPackage, \
+ org.omg.PortableServer.POAPackage, \
+ org.omg.PortableServer.portable, \
+ org.omg.PortableServer.ServantLocatorPackage, \
+ org.omg.SendingContext, \
+ org.omg.stub.java.rmi, \
+ org.omg.stub.javax.management.remote.rmi, \
+ org.w3c.dom, \
+ org.w3c.dom.bootstrap, \
+ org.w3c.dom.css, \
+ org.w3c.dom.events, \
+ org.w3c.dom.html, \
+ org.w3c.dom.ls, \
+ org.w3c.dom.ranges, \
+ org.w3c.dom.stylesheets, \
+ org.w3c.dom.traversal, \
+ org.w3c.dom.views, \
+ org.w3c.dom.xpath, \
+ org.xml.sax, \
+ org.xml.sax.ext, \
+ org.xml.sax.helpers
+
+# Standard package set.  Note that:
+#   - javax.transaction* is exported with a mandatory attribute
+jre-1.7= \
+ javax.accessibility, \
+ javax.activation;version="1.1", \
+ javax.activity, \
+ javax.crypto, \
+ javax.crypto.interfaces, \
+ javax.crypto.spec, \
+ javax.imageio, \
+ javax.imageio.event, \
+ javax.imageio.metadata, \
+ javax.imageio.plugins.bmp, \
+ javax.imageio.plugins.jpeg, \
+ javax.imageio.spi, \
+ javax.imageio.stream, \
+ javax.jws, \
+ javax.jws.soap, \
+ javax.lang.model, \
+ javax.lang.model.element, \
+ javax.lang.model.type, \
+ javax.lang.model.util, \
+ javax.management, \
+ javax.management.loading, \
+ javax.management.modelmbean, \
+ javax.management.monitor, \
+ javax.management.openmbean, \
+ javax.management.relation, \
+ javax.management.remote, \
+ javax.management.remote.rmi, \
+ javax.management.timer, \
+ javax.naming, \
+ javax.naming.directory, \
+ javax.naming.event, \
+ javax.naming.ldap, \
+ javax.naming.spi, \
+ javax.net, \
+ javax.net.ssl, \
+ javax.print, \
+ javax.print.attribute, \
+ javax.print.attribute.standard, \
+ javax.print.event, \
+ javax.rmi, \
+ javax.rmi.CORBA, \
+ javax.rmi.ssl, \
+ javax.script, \
+ javax.security.auth, \
+ javax.security.auth.callback, \
+ javax.security.auth.kerberos, \
+ javax.security.auth.login, \
+ javax.security.auth.spi, \
+ javax.security.auth.x500, \
+ javax.security.cert, \
+ javax.security.sasl, \
+ javax.sound.midi, \
+ javax.sound.midi.spi, \
+ javax.sound.sampled, \
+ javax.sound.sampled.spi, \
+ javax.sql, \
+ javax.sql.rowset, \
+ javax.sql.rowset.serial, \
+ javax.sql.rowset.spi, \
+ javax.swing, \
+ javax.swing.border, \
+ javax.swing.colorchooser, \
+ javax.swing.event, \
+ javax.swing.filechooser, \
+ javax.swing.plaf, \
+ javax.swing.plaf.basic, \
+ javax.swing.plaf.metal, \
+ javax.swing.plaf.multi, \
+ javax.swing.plaf.synth, \
+ javax.swing.table, \
+ javax.swing.text, \
+ javax.swing.text.html, \
+ javax.swing.text.html.parser, \
+ javax.swing.text.rtf, \
+ javax.swing.tree, \
+ javax.swing.undo, \
+ javax.tools, \
+ javax.transaction; javax.transaction.xa; partial=true; mandatory:=partial, \
+ javax.xml, \
+ javax.xml.bind;version="2.2.1", \
+ javax.xml.bind.annotation;version="2.2.1", \
+ javax.xml.bind.annotation.adapters;version="2.2.1", \
+ javax.xml.bind.attachment;version="2.2.1", \
+ javax.xml.bind.helpers;version="2.2.1", \
+ javax.xml.bind.util;version="2.2.1", \
+ javax.xml.crypto, \
+ javax.xml.crypto.dom, \
+ javax.xml.crypto.dsig, \
+ javax.xml.crypto.dsig.dom, \
+ javax.xml.crypto.dsig.keyinfo, \
+ javax.xml.crypto.dsig.spec, \
+ javax.xml.datatype, \
+ javax.xml.namespace, \
+ javax.xml.parsers, \
+ javax.xml.soap;version="1.3", \
+ javax.xml.stream;version="1.2", \
+ javax.xml.stream.events;version="1.2", \
+ javax.xml.stream.util;version="1.2", \
+ javax.xml.transform, \
+ javax.xml.transform.dom, \
+ javax.xml.transform.sax, \
+ javax.xml.transform.stax, \
+ javax.xml.transform.stream, \
+ javax.xml.validation, \
+ javax.xml.ws;version="2.2", \
+ javax.xml.ws.handler;version="2.2", \
+ javax.xml.ws.handler.soap;version="2.2", \
+ javax.xml.ws.http;version="2.2", \
+ javax.xml.ws.soap;version="2.2", \
+ javax.xml.ws.spi;version="2.2", \
+ javax.xml.ws.wsaddressing;version="2.2", \
+ javax.xml.ws.spi.http;version="2.2", \
+ javax.xml.xpath, \
+ org.ietf.jgss, \
+ org.omg.CORBA, \
+ org.omg.CORBA_2_3, \
+ org.omg.CORBA_2_3.portable, \
+ org.omg.CORBA.DynAnyPackage, \
+ org.omg.CORBA.ORBPackage, \
+ org.omg.CORBA.portable, \
+ org.omg.CORBA.TypeCodePackage, \
+ org.omg.CosNaming, \
+ org.omg.CosNaming.NamingContextExtPackage, \
+ org.omg.CosNaming.NamingContextPackage, \
+ org.omg.Dynamic, \
+ org.omg.DynamicAny, \
+ org.omg.DynamicAny.DynAnyFactoryPackage, \
+ org.omg.DynamicAny.DynAnyPackage, \
+ org.omg.IOP, \
+ org.omg.IOP.CodecFactoryPackage, \
+ org.omg.IOP.CodecPackage, \
+ org.omg.Messaging, \
+ org.omg.PortableInterceptor, \
+ org.omg.PortableInterceptor.ORBInitInfoPackage, \
+ org.omg.PortableServer, \
+ org.omg.PortableServer.CurrentPackage, \
+ org.omg.PortableServer.POAManagerPackage, \
+ org.omg.PortableServer.POAPackage, \
+ org.omg.PortableServer.portable, \
+ org.omg.PortableServer.ServantLocatorPackage, \
+ org.omg.SendingContext, \
+ org.omg.stub.java.rmi, \
+ org.omg.stub.javax.management.remote.rmi, \
+ org.w3c.dom, \
+ org.w3c.dom.bootstrap, \
+ org.w3c.dom.css, \
+ org.w3c.dom.events, \
+ org.w3c.dom.html, \
+ org.w3c.dom.ls, \
+ org.w3c.dom.ranges, \
+ org.w3c.dom.stylesheets, \
+ org.w3c.dom.traversal, \
+ org.w3c.dom.views, \
+ org.w3c.dom.xpath, \
+ org.xml.sax, \
+ org.xml.sax.ext, \
+ org.xml.sax.helpers
+
+jre-1.8= \
+ javax.accessibility, \
+ javax.activation;version="1.1", \
+ javax.activity, \
+ javax.crypto, \
+ javax.crypto.interfaces, \
+ javax.crypto.spec, \
+ javax.imageio, \
+ javax.imageio.event, \
+ javax.imageio.metadata, \
+ javax.imageio.plugins.bmp, \
+ javax.imageio.plugins.jpeg, \
+ javax.imageio.spi, \
+ javax.imageio.stream, \
+ javax.jws, \
+ javax.jws.soap, \
+ javax.lang.model, \
+ javax.lang.model.element, \
+ javax.lang.model.type, \
+ javax.lang.model.util, \
+ javax.management, \
+ javax.management.loading, \
+ javax.management.modelmbean, \
+ javax.management.monitor, \
+ javax.management.openmbean, \
+ javax.management.relation, \
+ javax.management.remote, \
+ javax.management.remote.rmi, \
+ javax.management.timer, \
+ javax.naming, \
+ javax.naming.directory, \
+ javax.naming.event, \
+ javax.naming.ldap, \
+ javax.naming.spi, \
+ javax.net, \
+ javax.net.ssl, \
+ javax.print, \
+ javax.print.attribute, \
+ javax.print.attribute.standard, \
+ javax.print.event, \
+ javax.rmi, \
+ javax.rmi.CORBA, \
+ javax.rmi.ssl, \
+ javax.script, \
+ javax.security.auth, \
+ javax.security.auth.callback, \
+ javax.security.auth.kerberos, \
+ javax.security.auth.login, \
+ javax.security.auth.spi, \
+ javax.security.auth.x500, \
+ javax.security.cert, \
+ javax.security.sasl, \
+ javax.sound.midi, \
+ javax.sound.midi.spi, \
+ javax.sound.sampled, \
+ javax.sound.sampled.spi, \
+ javax.sql, \
+ javax.sql.rowset, \
+ javax.sql.rowset.serial, \
+ javax.sql.rowset.spi, \
+ javax.swing, \
+ javax.swing.border, \
+ javax.swing.colorchooser, \
+ javax.swing.event, \
+ javax.swing.filechooser, \
+ javax.swing.plaf, \
+ javax.swing.plaf.basic, \
+ javax.swing.plaf.metal, \
+ javax.swing.plaf.multi, \
+ javax.swing.plaf.synth, \
+ javax.swing.table, \
+ javax.swing.text, \
+ javax.swing.text.html, \
+ javax.swing.text.html.parser, \
+ javax.swing.text.rtf, \
+ javax.swing.tree, \
+ javax.swing.undo, \
+ javax.tools, \
+ javax.transaction; javax.transaction.xa; partial=true; mandatory:=partial, \
+ javax.xml, \
+ javax.xml.bind;version="2.2.1", \
+ javax.xml.bind.annotation;version="2.2.1", \
+ javax.xml.bind.annotation.adapters;version="2.2.1", \
+ javax.xml.bind.attachment;version="2.2.1", \
+ javax.xml.bind.helpers;version="2.2.1", \
+ javax.xml.bind.util;version="2.2.1", \
+ javax.xml.crypto, \
+ javax.xml.crypto.dom, \
+ javax.xml.crypto.dsig, \
+ javax.xml.crypto.dsig.dom, \
+ javax.xml.crypto.dsig.keyinfo, \
+ javax.xml.crypto.dsig.spec, \
+ javax.xml.datatype, \
+ javax.xml.namespace, \
+ javax.xml.parsers, \
+ javax.xml.soap;version="1.3", \
+ javax.xml.stream;version="1.2", \
+ javax.xml.stream.events;version="1.2", \
+ javax.xml.stream.util;version="1.2", \
+ javax.xml.transform, \
+ javax.xml.transform.dom, \
+ javax.xml.transform.sax, \
+ javax.xml.transform.stax, \
+ javax.xml.transform.stream, \
+ javax.xml.validation, \
+ javax.xml.ws;version="2.2", \
+ javax.xml.ws.handler;version="2.2", \
+ javax.xml.ws.handler.soap;version="2.2", \
+ javax.xml.ws.http;version="2.2", \
+ javax.xml.ws.soap;version="2.2", \
+ javax.xml.ws.spi;version="2.2", \
+ javax.xml.ws.wsaddressing;version="2.2", \
+ javax.xml.ws.spi.http;version="2.2", \
+ javax.xml.xpath, \
+ org.ietf.jgss, \
+ org.omg.CORBA, \
+ org.omg.CORBA_2_3, \
+ org.omg.CORBA_2_3.portable, \
+ org.omg.CORBA.DynAnyPackage, \
+ org.omg.CORBA.ORBPackage, \
+ org.omg.CORBA.portable, \
+ org.omg.CORBA.TypeCodePackage, \
+ org.omg.CosNaming, \
+ org.omg.CosNaming.NamingContextExtPackage, \
+ org.omg.CosNaming.NamingContextPackage, \
+ org.omg.Dynamic, \
+ org.omg.DynamicAny, \
+ org.omg.DynamicAny.DynAnyFactoryPackage, \
+ org.omg.DynamicAny.DynAnyPackage, \
+ org.omg.IOP, \
+ org.omg.IOP.CodecFactoryPackage, \
+ org.omg.IOP.CodecPackage, \
+ org.omg.Messaging, \
+ org.omg.PortableInterceptor, \
+ org.omg.PortableInterceptor.ORBInitInfoPackage, \
+ org.omg.PortableServer, \
+ org.omg.PortableServer.CurrentPackage, \
+ org.omg.PortableServer.POAManagerPackage, \
+ org.omg.PortableServer.POAPackage, \
+ org.omg.PortableServer.portable, \
+ org.omg.PortableServer.ServantLocatorPackage, \
+ org.omg.SendingContext, \
+ org.omg.stub.java.rmi, \
+ org.omg.stub.javax.management.remote.rmi, \
+ org.w3c.dom, \
+ org.w3c.dom.bootstrap, \
+ org.w3c.dom.css, \
+ org.w3c.dom.events, \
+ org.w3c.dom.html, \
+ org.w3c.dom.ls, \
+ org.w3c.dom.ranges, \
+ org.w3c.dom.stylesheets, \
+ org.w3c.dom.traversal, \
+ org.w3c.dom.views, \
+ org.w3c.dom.xpath, \
+ org.xml.sax, \
+ org.xml.sax.ext, \
+ org.xml.sax.helpers
diff --git a/opendaylight/distribution/opendaylight-karaf/src/main/resources/etc/startup.properties b/opendaylight/distribution/opendaylight-karaf/src/main/resources/etc/startup.properties
new file mode 100644 (file)
index 0000000..ca8c83c
--- /dev/null
@@ -0,0 +1,53 @@
+#Bundles to be started on startup, with startlevel
+
+# feature: framework version: 3.0.1
+mvn\:org.ops4j.base/ops4j-base-lang/1.4.0 = 5
+mvn\:biz.aQute.bnd/bndlib/2.2.0 = 5
+mvn\:org.ops4j.pax.swissbox/pax-swissbox-bnd/1.7.0 = 5
+mvn\:org.ops4j.pax.url/pax-url-maven-commons/1.6.0 = 5
+mvn\:org.ops4j.pax.url/pax-url-aether/1.6.0 = 5
+mvn\:org.ops4j.pax.url/pax-url-wrap/1.6.0 = 5
+mvn\:javax.annotation/javax.annotation-api/1.2 = 5
+mvn\:org.ops4j.pax.logging/pax-logging-api/1.7.2 = 8
+mvn\:org.ops4j.pax.logging/pax-logging-service/1.7.2 = 8
+mvn\:org.apache.karaf.service/org.apache.karaf.service.guard/3.0.1 = 10
+mvn\:org.apache.felix/org.apache.felix.configadmin/1.6.0 = 10
+mvn\:org.apache.felix/org.apache.felix.fileinstall/3.2.8 = 11
+mvn\:org.ow2.asm/asm-all/4.1 = 12
+mvn\:org.apache.aries/org.apache.aries.util/1.1.0 = 20
+mvn\:org.apache.aries.proxy/org.apache.aries.proxy.api/1.0.0 = 20
+mvn\:org.apache.aries.proxy/org.apache.aries.proxy.impl/1.0.2 = 20
+mvn\:org.apache.aries.blueprint/org.apache.aries.blueprint.api/1.0.0 = 20
+mvn\:org.apache.aries.blueprint/org.apache.aries.blueprint.cm/1.0.3 = 20
+mvn\:org.apache.aries.blueprint/org.apache.aries.blueprint.core.compatibility/1.0.0 = 20
+mvn\:org.apache.aries.blueprint/org.apache.aries.blueprint.core/1.4.0 = 20
+mvn\:org.apache.karaf.deployer/org.apache.karaf.deployer.spring/3.0.1 = 24
+mvn\:org.apache.karaf.deployer/org.apache.karaf.deployer.blueprint/3.0.1 = 24
+mvn\:org.apache.karaf.deployer/org.apache.karaf.deployer.wrap/3.0.1 = 24
+mvn\:org.apache.karaf.region/org.apache.karaf.region.core/3.0.1 = 25
+mvn\:org.apache.karaf.features/org.apache.karaf.features.core/3.0.1 = 25
+mvn\:org.apache.karaf.deployer/org.apache.karaf.deployer.features/3.0.1 = 26
+mvn\:jline/jline/2.11 = 30
+mvn\:org.jledit/core/0.2.1 = 30
+mvn\:org.fusesource.jansi/jansi/1.11 = 30
+mvn\:org.ops4j.base/ops4j-base-util-property/1.4.0 = 30
+mvn\:org.ops4j.base/ops4j-base-util-xml/1.4.0 = 30
+mvn\:org.ops4j.base/ops4j-base-util-collections/1.4.0 = 30
+mvn\:org.ops4j.pax.url/pax-url-commons/1.6.0 = 30
+mvn\:org.ops4j.pax.swissbox/pax-swissbox-property/1.7.0 = 30
+mvn\:org.ops4j.base/ops4j-base-net/1.4.0 = 30
+mvn\:org.ops4j.base/ops4j-base-monitors/1.4.0 = 30
+mvn\:org.apache.karaf.features/org.apache.karaf.features.command/3.0.1 = 30
+mvn\:org.apache.karaf.shell/org.apache.karaf.shell.console/3.0.1 = 30
+mvn\:org.apache.karaf.jaas/org.apache.karaf.jaas.modules/3.0.1 = 30
+mvn\:org.apache.karaf.jaas/org.apache.karaf.jaas.config/3.0.1 = 30
+mvn\:org.apache.karaf.jaas/org.apache.karaf.jaas.boot/3.0.1 = 30
+mvn\:org.apache.sshd/sshd-core/0.9.0 = 30
+mvn\:org.apache.karaf.bundle/org.apache.karaf.bundle.command/3.0.1 = 30
+mvn\:org.apache.karaf.shell/org.apache.karaf.shell.table/3.0.1 = 30
+mvn\:org.apache.karaf.bundle/org.apache.karaf.bundle.core/3.0.1 = 30
+mvn\:org.apache.karaf.shell/org.apache.karaf.shell.help/3.0.1 = 30
+mvn\:org.apache.karaf.system/org.apache.karaf.system.core/3.0.1 = 30
+mvn\:org.apache.karaf.system/org.apache.karaf.system.command/3.0.1 = 30
+mvn\:org.apache.karaf.shell/org.apache.karaf.shell.commands/3.0.1 = 30
+mvn\:org.apache.aries.quiesce/org.apache.aries.quiesce.api/1.0.0 = 30
index 5b44bb7569b9c3df2bce4c6ef76c4741e7541061..3802370aca3e8a7726d27d7fab1ca67d6eeb1944 100644 (file)
           <groupId>org.opendaylight.controller</groupId>
           <artifactId>netconf-ssh</artifactId>
         </dependency>
+        <dependency>
+          <groupId>org.opendaylight.controller</groupId>
+          <artifactId>netconf-tcp</artifactId>
+        </dependency>
         <dependency>
           <groupId>org.opendaylight.controller</groupId>
           <artifactId>netconf-util</artifactId>
index f15f8f7404d1420d0efc361551c99b0355d60170..f05afbb346f7f157162b313981874c4d3d6bdd40 100644 (file)
@@ -14,13 +14,11 @@ osgi.bundles=\
 
 # Netconf startup configuration
 
-# Netconf tcp address:port is optional with default value 127.0.0.1:8383
+# Netconf tcp address:port is optional
 #netconf.tcp.address=127.0.0.1
-#netconf.tcp.port=8384
-
-#netconf.tcp.client.address=127.0.0.1
-#netconf.tcp.client.port=8384
+#netconf.tcp.port=8383
 
+# Netconf tcp address:port is optional
 netconf.ssh.address=0.0.0.0
 netconf.ssh.port=1830
 netconf.ssh.pk.path = ./configuration/RSA.pk
index fcbec19f5fdde988f2df619e47c8b3333115e2ef..b4b433dbca7c332787a8e14aa29e8b981dc308ed 100644 (file)
             <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-timer</type>
             <name>global-timer</name>
           </timer>
+        </module>
+
+        <!-- Netconf dispatcher to be used by all netconf-connectors --> 
+        <module>
+          <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">prefix:threadfactory-naming</type>
+          <name>global-netconf-processing-executor-threadfactory</name>
+          <name-prefix xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">remote-connector-processing-executor</name-prefix>
+        </module>  
+        <module>
+          <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">prefix:threadpool-flexible</type>
+          <name>global-netconf-processing-executor</name>
+          <minThreadCount xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">1</minThreadCount>
+          <max-thread-count xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">4</max-thread-count>
+          <keepAliveMillis xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">600000</keepAliveMillis>
+          <threadFactory xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">
+            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadfactory</type>
+            <name>global-netconf-processing-executor-threadfactory</name>
+          </threadFactory>
         </module>  
 
         <!-- Loopback connection to netconf server in controller using netconf-connector -->
             <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-event-executor</type>
             <name>global-event-executor</name>
           </event-executor>
+          <binding-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
+            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-broker-osgi-registry</type>
+            <name>binding-osgi-broker</name>
+          </binding-registry>
           <dom-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
             <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-broker-osgi-registry</type>
             <name>dom-broker</name>
             <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf">prefix:netconf-client-dispatcher</type>
             <name>global-netconf-dispatcher</name>
           </client-dispatcher>
+          <processing-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
+            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadpool</type>
+            <name>global-netconf-processing-executor</name>
+          </processing-executor>
         </module>
       </modules>
 
             <provider>/modules/module[type='netconf-client-dispatcher'][name='global-netconf-dispatcher']</provider>
           </instance>
         </service>
+        <service>
+          <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadfactory</type>
+          <instance>
+            <name>global-netconf-processing-executor-threadfactory</name>
+            <provider>/modules/module[type='threadfactory-naming'][name='global-netconf-processing-executor-threadfactory']</provider>
+          </instance>
+        </service>
+        <service>
+          <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadpool</type>
+          <instance>
+            <name>global-netconf-processing-executor</name>
+            <provider>/modules/module[type='threadpool-flexible'][name='global-netconf-processing-executor']</provider>
+          </instance>
+        </service>
       </services>
 
     </data>
   <required-capabilities>
       <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf?module=odl-sal-netconf-connector-cfg&amp;revision=2013-10-28</capability>
       <capability>urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher?module=odl-netconfig-client-cfg&amp;revision=2014-04-08</capability>
+      <capability>urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?module=threadpool-impl&amp;revision=2013-04-05</capability>
+      <capability>urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible?module=threadpool-impl-flexible&amp;revision=2013-12-01</capability>
   </required-capabilities>
 </snapshot>
index 18fb785dd16f332f4d381de0512713521a06ab5f..fc58ed96e1b9fba37af9ff846621e107a3671440 100644 (file)
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.eclipse.xtend</groupId>
-      <artifactId>org.eclipse.xtend.lib</artifactId>
-    </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>forwardingrulesmanager</artifactId>
           </instructions>
         </configuration>
       </plugin>
-      <plugin>
-        <groupId>org.eclipse.xtend</groupId>
-        <artifactId>xtend-maven-plugin</artifactId>
-      </plugin>
     </plugins>
   </build>
   <scm>
diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/inventory/InventoryReader.java b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/inventory/InventoryReader.java
new file mode 100644 (file)
index 0000000..b37b501
--- /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.compatibility.inventory;
+
+import java.util.ArrayList;
+import java.util.Set;
+
+import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider;
+import org.opendaylight.controller.sal.compatibility.InventoryMapping;
+import org.opendaylight.controller.switchmanager.ISwitchManager;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodesBuilder;
+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.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class InventoryReader implements RuntimeDataProvider {
+    private static final Logger LOG = LoggerFactory.getLogger(InventoryReader.class);
+    private ISwitchManager switchManager;
+
+    public ISwitchManager getSwitchManager() {
+        return switchManager;
+    }
+
+    public void setSwitchManager(final ISwitchManager switchManager) {
+        this.switchManager = switchManager;
+    }
+
+    @Override
+    public DataObject readConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
+        // Topology and Inventory are operational only
+        return null;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public DataObject readOperationalData(final InstanceIdentifier<? extends DataObject> path) {
+        final Class<? extends DataObject> type = path.getTargetType();
+        if (Nodes.class.equals(type)) {
+            return readNodes(((InstanceIdentifier<Nodes>) path));
+        }
+        if (Node.class.equals(type)) {
+            return readNode(((InstanceIdentifier<Node>) path));
+        }
+        if (NodeConnector.class.equals(type)) {
+            return readNodeConnector(((InstanceIdentifier<NodeConnector>) path));
+        }
+
+        LOG.debug("Unsupported type {}", type);
+        return null;
+    }
+
+    private NodeConnector readNodeConnector(final InstanceIdentifier<NodeConnector> identifier) {
+        return constructNodeConnector(InventoryMapping.toAdNodeConnector(identifier));
+    }
+
+    private Node readNode(final InstanceIdentifier<Node> identifier) {
+        return constructNode(InventoryMapping.toAdNode(identifier));
+    }
+
+    private Node constructNode(final org.opendaylight.controller.sal.core.Node node) {
+        final Set<org.opendaylight.controller.sal.core.NodeConnector> connectors = getSwitchManager().getNodeConnectors(node);
+        final ArrayList<NodeConnector> tpList = new ArrayList<NodeConnector>(connectors.size());
+        for (final org.opendaylight.controller.sal.core.NodeConnector connector : connectors) {
+            tpList.add(constructNodeConnector(connector));
+        }
+
+        return new NodeBuilder()
+        .setKey(InventoryMapping.toNodeKey(node))
+        .setNodeConnector(tpList)
+        .build();
+    }
+
+    private Nodes readNodes(final InstanceIdentifier<Nodes> identifier) {
+        final Set<org.opendaylight.controller.sal.core.Node> nodes = getSwitchManager().getNodes();
+        final ArrayList<Node> nodeList = new ArrayList<Node>(nodes.size());
+        for (final org.opendaylight.controller.sal.core.Node node : nodes) {
+            nodeList.add(constructNode(node));
+        }
+
+        return new NodesBuilder().setNode(nodeList).build();
+    }
+
+    private static NodeConnector constructNodeConnector(final org.opendaylight.controller.sal.core.NodeConnector connector) {
+        return new NodeConnectorBuilder().setKey(InventoryMapping.toNodeConnectorKey(connector)).build();
+    }
+}
diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/inventory/InventoryReader.xtend b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/inventory/InventoryReader.xtend
deleted file mode 100644 (file)
index 9b71369..0000000
+++ /dev/null
@@ -1,90 +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.compatibility.inventory
-
-import org.opendaylight.controller.switchmanager.ISwitchManager
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
-import org.opendaylight.yangtools.yang.binding.DataObject
-import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider
-import java.util.ArrayList
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector
-import static extension org.opendaylight.controller.sal.compatibility.InventoryMapping.*;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodesBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorBuilder
-
-class InventoryReader implements RuntimeDataProvider {
-
-    @Property
-    var ISwitchManager switchManager;
-
-    override readConfigurationData(InstanceIdentifier<? extends DataObject> path) {
-
-        // Topology and Inventory are operational only
-        return null;
-    }
-
-    override readOperationalData(InstanceIdentifier<? extends DataObject> path) {
-        val type = path.targetType;
-        var DataObject data = null;
-        switch (type) {
-            case Nodes:
-                data = readNodes(path as InstanceIdentifier<Nodes>)
-            case Node:
-                data = readNode(path as InstanceIdentifier<Node>)
-            case NodeConnector:
-                data = readNodeConnector(path as InstanceIdentifier<NodeConnector>)
-        }
-        return data;
-    }
-
-    def DataObject readNodeConnector(InstanceIdentifier<NodeConnector> identifier) {
-        val nodeConnector = identifier.toAdNodeConnector();
-        return constructNodeConnector(nodeConnector)
-    }
-
-    def DataObject readNode(InstanceIdentifier<Node> identifier) {
-        val node = identifier.toAdNode();
-        return constructNode(node);
-    }
-
-
-    def Node constructNode(org.opendaylight.controller.sal.core.Node node) {
-        val connectors = switchManager.getNodeConnectors(node)
-
-        val tpList = new ArrayList<NodeConnector>(connectors.size)
-        for (connector : connectors) {
-            tpList.add(constructNodeConnector(connector));
-        }
-
-        val it = new NodeBuilder()
-        key = node.toNodeKey();
-        nodeConnector = tpList
-        return build();
-    }
-
-    def NodeConnector constructNodeConnector(org.opendaylight.controller.sal.core.NodeConnector connector) {
-        val it = new NodeConnectorBuilder()
-        key = connector.toNodeConnectorKey()
-        return build();
-    }
-
-    def readNodes(InstanceIdentifier<Nodes> identifier) {
-        val nodes = switchManager.nodes
-        val nodeList = new ArrayList<Node>(nodes.size)
-        for (node : nodes) {
-            nodeList.add(constructNode(node))
-        }
-        val it = new NodesBuilder();
-        node = nodeList
-        return build()
-
-    }
-}
diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/switchmanager/CompatibleSwitchManager.java b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/switchmanager/CompatibleSwitchManager.java
new file mode 100644 (file)
index 0000000..82c5b7b
--- /dev/null
@@ -0,0 +1,347 @@
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.compatibility.switchmanager;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.controller.sal.compatibility.NodeMapping;
+import org.opendaylight.controller.sal.core.Bandwidth;
+import org.opendaylight.controller.sal.core.ConstructionException;
+import org.opendaylight.controller.sal.core.Description;
+import org.opendaylight.controller.sal.core.ForwardingMode;
+import org.opendaylight.controller.sal.core.MacAddress;
+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.sal.utils.Status;
+import org.opendaylight.controller.switchmanager.ISwitchManager;
+import org.opendaylight.controller.switchmanager.Subnet;
+import org.opendaylight.controller.switchmanager.Switch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+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.NodeRef;
+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.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CompatibleSwitchManager extends ConfigurableSwitchManager implements ISwitchManager {
+    private static final  Logger LOG = LoggerFactory.getLogger(CompatibleSwitchManager.class);
+
+    private DataBrokerService _dataService;
+
+    public DataBrokerService getDataService() {
+        return this._dataService;
+    }
+
+    public void setDataService(final DataBrokerService dataService) {
+        this._dataService = dataService;
+    }
+
+    @Override
+    public Status addNodeConnectorProp(final NodeConnector nodeConnector, final Property prop) {
+        final DataModificationTransaction it = getDataService().beginTransaction();
+        final NodeConnectorRef path = NodeMapping.toNodeConnectorRef(nodeConnector);
+        return null;
+    }
+
+    @Override
+    public Property createProperty(final String propName, final String propValue) {
+        try {
+            if (propName.equalsIgnoreCase(Description.propertyName)) {
+                return new Description(propValue);
+            } else if (propName.equalsIgnoreCase(Tier.TierPropName)) {
+                return new Tier(Integer.parseInt(propValue));
+            } else if (propName.equalsIgnoreCase(Bandwidth.BandwidthPropName)) {
+                return new Bandwidth(Long.parseLong(propValue));
+            } else if (propName.equalsIgnoreCase(ForwardingMode.name)) {
+                return new ForwardingMode(Integer.parseInt(propValue));
+            } else if (propName.equalsIgnoreCase(MacAddress.name)) {
+                return new MacAddress(propValue);
+            } else {
+                LOG.debug("Not able to create {} property", propName);
+            }
+        } catch (Exception e) {
+            LOG.debug("createProperty caught exception {}", e.getMessage());
+        }
+
+        return null;
+    }
+
+    @Override
+    public boolean doesNodeConnectorExist(final NodeConnector nc) {
+        return (getDataService().readOperationalData(NodeMapping.toNodeConnectorRef(nc).getValue()) != null);
+    }
+
+    @Override
+    public byte[] getControllerMAC() {
+        final Enumeration<NetworkInterface> nis;
+        try {
+            nis = NetworkInterface.getNetworkInterfaces();
+        } catch (SocketException e) {
+            LOG.error("Failed to acquire list of interfaces, cannot determine controller MAC", e);
+            return null;
+        }
+
+        while (nis.hasMoreElements()) {
+            final NetworkInterface ni = nis.nextElement();
+            try {
+                return ni.getHardwareAddress();
+            } catch (SocketException e) {
+                LOG.error("Failed to acquire controller MAC from interface {}", ni, e);
+            }
+        }
+
+        // This happens when running controller on windows VM, for example
+        // Try parsing the OS command output
+        LOG.warn("Failed to acquire controller MAC: No physical interface found");
+        return null;
+    }
+
+    @Override
+    public Map<String,Property> getControllerProperties() {
+        return Collections.<String, Property>emptyMap();
+    }
+
+    @Override
+    public Property getControllerProperty(final String propertyName) {
+        return null;
+    }
+
+    @Override
+    public List<Switch> getNetworkDevices() {
+        final InstanceIdentifier<Nodes> path = InstanceIdentifier.builder(Nodes.class).toInstance();
+        final Nodes data = ((Nodes) getDataService().readOperationalData(path));
+        final ArrayList<Switch> ret = new ArrayList<>();
+        for (final Node node : data.getNode()) {
+            try {
+                ret.add(toSwitch(node));
+            } catch (ConstructionException e) {
+                throw new IllegalStateException(String.format("Failed to create switch {}", node), e);
+            }
+        }
+        return ret;
+    }
+
+    @Override
+    public NodeConnector getNodeConnector(final org.opendaylight.controller.sal.core.Node node, final String nodeConnectorName) {
+        final NodeConnectorKey key = new NodeConnectorKey(new NodeConnectorId(nodeConnectorName));
+        try {
+            return new NodeConnector(NodeMapping.MD_SAL_TYPE, key, node);
+        } catch (ConstructionException e) {
+            throw new IllegalStateException(String.format("Failed to create node connector for {} {}", node, nodeConnectorName), e);
+        }
+    }
+
+    @Override
+    public Property getNodeConnectorProp(final NodeConnector nodeConnector, final String propName) {
+        return getNodeConnectorProps(nodeConnector).get(propName);
+    }
+
+    @Override
+    public Map<String,Property> getNodeConnectorProps(final NodeConnector nodeConnector) {
+        final NodeConnectorRef ref = NodeMapping.toNodeConnectorRef(nodeConnector);
+        return toAdProperties(readNodeConnector(ref.getValue()));
+    }
+
+    @Override
+    public Set<NodeConnector> getNodeConnectors(final org.opendaylight.controller.sal.core.Node node) {
+        final Node data = this.readNode(NodeMapping.toNodeRef(node).getValue());
+        final HashSet<NodeConnector> ret = new HashSet<>();
+        for (final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector nc : data.getNodeConnector()) {
+            try {
+                ret.add(new NodeConnector(NodeMapping.MD_SAL_TYPE, nc.getKey(), node));
+            } catch (ConstructionException e) {
+                throw new IllegalStateException(String.format("Failed to create node {} connector", node, nc.getKey()), e);
+            }
+        }
+        return ret;
+    }
+
+    @Override
+    public String getNodeDescription(final org.opendaylight.controller.sal.core.Node node) {
+        return ((Description) getNodeProps(node).get(Description.propertyName)).getValue();
+    }
+
+    @Override
+    public byte[] getNodeMAC(final org.opendaylight.controller.sal.core.Node node) {
+        return ((MacAddress) getNodeProps(node).get(MacAddress.name)).getMacAddress();
+    }
+
+    @Override
+    public Property getNodeProp(final org.opendaylight.controller.sal.core.Node node, final String propName) {
+        return getNodeProps(node).get(propName);
+    }
+
+    @Override
+    public Map<String,Property> getNodeProps(final org.opendaylight.controller.sal.core.Node node) {
+        final NodeRef ref = NodeMapping.toNodeRef(node);
+        return toAdProperties(((Node) getDataService().readOperationalData(ref.getValue())));
+    }
+
+    @Override
+    public Set<org.opendaylight.controller.sal.core.Node> getNodes() {
+        final InstanceIdentifier<Nodes> path = InstanceIdentifier.builder(Nodes.class).toInstance();
+        final Nodes data = ((Nodes) getDataService().readOperationalData(path));
+        final HashSet<org.opendaylight.controller.sal.core.Node> ret = new HashSet<>();
+        for (final Node node : data.getNode()) {
+            try {
+                ret.add(new org.opendaylight.controller.sal.core.Node(NodeMapping.MD_SAL_TYPE, node.getKey()));
+            } catch (ConstructionException e) {
+                throw new IllegalStateException(String.format("Failed to create node for {}", node), e);
+            }
+        }
+        return ret;
+    }
+
+    private static Switch toSwitch(final Node node) throws ConstructionException {
+        return new Switch(new org.opendaylight.controller.sal.core.Node(NodeMapping.MD_SAL_TYPE, node.getKey()));
+    }
+
+    @Override
+    public Set<NodeConnector> getPhysicalNodeConnectors(final org.opendaylight.controller.sal.core.Node node) {
+        final NodeRef ref = NodeMapping.toNodeRef(node);
+        final Node data = readNode(ref.getValue());
+        final HashSet<NodeConnector> ret = new HashSet<>();
+        for (final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector nc : data.getNodeConnector()) {
+            final FlowCapableNodeConnector flowConnector = nc.getAugmentation(FlowCapableNodeConnector.class);
+            try {
+                ret.add(new NodeConnector(NodeMapping.MD_SAL_TYPE, nc.getKey(), node));
+            } catch (ConstructionException e) {
+                throw new IllegalStateException(String.format("Failed to create connector for {} on node {}", nc.getKey(), node), e);
+            }
+        }
+        return ret;
+    }
+
+    private static Map<String,Property> toAdProperties(final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector connector) {
+        return Collections.emptyMap();
+    }
+
+    private static Map<String,Property> toAdProperties(final Node connector) {
+        return Collections.emptyMap();
+    }
+
+    private Node readNode(final InstanceIdentifier<? extends Object> ref) {
+        return (Node) getDataService().readOperationalData((ref));
+    }
+
+    private org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector readNodeConnector(final InstanceIdentifier<? extends Object> ref) {
+        return ((org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector) getDataService().readOperationalData(ref));
+    }
+
+    @Override
+    public List<NodeConnector> getSpanPorts(final org.opendaylight.controller.sal.core.Node node) {
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
+    }
+
+    @Override
+    public Subnet getSubnetByNetworkAddress(final InetAddress networkAddress) {
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
+    }
+
+    @Override
+    public Set<NodeConnector> getUpNodeConnectors(final org.opendaylight.controller.sal.core.Node node) {
+        final Node data = readNode(NodeMapping.toNodeRef(node).getValue());
+        final HashSet<NodeConnector> ret = new HashSet<>();
+        for (final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector nc : data.getNodeConnector()) {
+            final FlowCapableNodeConnector flowConn = nc.<FlowCapableNodeConnector>getAugmentation(FlowCapableNodeConnector.class);
+            if (flowConn != null && flowConn.getState() != null && !flowConn.getState().isLinkDown()) {
+                try {
+                    ret.add(new NodeConnector(NodeMapping.MD_SAL_TYPE, nc.getKey(), node));
+                } catch (ConstructionException e) {
+                    throw new IllegalStateException(String.format("Failed to create node connector for node {} connector {}", node, nc), e);
+                }
+            }
+        }
+        return ret;
+    }
+
+    @Override
+    public Boolean isNodeConnectorEnabled(final NodeConnector nodeConnector) {
+        final NodeConnectorRef ref = NodeMapping.toNodeConnectorRef(nodeConnector);
+        final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector data = readNodeConnector(ref.getValue());
+        return true;
+    }
+
+    @Override
+    public boolean isSpecial(final NodeConnector p) {
+        final NodeConnectorRef ref = NodeMapping.toNodeConnectorRef(p);
+        final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector data = readNodeConnector(ref.getValue());
+        return true;
+    }
+
+    @Override
+    public Status removeControllerProperty(final String propertyName) {
+        return null;
+    }
+
+    @Override
+    public Status removeNodeAllProps(final org.opendaylight.controller.sal.core.Node node) {
+        return null;
+    }
+
+    @Override
+    public Status removeNodeConnectorAllProps(final NodeConnector nodeConnector) {
+        return null;
+    }
+
+    @Override
+    public Status removeNodeConnectorProp(final NodeConnector nc, final String propName) {
+        return null;
+    }
+
+    @Override
+    public Status removeNodeProp(final org.opendaylight.controller.sal.core.Node node, final String propName) {
+        return null;
+    }
+
+    @Override
+    public Status removePortsFromSubnet(final String name, final List<String> nodeConnectors) {
+        return null;
+    }
+
+    @Override
+    public Status removeSubnet(final String name) {
+        return null;
+    }
+
+    @Override
+    public Status setControllerProperty(final Property property) {
+        return null;
+    }
+
+    @Override
+    public void setNodeProp(final org.opendaylight.controller.sal.core.Node node, final Property prop) {
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
+    }
+
+    @Override
+    public Status addPortsToSubnet(final String name, final List<String> nodeConnectors) {
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
+    }
+
+    @Override
+    public Set<Switch> getConfiguredNotConnectedSwitches() {
+        return null;
+    }
+}
diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/switchmanager/CompatibleSwitchManager.xtend b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/switchmanager/CompatibleSwitchManager.xtend
deleted file mode 100644 (file)
index 20d375f..0000000
+++ /dev/null
@@ -1,298 +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.compatibility.switchmanager
-
-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 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.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.flow.inventory.rev130819.FlowCapableNodeConnector
-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 {
-
-    private static val log = LoggerFactory.getLogger(CompatibleSwitchManager)
-
-    @org.eclipse.xtend.lib.Property
-    var DataBrokerService dataService;
-
-    override addNodeConnectorProp(NodeConnector nodeConnector, Property prop) {
-        val it = dataService.beginTransaction
-        val path = nodeConnector.toNodeConnectorRef
-
-        // TODO: Update FlowCapableNode
-        return null;
-    }
-
-    override createProperty(String propName, String propValue) {
-        try {
-            if (propName.equalsIgnoreCase(Description.propertyName)) {
-                return new Description(propValue);
-            } else if (propName.equalsIgnoreCase(Tier.TierPropName)) {
-                val tier = Integer.parseInt(propValue);
-                return new Tier(tier);
-            } else if (propName.equalsIgnoreCase(Bandwidth.BandwidthPropName)) {
-                val bw = Long.parseLong(propValue);
-                return new Bandwidth(bw);
-            } else if (propName.equalsIgnoreCase(ForwardingMode.name)) {
-                val mode = Integer.parseInt(propValue);
-                return new ForwardingMode(mode);
-            } else if (propName.equalsIgnoreCase(MacAddress.name)) {
-                return new MacAddress(propValue);
-            } else {
-                log.debug("Not able to create {} property", propName);
-            }
-        } catch (Exception e) {
-            log.debug("createProperty caught exception {}", e.getMessage());
-        }
-        return null;
-    }
-
-    override doesNodeConnectorExist(NodeConnector nc) {
-        val ref = nc.toNodeConnectorRef
-        return dataService.readOperationalData(ref.value as InstanceIdentifier<? extends DataObject>) !== null
-    }
-
-    override getControllerMAC() {
-        var byte[] macAddress = null;
-
-        try {
-            val nis = NetworkInterface.getNetworkInterfaces();
-            while (nis.hasMoreElements()) {
-                val ni = nis.nextElement();
-                try {
-                    macAddress = ni.getHardwareAddress();
-                    return macAddress;
-                } catch (SocketException e) {
-                    log.error("Failed to acquire controller MAC: ", e);
-                }
-            }
-        } catch (SocketException e) {
-            log.error("Failed to acquire controller MAC: ", e);
-            return macAddress;
-        }
-
-        if (macAddress == null) {
-            log.warn("Failed to acquire controller MAC: No physical interface found");
-
-            // This happens when running controller on windows VM, for example
-            // Try parsing the OS command output
-            }
-            return macAddress;
-        }
-
-    override getControllerProperties() {
-        return Collections.emptyMap()
-    }
-
-    override getControllerProperty(String propertyName) {
-        return null;
-    }
-
-    override getNetworkDevices() {
-        val path = InstanceIdentifier.builder(Nodes).toInstance;
-        val data = dataService.readOperationalData(path) as Nodes;
-        val ret = new ArrayList<Switch>();
-        for (node : data.node) {
-            ret.add(node.toSwitch());
-        }
-        return ret;
-    }
-
-    override getNodeConnector(Node node, String nodeConnectorName) {
-        val key = new NodeConnectorKey(new NodeConnectorId(nodeConnectorName));
-        return new NodeConnector(MD_SAL_TYPE, key, node);
-    }
-
-    override getNodeConnectorProp(NodeConnector nodeConnector, String propName) {
-        getNodeConnectorProps(nodeConnector).get(propName);
-    }
-
-    override getNodeConnectorProps(NodeConnector nodeConnector) {
-        val ref = nodeConnector.toNodeConnectorRef
-        val data = readNodeConnector(ref.value);
-        return data.toAdProperties();
-    }
-
-    override getNodeConnectors(Node node) {
-        val ref = node.toNodeRef;
-        val data = readNode(ref.value);
-        val ret = new HashSet();
-        for (nc : data.nodeConnector) {
-
-            val adConnector = new NodeConnector(MD_SAL_TYPE, nc.key, node);
-            ret.add(adConnector);
-        }
-        return ret;
-    }
-
-    override getNodeDescription(Node node) {
-        (getNodeProps(node).get(Description.propertyName) as Description).value;
-    }
-
-    override getNodeMAC(Node node) {
-        (getNodeProps(node).get(MacAddress.name) as MacAddress).macAddress;
-    }
-
-    override getNodeProp(Node node, String propName) {
-        getNodeProps(node).get(propName)
-    }
-
-    override getNodeProps(Node node) {
-        val ref = node.toNodeRef;
-        val data = dataService.readOperationalData(ref.value as InstanceIdentifier<? extends DataObject>) as org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-        return data.toAdProperties();
-    }
-
-    override getNodes() {
-        val path = InstanceIdentifier.builder(Nodes).toInstance;
-        val data = dataService.readOperationalData(path) as Nodes;
-        val ret = new HashSet<Node>();
-        for (node : data.node) {
-            ret.add(new Node(MD_SAL_TYPE, node.key));
-        }
-        return ret;
-    }
-
-    def Switch toSwitch(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node node) {
-        val adNode = new Node(MD_SAL_TYPE, node.key);
-        val sw = new Switch(adNode)
-        return sw;
-    }
-
-    override getPhysicalNodeConnectors(Node node) {
-        val ref = node.toNodeRef;
-        val data = readNode(ref.value);
-        val ret = new HashSet();
-        for (nc : data.nodeConnector) {
-            val flowConnector = nc.getAugmentation(FlowCapableNodeConnector)
-            val adConnector = new NodeConnector(MD_SAL_TYPE, nc.key, node);
-            ret.add(adConnector);
-        }
-        return ret;
-    }
-
-    def Map<String, Property> toAdProperties(
-        org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector connector) {
-        return Collections.emptyMap
-    }
-
-    def Map<String, Property> toAdProperties(
-        org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node connector) {
-        return Collections.emptyMap
-    }
-
-    def readNode(InstanceIdentifier<?> ref) {
-        dataService.readOperationalData(ref as InstanceIdentifier<? extends DataObject>) as org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node
-    }
-
-    def readNodeConnector(InstanceIdentifier<?> ref) {
-        dataService.readOperationalData(ref as InstanceIdentifier<? extends DataObject>) as org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector
-    }
-
-    override getSpanPorts(Node node) {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-    }
-
-    override getSubnetByNetworkAddress(InetAddress networkAddress) {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-    }
-
-    override getUpNodeConnectors(Node node) {
-        val ref = node.toNodeRef
-        val data = readNode(ref.value);
-        val ret = new HashSet<NodeConnector>();
-        for (nc : data.nodeConnector) {
-            val flowConn = nc.getAugmentation(FlowCapableNodeConnector);
-            if (flowConn != null && flowConn.state != null && !flowConn.state.linkDown) {
-                ret.add(new NodeConnector(MD_SAL_TYPE, nc.key, node));
-            }
-        }
-        return ret;
-    }
-
-    override isNodeConnectorEnabled(NodeConnector nodeConnector) {
-        val ref = nodeConnector.toNodeConnectorRef
-        val data = readNodeConnector(ref.value);
-
-        return true;
-    }
-
-    override isSpecial(NodeConnector p) {
-        val ref = p.toNodeConnectorRef
-        val data = readNodeConnector(ref.value);
-
-        return true;
-    }
-
-    override removeControllerProperty(String propertyName) {
-        // NOOP
-    }
-
-    override removeNodeAllProps(Node node) {
-        // NOOP: not supported node has more properties than AD-SAL is capable to see
-    }
-
-    override removeNodeConnectorAllProps(NodeConnector nodeConnector) {
-        // NOOP: not supported node has more properties than AD-SAL is capable to see
-    }
-
-    override removeNodeConnectorProp(NodeConnector nc, String propName) {
-        // NOOP: not supported node has more properties than AD-SAL is capable to see
-    }
-
-    override removeNodeProp(Node node, String propName) {
-        // NOOP: not supported node has more properties than AD-SAL is capable to see
-    }
-
-    override removePortsFromSubnet(String name, List<String> nodeConnectors) {
-        // NOOP
-    }
-
-    override removeSubnet(String name) {
-        // NOOP
-    }
-
-    override setControllerProperty(Property property) {
-        // NOOP
-    }
-
-    override setNodeProp(Node node, Property prop) {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-    }
-
-    override addPortsToSubnet(String name, List<String> nodeConnectors) {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-    }
-
-    override getConfiguredNotConnectedSwitches() {
-        return null;
-    }
-}
diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/switchmanager/ConfigurableSwitchManager.java b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/switchmanager/ConfigurableSwitchManager.java
new file mode 100644 (file)
index 0000000..63f682a
--- /dev/null
@@ -0,0 +1,86 @@
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.compatibility.switchmanager;
+
+import java.util.List;
+
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.switchmanager.ISwitchManager;
+import org.opendaylight.controller.switchmanager.SpanConfig;
+import org.opendaylight.controller.switchmanager.SubnetConfig;
+import org.opendaylight.controller.switchmanager.SwitchConfig;
+
+/**
+ * These methods should be backed by config subsystem.
+ */
+public abstract class ConfigurableSwitchManager implements ISwitchManager {
+    @Override
+    public Status saveSwitchConfig() {
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
+    }
+
+    @Override
+    public Status removeSpanConfig(final SpanConfig cfgObject) {
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
+    }
+
+    @Override
+    public Status addSubnet(final SubnetConfig configObject) {
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
+    }
+
+    @Override
+    public final Status addSpanConfig(final SpanConfig configObject) {
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
+    }
+
+    @Override
+    public final List<SpanConfig> getSpanConfigList() {
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
+    }
+
+    @Override
+    public final void updateSwitchConfig(final SwitchConfig cfgObject) {
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
+    }
+
+    @Override
+    public final Status updateNodeConfig(final SwitchConfig switchConfig) {
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
+    }
+
+    @Override
+    public final SubnetConfig getSubnetConfig(final String subnet) {
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
+    }
+
+    @Override
+    public final Status removeNodeConfig(final String nodeId) {
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
+    }
+
+    @Override
+    public final Status removeSubnet(final SubnetConfig configObject) {
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
+    }
+
+    @Override
+    public final List<SubnetConfig> getSubnetsConfigList() {
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
+    }
+
+    @Override
+    public final SwitchConfig getSwitchConfig(final String nodeId) {
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
+    }
+
+    @Override
+    public Status modifySubnet(final SubnetConfig configObject) {
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
+    }
+}
diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/switchmanager/ConfigurableSwitchManager.xtend b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/switchmanager/ConfigurableSwitchManager.xtend
deleted file mode 100644 (file)
index faa4b8d..0000000
+++ /dev/null
@@ -1,78 +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.compatibility.switchmanager
-
-import org.opendaylight.controller.switchmanager.ISwitchManager
-import org.opendaylight.controller.switchmanager.SpanConfig
-import org.opendaylight.controller.switchmanager.SwitchConfig
-import org.opendaylight.controller.switchmanager.SubnetConfig
-
-/**
- * 
- * THis methods should be backed by config subsystem.
- * 
- */
-abstract class ConfigurableSwitchManager implements ISwitchManager {
-
-    override saveSwitchConfig() {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-    }
-
-    override removeSpanConfig(SpanConfig cfgObject) {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-    }
-
-    override addSubnet(SubnetConfig configObject) {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-
-    }
-
-    final override addSpanConfig(SpanConfig configObject) {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-
-    }
-
-    final override getSpanConfigList() {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-
-    }
-
-    final override updateSwitchConfig(SwitchConfig cfgObject) {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-
-    }
-
-    final override updateNodeConfig(SwitchConfig switchConfig) {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-
-    }
-
-    final override getSubnetConfig(String subnet) {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-    }
-
-    final override removeNodeConfig(String nodeId) {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-    }
-
-    final override removeSubnet(SubnetConfig configObject) {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-    }
-
-    final override getSubnetsConfigList() {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-    }
-
-    final override getSwitchConfig(String nodeId) {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-    }
-
-    override modifySubnet(SubnetConfig configObject) {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-    }
-}
diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyMapping.java b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyMapping.java
new file mode 100644 (file)
index 0000000..8e50bd8
--- /dev/null
@@ -0,0 +1,82 @@
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.compatibility.topology;
+
+import java.util.Iterator;
+
+import org.opendaylight.controller.sal.compatibility.InventoryMapping;
+import org.opendaylight.controller.sal.core.ConstructionException;
+import org.opendaylight.controller.sal.core.Edge;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.LinkId;
+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.TpId;
+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.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkKey;
+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.NodeKey;
+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.TerminationPointKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+
+import com.google.common.base.Splitter;
+
+public class TopologyMapping {
+    private static final String HEAD_TAIL_STRING = "::::";
+    private static final Splitter HEAD_TAIL_SPLITTER = Splitter.on(HEAD_TAIL_STRING);
+
+    public TopologyMapping(final TopologyKey path, final InstanceIdentifier<Topology> key) {
+        // No-op for now. Multi-instance will require fixing InventoryMapping first.
+    }
+
+    public Edge toAdTopologyEdge(final InstanceIdentifier<Link> identifier) throws ConstructionException {
+        @SuppressWarnings("unchecked")
+        final LinkKey linkKey = ((KeyedInstanceIdentifier<Link, LinkKey>)identifier).getKey();
+
+        final Iterator<String> it = HEAD_TAIL_SPLITTER.split(linkKey.getLinkId().getValue()).iterator();
+        final NodeConnector tail = InventoryMapping.nodeConnectorFromId(it.next());
+        final NodeConnector head = InventoryMapping.nodeConnectorFromId(it.next());
+        return new Edge(tail, head);
+    }
+
+    public NodeConnector toAdTopologyNodeConnector(final InstanceIdentifier<TerminationPoint> identifier) {
+        @SuppressWarnings("unchecked")
+        final TerminationPointKey tpKey = ((KeyedInstanceIdentifier<TerminationPoint, TerminationPointKey>)identifier).getKey();
+
+        return InventoryMapping.nodeConnectorFromId(tpKey.getTpId().getValue());
+    }
+
+    public org.opendaylight.controller.sal.core.Node toAdTopologyNode(final InstanceIdentifier<Node> identifier) {
+        @SuppressWarnings("unchecked")
+        final NodeKey nodeKey = ((KeyedInstanceIdentifier<Node, NodeKey>)identifier).getKey();
+
+        return InventoryMapping.nodeFromNodeId(nodeKey.getNodeId().getValue());
+    }
+
+    public NodeKey toTopologyNodeKey(final org.opendaylight.controller.sal.core.Node node) {
+        return new NodeKey(new NodeId(InventoryMapping.toNodeId(node)));
+    }
+
+    public TerminationPointKey toTopologyTerminationPointKey(final NodeConnector nc) {
+        return new TerminationPointKey(new TpId(InventoryMapping.toNodeConnectorId(nc)));
+    }
+
+    public LinkKey toTopologyLinkKey(final Edge edge) {
+        final TerminationPointKey sourceTp = toTopologyTerminationPointKey(edge.getTailNodeConnector());
+        final TerminationPointKey destTp = toTopologyTerminationPointKey(edge.getHeadNodeConnector());
+
+        final StringBuilder sb = new StringBuilder();
+        sb.append(sourceTp.getTpId().toString());
+        sb.append(HEAD_TAIL_STRING);
+        sb.append(destTp.getTpId().toString());
+        return new LinkKey(new LinkId(sb.toString()));
+    }
+}
diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyMapping.xtend b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyMapping.xtend
deleted file mode 100644 (file)
index 0889de1..0000000
+++ /dev/null
@@ -1,74 +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.compatibility.topology
-
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
-import org.opendaylight.yangtools.yang.binding.DataObject
-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
-import org.opendaylight.controller.sal.core.Edge
-import java.util.Set
-import org.opendaylight.controller.sal.core.Property
-import org.opendaylight.controller.sal.core.NodeConnector
-
-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.topology.node.TerminationPointKey
-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.NodeKey
-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.LinkKey
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.LinkId
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node
-import org.opendaylight.controller.sal.compatibility.InventoryMapping
-class TopologyMapping {
-
-    new(TopologyKey path, InstanceIdentifier<Topology> key) {
-        // NOOP
-    }
-
-    def Edge toAdTopologyEdge(InstanceIdentifier<Link> identifier) {
-        val linkKey = (identifier.path.last as IdentifiableItem<Link,LinkKey>).key;
-        val components = linkKey.linkId.value.split("::::");
-        val tail = InventoryMapping.nodeConnectorFromId(components.get(0));
-        val head = InventoryMapping.nodeConnectorFromId(components.get(1));
-        return new Edge(tail, head);
-    }
-
-    def NodeConnector toAdTopologyNodeConnector(InstanceIdentifier<TerminationPoint> identifier) {
-        val tpKey = (identifier.path.last as IdentifiableItem<TerminationPoint,TerminationPointKey>).key;
-        return InventoryMapping.nodeConnectorFromId(tpKey.tpId.value);
-    }
-
-    def org.opendaylight.controller.sal.core.Node toAdTopologyNode(
-        InstanceIdentifier<Node> identifier) {
-        val tpKey = (identifier.path.last as IdentifiableItem<Node,NodeKey>).key;
-        return InventoryMapping.nodeFromNodeId(tpKey.nodeId.value);
-    }
-    
-
-
-    def NodeKey toTopologyNodeKey(org.opendaylight.controller.sal.core.Node node) {
-        val nodeId = new NodeId(InventoryMapping.toNodeId(node));
-        return new NodeKey(nodeId);
-    }
-
-    def TerminationPointKey toTopologyTerminationPointKey(NodeConnector nc) {
-        val node = nc.node;
-        val nodeId = new TpId(InventoryMapping.toNodeConnectorId(nc))
-        return new TerminationPointKey(nodeId);
-    }
-
-    def LinkKey toTopologyLinkKey(Edge edge) {
-        val sourceTp = edge.tailNodeConnector.toTopologyTerminationPointKey;
-        val destTp = edge.headNodeConnector.toTopologyTerminationPointKey;
-        val linkId = new LinkId('''«sourceTp.tpId»::::«destTp.tpId»''')
-        return new LinkKey(linkId);
-    }
-}
diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyReader.java b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyReader.java
new file mode 100644 (file)
index 0000000..a4ac6f9
--- /dev/null
@@ -0,0 +1,199 @@
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.compatibility.topology;
+
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider;
+import org.opendaylight.controller.sal.core.ConstructionException;
+import org.opendaylight.controller.sal.core.Edge;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.Property;
+import org.opendaylight.controller.switchmanager.ISwitchManager;
+import org.opendaylight.controller.topologymanager.ITopologyManager;
+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.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.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.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.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.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TopologyReader implements RuntimeDataProvider {
+    private static final Logger LOG = LoggerFactory.getLogger(TopologyReader.class);
+    private final InstanceIdentifier<Topology> topologyPath;
+    private final TopologyKey topologyKey;
+    private final TopologyMapping mapping;
+    private ITopologyManager topologyManager;
+    private ISwitchManager switchManager;
+
+    public ISwitchManager getSwitchManager() {
+        return this.switchManager;
+    }
+
+    public void setSwitchManager(final ISwitchManager switchManager) {
+        this.switchManager = switchManager;
+    }
+
+    public ITopologyManager getTopologyManager() {
+        return this.topologyManager;
+    }
+
+    public void setTopologyManager(final ITopologyManager topologyManager) {
+        this.topologyManager = topologyManager;
+    }
+
+    public TopologyKey getTopologyKey() {
+        return this.topologyKey;
+    }
+
+    public TopologyMapping getMapping() {
+        return this.mapping;
+    }
+
+    public TopologyReader() {
+        this.topologyKey = new TopologyKey(new TopologyId("compatibility:ad-sal"));
+        this.topologyPath = InstanceIdentifier.builder(NetworkTopology.class)
+                .child(Topology.class, topologyKey)
+                .toInstance();
+        this.mapping = new TopologyMapping(topologyKey, topologyPath);
+    }
+
+    @Override
+    public DataObject readConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
+        // Topology and Inventory are operational only
+        return null;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public DataObject readOperationalData(final InstanceIdentifier<? extends DataObject> path) {
+        if (!topologyPath.contains(path)) {
+            return null;
+        }
+
+        final Class<? extends DataObject> type = path.getTargetType();
+        if (Link.class.equals(type)) {
+            return readLink((InstanceIdentifier<Link>) path);
+        }
+        if (Node.class.equals(type)) {
+            return readNode((InstanceIdentifier<Node>) path);
+        }
+        if (TerminationPoint.class.equals(type)) {
+            return readTerminationPoint((InstanceIdentifier<TerminationPoint>) path);
+
+        }
+        if (Topology.class.equals(type)) {
+            return readTopology((InstanceIdentifier<Topology>) path);
+        }
+
+        LOG.debug("Unsupported type {}", type);
+        return null;
+    }
+
+    private Link readLink(final InstanceIdentifier<Link> identifier) {
+        final Edge edge;
+        try {
+            edge = this.mapping.toAdTopologyEdge(identifier);
+        } catch (ConstructionException e) {
+            throw new IllegalStateException(String.format("Failed to construct edge for link %s", identifier), e);
+        }
+
+        final Map<Edge,Set<Property>> edges;
+        if (topologyManager != null) {
+            edges = topologyManager.getEdges();
+        } else {
+            edges = null;
+        }
+
+        final Set<Property> properties;
+        if (edges != null) {
+            properties = edges.get(edge);
+        } else {
+            properties = null;
+        }
+
+        return constructLink(edge);
+    }
+
+    private TerminationPoint readTerminationPoint(final InstanceIdentifier<TerminationPoint> identifier) {
+        return constructTerminationPoint(mapping.toAdTopologyNodeConnector(identifier));
+    }
+
+    private Node readNode(final InstanceIdentifier<Node> identifier) {
+        return constructNode(mapping.toAdTopologyNode(identifier));
+    }
+
+    private Topology readTopology(final InstanceIdentifier<Topology> identifier) {
+        final Set<org.opendaylight.controller.sal.core.Node> nodes = getSwitchManager().getNodes();
+        final ArrayList<Node> nodeList = new ArrayList<Node>(nodes.size());
+        for (final org.opendaylight.controller.sal.core.Node node : nodes) {
+            nodeList.add(constructNode(node));
+        }
+
+        final Map<Edge,Set<Property>> edges = getTopologyManager().getEdges();
+        final ArrayList<Link> linkList = new ArrayList<Link>(edges.size());
+        for (final Edge edge : edges.keySet()) {
+            linkList.add(constructLink(edge));
+        }
+
+        return new TopologyBuilder()
+        .setKey(topologyKey)
+        .setNode(nodeList)
+        .setLink(linkList)
+        .build();
+    }
+
+    private Link constructLink(final Edge edge) {
+        final NodeConnector sourceNc = edge.getTailNodeConnector();
+        final NodeConnector destNc = edge.getHeadNodeConnector();
+
+        final LinkBuilder it = new LinkBuilder().setKey(mapping.toTopologyLinkKey(edge));
+
+        it.setSource(new SourceBuilder()
+        .setSourceNode(mapping.toTopologyNodeKey(sourceNc.getNode()).getNodeId())
+        .setSourceTp(mapping.toTopologyTerminationPointKey(sourceNc).getTpId())
+        .build());
+
+        it.setDestination(new DestinationBuilder()
+        .setDestNode(mapping.toTopologyNodeKey(destNc.getNode()).getNodeId())
+        .setDestTp(mapping.toTopologyTerminationPointKey(destNc).getTpId())
+        .build());
+
+        return it.build();
+    }
+
+    private Node constructNode(final org.opendaylight.controller.sal.core.Node node) {
+        final Set<NodeConnector> connectors = getSwitchManager().getNodeConnectors(node);
+        final ArrayList<TerminationPoint> tpList = new ArrayList<TerminationPoint>(connectors.size());
+        for (final NodeConnector connector : connectors) {
+            tpList.add(constructTerminationPoint(connector));
+        }
+
+        return new NodeBuilder()
+        .setKey(mapping.toTopologyNodeKey(node))
+        .setTerminationPoint(tpList)
+        .build();
+    }
+
+    private TerminationPoint constructTerminationPoint(final NodeConnector connector) {
+        return new TerminationPointBuilder().setKey(mapping.toTopologyTerminationPointKey(connector)).build();
+    }
+}
diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyReader.xtend b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topology/TopologyReader.xtend
deleted file mode 100644 (file)
index 6ebe20b..0000000
+++ /dev/null
@@ -1,153 +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.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.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.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.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.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.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.yangtools.yang.binding.DataObject
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
-
-class TopologyReader implements RuntimeDataProvider {
-
-    @Property
-    var ISwitchManager switchManager;
-
-    @Property
-    var ITopologyManager topologyManager;
-
-    @Property
-    val TopologyKey topologyKey;
-
-    @Property
-    val InstanceIdentifier<Topology> topologyPath;
-
-    @Property
-    val extension TopologyMapping mapping;
-
-    new() {
-        _topologyKey = new TopologyKey(new TopologyId("compatibility:ad-sal"));
-        _topologyPath = InstanceIdentifier.builder(NetworkTopology).child(Topology, topologyKey).toInstance;
-        _mapping = new TopologyMapping(topologyKey, topologyPath);
-    }
-
-    override readConfigurationData(InstanceIdentifier<? extends DataObject> path) {
-
-        // Topology and Inventory are operational only
-        return null;
-    }
-
-    override readOperationalData(InstanceIdentifier<? extends DataObject> path) {
-        val type = path.targetType;
-        var DataObject data = null;
-        if (false == topologyPath.contains(path)) {
-            return null;
-        }
-        switch (type) {
-            case Topology:
-                data = readTopology(path as InstanceIdentifier<Topology>)
-            case Node:
-                data = readNode(path as InstanceIdentifier<Node>)
-            case TerminationPoint:
-                data = readTerminationPoint(path as InstanceIdentifier<TerminationPoint>)
-            case Link:
-                data = readLink(path as InstanceIdentifier<Link>)
-        }
-        return data;
-    }
-
-    def DataObject readLink(InstanceIdentifier<Link> identifier) {
-        val edge = identifier.toAdTopologyEdge();
-        val properties = topologyManager?.edges?.get(edge);
-
-        return constructLink(edge);
-    }
-
-    def DataObject readTerminationPoint(InstanceIdentifier<TerminationPoint> identifier) {
-        val nodeConnector = identifier.toAdTopologyNodeConnector();
-        return constructTerminationPoint(nodeConnector)
-    }
-
-    def DataObject readNode(InstanceIdentifier<Node> identifier) {
-        val node = identifier.toAdTopologyNode();
-        return constructNode(node);
-    }
-
-    def DataObject readTopology(InstanceIdentifier<Topology> identifier) {
-
-        //val nodeConnectors = switchManager.
-        val nodes = switchManager.nodes
-        val edges = topologyManager.edges
-
-        val nodeList = new ArrayList<Node>(nodes.size)
-        for (node : nodes) {
-            nodeList.add(constructNode(node))
-        }
-
-        val linkList = new ArrayList<Link>(edges.size)
-        for (edge : edges.keySet) {
-            linkList.add(constructLink(edge))
-        }
-
-        val it = new TopologyBuilder();
-        key = topologyKey
-        node = nodeList
-        link = linkList
-        return build()
-    }
-
-    def constructLink(Edge edge) {
-        val sourceNc = edge.tailNodeConnector
-        val destNc = edge.headNodeConnector
-
-        val it = new LinkBuilder()
-        key = edge.toTopologyLinkKey();
-        source = new SourceBuilder().setSourceNode(sourceNc.node.toTopologyNodeKey.nodeId).setSourceTp(
-            sourceNc.toTopologyTerminationPointKey.tpId).build()
-        destination = new DestinationBuilder().setDestNode(destNc.node.toTopologyNodeKey.nodeId).setDestTp(
-            destNc.toTopologyTerminationPointKey.tpId).build
-        return build()
-    }
-
-    def Node constructNode(org.opendaylight.controller.sal.core.Node node) {
-        val connectors = switchManager.getNodeConnectors(node)
-
-        val tpList = new ArrayList<TerminationPoint>(connectors.size)
-        for (connector : connectors) {
-            tpList.add(constructTerminationPoint(connector));
-        }
-
-        val it = new NodeBuilder()
-        key = node.toTopologyNodeKey();
-        terminationPoint = tpList
-        return build();
-    }
-
-    def TerminationPoint constructTerminationPoint(NodeConnector connector) {
-        val it = new TerminationPointBuilder()
-        key = connector.toTopologyTerminationPointKey
-        return build();
-    }
-
-}
diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/AdSalTopologyMapping.java b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/AdSalTopologyMapping.java
new file mode 100644 (file)
index 0000000..a7a7a9a
--- /dev/null
@@ -0,0 +1,109 @@
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.compatibility.topologymanager;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.controller.sal.compatibility.NodeMapping;
+import org.opendaylight.controller.sal.core.ConstructionException;
+import org.opendaylight.controller.sal.core.Edge;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.Property;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+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.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;
+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.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.node.TerminationPointKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class AdSalTopologyMapping {
+    private final InstanceIdentifier<Topology> topologyPath;
+
+    public AdSalTopologyMapping(final TopologyKey topology) {
+        this.topologyPath = InstanceIdentifier.builder(NetworkTopology.class)
+                .child(Topology.class, topology).toInstance();
+    }
+
+    public InstanceIdentifier<Topology> getTopologyPath() {
+        return topologyPath;
+    }
+
+    public InstanceIdentifier<TerminationPoint> toTerminationPoint(final NodeConnector connector) {
+        return getTopologyPath().builder()
+                .child(Node.class)
+                .child(TerminationPoint.class, toTerminationPointKey(connector))
+                .toInstance();
+    }
+
+    public Map<Edge,Set<Property>> toEdgePropertiesMap(final Iterable<Link> links) {
+        final HashMap<Edge,Set<Property>> ret = new HashMap<>();
+        for (final Link link : links) {
+            try {
+                ret.put(toEdge(link), toProperties(link));
+            } catch (ConstructionException e) {
+                throw new IllegalStateException(String.format("Failed to create edge properties for {}", link), e);
+            }
+        }
+        return ret;
+    }
+
+    public static Set<Edge> toEdges(final Iterable<Link> links) throws ConstructionException {
+        final HashSet<Edge> ret = new HashSet<Edge>();
+        for (final Link link : links) {
+            ret.add(toEdge(link));
+        }
+        return ret;
+    }
+
+    public static Edge toEdge(final Link link) throws ConstructionException {
+        final NodeConnector tail = toNodeConnector(link.getSource());
+        final NodeConnector head = toNodeConnector(link.getDestination());
+        return new Edge(tail, head);
+    }
+
+    public static org.opendaylight.controller.sal.core.Node toAdNode(final Node node) throws ConstructionException {
+        return toAdNode(node.getNodeId());
+    }
+
+    public static org.opendaylight.controller.sal.core.Node toAdNode(final NodeId node) throws ConstructionException {
+        final NodeKey key = new NodeKey(
+                new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId(node));
+        return new org.opendaylight.controller.sal.core.Node(NodeMapping.MD_SAL_TYPE, key);
+    }
+
+    public static NodeConnector toNodeConnector(final Source ref) throws ConstructionException {
+        final org.opendaylight.controller.sal.core.Node adNode = toAdNode(ref.getSourceNode());
+        final NodeConnectorKey key = new NodeConnectorKey(new NodeConnectorId(ref.getSourceTp()));
+        return new NodeConnector(NodeMapping.MD_SAL_TYPE, key, adNode);
+    }
+
+    public static NodeConnector toNodeConnector(final Destination ref) throws ConstructionException {
+        final org.opendaylight.controller.sal.core.Node adNode = toAdNode(ref.getDestNode());
+        final NodeConnectorKey key = new NodeConnectorKey(new NodeConnectorId(ref.getDestTp()));
+        return new NodeConnector(NodeMapping.MD_SAL_TYPE, key, adNode);
+    }
+
+    public TerminationPointKey toTerminationPointKey(final NodeConnector connector) {
+        return null;
+    }
+
+    public Set<Property> toProperties(final Link link) {
+        return null;
+    }
+}
diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/AdSalTopologyMapping.xtend b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/AdSalTopologyMapping.xtend
deleted file mode 100644 (file)
index aa238a8..0000000
+++ /dev/null
@@ -1,96 +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.compatibility.topologymanager
-
-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
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint
-import org.opendaylight.controller.sal.core.NodeConnector
-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.NetworkTopology
-import java.util.Map
-import org.opendaylight.controller.sal.core.Edge
-import java.util.Set
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node
-import java.util.HashSet
-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
-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.link.attributes.Source
-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.network.topology.topology.node.TerminationPointKey
-import java.util.HashMap
-
-class AdSalTopologyMapping {
-
-    val TopologyKey topologyMapping;
-    @Property
-    val InstanceIdentifier<Topology> topologyPath;
-
-    new(TopologyKey topology) {
-        topologyMapping = topology;
-        _topologyPath = InstanceIdentifier.builder(NetworkTopology).child(Topology, topology).toInstance;
-    }
-
-    def InstanceIdentifier<TerminationPoint> toTerminationPoint(NodeConnector connector) {
-        InstanceIdentifier.builder(topologyPath).child(Node).child(TerminationPoint, connector.toTerminationPointKey()).toInstance;
-    }
-
-    def Map<Edge, Set<org.opendaylight.controller.sal.core.Property>> toEdgePropertiesMap(Iterable<Link> links) {
-        val ret = new HashMap<Edge, Set<org.opendaylight.controller.sal.core.Property>>
-        for (link : links) {
-            ret.put(link.toEdge(), link.toProperties())
-        }
-        return ret;
-    }
-
-    def Set<Edge> toEdges(Iterable<Link> links) {
-        val ret = new HashSet<Edge>
-        for (link : links) {
-            ret.add(link.toEdge)
-        }
-        return ret;
-    }
-
-    def Edge toEdge(Link link) {
-        val tail = link.source.toNodeConnector();
-        val head = link.destination.toNodeConnector();
-        return new Edge(tail, head);
-    }
-
-    def org.opendaylight.controller.sal.core.Node toAdNode(Node node) {
-        return node.nodeId.toAdNode;
-    }
-
-    def org.opendaylight.controller.sal.core.Node toAdNode(
-        org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId node) {
-        val key = new NodeKey(new NodeId(node))
-        return new org.opendaylight.controller.sal.core.Node(NodeMapping.MD_SAL_TYPE, key);
-    }
-
-    def NodeConnector toNodeConnector(Source ref) {
-        val adNode = ref.sourceNode.toAdNode();
-        val key = new NodeConnectorKey(new NodeConnectorId(ref.sourceTp))
-        return new NodeConnector(NodeMapping.MD_SAL_TYPE, key, adNode);
-    }
-
-    def NodeConnector toNodeConnector(Destination ref) {
-        val adNode = ref.destNode.toAdNode();
-        val key = new NodeConnectorKey(new NodeConnectorId(ref.destTp))
-        return new NodeConnector(NodeMapping.MD_SAL_TYPE, key, adNode);
-    }
-
-    def TerminationPointKey toTerminationPointKey(NodeConnector connector) {
-    }
-
-    def Set<org.opendaylight.controller.sal.core.Property> toProperties(Link link) {
-    }
-}
diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/CompatibleTopologyManager.java b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/CompatibleTopologyManager.java
new file mode 100644 (file)
index 0000000..11320a1
--- /dev/null
@@ -0,0 +1,149 @@
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.compatibility.topologymanager;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.controller.md.sal.binding.util.TypeSafeDataReader;
+import org.opendaylight.controller.sal.core.ConstructionException;
+import org.opendaylight.controller.sal.core.Edge;
+import org.opendaylight.controller.sal.core.Host;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.Property;
+import org.opendaylight.controller.sal.core.UpdateType;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.topologymanager.ITopologyManager;
+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.TpId;
+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.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.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+
+@SuppressWarnings("all")
+public class CompatibleTopologyManager extends ConfigurableLinkManager implements ITopologyManager {
+    private AdSalTopologyMapping topologyMapping;
+    private TypeSafeDataReader dataReader;
+
+    public TypeSafeDataReader getDataReader() {
+        return dataReader;
+    }
+
+    public void setDataReader(final TypeSafeDataReader dataReader) {
+        this.dataReader = dataReader;
+    }
+
+    public AdSalTopologyMapping getTopologyMapping() {
+        return topologyMapping;
+    }
+
+    public void setTopologyMapping(final AdSalTopologyMapping topologyMapping) {
+        this.topologyMapping = topologyMapping;
+    }
+
+    @Override
+    public Map<Edge,Set<Property>> getEdges() {
+        final Topology topology = getDataReader().readOperationalData(topologyMapping.getTopologyPath());
+        return this.topologyMapping.toEdgePropertiesMap(topology.getLink());
+    }
+
+    @Override
+    public Map<org.opendaylight.controller.sal.core.Node, Set<Edge>> getNodeEdges() {
+        final Topology topology = getDataReader().readOperationalData(topologyMapping.getTopologyPath());
+        final HashMap<org.opendaylight.controller.sal.core.Node, Set<Edge>> ret = new HashMap<>();
+        for (final Node node : topology.getNode()) {
+            try {
+                ret.put(topologyMapping.toAdNode(node), topologyMapping.toEdges(
+                        FluentIterable.from(topology.getLink()).filter(new Predicate<Link>() {
+                            @Override
+                            public boolean apply(final Link input) {
+                                final NodeId nodeId = node.getNodeId();
+                                if (nodeId.equals(input.getSource().getSourceNode())) {
+                                    return true;
+                                }
+                                if (nodeId.equals(input.getDestination().getDestNode())) {
+                                    return true;
+                                }
+
+                                return false;
+                            }
+                        })));
+            } catch (ConstructionException e) {
+                throw new IllegalStateException(String.format("Failed to construct node for {}", node), e);
+            }
+        }
+        return ret;
+    }
+
+    /**
+     * Returns true if point is connected to link
+     */
+    private boolean isInternal(final TerminationPoint point) {
+        final Topology topology = getDataReader().readConfigurationData(topologyMapping.getTopologyPath());
+        final TpId tpId = point.getKey().getTpId();
+        return FluentIterable.from(topology.getLink()).anyMatch(new Predicate<Link>() {
+            @Override
+            public boolean apply(final Link input) {
+                if (tpId.equals(input.getSource().getSourceTp())) {
+                    return true;
+                }
+                if (tpId.equals(input.getDestination().getDestTp())) {
+                    return true;
+                }
+                return false;
+            }
+        });
+    }
+
+    @Override
+    public Set<NodeConnector> getNodeConnectorWithHost() {
+        return null;
+    }
+
+    @Override
+    public Host getHostAttachedToNodeConnector(final NodeConnector p) {
+        final InstanceIdentifier<TerminationPoint> tpPath = topologyMapping.toTerminationPoint(p);
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
+    }
+
+    @Override
+    public List<Host> getHostsAttachedToNodeConnector(final NodeConnector p) {
+        final Topology topology = getDataReader().readOperationalData(topologyMapping.getTopologyPath());
+        throw new UnsupportedOperationException("Hosts not mapped yet");
+    }
+
+    @Override
+    public Map<org.opendaylight.controller.sal.core.Node, Set<NodeConnector>> getNodesWithNodeConnectorHost() {
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
+    }
+
+    @Override
+    public boolean isInternal(final NodeConnector p) {
+        final TerminationPoint connector = getDataReader()
+                .readConfigurationData(topologyMapping.toTerminationPoint(p));
+        return this.isInternal(connector);
+    }
+
+    @Override
+    public void updateHostLink(final NodeConnector p, final Host h, final UpdateType t, final Set<Property> props) {
+        // Update app defined topology
+    }
+
+    @Override
+    public Status saveConfig() {
+        // FIXME: commit configuration
+        return null;
+    }
+}
diff --git a/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/CompatibleTopologyManager.xtend b/opendaylight/md-sal/compatibility/inventory-topology-compatibility/src/main/java/org/opendaylight/controller/md/compatibility/topologymanager/CompatibleTopologyManager.xtend
deleted file mode 100644 (file)
index 4fdea4b..0000000
+++ /dev/null
@@ -1,92 +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.compatibility.topologymanager
-
-import org.opendaylight.controller.topologymanager.ITopologyManager
-import org.opendaylight.controller.sal.core.NodeConnector
-import org.opendaylight.controller.sal.core.Host
-import org.opendaylight.controller.sal.core.UpdateType
-import java.util.Set
-import org.opendaylight.controller.md.sal.binding.util.TypeSafeDataReader
-import java.util.HashMap
-import org.opendaylight.controller.sal.core.Edge
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint
-import com.google.common.collect.FluentIterable
-
-class CompatibleTopologyManager extends ConfigurableLinkManager implements ITopologyManager {
-
-    @Property
-    var TypeSafeDataReader dataReader;
-
-    @Property
-    var extension AdSalTopologyMapping topologyMapping;
-
-    override getEdges() {
-        val topology = dataReader.readOperationalData(topologyPath);
-        return topology.link.toEdgePropertiesMap();
-    }
-
-    override getNodeEdges() {
-        val topology = dataReader.readOperationalData(topologyPath);
-        val ret = new HashMap<org.opendaylight.controller.sal.core.Node, Set<Edge>>;
-        for (node : topology.node) {
-            val adNode = node.toAdNode();
-            val adEdges = FluentIterable.from(topology.link).filter[
-                source.sourceNode == node.nodeId || destination.destNode == node.nodeId].toEdges();
-            ret.put(adNode, adEdges)
-        }
-        return ret;
-    }
-
-    /**
-     *   Returns true if point is connected to link
-    */
-    def isInternal(TerminationPoint point) {
-        val topology = dataReader.readConfigurationData(topologyPath);
-        val tpId = point.key.tpId;
-        return FluentIterable.from(topology.link).anyMatch(
-            [
-                source.sourceTp == tpId || destination.destTp == tpId
-            ])
-    }
-
-    override getNodeConnectorWithHost() {
-    }
-
-    override getHostAttachedToNodeConnector(NodeConnector p) {
-        val tpPath = p.toTerminationPoint();
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-    }
-
-    override getHostsAttachedToNodeConnector(NodeConnector p) {
-        val topology = dataReader.readOperationalData(topologyPath);
-
-        throw new UnsupportedOperationException("Hosts not mapped yet")
-    }
-
-    override getNodesWithNodeConnectorHost() {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-
-    }
-
-    override isInternal(NodeConnector p) {
-        val tpPath = p.toTerminationPoint();
-        val connector = dataReader.readConfigurationData(tpPath);
-        return connector.isInternal();
-    }
-
-    override updateHostLink(NodeConnector p, Host h, UpdateType t,
-        Set<org.opendaylight.controller.sal.core.Property> props) {
-        // Update app defined topology
-    }
-
-    override saveConfig() {
-        // FIXME: commit configuration
-    }
-
-}
@@ -1,31 +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.compatibility.topologymanager
+package org.opendaylight.controller.md.compatibility.topologymanager;
 
-import org.opendaylight.controller.topologymanager.ITopologyManager
-import org.opendaylight.controller.topologymanager.TopologyUserLinkConfig
+import java.util.concurrent.ConcurrentMap;
 
-abstract class ConfigurableLinkManager implements ITopologyManager {
-    
-    final override addUserLink(TopologyUserLinkConfig link) {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-        
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.topologymanager.ITopologyManager;
+import org.opendaylight.controller.topologymanager.TopologyUserLinkConfig;
+
+public abstract class ConfigurableLinkManager implements ITopologyManager {
+    @Override
+    public final Status addUserLink(final TopologyUserLinkConfig link) {
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
     }
-    
-    
-    final override deleteUserLink(String linkName) {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-        
+
+    @Override
+    public final Status deleteUserLink(final String linkName) {
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
     }
-    
-    
-    final override getUserLinks() {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-        
+
+    @Override
+    public final ConcurrentMap<String,TopologyUserLinkConfig> getUserLinks() {
+        throw new UnsupportedOperationException("TODO: auto-generated method stub");
     }
 }
index 3ed1edf7d68b3fb8617af9689b92b93b9b80441d..688e6ac09b25b29d8160ad05b10ef078430e1d37 100644 (file)
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.eclipse.xtend</groupId>
-      <artifactId>org.eclipse.xtend.lib</artifactId>
-    </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>sal</artifactId>
           </instructions>
         </configuration>
       </plugin>
-      <plugin>
-        <groupId>org.eclipse.xtend</groupId>
-        <artifactId>xtend-maven-plugin</artifactId>
-      </plugin>
       <plugin>
         <groupId>org.jacoco</groupId>
         <artifactId>jacoco-maven-plugin</artifactId>
index a145a3b4e179dcc9a9dea8c9df54cd526a84071e..6c79e053e16b353a5e4bdd51f6e44a469aec8b26 100644 (file)
           </instructions>
         </configuration>
       </plugin>
-      <plugin>
-        <groupId>org.eclipse.xtend</groupId>
-        <artifactId>xtend-maven-plugin</artifactId>
-      </plugin>
       <plugin>
         <groupId>org.jacoco</groupId>
         <artifactId>jacoco-maven-plugin</artifactId>
diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryAndReadAdapter.java b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryAndReadAdapter.java
new file mode 100644 (file)
index 0000000..2830165
--- /dev/null
@@ -0,0 +1,770 @@
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse 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.compatibility;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.opendaylight.controller.md.sal.binding.util.TypeSafeDataReader;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.core.ConstructionException;
+import org.opendaylight.controller.sal.core.Edge;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.NodeTable;
+import org.opendaylight.controller.sal.core.Property;
+import org.opendaylight.controller.sal.core.UpdateType;
+import org.opendaylight.controller.sal.inventory.IPluginInInventoryService;
+import org.opendaylight.controller.sal.inventory.IPluginOutInventoryService;
+import org.opendaylight.controller.sal.reader.FlowOnNode;
+import org.opendaylight.controller.sal.reader.IPluginInReadService;
+import org.opendaylight.controller.sal.reader.IPluginOutReadService;
+import org.opendaylight.controller.sal.reader.NodeConnectorStatistics;
+import org.opendaylight.controller.sal.reader.NodeDescription;
+import org.opendaylight.controller.sal.reader.NodeTableStatistics;
+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.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.statistics.rev130819.AggregateFlowStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowsStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAllFlowsStatisticsFromAllFlowTablesInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetFlowStatisticsFromFlowTableInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.OpendaylightFlowStatisticsListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.OpendaylightFlowStatisticsService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.and.statistics.map.list.FlowAndStatisticsMapList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.FlowTableStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.FlowTableStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.GetFlowTablesStatisticsInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.OpendaylightFlowTableStatisticsListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.OpendaylightFlowTableStatisticsService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.flow.table.and.statistics.map.FlowTableAndStatisticsMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.flow.table.statistics.FlowTableStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819.FlowTopologyDiscoveryService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819.Link;
+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.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.OpendaylightInventoryListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.GenericStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.node.connector.statistics.Bytes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.node.connector.statistics.Packets;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.FlowCapableNodeConnectorStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.FlowCapableNodeConnectorStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.GetAllNodeConnectorsStatisticsInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.GetNodeConnectorStatisticsInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.NodeConnectorStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.OpendaylightPortStatisticsListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.OpendaylightPortStatisticsService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.node.connector.statistics.and.port.number.map.NodeConnectorStatisticsAndPortNumberMap;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class InventoryAndReadAdapter implements IPluginInReadService, IPluginInInventoryService, OpendaylightInventoryListener, OpendaylightFlowStatisticsListener, OpendaylightFlowTableStatisticsListener, OpendaylightPortStatisticsListener {
+    private static final Logger LOG = LoggerFactory.getLogger(InventoryAndReadAdapter.class);
+    private static final short OPENFLOWV10_TABLE_ID = 0;
+
+    private final InventoryNotificationProvider inventoryNotificationProvider = new InventoryNotificationProvider();
+    private final Map<PathArgument,List<PathArgument>> nodeToNodeConnectorsMap = new ConcurrentHashMap<>();
+    private List<IPluginOutInventoryService> inventoryPublisher = new CopyOnWriteArrayList<>();
+    private List<IPluginOutReadService> statisticsPublisher = new CopyOnWriteArrayList<>();
+
+    private OpendaylightFlowTableStatisticsService flowTableStatisticsService;
+    private OpendaylightPortStatisticsService nodeConnectorStatisticsService;
+    private OpendaylightFlowStatisticsService flowStatisticsService;
+    private FlowTopologyDiscoveryService topologyDiscovery;
+    private DataProviderService dataProviderService;
+    private DataBrokerService dataService;
+
+    public DataBrokerService getDataService() {
+        return dataService;
+    }
+
+    public void setDataService(final DataBrokerService dataService) {
+        this.dataService = dataService;
+    }
+
+    public DataProviderService getDataProviderService() {
+        return dataProviderService;
+    }
+
+    public void setDataProviderService(final DataProviderService dataProviderService) {
+        this.dataProviderService = dataProviderService;
+    }
+
+    public OpendaylightFlowStatisticsService getFlowStatisticsService() {
+        return flowStatisticsService;
+    }
+
+    public void setFlowStatisticsService(final OpendaylightFlowStatisticsService flowStatisticsService) {
+        this.flowStatisticsService = flowStatisticsService;
+    }
+
+    public OpendaylightPortStatisticsService getNodeConnectorStatisticsService() {
+        return nodeConnectorStatisticsService;
+    }
+
+    public void setNodeConnectorStatisticsService(final OpendaylightPortStatisticsService nodeConnectorStatisticsService) {
+        this.nodeConnectorStatisticsService = nodeConnectorStatisticsService;
+    }
+
+    public OpendaylightFlowTableStatisticsService getFlowTableStatisticsService() {
+        return flowTableStatisticsService;
+    }
+
+    public void setFlowTableStatisticsService(final OpendaylightFlowTableStatisticsService flowTableStatisticsService) {
+        this.flowTableStatisticsService = flowTableStatisticsService;
+    }
+
+    public FlowTopologyDiscoveryService getTopologyDiscovery() {
+        return topologyDiscovery;
+    }
+
+    public void setTopologyDiscovery(final FlowTopologyDiscoveryService topologyDiscovery) {
+        this.topologyDiscovery = topologyDiscovery;
+    }
+
+    public List<IPluginOutReadService> getStatisticsPublisher() {
+        return statisticsPublisher;
+    }
+
+    public void setStatisticsPublisher(final List<IPluginOutReadService> statisticsPublisher) {
+        this.statisticsPublisher = statisticsPublisher;
+    }
+
+    public List<IPluginOutInventoryService> getInventoryPublisher() {
+        return inventoryPublisher;
+    }
+
+    public void setInventoryPublisher(final List<IPluginOutInventoryService> inventoryPublisher) {
+        this.inventoryPublisher = inventoryPublisher;
+    }
+
+    public void startAdapter() {
+        inventoryNotificationProvider.setDataProviderService(getDataProviderService());
+        inventoryNotificationProvider.setInventoryPublisher(getInventoryPublisher());
+        // inventoryNotificationProvider.start();
+    }
+
+    public boolean setInventoryPublisher(final IPluginOutInventoryService listener) {
+        return getInventoryPublisher().add(listener);
+    }
+
+    public boolean unsetInventoryPublisher(final IPluginOutInventoryService listener) {
+        return getInventoryPublisher().remove(listener);
+    }
+
+    public boolean setReadPublisher(final IPluginOutReadService listener) {
+        return getStatisticsPublisher().add(listener);
+    }
+
+    public Boolean unsetReadPublisher(final IPluginOutReadService listener) {
+        if (listener != null) {
+            return getStatisticsPublisher().remove(listener);
+        }
+        return false;
+    }
+
+    protected DataModificationTransaction startChange() {
+        return getDataProviderService().beginTransaction();
+    }
+
+    @Override
+    public long getTransmitRate(final NodeConnector connector) {
+        final FlowCapableNodeConnector nodeConnector = this.readOperFlowCapableNodeConnector(NodeMapping.toNodeConnectorRef(connector));
+        return nodeConnector.getCurrentSpeed().longValue();
+    }
+
+    private FlowCapableNode readOperFlowCapableNode(final NodeRef ref) {
+        final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node node =
+                (org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node)getDataService().readOperationalData(ref.getValue());
+        if (node == null) {
+            return null;
+        }
+
+        return node.getAugmentation(FlowCapableNode.class);
+    }
+
+    private org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node readConfigNode(final Node node) {
+        final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node> nodeRef =
+                InstanceIdentifier.builder(Nodes.class)
+                .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, InventoryMapping.toNodeKey(node))
+                .build();
+
+        return (org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node) startChange().readConfigurationData(nodeRef);
+    }
+
+    private org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector readConfigNodeConnector(final NodeConnector connector) {
+        final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector> nodeConnectorRef =
+                InstanceIdentifier.builder(Nodes.class)
+                .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, InventoryMapping.toNodeKey(connector.getNode()))
+                .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector.class, InventoryMapping.toNodeConnectorKey(connector))
+                .toInstance();
+
+        return((org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector) startChange().readConfigurationData(nodeConnectorRef));
+    }
+
+    /**
+     * Read a table of a node from configuration data store.
+     *
+     * @param node Node id
+     * @param id Table id
+     * @return Table contents, or null if not present
+     */
+    private Table readConfigTable(final Node node, final short id) {
+        final InstanceIdentifier<Table> tableRef = InstanceIdentifier.builder(Nodes.class)
+                .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, InventoryMapping.toNodeKey(node))
+                .augmentation(FlowCapableNode.class)
+                .child(Table.class, new TableKey(id))
+                .build();
+
+        return (Table) startChange().readConfigurationData(tableRef);
+    }
+
+    @Override
+    public List<FlowOnNode> readAllFlow(final Node node, final boolean cached) {
+        final ArrayList<FlowOnNode> output = new ArrayList<>();
+        final Table table = readConfigTable(node, OPENFLOWV10_TABLE_ID);
+        if (table != null) {
+            final List<Flow> flows = table.getFlow();
+            LOG.trace("Number of flows installed in table 0 of node {} : {}", node, flows.size());
+
+            for (final Flow flow : flows) {
+                final FlowStatisticsData statsFromDataStore = flow.getAugmentation(FlowStatisticsData.class);
+                if (statsFromDataStore != null) {
+                    final FlowOnNode it = new FlowOnNode(ToSalConversionsUtils.toFlow(flow, node));
+                    output.add(addFlowStats(it, statsFromDataStore.getFlowStatistics()));
+                }
+            }
+        }
+
+        // TODO (main): Shall we send request to the switch? It will make async request to the switch.
+        // Once the plugin receives a response, it will let the adaptor know through onFlowStatisticsUpdate()
+        // If we assume that md-sal statistics manager will always be running, then it is not required
+        // But if not, then sending request will collect the latest data for adaptor at least.
+        getFlowStatisticsService().getAllFlowsStatisticsFromAllFlowTables(
+                new GetAllFlowsStatisticsFromAllFlowTablesInputBuilder().setNode(NodeMapping.toNodeRef(node)).build());
+        return output;
+    }
+
+    @Override
+    public List<NodeConnectorStatistics> readAllNodeConnector(final Node node, final boolean cached) {
+        final ArrayList<NodeConnectorStatistics> ret = new ArrayList<>();
+
+        final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node dsNode = readConfigNode(node);
+        if (dsNode != null) {
+            for (final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector dsNodeConnector : dsNode.getNodeConnector()) {
+                final FlowCapableNodeConnectorStatistics stats = (dsNodeConnector.getAugmentation(FlowCapableNodeConnectorStatisticsData.class));
+                if (stats != null) {
+                    try {
+                        ret.add(toNodeConnectorStatistics(stats.getFlowCapableNodeConnectorStatistics(), dsNode.getId(), dsNodeConnector.getId()));
+                    } catch (ConstructionException e) {
+                        LOG.warn("Failed to instantiate node connector statistics for node {} connector {}, ignoring it",
+                                dsNode.getId(), dsNodeConnector.getId(), e);
+                    }
+                }
+            }
+        }
+
+        //TODO: Refer TODO (main)
+        getNodeConnectorStatisticsService().getAllNodeConnectorsStatistics(
+                new GetAllNodeConnectorsStatisticsInputBuilder().setNode(NodeMapping.toNodeRef(node)).build());
+        return ret;
+    }
+
+    @Override
+    public List<NodeTableStatistics> readAllNodeTable(final Node node, final boolean cached) {
+        final NodeRef nodeRef = NodeMapping.toNodeRef(node);
+
+        final ArrayList<NodeTableStatistics> ret = new ArrayList<>();
+        final FlowCapableNode dsFlowCapableNode = this.readOperFlowCapableNode(nodeRef);
+        if (dsFlowCapableNode != null) {
+            for (final Table table : dsFlowCapableNode.getTable()) {
+                final FlowTableStatisticsData tableStats = table.getAugmentation(FlowTableStatisticsData.class);
+                if (tableStats != null) {
+                    try {
+                        ret.add(toNodeTableStatistics(tableStats.getFlowTableStatistics(), table.getId(), node));
+                    } catch (ConstructionException e) {
+                        LOG.warn("Failed to instantiate table statistics for node {} table {}, ignoring it", node, table.getId(), e);
+                    }
+                }
+            }
+        }
+
+        //TODO: Refer TODO (main)
+        getFlowTableStatisticsService().getFlowTablesStatistics(new GetFlowTablesStatisticsInputBuilder().setNode(nodeRef).build());
+        return ret;
+    }
+
+    @Override
+    public NodeDescription readDescription(final Node node, final boolean cached) {
+        return this.toNodeDescription(NodeMapping.toNodeRef(node));
+    }
+
+    @Override
+    public FlowOnNode readFlow(final Node node, final org.opendaylight.controller.sal.flowprogrammer.Flow targetFlow, final boolean cached) {
+        FlowOnNode ret = null;
+        final Table table = readConfigTable(node, OPENFLOWV10_TABLE_ID);
+        if (table != null) {
+            final List<Flow> flows = table.getFlow();
+            InventoryAndReadAdapter.LOG.trace("Number of flows installed in table 0 of node {} : {}", node, flows.size());
+
+            for (final Flow mdsalFlow : flows) {
+                if(FromSalConversionsUtils.flowEquals(mdsalFlow, MDFlowMapping.toMDSalflow(targetFlow))) {
+                    final FlowStatisticsData statsFromDataStore = mdsalFlow.getAugmentation(FlowStatisticsData.class);
+                    if (statsFromDataStore != null) {
+                        InventoryAndReadAdapter.LOG.debug("Found matching flow in the data store flow table ");
+                        ret = addFlowStats(new FlowOnNode(targetFlow), statsFromDataStore.getFlowStatistics());
+
+                        // FIXME: break; ?
+                    }
+                }
+            }
+        }
+
+        //TODO: Refer TODO (main)
+        final GetFlowStatisticsFromFlowTableInputBuilder input = new GetFlowStatisticsFromFlowTableInputBuilder().setNode(NodeMapping.toNodeRef(node));
+        input.fieldsFrom(MDFlowMapping.toMDSalflow(targetFlow));
+        getFlowStatisticsService().getFlowStatisticsFromFlowTable(input.build());
+        return ret;
+    }
+
+    @Override
+    public NodeConnectorStatistics readNodeConnector(final NodeConnector connector, final boolean cached) {
+        final NodeConnectorId ncId = InventoryMapping.toNodeConnectorKey(connector).getId();
+
+        NodeConnectorStatistics ret = null;
+        final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector nodeConnectorFromDS = readConfigNodeConnector(connector);
+        if (nodeConnectorFromDS != null) {
+            final FlowCapableNodeConnectorStatistics stats = nodeConnectorFromDS.getAugmentation(FlowCapableNodeConnectorStatisticsData.class);
+            if (stats != null) {
+                try {
+                    ret = toNodeConnectorStatistics(stats.getFlowCapableNodeConnectorStatistics(),
+                            InventoryMapping.toNodeKey(connector.getNode()).getId(), ncId);
+                } catch (ConstructionException e) {
+                    LOG.warn("Failed to instantiate node connector statistics for connector {}, ignoring it",
+                            connector, e);
+                }
+            }
+        }
+
+        getNodeConnectorStatisticsService().getNodeConnectorStatistics(
+                new GetNodeConnectorStatisticsInputBuilder().setNode(NodeMapping.toNodeRef(connector.getNode())).setNodeConnectorId(ncId).build());
+        return ret;
+    }
+
+    @Override
+    public NodeTableStatistics readNodeTable(final NodeTable nodeTable, final boolean cached) {
+        NodeTableStatistics nodeStats = null;
+        final Table table = readConfigTable(nodeTable.getNode(), (short) nodeTable.getID());
+        if (table != null) {
+            final FlowTableStatisticsData tableStats = table.getAugmentation(FlowTableStatisticsData.class);
+            if (tableStats != null) {
+                try {
+                    nodeStats = toNodeTableStatistics(tableStats.getFlowTableStatistics(), table.getId(), nodeTable.getNode());
+                } catch (ConstructionException e) {
+                    LOG.warn("Failed to instantiate table statistics for node {} table {}, ignoring it",
+                            nodeTable.getNode(), table.getId(), e);
+                }
+            }
+        }
+
+        //TODO: Refer TODO (main)
+        getFlowTableStatisticsService().getFlowTablesStatistics(
+                new GetFlowTablesStatisticsInputBuilder().setNode(NodeMapping.toNodeRef(nodeTable.getNode())).build());
+        return nodeStats;
+    }
+
+    @Override
+    public void onNodeConnectorRemoved(final NodeConnectorRemoved update) {
+        // Never received
+    }
+
+    @Override
+    public void onNodeRemoved(final NodeRemoved notification) {
+        this.removeNodeConnectors(notification.getNodeRef().getValue());
+        try {
+            final Node aDNode = NodeMapping.toADNode(notification.getNodeRef());
+            this.publishNodeUpdate(aDNode, UpdateType.REMOVED, Collections.<Property>emptySet());
+        } catch (ConstructionException e) {
+            LOG.warn("Failed to construct node for {}, not propagating update", notification.getNodeRef(), e);
+        }
+    }
+
+    @Override
+    public void onNodeConnectorUpdated(final NodeConnectorUpdated update) {
+        final NodeConnectorRef ref = update.getNodeConnectorRef();
+        final UpdateType updateType;
+        if (!this.isKnownNodeConnector(ref.getValue())) {
+            this.recordNodeConnector(ref.getValue());
+            updateType = UpdateType.ADDED;
+        } else {
+            updateType = UpdateType.CHANGED;
+        }
+
+        try {
+            final NodeConnector nodeConnector;
+            nodeConnector = NodeMapping.toADNodeConnector(ref);
+            this.publishNodeConnectorUpdate(nodeConnector, updateType, NodeMapping.toADNodeConnectorProperties(update));
+        } catch (ConstructionException e) {
+            LOG.warn("Failed to construct node connector for {}, not reporting the update", ref, e);
+        }
+    }
+
+    @Override
+    public void onNodeUpdated(final NodeUpdated notification) {
+        final NodeRef ref = notification.getNodeRef();
+
+        final UpdateType updateType;
+        if (dataService.readOperationalData(ref.getValue()) == null) {
+            updateType = UpdateType.ADDED;
+        } else {
+            updateType = UpdateType.CHANGED;
+        }
+
+        final Node aDNode;
+        try {
+            aDNode = NodeMapping.toADNode(ref);
+        } catch (ConstructionException e) {
+            LOG.warn("Failed to construct node for {}, not reporting the update", ref, e);
+            return;
+        }
+
+        this.publishNodeUpdate(aDNode, updateType, NodeMapping.toADNodeProperties(notification));
+        for (final IPluginOutReadService statsPublisher : getStatisticsPublisher()) {
+            final NodeDescription description = this.toNodeDescription(ref);
+            if (description != null) {
+                final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node> nodeRef =
+                        InstanceIdentifier.builder(Nodes.class)
+                        .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, new NodeKey(notification.getId()))
+                        .toInstance();
+                try {
+                    statsPublisher.descriptionStatisticsUpdated(NodeMapping.toADNode(nodeRef), description);
+                } catch (ConstructionException e) {
+                    LOG.warn("Failed to construct node for {}, not reporting the update to publisher {}", nodeRef, statsPublisher, e);
+                }
+            }
+        }
+    }
+
+    @Override
+    public ConcurrentMap<Node,Map<String,Property>> getNodeProps() {
+        final ConcurrentHashMap<Node,Map<String,Property>> props = new ConcurrentHashMap<>();
+        final Nodes nodes = this.readOperAllMDNodes();
+        for (final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node node : nodes.getNode()) {
+            final FlowCapableNode fcn = node.getAugmentation(FlowCapableNode.class);
+            if (fcn != null) {
+                final ConcurrentHashMap<String,Property> perNodePropMap = new ConcurrentHashMap<String, Property>();
+                final HashSet<Property> perNodeProps = NodeMapping.toADNodeProperties(fcn, node.getId());
+                if (perNodeProps != null) {
+                    for (final Property perNodeProp : perNodeProps) {
+                        perNodePropMap.put(perNodeProp.getName(), perNodeProp);
+                    }
+                }
+
+                try {
+                    final Node adNode = new Node(NodeMapping.MD_SAL_TYPE, NodeMapping.toADNodeId(node.getId()));
+                    props.put(adNode, perNodePropMap);
+                } catch (ConstructionException e) {
+                    LOG.warn("Failed to construct node for {}, skipping it", node, e);
+                }
+            }
+        }
+        return props;
+    }
+
+    private Nodes readOperAllMDNodes() {
+        final TypeSafeDataReader reader = TypeSafeDataReader.forReader(getDataService());
+        return reader.readOperationalData(InstanceIdentifier.builder(Nodes.class).build());
+    }
+
+    @Override
+    public ConcurrentMap<NodeConnector,Map<String,Property>> getNodeConnectorProps(final Boolean refresh) {
+        final ConcurrentHashMap<NodeConnector,Map<String,Property>> props = new ConcurrentHashMap<>();
+        for (final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node node : this.readOperAllMDNodes().getNode()) {
+            for (final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector nc : node.getNodeConnector()) {
+                final FlowCapableNodeConnector fcnc = nc.getAugmentation(FlowCapableNodeConnector.class);
+                if (fcnc != null) {
+                    final ConcurrentHashMap<String,Property> ncpsm = new ConcurrentHashMap<>();
+                    final HashSet<Property> ncps = NodeMapping.toADNodeConnectorProperties(fcnc);
+                    if (ncps != null) {
+                        for (final Property p : ncps) {
+                            ncpsm.put(p.getName(), p);
+                        }
+                    }
+
+                    try {
+                        props.put(NodeMapping.toADNodeConnector(nc.getId(), node.getId()), ncpsm);
+                    } catch (ConstructionException e) {
+                        LOG.warn("Failed to instantiate node {} connector {}, not reporting it", node.getId(), nc.getId(), e);
+                    }
+                }
+            }
+        }
+        return props;
+    }
+
+    private FlowCapableNodeConnector readOperFlowCapableNodeConnector(final NodeConnectorRef ref) {
+        final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector nc =
+                (org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector)
+                getDataService().readOperationalData(ref.getValue());
+        return nc.getAugmentation(FlowCapableNodeConnector.class);
+    }
+
+    private static NodeConnectorStatistics toNodeConnectorStatistics(final org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.NodeConnectorStatistics nodeConnectorStatistics, final NodeId nodeId, final NodeConnectorId nodeConnectorId) throws ConstructionException {
+        final NodeConnectorStatistics it = new NodeConnectorStatistics();
+
+        final Packets packets = nodeConnectorStatistics.getPackets();
+        it.setReceivePacketCount(packets.getReceived().longValue());
+        it.setTransmitPacketCount(packets.getTransmitted().longValue());
+
+        final Bytes bytes = nodeConnectorStatistics.getBytes();
+        it.setReceiveByteCount(bytes.getReceived().longValue());
+        it.setTransmitByteCount(bytes.getTransmitted().longValue());
+
+        it.setReceiveDropCount(nodeConnectorStatistics.getReceiveDrops().longValue());
+        it.setTransmitDropCount(nodeConnectorStatistics.getTransmitDrops().longValue());
+        it.setReceiveErrorCount(nodeConnectorStatistics.getReceiveErrors().longValue());
+        it.setTransmitErrorCount(nodeConnectorStatistics.getTransmitErrors().longValue());
+        it.setReceiveFrameErrorCount(nodeConnectorStatistics.getReceiveFrameError().longValue());
+        it.setReceiveOverRunErrorCount(nodeConnectorStatistics.getReceiveOverRunError().longValue());
+        it.setReceiveCRCErrorCount(nodeConnectorStatistics.getReceiveCrcError().longValue());
+        it.setCollisionCount(nodeConnectorStatistics.getCollisionCount().longValue());
+
+        final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector> nodeConnectorRef =
+                InstanceIdentifier.builder(Nodes.class)
+                .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, new NodeKey(nodeId))
+                .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector.class, new NodeConnectorKey(nodeConnectorId))
+                .build();
+        it.setNodeConnector(NodeMapping.toADNodeConnector(new NodeConnectorRef(nodeConnectorRef)));
+        return it;
+    }
+
+    private static NodeTableStatistics toNodeTableStatistics(final FlowTableStatistics tableStats, final Short tableId, final Node node) throws ConstructionException {
+        final NodeTableStatistics it = new NodeTableStatistics();
+        it.setActiveCount(tableStats.getActiveFlows().getValue().intValue());
+        it.setLookupCount(tableStats.getPacketsLookedUp().getValue().longValue());
+        it.setMatchedCount(tableStats.getPacketsMatched().getValue().longValue());
+        it.setName(tableId.toString());
+        it.setNodeTable(new NodeTable(NodeMapping.MD_SAL_TYPE, tableId, node));
+        return it;
+    }
+
+    private NodeDescription toNodeDescription(final NodeRef nodeRef) {
+        final FlowCapableNode capableNode = this.readOperFlowCapableNode(nodeRef);
+        if (capableNode == null) {
+            return null;
+        }
+
+        final NodeDescription it = new NodeDescription();
+        it.setManufacturer(capableNode.getManufacturer());
+        it.setSerialNumber(capableNode.getSerialNumber());
+        it.setSoftware(capableNode.getSoftware());
+        it.setDescription(capableNode.getDescription());
+        return it;
+    }
+
+    public Edge toADEdge(final Link link) throws ConstructionException {
+        NodeConnectorRef _source = link.getSource();
+        NodeConnector _aDNodeConnector = NodeMapping.toADNodeConnector(_source);
+        NodeConnectorRef _destination = link.getDestination();
+        NodeConnector _aDNodeConnector_1 = NodeMapping.toADNodeConnector(_destination);
+        Edge _edge = new Edge(_aDNodeConnector, _aDNodeConnector_1);
+        return _edge;
+    }
+
+    /**
+     * OpendaylightFlowStatisticsListener interface implementation
+     */
+    @Override
+    public void onAggregateFlowStatisticsUpdate(final AggregateFlowStatisticsUpdate notification) {
+        // Ignoring this notification as there does not seem to be a way to bubble this up to AD-SAL
+    }
+
+    @Override
+    public void onFlowsStatisticsUpdate(final FlowsStatisticsUpdate notification) {
+        final ArrayList<FlowOnNode> adsalFlowsStatistics = new ArrayList<>();
+        final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node> nodeRef =
+                InstanceIdentifier.builder(Nodes.class)
+                .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, new NodeKey(notification.getId()))
+                .build();
+
+        final Node aDNode;
+        try {
+            aDNode = NodeMapping.toADNode(nodeRef);
+        } catch (ConstructionException e) {
+            LOG.warn("Failed to construct node for {}, ignoring it", notification.getId(), e);
+            return;
+        }
+
+        for (final FlowAndStatisticsMapList flowStats : notification.getFlowAndStatisticsMapList()) {
+            if (flowStats.getTableId() == 0) {
+                adsalFlowsStatistics.add(InventoryAndReadAdapter.toFlowOnNode(flowStats, aDNode));
+            }
+        }
+        for (final IPluginOutReadService statsPublisher : getStatisticsPublisher()) {
+            statsPublisher.nodeFlowStatisticsUpdated(aDNode, adsalFlowsStatistics);
+        }
+    }
+
+    /**
+     * OpendaylightFlowTableStatisticsListener interface implementation
+     */
+    @Override
+    public void onFlowTableStatisticsUpdate(final FlowTableStatisticsUpdate notification) {
+        ArrayList<NodeTableStatistics> adsalFlowTableStatistics = new ArrayList<>();
+        for (final FlowTableAndStatisticsMap stats : notification.getFlowTableAndStatisticsMap()) {
+            if (stats.getTableId().getValue() == 0) {
+                final NodeTableStatistics it = new NodeTableStatistics();
+                it.setActiveCount(stats.getActiveFlows().getValue().intValue());
+                it.setLookupCount(stats.getPacketsLookedUp().getValue().longValue());
+                it.setMatchedCount(stats.getPacketsMatched().getValue().longValue());
+                adsalFlowTableStatistics.add(it);
+            }
+        }
+
+        final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node> nodeRef =
+                InstanceIdentifier.builder(Nodes.class)
+                .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, new NodeKey(notification.getId()))
+                .build();
+
+        final Node aDNode;
+        try {
+            aDNode = NodeMapping.toADNode(nodeRef);
+        } catch (ConstructionException e) {
+            LOG.warn("Failed to construct node for {}, ignoring it", notification.getId(), e);
+            return;
+        }
+
+        for (final IPluginOutReadService statsPublisher : getStatisticsPublisher()) {
+            statsPublisher.nodeTableStatisticsUpdated(aDNode, adsalFlowTableStatistics);
+        }
+    }
+
+    /**
+     * OpendaylightPortStatisticsUpdate interface implementation
+     */
+    @Override
+    public void onNodeConnectorStatisticsUpdate(final NodeConnectorStatisticsUpdate notification) {
+        final ArrayList<NodeConnectorStatistics> adsalPortStatistics = new ArrayList<NodeConnectorStatistics>();
+        for (final NodeConnectorStatisticsAndPortNumberMap nodeConnectorStatistics : notification.getNodeConnectorStatisticsAndPortNumberMap()) {
+            try {
+                adsalPortStatistics.add(toNodeConnectorStatistics(
+                        nodeConnectorStatistics, notification.getId(), nodeConnectorStatistics.getNodeConnectorId()));
+            } catch (ConstructionException e) {
+                LOG.warn("Failed to create statistics for node {} connector {}, not updating them",
+                        notification.getId(), nodeConnectorStatistics.getNodeConnectorId(), e);
+            }
+        }
+
+        final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node> nodeRef =
+                InstanceIdentifier.builder(Nodes.class)
+                .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, new NodeKey(notification.getId()))
+                .build();
+
+        final Node aDNode;
+        try {
+            aDNode = NodeMapping.toADNode(nodeRef);
+        } catch (ConstructionException e) {
+            LOG.warn("Failed to construct node for {}, ignoring it", notification.getId(), e);
+            return;
+        }
+
+        for (final IPluginOutReadService statsPublisher : getStatisticsPublisher()) {
+            statsPublisher.nodeConnectorStatisticsUpdated(aDNode, adsalPortStatistics);
+        }
+    }
+
+    private static FlowOnNode toFlowOnNode(final FlowAndStatisticsMapList flowAndStatsMap, final Node node) {
+        final FlowOnNode it = new FlowOnNode(ToSalConversionsUtils.toFlow(flowAndStatsMap, node));
+        return addFlowStats(it, flowAndStatsMap);
+    }
+
+    private static FlowOnNode addFlowStats(final FlowOnNode node, final GenericStatistics stats) {
+        node.setByteCount(stats.getByteCount().getValue().longValue());
+        node.setPacketCount(stats.getPacketCount().getValue().longValue());
+        node.setDurationSeconds(stats.getDuration().getSecond().getValue().intValue());
+        node.setDurationNanoseconds(stats.getDuration().getNanosecond().getValue().intValue());
+        return node;
+    }
+
+    @Override
+    public Set<Node> getConfiguredNotConnectedNodes() {
+        return Collections.emptySet();
+    }
+
+    private void publishNodeUpdate(final Node node, final UpdateType updateType, final Set<Property> properties) {
+        for (final IPluginOutInventoryService publisher : getInventoryPublisher()) {
+            publisher.updateNode(node, updateType, properties);
+        }
+    }
+
+    private void publishNodeConnectorUpdate(final NodeConnector nodeConnector, final UpdateType updateType, final Set<Property> properties) {
+        for (final IPluginOutInventoryService publisher : getInventoryPublisher()) {
+            publisher.updateNodeConnector(nodeConnector, updateType, properties);
+        }
+    }
+
+    private boolean isKnownNodeConnector(final InstanceIdentifier<? extends Object> nodeConnectorIdentifier) {
+        final List<PathArgument> path = nodeConnectorIdentifier.getPath();
+        if (path.size() >= 3) {
+            final PathArgument nodePath = path.get(1);
+            final PathArgument nodeConnectorPath = path.get(2);
+            final List<PathArgument> nodeConnectors = nodeToNodeConnectorsMap.get(nodePath);
+            if (nodeConnectors != null) {
+                return nodeConnectors.contains(nodeConnectorPath);
+            }
+        }
+        return false;
+    }
+
+    private boolean recordNodeConnector(final InstanceIdentifier<? extends Object> nodeConnectorIdentifier) {
+        final List<PathArgument> path = nodeConnectorIdentifier.getPath();
+        if (path.size() < 3) {
+            return false;
+        }
+
+        final PathArgument nodePath = path.get(1);
+        final PathArgument nodeConnectorPath = path.get(2);
+
+        synchronized (this) {
+            List<PathArgument> nodeConnectors = this.nodeToNodeConnectorsMap.get(nodePath);
+            if (nodeConnectors == null) {
+                nodeConnectors = new ArrayList<>();
+                this.nodeToNodeConnectorsMap.put(nodePath, nodeConnectors);
+            }
+
+            return nodeConnectors.add(nodeConnectorPath);
+        }
+    }
+
+    private List<PathArgument> removeNodeConnectors(final InstanceIdentifier<? extends Object> nodeIdentifier) {
+        return this.nodeToNodeConnectorsMap.remove(nodeIdentifier.getPath().get(1));
+    }
+}
diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryAndReadAdapter.xtend b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryAndReadAdapter.xtend
deleted file mode 100644 (file)
index 8908504..0000000
+++ /dev/null
@@ -1,683 +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.sal.compatibility
-
-import java.util.ArrayList
-import java.util.Collections
-import java.util.List
-import java.util.Map
-import java.util.Set
-import java.util.concurrent.ConcurrentHashMap
-import java.util.concurrent.CopyOnWriteArrayList
-import java.util.concurrent.locks.Lock
-import java.util.concurrent.locks.ReentrantLock
-import org.opendaylight.controller.md.sal.binding.util.TypeSafeDataReader
-import org.opendaylight.controller.sal.binding.api.data.DataBrokerService
-import org.opendaylight.controller.sal.binding.api.data.DataProviderService
-import org.opendaylight.controller.sal.core.Edge
-import org.opendaylight.controller.sal.core.Node
-import org.opendaylight.controller.sal.core.NodeTable
-import org.opendaylight.controller.sal.core.UpdateType
-import org.opendaylight.controller.sal.flowprogrammer.Flow
-import org.opendaylight.controller.sal.inventory.IPluginInInventoryService
-import org.opendaylight.controller.sal.inventory.IPluginOutInventoryService
-import org.opendaylight.controller.sal.reader.FlowOnNode
-import org.opendaylight.controller.sal.reader.IPluginInReadService
-import org.opendaylight.controller.sal.reader.IPluginOutReadService
-import org.opendaylight.controller.sal.reader.NodeConnectorStatistics
-import org.opendaylight.controller.sal.reader.NodeDescription
-import org.opendaylight.controller.sal.reader.NodeTableStatistics
-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.tables.Table
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.AggregateFlowStatisticsUpdate
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowStatisticsData
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowsStatisticsUpdate
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAllFlowsStatisticsFromAllFlowTablesInputBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetFlowStatisticsFromFlowTableInputBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.OpendaylightFlowStatisticsListener
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.OpendaylightFlowStatisticsService
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.and.statistics.map.list.FlowAndStatisticsMapList
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.FlowTableStatisticsData
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.FlowTableStatisticsUpdate
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.GetFlowTablesStatisticsInputBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.OpendaylightFlowTableStatisticsListener
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.OpendaylightFlowTableStatisticsService
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.flow.table.statistics.FlowTableStatistics
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819.FlowTopologyDiscoveryService
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819.Link
-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.Nodes
-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.NodeConnectorKey
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
-import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.FlowCapableNodeConnectorStatistics
-import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.FlowCapableNodeConnectorStatisticsData
-import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.GetAllNodeConnectorsStatisticsInputBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.GetNodeConnectorStatisticsInputBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.NodeConnectorStatisticsUpdate
-import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.OpendaylightPortStatisticsListener
-import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.OpendaylightPortStatisticsService
-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.common.util.Arguments.*
-import static extension org.opendaylight.controller.sal.compatibility.NodeMapping.*
-
-class InventoryAndReadAdapter implements IPluginInReadService,
-                                             IPluginInInventoryService,
-                                             OpendaylightInventoryListener,
-                                             OpendaylightFlowStatisticsListener,
-                                             OpendaylightFlowTableStatisticsListener,
-                                             OpendaylightPortStatisticsListener {
-
-    private static val LOG = LoggerFactory.getLogger(InventoryAndReadAdapter);
-
-    private static val OPENFLOWV10_TABLE_ID = new Integer(0).shortValue;
-    @Property
-    DataBrokerService dataService;
-
-    @Property
-    DataProviderService dataProviderService;
-
-    @Property
-    OpendaylightFlowStatisticsService flowStatisticsService;
-
-    @Property
-    OpendaylightPortStatisticsService nodeConnectorStatisticsService;
-    
-    @Property
-    OpendaylightFlowTableStatisticsService flowTableStatisticsService;
-
-    @Property
-    FlowTopologyDiscoveryService topologyDiscovery;
-    
-    @Property
-    List<IPluginOutReadService> statisticsPublisher = new CopyOnWriteArrayList<IPluginOutReadService>();
-
-    @Property
-    List<IPluginOutInventoryService> inventoryPublisher = new CopyOnWriteArrayList<IPluginOutInventoryService>();
-
-    private final InventoryNotificationProvider inventoryNotificationProvider = new InventoryNotificationProvider();
-
-    private final Map<InstanceIdentifier.PathArgument, List<InstanceIdentifier.PathArgument>> nodeToNodeConnectorsMap = new ConcurrentHashMap<InstanceIdentifier.PathArgument, List<InstanceIdentifier.PathArgument>>();
-
-    private final Lock nodeToNodeConnectorsLock = new ReentrantLock();
-
-
-    def startAdapter(){
-        inventoryNotificationProvider.dataProviderService = dataProviderService;
-        inventoryNotificationProvider.inventoryPublisher = inventoryPublisher;
-        // inventoryNotificationProvider.start();
-    }
-
-    def start(){
-    }
-
-    def setInventoryPublisher(IPluginOutInventoryService listener){
-        inventoryPublisher.add(listener);
-    }
-
-    def unsetInventoryPublisher(IPluginOutInventoryService listener){
-        inventoryPublisher.remove(listener);
-    }
-
-    def setReadPublisher(IPluginOutReadService listener) {
-        statisticsPublisher.add(listener);
-    }
-    
-    def unsetReadPublisher (IPluginOutReadService listener) {
-        if( listener != null)
-            statisticsPublisher.remove(listener);
-    }
-
-    protected def startChange() {
-        return dataProviderService.beginTransaction;
-    }
-
-    override getTransmitRate(org.opendaylight.controller.sal.core.NodeConnector connector) {
-        val nodeConnector = readFlowCapableNodeConnector(connector.toNodeConnectorRef);
-        return nodeConnector.currentSpeed
-    }
-
-    override readAllFlow(Node node, boolean cached) {
-
-        val output = new ArrayList<FlowOnNode>();
-        val tableRef = InstanceIdentifier.builder(Nodes)
-                                        .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node, InventoryMapping.toNodeKey(node))
-                                        .augmentation(FlowCapableNode).child(Table, new TableKey(OPENFLOWV10_TABLE_ID)).toInstance();
-        
-        val it = this.startChange();
-        
-        val table= it.readConfigurationData(tableRef) as Table;
-        
-        if(table != null){
-            LOG.trace("Number of flows installed in table 0 of node {} : {}",node,table.flow.size);
-            
-            for(flow : table.flow){
-                
-                val adsalFlow = ToSalConversionsUtils.toFlow(flow,node);
-                val statsFromDataStore = flow.getAugmentation(FlowStatisticsData);
-                
-                if(statsFromDataStore != null){
-                    val it = new FlowOnNode(adsalFlow);
-                    byteCount =  statsFromDataStore.flowStatistics.byteCount.value.longValue;
-                    packetCount = statsFromDataStore.flowStatistics.packetCount.value.longValue;
-                    durationSeconds = statsFromDataStore.flowStatistics.duration.second.value.intValue;
-                    durationNanoseconds = statsFromDataStore.flowStatistics.duration.nanosecond.value.intValue;
-                    
-                    output.add(it);
-                }
-            }
-        }
-        
-        //TODO (main): Shell we send request to the switch? It will make async request to the switch.
-        // Once plugin receive response, it will let adaptor know through onFlowStatisticsUpdate()
-        // If we assume that md-sal statistics manager will always be running, then its not required
-        // But if not, then sending request will collect the latest data for adaptor atleast.
-        val input = new GetAllFlowsStatisticsFromAllFlowTablesInputBuilder;
-        input.setNode(node.toNodeRef);
-        flowStatisticsService.getAllFlowsStatisticsFromAllFlowTables(input.build)
-        
-        return output;
-    }
-
-    override readAllNodeConnector(Node node, boolean cached) {
-        
-        val ret = new ArrayList<NodeConnectorStatistics>();
-        val nodeRef = InstanceIdentifier.builder(Nodes)
-                                    .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node, InventoryMapping.toNodeKey(node))
-                                    .toInstance();
-        
-        val provider = this.startChange();
-        
-        val dsNode= provider.readConfigurationData(nodeRef) as org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-        
-         if(dsNode != null){
-             
-             for (dsNodeConnector : dsNode.nodeConnector){
-                val nodeConnectorRef = InstanceIdentifier.builder(Nodes)
-                                    .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node, InventoryMapping.toNodeKey(node))
-                                    .child(NodeConnector, dsNodeConnector.key)
-                                    .toInstance();
-                 
-                 val nodeConnectorFromDS = provider.readConfigurationData(nodeConnectorRef) as NodeConnector;
-                 
-                 if(nodeConnectorFromDS != null){
-                     val nodeConnectorStatsFromDs = nodeConnectorFromDS.getAugmentation(FlowCapableNodeConnectorStatisticsData) as FlowCapableNodeConnectorStatistics;
-                     
-                    ret.add(toNodeConnectorStatistics(nodeConnectorStatsFromDs.flowCapableNodeConnectorStatistics,dsNode.id,dsNodeConnector.id));
-                 }
-             }
-         }
-
-        //TODO: Refer TODO (main)
-        val input = new GetAllNodeConnectorsStatisticsInputBuilder();
-        input.setNode(node.toNodeRef);
-        nodeConnectorStatisticsService.getAllNodeConnectorsStatistics(input.build());
-        return ret;
-    }
-
-    override readAllNodeTable(Node node, boolean cached) {
-        val ret = new ArrayList<NodeTableStatistics>();
-        
-        val dsFlowCapableNode= readFlowCapableNode(node.toNodeRef)
-        
-         if(dsFlowCapableNode != null){
-             
-             for (table : dsFlowCapableNode.table){
-                 
-                 val tableStats = table.getAugmentation(FlowTableStatisticsData);
-                 
-                 if(tableStats != null){
-                     ret.add(toNodeTableStatistics(tableStats.flowTableStatistics,table.id,node));
-                 }
-             }
-         }
-
-        //TODO: Refer TODO (main)
-        val input = new GetFlowTablesStatisticsInputBuilder();
-        input.setNode(node.toNodeRef);
-        flowTableStatisticsService.getFlowTablesStatistics(input.build);
-        return ret;
-    }
-
-    override readDescription(Node node, boolean cached) {
-        return toNodeDescription(node.toNodeRef);
-    }
-
-    override readFlow(Node node, Flow targetFlow, boolean cached) {
-        var FlowOnNode ret= null;
-        
-        val tableRef = InstanceIdentifier.builder(Nodes)
-                                        .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node, InventoryMapping.toNodeKey(node))
-                                        .augmentation(FlowCapableNode).child(Table, new TableKey(OPENFLOWV10_TABLE_ID)).toInstance();
-        
-        val it = this.startChange();
-        
-        val table= it.readConfigurationData(tableRef) as Table;
-        
-        if(table != null){
-            LOG.trace("Number of flows installed in table 0 of node {} : {}",node,table.flow.size);
-            
-            for(mdsalFlow : table.flow){
-                if(FromSalConversionsUtils.flowEquals(mdsalFlow, MDFlowMapping.toMDSalflow(targetFlow))){
-                    val statsFromDataStore = mdsalFlow.getAugmentation(FlowStatisticsData);
-                    
-                    if(statsFromDataStore != null){
-                        LOG.debug("Found matching flow in the data store flow table ");
-                        val it = new FlowOnNode(targetFlow);
-                        byteCount =  statsFromDataStore.flowStatistics.byteCount.value.longValue;
-                        packetCount = statsFromDataStore.flowStatistics.packetCount.value.longValue;
-                        durationSeconds = statsFromDataStore.flowStatistics.duration.second.value.intValue;
-                        durationNanoseconds = statsFromDataStore.flowStatistics.duration.nanosecond.value.intValue;
-                        
-                        ret = it;
-                    }
-                }            
-            }
-        }
-        
-        //TODO: Refer TODO (main)
-        val input = new GetFlowStatisticsFromFlowTableInputBuilder;
-        input.setNode(node.toNodeRef);
-        input.fieldsFrom(MDFlowMapping.toMDSalflow(targetFlow));
-        flowStatisticsService.getFlowStatisticsFromFlowTable(input.build)
-        
-        return ret;
-        
-    }
-
-    override readNodeConnector(org.opendaylight.controller.sal.core.NodeConnector connector, boolean cached) {
-        var NodeConnectorStatistics  nodeConnectorStatistics = null;
-    
-        val nodeConnectorRef = InstanceIdentifier.builder(Nodes)
-                                    .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node, InventoryMapping.toNodeKey(connector.node))
-                                    .child(NodeConnector, InventoryMapping.toNodeConnectorKey(connector))
-                                    .toInstance();
-         val provider = this.startChange();
-                 
-         val nodeConnectorFromDS = provider.readConfigurationData(nodeConnectorRef) as NodeConnector;
-                 
-         if(nodeConnectorFromDS != null){
-            val nodeConnectorStatsFromDs = nodeConnectorFromDS.getAugmentation(FlowCapableNodeConnectorStatisticsData) as FlowCapableNodeConnectorStatistics;
-            if(nodeConnectorStatsFromDs != null) {
-                nodeConnectorStatistics = toNodeConnectorStatistics(nodeConnectorStatsFromDs.flowCapableNodeConnectorStatistics,
-                                                                        InventoryMapping.toNodeKey(connector.node).id,
-                                                                        InventoryMapping.toNodeConnectorKey(connector).id);
-            }
-        }
-
-        //TODO: Refer TODO (main)
-        val input = new GetNodeConnectorStatisticsInputBuilder();
-        input.setNode(connector.node.toNodeRef);
-        input.setNodeConnectorId(InventoryMapping.toNodeConnectorKey(connector).id);
-        nodeConnectorStatisticsService.getNodeConnectorStatistics(input.build());
-        return nodeConnectorStatistics;
-    }
-
-    override readNodeTable(NodeTable nodeTable, boolean cached) {
-        var NodeTableStatistics nodeStats = null
-        
-        val tableRef = InstanceIdentifier.builder(Nodes)
-                                        .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node, InventoryMapping.toNodeKey(nodeTable.node))
-                                        .augmentation(FlowCapableNode).child(Table, new TableKey(nodeTable.ID as Short)).toInstance();
-        
-        val it = this.startChange();
-        
-        val table= it.readConfigurationData(tableRef) as Table;
-        
-        if(table != null){
-            val tableStats = table.getAugmentation(FlowTableStatisticsData);
-                 
-             if(tableStats != null){
-                 nodeStats =  toNodeTableStatistics(tableStats.flowTableStatistics,table.id,nodeTable.node);
-            }
-        }
-
-        //TODO: Refer TODO (main)
-        val input = new GetFlowTablesStatisticsInputBuilder();
-        input.setNode(nodeTable.node.toNodeRef);
-        flowTableStatisticsService.getFlowTablesStatistics(input.build);
-        
-        return nodeStats;
-    }
-
-    override onNodeConnectorRemoved(NodeConnectorRemoved update) {
-        // Never received
-    }
-
-    override onNodeRemoved(NodeRemoved notification) {
-        val properties = Collections.<org.opendaylight.controller.sal.core.Property>emptySet();
-
-        removeNodeConnectors(notification.nodeRef.value);
-
-        publishNodeUpdate(notification.nodeRef.toADNode, UpdateType.REMOVED, properties);
-    }
-
-    override onNodeConnectorUpdated(NodeConnectorUpdated update) {
-        var updateType = UpdateType.CHANGED;
-        if(!isKnownNodeConnector(update.nodeConnectorRef.value)){
-            updateType = UpdateType.ADDED;
-            recordNodeConnector(update.nodeConnectorRef.value);
-        }
-
-        var nodeConnector = update.nodeConnectorRef.toADNodeConnector
-
-        publishNodeConnectorUpdate(nodeConnector , updateType , update.toADNodeConnectorProperties);
-    }
-
-    override onNodeUpdated(NodeUpdated notification) {
-        val InstanceIdentifier<? extends DataObject> identifier = notification.nodeRef.value  as InstanceIdentifier<? extends DataObject>;
-
-        var updateType = UpdateType.CHANGED;
-        if ( this._dataService.readOperationalData(identifier) == null ){
-            updateType = UpdateType.ADDED;
-        }
-        publishNodeUpdate(notification.nodeRef.toADNode, updateType, notification.toADNodeProperties);
-
-        //Notify the listeners of IPluginOutReadService
-
-        for (statsPublisher : statisticsPublisher){
-            val nodeRef = InstanceIdentifier.builder(Nodes).child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node,new NodeKey(notification.id)).toInstance;
-            val description = notification.nodeRef.toNodeDescription
-            if(description != null) {
-              statsPublisher.descriptionStatisticsUpdated(nodeRef.toADNode,description);
-            }
-        }
-    }
-
-    override getNodeProps() {
-        val props = new ConcurrentHashMap<Node, Map<String, org.opendaylight.controller.sal.core.Property>>()
-        
-        val nodes = readAllMDNodes()
-        for (node : nodes.node ) {
-            val fcn = node.getAugmentation(FlowCapableNode)
-            if(fcn != null) {
-                val perNodeProps = fcn.toADNodeProperties(node.id)
-                val perNodePropMap = new ConcurrentHashMap<String, org.opendaylight.controller.sal.core.Property>
-                if(perNodeProps != null ) {
-                    for(perNodeProp : perNodeProps) {
-                        perNodePropMap.put(perNodeProp.name,perNodeProp)
-                    }
-                }
-                props.put(new Node(MD_SAL_TYPE, node.id.toADNodeId),perNodePropMap)
-            }
-        }
-        return props;
-    }
-    
-    private def readAllMDNodes() {
-        val nodesRef = InstanceIdentifier.builder(Nodes)
-            .toInstance
-        val reader = TypeSafeDataReader.forReader(dataService)
-        return reader.readOperationalData(nodesRef)
-    }
-    
-    private def readAllMDNodeConnectors(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node node) {
-        val nodeRef = InstanceIdentifier.builder(Nodes)
-            .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node,new NodeKey(node.id))
-            .toInstance
-        val reader = TypeSafeDataReader.forReader(dataService)
-        return reader.readOperationalData(nodeRef).nodeConnector
-    }
-
-    override getNodeConnectorProps(Boolean refresh) {
-        // Note, because the MD-SAL has a unified data store, we can ignore the Boolean refresh, as we have no secondary 
-        // data store to refresh from
-        val props = new ConcurrentHashMap<org.opendaylight.controller.sal.core.NodeConnector, Map<String, org.opendaylight.controller.sal.core.Property>>()
-        val nodes = readAllMDNodes()
-        for (node : nodes.node) {
-            val ncs = node.readAllMDNodeConnectors
-            if(ncs != null) {
-                for( nc : ncs ) {
-                    val fcnc = nc.getAugmentation(FlowCapableNodeConnector)
-                    if(fcnc != null) {
-                        val ncps = fcnc.toADNodeConnectorProperties
-                        val ncpsm = new ConcurrentHashMap<String, org.opendaylight.controller.sal.core.Property>
-                        if(ncps != null) {
-                            for(p : ncps) {
-                                ncpsm.put(p.name,p)
-                            }
-                        }  
-                        props.put(nc.id.toADNodeConnector(node.id),ncpsm)
-                    }
-                }
-            }
-        }
-        return props
-    }
-
-    private def FlowCapableNode readFlowCapableNode(NodeRef ref) {
-        val dataObject = dataService.readOperationalData(ref.value as InstanceIdentifier<? extends DataObject>);
-        if(dataObject != null) {
-            val node = dataObject.checkInstanceOf(
-                org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node);
-            return node.getAugmentation(FlowCapableNode);
-        }
-        return null;
-    }
-
-    private def FlowCapableNodeConnector readFlowCapableNodeConnector(NodeConnectorRef ref) {
-        val dataObject = dataService.readOperationalData(ref.value as InstanceIdentifier<? extends DataObject>);
-        val node = dataObject.checkInstanceOf(
-            NodeConnector);
-        return node.getAugmentation(FlowCapableNodeConnector);
-    }
-
-    private def toNodeConnectorStatistics(
-        org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.NodeConnectorStatistics nodeConnectorStatistics, NodeId nodeId, NodeConnectorId nodeConnectorId) {
-            
-            val it = new NodeConnectorStatistics();
-            
-            receivePacketCount = nodeConnectorStatistics.packets.received.longValue;
-            transmitPacketCount = nodeConnectorStatistics.packets.transmitted.longValue;
-            
-            receiveByteCount = nodeConnectorStatistics.bytes.received.longValue;
-            transmitByteCount = nodeConnectorStatistics.bytes.transmitted.longValue;
-            
-            receiveDropCount = nodeConnectorStatistics.receiveDrops.longValue;
-            transmitDropCount = nodeConnectorStatistics.transmitDrops.longValue;
-            
-            receiveErrorCount = nodeConnectorStatistics.receiveErrors.longValue;
-            transmitErrorCount = nodeConnectorStatistics.transmitErrors.longValue;
-            
-            receiveFrameErrorCount = nodeConnectorStatistics.receiveFrameError.longValue;
-            receiveOverRunErrorCount = nodeConnectorStatistics.receiveOverRunError.longValue;
-            receiveCRCErrorCount = nodeConnectorStatistics.receiveCrcError.longValue;
-            collisionCount = nodeConnectorStatistics.collisionCount.longValue;
-            
-            val nodeConnectorRef = InstanceIdentifier.builder(Nodes)
-                                .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node,new NodeKey(nodeId))
-                                .child(NodeConnector,new NodeConnectorKey(nodeConnectorId)).toInstance;
-            
-            nodeConnector = NodeMapping.toADNodeConnector(new NodeConnectorRef(nodeConnectorRef));
-            
-            return it;
-    }
-
-    private def toNodeTableStatistics(
-        FlowTableStatistics tableStats,
-        Short tableId,Node node){
-        var it = new NodeTableStatistics();
-        
-        activeCount = tableStats.activeFlows.value.intValue;
-        lookupCount = tableStats.packetsLookedUp.value.intValue;
-        matchedCount = tableStats.packetsMatched.value.intValue;
-        name = tableId.toString;
-        nodeTable = new NodeTable(NodeMapping.MD_SAL_TYPE,tableId,node);
-        return it;
-    }
-    
-    private def toNodeDescription(NodeRef nodeRef){
-        val capableNode = readFlowCapableNode(nodeRef);
-        if(capableNode !=null) {
-            val it = new NodeDescription()
-            manufacturer = capableNode.manufacturer
-            serialNumber = capableNode.serialNumber
-            software = capableNode.software
-            description = capableNode.description
-            
-            return it;
-         }
-         return null;
-    }
-    
-    
-    def Edge toADEdge(Link link) {
-        new Edge(link.source.toADNodeConnector,link.destination.toADNodeConnector)
-    }
-    
-    /*
-     * OpendaylightFlowStatisticsListener interface implementation
-     */
-    override onAggregateFlowStatisticsUpdate(AggregateFlowStatisticsUpdate notification) {
-        //Ignoring this notification as there does not seem to be a way to bubble this up to AD-SAL
-    }
-    
-    override onFlowsStatisticsUpdate(FlowsStatisticsUpdate notification) {
-        
-        val adsalFlowsStatistics = new ArrayList<FlowOnNode>();
-        val nodeRef = InstanceIdentifier.builder(Nodes).child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node,new NodeKey(notification.id)).toInstance;
-        
-        for(flowStats : notification.flowAndStatisticsMapList){
-            if(flowStats.tableId == 0)
-                adsalFlowsStatistics.add(toFlowOnNode(flowStats,nodeRef.toADNode));
-        }
-        
-        for (statsPublisher : statisticsPublisher){
-            statsPublisher.nodeFlowStatisticsUpdated(nodeRef.toADNode,adsalFlowsStatistics);
-        }
-        
-    }
-    /*
-     * OpendaylightFlowTableStatisticsListener interface implementation
-     */    
-    override onFlowTableStatisticsUpdate(FlowTableStatisticsUpdate notification) {
-        var adsalFlowTableStatistics = new ArrayList<NodeTableStatistics>();
-        
-        for(stats : notification.flowTableAndStatisticsMap){
-            if (stats.tableId.value == 0){
-                val it = new NodeTableStatistics();
-                activeCount = stats.activeFlows.value.intValue;
-                lookupCount = stats.packetsLookedUp.value.longValue;
-                matchedCount = stats.packetsMatched.value.longValue;
-                
-                adsalFlowTableStatistics.add(it);
-            }
-        }
-        for (statsPublisher : statisticsPublisher){
-            val nodeRef = InstanceIdentifier.builder(Nodes).child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node,new NodeKey(notification.id)).toInstance;
-            statsPublisher.nodeTableStatisticsUpdated(nodeRef.toADNode,adsalFlowTableStatistics);
-        }
-    }
-    
-    /*
-     * OpendaylightPortStatisticsUpdate interface implementation
-     */
-    override onNodeConnectorStatisticsUpdate(NodeConnectorStatisticsUpdate notification) {
-        
-        val adsalPortStatistics  = new ArrayList<NodeConnectorStatistics>();
-        
-        for(nodeConnectorStatistics : notification.nodeConnectorStatisticsAndPortNumberMap){
-            adsalPortStatistics.add(toNodeConnectorStatistics(nodeConnectorStatistics,notification.id,nodeConnectorStatistics.nodeConnectorId));
-        }
-        
-        for (statsPublisher : statisticsPublisher){
-            val nodeRef = InstanceIdentifier.builder(Nodes).child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node,new NodeKey(notification.id)).toInstance;
-            statsPublisher.nodeConnectorStatisticsUpdated(nodeRef.toADNode,adsalPortStatistics);
-        }
-        
-    }
-    
-    private static def toFlowOnNode (FlowAndStatisticsMapList flowAndStatsMap,Node node){
-        
-        val it = new FlowOnNode(ToSalConversionsUtils.toFlow(flowAndStatsMap,node));
-        
-        byteCount = flowAndStatsMap.byteCount.value.longValue;
-        packetCount = flowAndStatsMap.packetCount.value.longValue;
-        durationSeconds = flowAndStatsMap.duration.second.value.intValue;
-        durationNanoseconds = flowAndStatsMap.duration.nanosecond.value.intValue;
-        
-        return it;
-    }
-
-    override  getConfiguredNotConnectedNodes() {
-        return Collections.emptySet();
-    }
-
-
-    private def publishNodeUpdate(Node node, UpdateType updateType, Set<org.opendaylight.controller.sal.core.Property> properties){
-        for( publisher : inventoryPublisher){
-            publisher.updateNode(node, updateType, properties);
-        }
-    }
-
-    private def publishNodeConnectorUpdate(org.opendaylight.controller.sal.core.NodeConnector nodeConnector, UpdateType updateType, Set<org.opendaylight.controller.sal.core.Property> properties){
-        for( publisher : inventoryPublisher){
-            publisher.updateNodeConnector(nodeConnector, updateType, properties);
-        }
-    }
-
-    private def isKnownNodeConnector(InstanceIdentifier<? extends Object> nodeConnectorIdentifier){
-        if(nodeConnectorIdentifier.path.size() < 3) {
-            return false;
-        }
-
-        val nodePath = nodeConnectorIdentifier.path.get(1);
-        val nodeConnectorPath = nodeConnectorIdentifier.getPath().get(2);
-
-        val nodeConnectors = nodeToNodeConnectorsMap.get(nodePath);
-
-        if(nodeConnectors == null){
-            return false;
-        }
-        return nodeConnectors.contains(nodeConnectorPath);
-    }
-
-
-    private def recordNodeConnector(InstanceIdentifier<? extends Object> nodeConnectorIdentifier){
-        if(nodeConnectorIdentifier.path.size() < 3) {
-            return false;
-        }
-
-        val nodePath = nodeConnectorIdentifier.path.get(1);
-        val nodeConnectorPath = nodeConnectorIdentifier.getPath().get(2);
-
-        nodeToNodeConnectorsLock.lock();
-
-        try {
-            var nodeConnectors = nodeToNodeConnectorsMap.get(nodePath);
-
-            if(nodeConnectors == null){
-                nodeConnectors = new ArrayList<InstanceIdentifier.PathArgument>();
-                nodeToNodeConnectorsMap.put(nodePath, nodeConnectors);
-            }
-
-            nodeConnectors.add(nodeConnectorPath);
-        } finally {
-            nodeToNodeConnectorsLock.unlock();
-        }
-    }
-
-    private def removeNodeConnectors(InstanceIdentifier<? extends Object> nodeIdentifier){
-        val nodePath = nodeIdentifier.path.get(1);
-
-        nodeToNodeConnectorsMap.remove(nodePath);
-    }
-}
index 29904220d710933c71320f0069e78aef03e2046d..1e4d97446f3596b6fef7668b3bd2f74da316a9f1 100644 (file)
 /**
  * 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.compatibility;
 
-import org.eclipse.xtend2.lib.StringConcatenation;
-import org.eclipse.xtext.xbase.lib.Conversions;
-import org.eclipse.xtext.xbase.lib.IterableExtensions;
-import org.opendaylight.controller.sal.core.Node;
-import org.opendaylight.controller.sal.core.NodeConnector;
+import java.util.Iterator;
+
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
 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.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 org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
-
-import java.util.List;
-
-@SuppressWarnings("all")
-public class InventoryMapping {
-  public static NodeConnector toAdNodeConnector(final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector> identifier) {
-    final List<PathArgument> path = identifier.getPath();
-    final PathArgument lastPathArgument = IterableExtensions.<PathArgument>last(path);
-    final NodeConnectorKey tpKey = ((IdentifiableItem<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector,NodeConnectorKey>) lastPathArgument).getKey();
-    return InventoryMapping.nodeConnectorFromId(tpKey.getId().getValue());
-  }
-  
-  public static Node toAdNode(final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node> identifier) {
-    final List<PathArgument> path = identifier.getPath();
-    final PathArgument lastPathArgument = IterableExtensions.<PathArgument>last(path);
-    final NodeKey tpKey = ((IdentifiableItem<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node,NodeKey>) lastPathArgument).getKey();
-    return InventoryMapping.nodeFromNodeId(tpKey.getId().getValue());
-  }
-  
-  public static NodeRef toNodeRef(final Node node) {
-    final NodeId nodeId = new NodeId(InventoryMapping.toNodeId(node));
-    final NodeKey nodeKey = new NodeKey(nodeId);
-    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();
-    return new NodeRef(path);
-  }
-  
-  public static NodeKey toNodeKey(final Node node) {
-    final NodeId nodeId = new NodeId(InventoryMapping.toNodeId(node));
-    return new NodeKey(nodeId);
-  }
-  
-  public static NodeConnectorKey toNodeConnectorKey(final NodeConnector nc) {
-    final NodeConnectorId nodeConnectorId = new NodeConnectorId(InventoryMapping.toNodeConnectorId(nc));
-    return new NodeConnectorKey(nodeConnectorId);
-  }
-  
-  public static String toNodeId(final Node node) {
-    final StringConcatenation builder = new StringConcatenation();
-    builder.append("ad-sal:");
-    builder.append(node.getType(), "");
-    builder.append("::");
-    builder.append(node.getNodeIDString(), "");
-    return builder.toString();
-  }
-  
-  public static String toNodeConnectorId(final NodeConnector nc) {
-    final StringConcatenation builder = new StringConcatenation();
-    builder.append(InventoryMapping.toNodeId(nc.getNode()), "");
-    builder.append("::");
-    builder.append(nc.getNodeConnectorIDString(), "");
-    return builder.toString();
-  }
-  
-  public static Node nodeFromNodeId(final String nodeId) {
-    final String[] split = nodeId.split("::");
-    return InventoryMapping.nodeFromString(split);
-  }
-  
-  public static NodeConnector nodeConnectorFromId(final String invId) {
-    final String[] split = invId.split("::");
-    return InventoryMapping.nodeConnectorFromString(split);
-  }
-  
-  private static NodeConnector nodeConnectorFromString(final String[] string) {
-    final List<String> subList = ((List<String>)Conversions.doWrapArray(string)).subList(0, 1);
-    final Node node = InventoryMapping.nodeFromString(((String[])Conversions.unwrapArray(subList, String.class)));
-    final String index3 = string[2];
-    return NodeConnector.fromStringNoNode(index3, node);
-  }
-  
-  private static Node nodeFromString(final String[] strings) {
-      String index0 = strings[0];
-      final String type = index0.substring(6);
-      String id = strings[1];
-      return Node.fromString(type, id);
-  }
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+
+import com.google.common.base.Splitter;
+
+public final class InventoryMapping {
+    private static final String NODE_TYPE_STRING = "::";
+    private static final Splitter NODE_TYPE_SPLITTER = Splitter.on(NODE_TYPE_STRING);
+
+    private InventoryMapping() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    public static org.opendaylight.controller.sal.core.NodeConnector toAdNodeConnector(final InstanceIdentifier<NodeConnector> identifier) {
+        @SuppressWarnings("unchecked")
+        final NodeConnectorKey tpKey = ((KeyedInstanceIdentifier<NodeConnector, NodeConnectorKey>) identifier).getKey();
+        return InventoryMapping.nodeConnectorFromId(tpKey.getId().getValue());
+    }
+
+    public static org.opendaylight.controller.sal.core.Node toAdNode(final InstanceIdentifier<Node> identifier) {
+        @SuppressWarnings("unchecked")
+        final NodeKey tpKey = ((KeyedInstanceIdentifier<Node,NodeKey>)identifier).getKey();
+        return InventoryMapping.nodeFromNodeId(tpKey.getId().getValue());
+    }
+
+    public static NodeRef toNodeRef(final org.opendaylight.controller.sal.core.Node node) {
+        final NodeKey nodeKey = new NodeKey(new NodeId(InventoryMapping.toNodeId(node)));
+        final InstanceIdentifier<Node> path = InstanceIdentifier.builder(Nodes.class)
+                .child(Node.class, nodeKey).toInstance();
+        return new NodeRef(path);
+    }
+
+    public static NodeKey toNodeKey(final org.opendaylight.controller.sal.core.Node node) {
+        final NodeId nodeId = new NodeId(InventoryMapping.toNodeId(node));
+        return new NodeKey(nodeId);
+    }
+
+    public static NodeConnectorKey toNodeConnectorKey(final org.opendaylight.controller.sal.core.NodeConnector nc) {
+        final NodeConnectorId nodeConnectorId = new NodeConnectorId(InventoryMapping.toNodeConnectorId(nc));
+        return new NodeConnectorKey(nodeConnectorId);
+    }
+
+    private static StringBuilder nodeIdBulder(final org.opendaylight.controller.sal.core.Node node) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("ad-sal:");
+        sb.append(node.getType());
+        sb.append(NODE_TYPE_STRING);
+        sb.append(node.getNodeIDString());
+        return sb;
+    }
+
+    public static String toNodeId(final org.opendaylight.controller.sal.core.Node node) {
+        return nodeIdBulder(node).toString();
+    }
+
+    public static String toNodeConnectorId(final org.opendaylight.controller.sal.core.NodeConnector nc) {
+        final StringBuilder sb = nodeIdBulder(nc.getNode());
+        sb.append(NODE_TYPE_STRING);
+        sb.append(nc.getNodeConnectorIDString());
+        return sb.toString();
+    }
+
+    public static org.opendaylight.controller.sal.core.Node nodeFromNodeId(final String nodeId) {
+        return InventoryMapping.nodeFromStrings(NODE_TYPE_SPLITTER.split(nodeId).iterator());
+    }
+
+    public static org.opendaylight.controller.sal.core.NodeConnector nodeConnectorFromId(final String invId) {
+        return InventoryMapping.nodeConnectorFromString(NODE_TYPE_SPLITTER.split(invId).iterator());
+    }
+
+    private static org.opendaylight.controller.sal.core.NodeConnector nodeConnectorFromString(final Iterator<String> it) {
+        final org.opendaylight.controller.sal.core.Node node = InventoryMapping.nodeFromStrings(it);
+        return org.opendaylight.controller.sal.core.NodeConnector.fromStringNoNode(it.next(), node);
+    }
+
+    private static org.opendaylight.controller.sal.core.Node nodeFromStrings(final Iterator<String> it) {
+        final String type = it.next().substring(6);
+        return org.opendaylight.controller.sal.core.Node.fromString(type, it.next());
+    }
 }
index 2c95252ac7f6a3e571dcb8cceb03de24dbc2d0c0..ba86ad99fb9bcde573c3cb4404d12d444ab697ba 100644 (file)
@@ -61,314 +61,314 @@ import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
 
 public final class NodeMapping {
-  public final static String MD_SAL_TYPE = "MD_SAL";
-
-  private final static Class<Node> NODE_CLASS = Node.class;
-
-  private final static Class<NodeConnector> NODECONNECTOR_CLASS = NodeConnector.class;
-
-  private NodeMapping() {
-    throw new UnsupportedOperationException("Utility class. Instantiation is not allowed.");
-  }
-
-  public static org.opendaylight.controller.sal.core.Node toADNode(final InstanceIdentifier<? extends Object> node) throws ConstructionException {
-    NodeId nodeId = NodeMapping.toNodeId(node);
-    return NodeMapping.toADNode(nodeId);
-  }
-
-  public static org.opendaylight.controller.sal.core.Node toADNode(final NodeId id) throws ConstructionException {
-      String aDNodeId = NodeMapping.toADNodeId(id);
-      return  new org.opendaylight.controller.sal.core.Node(NodeMapping.MD_SAL_TYPE, aDNodeId);
-  }
-
-  public static NodeId toNodeId(final InstanceIdentifier<?> id) {
-    final NodeKey key = id.firstKeyOf(Node.class, NodeKey.class);
-    Preconditions.checkArgument(key != null, "No node identifier found in %s", id);
-    return key.getId();
-  }
-
-  public static String toADNodeId(final NodeId nodeId) {
-    return nodeId.getValue();
-  }
-
-  public static org.opendaylight.controller.sal.core.NodeConnector toADNodeConnector(final NodeConnectorRef source) throws ConstructionException {
-    final InstanceIdentifier<?> id = Preconditions.checkNotNull(source.getValue());
-    final NodeConnectorKey key = id.firstKeyOf(NodeConnector.class, NodeConnectorKey.class);
-    return NodeMapping.toADNodeConnector(key.getId(), NodeMapping.toNodeId(id));
-  }
-
-  public static org.opendaylight.controller.sal.core.NodeConnector toADNodeConnector(final NodeConnectorId ncid, final NodeId nid) throws ConstructionException {
-    String nodeConnectorType = NodeMapping.toNodeConnectorType(ncid, nid);
-    Object aDNodeConnectorId = NodeMapping.toADNodeConnectorId(ncid, nid);
-    org.opendaylight.controller.sal.core.Node aDNode = NodeMapping.toADNode(nid);
-    return new org.opendaylight.controller.sal.core.NodeConnector(nodeConnectorType, aDNodeConnectorId, aDNode);
-  }
-
-  public static String toNodeConnectorType(final NodeConnectorId ncId, final NodeId nodeId) {
-    if (ncId.equals(toLocalNodeConnectorId(nodeId))) {
-        return NodeConnectorIDType.SWSTACK;
-    } else if (ncId.equals(toNormalNodeConnectorId(nodeId))) {
-        return NodeConnectorIDType.HWPATH;
-    } else if (ncId.equals(toControllerNodeConnectorId(nodeId))) {
-        return NodeConnectorIDType.CONTROLLER;
-    }
-    return MD_SAL_TYPE;
-  }
-
-  public static Object toADNodeConnectorId(final NodeConnectorId nodeConnectorId, final NodeId nodeId) {
-    if (nodeConnectorId.equals(toLocalNodeConnectorId(nodeId)) ||
-        nodeConnectorId.equals(toNormalNodeConnectorId(nodeId)) ||
-        nodeConnectorId.equals(toControllerNodeConnectorId(nodeId))) {
-        return org.opendaylight.controller.sal.core.NodeConnector.SPECIALNODECONNECTORID;
-    }
-    return nodeConnectorId.getValue();
-  }
-
-  public static NodeConnectorId toControllerNodeConnectorId(final NodeId node) {
-    return new NodeConnectorId(node.getValue() + ":" + 4294967293L);
-  }
-
-  public static NodeConnectorId toLocalNodeConnectorId(final NodeId node) {
-    return new NodeConnectorId(node.getValue() + ":" + 4294967294L);
-  }
-
-  public static NodeConnectorId toNormalNodeConnectorId(final NodeId node) {
-    return new NodeConnectorId(node.getValue() + ":" + 4294967290L);
-  }
-
-  public static NodeRef toNodeRef(final org.opendaylight.controller.sal.core.Node node) {
-    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(Nodes.class).child(NODE_CLASS, nodeKey).toInstance();
-    return new NodeRef(nodePath);
-  }
-
-  public static NodeConnectorRef toNodeConnectorRef(final org.opendaylight.controller.sal.core.NodeConnector nodeConnector) {
-
-    final NodeRef node = NodeMapping.toNodeRef(nodeConnector.getNode());
-    @SuppressWarnings("unchecked")
-    final InstanceIdentifier<Node> nodePath = ((InstanceIdentifier<Node>) node.getValue());
-    NodeConnectorId nodeConnectorId = null;
-
-    if (nodeConnector.getID().equals(org.opendaylight.controller.sal.core.NodeConnector.SPECIALNODECONNECTORID)) {
-        final NodeId nodeId = toNodeId(nodePath);
-        final String nodeConnectorType = nodeConnector.getType();
-        if (nodeConnectorType.equals(NodeConnectorIDType.SWSTACK)) {
-            nodeConnectorId = toLocalNodeConnectorId(nodeId);
-        } else if (nodeConnectorType.equals(NodeConnectorIDType.HWPATH)) {
-            nodeConnectorId = toNormalNodeConnectorId(nodeId);
-        } else if (nodeConnectorType.equals(NodeConnectorIDType.CONTROLLER)) {
-            nodeConnectorId = toControllerNodeConnectorId(nodeId);
+    public final static String MD_SAL_TYPE = "MD_SAL";
+
+    private final static Class<Node> NODE_CLASS = Node.class;
+
+    private final static Class<NodeConnector> NODECONNECTOR_CLASS = NodeConnector.class;
+
+    private NodeMapping() {
+        throw new UnsupportedOperationException("Utility class. Instantiation is not allowed.");
+    }
+
+    public static org.opendaylight.controller.sal.core.Node toADNode(final InstanceIdentifier<? extends Object> node) throws ConstructionException {
+        NodeId nodeId = NodeMapping.toNodeId(node);
+        return NodeMapping.toADNode(nodeId);
+    }
+
+    public static org.opendaylight.controller.sal.core.Node toADNode(final NodeId id) throws ConstructionException {
+        String aDNodeId = NodeMapping.toADNodeId(id);
+        return new org.opendaylight.controller.sal.core.Node(NodeMapping.MD_SAL_TYPE, aDNodeId);
+    }
+
+    public static NodeId toNodeId(final InstanceIdentifier<?> id) {
+        final NodeKey key = id.firstKeyOf(Node.class, NodeKey.class);
+        Preconditions.checkArgument(key != null, "No node identifier found in %s", id);
+        return key.getId();
+    }
+
+    public static String toADNodeId(final NodeId nodeId) {
+        return nodeId.getValue();
+    }
+
+    public static org.opendaylight.controller.sal.core.NodeConnector toADNodeConnector(final NodeConnectorRef source) throws ConstructionException {
+        final InstanceIdentifier<?> id = Preconditions.checkNotNull(source.getValue());
+        final NodeConnectorKey key = id.firstKeyOf(NodeConnector.class, NodeConnectorKey.class);
+        return NodeMapping.toADNodeConnector(key.getId(), NodeMapping.toNodeId(id));
+    }
+
+    public static org.opendaylight.controller.sal.core.NodeConnector toADNodeConnector(final NodeConnectorId ncid, final NodeId nid) throws ConstructionException {
+        String nodeConnectorType = NodeMapping.toNodeConnectorType(ncid, nid);
+        Object aDNodeConnectorId = NodeMapping.toADNodeConnectorId(ncid, nid);
+        org.opendaylight.controller.sal.core.Node aDNode = NodeMapping.toADNode(nid);
+        return new org.opendaylight.controller.sal.core.NodeConnector(nodeConnectorType, aDNodeConnectorId, aDNode);
+    }
+
+    public static String toNodeConnectorType(final NodeConnectorId ncId, final NodeId nodeId) {
+        if (ncId.equals(toLocalNodeConnectorId(nodeId))) {
+            return NodeConnectorIDType.SWSTACK;
+        } else if (ncId.equals(toNormalNodeConnectorId(nodeId))) {
+            return NodeConnectorIDType.HWPATH;
+        } else if (ncId.equals(toControllerNodeConnectorId(nodeId))) {
+            return NodeConnectorIDType.CONTROLLER;
         }
-    } else {
-        nodeConnectorId = new NodeConnectorId(Arguments.<String>checkInstanceOf(nodeConnector.getID(), String.class));
+        return MD_SAL_TYPE;
     }
-    final NodeConnectorKey connectorKey = new NodeConnectorKey(nodeConnectorId);
-    final InstanceIdentifier<NodeConnector> path = nodePath.child(NODECONNECTOR_CLASS, connectorKey);
-    return new NodeConnectorRef(path);
-  }
 
-  public static org.opendaylight.controller.sal.core.Node toADNode(final NodeRef node) throws ConstructionException {
-    return NodeMapping.toADNode(node.getValue());
-  }
+    public static Object toADNodeConnectorId(final NodeConnectorId nodeConnectorId, final NodeId nodeId) {
+        if (nodeConnectorId.equals(toLocalNodeConnectorId(nodeId)) ||
+                nodeConnectorId.equals(toNormalNodeConnectorId(nodeId)) ||
+                nodeConnectorId.equals(toControllerNodeConnectorId(nodeId))) {
+            return org.opendaylight.controller.sal.core.NodeConnector.SPECIALNODECONNECTORID;
+        }
+        return nodeConnectorId.getValue();
+    }
 
-  public static HashSet<Property> toADNodeConnectorProperties(final NodeConnectorUpdated nc) {
-    final FlowCapableNodeConnectorUpdated fcncu = nc.<FlowCapableNodeConnectorUpdated>getAugmentation(FlowCapableNodeConnectorUpdated.class);
-    if (!Objects.equal(fcncu, null)) {
-      return NodeMapping.toADNodeConnectorProperties(fcncu);
+    public static NodeConnectorId toControllerNodeConnectorId(final NodeId node) {
+        return new NodeConnectorId(node.getValue() + ":" + 4294967293L);
     }
-    return new HashSet<Property>();
-  }
 
-  public static HashSet<Property> toADNodeConnectorProperties(final NodeConnector nc) {
-    final FlowCapableNodeConnector fcnc = nc.<FlowCapableNodeConnector>getAugmentation(FlowCapableNodeConnector.class);
-    if (!Objects.equal(fcnc, null)) {
-      return NodeMapping.toADNodeConnectorProperties(fcnc);
+    public static NodeConnectorId toLocalNodeConnectorId(final NodeId node) {
+        return new NodeConnectorId(node.getValue() + ":" + 4294967294L);
     }
-    return new HashSet<Property>();
-  }
 
-  public static HashSet<Property> toADNodeConnectorProperties(final FlowNodeConnector fcncu) {
+    public static NodeConnectorId toNormalNodeConnectorId(final NodeId node) {
+        return new NodeConnectorId(node.getValue() + ":" + 4294967290L);
+    }
+
+    public static NodeRef toNodeRef(final org.opendaylight.controller.sal.core.Node node) {
+        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(Nodes.class).child(NODE_CLASS, nodeKey).toInstance();
+        return new NodeRef(nodePath);
+    }
 
-    final HashSet<org.opendaylight.controller.sal.core.Property> props = new HashSet<>();
-    if (fcncu != null) {
-        if (fcncu.getCurrentFeature() != null && toAdBandwidth(fcncu.getCurrentFeature()) != null) {
-            props.add(toAdBandwidth(fcncu.getCurrentFeature()));
+    public static NodeConnectorRef toNodeConnectorRef(final org.opendaylight.controller.sal.core.NodeConnector nodeConnector) {
+
+        final NodeRef node = NodeMapping.toNodeRef(nodeConnector.getNode());
+        @SuppressWarnings("unchecked")
+        final InstanceIdentifier<Node> nodePath = ((InstanceIdentifier<Node>) node.getValue());
+        NodeConnectorId nodeConnectorId = null;
+
+        if (nodeConnector.getID().equals(org.opendaylight.controller.sal.core.NodeConnector.SPECIALNODECONNECTORID)) {
+            final NodeId nodeId = toNodeId(nodePath);
+            final String nodeConnectorType = nodeConnector.getType();
+            if (nodeConnectorType.equals(NodeConnectorIDType.SWSTACK)) {
+                nodeConnectorId = toLocalNodeConnectorId(nodeId);
+            } else if (nodeConnectorType.equals(NodeConnectorIDType.HWPATH)) {
+                nodeConnectorId = toNormalNodeConnectorId(nodeId);
+            } else if (nodeConnectorType.equals(NodeConnectorIDType.CONTROLLER)) {
+                nodeConnectorId = toControllerNodeConnectorId(nodeId);
+            }
+        } else {
+            nodeConnectorId = new NodeConnectorId(Arguments.<String>checkInstanceOf(nodeConnector.getID(), String.class));
         }
-        if (fcncu.getAdvertisedFeatures() != null && toAdAdvertizedBandwidth(fcncu.getAdvertisedFeatures()) != null) {
-            props.add(toAdAdvertizedBandwidth(fcncu.getAdvertisedFeatures()));
+        final NodeConnectorKey connectorKey = new NodeConnectorKey(nodeConnectorId);
+        final InstanceIdentifier<NodeConnector> path = nodePath.child(NODECONNECTOR_CLASS, connectorKey);
+        return new NodeConnectorRef(path);
+    }
+
+    public static org.opendaylight.controller.sal.core.Node toADNode(final NodeRef node) throws ConstructionException {
+        return NodeMapping.toADNode(node.getValue());
+    }
+
+    public static HashSet<Property> toADNodeConnectorProperties(final NodeConnectorUpdated nc) {
+        final FlowCapableNodeConnectorUpdated fcncu = nc.<FlowCapableNodeConnectorUpdated>getAugmentation(FlowCapableNodeConnectorUpdated.class);
+        if (!Objects.equal(fcncu, null)) {
+            return NodeMapping.toADNodeConnectorProperties(fcncu);
         }
-        if (fcncu.getSupported() != null && toAdSupportedBandwidth(fcncu.getSupported()) != null) {
-            props.add(toAdSupportedBandwidth(fcncu.getSupported()));
+        return new HashSet<Property>();
+    }
+
+    public static HashSet<Property> toADNodeConnectorProperties(final NodeConnector nc) {
+        final FlowCapableNodeConnector fcnc = nc.<FlowCapableNodeConnector>getAugmentation(FlowCapableNodeConnector.class);
+        if (!Objects.equal(fcnc, null)) {
+            return NodeMapping.toADNodeConnectorProperties(fcnc);
         }
-        if (fcncu.getPeerFeatures() != null && toAdPeerBandwidth(fcncu.getPeerFeatures()) != null) {
-            props.add(toAdPeerBandwidth(fcncu.getPeerFeatures()));
+        return new HashSet<Property>();
+    }
+
+    public static HashSet<Property> toADNodeConnectorProperties(final FlowNodeConnector fcncu) {
+
+        final HashSet<org.opendaylight.controller.sal.core.Property> props = new HashSet<>();
+        if (fcncu != null) {
+            if (fcncu.getCurrentFeature() != null && toAdBandwidth(fcncu.getCurrentFeature()) != null) {
+                props.add(toAdBandwidth(fcncu.getCurrentFeature()));
+            }
+            if (fcncu.getAdvertisedFeatures() != null && toAdAdvertizedBandwidth(fcncu.getAdvertisedFeatures()) != null) {
+                props.add(toAdAdvertizedBandwidth(fcncu.getAdvertisedFeatures()));
+            }
+            if (fcncu.getSupported() != null && toAdSupportedBandwidth(fcncu.getSupported()) != null) {
+                props.add(toAdSupportedBandwidth(fcncu.getSupported()));
+            }
+            if (fcncu.getPeerFeatures() != null && toAdPeerBandwidth(fcncu.getPeerFeatures()) != null) {
+                props.add(toAdPeerBandwidth(fcncu.getPeerFeatures()));
+            }
+            if (fcncu.getName() != null && toAdName(fcncu.getName()) != null) {
+                props.add(toAdName(fcncu.getName()));
+            }
+            if (fcncu.getConfiguration() != null && toAdConfig(fcncu.getConfiguration()) != null) {
+                props.add(toAdConfig(fcncu.getConfiguration()));
+            }
+            if (fcncu.getState() != null && toAdState(fcncu.getState()) != null) {
+                props.add(toAdState(fcncu.getState()));
+            }
         }
-        if (fcncu.getName() != null && toAdName(fcncu.getName()) != null) {
-            props.add(toAdName(fcncu.getName()));
+        return props;
+    }
+
+    public static Name toAdName(final String name) {
+        return new Name(name);
+    }
+
+    public static Config toAdConfig(final PortConfig pc) {
+        Config config = null;
+        if (pc.isPORTDOWN()) {
+            config = new Config(Config.ADMIN_DOWN);
+        } else {
+            config = new Config(Config.ADMIN_UP);
+        }
+        return config;
+    }
+
+    public static org.opendaylight.controller.sal.core.State toAdState(final State s) {
+
+        org.opendaylight.controller.sal.core.State state = null;
+        if (s.isLinkDown()) {
+            state = new org.opendaylight.controller.sal.core.State(org.opendaylight.controller.sal.core.State.EDGE_DOWN);
+        } else {
+            state = new org.opendaylight.controller.sal.core.State(org.opendaylight.controller.sal.core.State.EDGE_UP);
         }
-        if (fcncu.getConfiguration() != null && toAdConfig(fcncu.getConfiguration()) != null) {
-            props.add(toAdConfig(fcncu.getConfiguration()));
+        return state;
+    }
+
+    public static Bandwidth toAdBandwidth(final PortFeatures pf) {
+        Bandwidth bw = null;
+        if (pf.isTenMbHd() || pf.isTenMbFd()) {
+            bw = new Bandwidth(Bandwidth.BW10Mbps);
+        } else if (pf.isHundredMbHd() || pf.isHundredMbFd()) {
+            bw = new Bandwidth(Bandwidth.BW100Mbps);
+        } else if (pf.isOneGbHd() || pf.isOneGbFd()) {
+            bw = new Bandwidth(Bandwidth.BW1Gbps);
+        } else if (pf.isOneGbFd()) {
+            bw = new Bandwidth(Bandwidth.BW10Gbps);
+        } else if (pf.isTenGbFd()) {
+            bw = new Bandwidth(Bandwidth.BW10Gbps);
+        } else if (pf.isFortyGbFd()) {
+            bw = new Bandwidth(Bandwidth.BW40Gbps);
+        } else if (pf.isHundredGbFd()) {
+            bw = new Bandwidth(Bandwidth.BW100Gbps);
+        } else if (pf.isOneTbFd()) {
+            bw = new Bandwidth(Bandwidth.BW1Tbps);
         }
-        if (fcncu.getState() != null && toAdState(fcncu.getState()) != null) {
-            props.add(toAdState(fcncu.getState()));
+        return bw;
+    }
+
+    public static AdvertisedBandwidth toAdAdvertizedBandwidth(final PortFeatures pf) {
+        AdvertisedBandwidth abw = null;
+        final Bandwidth bw = toAdBandwidth(pf);
+        if (bw != null) {
+            abw = new AdvertisedBandwidth(bw.getValue());
         }
+        return abw;
     }
-    return props;
-  }
-
-  public static Name toAdName(final String name) {
-    return new Name(name);
-  }
-
-  public static Config toAdConfig(final PortConfig pc) {
-    Config config = null;
-    if (pc.isPORTDOWN()) {
-        config = new Config(Config.ADMIN_DOWN);
-    } else {
-        config = new Config(Config.ADMIN_UP);
-    }
-    return config;
-  }
-
-  public static org.opendaylight.controller.sal.core.State toAdState(final State s) {
-
-    org.opendaylight.controller.sal.core.State state = null;
-    if (s.isLinkDown()) {
-        state = new org.opendaylight.controller.sal.core.State(org.opendaylight.controller.sal.core.State.EDGE_DOWN);
-    } else {
-        state = new org.opendaylight.controller.sal.core.State(org.opendaylight.controller.sal.core.State.EDGE_UP);
-    }
-    return state;
-  }
-
-  public static Bandwidth toAdBandwidth(final PortFeatures pf) {
-    Bandwidth bw = null;
-    if (pf.isTenMbHd() || pf.isTenMbFd()) {
-        bw = new Bandwidth(Bandwidth.BW10Mbps);
-    } else if (pf.isHundredMbHd() || pf.isHundredMbFd()) {
-        bw = new Bandwidth(Bandwidth.BW100Mbps);
-    } else if (pf.isOneGbHd() || pf.isOneGbFd()) {
-        bw = new Bandwidth(Bandwidth.BW1Gbps);
-    } else if (pf.isOneGbFd()) {
-        bw = new Bandwidth(Bandwidth.BW10Gbps);
-    } else if (pf.isTenGbFd()) {
-        bw = new Bandwidth(Bandwidth.BW10Gbps);
-    } else if (pf.isFortyGbFd()) {
-        bw = new Bandwidth(Bandwidth.BW40Gbps);
-    } else if (pf.isHundredGbFd()) {
-        bw = new Bandwidth(Bandwidth.BW100Gbps);
-    } else if (pf.isOneTbFd()) {
-        bw = new Bandwidth(Bandwidth.BW1Tbps);
-    }
-    return bw;
-  }
-
-  public static AdvertisedBandwidth toAdAdvertizedBandwidth(final PortFeatures pf) {
-    AdvertisedBandwidth abw = null;
-    final Bandwidth bw = toAdBandwidth(pf);
-    if (bw != null) {
-        abw = new AdvertisedBandwidth(bw.getValue());
-    }
-    return abw;
-  }
-
-  public static SupportedBandwidth toAdSupportedBandwidth(final PortFeatures pf) {
-    SupportedBandwidth sbw = null;
-    final Bandwidth bw = toAdBandwidth(pf);
-    if (bw != null) {
-        sbw = new SupportedBandwidth(bw.getValue());
-    }
-    return sbw;
-  }
-
-  public static PeerBandwidth toAdPeerBandwidth(final PortFeatures pf) {
-    PeerBandwidth pbw = null;
-    final Bandwidth bw = toAdBandwidth(pf);
-    if (bw != null) {
-        pbw = new PeerBandwidth(bw.getValue());
-    }
-    return pbw;
-  }
-
-  public static HashSet<Property> toADNodeProperties(final NodeUpdated nu) {
-    final FlowCapableNodeUpdated fcnu = nu.getAugmentation(FlowCapableNodeUpdated.class);
-    if (fcnu != null) {
-        return toADNodeProperties(fcnu, nu.getId());
-    }
-    return new HashSet<org.opendaylight.controller.sal.core.Property>();
-  }
-
-  public static HashSet<Property> toADNodeProperties(final FlowNode fcnu, final NodeId id) {
-
-    final HashSet<org.opendaylight.controller.sal.core.Property> props = new HashSet<>();
-
-    if (fcnu != null) {
-        props.add(toADTimestamp());
-
-        // props.add(fcnu.supportedActions.toADActions) - TODO
-        if (id != null) {
-            props.add(toADMacAddress(id));
+
+    public static SupportedBandwidth toAdSupportedBandwidth(final PortFeatures pf) {
+        SupportedBandwidth sbw = null;
+        final Bandwidth bw = toAdBandwidth(pf);
+        if (bw != null) {
+            sbw = new SupportedBandwidth(bw.getValue());
         }
-        SwitchFeatures switchFeatures = fcnu.getSwitchFeatures();
-        if (switchFeatures != null) {
-            if (switchFeatures.getMaxTables() != null) {
-                props.add(toADTables(switchFeatures.getMaxTables()));
-            }
-            if (switchFeatures.getCapabilities() != null) {
-                props.add(toADCapabiliities(switchFeatures.getCapabilities()));
+        return sbw;
+    }
+
+    public static PeerBandwidth toAdPeerBandwidth(final PortFeatures pf) {
+        PeerBandwidth pbw = null;
+        final Bandwidth bw = toAdBandwidth(pf);
+        if (bw != null) {
+            pbw = new PeerBandwidth(bw.getValue());
+        }
+        return pbw;
+    }
+
+    public static HashSet<Property> toADNodeProperties(final NodeUpdated nu) {
+        final FlowCapableNodeUpdated fcnu = nu.getAugmentation(FlowCapableNodeUpdated.class);
+        if (fcnu != null) {
+            return toADNodeProperties(fcnu, nu.getId());
+        }
+        return new HashSet<org.opendaylight.controller.sal.core.Property>();
+    }
+
+    public static HashSet<Property> toADNodeProperties(final FlowNode fcnu, final NodeId id) {
+
+        final HashSet<org.opendaylight.controller.sal.core.Property> props = new HashSet<>();
+
+        if (fcnu != null) {
+            props.add(toADTimestamp());
+
+            // props.add(fcnu.supportedActions.toADActions) - TODO
+            if (id != null) {
+                props.add(toADMacAddress(id));
             }
-            if (switchFeatures.getMaxBuffers() != null) {
-                props.add(toADBuffers(switchFeatures.getMaxBuffers()));
+            SwitchFeatures switchFeatures = fcnu.getSwitchFeatures();
+            if (switchFeatures != null) {
+                if (switchFeatures.getMaxTables() != null) {
+                    props.add(toADTables(switchFeatures.getMaxTables()));
+                }
+                if (switchFeatures.getCapabilities() != null) {
+                    props.add(toADCapabiliities(switchFeatures.getCapabilities()));
+                }
+                if (switchFeatures.getMaxBuffers() != null) {
+                    props.add(toADBuffers(switchFeatures.getMaxBuffers()));
+                }
             }
         }
+        return props;
     }
-    return props;
-  }
-
-  public static TimeStamp toADTimestamp() {
-    final Date date = new Date();
-    final TimeStamp timestamp = new TimeStamp(date.getTime(), "connectedSince");
-    return timestamp;
-  }
-
-  public static MacAddress toADMacAddress(final NodeId id) {
-    final String nodeId = id.getValue().replaceAll("openflow:", "");
-    BigInteger nodeIdRaw = new BigInteger(nodeId);
-    long lNodeId = nodeIdRaw.longValue();
-    byte[] bytesFromDpid = ToSalConversionsUtils.bytesFromDpid(lNodeId);
-    return new MacAddress(bytesFromDpid);
-  }
-
-  public static Tables toADTables(final Short tables) {
-    return new Tables(tables.byteValue());
-  }
-
-  public static Capabilities toADCapabiliities(final List<Class<? extends FeatureCapability>> capabilities) {
-
-    int b = 0;
-    for (Class<? extends FeatureCapability> capability : capabilities) {
-        if (capability.equals(FlowFeatureCapabilityFlowStats.class)) {
-            b = Capabilities.CapabilitiesType.FLOW_STATS_CAPABILITY.getValue() | b;
-        } else if (capability.equals(FlowFeatureCapabilityTableStats.class)) {
-            b = Capabilities.CapabilitiesType.TABLE_STATS_CAPABILITY.getValue() | b;
-        } else if (capability.equals(FlowFeatureCapabilityPortStats.class)) {
-            b = Capabilities.CapabilitiesType.PORT_STATS_CAPABILITY.getValue() | b;
-        } else if (capability.equals(FlowFeatureCapabilityStp.class)) {
-            b = Capabilities.CapabilitiesType.STP_CAPABILITY.getValue() | b;
-        } else if (capability.equals(FlowFeatureCapabilityIpReasm.class)) {
-            b = Capabilities.CapabilitiesType.IP_REASSEM_CAPABILITY.getValue() | b;
-        } else if (capability.equals(FlowFeatureCapabilityQueueStats.class)) {
-            b = Capabilities.CapabilitiesType.QUEUE_STATS_CAPABILITY.getValue() | b;
-        } else if (capability.equals(FlowFeatureCapabilityArpMatchIp.class)) {
-            b = Capabilities.CapabilitiesType.ARP_MATCH_IP_CAPABILITY.getValue() | b;
+
+    public static TimeStamp toADTimestamp() {
+        final Date date = new Date();
+        final TimeStamp timestamp = new TimeStamp(date.getTime(), "connectedSince");
+        return timestamp;
+    }
+
+    public static MacAddress toADMacAddress(final NodeId id) {
+        final String nodeId = id.getValue().replaceAll("openflow:", "");
+        BigInteger nodeIdRaw = new BigInteger(nodeId);
+        long lNodeId = nodeIdRaw.longValue();
+        byte[] bytesFromDpid = ToSalConversionsUtils.bytesFromDpid(lNodeId);
+        return new MacAddress(bytesFromDpid);
+    }
+
+    public static Tables toADTables(final Short tables) {
+        return new Tables(tables.byteValue());
+    }
+
+    public static Capabilities toADCapabiliities(final List<Class<? extends FeatureCapability>> capabilities) {
+
+        int b = 0;
+        for (Class<? extends FeatureCapability> capability : capabilities) {
+            if (capability.equals(FlowFeatureCapabilityFlowStats.class)) {
+                b = Capabilities.CapabilitiesType.FLOW_STATS_CAPABILITY.getValue() | b;
+            } else if (capability.equals(FlowFeatureCapabilityTableStats.class)) {
+                b = Capabilities.CapabilitiesType.TABLE_STATS_CAPABILITY.getValue() | b;
+            } else if (capability.equals(FlowFeatureCapabilityPortStats.class)) {
+                b = Capabilities.CapabilitiesType.PORT_STATS_CAPABILITY.getValue() | b;
+            } else if (capability.equals(FlowFeatureCapabilityStp.class)) {
+                b = Capabilities.CapabilitiesType.STP_CAPABILITY.getValue() | b;
+            } else if (capability.equals(FlowFeatureCapabilityIpReasm.class)) {
+                b = Capabilities.CapabilitiesType.IP_REASSEM_CAPABILITY.getValue() | b;
+            } else if (capability.equals(FlowFeatureCapabilityQueueStats.class)) {
+                b = Capabilities.CapabilitiesType.QUEUE_STATS_CAPABILITY.getValue() | b;
+            } else if (capability.equals(FlowFeatureCapabilityArpMatchIp.class)) {
+                b = Capabilities.CapabilitiesType.ARP_MATCH_IP_CAPABILITY.getValue() | b;
+            }
         }
+        return new Capabilities(b);
     }
-    return new Capabilities(b);
-  }
 
-  public static Buffers toADBuffers(final Long buffers) {
-    return new Buffers(buffers.intValue());
-  }
+    public static Buffers toADBuffers(final Long buffers) {
+        return new Buffers(buffers.intValue());
+    }
 }
index b02b0dc25cbf3ade673b927d0851ee898f69432c..efe1ce3e3aaebad8d2b2a0a866d5d12392238a7c 100644 (file)
@@ -269,6 +269,12 @@ module opendaylight-match-types {
         }
     }
 
+    grouping "tcp-flag-match-fields" {
+        leaf tcp-flag {
+            type uint16;
+        }
+    }
+
     grouping match {
         leaf in-port {
             type inv:node-connector-id;
@@ -340,5 +346,9 @@ module opendaylight-match-types {
         container "protocol-match-fields" {
             uses "protocol-match-fields";
         }
+
+        container tcp-flag-match {
+            uses "tcp-flag-match-fields";
+        }
     }
 }
\ No newline at end of file
index e74b5483428fc1765e8bf29b763fb840c688fd25..c271f8f4d00a6b99318bc25baeae9251ac13ea97 100644 (file)
@@ -188,6 +188,10 @@ module opendaylight-table-types {
         base match-field;
         description "Match for IPv6 Extension Header pseudo-field";
     }
+    identity tcp_flag {
+        base match-field;
+        description "TCP Flag Match";
+    }
         
     grouping set-field-match {
         list set-field-match {
index 94bd0731aaa64b8bed970a2bee7a8fe4aec5f0ce..9222734360a0b1cb81b58caafe36381728056048 100644 (file)
       </plugin>
       <plugin>
         <!-- FIXME: BUG-272: remove this configuration override -->
+        <!-- replaced with new configuration -->
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-checkstyle-plugin</artifactId>
-        <version>${checkstyle.version}</version>
+        <version>2.12</version>
+        <configuration>
+          <failsOnError>false</failsOnError>
+          <failOnViolation>false</failOnViolation>
+          <configLocation>checkstyle-logging.xml</configLocation>
+          <consoleOutput>true</consoleOutput>
+          <includeTestSourceDirectory>true</includeTestSourceDirectory>
+          <sourceDirectory>${project.basedir}</sourceDirectory>
+          <includes>**\/*.java,**\/*.xml,**\/*.ini,**\/*.sh,**\/*.bat,**\/*.yang</includes>
+          <excludes>**\/target\/,**\/bin\/,**\/target-ide\/,**\/${jmxGeneratorPath}\/,**\/${salGeneratorPath}\/</excludes>
+        </configuration>
+        <dependencies>
+          <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>checkstyle-logging</artifactId>
+            <version>${yangtools.version}</version>
+          </dependency>
+        </dependencies>
         <executions>
           <execution>
-            <phase>none</phase>
+            <goals>
+              <goal>check</goal>
+            </goals>
           </execution>
         </executions>
       </plugin>
index cab4fe90e0f30da4c0f1b8a38be37a72fe8f25ea..fdd9350680f450131049800a9965843ae2c807a3 100644 (file)
@@ -6,25 +6,22 @@
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-
 package org.opendaylight.controller.sal.binding.codegen.impl;
 
-import org.eclipse.xtext.xbase.lib.Exceptions;
-
-@SuppressWarnings("all")
 public class BrokerImplClassLoader extends ClassLoader {
-  private final ClassLoader spiClassLoader;
+    private final ClassLoader spiClassLoader;
 
-  public BrokerImplClassLoader(final ClassLoader model, final ClassLoader spi) {
-    super(model);
-    this.spiClassLoader = spi;
-  }
+    public BrokerImplClassLoader(final ClassLoader model, final ClassLoader spi) {
+        super(model);
+        this.spiClassLoader = spi;
+    }
 
-  public Class<? extends Object> loadClass(final String name) throws ClassNotFoundException {
-    try {
-      return super.loadClass(name);
-    } catch (ClassNotFoundException e) {
-        return this.spiClassLoader.loadClass(name);
+    @Override
+    public Class<? extends Object> loadClass(final String name) throws ClassNotFoundException {
+        try {
+            return super.loadClass(name);
+        } catch (ClassNotFoundException e) {
+            return this.spiClassLoader.loadClass(name);
+        }
     }
-  }
 }
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcServiceMetadata.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcServiceMetadata.java
new file mode 100644 (file)
index 0000000..399afe7
--- /dev/null
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.codegen.impl;
+
+import java.util.HashMap;
+import java.util.HashSet;
+
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+
+final class RpcServiceMetadata {
+    private final HashMap<Class<? extends DataContainer>, RpcMetadata> rpcInputs = new HashMap<>();
+    private final HashSet<Class<? extends DataContainer>> supportedInputs = new HashSet<>();
+    private final HashSet<Class<? extends BaseIdentity>> contexts = new HashSet<>();
+    private final HashMap<String, RpcMetadata> rpcMethods = new HashMap<>();
+
+    public HashSet<Class<? extends BaseIdentity>> getContexts() {
+        return this.contexts;
+    }
+
+    public HashMap<String, RpcMetadata> getRpcMethods() {
+        return this.rpcMethods;
+    }
+
+    public HashMap<Class<? extends DataContainer>, RpcMetadata> getRpcInputs() {
+        return this.rpcInputs;
+    }
+
+    public HashSet<Class<? extends DataContainer>> getSupportedInputs() {
+        return this.supportedInputs;
+    }
+}
index 465a1f7d9dd2d1e5ef33fd088ed076a2597a326b..4fb95d99b6028bbb93513388d23c6c538968bc5f 100644 (file)
@@ -7,38 +7,31 @@
  */
 package org.opendaylight.controller.sal.binding.codegen.impl
 
+import java.util.Map
+import java.util.WeakHashMap
 import javassist.ClassPool
-import org.opendaylight.yangtools.yang.binding.RpcService
-
 import javassist.CtClass
 import javassist.CtMethod
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
-import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext
+import javassist.LoaderClassPath
+import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper
+import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory
+import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory.NotificationInvoker
+import org.opendaylight.yangtools.sal.binding.generator.util.ClassLoaderUtils
+import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils
 import org.opendaylight.yangtools.yang.binding.BaseIdentity
-
-import java.util.Map
-import java.util.HashMap
-
-
-import org.opendaylight.yangtools.yang.binding.NotificationListener
+import org.opendaylight.yangtools.yang.binding.DataContainer
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
 import org.opendaylight.yangtools.yang.binding.Notification
+import org.opendaylight.yangtools.yang.binding.NotificationListener
+import org.opendaylight.yangtools.yang.binding.RpcImplementation
+import org.opendaylight.yangtools.yang.binding.RpcService
+import org.opendaylight.yangtools.yang.binding.annotations.QName
+import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext
 
+import static org.opendaylight.yangtools.concepts.util.ClassLoaderUtils.*
 
-import static extension org.opendaylight.controller.sal.binding.codegen.YangtoolsMappingHelper.*
 import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeSpecification.*
-import java.util.HashSet
-import static org.opendaylight.yangtools.concepts.util.ClassLoaderUtils.*
-import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory
-import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory.NotificationInvoker
-import java.util.Set
-import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper
-import java.util.WeakHashMap
-import org.opendaylight.yangtools.yang.binding.annotations.QName
-import org.opendaylight.yangtools.yang.binding.DataContainer
-import org.opendaylight.yangtools.yang.binding.RpcImplementation
-import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils
-import org.opendaylight.yangtools.sal.binding.generator.util.ClassLoaderUtils
-import javassist.LoaderClassPath
+import static extension org.opendaylight.controller.sal.binding.codegen.YangtoolsMappingHelper.*
 
 class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator, NotificationInvokerFactory {
 
@@ -275,32 +268,6 @@ package class RuntimeGeneratedInvoker implements NotificationInvoker {
     }
 }
 
-@Data
-package class RuntimeGeneratedInvokerPrototype {
-
-    @Property
-    val Set<Class<? extends Notification>> supportedNotifications;
-
-    @Property
-    val Class<? extends org.opendaylight.controller.sal.binding.api.NotificationListener<?>> protoClass;
-}
-
-package class RpcServiceMetadata {
-
-    @Property
-    val contexts = new HashSet<Class<? extends BaseIdentity>>();
-
-    @Property
-    val rpcMethods = new HashMap<String, RpcMetadata>();
-
-    @Property
-    val rpcInputs = new HashMap<Class<? extends DataContainer>, RpcMetadata>();
-
-
-    @Property
-    val supportedInputs = new HashSet<Class<? extends DataContainer>>();
-}
-
 @Data
 package class RpcMetadata {
 
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeGeneratedInvokerPrototype.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeGeneratedInvokerPrototype.java
new file mode 100644 (file)
index 0000000..f160748
--- /dev/null
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.codegen.impl;
+
+import java.util.Set;
+
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+
+final class RuntimeGeneratedInvokerPrototype {
+    private final Set<Class<? extends Notification>> supportedNotifications;
+    private final Class<? extends NotificationListener<?>> protoClass;
+
+    public RuntimeGeneratedInvokerPrototype(final Set<Class<? extends Notification>> supportedNotifications, final Class<? extends NotificationListener<?>> protoClass) {
+        this.supportedNotifications = Preconditions.checkNotNull(supportedNotifications);
+        this.protoClass = Preconditions.checkNotNull(protoClass);
+    }
+
+    public Set<Class<? extends Notification>> getSupportedNotifications() {
+        return supportedNotifications;
+    }
+
+    public Class<? extends NotificationListener<? extends Object>> getProtoClass() {
+        return protoClass;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + supportedNotifications.hashCode();
+        result = prime * result + protoClass.hashCode();
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof RuntimeGeneratedInvokerPrototype)) {
+            return false;
+        }
+        final RuntimeGeneratedInvokerPrototype other = (RuntimeGeneratedInvokerPrototype) obj;
+        if (!protoClass.equals(other.protoClass)) {
+            return false;
+        }
+        return supportedNotifications.equals(other.supportedNotifications);
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this)
+                .add("protoClass", protoClass)
+                .add("supportedNotifications", supportedNotifications)
+                .toString();
+    }
+}
index 827644676665a1d1576abdbd2fe5caf2d8113a66..1ec4aa2d30bc9da7307891a41050bfc33e55642a 100644 (file)
@@ -11,6 +11,7 @@ import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.RejectedExecutionHandler;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.ThreadPoolExecutor;
@@ -24,6 +25,7 @@ import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.util.concurrent.ForwardingBlockingQueue;
 import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
@@ -62,41 +64,49 @@ public class SingletonHolder {
                 try {
                     queueSize = Integer.parseInt(queueValue);
                     logger.trace("Queue size was set to {}", queueSize);
-                }catch(NumberFormatException e) {
+                } catch (NumberFormatException e) {
                     logger.warn("Cannot parse {} as set by {}, using default {}", queueValue,
                             NOTIFICATION_QUEUE_SIZE_PROPERTY, queueSize);
                 }
             }
+
             // Overriding the queue:
             // ThreadPoolExecutor would not create new threads if the queue is not full, thus adding
             // occurs in RejectedExecutionHandler.
             // This impl saturates threadpool first, then queue. When both are full caller will get blocked.
-            BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(queueSize) {
-                private static final long serialVersionUID = 1L;
+            final BlockingQueue<Runnable> delegate = new LinkedBlockingQueue<>(queueSize);
+            final BlockingQueue<Runnable> queue = new ForwardingBlockingQueue<Runnable>() {
+                @Override
+                protected BlockingQueue<Runnable> delegate() {
+                    return delegate;
+                }
 
                 @Override
-                public boolean offer(Runnable r) {
-                    // ThreadPoolExecutor will spawn a new thread after core size is reached only if the queue.offer returns false.
+                public boolean offer(final Runnable r) {
+                    // ThreadPoolExecutor will spawn a new thread after core size is reached only
+                    // if the queue.offer returns false.
                     return false;
                 }
             };
 
-            ThreadFactory factory = new ThreadFactoryBuilder().setDaemon(true).setNameFormat("md-sal-binding-notification-%d").build();
+            final ThreadFactory factory = new ThreadFactoryBuilder()
+            .setDaemon(true)
+            .setNameFormat("md-sal-binding-notification-%d")
+            .build();
 
-            ThreadPoolExecutor executor = new ThreadPoolExecutor(CORE_NOTIFICATION_THREADS, MAX_NOTIFICATION_THREADS,
-                    NOTIFICATION_THREAD_LIFE, TimeUnit.SECONDS, queue , factory,
+            final ThreadPoolExecutor executor = new ThreadPoolExecutor(CORE_NOTIFICATION_THREADS, MAX_NOTIFICATION_THREADS,
+                    NOTIFICATION_THREAD_LIFE, TimeUnit.SECONDS, queue, factory,
                     new RejectedExecutionHandler() {
-                        // if the max threads are met, then it will raise a rejectedExecution. We then push to the queue.
-                        @Override
-                        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
-                            try {
-                                executor.getQueue().put(r);
-                            } catch (InterruptedException e) {
-                                Thread.currentThread().interrupt();// set interrupt flag after clearing
-                                throw new IllegalStateException(e);
-                            }
-                        }
-                    });
+                // if the max threads are met, then it will raise a rejectedExecution. We then push to the queue.
+                @Override
+                public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) {
+                    try {
+                        executor.getQueue().put(r);
+                    } catch (InterruptedException e) {
+                        throw new RejectedExecutionException("Interrupted while waiting on the queue", e);
+                    }
+                }
+            });
 
             NOTIFICATION_EXECUTOR = MoreExecutors.listeningDecorator(executor);
         }
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/AbstractNotificationListenerRegistration.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/AbstractNotificationListenerRegistration.java
new file mode 100644 (file)
index 0000000..5e7c913
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.impl;
+
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Abstract implementation of {@link NotificationListenerRegistration}.
+ *
+ * @param <T> Notification type
+ */
+abstract class AbstractNotificationListenerRegistration<T extends Notification> extends AbstractListenerRegistration<NotificationListener<T>> implements NotificationListenerRegistration<T> {
+    private final Class<? extends Notification> type;
+
+    protected AbstractNotificationListenerRegistration(final Class<? extends Notification> type, final NotificationListener<T> listener) {
+        super(listener);
+        this.type = Preconditions.checkNotNull(type);
+    }
+
+    @Override
+    public Class<? extends Notification> getType() {
+        return type;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void notify(final Notification notification) {
+        if (!isClosed()) {
+            getInstance().onNotification((T)notification);
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/AggregatedNotificationListenerRegistration.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/AggregatedNotificationListenerRegistration.java
new file mode 100644 (file)
index 0000000..f0db891
--- /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.binding.impl;
+
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * An aggregated listener registration. This is a result of registering an invoker which can handle multiple
+ * interfaces at the same time. In order to support correct delivery, we need to maintain per-type registrations
+ * which get squashed if a notification which implements multiple interfaces is encountered.
+ *
+ * We take care of that by implementing alternate {@link #hashCode()}/{@link #equals(Object)}, which resolve
+ * to the backing aggregator.
+ *
+ * @param <N> Notification type
+ * @param <A> Aggregator type
+ */
+abstract class AggregatedNotificationListenerRegistration<N extends Notification, A> extends AbstractNotificationListenerRegistration<N> {
+    private final A aggregator;
+
+    protected AggregatedNotificationListenerRegistration(final Class<? extends Notification> type, final NotificationListener<N> listener, final A aggregator) {
+        super(type, listener);
+        this.aggregator = Preconditions.checkNotNull(aggregator);
+    }
+
+    protected A getAggregator() {
+        return aggregator;
+    }
+
+    @Override
+    public int hashCode() {
+        return aggregator.hashCode();
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!this.getClass().equals(obj.getClass())) {
+            return false;
+        }
+
+        return aggregator.equals(((AggregatedNotificationListenerRegistration<?, ?>)obj).aggregator);
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/ListenerMapGeneration.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/ListenerMapGeneration.java
new file mode 100644 (file)
index 0000000..4d893aa
--- /dev/null
@@ -0,0 +1,106 @@
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.impl;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+import com.google.common.base.Predicate;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+
+/**
+ * An immutable view of the current generation of listeners.
+ */
+final class ListenerMapGeneration {
+    private static final int CACHE_MAX_ENTRIES = 1000;
+
+    /**
+     * Constant map of notification type to subscribed listeners.
+     */
+    private final Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> typeToListeners;
+
+    /**
+     * Dynamic cache of notification implementation to matching listeners. This cache loads entries based on
+     * the contents of the {@link #typeToListeners} map.
+     */
+    private final LoadingCache<Class<?>, Iterable<NotificationListenerRegistration<?>>> implementationToListeners =
+            CacheBuilder.newBuilder()
+            .weakKeys()
+            .maximumSize(CACHE_MAX_ENTRIES)
+            .build(new CacheLoader<Class<?>, Iterable<NotificationListenerRegistration<?>>>() {
+                @Override
+                public Iterable<NotificationListenerRegistration<?>> load(final Class<?> key) {
+                    final Set<NotificationListenerRegistration<?>> regs = new HashSet<>();
+
+                    for (final Class<?> type : getNotificationTypes(key)) {
+                        @SuppressWarnings("unchecked")
+                        final Collection<NotificationListenerRegistration<?>> l = typeToListeners.get((Class<? extends Notification>) type);
+                        if (l != null) {
+                            regs.addAll(l);
+                        }
+                    }
+
+                    return ImmutableSet.copyOf(regs);
+                }
+            });
+
+    ListenerMapGeneration() {
+        typeToListeners = ImmutableMultimap.of();
+    }
+
+    ListenerMapGeneration(final Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> listeners) {
+        this.typeToListeners = ImmutableMultimap.copyOf(listeners);
+    }
+
+    /**
+     * Current listeners. Exposed for creating the next generation.
+     *
+     * @return Current type-to-listener map.
+     */
+    Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> getListeners() {
+        return typeToListeners;
+    }
+
+    /**
+     * Look up the listeners which need to see this notification delivered.
+     *
+     * @param notification Notification object
+     * @return Iterable of listeners, guaranteed to be nonnull.
+     */
+    public Iterable<NotificationListenerRegistration<?>> listenersFor(final Notification notification) {
+        // Safe to use, as our loader does not throw checked exceptions
+        return implementationToListeners.getUnchecked(notification.getClass());
+    }
+
+    public Iterable<Class<? extends Notification>> getKnownTypes() {
+        return typeToListeners.keySet();
+    }
+
+    private static Iterable<Class<?>> getNotificationTypes(final Class<?> cls) {
+        final Class<?>[] ifaces = cls.getInterfaces();
+        return Iterables.filter(Arrays.asList(ifaces), new Predicate<Class<?>>() {
+            @Override
+            public boolean apply(final Class<?> input) {
+                if (Notification.class.equals(input)) {
+                    return false;
+                }
+                return Notification.class.isAssignableFrom(input);
+            }
+        });
+    }
+}
\ No newline at end of file
index b999a6f01cef0e4094288108734ea96f1f1c050a..df09f78620a11bcce20f04034e6a7b7cf144780f 100644 (file)
@@ -11,10 +11,10 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
 import org.opendaylight.controller.md.sal.binding.util.AbstractBindingSalProviderInstance;
-import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
 import org.opendaylight.controller.sal.binding.api.mount.MountProviderInstance;
 import org.opendaylight.controller.sal.binding.api.mount.MountProviderService;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -27,7 +27,7 @@ public class MountPointManagerImpl implements MountProviderService {
 
     private final ConcurrentMap<InstanceIdentifier<?>, BindingMountPointImpl> mountPoints;
     private final ListenerRegistry<MountProvisionListener> listeners = ListenerRegistry.create();
-    
+
     private ListeningExecutorService notificationExecutor;
     private ListeningExecutorService dataCommitExecutor;
 
@@ -39,7 +39,7 @@ public class MountPointManagerImpl implements MountProviderService {
         return notificationExecutor;
     }
 
-    public void setNotificationExecutor(ListeningExecutorService notificationExecutor) {
+    public void setNotificationExecutor(final ListeningExecutorService notificationExecutor) {
         this.notificationExecutor = notificationExecutor;
     }
 
@@ -47,12 +47,12 @@ public class MountPointManagerImpl implements MountProviderService {
         return dataCommitExecutor;
     }
 
-    public void setDataCommitExecutor(ListeningExecutorService dataCommitExecutor) {
+    public void setDataCommitExecutor(final ListeningExecutorService dataCommitExecutor) {
         this.dataCommitExecutor = dataCommitExecutor;
     }
 
     @Override
-    public synchronized BindingMountPointImpl createMountPoint(InstanceIdentifier<?> path) {
+    public synchronized BindingMountPointImpl createMountPoint(final InstanceIdentifier<?> path) {
         BindingMountPointImpl potential = mountPoints.get(path);
         if (potential != null) {
             throw new IllegalStateException("Mount point already exists.");
@@ -61,7 +61,7 @@ public class MountPointManagerImpl implements MountProviderService {
     }
 
     @Override
-    public BindingMountPointImpl createOrGetMountPoint(InstanceIdentifier<?> path) {
+    public BindingMountPointImpl createOrGetMountPoint(final InstanceIdentifier<?> path) {
         BindingMountPointImpl potential = getMountPoint(path);
         if (potential != null) {
             return potential;
@@ -70,18 +70,17 @@ public class MountPointManagerImpl implements MountProviderService {
     }
 
     @Override
-    public BindingMountPointImpl getMountPoint(InstanceIdentifier<?> path) {
+    public BindingMountPointImpl getMountPoint(final InstanceIdentifier<?> path) {
         return mountPoints.get(path);
     }
 
-    private synchronized BindingMountPointImpl createOrGetMountPointImpl(InstanceIdentifier<?> path) {
+    private synchronized BindingMountPointImpl createOrGetMountPointImpl(final InstanceIdentifier<?> path) {
         BindingMountPointImpl potential = getMountPoint(path);
         if (potential != null) {
             return potential;
         }
         RpcProviderRegistryImpl rpcRegistry = new RpcProviderRegistryImpl("mount");
-        NotificationBrokerImpl notificationBroker = new NotificationBrokerImpl();
-        notificationBroker.setExecutor(getNotificationExecutor());
+        NotificationBrokerImpl notificationBroker = new NotificationBrokerImpl(getNotificationExecutor());
         DataBrokerImpl dataBroker = new DataBrokerImpl();
         dataBroker.setExecutor(getDataCommitExecutor());
         BindingMountPointImpl mountInstance = new BindingMountPointImpl(path, rpcRegistry, notificationBroker,
@@ -91,7 +90,7 @@ public class MountPointManagerImpl implements MountProviderService {
         return mountInstance;
     }
 
-    private void notifyMountPointCreated(InstanceIdentifier<?> path) {
+    private void notifyMountPointCreated(final InstanceIdentifier<?> path) {
         for (ListenerRegistration<MountProvisionListener> listener : listeners) {
             try {
                 listener.getInstance().onMountPointCreated(path);
@@ -102,28 +101,28 @@ public class MountPointManagerImpl implements MountProviderService {
     }
 
     @Override
-    public ListenerRegistration<MountProvisionListener> registerProvisionListener(MountProvisionListener listener) {
+    public ListenerRegistration<MountProvisionListener> registerProvisionListener(final MountProvisionListener listener) {
         return listeners.register(listener);
     }
 
     public class BindingMountPointImpl extends
-            AbstractBindingSalProviderInstance<DataBrokerImpl, NotificationBrokerImpl, RpcProviderRegistryImpl>
+    AbstractBindingSalProviderInstance<DataBrokerImpl, NotificationBrokerImpl, RpcProviderRegistryImpl>
     implements MountProviderInstance {
 
-        private InstanceIdentifier<?> identifier;
+        private final InstanceIdentifier<?> identifier;
 
-        public BindingMountPointImpl(org.opendaylight.yangtools.yang.binding.InstanceIdentifier<?> identifier,
-                RpcProviderRegistryImpl rpcRegistry, NotificationBrokerImpl notificationBroker,
-                DataBrokerImpl dataBroker) {
+        public BindingMountPointImpl(final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<?> identifier,
+                final RpcProviderRegistryImpl rpcRegistry, final NotificationBrokerImpl notificationBroker,
+                final DataBrokerImpl dataBroker) {
             super(rpcRegistry, notificationBroker, dataBroker);
             this.identifier = identifier;
         }
 
         // Needed only for BI Connector
         public DataBrokerImpl getDataBrokerImpl() {
-            return (DataBrokerImpl) getDataBroker();
+            return getDataBroker();
         }
-        
+
         @Override
         public InstanceIdentifier<?> getIdentifier() {
             return this.identifier;
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationBrokerImpl.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationBrokerImpl.java
new file mode 100644 (file)
index 0000000..258ba51
--- /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.sal.binding.impl;
+
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.annotation.concurrent.GuardedBy;
+
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder;
+import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory.NotificationInvoker;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+
+public class NotificationBrokerImpl implements NotificationProviderService, AutoCloseable {
+    private static final Logger LOG = LoggerFactory.getLogger(NotificationBrokerImpl.class);
+
+    private final ListenerRegistry<NotificationInterestListener> interestListeners =
+            ListenerRegistry.create();
+    private final AtomicReference<ListenerMapGeneration> listeners = new AtomicReference<>(new ListenerMapGeneration());
+    private final ExecutorService executor;
+
+    public NotificationBrokerImpl(final ExecutorService executor) {
+        this.executor = Preconditions.checkNotNull(executor);
+    }
+
+    @Override
+    public void publish(final Notification notification) {
+        publish(notification, executor);
+    }
+
+    @Override
+    public void publish(final Notification notification, final ExecutorService service) {
+        for (NotificationListenerRegistration<?> r : listeners.get().listenersFor(notification)) {
+            service.submit(new NotifyTask(r, notification));
+        }
+    }
+
+    @GuardedBy("this")
+    private Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> mutableListeners() {
+        return HashMultimap.create(listeners.get().getListeners());
+    }
+
+    private final void addRegistrations(final NotificationListenerRegistration<?>... registrations) {
+        synchronized (this) {
+            final Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> newListeners =
+                    mutableListeners();
+            for (NotificationListenerRegistration<?> reg : registrations) {
+                newListeners.put(reg.getType(), reg);
+            }
+
+            listeners.set(new ListenerMapGeneration(newListeners));
+        }
+
+        // Notifications are dispatched out of lock...
+        for (NotificationListenerRegistration<?> reg : registrations) {
+            announceNotificationSubscription(reg.getType());
+        }
+    }
+
+    private synchronized void removeRegistrations(final NotificationListenerRegistration<?>... registrations) {
+        final Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> newListeners =
+                mutableListeners();
+
+        for (NotificationListenerRegistration<?> reg : registrations) {
+            newListeners.remove(reg.getType(), reg);
+        }
+
+        listeners.set(new ListenerMapGeneration(newListeners));
+    }
+
+    private void announceNotificationSubscription(final Class<? extends Notification> notification) {
+        for (final ListenerRegistration<NotificationInterestListener> listener : interestListeners) {
+            try {
+                listener.getInstance().onNotificationSubscribtion(notification);
+            } catch (Exception e) {
+                LOG.warn("Listener {} reported unexpected error on notification {}",
+                        listener.getInstance(), notification, e);
+            }
+        }
+    }
+
+    @Override
+    public ListenerRegistration<NotificationInterestListener> registerInterestListener(final NotificationInterestListener interestListener) {
+        final ListenerRegistration<NotificationInterestListener> registration = this.interestListeners.register(interestListener);
+
+        for (final Class<? extends Notification> notification : listeners.get().getKnownTypes()) {
+            interestListener.onNotificationSubscribtion(notification);
+        }
+        return registration;
+    }
+
+    @Override
+    public <T extends Notification> NotificationListenerRegistration<T> registerNotificationListener(final Class<T> notificationType, final NotificationListener<T> listener) {
+        final NotificationListenerRegistration<T> reg = new AbstractNotificationListenerRegistration<T>(notificationType, listener) {
+            @Override
+            protected void removeRegistration() {
+                removeRegistrations(this);
+            }
+        };
+
+        addRegistrations(reg);
+        return reg;
+    }
+
+    @Override
+    public ListenerRegistration<org.opendaylight.yangtools.yang.binding.NotificationListener> registerNotificationListener(final org.opendaylight.yangtools.yang.binding.NotificationListener listener) {
+        final NotificationInvoker invoker = SingletonHolder.INVOKER_FACTORY.invokerFor(listener);
+        final Set<Class<? extends Notification>> types = invoker.getSupportedNotifications();
+        final NotificationListenerRegistration<?>[] regs = new NotificationListenerRegistration<?>[types.size()];
+
+        // Populate the registrations...
+        int i = 0;
+        for (Class<? extends Notification> type : types) {
+            regs[i] = new AggregatedNotificationListenerRegistration<Notification, Object>(type, invoker.getInvocationProxy(), regs) {
+                @Override
+                protected void removeRegistration() {
+                    // Nothing to do, will be cleaned up by parent (below)
+                }
+            };
+            ++i;
+        }
+
+        // ... now put them to use ...
+        addRegistrations(regs);
+
+        // ... finally return the parent registration
+        return new AbstractListenerRegistration<org.opendaylight.yangtools.yang.binding.NotificationListener>(listener) {
+            @Override
+            protected void removeRegistration() {
+                removeRegistrations(regs);
+                for (ListenerRegistration<?> reg : regs) {
+                    reg.close();
+                }
+            }
+        };
+    }
+
+    @Override
+    public void close() {
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationBrokerImpl.xtend b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationBrokerImpl.xtend
deleted file mode 100644 (file)
index 6d675b4..0000000
+++ /dev/null
@@ -1,201 +0,0 @@
-/*\r
- * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.\r
- *\r
- * This program and the accompanying materials are made available under the\r
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
- * and is available at http://www.eclipse.org/legal/epl-v10.html\r
- */\r
-package org.opendaylight.controller.sal.binding.impl\r
-\r
-import com.google.common.collect.HashMultimap\r
-import com.google.common.collect.ImmutableSet\r
-import com.google.common.collect.Multimap\r
-import com.google.common.collect.Multimaps\r
-import java.util.Collections\r
-import java.util.concurrent.Callable\r
-import java.util.concurrent.ExecutorService\r
-import java.util.concurrent.Future\r
-import java.util.Set\r
-import org.opendaylight.controller.sal.binding.api.NotificationListener\r
-import org.opendaylight.controller.sal.binding.api.NotificationProviderService\r
-import org.opendaylight.controller.sal.binding.api.NotificationProviderService.NotificationInterestListener\r
-import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder\r
-import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory.NotificationInvoker\r
-import org.opendaylight.yangtools.concepts.AbstractObjectRegistration\r
-import org.opendaylight.yangtools.concepts.ListenerRegistration\r
-import org.opendaylight.yangtools.concepts.Registration\r
-import org.opendaylight.yangtools.concepts.util.ListenerRegistry\r
-import org.opendaylight.yangtools.yang.binding.Notification\r
-import org.slf4j.LoggerFactory\r
-\r
-class NotificationBrokerImpl implements NotificationProviderService, AutoCloseable {\r
-    \r
-    val ListenerRegistry<NotificationInterestListener> interestListeners = ListenerRegistry.create;\r
-    \r
-    val Multimap<Class<? extends Notification>, NotificationListener<?>> listeners;\r
-\r
-    @Property\r
-    var ExecutorService executor;\r
-    \r
-    val logger = LoggerFactory.getLogger(NotificationBrokerImpl)\r
-\r
-    new() {\r
-        listeners = Multimaps.synchronizedSetMultimap(HashMultimap.create())\r
-    }\r
-\r
-    @Deprecated\r
-    new(ExecutorService executor) {\r
-        listeners = Multimaps.synchronizedSetMultimap(HashMultimap.create())\r
-        this.executor = executor;\r
-    }\r
-\r
-    def getNotificationTypes(Notification notification) {\r
-        notification.class.interfaces.filter[it != Notification && Notification.isAssignableFrom(it)]\r
-    }\r
-\r
-    override publish(Notification notification) {\r
-        publish(notification, executor)\r
-    }\r
-\r
-    override publish(Notification notification, ExecutorService service) {\r
-        val allTypes = notification.notificationTypes\r
-\r
-        var Iterable<NotificationListener<? extends Object>> listenerToNotify = Collections.emptySet();\r
-        for (type : allTypes) {\r
-            listenerToNotify = listenerToNotify + listeners.get(type as Class<? extends Notification>)\r
-        }\r
-        val tasks = listenerToNotify.map[new NotifyTask(it, notification)].toSet;\r
-        submitAll(executor,tasks);\r
-    }\r
-    \r
-    def submitAll(ExecutorService service, Set<NotifyTask> tasks) {\r
-        val ret = ImmutableSet.<Future<Object>>builder();\r
-        for(task : tasks) {\r
-            ret.add(service.submit(task));\r
-        }\r
-        return ret.build();\r
-    }\r
-    \r
-    override <T extends Notification> registerNotificationListener(Class<T> notificationType,\r
-        NotificationListener<T> listener) {\r
-        val reg = new GenericNotificationRegistration<T>(notificationType, listener, this);\r
-        listeners.put(notificationType, listener);\r
-        announceNotificationSubscription(notificationType);\r
-        return reg;\r
-    }\r
-    \r
-    def announceNotificationSubscription(Class<? extends Notification> notification) {\r
-        for (listener : interestListeners) {\r
-            try {\r
-                listener.instance.onNotificationSubscribtion(notification);\r
-            } catch (Exception e) {\r
-                logger.error("", e.message)\r
-            }\r
-        }\r
-    }\r
-\r
-    override registerNotificationListener(\r
-        org.opendaylight.yangtools.yang.binding.NotificationListener listener) {\r
-        val invoker = SingletonHolder.INVOKER_FACTORY.invokerFor(listener);\r
-        for (notifyType : invoker.supportedNotifications) {\r
-            listeners.put(notifyType, invoker.invocationProxy)\r
-            announceNotificationSubscription(notifyType)\r
-        }\r
-        val registration = new GeneratedListenerRegistration(listener, invoker,this);\r
-        return registration as Registration<org.opendaylight.yangtools.yang.binding.NotificationListener>;\r
-    }\r
-\r
-    protected def unregisterListener(GenericNotificationRegistration<?> reg) {\r
-        listeners.remove(reg.type, reg.instance);\r
-    }\r
-\r
-    protected def unregisterListener(GeneratedListenerRegistration reg) {\r
-        for (notifyType : reg.invoker.supportedNotifications) {\r
-            listeners.remove(notifyType, reg.invoker.invocationProxy)\r
-        }\r
-    }\r
-    \r
-    override close()  {\r
-        //FIXME: implement properly.\r
-    }\r
-    \r
-    override registerInterestListener(NotificationInterestListener interestListener) {\r
-        val registration = interestListeners.register(interestListener);\r
-        \r
-        for(notification : listeners.keySet) {\r
-            interestListener.onNotificationSubscribtion(notification);\r
-        }\r
-        return registration\r
-    }\r
-}\r
-\r
-class GenericNotificationRegistration<T extends Notification> extends AbstractObjectRegistration<NotificationListener<T>> implements ListenerRegistration<NotificationListener<T>> {\r
-\r
-    @Property\r
-    val Class<T> type;\r
-\r
-    var NotificationBrokerImpl notificationBroker;\r
-\r
-    public new(Class<T> type, NotificationListener<T> instance, NotificationBrokerImpl broker) {\r
-        super(instance);\r
-        _type = type;\r
-        notificationBroker = broker;\r
-    }\r
-\r
-    override protected removeRegistration() {\r
-        notificationBroker.unregisterListener(this);\r
-        notificationBroker = null;\r
-    }\r
-}\r
-\r
-class GeneratedListenerRegistration extends AbstractObjectRegistration<org.opendaylight.yangtools.yang.binding.NotificationListener> implements ListenerRegistration<org.opendaylight.yangtools.yang.binding.NotificationListener> {\r
-\r
-    @Property\r
-    val NotificationInvoker invoker;\r
-    \r
-    var NotificationBrokerImpl notificationBroker;\r
-    \r
-\r
-    new(org.opendaylight.yangtools.yang.binding.NotificationListener instance, NotificationInvoker invoker, NotificationBrokerImpl broker) {\r
-        super(instance);\r
-        _invoker = invoker;\r
-        notificationBroker = broker;\r
-    }\r
-\r
-    override protected removeRegistration() {\r
-        notificationBroker.unregisterListener(this);\r
-        notificationBroker = null;\r
-        invoker.close();\r
-    }\r
-}\r
-\r
-@Data\r
-class NotifyTask implements Callable<Object> {\r
-\r
-    private static val log = LoggerFactory.getLogger(NotifyTask);\r
-\r
-    @SuppressWarnings("rawtypes")\r
-    val NotificationListener listener;\r
-    val Notification notification;\r
-\r
-    override call() {\r
-        //Only logging the complete notification in debug mode\r
-        try {\r
-            if(log.isDebugEnabled){\r
-                log.debug("Delivering notification {} to {}",notification,listener);\r
-            } else {\r
-                log.trace("Delivering notification {} to {}",notification.class.name,listener);\r
-            }\r
-            listener.onNotification(notification);\r
-            if(log.isDebugEnabled){\r
-                log.debug("Notification delivered {} to {}",notification,listener);\r
-            } else {\r
-                log.trace("Notification delivered {} to {}",notification.class.name,listener);\r
-            }\r
-        } catch (Exception e) {\r
-            log.error("Unhandled exception thrown by listener: {}", listener, e);\r
-        }\r
-        return null;\r
-    }\r
-\r
-}\r
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationListenerRegistration.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationListenerRegistration.java
new file mode 100644 (file)
index 0000000..3dba868
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.impl;
+
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+/**
+ * A registration of a {@link NotificationListener}. Allows query of the type
+ * of the notification and dispatching the notification atomically with regard
+ * to unregistration.
+ *
+ * @param <T> Type of notification
+ */
+interface NotificationListenerRegistration<T extends Notification> extends ListenerRegistration<NotificationListener<T>> {
+    /**
+     * Return the interface class of the notification type.
+     *
+     * @return Notification type.
+     */
+    Class<? extends Notification> getType();
+
+    /**
+     * Dispatch a notification to the listener.
+     *
+     * @param notification Notification to be dispatched
+     */
+    void notify(Notification notification);
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotifyTask.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotifyTask.java
new file mode 100644 (file)
index 0000000..2622a71
--- /dev/null
@@ -0,0 +1,92 @@
+/**
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.impl;
+
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+
+class NotifyTask implements Runnable {
+    private static final Logger LOG = LoggerFactory.getLogger(NotifyTask.class);
+
+    private final NotificationListenerRegistration<?> registration;
+    private final Notification notification;
+
+    public NotifyTask(final NotificationListenerRegistration<?> registration, final Notification notification) {
+        this.registration = Preconditions.checkNotNull(registration);
+        this.notification = Preconditions.checkNotNull(notification);
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T extends Notification> NotificationListenerRegistration<T> getRegistration() {
+        return (NotificationListenerRegistration<T>)registration;
+    }
+
+    @Override
+    public void run() {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Delivering notification {} to {}", notification, registration.getInstance());
+        } else {
+            LOG.trace("Delivering notification {} to {}", notification.getClass().getName(), registration.getInstance());
+        }
+
+        try {
+            getRegistration().notify(notification);
+        } catch (final Exception e) {
+            LOG.error("Unhandled exception thrown by listener: {}", registration.getInstance(), e);
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Notification delivered {} to {}", notification, registration.getInstance());
+        } else {
+            LOG.trace("Notification delivered {} to {}", notification.getClass().getName(), registration.getInstance());
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((registration== null) ? 0 : registration.hashCode());
+        result = prime * result + ((notification== null) ? 0 : notification.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        NotifyTask other = (NotifyTask) obj;
+        if (registration == null) {
+            if (other.registration != null)
+                return false;
+        } else if (!registration.equals(other.registration))
+            return false;
+        if (notification == null) {
+            if (other.notification != null)
+                return false;
+        } else if (!notification.equals(other.notification))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this)
+                .add("listener", registration)
+                .add("notification", notification.getClass())
+                .toString();
+    }
+}
index 9fd9bc80423e88400aaa06651d47fc7e9d8f7b5a..6b387b5a61cd206559e2cb11e43e53e3fe0d9ff5 100644 (file)
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.eclipse.xtend</groupId>
-      <artifactId>org.eclipse.xtend.lib</artifactId>
-    </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>sal-common-api</artifactId>
           </instructions>
         </configuration>
       </plugin>
-      <plugin>
-        <groupId>org.eclipse.xtend</groupId>
-        <artifactId>xtend-maven-plugin</artifactId>
-      </plugin>
     </plugins>
   </build>
   <scm>
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/AbstractDOMStoreTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/AbstractDOMStoreTransaction.java
new file mode 100644 (file)
index 0000000..8a190c1
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransaction;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Preconditions;
+
+/**
+ * Abstract DOM Store Transaction
+ *
+ * Convenience super implementation of DOM Store transaction which provides
+ * common implementation of {@link #toString()} and {@link #getIdentifier()}.
+ *
+ *
+ */
+abstract class AbstractDOMStoreTransaction implements DOMStoreTransaction {
+    private final Object identifier;
+
+    protected AbstractDOMStoreTransaction(final Object identifier) {
+        this.identifier = Preconditions.checkNotNull(identifier,"Identifier must not be null.");
+    }
+
+    @Override
+    public final Object getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(Objects.toStringHelper(this)).toString();
+    }
+
+    /**
+     * Add class-specific toString attributes.
+     *
+     * @param toStringHelper
+     *            ToStringHelper instance
+     * @return ToStringHelper instance which was passed in
+     */
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper.add("id", identifier);
+    }
+}
\ No newline at end of file
index 87c68596efa60a668a7afe005d482eefac1f6ad7..25e6d047217ce33480918217eafb2b1576fba089 100644 (file)
@@ -7,15 +7,17 @@
  */
 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 java.util.Collections;
 import java.util.concurrent.Callable;
 import java.util.concurrent.atomic.AtomicLong;
 
+import javax.annotation.concurrent.GuardedBy;
+
 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.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
 import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
 import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTree;
 import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeCandidate;
@@ -27,7 +29,6 @@ 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.DOMStoreTransactionChain;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
 import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
@@ -40,15 +41,24 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Objects;
-import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
+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 InMemoryDOMDataStore implements DOMStore, Identifiable<String>, SchemaContextListener {
+/**
+ * In-memory DOM Data Store
+ * 
+ * Implementation of {@link DOMStore} which uses {@link DataTree} and other
+ * classes such as {@link SnapshotBackedWriteTransaction}.
+ * {@link SnapshotBackedReadTransaction} and {@link ResolveDataChangeEventsTask}
+ * to implement {@link DOMStore} contract.
+ * 
+ */
+public class InMemoryDOMDataStore implements DOMStore, Identifiable<String>, SchemaContextListener,
+        TransactionReadyPrototype {
     private static final Logger LOG = LoggerFactory.getLogger(InMemoryDOMDataStore.class);
     private final DataTree dataTree = InMemoryDataTreeFactory.getInstance().create();
     private final ListenerTree listenerTree = ListenerTree.create();
@@ -83,7 +93,7 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable<String>, Sch
 
     @Override
     public DOMStoreTransactionChain createTransactionChain() {
-        throw new UnsupportedOperationException("Not implemented yet.");
+        return new DOMStoreTransactionChainImpl();
     }
 
     @Override
@@ -98,7 +108,7 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable<String>, Sch
         /*
          * 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
          */
@@ -130,7 +140,8 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable<String>, Sch
         };
     }
 
-    private synchronized DOMStoreThreePhaseCommitCohort submit(final SnapshotBackedWriteTransaction writeTx) {
+    @Override
+    public synchronized DOMStoreThreePhaseCommitCohort ready(final SnapshotBackedWriteTransaction writeTx) {
         LOG.debug("Tx: {} is submitted. Modifications: {}", writeTx.getIdentifier(), writeTx.getMutatedView());
         return new ThreePhaseCommitImpl(writeTx);
     }
@@ -139,162 +150,139 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable<String>, Sch
         return name + "-" + txCounter.getAndIncrement();
     }
 
-    private static abstract class AbstractDOMStoreTransaction implements DOMStoreTransaction {
-        private final Object identifier;
+    private class DOMStoreTransactionChainImpl implements DOMStoreTransactionChain, TransactionReadyPrototype {
 
-        protected AbstractDOMStoreTransaction(final Object identifier) {
-            this.identifier = identifier;
+        @GuardedBy("this")
+        private SnapshotBackedWriteTransaction latestOutstandingTx;
+
+        private boolean chainFailed = false;
+
+        private void checkFailed() {
+            Preconditions.checkState(!chainFailed, "Transaction chain is failed.");
         }
 
         @Override
-        public final Object getIdentifier() {
-            return identifier;
+        public synchronized DOMStoreReadTransaction newReadOnlyTransaction() {
+            final DataTreeSnapshot snapshot;
+            checkFailed();
+            if (latestOutstandingTx != null) {
+                checkState(latestOutstandingTx.isReady(), "Previous transaction in chain must be ready.");
+                snapshot = latestOutstandingTx.getMutatedView();
+            } else {
+                snapshot = dataTree.takeSnapshot();
+            }
+            return new SnapshotBackedReadTransaction(nextIdentifier(), snapshot);
         }
 
         @Override
-        public final String toString() {
-            return addToStringAttributes(Objects.toStringHelper(this)).toString();
+        public synchronized DOMStoreReadWriteTransaction newReadWriteTransaction() {
+            final DataTreeSnapshot snapshot;
+            checkFailed();
+            if (latestOutstandingTx != null) {
+                checkState(latestOutstandingTx.isReady(), "Previous transaction in chain must be ready.");
+                snapshot = latestOutstandingTx.getMutatedView();
+            } else {
+                snapshot = dataTree.takeSnapshot();
+            }
+            final SnapshotBackedReadWriteTransaction ret = new SnapshotBackedReadWriteTransaction(nextIdentifier(),
+                    snapshot, this);
+            latestOutstandingTx = ret;
+            return ret;
         }
 
-        /**
-         * Add class-specific toString attributes.
-         *
-         * @param toStringHelper
-         *            ToStringHelper instance
-         * @return ToStringHelper instance which was passed in
-         */
-        protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
-            return toStringHelper.add("id", identifier);
+        @Override
+        public synchronized DOMStoreWriteTransaction newWriteOnlyTransaction() {
+            final DataTreeSnapshot snapshot;
+            checkFailed();
+            if (latestOutstandingTx != null) {
+                checkState(latestOutstandingTx.isReady(), "Previous transaction in chain must be ready.");
+                snapshot = latestOutstandingTx.getMutatedView();
+            } else {
+                snapshot = dataTree.takeSnapshot();
+            }
+            final SnapshotBackedWriteTransaction ret = new SnapshotBackedWriteTransaction(nextIdentifier(), snapshot,
+                    this);
+            latestOutstandingTx = ret;
+            return ret;
         }
-    }
-
-    private static final class SnapshotBackedReadTransaction extends AbstractDOMStoreTransaction implements
-    DOMStoreReadTransaction {
-        private DataTreeSnapshot stableSnapshot;
 
-        public SnapshotBackedReadTransaction(final Object identifier, final DataTreeSnapshot snapshot) {
-            super(identifier);
-            this.stableSnapshot = Preconditions.checkNotNull(snapshot);
-            LOG.debug("ReadOnly Tx: {} allocated with snapshot {}", identifier, snapshot);
+        @Override
+        public DOMStoreThreePhaseCommitCohort ready(final SnapshotBackedWriteTransaction tx) {
+            DOMStoreThreePhaseCommitCohort storeCohort = InMemoryDOMDataStore.this.ready(tx);
+            return new ChainedTransactionCommitImpl(tx, storeCohort, this);
         }
 
         @Override
         public void close() {
-            LOG.debug("Store transaction: {} : Closed", getIdentifier());
-            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(stableSnapshot.readNode(path));
         }
-    }
 
-    private static class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction implements
-    DOMStoreWriteTransaction {
-        private DataTreeModification mutableTree;
-        private InMemoryDOMDataStore store;
-        private boolean ready = false;
-
-        public SnapshotBackedWriteTransaction(final Object identifier, final DataTreeSnapshot snapshot,
-                final InMemoryDOMDataStore store) {
-            super(identifier);
-            mutableTree = snapshot.newModification();
-            this.store = store;
-            LOG.debug("Write Tx: {} allocated with snapshot {}", identifier, snapshot);
-        }
+        protected synchronized void onTransactionFailed(final SnapshotBackedWriteTransaction transaction,
+                final Throwable t) {
+            chainFailed = true;
 
-        @Override
-        public void close() {
-            LOG.debug("Store transaction: {} : Closed", getIdentifier());
-            this.mutableTree = null;
-            this.store = null;
         }
 
-        @Override
-        public void write(final InstanceIdentifier path, final NormalizedNode<?, ?> data) {
-            checkNotReady();
-            try {
-                LOG.trace("Tx: {} Write: {}:{}", getIdentifier(), path, data);
-                mutableTree.write(path, data);
-                // FIXME: Add checked exception
-            } catch (Exception e) {
-                LOG.error("Tx: {}, failed to write {}:{} in {}", getIdentifier(), path, data, mutableTree, e);
+        public synchronized void onTransactionCommited(final SnapshotBackedWriteTransaction transaction) {
+            // If commited transaction is latestOutstandingTx we clear
+            // latestOutstandingTx
+            // field in order to base new transactions on Datastore Data Tree
+            // directly.
+            if (transaction.equals(latestOutstandingTx)) {
+                latestOutstandingTx = null;
             }
         }
 
-        @Override
-        public void merge(final InstanceIdentifier path, final NormalizedNode<?, ?> data) {
-            checkNotReady();
-            try {
-                LOG.trace("Tx: {} Merge: {}:{}", getIdentifier(), path, data);
-                mutableTree.merge(path, data);
-                // FIXME: Add checked exception
-            } catch (Exception e) {
-                LOG.error("Tx: {}, failed to write {}:{} in {}", getIdentifier(), path, data, mutableTree, e);
-            }
-        }
+    }
 
-        @Override
-        public void delete(final InstanceIdentifier path) {
-            checkNotReady();
-            try {
-                LOG.trace("Tx: {} Delete: {}", getIdentifier(), path);
-                mutableTree.delete(path);
-                // FIXME: Add checked exception
-            } catch (Exception e) {
-                LOG.error("Tx: {}, failed to delete {} in {}", getIdentifier(), path, mutableTree, e);
-            }
-        }
+    private static class ChainedTransactionCommitImpl implements DOMStoreThreePhaseCommitCohort {
 
-        protected final boolean isReady() {
-            return ready;
-        }
+        private final SnapshotBackedWriteTransaction transaction;
+        private final DOMStoreThreePhaseCommitCohort delegate;
+
+        private final DOMStoreTransactionChainImpl txChain;
 
-        protected final void checkNotReady() {
-            checkState(!ready, "Transaction %s is ready. No further modifications allowed.", getIdentifier());
+        protected ChainedTransactionCommitImpl(final SnapshotBackedWriteTransaction transaction,
+                final DOMStoreThreePhaseCommitCohort delegate, final DOMStoreTransactionChainImpl txChain) {
+            super();
+            this.transaction = transaction;
+            this.delegate = delegate;
+            this.txChain = txChain;
         }
 
         @Override
-        public synchronized DOMStoreThreePhaseCommitCohort ready() {
-            checkState(!ready, "Transaction %s is already ready.", getIdentifier());
-            ready = true;
-
-            LOG.debug("Store transaction: {} : Ready", getIdentifier());
-            mutableTree.ready();
-            return store.submit(this);
+        public ListenableFuture<Boolean> canCommit() {
+            return delegate.canCommit();
         }
 
-        protected DataTreeModification getMutatedView() {
-            return mutableTree;
+        @Override
+        public ListenableFuture<Void> preCommit() {
+            return delegate.preCommit();
         }
 
         @Override
-        protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
-            return toStringHelper.add("ready", isReady());
+        public ListenableFuture<Void> abort() {
+            return delegate.abort();
         }
-    }
 
-    private static class SnapshotBackedReadWriteTransaction extends SnapshotBackedWriteTransaction implements
-    DOMStoreReadWriteTransaction {
+        @Override
+        public ListenableFuture<Void> commit() {
+            ListenableFuture<Void> commitFuture = delegate.commit();
+            Futures.addCallback(commitFuture, new FutureCallback<Void>() {
+                @Override
+                public void onFailure(final Throwable t) {
+                    txChain.onTransactionFailed(transaction, t);
+                }
 
-        protected SnapshotBackedReadWriteTransaction(final Object identifier, final DataTreeSnapshot snapshot,
-                final InMemoryDOMDataStore store) {
-            super(identifier, snapshot, store);
-        }
+                @Override
+                public void onSuccess(final Void result) {
+                    txChain.onTransactionCommited(transaction);
+                }
 
-        @Override
-        public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final InstanceIdentifier path) {
-            LOG.trace("Tx: {} Read: {}", getIdentifier(), path);
-            try {
-                return Futures.immediateFuture(getMutatedView().readNode(path));
-            } catch (Exception e) {
-                LOG.error("Tx: {} Failed Read of {}", getIdentifier(), path, e);
-                throw e;
-            }
+            });
+            return commitFuture;
         }
+
     }
 
     private class ThreePhaseCommitImpl implements DOMStoreThreePhaseCommitCohort {
@@ -320,7 +308,8 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable<String>, Sch
                         LOG.debug("Store Transaction: {} can be committed", transaction.getIdentifier());
                         return true;
                     } catch (DataPreconditionFailedException e) {
-                        LOG.warn("Store Tx: {} Data Precondition failed for {}.",transaction.getIdentifier(),e.getPath(),e);
+                        LOG.warn("Store Tx: {} Data Precondition failed for {}.", transaction.getIdentifier(),
+                                e.getPath(), e);
                         return false;
                     }
                 }
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadTransaction.java
new file mode 100644 (file)
index 0000000..315293f
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeSnapshot;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
+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.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ *
+ * Implementation of read-only transaction backed by {@link DataTreeSnapshot}
+ *
+ * Implementation of read-only transaction backed by {@link DataTreeSnapshot}
+ * which delegates most of its calls to similar methods provided by underlying snapshot.
+ *
+ */
+final class SnapshotBackedReadTransaction extends AbstractDOMStoreTransaction implements
+DOMStoreReadTransaction {
+    private static final Logger LOG = LoggerFactory.getLogger(SnapshotBackedReadTransaction.class);
+    private DataTreeSnapshot stableSnapshot;
+
+    public SnapshotBackedReadTransaction(final Object identifier, final DataTreeSnapshot snapshot) {
+        super(identifier);
+        this.stableSnapshot = Preconditions.checkNotNull(snapshot);
+        LOG.debug("ReadOnly Tx: {} allocated with snapshot {}", identifier, snapshot);
+    }
+
+    @Override
+    public void close() {
+        LOG.debug("Store transaction: {} : Closed", getIdentifier());
+        stableSnapshot = null;
+    }
+
+    @Override
+    public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final InstanceIdentifier path) {
+        checkNotNull(path, "Path must not be null.");
+        checkState(stableSnapshot != null, "Transaction is closed");
+        return Futures.immediateFuture(stableSnapshot.readNode(path));
+    }
+}
\ 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/SnapshotBackedReadWriteTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadWriteTransaction.java
new file mode 100644 (file)
index 0000000..4abc802
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeSnapshot;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+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.Optional;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Implementation of Read-Write transaction which is backed by {@link DataTreeSnapshot}
+ * and executed according to {@link TransactionReadyPrototype}.
+ *
+ */
+class SnapshotBackedReadWriteTransaction extends SnapshotBackedWriteTransaction implements
+DOMStoreReadWriteTransaction {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SnapshotBackedReadWriteTransaction.class);
+
+    /**
+     * Creates new read-write transaction.
+     *
+     * @param identifier transaction Identifier
+     * @param snapshot Snapshot which will be modified.
+     * @param readyImpl Implementation of ready method.
+     */
+    protected SnapshotBackedReadWriteTransaction(final Object identifier, final DataTreeSnapshot snapshot,
+            final TransactionReadyPrototype store) {
+        super(identifier, snapshot, store);
+    }
+
+    @Override
+    public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final InstanceIdentifier path) {
+        LOG.debug("Tx: {} Read: {}", getIdentifier(), path);
+        try {
+            return Futures.immediateFuture(getMutatedView().readNode(path));
+        } catch (Exception e) {
+            LOG.error("Tx: {} Failed Read of {}", getIdentifier(), path, e);
+            throw e;
+        }
+    }
+}
\ 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/SnapshotBackedWriteTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedWriteTransaction.java
new file mode 100644 (file)
index 0000000..717fb11
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 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 org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeModification;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeSnapshot;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+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.Objects.ToStringHelper;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+
+/**
+ * Implementation of Write transaction which is backed by
+ * {@link DataTreeSnapshot} and executed according to
+ * {@link TransactionReadyPrototype}.
+ * 
+ */
+class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction implements DOMStoreWriteTransaction {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SnapshotBackedWriteTransaction.class);
+    private DataTreeModification mutableTree;
+    private boolean ready = false;
+    private TransactionReadyPrototype readyImpl;
+
+    /**
+     * Creates new write-only transaction.
+     * 
+     * @param identifier
+     *            transaction Identifier
+     * @param snapshot
+     *            Snapshot which will be modified.
+     * @param readyImpl
+     *            Implementation of ready method.
+     */
+    public SnapshotBackedWriteTransaction(final Object identifier, final DataTreeSnapshot snapshot,
+            final TransactionReadyPrototype readyImpl) {
+        super(identifier);
+        mutableTree = snapshot.newModification();
+        this.readyImpl = Preconditions.checkNotNull(readyImpl, "readyImpl must not be null.");
+        LOG.debug("Write Tx: {} allocated with snapshot {}", identifier, snapshot);
+    }
+
+    @Override
+    public void close() {
+        LOG.debug("Store transaction: {} : Closed", getIdentifier());
+        this.mutableTree = null;
+        this.readyImpl = null;
+    }
+
+    @Override
+    public void write(final InstanceIdentifier path, final NormalizedNode<?, ?> data) {
+        checkNotReady();
+        try {
+            LOG.debug("Tx: {} Write: {}:{}", getIdentifier(), path, data);
+            mutableTree.write(path, data);
+            // FIXME: Add checked exception
+        } catch (Exception e) {
+            LOG.error("Tx: {}, failed to write {}:{} in {}", getIdentifier(), path, data, mutableTree, e);
+            // Rethrow original ones if they are subclasses of RuntimeException
+            // or Error
+            Throwables.propagateIfPossible(e);
+            // FIXME: Introduce proper checked exception
+            throw new IllegalArgumentException("Illegal input data.", e);
+        }
+    }
+
+    @Override
+    public void merge(final InstanceIdentifier path, final NormalizedNode<?, ?> data) {
+        checkNotReady();
+        try {
+            LOG.debug("Tx: {} Merge: {}:{}", getIdentifier(), path, data);
+            mutableTree.merge(path, data);
+            // FIXME: Add checked exception
+        } catch (Exception e) {
+            LOG.error("Tx: {}, failed to write {}:{} in {}", getIdentifier(), path, data, mutableTree, e);
+            // Rethrow original ones if they are subclasses of RuntimeException
+            // or Error
+            Throwables.propagateIfPossible(e);
+            // FIXME: Introduce proper checked exception
+            throw new IllegalArgumentException("Illegal input data.", e);
+        }
+    }
+
+    @Override
+    public void delete(final InstanceIdentifier path) {
+        checkNotReady();
+        try {
+            LOG.debug("Tx: {} Delete: {}", getIdentifier(), path);
+            mutableTree.delete(path);
+            // FIXME: Add checked exception
+        } catch (Exception e) {
+            LOG.error("Tx: {}, failed to delete {} in {}", getIdentifier(), path, mutableTree, e);
+            // Rethrow original ones if they are subclasses of RuntimeException
+            // or Error
+            Throwables.propagateIfPossible(e);
+            // FIXME: Introduce proper checked exception
+            throw new IllegalArgumentException("Illegal path to delete.", e);
+        }
+    }
+
+    protected final boolean isReady() {
+        return ready;
+    }
+
+    protected final void checkNotReady() {
+        checkState(!ready, "Transaction %s is ready. No further modifications allowed.", getIdentifier());
+    }
+
+    @Override
+    public synchronized DOMStoreThreePhaseCommitCohort ready() {
+        checkState(!ready, "Transaction %s is already ready.", getIdentifier());
+        ready = true;
+        LOG.debug("Store transaction: {} : Ready", getIdentifier());
+        mutableTree.ready();
+        return readyImpl.ready(this);
+    }
+
+    protected DataTreeModification getMutatedView() {
+        return mutableTree;
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper.add("ready", isReady());
+    }
+
+    /**
+     * Prototype implementation of
+     * {@link #ready(SnapshotBackedWriteTransaction)}
+     * 
+     * This class is intended to be implemented by Transaction factories
+     * responsible for allocation of {@link SnapshotBackedWriteTransaction} and
+     * providing underlying logic for applying implementation.
+     * 
+     */
+    public static interface TransactionReadyPrototype {
+
+        /**
+         * Returns a commit coordinator associated with supplied transactions.
+         * 
+         * This call must not fail.
+         * 
+         * @param tx
+         *            Transaction on which ready was invoked.
+         * @return DOMStoreThreePhaseCommitCohort associated with transaction
+         */
+        DOMStoreThreePhaseCommitCohort ready(SnapshotBackedWriteTransaction tx);
+    }
+}
\ No newline at end of file
index f7e95b84bd4df38c214c32f10c1b01df61fb4798..39ff4f0aa03f9b0ad1d3ccdce080b4cf7c7989a2 100644 (file)
@@ -12,6 +12,7 @@ import java.util.Map.Entry;
 import javax.annotation.concurrent.GuardedBy;
 
 import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeModification;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
 import org.opendaylight.controller.md.sal.dom.store.impl.tree.TreeNodeUtils;
 import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.TreeNode;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
@@ -151,7 +152,27 @@ final class InMemoryDataTreeModification implements DataTreeModification {
     public synchronized DataTreeModification newModification() {
         Preconditions.checkState(sealed, "Attempted to chain on an unsealed modification");
 
-        // FIXME: transaction chaining
-        throw new UnsupportedOperationException("Implement this as part of transaction chaining");
+        if(rootNode.getType() == ModificationType.UNMODIFIED) {
+            return snapshot.newModification();
+        }
+
+        /*
+         *  FIXME: Add advanced transaction chaining for modification of not rebased
+         *  modification.
+         *
+         *  Current computation of tempRoot may yeld incorrect subtree versions
+         *  if there are multiple concurrent transactions, which may break
+         *  versioning preconditions for modification of previously occured write,
+         *  directly nested under parent node, since node version is derived from
+         *  subtree version.
+         *
+         *  For deeper nodes subtree version is derived from their respective metadata
+         *  nodes, so this incorrect root subtree version is not affecting us.
+         */
+        TreeNode originalSnapshotRoot = snapshot.getRootNode();
+        Optional<TreeNode> tempRoot = strategyTree.apply(rootNode, Optional.of(originalSnapshotRoot), originalSnapshotRoot.getSubtreeVersion().next());
+
+        InMemoryDataTreeSnapshot tempTree = new InMemoryDataTreeSnapshot(snapshot.getSchemaContext(), tempRoot.get(), strategyTree);
+        return tempTree.newModification();
     }
 }
index 53423f6b09eb8798b3e6c89a3b20a4c6f98eb862..ba9b2b7f55964d90023532082bc41767f037daa4 100644 (file)
@@ -103,7 +103,7 @@ AbstractDataReadRouter<InstanceIdentifier, CompositeNode> {
                         "Only one simple node for key $s is allowed in node $s",
                         keyValue.getKey(), node);
                 checkState(
-                        simpleNode.get(0).getValue() == keyValue.getValue(),
+                        simpleNode.get(0).getValue().equals(keyValue.getValue()),
                         "Key node must equal to instance identifier value in node $s",
                         node);
                 ret.put(keyValue.getKey(), simpleNode.get(0));
index c0f0a35565db16558354096d27ea75189cd60873..94ac8d652e637491311c7ac94ce1ba043016569d 100644 (file)
@@ -12,6 +12,7 @@ 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.controller.sal.core.spi.data.DOMStoreTransactionChain;
 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;
@@ -25,7 +26,6 @@ public class InMemoryDataStoreTest {
     private SchemaContext schemaContext;
     private InMemoryDOMDataStore domStore;
 
-
     @Before
     public void setupStore() {
         domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.sameThreadExecutor());
@@ -34,40 +34,36 @@ public class InMemoryDataStoreTest {
 
     }
 
-
     @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.
-         *
+         * 
+         * 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.
-        *
-        */
+         * 
+         * Reads /test from readTx Read should return Absent.
+         * 
+         */
         ListenableFuture<Optional<NormalizedNode<?, ?>>> readTxContainer = readTx.read(TestModel.TEST_PATH);
         assertFalse(readTxContainer.get().isPresent());
     }
@@ -78,17 +74,16 @@ public class InMemoryDataStoreTest {
         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.
-         *
+         * 
+         * Reads /test from writeTx Read should return container.
+         * 
          */
         ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
         assertTrue(writeTxContainer.get().isPresent());
@@ -97,7 +92,8 @@ public class InMemoryDataStoreTest {
 
         assertThreePhaseCommit(cohort);
 
-        Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
+        Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
+                .get();
         assertTrue(afterCommitRead.isPresent());
     }
 
@@ -115,10 +111,91 @@ public class InMemoryDataStoreTest {
         cohort.preCommit().get();
         cohort.abort().get();
 
-        Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
+        Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
+                .get();
         assertFalse(afterCommitRead.isPresent());
     }
 
+    @Test
+    public void testTransactionChain() throws InterruptedException, ExecutionException {
+        DOMStoreTransactionChain txChain = domStore.createTransactionChain();
+        assertNotNull(txChain);
+
+        /**
+         * We alocate new read-write transaction and write /test
+         * 
+         * 
+         */
+        DOMStoreReadWriteTransaction firstTx = txChain.newReadWriteTransaction();
+        assertTestContainerWrite(firstTx);
+
+        /**
+         * First transaction is marked as ready, we are able to allocate chained
+         * transactions
+         */
+        DOMStoreThreePhaseCommitCohort firstWriteTxCohort = firstTx.ready();
+
+        /**
+         * We alocate chained transaction - read transaction, note first one is
+         * still not commited to datastore.
+         */
+        DOMStoreReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
+
+        /**
+         * 
+         * We test if we are able to read data from tx, read should not fail
+         * since we are using chained transaction.
+         * 
+         * 
+         */
+        assertTestContainerExists(secondReadTx);
+
+        /**
+         * 
+         * We alocate next transaction, which is still based on first one, but
+         * is read-write.
+         * 
+         */
+        DOMStoreReadWriteTransaction thirdDeleteTx = txChain.newReadWriteTransaction();
+
+        /**
+         * We test existence of /test in third transaction container should
+         * still be visible from first one (which is still uncommmited).
+         * 
+         * 
+         */
+        assertTestContainerExists(thirdDeleteTx);
+
+        /**
+         * We delete node in third transaction
+         */
+        thirdDeleteTx.delete(TestModel.TEST_PATH);
+
+        /**
+         * third transaction is sealed.
+         */
+        DOMStoreThreePhaseCommitCohort thirdDeleteTxCohort = thirdDeleteTx.ready();
+
+        /**
+         * We commit first transaction
+         * 
+         */
+        assertThreePhaseCommit(firstWriteTxCohort);
+
+        // Alocates store transacion
+        DOMStoreReadTransaction storeReadTx = domStore.newReadOnlyTransaction();
+        /**
+         * We verify transaction is commited to store, container should exists
+         * in datastore.
+         */
+        assertTestContainerExists(storeReadTx);
+        /**
+         * We commit third transaction
+         * 
+         */
+        assertThreePhaseCommit(thirdDeleteTxCohort);
+    }
+
     @Test
     @Ignore
     public void testTransactionConflict() throws InterruptedException, ExecutionException {
@@ -138,32 +215,36 @@ public class InMemoryDataStoreTest {
         assertFalse(txTwo.ready().canCommit().get());
     }
 
-
-
-    private static void assertThreePhaseCommit(final DOMStoreThreePhaseCommitCohort cohort) throws InterruptedException, ExecutionException {
+    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 {
+    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));
+         * 
+         * Writes /test in writeTx
+         * 
+         */
+        writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+        return assertTestContainerExists(writeTx);
+    }
+
+    /**
+     * 
+     * Reads /test from readTx Read should return container.
+     * 
+     */
+    private static Optional<NormalizedNode<?, ?>> assertTestContainerExists(DOMStoreReadTransaction readTx)
+            throws InterruptedException, ExecutionException {
 
-       /**
-        *
-        * Reads /test from writeTx
-        * Read should return container.
-        *
-        */
-       ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
-       assertTrue(writeTxContainer.get().isPresent());
-       return writeTxContainer.get();
+        ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
+        assertTrue(writeTxContainer.get().isPresent());
+        return writeTxContainer.get();
     }
 
 }
index 9ad95c18d9777be4f1fff9432b7c2d7eaf2e55a6..0dd25292c2b3810e78fbaf3009f9b3a212f8675a 100644 (file)
       <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>threadpool-config-api</artifactId>
       <artifactId>junit</artifactId>
       <scope>test</scope>
     </dependency>
-    <dependency>
-      <groupId>org.mockito</groupId>
-      <artifactId>mockito-all</artifactId>
-      <scope>test</scope>
-    </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>logback-config</artifactId>
       <type>jar</type>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>mockito-configuration</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
index 1839de15975f381ebc8d32980050cfb16ace72ad..f73d9cc72f893aa0260e6adc7bf0f257eb18252e 100644 (file)
@@ -10,38 +10,29 @@ package org.opendaylight.controller.config.yang.md.sal.connector.netconf;
 import static org.opendaylight.controller.config.api.JmxAttributeValidationException.checkCondition;
 import static org.opendaylight.controller.config.api.JmxAttributeValidationException.checkNotNull;
 
-import com.google.common.net.InetAddresses;
-import io.netty.util.HashedWheelTimer;
-import io.netty.util.concurrent.GlobalEventExecutor;
 import java.io.File;
 import java.io.InputStream;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
-import java.net.URI;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
-import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+
 import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
 import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl;
 import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration;
-import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder;
 import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfiguration;
 import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
 import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
-import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
-import org.opendaylight.controller.sal.connect.netconf.InventoryUtils;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.controller.sal.connect.netconf.NetconfDevice;
-import org.opendaylight.controller.sal.connect.netconf.NetconfDeviceListener;
-import org.opendaylight.controller.sal.core.api.data.DataChangeListener;
+import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCommunicator;
+import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceSalFacade;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.controller.sal.core.api.Broker;
 import org.opendaylight.protocol.framework.ReconnectStrategy;
 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
 import org.opendaylight.protocol.framework.TimedReconnectStrategy;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.inventory.rev140108.NetconfNode;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.model.util.repo.AbstractCachingSchemaSourceProvider;
 import org.opendaylight.yangtools.yang.model.util.repo.FilesystemSchemaCachingProvider;
 import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
@@ -51,6 +42,10 @@ import org.osgi.framework.ServiceReference;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Preconditions;
+import com.google.common.net.InetAddresses;
+import io.netty.util.HashedWheelTimer;
+
 /**
  *
  */
@@ -58,22 +53,20 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
 {
     private static final Logger logger = LoggerFactory.getLogger(NetconfConnectorModule.class);
 
-    private static ExecutorService GLOBAL_PROCESSING_EXECUTOR = null;
     private static AbstractCachingSchemaSourceProvider<String, InputStream> GLOBAL_NETCONF_SOURCE_PROVIDER = null;
     private BundleContext bundleContext;
 
-    public NetconfConnectorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+    public NetconfConnectorModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
         super(identifier, dependencyResolver);
     }
 
-    public NetconfConnectorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, NetconfConnectorModule oldModule, java.lang.AutoCloseable oldInstance) {
+    public NetconfConnectorModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final NetconfConnectorModule oldModule, final java.lang.AutoCloseable oldInstance) {
         super(identifier, dependencyResolver, oldModule, oldInstance);
     }
 
     @Override
     protected void customValidation() {
         checkNotNull(getAddress(), addressJmxAttribute);
-        //checkState(getAddress().getIpv4Address() != null || getAddress().getIpv6Address() != null,"Address must be set.");
         checkNotNull(getPort(), portJmxAttribute);
         checkNotNull(getDomRegistry(), portJmxAttribute);
         checkNotNull(getDomRegistry(), domRegistryJmxAttribute);
@@ -96,41 +89,83 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
             checkNotNull(getPassword(), passwordJmxAttribute);
         }
 
+        // FIXME BUG 944 remove this warning
+        if(getBindingRegistry() == null) {
+            logger.warn("Configuration property: \"binding-registry\" not set for sal-netconf-connector (" + getIdentifier() + "). " +
+                    "Netconf-connector now requires a dependency on \"binding-broker-osgi-registry\". " +
+                    "The dependency is optional for now to preserve backwards compatibility, but will be mandatory in the future. " +
+                    "Please set the property as in \"01-netconf-connector\" initial config file. " +
+                    "The service will be retrieved from OSGi service registry now.");
+        }
+
+        // FIXME BUG 944 remove this warning
+        if(getProcessingExecutor() == null) {
+            logger.warn("Configuration property: \"processing-executor\" not set for sal-netconf-connector (" + getIdentifier() + "). " +
+                    "Netconf-connector now requires a dependency on \"threadpool\". " +
+                    "The dependency is optional for now to preserve backwards compatibility, but will be mandatory in the future. " +
+                    "Please set the property as in \"01-netconf-connector\" initial config file. " +
+                    "New instance will be created for the executor.");
+        }
     }
 
     @Override
     public java.lang.AutoCloseable createInstance() {
-        ServiceReference<DataProviderService> serviceReference = bundleContext.getServiceReference(DataProviderService.class);
+        final RemoteDeviceId id = new RemoteDeviceId(getIdentifier());
 
-        DataProviderService dataProviderService =
-                bundleContext.getService(serviceReference);
+        final ExecutorService globalProcessingExecutor = getGlobalProcessingExecutor();
 
-        getDomRegistryDependency();
-        NetconfDevice device = new NetconfDevice(getIdentifier().getInstanceName());
+        final Broker domBroker = getDomRegistryDependency();
+        final BindingAwareBroker bindingBroker = getBindingRegistryBackwards();
 
-        device.setClientConfig(getClientConfig(device));
+        final RemoteDeviceHandler salFacade = new NetconfDeviceSalFacade(id, domBroker, bindingBroker, bundleContext, globalProcessingExecutor);
+        final NetconfDevice device =
+                NetconfDevice.createNetconfDevice(id, getGlobalNetconfSchemaProvider(), globalProcessingExecutor, salFacade);
+        final NetconfDeviceCommunicator listener = new NetconfDeviceCommunicator(id, device);
+        final NetconfReconnectingClientConfiguration clientConfig = getClientConfig(listener);
 
-        device.setProcessingExecutor(getGlobalProcessingExecutor());
+        // FIXME BUG-944 remove backwards compatibility
+        final NetconfClientDispatcher dispatcher = getClientDispatcher() == null ? createDispatcher() : getClientDispatcherDependency();
+        listener.initializeRemoteConnection(dispatcher, clientConfig);
 
-        device.setEventExecutor(getEventExecutorDependency());
-        device.setDispatcher(getClientDispatcher() == null ? createDispatcher() : getClientDispatcherDependency());
-        device.setSchemaSourceProvider(getGlobalNetconfSchemaProvider(bundleContext));
-        device.setDataProviderService(dataProviderService);
-        getDomRegistryDependency().registerProvider(device, bundleContext);
-        device.start();
-        return device;
+        return new AutoCloseable() {
+            @Override
+            public void close() throws Exception {
+                listener.close();
+                salFacade.close();
+            }
+        };
+    }
+
+    private BindingAwareBroker getBindingRegistryBackwards() {
+        if(getBindingRegistry() != null) {
+            return getBindingRegistryDependency();
+        } else {
+            // FIXME BUG 944 remove backwards compatibility
+            final ServiceReference<BindingAwareBroker> serviceReference = bundleContext.getServiceReference(BindingAwareBroker.class);
+            Preconditions
+                    .checkNotNull(
+                            serviceReference,
+                            "Unable to retrieve %s from OSGi service registry, use binding-registry config property to inject %s with config subsystem",
+                            BindingAwareBroker.class, BindingAwareBroker.class);
+            return bundleContext.getService(serviceReference);
+        }
     }
 
     private ExecutorService getGlobalProcessingExecutor() {
-        return GLOBAL_PROCESSING_EXECUTOR == null ? Executors.newCachedThreadPool() : GLOBAL_PROCESSING_EXECUTOR;
+        if(getProcessingExecutor() != null) {
+            return getProcessingExecutorDependency().getExecutor();
+        } else {
+            // FIXME BUG 944 remove backwards compatibility
+            return Executors.newCachedThreadPool();
+        }
     }
 
-    private synchronized AbstractCachingSchemaSourceProvider<String, InputStream> getGlobalNetconfSchemaProvider(BundleContext bundleContext) {
+    private synchronized AbstractCachingSchemaSourceProvider<String, InputStream> getGlobalNetconfSchemaProvider() {
         if(GLOBAL_NETCONF_SOURCE_PROVIDER == null) {
-            String storageFile = "cache/schema";
+            final String storageFile = "cache/schema";
             //            File directory = bundleContext.getDataFile(storageFile);
-            File directory = new File(storageFile);
-            SchemaSourceProvider<String> defaultProvider = SchemaSourceProviders.noopProvider();
+            final File directory = new File(storageFile);
+            final SchemaSourceProvider<String> defaultProvider = SchemaSourceProviders.noopProvider();
             GLOBAL_NETCONF_SOURCE_PROVIDER = FilesystemSchemaCachingProvider.createFromStringSourceProvider(defaultProvider, directory);
         }
         return GLOBAL_NETCONF_SOURCE_PROVIDER;
@@ -146,20 +181,20 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
         return new NetconfClientDispatcherImpl(getBossThreadGroupDependency(), getWorkerThreadGroupDependency(), new HashedWheelTimer());
     }
 
-    public void setBundleContext(BundleContext bundleContext) {
+    public void setBundleContext(final BundleContext bundleContext) {
         this.bundleContext = bundleContext;
     }
 
-    public NetconfReconnectingClientConfiguration getClientConfig(final NetconfDevice device) {
-        InetSocketAddress socketAddress = getSocketAddress();
-        ReconnectStrategy strategy = getReconnectStrategy();
-        long clientConnectionTimeoutMillis = getConnectionTimeoutMillis();
+    public NetconfReconnectingClientConfiguration getClientConfig(final NetconfDeviceCommunicator listener) {
+        final InetSocketAddress socketAddress = getSocketAddress();
+        final ReconnectStrategy strategy = getReconnectStrategy();
+        final long clientConnectionTimeoutMillis = getConnectionTimeoutMillis();
 
         return NetconfReconnectingClientConfigurationBuilder.create()
         .withAddress(socketAddress)
         .withConnectionTimeoutMillis(clientConnectionTimeoutMillis)
         .withReconnectStrategy(strategy)
-        .withSessionListener(new NetconfDeviceListener(device))
+        .withSessionListener(listener)
         .withAuthHandler(new LoginPassword(getUsername(),getPassword()))
         .withProtocol(getTcpOnly() ?
                 NetconfClientConfiguration.NetconfClientProtocol.TCP :
@@ -174,19 +209,19 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
     }
 
     private ReconnectStrategy getReconnectStrategy() {
-        Long connectionAttempts;
+        final Long connectionAttempts;
         if (getMaxConnectionAttempts() != null && getMaxConnectionAttempts() > 0) {
             connectionAttempts = getMaxConnectionAttempts();
         } else {
             logger.trace("Setting {} on {} to infinity", maxConnectionAttemptsJmxAttribute, this);
             connectionAttempts = null;
         }
-        double sleepFactor = 1.5;
-        int minSleep = 1000;
-        Long maxSleep = null;
-        Long deadline = null;
+        final double sleepFactor = getSleepFactor().doubleValue();
+        final int minSleep = getBetweenAttemptsTimeoutMillis();
+        final Long maxSleep = null;
+        final Long deadline = null;
 
-        return new TimedReconnectStrategy(GlobalEventExecutor.INSTANCE, getBetweenAttemptsTimeoutMillis(),
+        return new TimedReconnectStrategy(getEventExecutorDependency(), getBetweenAttemptsTimeoutMillis(),
                 minSleep, sleepFactor, maxSleep, connectionAttempts, deadline);
     }
 
@@ -199,7 +234,7 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
             addressValue = getAddress().getIpv6Address().getValue();
         }
          */
-        InetAddress inetAddress = InetAddresses.forString(getAddress());
+        final InetAddress inetAddress = InetAddresses.forString(getAddress());
         return new InetSocketAddress(inetAddress, getPort().intValue());
     }
 }
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/MessageTransformer.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/MessageTransformer.java
new file mode 100644 (file)
index 0000000..7a392a8
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.connect.api;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+
+public interface MessageTransformer<M> extends SchemaContextListener {
+
+    CompositeNode toNotification(M message);
+
+    M toRpcRequest(QName rpc, CompositeNode node);
+
+    RpcResult<CompositeNode> toRpcResult(M message, QName rpc);
+
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDevice.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDevice.java
new file mode 100644 (file)
index 0000000..e0d2433
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.connect.api;
+
+/**
+ *
+ */
+public interface RemoteDevice<PREF, M> {
+
+    void onRemoteSessionUp(PREF remoteSessionCapabilities, RemoteDeviceCommunicator<M> listener);
+
+    void onRemoteSessionDown();
+
+    void onNotification(M notification);
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceCommunicator.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceCommunicator.java
new file mode 100644 (file)
index 0000000..67cb29a
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.connect.api;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+public interface RemoteDeviceCommunicator<M> extends AutoCloseable {
+
+    ListenableFuture<RpcResult<M>> sendRequest(M message, QName rpc);
+
+    void close();
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceHandler.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceHandler.java
new file mode 100644 (file)
index 0000000..b2845d5
--- /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.connect.api;
+
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+
+public interface RemoteDeviceHandler<PREF> extends AutoCloseable {
+
+    void onDeviceConnected(SchemaContextProvider remoteSchemaContextProvider,
+                           PREF netconfSessionPreferences, RpcImplementation deviceRpc);
+
+    void onDeviceDisconnected();
+
+    void onNotification(CompositeNode domNotification);
+
+    void close();
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/SchemaContextProviderFactory.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/SchemaContextProviderFactory.java
new file mode 100644 (file)
index 0000000..43577c3
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.connect.api;
+
+import java.io.InputStream;
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
+
+public interface SchemaContextProviderFactory {
+
+    SchemaContextProvider createContextProvider(Collection<QName> capabilities, SchemaSourceProvider<InputStream> sourceProvider);
+
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/SchemaSourceProviderFactory.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/SchemaSourceProviderFactory.java
new file mode 100644 (file)
index 0000000..7037231
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.connect.api;
+
+import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceRpc;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
+
+public interface SchemaSourceProviderFactory<T> {
+
+    SchemaSourceProvider<T> createSourceProvider(final RpcImplementation deviceRpc);
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/package-info.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/package-info.java
new file mode 100644 (file)
index 0000000..022021d
--- /dev/null
@@ -0,0 +1,6 @@
+/**
+ * General API for remote connectors e.g. netconf connector
+ *
+ * TODO extract into separate bundle when another connector is implemented e.g. restconf connector
+ */
+package org.opendaylight.controller.sal.connect.api;
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/InventoryUtils.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/InventoryUtils.java
deleted file mode 100644 (file)
index e6dc59c..0000000
+++ /dev/null
@@ -1,57 +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.sal.connect.netconf;
-
-import java.net.URI;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class InventoryUtils {
-    private static final Logger LOG = LoggerFactory.getLogger(InventoryUtils.class);
-    private static final URI INVENTORY_NAMESPACE = URI.create("urn:opendaylight:inventory");
-    private static final URI NETCONF_INVENTORY_NAMESPACE = URI.create("urn:opendaylight:netconf-node-inventory");
-    private static final Date INVENTORY_REVISION = dateFromString("2013-08-19");
-    private static final Date NETCONF_INVENTORY_REVISION = dateFromString("2014-01-08");
-    public static final QName INVENTORY_NODES = new QName(INVENTORY_NAMESPACE, INVENTORY_REVISION, "nodes");
-    public static final QName INVENTORY_NODE = new QName(INVENTORY_NAMESPACE, INVENTORY_REVISION, "node");
-    public static final QName INVENTORY_ID = new QName(INVENTORY_NAMESPACE, INVENTORY_REVISION, "id");
-    public static final QName INVENTORY_CONNECTED = new QName(NETCONF_INVENTORY_NAMESPACE, NETCONF_INVENTORY_REVISION,
-            "connected");
-    public static final QName NETCONF_INVENTORY_INITIAL_CAPABILITY = new QName(NETCONF_INVENTORY_NAMESPACE,
-            NETCONF_INVENTORY_REVISION, "initial-capability");
-
-    public static final InstanceIdentifier INVENTORY_PATH = InstanceIdentifier.builder().node(INVENTORY_NODES)
-            .toInstance();
-    public static final QName NETCONF_INVENTORY_MOUNT = null;
-
-    private InventoryUtils() {
-        throw new UnsupportedOperationException("Utility class cannot be instantiated");
-    }
-
-    /**
-     * Converts date in string format yyyy-MM-dd to java.util.Date.
-     *
-     * @return java.util.Date conformant to string formatted date yyyy-MM-dd.
-     */
-    private static Date dateFromString(final String date) {
-        // We do not reuse the formatter because it's not thread-safe
-        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
-        try {
-            return formatter.parse(date);
-        } catch (ParseException e) {
-            LOG.error("Failed to parse date {}", date, e);
-            return null;
-        }
-    }
-}
index 94beaed0dfc61aeb0c5f260c24a1a689ec9fd7a1..dca8fcafef4a88e31ecc6898dc52bc12b7aa5936 100644 (file)
  */
 package org.opendaylight.controller.sal.connect.netconf;
 
-import static com.google.common.base.Preconditions.checkState;
-import static org.opendaylight.controller.sal.connect.netconf.InventoryUtils.INVENTORY_CONNECTED;
-import static org.opendaylight.controller.sal.connect.netconf.InventoryUtils.INVENTORY_ID;
-import static org.opendaylight.controller.sal.connect.netconf.InventoryUtils.INVENTORY_NODE;
-import static org.opendaylight.controller.sal.connect.netconf.InventoryUtils.INVENTORY_PATH;
-import static org.opendaylight.controller.sal.connect.netconf.InventoryUtils.NETCONF_INVENTORY_INITIAL_CAPABILITY;
-import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.CONFIG_SOURCE_RUNNING;
-import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_DATA_QNAME;
-import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_GET_CONFIG_QNAME;
-import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_GET_QNAME;
-import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.toFilterStructure;
-import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.toRpcMessage;
-import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.wrap;
-
-import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
 import java.io.InputStream;
-import java.net.InetSocketAddress;
-import java.net.URI;
-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.ExecutorService;
 
-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.md.sal.common.api.data.DataReader;
-import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
-import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration;
-import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfiguration;
-import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
-import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
-import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration;
-import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.sal.connect.api.MessageTransformer;
+import org.opendaylight.controller.sal.connect.api.RemoteDevice;
+import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator;
+import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
+import org.opendaylight.controller.sal.connect.api.SchemaContextProviderFactory;
+import org.opendaylight.controller.sal.connect.api.SchemaSourceProviderFactory;
+import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionCapabilities;
+import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceRpc;
+import org.opendaylight.controller.sal.connect.netconf.schema.NetconfDeviceSchemaProviderFactory;
+import org.opendaylight.controller.sal.connect.netconf.schema.NetconfRemoteSchemaSourceProvider;
+import org.opendaylight.controller.sal.connect.netconf.schema.mapping.NetconfMessageTransformer;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.controller.sal.core.api.RpcImplementation;
-import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
-import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
-import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
-import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
-import org.opendaylight.protocol.framework.ReconnectStrategy;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.inventory.rev140108.NetconfNode;
-import org.opendaylight.yangtools.concepts.Registration;
-import org.opendaylight.yangtools.yang.common.QName;
-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.Node;
-import org.opendaylight.yangtools.yang.data.api.SimpleNode;
-import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
-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.Module;
-import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
 import org.opendaylight.yangtools.yang.model.util.repo.AbstractCachingSchemaSourceProvider;
 import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
-import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
-import org.opendaylight.yangtools.yang.parser.impl.util.YangSourceContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.Iterables;
-import com.google.common.util.concurrent.ListenableFuture;
-import io.netty.util.concurrent.EventExecutor;
-
-public class NetconfDevice implements Provider, //
-        DataReader<InstanceIdentifier, CompositeNode>, //
-        DataCommitHandler<InstanceIdentifier, CompositeNode>, //
-        RpcImplementation, //
-        AutoCloseable {
-
-    InetSocketAddress socketAddress;
-
-    MountProvisionInstance mountInstance;
-
-    EventExecutor eventExecutor;
-
-    ExecutorService processingExecutor;
-
-    InstanceIdentifier path;
-
-    ReconnectStrategy reconnectStrategy;
-
-    AbstractCachingSchemaSourceProvider<String, InputStream> schemaSourceProvider;
-
-    private NetconfDeviceSchemaContextProvider deviceContextProvider;
-
-    protected Logger logger;
-
-    Registration<DataReader<InstanceIdentifier, CompositeNode>> operReaderReg;
-    Registration<DataReader<InstanceIdentifier, CompositeNode>> confReaderReg;
-    Registration<DataCommitHandler<InstanceIdentifier, CompositeNode>> commitHandlerReg;
-    List<RpcRegistration> rpcReg;
-
-    String name;
-
-    MountProvisionService mountService;
-
-    NetconfClientDispatcher dispatcher;
-
-    static InstanceIdentifier ROOT_PATH = InstanceIdentifier.builder().toInstance();
-
-    SchemaSourceProvider<InputStream> remoteSourceProvider;
-
-    private volatile DataBrokerService dataBroker;
-
-    NetconfDeviceListener listener;
-
-    private boolean rollbackSupported;
-
-    private NetconfReconnectingClientConfiguration clientConfig;
-    private volatile DataProviderService dataProviderService;
-
-    public NetconfDevice(String name) {
-        this.name = name;
-        this.logger = LoggerFactory.getLogger(NetconfDevice.class + "#" + name);
-        this.path = InstanceIdentifier.builder(INVENTORY_PATH)
-                .nodeWithKey(INVENTORY_NODE, Collections.<QName, Object>singletonMap(INVENTORY_ID, name)).toInstance();
-    }
-
-    public void start() {
-        checkState(dispatcher != null, "Dispatcher must be set.");
-        checkState(schemaSourceProvider != null, "Schema Source Provider must be set.");
-        checkState(eventExecutor != null, "Event executor must be set.");
-
-        Preconditions.checkArgument(clientConfig.getSessionListener() instanceof NetconfDeviceListener);
-        listener = (NetconfDeviceListener) clientConfig.getSessionListener();
-
-        logger.info("Starting NETCONF Client {} for address {}", name, socketAddress);
-
-        dispatcher.createReconnectingClient(clientConfig);
-    }
-
-    Optional<SchemaContext> getSchemaContext() {
-        if (deviceContextProvider == null) {
-            return Optional.absent();
-        }
-        return deviceContextProvider.currentContext;
-    }
-
-    void bringDown() {
-        if (rpcReg != null) {
-            for (RpcRegistration reg : rpcReg) {
-                reg.close();
-            }
-            rpcReg = null;
-        }
-        closeGracefully(confReaderReg);
-        confReaderReg = null;
-        closeGracefully(operReaderReg);
-        operReaderReg = null;
-        closeGracefully(commitHandlerReg);
-        commitHandlerReg = null;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
 
-        updateDeviceState(false, Collections.<QName> emptySet());
-    }
+/**
+ *  This is a mediator between NetconfDeviceCommunicator and NetconfDeviceSalFacade
+ */
+public final class NetconfDevice implements RemoteDevice<NetconfSessionCapabilities, NetconfMessage> {
 
-    private void closeGracefully(final AutoCloseable resource) {
-        if (resource != null) {
-            try {
-                resource.close();
-            } catch (Exception e) {
-                logger.warn("Ignoring exception while closing {}", resource, e);
-            }
-        }
-    }
+    private static final Logger logger = LoggerFactory.getLogger(NetconfDevice.class);
 
-    void bringUp(final SchemaSourceProvider<String> delegate, final Set<QName> capabilities, final boolean rollbackSupported) {
-        // This has to be called from separate thread, not from netty thread calling onSessionUp in DeviceListener.
-        // Reason: delegate.getSchema blocks thread when waiting for response
-        // however, if the netty thread is blocked, no incoming message can be processed
-        // ... netty should pick another thread from pool to process incoming message, but it does not http://netty.io/wiki/thread-model.html
-        // TODO redesign +refactor
-        processingExecutor.submit(new Runnable() {
-            @Override
-            public void run() {
-                NetconfDevice.this.rollbackSupported = rollbackSupported;
-                remoteSourceProvider = schemaSourceProvider.createInstanceFor(delegate);
-                deviceContextProvider = new NetconfDeviceSchemaContextProvider(NetconfDevice.this, remoteSourceProvider);
-                deviceContextProvider.createContextFromCapabilities(capabilities);
-                if (mountInstance != null && getSchemaContext().isPresent()) {
-                    mountInstance.setSchemaContext(getSchemaContext().get());
-                }
+    private final RemoteDeviceId id;
 
-                updateDeviceState(true, capabilities);
+    private final RemoteDeviceHandler<NetconfSessionCapabilities> salFacade;
+    private final ListeningExecutorService processingExecutor;
+    private final MessageTransformer<NetconfMessage> messageTransformer;
+    private final SchemaContextProviderFactory schemaContextProviderFactory;
+    private final SchemaSourceProviderFactory<InputStream> sourceProviderFactory;
 
-                if (mountInstance != null) {
-                    confReaderReg = mountInstance.registerConfigurationReader(ROOT_PATH, NetconfDevice.this);
-                    operReaderReg = mountInstance.registerOperationalReader(ROOT_PATH, NetconfDevice.this);
-                    commitHandlerReg = mountInstance.registerCommitHandler(ROOT_PATH, NetconfDevice.this);
+    public static NetconfDevice createNetconfDevice(final RemoteDeviceId id,
+            final AbstractCachingSchemaSourceProvider<String, InputStream> schemaSourceProvider,
+            final ExecutorService executor, final RemoteDeviceHandler<NetconfSessionCapabilities> salFacade) {
 
-                    List<RpcRegistration> rpcs = new ArrayList<>();
-                    // TODO same condition twice
-                    if (mountInstance != null && getSchemaContext().isPresent()) {
-                        for (RpcDefinition rpc : mountInstance.getSchemaContext().getOperations()) {
-                            rpcs.add(mountInstance.addRpcImplementation(rpc.getQName(), NetconfDevice.this));
-                        }
+        return new NetconfDevice(id, salFacade, executor, new NetconfMessageTransformer(),
+                new NetconfDeviceSchemaProviderFactory(id), new SchemaSourceProviderFactory<InputStream>() {
+                    @Override
+                    public SchemaSourceProvider<InputStream> createSourceProvider(final RpcImplementation deviceRpc) {
+                        return schemaSourceProvider.createInstanceFor(new NetconfRemoteSchemaSourceProvider(id,
+                                deviceRpc));
                     }
-                    rpcReg = rpcs;
-                }
-            }
-        });
-    }
-
-    private void updateDeviceState(boolean up, Set<QName> capabilities) {
-        checkDataStoreState();
-
-        DataModificationTransaction transaction = dataBroker.beginTransaction();
-
-        CompositeNodeBuilder<ImmutableCompositeNode> it = ImmutableCompositeNode.builder();
-        it.setQName(INVENTORY_NODE);
-        it.addLeaf(INVENTORY_ID, name);
-        it.addLeaf(INVENTORY_CONNECTED, up);
-
-        logger.debug("Client capabilities {}", capabilities);
-        for (QName capability : capabilities) {
-            it.addLeaf(NETCONF_INVENTORY_INITIAL_CAPABILITY, capability.toString());
-        }
-
-        logger.debug("Update device state transaction " + transaction.getIdentifier()
-                + " putting operational data started.");
-        transaction.removeOperationalData(path);
-        transaction.putOperationalData(path, it.toInstance());
-        logger.debug("Update device state transaction " + transaction.getIdentifier()
-                + " putting operational data ended.");
-
-        // FIXME: this has to be asynchronous
-        RpcResult<TransactionStatus> transactionStatus = null;
-        try {
-            transactionStatus = transaction.commit().get();
-        } catch (InterruptedException e) {
-            throw new RuntimeException("Interrupted while waiting for response", e);
-        } catch (ExecutionException e) {
-            throw new RuntimeException("Read configuration data " + path + " failed", e);
-        }
-        // TODO better ex handling
-
-        if (transactionStatus.isSuccessful()) {
-            logger.debug("Update device state transaction " + transaction.getIdentifier() + " SUCCESSFUL.");
-        } else {
-            logger.debug("Update device state transaction " + transaction.getIdentifier() + " FAILED!");
-            logger.debug("Update device state transaction status " + transaction.getStatus());
-        }
-    }
-
-    @Override
-    public CompositeNode readConfigurationData(InstanceIdentifier path) {
-        RpcResult<CompositeNode> result = null;
-        try {
-            result = this.invokeRpc(NETCONF_GET_CONFIG_QNAME,
-                    wrap(NETCONF_GET_CONFIG_QNAME, CONFIG_SOURCE_RUNNING, toFilterStructure(path))).get();
-        } catch (InterruptedException e) {
-            throw new RuntimeException("Interrupted while waiting for response", e);
-        } catch (ExecutionException e) {
-            throw new RuntimeException("Read configuration data " + path + " failed", e);
-        }
-
-        CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME);
-        return data == null ? null : (CompositeNode) findNode(data, path);
-    }
-
-    @Override
-    public CompositeNode readOperationalData(InstanceIdentifier path) {
-        RpcResult<CompositeNode> result = null;
-        try {
-            result = invokeRpc(NETCONF_GET_QNAME, wrap(NETCONF_GET_QNAME, toFilterStructure(path))).get();
-        } catch (InterruptedException e) {
-            throw new RuntimeException("Interrupted while waiting for response", e);
-        } catch (ExecutionException e) {
-            throw new RuntimeException("Read configuration data " + path + " failed", e);
-        }
-
-        CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME);
-        return (CompositeNode) findNode(data, path);
-    }
-
-    @Override
-    public Set<QName> getSupportedRpcs() {
-        return Collections.emptySet();
-    }
-
-    @Override
-    public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(QName rpc, CompositeNode input) {
-        return listener.sendRequest(toRpcMessage(rpc, input, getSchemaContext()), rpc);
+                });
     }
 
-    @Override
-    public Collection<ProviderFunctionality> getProviderFunctionality() {
-        return Collections.emptySet();
+    @VisibleForTesting
+    protected NetconfDevice(final RemoteDeviceId id, final RemoteDeviceHandler<NetconfSessionCapabilities> salFacade,
+            final ExecutorService processingExecutor, final MessageTransformer<NetconfMessage> messageTransformer,
+            final SchemaContextProviderFactory schemaContextProviderFactory,
+            final SchemaSourceProviderFactory<InputStream> sourceProviderFactory) {
+        this.id = id;
+        this.messageTransformer = messageTransformer;
+        this.salFacade = salFacade;
+        this.sourceProviderFactory = sourceProviderFactory;
+        this.processingExecutor = MoreExecutors.listeningDecorator(processingExecutor);
+        this.schemaContextProviderFactory = schemaContextProviderFactory;
     }
 
     @Override
-    public void onSessionInitiated(ProviderSession session) {
-        dataBroker = session.getService(DataBrokerService.class);
-
-        processingExecutor.submit(new Runnable() {
+    public void onRemoteSessionUp(final NetconfSessionCapabilities remoteSessionCapabilities,
+                                  final RemoteDeviceCommunicator<NetconfMessage> listener) {
+        // SchemaContext setup has to be performed in a dedicated thread since
+        // we are in a netty thread in this method
+        // Yang models are being downloaded in this method and it would cause a
+        // deadlock if we used the netty thread
+        // http://netty.io/wiki/thread-model.html
+        logger.debug("{}: Session to remote device established with {}", id, remoteSessionCapabilities);
+
+        final ListenableFuture<?> salInitializationFuture = processingExecutor.submit(new Runnable() {
             @Override
             public void run() {
-                updateInitialState();
+                final NetconfDeviceRpc deviceRpc = setUpDeviceRpc(remoteSessionCapabilities, listener);
+                final SchemaSourceProvider<InputStream> delegate = sourceProviderFactory.createSourceProvider(deviceRpc);
+                final SchemaContextProvider schemaContextProvider = setUpSchemaContext(delegate, remoteSessionCapabilities);
+                updateMessageTransformer(schemaContextProvider);
+                salFacade.onDeviceConnected(schemaContextProvider, remoteSessionCapabilities, deviceRpc);
             }
         });
 
-        mountService = session.getService(MountProvisionService.class);
-        if (mountService != null) {
-            mountInstance = mountService.createOrGetMountPoint(path);
-        }
-    }
-
-    private void updateInitialState() {
-        checkDataStoreState();
-
-        DataModificationTransaction transaction = dataBroker.beginTransaction();
-        if (operationalNodeNotExisting(transaction)) {
-            transaction.putOperationalData(path, getNodeWithId());
-        }
-        if (configurationNodeNotExisting(transaction)) {
-            transaction.putConfigurationData(path, getNodeWithId());
-        }
-
-        try {
-            transaction.commit().get();
-        } catch (InterruptedException e) {
-            throw new RuntimeException("Interrupted while waiting for response", e);
-        } catch (ExecutionException e) {
-            throw new RuntimeException("Read configuration data " + path + " failed", e);
-        }
-    }
-
-    private void checkDataStoreState() {
-        // read data from Nodes/Node in order to wait with write until schema for Nodes/Node is present in datastore
-        dataProviderService.readOperationalData(org.opendaylight.yangtools.yang.binding.InstanceIdentifier.builder(
-                Nodes.class).child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class).augmentation(NetconfNode.class).build());    }
-
-    CompositeNode getNodeWithId() {
-        SimpleNodeTOImpl id = new SimpleNodeTOImpl(INVENTORY_ID, null, name);
-        return new CompositeNodeTOImpl(INVENTORY_NODE, null, Collections.<Node<?>> singletonList(id));
-    }
-
-    boolean configurationNodeNotExisting(DataModificationTransaction transaction) {
-        return null == transaction.readConfigurationData(path);
-    }
-
-    boolean operationalNodeNotExisting(DataModificationTransaction transaction) {
-        return null == transaction.readOperationalData(path);
-    }
-
-    static Node<?> findNode(CompositeNode node, InstanceIdentifier identifier) {
-
-        Node<?> current = node;
-        for (InstanceIdentifier.PathArgument arg : identifier.getPath()) {
-            if (current instanceof SimpleNode<?>) {
-                return null;
-            } else if (current instanceof CompositeNode) {
-                CompositeNode currentComposite = (CompositeNode) current;
-
-                current = currentComposite.getFirstCompositeByName(arg.getNodeType());
-                if (current == null) {
-                    current = currentComposite.getFirstCompositeByName(arg.getNodeType().withoutRevision());
-                }
-                if (current == null) {
-                    current = currentComposite.getFirstSimpleByName(arg.getNodeType());
-                }
-                if (current == null) {
-                    current = currentComposite.getFirstSimpleByName(arg.getNodeType().withoutRevision());
-                }
-                if (current == null) {
-                    return null;
-                }
-            }
-        }
-        return current;
-    }
-
-    @Override
-    public DataCommitTransaction<InstanceIdentifier, CompositeNode> requestCommit(
-            DataModification<InstanceIdentifier, CompositeNode> modification) {
-        NetconfDeviceTwoPhaseCommitTransaction twoPhaseCommit = new NetconfDeviceTwoPhaseCommitTransaction(this,
-                modification, true, rollbackSupported);
-        try {
-            twoPhaseCommit.prepare();
-        } catch (InterruptedException e) {
-            throw new RuntimeException("Interrupted while waiting for response", e);
-        } catch (ExecutionException e) {
-            throw new RuntimeException("Read configuration data " + path + " failed", e);
-        }
-         return twoPhaseCommit;
-    }
-
-    Set<QName> getCapabilities(Collection<String> capabilities) {
-        return FluentIterable.from(capabilities).filter(new Predicate<String>() {
+        Futures.addCallback(salInitializationFuture, new FutureCallback<Object>() {
             @Override
-            public boolean apply(final String capability) {
-                return capability.contains("?") && capability.contains("module=") && capability.contains("revision=");
+            public void onSuccess(final Object result) {
+                logger.debug("{}: Initialization in sal successful", id);
+                logger.info("{}: Netconf connector initialized successfully", id);
             }
-        }).transform(new Function<String, QName>() {
-            @Override
-            public QName apply(final String capability) {
-                String[] parts = capability.split("\\?");
-                String namespace = parts[0];
-                FluentIterable<String> queryParams = FluentIterable.from(Arrays.asList(parts[1].split("&")));
-
-                String revision = getStringAndTransform(queryParams, "revision=", "revision=");
 
-                String moduleName = getStringAndTransform(queryParams, "module=", "module=");
-
-                if (revision == null) {
-                    logger.warn("Netconf device was not reporting revision correctly, trying to get amp;revision=");
-                    revision = getStringAndTransform(queryParams, "amp;revision==", "revision=");
-
-                    if (revision != null) {
-                        logger.warn("Netconf device returned revision incorectly escaped for {}", capability);
-                    }
-                }
-                if (revision == null) {
-                    return QName.create(URI.create(namespace), null, moduleName);
-                }
-                return QName.create(namespace, revision, moduleName);
-            }
-
-            private String getStringAndTransform(final Iterable<String> queryParams, final String match,
-                    final String substringToRemove) {
-                Optional<String> found = Iterables.tryFind(queryParams, new Predicate<String>() {
-                    @Override
-                    public boolean apply(final String input) {
-                        return input.startsWith(match);
-                    }
-                });
-
-                return found.isPresent() ? found.get().replaceAll(substringToRemove, "") : null;
+            @Override
+            public void onFailure(final Throwable t) {
+                // Unable to initialize device, set as disconnected
+                logger.error("{}: Initialization failed", id, t);
+                salFacade.onDeviceDisconnected();
             }
-
-        }).toSet();
-    }
-
-    @Override
-    public void close() {
-        bringDown();
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public InetSocketAddress getSocketAddress() {
-        return socketAddress;
-    }
-
-    public MountProvisionInstance getMountInstance() {
-        return mountInstance;
-    }
-
-    public void setReconnectStrategy(final ReconnectStrategy reconnectStrategy) {
-        this.reconnectStrategy = reconnectStrategy;
-    }
-
-    public void setProcessingExecutor(final ExecutorService processingExecutor) {
-        this.processingExecutor = processingExecutor;
-    }
-
-    public void setSocketAddress(final InetSocketAddress socketAddress) {
-        this.socketAddress = socketAddress;
-    }
-
-    public void setEventExecutor(final EventExecutor eventExecutor) {
-        this.eventExecutor = eventExecutor;
-    }
-
-    public void setSchemaSourceProvider(final AbstractCachingSchemaSourceProvider<String, InputStream> schemaSourceProvider) {
-        this.schemaSourceProvider = schemaSourceProvider;
-    }
-
-    public void setDispatcher(final NetconfClientDispatcher dispatcher) {
-        this.dispatcher = dispatcher;
+        });
     }
 
-    public void setClientConfig(final NetconfReconnectingClientConfiguration clientConfig) {
-        this.clientConfig = clientConfig;
+    /**
+     * Update initial message transformer to use retrieved schema
+     */
+    private void updateMessageTransformer(final SchemaContextProvider schemaContextProvider) {
+        messageTransformer.onGlobalContextUpdated(schemaContextProvider.getSchemaContext());
     }
 
-    public void setDataProviderService(final DataProviderService dataProviderService) {
-        this.dataProviderService = dataProviderService;
+    private SchemaContextProvider setUpSchemaContext(final SchemaSourceProvider<InputStream> sourceProvider, final NetconfSessionCapabilities capabilities) {
+        return schemaContextProviderFactory.createContextProvider(capabilities.getModuleBasedCaps(), sourceProvider);
     }
-}
-
-class NetconfDeviceSchemaContextProvider {
-
-    NetconfDevice device;
-
-    SchemaSourceProvider<InputStream> sourceProvider;
 
-    Optional<SchemaContext> currentContext;
-
-    NetconfDeviceSchemaContextProvider(NetconfDevice device, SchemaSourceProvider<InputStream> sourceProvider) {
-        this.device = device;
-        this.sourceProvider = sourceProvider;
-        this.currentContext = Optional.absent();
+    private NetconfDeviceRpc setUpDeviceRpc(final NetconfSessionCapabilities capHolder, final RemoteDeviceCommunicator<NetconfMessage> listener) {
+        Preconditions.checkArgument(capHolder.isMonitoringSupported(),
+                "%s: Netconf device does not support netconf monitoring, yang schemas cannot be acquired. Netconf device capabilities", capHolder);
+        return new NetconfDeviceRpc(listener, messageTransformer);
     }
 
-    void createContextFromCapabilities(Iterable<QName> capabilities) {
-        YangSourceContext sourceContext = YangSourceContext.createFrom(capabilities, sourceProvider);
-        if (!sourceContext.getMissingSources().isEmpty()) {
-            device.logger.warn("Sources for following models are missing {}", sourceContext.getMissingSources());
-        }
-        device.logger.debug("Trying to create schema context from {}", sourceContext.getValidSources());
-        List<InputStream> modelsToParse = YangSourceContext.getValidInputStreams(sourceContext);
-        if (!sourceContext.getValidSources().isEmpty()) {
-            SchemaContext schemaContext = tryToCreateContext(modelsToParse);
-            currentContext = Optional.fromNullable(schemaContext);
-        } else {
-            currentContext = Optional.absent();
-        }
-        if (currentContext.isPresent()) {
-            device.logger.debug("Schema context successfully created.");
-        }
+    @Override
+    public void onRemoteSessionDown() {
+        salFacade.onDeviceDisconnected();
     }
 
-    SchemaContext tryToCreateContext(List<InputStream> modelsToParse) {
-        YangParserImpl parser = new YangParserImpl();
-        try {
-
-            Set<Module> models = parser.parseYangModelsFromStreams(modelsToParse);
-            return parser.resolveSchemaContext(models);
-        } catch (Exception e) {
-            device.logger.debug("Error occured during parsing YANG schemas", e);
-            return null;
-        }
+    @Override
+    public void onNotification(final NetconfMessage notification) {
+        final CompositeNode parsedNotification = messageTransformer.toNotification(notification);
+        salFacade.onNotification(parsedNotification);
     }
 }
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceListener.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceListener.java
deleted file mode 100644 (file)
index 68667f0..0000000
+++ /dev/null
@@ -1,237 +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.sal.connect.netconf;
-
-import com.google.common.collect.Sets;
-import io.netty.util.concurrent.Future;
-import io.netty.util.concurrent.FutureListener;
-
-import java.util.ArrayDeque;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.Queue;
-import java.util.Set;
-
-import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
-import org.opendaylight.controller.netconf.api.NetconfMessage;
-import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
-import org.opendaylight.controller.netconf.client.NetconfClientSession;
-import org.opendaylight.controller.netconf.client.NetconfClientSessionListener;
-import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
-import org.opendaylight.controller.netconf.util.xml.XmlUtil;
-import org.opendaylight.controller.sal.common.util.Rpcs;
-import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcError;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
-import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProviders;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Preconditions;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-
-public class NetconfDeviceListener implements NetconfClientSessionListener {
-    private static final class Request {
-        final UncancellableFuture<RpcResult<CompositeNode>> future;
-        final NetconfMessage request;
-        final QName rpc;
-
-        private Request(UncancellableFuture<RpcResult<CompositeNode>> future, NetconfMessage request, final QName rpc) {
-            this.future = future;
-            this.request = request;
-            this.rpc = rpc;
-        }
-    }
-
-    private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceListener.class);
-    private final Queue<Request> requests = new ArrayDeque<>();
-    private final NetconfDevice device;
-    private NetconfClientSession session;
-
-    public NetconfDeviceListener(final NetconfDevice device) {
-        this.device = Preconditions.checkNotNull(device);
-    }
-
-    @Override
-    public synchronized void onSessionUp(final NetconfClientSession session) {
-        LOG.debug("Session with {} established as address {} session-id {}",
-                device.getName(), device.getSocketAddress(), session.getSessionId());
-
-        this.session = session;
-
-        final Set<QName> caps = device.getCapabilities(session.getServerCapabilities());
-        LOG.trace("Server {} advertized capabilities {}", device.getName(), caps);
-
-        // Select the appropriate provider
-        final SchemaSourceProvider<String> delegate;
-        if (NetconfRemoteSchemaSourceProvider.isSupportedFor(caps)) {
-            delegate = new NetconfRemoteSchemaSourceProvider(device);
-            // FIXME caps do not contain urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring, since it is filtered out in getCapabilitites
-        } else if(session.getServerCapabilities().contains(NetconfRemoteSchemaSourceProvider.IETF_NETCONF_MONITORING.getNamespace().toString())) {
-            delegate = new NetconfRemoteSchemaSourceProvider(device);
-        } else {
-            LOG.info("Netconf server {} does not support IETF Netconf Monitoring", device.getName());
-            delegate = SchemaSourceProviders.noopProvider();
-        }
-
-        device.bringUp(delegate, caps, isRollbackSupported(session.getServerCapabilities()));
-
-    }
-
-    private static boolean isRollbackSupported(final Collection<String> serverCapabilities) {
-        // TODO rollback capability cannot be searched for in Set<QName> caps
-        // since this set does not contain module-less capabilities
-        return Sets.newHashSet(serverCapabilities).contains(NetconfMapping.NETCONF_ROLLBACK_ON_ERROR_URI.toString());
-    }
-
-    private synchronized void tearDown(final Exception e) {
-        session = null;
-
-        /*
-         * Walk all requests, check if they have been executing
-         * or cancelled and remove them from the queue.
-         */
-        final Iterator<Request> it = requests.iterator();
-        while (it.hasNext()) {
-            final Request r = it.next();
-            if (r.future.isUncancellable()) {
-                // FIXME: add a RpcResult instead?
-                r.future.setException(e);
-                it.remove();
-            } else if (r.future.isCancelled()) {
-                // This just does some house-cleaning
-                it.remove();
-            }
-        }
-
-        device.bringDown();
-    }
-
-    @Override
-    public void onSessionDown(final NetconfClientSession session, final Exception e) {
-        LOG.debug("Session with {} went down", device.getName(), e);
-        tearDown(e);
-    }
-
-    @Override
-    public void onSessionTerminated(final NetconfClientSession session, final NetconfTerminationReason reason) {
-        LOG.debug("Session with {} terminated {}", session, reason);
-        tearDown(new RuntimeException(reason.getErrorMessage()));
-    }
-
-    @Override
-    public void onMessage(final NetconfClientSession session, final NetconfMessage message) {
-        /*
-         * Dispatch between notifications and messages. Messages need to be processed
-         * with lock held, notifications do not.
-         */
-        if (isNotification(message)) {
-            processNotification(message);
-        } else {
-            processMessage(message);
-        }
-    }
-
-    private synchronized void processMessage(final NetconfMessage message) {
-        final Request r = requests.peek();
-        if (r.future.isUncancellable()) {
-            requests.poll();
-            LOG.debug("Matched {} to {}", r.request, message);
-
-            try {
-                NetconfMapping.checkValidReply(r.request, message);
-            } catch (IllegalStateException e) {
-                LOG.warn("Invalid request-reply match, reply message contains different message-id", e);
-                r.future.setException(e);
-                return;
-            }
-
-            try {
-                NetconfMapping.checkSuccessReply(message);
-            } catch (NetconfDocumentedException | IllegalStateException e) {
-                LOG.warn("Error reply from remote device", e);
-                r.future.setException(e);
-                return;
-            }
-
-            r.future.set(NetconfMapping.toRpcResult(message, r.rpc, device.getSchemaContext()));
-        } else {
-            LOG.warn("Ignoring unsolicited message", message);
-        }
-    }
-
-    synchronized ListenableFuture<RpcResult<CompositeNode>> sendRequest(final NetconfMessage message, final QName rpc) {
-        if (session == null) {
-            LOG.debug("Session to {} is disconnected, failing RPC request {}", device.getName(), message);
-            return Futures.<RpcResult<CompositeNode>>immediateFuture(new RpcResult<CompositeNode>() {
-                @Override
-                public boolean isSuccessful() {
-                    return false;
-                }
-
-                @Override
-                public CompositeNode getResult() {
-                    return null;
-                }
-
-                @Override
-                public Collection<RpcError> getErrors() {
-                    // FIXME: indicate that the session is down
-                    return Collections.emptySet();
-                }
-            });
-        }
-
-        final Request req = new Request(new UncancellableFuture<RpcResult<CompositeNode>>(true), message, rpc);
-        requests.add(req);
-
-        session.sendMessage(req.request).addListener(new FutureListener<Void>() {
-            @Override
-            public void operationComplete(final Future<Void> future) throws Exception {
-                if (!future.isSuccess()) {
-                    // We expect that a session down will occur at this point
-                    LOG.debug("Failed to send request {}", XmlUtil.toString(req.request.getDocument()), future.cause());
-                    req.future.setException(future.cause());
-                } else {
-                    LOG.trace("Finished sending request {}", req.request);
-                }
-            }
-        });
-
-        return req.future;
-    }
-
-    /**
-     * Process an incoming notification.
-     *
-     * @param notification Notification message
-     */
-    private void processNotification(final NetconfMessage notification) {
-        this.device.logger.debug("Received NETCONF notification.", notification);
-        CompositeNode domNotification = NetconfMapping.toNotificationNode(notification, device.getSchemaContext());
-        if (domNotification == null) {
-            return;
-        }
-
-        MountProvisionInstance mountInstance =  this.device.getMountInstance();
-        if (mountInstance != null) {
-            mountInstance.publish(domNotification);
-        }
-    }
-
-    private static boolean isNotification(final NetconfMessage message) {
-        final XmlElement xmle = XmlElement.fromDomDocument(message.getDocument());
-        return XmlNetconfConstants.NOTIFICATION_ELEMENT_NAME.equals(xmle.getName()) ;
-    }
-}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTwoPhaseCommitTransaction.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTwoPhaseCommitTransaction.java
deleted file mode 100644 (file)
index 34cd9aa..0000000
+++ /dev/null
@@ -1,204 +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.sal.connect.netconf;
-
-import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_CANDIDATE_QNAME;
-import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_COMMIT_QNAME;
-import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_CONFIG_QNAME;
-import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_EDIT_CONFIG_QNAME;
-import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_ERROR_OPTION_QNAME;
-import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_OPERATION_QNAME;
-import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_RUNNING_QNAME;
-import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_TARGET_QNAME;
-import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.ROLLBACK_ON_ERROR_OPTION;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.concurrent.ExecutionException;
-
-import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction;
-import org.opendaylight.controller.md.sal.common.api.data.DataModification;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcError;
-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.NodeIdentifierWithPredicates;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.data.api.Node;
-import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
-import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
-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.ImmutableList;
-import com.google.common.collect.Lists;
-
-class NetconfDeviceTwoPhaseCommitTransaction implements DataCommitTransaction<InstanceIdentifier, CompositeNode> {
-    private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceTwoPhaseCommitTransaction.class);
-    private final DataModification<InstanceIdentifier, CompositeNode> modification;
-    private final NetconfDevice device;
-    private final boolean candidateSupported;
-    private final boolean rollbackSupported;
-
-    public NetconfDeviceTwoPhaseCommitTransaction(final NetconfDevice device,
-            final DataModification<InstanceIdentifier, CompositeNode> modification,
-            final boolean candidateSupported, final boolean rollbackOnErrorSupported) {
-        this.device = Preconditions.checkNotNull(device);
-        this.modification = Preconditions.checkNotNull(modification);
-        this.candidateSupported = candidateSupported;
-        this.rollbackSupported = rollbackOnErrorSupported;
-    }
-
-    void prepare() throws InterruptedException, ExecutionException {
-        for (InstanceIdentifier toRemove : modification.getRemovedConfigurationData()) {
-            sendDelete(toRemove);
-        }
-        for(Entry<InstanceIdentifier, CompositeNode> toUpdate : modification.getUpdatedConfigurationData().entrySet()) {
-            sendMerge(toUpdate.getKey(),toUpdate.getValue());
-        }
-    }
-
-    private void sendMerge(final InstanceIdentifier key, final CompositeNode value) throws InterruptedException, ExecutionException {
-        sendEditRpc(createEditStructure(key, Optional.<String>absent(), Optional.of(value)));
-    }
-
-    private void sendDelete(final InstanceIdentifier toDelete) throws InterruptedException, ExecutionException {
-        sendEditRpc(createEditStructure(toDelete, Optional.of("delete"), Optional.<CompositeNode> absent()));
-    }
-
-    private void sendEditRpc(final CompositeNode editStructure) throws InterruptedException, ExecutionException {
-        CompositeNodeBuilder<ImmutableCompositeNode> builder = configurationRpcBuilder();
-        builder.setQName(NETCONF_EDIT_CONFIG_QNAME);
-        builder.add(editStructure);
-
-        RpcResult<CompositeNode> rpcResult = device.invokeRpc(NETCONF_EDIT_CONFIG_QNAME, builder.toInstance()).get();
-        Preconditions.checkState(rpcResult.isSuccessful(),"Rpc Result was unsuccessful");
-    }
-
-    private CompositeNodeBuilder<ImmutableCompositeNode> configurationRpcBuilder() {
-        CompositeNodeBuilder<ImmutableCompositeNode> ret = ImmutableCompositeNode.builder();
-
-        Node<?> targetNode;
-        if(candidateSupported) {
-            targetNode = ImmutableCompositeNode.create(NETCONF_CANDIDATE_QNAME, ImmutableList.<Node<?>>of());
-        } else {
-            targetNode = ImmutableCompositeNode.create(NETCONF_RUNNING_QNAME, ImmutableList.<Node<?>>of());
-        }
-
-        Node<?> targetWrapperNode = ImmutableCompositeNode.create(NETCONF_TARGET_QNAME, ImmutableList.<Node<?>>of(targetNode));
-
-        if(rollbackSupported) {
-            LOG.debug("Rollback-on-error supported, setting {} to {}", NETCONF_ERROR_OPTION_QNAME, ROLLBACK_ON_ERROR_OPTION);
-            ret.addLeaf(NETCONF_ERROR_OPTION_QNAME, ROLLBACK_ON_ERROR_OPTION);
-        }
-
-        ret.add(targetWrapperNode);
-        return ret;
-    }
-
-    private CompositeNode createEditStructure(final InstanceIdentifier dataPath, final Optional<String> operation,
-            final Optional<CompositeNode> lastChildOverride) {
-        List<PathArgument> path = dataPath.getPath();
-        List<PathArgument> reversed = Lists.reverse(path);
-        CompositeNode previous = null;
-        boolean isLast = true;
-        for (PathArgument arg : reversed) {
-            CompositeNodeBuilder<ImmutableCompositeNode> builder = ImmutableCompositeNode.builder();
-            builder.setQName(arg.getNodeType());
-            Map<QName, Object> predicates = Collections.emptyMap();
-            if (arg instanceof NodeIdentifierWithPredicates) {
-                predicates = ((NodeIdentifierWithPredicates) arg).getKeyValues();
-            }
-            for (Entry<QName, Object> entry : predicates.entrySet()) {
-                builder.addLeaf(entry.getKey(), entry.getValue());
-            }
-
-            if (isLast) {
-                if (operation.isPresent()) {
-                    builder.setAttribute(NETCONF_OPERATION_QNAME, operation.get());
-                }
-                if (lastChildOverride.isPresent()) {
-                    List<Node<?>> children = lastChildOverride.get().getValue();
-                    for(Node<?> child : children) {
-                        if(!predicates.containsKey(child.getKey())) {
-                            builder.add(child);
-                        }
-                    }
-
-                }
-            } else {
-                builder.add(previous);
-            }
-            previous = builder.toInstance();
-            isLast = false;
-        }
-        return ImmutableCompositeNode.create(NETCONF_CONFIG_QNAME, ImmutableList.<Node<?>>of(previous));
-    }
-
-    @Override
-    public RpcResult<Void> finish() {
-        CompositeNodeBuilder<ImmutableCompositeNode> commitInput = ImmutableCompositeNode.builder();
-        commitInput.setQName(NETCONF_COMMIT_QNAME);
-        try {
-            final RpcResult<?> rpcResult = device.invokeRpc(NetconfMapping.NETCONF_COMMIT_QNAME, commitInput.toInstance()).get();
-            return new RpcResult<Void>() {
-
-                @Override
-                public boolean isSuccessful() {
-                    return rpcResult.isSuccessful();
-                }
-
-                @Override
-                public Void getResult() {
-                    return null;
-                }
-
-                @Override
-                public Collection<RpcError> getErrors() {
-                    return rpcResult.getErrors();
-                }
-            };
-        } catch (final InterruptedException | ExecutionException e) {
-            LOG.warn("Failed to finish operation", e);
-            return new RpcResult<Void>() {
-                @Override
-                public boolean isSuccessful() {
-                    return false;
-                }
-
-                @Override
-                public Void getResult() {
-                    return null;
-                }
-
-                @Override
-                public Collection<RpcError> getErrors() {
-                    // FIXME: wrap the exception
-                    return Collections.emptySet();
-                }
-            };
-        }
-    }
-
-    @Override
-    public DataModification<InstanceIdentifier, CompositeNode> getModification() {
-        return this.modification;
-    }
-
-    @Override
-    public RpcResult<Void> rollback() throws IllegalStateException {
-        // TODO Auto-generated method stub
-        return null;
-    }
-}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfInventoryUtils.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfInventoryUtils.java
deleted file mode 100644 (file)
index b68f18f..0000000
+++ /dev/null
@@ -1,30 +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.sal.connect.netconf;
-
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-
-public class NetconfInventoryUtils {
-    public static final QName NETCONF_MOUNT = null;
-    public static final QName NETCONF_ENDPOINT = null;
-    public static final QName NETCONF_ENDPOINT_ADDRESS = null;
-    public static final QName NETCONF_ENDPOINT_PORT = null;
-
-    private NetconfInventoryUtils() {
-        throw new UnsupportedOperationException("Utility class cannot be instantiated");
-    }
-
-    public static String getEndpointAddress(CompositeNode node) {
-        return node.getCompositesByName(NETCONF_ENDPOINT).get(0).getFirstSimpleByName(NETCONF_ENDPOINT_ADDRESS).getValue().toString();
-    }
-
-    public static String getEndpointPort(CompositeNode node) {
-        return node.getCompositesByName(NETCONF_ENDPOINT).get(0).getFirstSimpleByName(NETCONF_ENDPOINT_PORT).getValue().toString();
-    }
-}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfRemoteSchemaSourceProvider.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfRemoteSchemaSourceProvider.java
deleted file mode 100644 (file)
index 31c6bd0..0000000
+++ /dev/null
@@ -1,84 +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.sal.connect.netconf;
-
-import java.util.Collection;
-import java.util.concurrent.ExecutionException;
-
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.SimpleNode;
-import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
-import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
-import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
-
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-class NetconfRemoteSchemaSourceProvider implements SchemaSourceProvider<String> {
-
-    public static final QName IETF_NETCONF_MONITORING = QName.create(
-            "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", "2010-10-04", "ietf-netconf-monitoring");
-    public static final QName GET_SCHEMA_QNAME = QName.create(IETF_NETCONF_MONITORING, "get-schema");
-    public static final QName GET_DATA_QNAME = QName.create(IETF_NETCONF_MONITORING, "data");
-
-    private final NetconfDevice device;
-
-    private final Logger logger;
-
-    public NetconfRemoteSchemaSourceProvider(NetconfDevice device) {
-        this.device = Preconditions.checkNotNull(device);
-        logger = LoggerFactory.getLogger(NetconfDevice.class + "#" + device.getName());
-    }
-
-    @Override
-    public Optional<String> getSchemaSource(String moduleName, Optional<String> revision) {
-        CompositeNodeBuilder<ImmutableCompositeNode> request = ImmutableCompositeNode.builder(); //
-        request.setQName(GET_SCHEMA_QNAME) //
-                .addLeaf("format", "yang") //
-                .addLeaf("identifier", moduleName); //
-        if (revision.isPresent()) {
-            request.addLeaf("version", revision.get());
-        }
-
-        logger.trace("Loading YANG schema source for {}:{}", moduleName, revision);
-        try {
-            RpcResult<CompositeNode> schemaReply = device.invokeRpc(GET_SCHEMA_QNAME, request.toInstance()).get();
-            if (schemaReply.isSuccessful()) {
-                String schemaBody = getSchemaFromRpc(schemaReply.getResult());
-                if (schemaBody != null) {
-                    device.logger.trace("YANG Schema successfully retrieved from remote for {}:{}", moduleName, revision);
-                    return Optional.of(schemaBody);
-                }
-            }
-            logger.warn("YANG shcema was not successfully retrieved. Errors: {}", schemaReply.getErrors());
-        } catch (InterruptedException | ExecutionException e) {
-            logger.warn("YANG shcema was not successfully retrieved.", e);
-        }
-        return Optional.absent();
-    }
-
-    private String getSchemaFromRpc(CompositeNode result) {
-        if (result == null) {
-            return null;
-        }
-        SimpleNode<?> simpleNode = result.getFirstSimpleByName(GET_DATA_QNAME.withoutRevision());
-        Object potential = simpleNode.getValue();
-        if (potential instanceof String) {
-            return (String) potential;
-        }
-        return null;
-    }
-
-    public static final boolean isSupportedFor(Collection<QName> capabilities) {
-        return capabilities.contains(IETF_NETCONF_MONITORING);
-    }
-}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java
new file mode 100644 (file)
index 0000000..fa6e252
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse 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.connect.netconf.listener;
+
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Queue;
+
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
+import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.controller.netconf.client.NetconfClientSession;
+import org.opendaylight.controller.netconf.client.NetconfClientSessionListener;
+import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration;
+import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfiguration;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.controller.sal.common.util.RpcErrors;
+import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.controller.sal.connect.api.RemoteDevice;
+import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator;
+import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.controller.sal.connect.util.FailedRpcResult;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.FutureListener;
+
+public class NetconfDeviceCommunicator implements NetconfClientSessionListener, RemoteDeviceCommunicator<NetconfMessage> {
+
+    private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceCommunicator.class);
+
+    private static final RpcResult<NetconfMessage> FAILED_RPC_RESULT = new FailedRpcResult<>(RpcErrors.getRpcError(
+            null, null, null, RpcError.ErrorSeverity.ERROR, "Netconf session disconnected",
+            RpcError.ErrorType.PROTOCOL, null));
+
+    private final RemoteDevice<NetconfSessionCapabilities, NetconfMessage> remoteDevice;
+    private final RemoteDeviceId id;
+
+    public NetconfDeviceCommunicator(final RemoteDeviceId id,
+            final RemoteDevice<NetconfSessionCapabilities, NetconfMessage> remoteDevice) {
+        this.id = id;
+        this.remoteDevice = remoteDevice;
+    }
+
+    private final Queue<Request> requests = new ArrayDeque<>();
+    private NetconfClientSession session;
+
+    @Override
+    public synchronized void onSessionUp(final NetconfClientSession session) {
+        logger.debug("{}: Session established", id);
+        this.session = session;
+
+        final NetconfSessionCapabilities netconfSessionCapabilities = NetconfSessionCapabilities.fromNetconfSession(session);
+        logger.trace("{}: Session advertised capabilities: {}", id, netconfSessionCapabilities);
+
+        remoteDevice.onRemoteSessionUp(netconfSessionCapabilities, this);
+    }
+
+    public void initializeRemoteConnection(final NetconfClientDispatcher dispatch,
+                                           final NetconfReconnectingClientConfiguration config) {
+        dispatch.createReconnectingClient(config);
+    }
+
+    private synchronized void tearDown(final Exception e) {
+        remoteDevice.onRemoteSessionDown();
+        session = null;
+
+        /*
+         * Walk all requests, check if they have been executing
+         * or cancelled and remove them from the queue.
+         */
+        final Iterator<Request> it = requests.iterator();
+        while (it.hasNext()) {
+            final Request r = it.next();
+            if (r.future.isUncancellable()) {
+                r.future.setException(e);
+                it.remove();
+            } else if (r.future.isCancelled()) {
+                // This just does some house-cleaning
+                it.remove();
+            }
+        }
+    }
+
+    @Override
+    public void onSessionDown(final NetconfClientSession session, final Exception e) {
+        logger.warn("{}: Session went down", id, e);
+        tearDown(e);
+    }
+
+    @Override
+    public void onSessionTerminated(final NetconfClientSession session, final NetconfTerminationReason reason) {
+        logger.warn("{}: Session terminated {}", id, reason);
+        tearDown(new RuntimeException(reason.getErrorMessage()));
+    }
+
+    @Override
+    public void onMessage(final NetconfClientSession session, final NetconfMessage message) {
+        /*
+         * Dispatch between notifications and messages. Messages need to be processed
+         * with lock held, notifications do not.
+         */
+        if (isNotification(message)) {
+            processNotification(message);
+        } else {
+            processMessage(message);
+        }
+    }
+
+    private synchronized void processMessage(final NetconfMessage message) {
+        final Request r = requests.peek();
+        if (r.future.isUncancellable()) {
+            requests.poll();
+
+            logger.debug("{}: Message received {}", id, message);
+
+            if(logger.isTraceEnabled()) {
+                logger.trace("{}: Matched request: {} to response: {}", id, msgToS(r.request), msgToS(message));
+            }
+
+            try {
+                NetconfMessageTransformUtil.checkValidReply(r.request, message);
+            } catch (final IllegalStateException e) {
+                logger.warn("{}: Invalid request-reply match, reply message contains different message-id, request: {}, response: {}", id,
+                        msgToS(r.request), msgToS(message), e);
+                r.future.setException(e);
+                return;
+            }
+
+            try {
+                NetconfMessageTransformUtil.checkSuccessReply(message);
+            } catch (NetconfDocumentedException | IllegalStateException e) {
+                logger.warn("{}: Error reply from remote device, request: {}, response: {}", id,
+                        msgToS(r.request), msgToS(message), e);
+                r.future.setException(e);
+                return;
+            }
+
+            r.future.set(Rpcs.getRpcResult(true, message, Collections.<RpcError>emptySet()));
+        } else {
+            logger.warn("{}: Ignoring unsolicited message {}", id, msgToS(message));
+        }
+    }
+
+    @Override
+    public void close() {
+        tearDown(new RuntimeException("Closed"));
+    }
+
+    private static String msgToS(final NetconfMessage msg) {
+        return XmlUtil.toString(msg.getDocument());
+    }
+
+    @Override
+    public synchronized ListenableFuture<RpcResult<NetconfMessage>> sendRequest(final NetconfMessage message, final QName rpc) {
+        if(logger.isTraceEnabled()) {
+            logger.trace("{}: Sending message {}", id, msgToS(message));
+        }
+
+        if (session == null) {
+            logger.warn("{}: Session is disconnected, failing RPC request {}", id, message);
+            return Futures.immediateFuture(FAILED_RPC_RESULT);
+        }
+
+        final Request req = new Request(new UncancellableFuture<RpcResult<NetconfMessage>>(true), message, rpc);
+        requests.add(req);
+
+        session.sendMessage(req.request).addListener(new FutureListener<Void>() {
+            @Override
+            public void operationComplete(final Future<Void> future) throws Exception {
+                if (!future.isSuccess()) {
+                    // We expect that a session down will occur at this point
+                    logger.debug("{}: Failed to send request {}", id, XmlUtil.toString(req.request.getDocument()), future.cause());
+                    req.future.setException(future.cause());
+                } else {
+                    logger.trace("{}: Finished sending request {}", id, req.request);
+                }
+            }
+        });
+
+        return req.future;
+    }
+
+    private void processNotification(final NetconfMessage notification) {
+        logger.debug("{}: Notification received: {}", id, notification);
+
+        if(logger.isTraceEnabled()) {
+            logger.trace("{}: Notification received: {}", id, msgToS(notification));
+        }
+
+        remoteDevice.onNotification(notification);
+    }
+
+    private static boolean isNotification(final NetconfMessage message) {
+        final XmlElement xmle = XmlElement.fromDomDocument(message.getDocument());
+        return XmlNetconfConstants.NOTIFICATION_ELEMENT_NAME.equals(xmle.getName()) ;
+    }
+
+    private static final class Request {
+        final UncancellableFuture<RpcResult<NetconfMessage>> future;
+        final NetconfMessage request;
+        final QName rpc;
+
+        private Request(final UncancellableFuture<RpcResult<NetconfMessage>> future, final NetconfMessage request, final QName rpc) {
+            this.future = future;
+            this.request = request;
+            this.rpc = rpc;
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java
new file mode 100644 (file)
index 0000000..82903ea
--- /dev/null
@@ -0,0 +1,111 @@
+package org.opendaylight.controller.sal.connect.netconf.listener;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Set;
+
+import org.opendaylight.controller.netconf.client.NetconfClientSession;
+import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+
+public final class NetconfSessionCapabilities {
+
+    private static final Logger logger = LoggerFactory.getLogger(NetconfSessionCapabilities.class);
+
+    private final Set<String> capabilities;
+
+    private final Set<QName> moduleBasedCaps;
+
+    private NetconfSessionCapabilities(final Set<String> capabilities, final Set<QName> moduleBasedCaps) {
+        this.capabilities = capabilities;
+        this.moduleBasedCaps = moduleBasedCaps;
+    }
+
+    public Set<QName> getModuleBasedCaps() {
+        return moduleBasedCaps;
+    }
+
+    public boolean containsCapability(final String capability) {
+        return capabilities.contains(capability);
+    }
+
+    public boolean containsCapability(final QName capability) {
+        return moduleBasedCaps.contains(capability);
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this)
+                .add("capabilities", capabilities)
+                .add("rollback", isRollbackSupported())
+                .add("monitoring", isMonitoringSupported())
+                .toString();
+    }
+
+    public boolean isRollbackSupported() {
+        return containsCapability(NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString());
+    }
+
+    public boolean isMonitoringSupported() {
+        return containsCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING)
+                || containsCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString());
+    }
+
+    public static NetconfSessionCapabilities fromNetconfSession(final NetconfClientSession session) {
+        return fromStrings(session.getServerCapabilities());
+    }
+
+    public static NetconfSessionCapabilities fromStrings(final Collection<String> capabilities) {
+        final Set<QName> moduleBasedCaps = Sets.newHashSet();
+
+        for (final String capability : capabilities) {
+            if(isModuleBasedCapability(capability)) {
+                final String[] parts = capability.split("\\?");
+                final String namespace = parts[0];
+                final FluentIterable<String> queryParams = FluentIterable.from(Arrays.asList(parts[1].split("&")));
+
+                String revision = getStringAndTransform(queryParams, "revision=", "revision=");
+
+                final String moduleName = getStringAndTransform(queryParams, "module=", "module=");
+
+                if (revision == null) {
+                    logger.debug("Netconf device was not reporting revision correctly, trying to get amp;revision=");
+                    revision = getStringAndTransform(queryParams, "amp;revision=", "amp;revision=");
+
+                    if (revision == null) {
+                        logger.warn("Netconf device returned revision incorrectly escaped for {}", capability);
+                    }
+                }
+                moduleBasedCaps.add(QName.create(namespace, revision, moduleName));
+            }
+        }
+
+        return new NetconfSessionCapabilities(Sets.newHashSet(capabilities), moduleBasedCaps);
+    }
+
+    private static boolean isModuleBasedCapability(final String capability) {
+        return capability.contains("?") && capability.contains("module=") && capability.contains("revision=");
+    }
+
+    private static String getStringAndTransform(final Iterable<String> queryParams, final String match,
+                                                final String substringToRemove) {
+        final Optional<String> found = Iterables.tryFind(queryParams, new Predicate<String>() {
+            @Override
+            public boolean apply(final String input) {
+                return input.startsWith(match);
+            }
+        });
+
+        return found.isPresent() ? found.get().replaceAll(substringToRemove, "") : null;
+    }
+
+}
@@ -5,7 +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.sal.connect.netconf;
+package org.opendaylight.controller.sal.connect.netconf.listener;
 
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.GuardedBy;
@@ -17,7 +17,7 @@ final class UncancellableFuture<V> extends AbstractFuture<V> {
     @GuardedBy("this")
     private boolean uncancellable = false;
 
-    public UncancellableFuture(boolean uncancellable) {
+    public UncancellableFuture(final boolean uncancellable) {
         this.uncancellable = uncancellable;
     }
 
@@ -35,23 +35,19 @@ final class UncancellableFuture<V> extends AbstractFuture<V> {
     }
 
     @Override
-    public synchronized boolean cancel(boolean mayInterruptIfRunning) {
-        if (uncancellable) {
-            return false;
-        }
-
-        return super.cancel(mayInterruptIfRunning);
+    public synchronized boolean cancel(final boolean mayInterruptIfRunning) {
+        return uncancellable ? false : super.cancel(mayInterruptIfRunning);
     }
 
     @Override
-    public synchronized boolean set(@Nullable V value) {
-        Preconditions.checkState(uncancellable == true);
+    public synchronized boolean set(@Nullable final V value) {
+        Preconditions.checkState(uncancellable);
         return super.set(value);
     }
 
     @Override
-    protected boolean setException(Throwable throwable) {
-        Preconditions.checkState(uncancellable == true);
+    protected boolean setException(final Throwable throwable) {
+        Preconditions.checkState(uncancellable);
         return super.setException(throwable);
     }
 }
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/package-info.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/package-info.java
new file mode 100644 (file)
index 0000000..4a95582
--- /dev/null
@@ -0,0 +1,4 @@
+/**
+ * Implementation of netconf southbound connector
+ */
+package org.opendaylight.controller.sal.connect.netconf;
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceCommitHandler.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceCommitHandler.java
new file mode 100644 (file)
index 0000000..457b8c3
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse 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.connect.netconf.sal;
+
+import java.util.concurrent.ExecutionException;
+
+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.RpcErrors;
+import org.opendaylight.controller.sal.connect.util.FailedRpcResult;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.yangtools.yang.common.RpcError;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class NetconfDeviceCommitHandler implements DataCommitHandler<InstanceIdentifier,CompositeNode> {
+
+    private static final Logger logger= LoggerFactory.getLogger(NetconfDeviceCommitHandler.class);
+
+    private final RemoteDeviceId id;
+    private final RpcImplementation rpc;
+    private final boolean rollbackSupported;
+
+    public NetconfDeviceCommitHandler(final RemoteDeviceId id, final RpcImplementation rpc, final boolean rollbackSupported) {
+        this.id = id;
+        this.rpc = rpc;
+        this.rollbackSupported = rollbackSupported;
+    }
+
+    @Override
+    public DataCommitTransaction<InstanceIdentifier, CompositeNode> requestCommit(
+            final DataModification<InstanceIdentifier, CompositeNode> modification) {
+
+        final NetconfDeviceTwoPhaseCommitTransaction twoPhaseCommit = new NetconfDeviceTwoPhaseCommitTransaction(id, rpc,
+                modification, true, rollbackSupported);
+        try {
+            twoPhaseCommit.prepare();
+        } catch (final InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new RuntimeException(id + ": Interrupted while waiting for response", e);
+        } catch (final ExecutionException e) {
+            logger.warn("%s: Error executing pre commit operation on remote device", id, e);
+            return new FailingTransaction(twoPhaseCommit, e);
+        }
+
+        return twoPhaseCommit;
+    }
+
+    /**
+     * Always fail commit transaction that rolls back delegate transaction afterwards
+     */
+    private class FailingTransaction implements DataCommitTransaction<InstanceIdentifier, CompositeNode> {
+        private final NetconfDeviceTwoPhaseCommitTransaction twoPhaseCommit;
+        private final ExecutionException e;
+
+        public FailingTransaction(final NetconfDeviceTwoPhaseCommitTransaction twoPhaseCommit, final ExecutionException e) {
+            this.twoPhaseCommit = twoPhaseCommit;
+            this.e = e;
+        }
+
+        @Override
+        public DataModification<InstanceIdentifier, CompositeNode> getModification() {
+            return twoPhaseCommit.getModification();
+        }
+
+        @Override
+        public RpcResult<Void> finish() throws IllegalStateException {
+            return new FailedRpcResult<>(RpcErrors.getRpcError(null, null, null, RpcError.ErrorSeverity.ERROR,
+                    id + ": Unexpected operation error during pre-commit operations", RpcError.ErrorType.APPLICATION, e));
+        }
+
+        @Override
+        public RpcResult<Void> rollback() throws IllegalStateException {
+            return twoPhaseCommit.rollback();
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataReader.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataReader.java
new file mode 100644 (file)
index 0000000..2909bac
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse 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.connect.netconf.sal;
+
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.CONFIG_SOURCE_RUNNING;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DATA_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toFilterStructure;
+
+import java.util.concurrent.ExecutionException;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+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.Node;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+
+public final class NetconfDeviceDataReader implements DataReader<InstanceIdentifier,CompositeNode> {
+
+    private final RpcImplementation rpc;
+    private final RemoteDeviceId id;
+
+    public NetconfDeviceDataReader(final RemoteDeviceId id, final RpcImplementation rpc) {
+        this.id = id;
+        this.rpc = rpc;
+    }
+
+    @Override
+    public CompositeNode readConfigurationData(final InstanceIdentifier path) {
+        final RpcResult<CompositeNode> result;
+        try {
+            result = rpc.invokeRpc(NETCONF_GET_CONFIG_QNAME,
+                    NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_QNAME, CONFIG_SOURCE_RUNNING, toFilterStructure(path))).get();
+        } catch (final InterruptedException e) {
+            throw onInterruptedException(e);
+        } catch (final ExecutionException e) {
+            throw new RuntimeException(id + ": Read configuration data " + path + " failed", e);
+        }
+
+        final CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME);
+        return data == null ? null : (CompositeNode) findNode(data, path);
+    }
+
+    private RuntimeException onInterruptedException(final InterruptedException e) {
+        Thread.currentThread().interrupt();
+        return new RuntimeException(id + ": Interrupted while waiting for response", e);
+    }
+
+    @Override
+    public CompositeNode readOperationalData(final InstanceIdentifier path) {
+        final RpcResult<CompositeNode> result;
+        try {
+            result = rpc.invokeRpc(NETCONF_GET_QNAME, NetconfMessageTransformUtil.wrap(NETCONF_GET_QNAME, toFilterStructure(path))).get();
+        } catch (final InterruptedException e) {
+            throw onInterruptedException(e);
+        } catch (final ExecutionException e) {
+            throw new RuntimeException(id + ": Read operational data " + path + " failed", e);
+        }
+
+        final CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME);
+        return (CompositeNode) findNode(data, path);
+    }
+
+    private static Node<?> findNode(final CompositeNode node, final InstanceIdentifier identifier) {
+
+        Node<?> current = node;
+        for (final InstanceIdentifier.PathArgument arg : identifier.getPath()) {
+            if (current instanceof SimpleNode<?>) {
+                return null;
+            } else if (current instanceof CompositeNode) {
+                final CompositeNode currentComposite = (CompositeNode) current;
+
+                current = currentComposite.getFirstCompositeByName(arg.getNodeType());
+                if (current == null) {
+                    current = currentComposite.getFirstCompositeByName(arg.getNodeType().withoutRevision());
+                }
+                if (current == null) {
+                    current = currentComposite.getFirstSimpleByName(arg.getNodeType());
+                }
+                if (current == null) {
+                    current = currentComposite.getFirstSimpleByName(arg.getNodeType().withoutRevision());
+                }
+                if (current == null) {
+                    return null;
+                }
+            }
+        }
+        return current;
+    }
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDatastoreAdapter.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDatastoreAdapter.java
new file mode 100644 (file)
index 0000000..e491496
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse 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.connect.netconf.sal;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.FluentIterable;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
+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.netconf.node.inventory.rev140108.NetconfNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.inventory.rev140108.NetconfNodeBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Asynchronous (Binding-aware) adapter over datastore subtree for netconf device.
+ *
+ * All data changes are submitted to an ExecutorService to avoid Thread blocking while sal is waiting for schema.
+ */
+final class NetconfDeviceDatastoreAdapter implements AutoCloseable {
+
+    private static final Logger logger  = LoggerFactory.getLogger(NetconfDeviceDatastoreAdapter.class);
+
+    private final RemoteDeviceId id;
+    private final DataProviderService dataService;
+    private final ListeningExecutorService executor;
+
+    NetconfDeviceDatastoreAdapter(final RemoteDeviceId deviceId, final DataProviderService dataService,
+            final ExecutorService executor) {
+        this.id = Preconditions.checkNotNull(deviceId);
+        this.dataService = Preconditions.checkNotNull(dataService);
+        this.executor = MoreExecutors.listeningDecorator(Preconditions.checkNotNull(executor));
+
+        // Initial data change scheduled
+        submitDataChangeToExecutor(this.executor, new Runnable() {
+            @Override
+            public void run() {
+                initDeviceData();
+            }
+        }, deviceId);
+    }
+
+    public void updateDeviceState(final boolean up, final Set<QName> capabilities) {
+        submitDataChangeToExecutor(this.executor, new Runnable() {
+            @Override
+            public void run() {
+                updateDeviceStateInternal(up, capabilities);
+            }
+        }, id);
+    }
+
+    private void updateDeviceStateInternal(final boolean up, final Set<QName> capabilities) {
+        final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node data = buildDataForDeviceState(
+                up, capabilities, id);
+
+        final DataModificationTransaction transaction = dataService.beginTransaction();
+        logger.trace("{}: Update device state transaction {} putting operational data started.", id, transaction.getIdentifier());
+        transaction.removeOperationalData(id.getBindingPath());
+        transaction.putOperationalData(id.getBindingPath(), data);
+        logger.trace("{}: Update device state transaction {} putting operational data ended.", id, transaction.getIdentifier());
+
+        commitTransaction(transaction, "update");
+    }
+
+    private void removeDeviceConfigAndState() {
+        final DataModificationTransaction transaction = dataService.beginTransaction();
+        logger.trace("{}: Close device state transaction {} removing all data started.", id, transaction.getIdentifier());
+        transaction.removeConfigurationData(id.getBindingPath());
+        transaction.removeOperationalData(id.getBindingPath());
+        logger.trace("{}: Close device state transaction {} removing all data ended.", id, transaction.getIdentifier());
+
+        commitTransaction(transaction, "close");
+    }
+
+    private void initDeviceData() {
+        final DataModificationTransaction transaction = dataService.beginTransaction();
+
+        final InstanceIdentifier<Node> path = id.getBindingPath();
+
+        final Node nodeWithId = getNodeWithId(id);
+        if (operationalNodeNotExisting(transaction, path)) {
+            transaction.putOperationalData(path, nodeWithId);
+        }
+        if (configurationNodeNotExisting(transaction, path)) {
+            transaction.putConfigurationData(path, nodeWithId);
+        }
+
+        commitTransaction(transaction, "init");
+    }
+
+    private void commitTransaction(final DataModificationTransaction transaction, final String txType) {
+        // attempt commit
+        final RpcResult<TransactionStatus> result;
+        try {
+            result = transaction.commit().get();
+        } catch (InterruptedException | ExecutionException e) {
+            logger.error("{}: Transaction({}) failed", id, txType, e);
+            throw new IllegalStateException(id + " Transaction(" + txType + ") not committed correctly", e);
+        }
+
+        // verify success result + committed state
+        if (isUpdateSuccessful(result)) {
+            logger.trace("{}: Transaction({}) {} SUCCESSFUL", id, txType, transaction.getIdentifier());
+        } else {
+            logger.error("{}: Transaction({}) {} FAILED!", id, txType, transaction.getIdentifier());
+            throw new IllegalStateException(id + "  Transaction(" + txType + ") not committed correctly, " +
+                    "Errors: " + result.getErrors());
+        }
+    }
+
+    @Override
+    public void close() throws Exception {
+        // Remove device data from datastore
+        submitDataChangeToExecutor(executor, new Runnable() {
+            @Override
+            public void run() {
+                removeDeviceConfigAndState();
+            }
+        }, id);
+    }
+
+    private static boolean isUpdateSuccessful(final RpcResult<TransactionStatus> result) {
+        return result.getResult() == TransactionStatus.COMMITED && result.isSuccessful();
+    }
+
+    private static void submitDataChangeToExecutor(final ListeningExecutorService executor, final Runnable r,
+            final RemoteDeviceId id) {
+        // Submit data change
+        final ListenableFuture<?> f = executor.submit(r);
+        // Verify update execution
+        Futures.addCallback(f, new FutureCallback<Object>() {
+            @Override
+            public void onSuccess(final Object result) {
+                logger.debug("{}: Device data updated successfully", id);
+            }
+
+            @Override
+            public void onFailure(final Throwable t) {
+                logger.warn("{}: Device data update failed", id, t);
+            }
+        });
+    }
+
+    public static org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node buildDataForDeviceState(
+            final boolean up, final Set<QName> capabilities, final RemoteDeviceId id) {
+
+        final NodeBuilder nodeBuilder = getNodeWithIdBuilder(id);
+        final NetconfNodeBuilder netconfNodeBuilder = new NetconfNodeBuilder();
+        netconfNodeBuilder.setConnected(up);
+        netconfNodeBuilder.setInitialCapability(FluentIterable.from(capabilities)
+                .transform(new Function<QName, String>() {
+                    @Override
+                    public String apply(final QName input) {
+                        return input.toString();
+                    }
+                }).toList());
+        nodeBuilder.addAugmentation(NetconfNode.class, netconfNodeBuilder.build());
+
+        return nodeBuilder.build();
+    }
+
+    private static boolean configurationNodeNotExisting(final DataModificationTransaction transaction,
+            final InstanceIdentifier<Node> path) {
+        return null == transaction.readConfigurationData(path);
+    }
+
+    private static boolean operationalNodeNotExisting(final DataModificationTransaction transaction,
+            final InstanceIdentifier<Node> path) {
+        return null == transaction.readOperationalData(path);
+    }
+
+    private static Node getNodeWithId(final RemoteDeviceId id) {
+        final NodeBuilder nodeBuilder = getNodeWithIdBuilder(id);
+        return nodeBuilder.build();
+    }
+
+    private static NodeBuilder getNodeWithIdBuilder(final RemoteDeviceId id) {
+        final NodeBuilder nodeBuilder = new NodeBuilder();
+        nodeBuilder.setKey(id.getBindingKey());
+        nodeBuilder.setId(id.getBindingKey().getId());
+        return nodeBuilder;
+    }
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceRpc.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceRpc.java
new file mode 100644 (file)
index 0000000..927d418
--- /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.sal.connect.netconf.sal;
+
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.Futures;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.annotation.Nullable;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.controller.sal.connect.api.MessageTransformer;
+import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+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.ListenableFuture;
+
+/**
+ * Invokes RPC by sending netconf message via listener. Also transforms result from NetconfMessage to CompositeNode.
+ */
+public final class NetconfDeviceRpc implements RpcImplementation {
+    private final RemoteDeviceCommunicator<NetconfMessage> listener;
+    private final MessageTransformer<NetconfMessage> transformer;
+
+    public NetconfDeviceRpc(final RemoteDeviceCommunicator<NetconfMessage> listener, final MessageTransformer<NetconfMessage> transformer) {
+        this.listener = listener;
+        this.transformer = transformer;
+    }
+
+    @Override
+    public Set<QName> getSupportedRpcs() {
+        // TODO is this correct ?
+        return Collections.emptySet();
+    }
+
+    @Override
+    public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(final QName rpc, final CompositeNode input) {
+        final NetconfMessage message = transformRequest(rpc, input);
+        final ListenableFuture<RpcResult<NetconfMessage>> delegateFutureWithPureResult = listener.sendRequest(
+                message, rpc);
+
+
+        return Futures.transform(delegateFutureWithPureResult, new Function<RpcResult<NetconfMessage>, RpcResult<CompositeNode>>() {
+            @Override
+            public RpcResult<CompositeNode> apply(@Nullable final RpcResult<NetconfMessage> input) {
+                return transformResult(input, rpc);
+            }
+        });
+    }
+
+    private NetconfMessage transformRequest(final QName rpc, final CompositeNode input) {
+        return transformer.toRpcRequest(rpc, input);
+    }
+
+    private RpcResult<CompositeNode> transformResult(final RpcResult<NetconfMessage> netconfMessageRpcResult,
+                                                                  final QName rpc) {
+        if (netconfMessageRpcResult.isSuccessful()) {
+            return transformer.toRpcResult(netconfMessageRpcResult.getResult(), rpc);
+        } else {
+            return Rpcs.getRpcResult(false, netconfMessageRpcResult.getErrors());
+        }
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalFacade.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalFacade.java
new file mode 100644 (file)
index 0000000..37b8704
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse 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.connect.netconf.sal;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
+import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionCapabilities;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
+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.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDeviceHandler<NetconfSessionCapabilities> {
+
+    private static final Logger logger= LoggerFactory.getLogger(NetconfDeviceSalFacade.class);
+    private static final InstanceIdentifier ROOT_PATH = InstanceIdentifier.builder().toInstance();
+
+    private final RemoteDeviceId id;
+    private final NetconfDeviceSalProvider salProvider;
+
+    private final List<AutoCloseable> salRegistrations = Lists.newArrayList();
+
+    public NetconfDeviceSalFacade(final RemoteDeviceId id, final Broker domBroker, final BindingAwareBroker bindingBroker, final BundleContext bundleContext, final ExecutorService executor) {
+        this.id = id;
+        this.salProvider = new NetconfDeviceSalProvider(id, executor);
+        registerToSal(domBroker, bindingBroker, bundleContext);
+    }
+
+    public void registerToSal(final Broker domRegistryDependency, final BindingAwareBroker bindingBroker, final BundleContext bundleContext) {
+        domRegistryDependency.registerProvider(salProvider, bundleContext);
+        bindingBroker.registerProvider(salProvider, bundleContext);
+    }
+
+    @Override
+    public synchronized void onNotification(final CompositeNode domNotification) {
+        salProvider.getMountInstance().publish(domNotification);
+    }
+
+    @Override
+    public synchronized void onDeviceConnected(final SchemaContextProvider remoteSchemaContextProvider,
+                                               final NetconfSessionCapabilities netconfSessionPreferences, final RpcImplementation deviceRpc) {
+        salProvider.getMountInstance().setSchemaContext(remoteSchemaContextProvider.getSchemaContext());
+        salProvider.getDatastoreAdapter().updateDeviceState(true, netconfSessionPreferences.getModuleBasedCaps());
+        registerDataHandlersToSal(deviceRpc, netconfSessionPreferences);
+        registerRpcsToSal(deviceRpc);
+    }
+
+    @Override
+    public void onDeviceDisconnected() {
+        salProvider.getDatastoreAdapter().updateDeviceState(false, Collections.<QName>emptySet());
+    }
+
+    private void registerRpcsToSal(final RpcImplementation deviceRpc) {
+        final MountProvisionInstance mountInstance = salProvider.getMountInstance();
+
+        final Map<QName, String> failedRpcs = Maps.newHashMap();
+        for (final RpcDefinition rpcDef : mountInstance.getSchemaContext().getOperations()) {
+            try {
+                salRegistrations.add(mountInstance.addRpcImplementation(rpcDef.getQName(), deviceRpc));
+                logger.debug("{}: Rpc {} from netconf registered successfully", id, rpcDef.getQName());
+            } catch (final Exception e) {
+                // Only debug per rpc, warn for all of them at the end to pollute log a little less (e.g. routed rpcs)
+                logger.debug("{}: Unable to register rpc {} from netconf device. This rpc will not be available", id,
+                        rpcDef.getQName(), e);
+                failedRpcs.put(rpcDef.getQName(), e.getClass() + ":" + e.getMessage());
+            }
+        }
+
+        if (failedRpcs.isEmpty() == false) {
+            if (logger.isDebugEnabled()) {
+                logger.warn("{}: Some rpcs from netconf device were not registered: {}", id, failedRpcs);
+            } else {
+                logger.warn("{}: Some rpcs from netconf device were not registered: {}", id, failedRpcs.keySet());
+            }
+        }
+    }
+
+    private void registerDataHandlersToSal(final RpcImplementation deviceRpc,
+            final NetconfSessionCapabilities netconfSessionPreferences) {
+        final NetconfDeviceDataReader dataReader = new NetconfDeviceDataReader(id, deviceRpc);
+        final NetconfDeviceCommitHandler commitHandler = new NetconfDeviceCommitHandler(id, deviceRpc,
+                netconfSessionPreferences.isRollbackSupported());
+
+        final MountProvisionInstance mountInstance = salProvider.getMountInstance();
+        salRegistrations.add(mountInstance.registerConfigurationReader(ROOT_PATH, dataReader));
+        salRegistrations.add(mountInstance.registerOperationalReader(ROOT_PATH, dataReader));
+        salRegistrations.add(mountInstance.registerCommitHandler(ROOT_PATH, commitHandler));
+    }
+
+    @Override
+    public void close() {
+        for (final AutoCloseable reg : Lists.reverse(salRegistrations)) {
+            closeGracefully(reg);
+        }
+        closeGracefully(salProvider);
+    }
+
+    private void closeGracefully(final AutoCloseable resource) {
+        if (resource != null) {
+            try {
+                resource.close();
+            } catch (final Exception e) {
+                logger.warn("{}: Ignoring exception while closing {}", id, resource, e);
+            }
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalProvider.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalProvider.java
new file mode 100644 (file)
index 0000000..01af84c
--- /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.sal.connect.netconf.sal;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import java.util.concurrent.ExecutorService;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+
+final class NetconfDeviceSalProvider implements AutoCloseable, Provider, BindingAwareProvider {
+
+    private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceSalProvider.class);
+
+    private final RemoteDeviceId id;
+    private final ExecutorService executor;
+    private volatile MountProvisionInstance mountInstance;
+    private volatile NetconfDeviceDatastoreAdapter datastoreAdapter;
+
+    public NetconfDeviceSalProvider(final RemoteDeviceId deviceId, final ExecutorService executor) {
+        this.id = deviceId;
+        this.executor = executor;
+    }
+
+    public MountProvisionInstance getMountInstance() {
+        Preconditions.checkState(mountInstance != null,
+                "%s: Sal provider was not initialized by sal. Cannot publish notification", id);
+        return mountInstance;
+    }
+
+    public NetconfDeviceDatastoreAdapter getDatastoreAdapter() {
+        Preconditions.checkState(datastoreAdapter != null,
+                "%s: Sal provider %s was not initialized by sal. Cannot publish notification", id);
+        return datastoreAdapter;
+    }
+
+    @Override
+    public void onSessionInitiated(final Broker.ProviderSession session) {
+        final MountProvisionService mountService = session.getService(MountProvisionService.class);
+        if (mountService != null) {
+            mountInstance = mountService.createOrGetMountPoint(id.getPath());
+        }
+
+        logger.debug("{}: (BI)Session with sal established {}", id, session);
+    }
+
+    @Override
+    public Collection<Provider.ProviderFunctionality> getProviderFunctionality() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public Collection<? extends RpcService> getImplementations() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public Collection<? extends BindingAwareProvider.ProviderFunctionality> getFunctionality() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public void onSessionInitiated(final BindingAwareBroker.ProviderContext session) {
+        final DataProviderService dataBroker = session.getSALService(DataProviderService.class);
+        datastoreAdapter = new NetconfDeviceDatastoreAdapter(id, dataBroker, executor);
+
+        logger.debug("{}: Session with sal established {}", id, session);
+    }
+
+    @Override
+    public void onSessionInitialized(final BindingAwareBroker.ConsumerContext session) {
+    }
+
+    public void close() throws Exception {
+        mountInstance = null;
+        datastoreAdapter.close();
+        datastoreAdapter = null;
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceTwoPhaseCommitTransaction.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceTwoPhaseCommitTransaction.java
new file mode 100644 (file)
index 0000000..41f9fec
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse 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.connect.netconf.sal;
+
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CONFIG_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_ERROR_OPTION_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_OPERATION_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_RUNNING_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_TARGET_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.ROLLBACK_ON_ERROR_OPTION;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ExecutionException;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.DataModification;
+import org.opendaylight.controller.sal.common.util.RpcErrors;
+import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.controller.sal.connect.util.FailedRpcResult;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcError;
+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.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+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.ImmutableList;
+import com.google.common.collect.Lists;
+
+/**
+ *  Remote transaction that delegates data change to remote device using netconf messages.
+ */
+final class NetconfDeviceTwoPhaseCommitTransaction implements DataCommitTransaction<InstanceIdentifier, CompositeNode> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceTwoPhaseCommitTransaction.class);
+
+    private final DataModification<InstanceIdentifier, CompositeNode> modification;
+    private final RpcImplementation rpc;
+    private final boolean rollbackSupported;
+    private final RemoteDeviceId id;
+    private final CompositeNode targetNode;
+
+    public NetconfDeviceTwoPhaseCommitTransaction(final RemoteDeviceId id, final RpcImplementation rpc,
+            final DataModification<InstanceIdentifier, CompositeNode> modification,
+            final boolean candidateSupported, final boolean rollbackOnErrorSupported) {
+        this.id = id;
+        this.rpc = Preconditions.checkNotNull(rpc);
+        this.modification = Preconditions.checkNotNull(modification);
+        this.targetNode = getTargetNode(candidateSupported);
+        this.rollbackSupported = rollbackOnErrorSupported;
+    }
+
+    /**
+     * Prepare phase, sends 1 or more netconf edit config operations to modify the data
+     *
+     * In case of failure or unexpected error response, ExecutionException is thrown
+     */
+    void prepare() throws InterruptedException, ExecutionException {
+        for (final InstanceIdentifier toRemove : modification.getRemovedConfigurationData()) {
+            sendDelete(toRemove);
+        }
+        for(final Entry<InstanceIdentifier, CompositeNode> toUpdate : modification.getUpdatedConfigurationData().entrySet()) {
+            sendMerge(toUpdate.getKey(),toUpdate.getValue());
+        }
+    }
+
+    private void sendMerge(final InstanceIdentifier key, final CompositeNode value) throws InterruptedException, ExecutionException {
+        sendEditRpc(createEditConfigStructure(key, Optional.<String>absent(), Optional.of(value)));
+    }
+
+    private void sendDelete(final InstanceIdentifier toDelete) throws InterruptedException, ExecutionException {
+        sendEditRpc(createEditConfigStructure(toDelete, Optional.of("delete"), Optional.<CompositeNode>absent()));
+    }
+
+    private void sendEditRpc(final CompositeNode editStructure) throws InterruptedException, ExecutionException {
+        final ImmutableCompositeNode editConfigRequest = createEditConfigRequest(editStructure);
+        final RpcResult<CompositeNode> rpcResult = rpc.invokeRpc(NETCONF_EDIT_CONFIG_QNAME, editConfigRequest).get();
+        // TODO 874 add default operation when sending delete
+
+        // Check result
+        if(rpcResult.isSuccessful() == false) {
+            throw new ExecutionException(
+                    String.format("%s: Pre-commit rpc failed, request: %s, errors: %s", id, editConfigRequest, rpcResult.getErrors()), null);
+        }
+    }
+
+    private ImmutableCompositeNode createEditConfigRequest(final CompositeNode editStructure) {
+        final CompositeNodeBuilder<ImmutableCompositeNode> ret = ImmutableCompositeNode.builder();
+
+        final Node<?> targetWrapperNode = ImmutableCompositeNode.create(NETCONF_TARGET_QNAME, ImmutableList.<Node<?>>of(targetNode));
+        ret.add(targetWrapperNode);
+
+        if(rollbackSupported) {
+            ret.addLeaf(NETCONF_ERROR_OPTION_QNAME, ROLLBACK_ON_ERROR_OPTION);
+        }
+        ret.setQName(NETCONF_EDIT_CONFIG_QNAME);
+        ret.add(editStructure);
+        return ret.toInstance();
+    }
+
+    private CompositeNode createEditConfigStructure(final InstanceIdentifier dataPath, final Optional<String> operation,
+                                                    final Optional<CompositeNode> lastChildOverride) {
+        Preconditions.checkArgument(dataPath.getPath().isEmpty() == false, "Instance identifier with empty path %s", dataPath);
+
+        List<PathArgument> reversedPath = Lists.reverse(dataPath.getPath());
+
+        // Create deepest edit element with expected edit operation
+        CompositeNode previous = getDeepestEditElement(reversedPath.get(0), operation, lastChildOverride);
+
+        // Remove already processed deepest child
+        reversedPath = Lists.newArrayList(reversedPath);
+        reversedPath.remove(0);
+
+        // Create edit structure in reversed order
+        for (final PathArgument arg : reversedPath) {
+            final CompositeNodeBuilder<ImmutableCompositeNode> builder = ImmutableCompositeNode.builder();
+            builder.setQName(arg.getNodeType());
+
+            addPredicatesToCompositeNodeBuilder(getPredicates(arg), builder);
+
+            builder.add(previous);
+            previous = builder.toInstance();
+        }
+        return ImmutableCompositeNode.create(NETCONF_CONFIG_QNAME, ImmutableList.<Node<?>>of(previous));
+    }
+
+    private void addPredicatesToCompositeNodeBuilder(final Map<QName, Object> predicates, final CompositeNodeBuilder<ImmutableCompositeNode> builder) {
+        for (final Entry<QName, Object> entry : predicates.entrySet()) {
+            builder.addLeaf(entry.getKey(), entry.getValue());
+        }
+    }
+
+    private Map<QName, Object> getPredicates(final PathArgument arg) {
+        Map<QName, Object> predicates = Collections.emptyMap();
+        if (arg instanceof NodeIdentifierWithPredicates) {
+            predicates = ((NodeIdentifierWithPredicates) arg).getKeyValues();
+        }
+        return predicates;
+    }
+
+    private CompositeNode getDeepestEditElement(final PathArgument arg, final Optional<String> operation, final Optional<CompositeNode> lastChildOverride) {
+        final CompositeNodeBuilder<ImmutableCompositeNode> builder = ImmutableCompositeNode.builder();
+        builder.setQName(arg.getNodeType());
+
+        final Map<QName, Object> predicates = getPredicates(arg);
+        addPredicatesToCompositeNodeBuilder(predicates, builder);
+
+        if (operation.isPresent()) {
+            builder.setAttribute(NETCONF_OPERATION_QNAME, operation.get());
+        }
+        if (lastChildOverride.isPresent()) {
+            final List<Node<?>> children = lastChildOverride.get().getValue();
+            for(final Node<?> child : children) {
+                if(!predicates.containsKey(child.getKey())) {
+                    builder.add(child);
+                }
+            }
+        }
+
+        return builder.toInstance();
+    }
+
+    /**
+     * Send commit rpc to finish the transaction
+     * In case of failure or unexpected error response, ExecutionException is thrown
+     */
+    @Override
+    public RpcResult<Void> finish() {
+        try {
+            final RpcResult<?> rpcResult = rpc.invokeRpc(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME, getCommitRequest()).get();
+            return new RpcResultVoidWrapper(rpcResult);
+        } catch (final InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new RuntimeException(id + ": Interrupted while waiting for response", e);
+        } catch (final ExecutionException e) {
+            LOG.warn("{}: Failed to finish commit operation", id, e);
+            return new FailedRpcResult<>(RpcErrors.getRpcError(null, null, null, RpcError.ErrorSeverity.ERROR,
+                    id + ": Unexpected operation error during commit operation", RpcError.ErrorType.APPLICATION, e));
+        }
+    }
+
+    private ImmutableCompositeNode getCommitRequest() {
+        final CompositeNodeBuilder<ImmutableCompositeNode> commitInput = ImmutableCompositeNode.builder();
+        commitInput.setQName(NETCONF_COMMIT_QNAME);
+        return commitInput.toInstance();
+    }
+
+    @Override
+    public DataModification<InstanceIdentifier, CompositeNode> getModification() {
+        return this.modification;
+    }
+
+    @Override
+    public RpcResult<Void> rollback() throws IllegalStateException {
+        // TODO BUG-732 implement rollback by sending discard changes
+        return null;
+    }
+
+    public CompositeNode getTargetNode(final boolean candidateSupported) {
+        if(candidateSupported) {
+            return ImmutableCompositeNode.create(NETCONF_CANDIDATE_QNAME, ImmutableList.<Node<?>>of());
+        } else {
+            return ImmutableCompositeNode.create(NETCONF_RUNNING_QNAME, ImmutableList.<Node<?>>of());
+        }
+    }
+
+    private static final class RpcResultVoidWrapper implements RpcResult<Void> {
+
+        private final RpcResult<?> rpcResult;
+
+        public RpcResultVoidWrapper(final RpcResult<?> rpcResult) {
+            this.rpcResult = rpcResult;
+        }
+
+        @Override
+        public boolean isSuccessful() {
+            return rpcResult.isSuccessful();
+        }
+
+        @Override
+        public Void getResult() {
+            return null;
+        }
+
+        @Override
+        public Collection<RpcError> getErrors() {
+            return rpcResult.getErrors();
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfDeviceSchemaProviderFactory.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfDeviceSchemaProviderFactory.java
new file mode 100644 (file)
index 0000000..9f844fd
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse 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.connect.netconf.schema;
+
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.opendaylight.controller.sal.connect.api.SchemaContextProviderFactory;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.opendaylight.yangtools.yang.parser.impl.util.YangSourceContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+
+public final class NetconfDeviceSchemaProviderFactory implements SchemaContextProviderFactory {
+
+    private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceSchemaProviderFactory.class);
+
+    private final RemoteDeviceId id;
+
+    public NetconfDeviceSchemaProviderFactory(final RemoteDeviceId id) {
+        this.id = id;
+    }
+
+    @Override
+    public SchemaContextProvider createContextProvider(final Collection<QName> capabilities, final SchemaSourceProvider<InputStream> sourceProvider) {
+
+        final YangSourceContext sourceContext = YangSourceContext.createFrom(capabilities, sourceProvider);
+
+        if (sourceContext.getMissingSources().isEmpty() == false) {
+            logger.warn("{}: Sources for following models are missing {}", id, sourceContext.getMissingSources());
+        }
+
+        logger.debug("{}: Trying to create schema context from {}", id, sourceContext.getValidSources());
+        final List<InputStream> modelsToParse = YangSourceContext.getValidInputStreams(sourceContext);
+
+        Preconditions.checkState(sourceContext.getValidSources().isEmpty() == false,
+                "%s: Unable to create schema context, no sources provided by device", id);
+        try {
+            final SchemaContext schemaContext = tryToParseContext(modelsToParse);
+            logger.debug("{}: Schema context successfully created.", id);
+            return new NetconfSchemaContextProvider(schemaContext);
+        } catch (final RuntimeException e) {
+            logger.error("{}: Unable to create schema context, unexpected error", id, e);
+            throw new IllegalStateException(id + ": Unable to create schema context", e);
+        }
+    }
+
+    private static SchemaContext tryToParseContext(final List<InputStream> modelsToParse) {
+        final YangParserImpl parser = new YangParserImpl();
+        final Set<Module> models = parser.parseYangModelsFromStreams(modelsToParse);
+        return parser.resolveSchemaContext(models);
+    }
+
+    private static final class NetconfSchemaContextProvider implements SchemaContextProvider {
+        private final SchemaContext schemaContext;
+
+        public NetconfSchemaContextProvider(final SchemaContext schemaContext) {
+            this.schemaContext = schemaContext;
+        }
+
+        @Override
+        public SchemaContext getSchemaContext() {
+            return schemaContext;
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaSourceProvider.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaSourceProvider.java
new file mode 100644 (file)
index 0000000..44ff2ef
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse 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.connect.netconf.schema;
+
+import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
+public final class NetconfRemoteSchemaSourceProvider implements SchemaSourceProvider<String> {
+
+    public static final QName GET_SCHEMA_QNAME = QName.create(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING,
+            "get-schema");
+    public static final QName GET_DATA_QNAME = QName
+            .create(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING, "data");
+
+    private static final Logger logger = LoggerFactory.getLogger(NetconfRemoteSchemaSourceProvider.class);
+
+    private final RpcImplementation rpc;
+    private final RemoteDeviceId id;
+
+    public NetconfRemoteSchemaSourceProvider(final RemoteDeviceId id, final RpcImplementation rpc) {
+        this.id = id;
+        this.rpc = Preconditions.checkNotNull(rpc);
+    }
+
+    @Override
+    public Optional<String> getSchemaSource(final String moduleName, final Optional<String> revision) {
+        final ImmutableCompositeNode getSchemaRequest = createGetSchemaRequest(moduleName, revision);
+
+        logger.trace("{}: Loading YANG schema source for {}:{}", id, moduleName, revision);
+        try {
+            final RpcResult<CompositeNode> schemaReply = rpc.invokeRpc(GET_SCHEMA_QNAME, getSchemaRequest).get();
+            if (schemaReply.isSuccessful()) {
+                final Optional<String> schemaBody = getSchemaFromRpc(id, schemaReply.getResult());
+                if (schemaBody.isPresent()) {
+                    logger.debug("{}: YANG Schema successfully retrieved for {}:{}", id, moduleName, revision);
+                    return schemaBody;
+                }
+            } else {
+                logger.warn("{}: YANG schema was not successfully retrieved for {}:{}. Errors: {}", id, moduleName,
+                        revision, schemaReply.getErrors());
+            }
+            return Optional.absent();
+        } catch (final InterruptedException e){
+            Thread.currentThread().interrupt();
+            throw new IllegalStateException(e);
+        } catch (final Exception e) {
+            logger.error("{}: YANG schema was not successfully retrieved for {}:{}", id, moduleName, revision, e);
+            throw new IllegalStateException(e);
+        }
+    }
+
+    private ImmutableCompositeNode createGetSchemaRequest(final String moduleName, final Optional<String> revision) {
+        final CompositeNodeBuilder<ImmutableCompositeNode> request = ImmutableCompositeNode.builder();
+        request.setQName(GET_SCHEMA_QNAME).addLeaf("identifier", moduleName);
+        if (revision.isPresent()) {
+            request.addLeaf("version", revision.get());
+        }
+        request.addLeaf("format", "yang");
+        return request.toInstance();
+    }
+
+    private static Optional<String> getSchemaFromRpc(final RemoteDeviceId id, final CompositeNode result) {
+        if (result == null) {
+            return Optional.absent();
+        }
+        final SimpleNode<?> simpleNode = result.getFirstSimpleByName(GET_DATA_QNAME.withoutRevision());
+
+        Preconditions.checkNotNull(simpleNode,
+                "%s Unexpected response to get-schema, expected response with one child %s, but was %s",
+                id, GET_DATA_QNAME.withoutRevision(), result);
+
+        final Object potential = simpleNode.getValue();
+        return potential instanceof String ? Optional.of((String) potential) : Optional.<String>absent();
+    }
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java
new file mode 100644 (file)
index 0000000..c85a529
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.connect.netconf.schema.mapping;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import javax.activation.UnsupportedDataTypeException;
+
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.controller.sal.connect.api.MessageTransformer;
+import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.controller.sal.connect.util.MessageCounter;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.google.common.base.Optional;
+
+public class NetconfMessageTransformer implements MessageTransformer<NetconfMessage> {
+
+    public static final String MESSAGE_ID_PREFIX = "m";
+
+    private Optional<SchemaContext> schemaContext = Optional.absent();
+    private final MessageCounter counter;
+
+    public NetconfMessageTransformer() {
+        this.counter = new MessageCounter();
+    }
+
+    @Override
+    public synchronized CompositeNode toNotification(final NetconfMessage message) {
+        if(schemaContext.isPresent()) {
+            return toNotification(message, schemaContext.get());
+        } else {
+            return XmlDocumentUtils.notificationToDomNodes(message.getDocument(), Optional.<Set<NotificationDefinition>>absent());
+        }
+    }
+
+    private static CompositeNode toNotification(final NetconfMessage message, final SchemaContext ctx) {
+        final Set<NotificationDefinition> notifications = ctx.getNotifications();
+        final Document document = message.getDocument();
+        return XmlDocumentUtils.notificationToDomNodes(document, Optional.fromNullable(notifications));
+    }
+
+    @Override
+    public NetconfMessage toRpcRequest(final QName rpc, final CompositeNode node) {
+        final CompositeNodeTOImpl rpcPayload = NetconfMessageTransformUtil.wrap(
+                NetconfMessageTransformUtil.NETCONF_RPC_QNAME, NetconfMessageTransformUtil.flattenInput(node));
+        final Document w3cPayload;
+        try {
+            w3cPayload = XmlDocumentUtils.toDocument(rpcPayload, XmlDocumentUtils.defaultValueCodecProvider());
+        } catch (final UnsupportedDataTypeException e) {
+            throw new IllegalArgumentException("Unable to create message", e);
+        }
+        w3cPayload.getDocumentElement().setAttribute("message-id", counter.getNewMessageId(MESSAGE_ID_PREFIX));
+        return new NetconfMessage(w3cPayload);
+    }
+
+    @Override
+    public synchronized RpcResult<CompositeNode> toRpcResult(final NetconfMessage message, final QName rpc) {
+        if(schemaContext.isPresent()) {
+            return toRpcResult(message, rpc, schemaContext.get());
+        } else {
+            final CompositeNode node = (CompositeNode) XmlDocumentUtils.toDomNode(message.getDocument());
+            return Rpcs.getRpcResult(true, node, Collections.<RpcError>emptySet());
+        }
+    }
+
+    private static RpcResult<CompositeNode> toRpcResult(final NetconfMessage message, final QName rpc, final SchemaContext context) {
+        final CompositeNode compositeNode;
+
+        if (NetconfMessageTransformUtil.isDataRetrievalOperation(rpc)) {
+
+            final Element xmlData = NetconfMessageTransformUtil.getDataSubtree(message.getDocument());
+
+            final List<org.opendaylight.yangtools.yang.data.api.Node<?>> dataNodes = XmlDocumentUtils.toDomNodes(xmlData,
+                    Optional.of(context.getDataDefinitions()), context);
+
+            final CompositeNodeBuilder<ImmutableCompositeNode> it = ImmutableCompositeNode.builder();
+            it.setQName(NetconfMessageTransformUtil.NETCONF_RPC_REPLY_QNAME);
+            it.add(ImmutableCompositeNode.create(NetconfMessageTransformUtil.NETCONF_DATA_QNAME, dataNodes));
+
+            compositeNode = it.toInstance();
+        } else {
+            // TODO map rpc with schema
+            compositeNode = (CompositeNode) XmlDocumentUtils.toDomNode(message.getDocument());
+        }
+
+        return Rpcs.getRpcResult(true, compositeNode, Collections.<RpcError> emptySet());
+    }
+
+    @Override
+    public synchronized void onGlobalContextUpdated(final SchemaContext schemaContext) {
+        this.schemaContext = Optional.of(schemaContext);
+    }
+}
  * terms of the Eclipse 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.connect.netconf;
+package org.opendaylight.controller.sal.connect.netconf.util;
 
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
 
-import javax.activation.UnsupportedDataTypeException;
+import java.util.Map;
 import javax.annotation.Nullable;
 
 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
-import org.opendaylight.controller.sal.common.util.Rpcs;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcError;
-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.NodeIdentifierWithPredicates;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
 import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
 import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
 import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
-import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
-import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 
-public class NetconfMapping {
+public class NetconfMessageTransformUtil {
 
-    public static URI NETCONF_URI = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0");
-    public static String NETCONF_MONITORING_URI = "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring";
-    public static URI NETCONF_NOTIFICATION_URI = URI.create("urn:ietf:params:xml:ns:netconf:notification:1.0");
-    public static URI NETCONF_ROLLBACK_ON_ERROR_URI = URI.create("urn:ietf:params:netconf:capability:rollback-on-error:1.0");
+    private NetconfMessageTransformUtil() {
+    }
 
+    public static final QName IETF_NETCONF_MONITORING = QName.create(
+            "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", "2010-10-04", "ietf-netconf-monitoring");
+    public static URI NETCONF_URI = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0");
     public static QName NETCONF_QNAME = QName.create(NETCONF_URI, null, "netconf");
-    public static QName NETCONF_RPC_QNAME = QName.create(NETCONF_QNAME, "rpc");
-    public static QName NETCONF_GET_QNAME = QName.create(NETCONF_QNAME, "get");
-    public static QName NETCONF_FILTER_QNAME = QName.create(NETCONF_QNAME, "filter");
-    public static QName NETCONF_TYPE_QNAME = QName.create(NETCONF_QNAME, "type");
-    public static QName NETCONF_GET_CONFIG_QNAME = QName.create(NETCONF_QNAME, "get-config");
-    public static QName NETCONF_EDIT_CONFIG_QNAME = QName.create(NETCONF_QNAME, "edit-config");
-    public static QName NETCONF_DELETE_CONFIG_QNAME = QName.create(NETCONF_QNAME, "delete-config");
-    public static QName NETCONF_OPERATION_QNAME = QName.create(NETCONF_QNAME, "operation");
-    public static QName NETCONF_COMMIT_QNAME = QName.create(NETCONF_QNAME, "commit");
-
-    public static QName NETCONF_CONFIG_QNAME = QName.create(NETCONF_QNAME, "config");
-    public static QName NETCONF_SOURCE_QNAME = QName.create(NETCONF_QNAME, "source");
-    public static QName NETCONF_TARGET_QNAME = QName.create(NETCONF_QNAME, "target");
-
-    public static QName NETCONF_CANDIDATE_QNAME = QName.create(NETCONF_QNAME, "candidate");
-    public static QName NETCONF_RUNNING_QNAME = QName.create(NETCONF_QNAME, "running");
-
-    public static QName NETCONF_ERROR_OPTION_QNAME = QName.create(NETCONF_QNAME, "error-option");
-    public static String ROLLBACK_ON_ERROR_OPTION = "rollback-on-error";
-
-    public static QName NETCONF_RPC_REPLY_QNAME = QName.create(NETCONF_QNAME, "rpc-reply");
-    public static QName NETCONF_OK_QNAME = QName.create(NETCONF_QNAME, "ok");
     public static QName NETCONF_DATA_QNAME = QName.create(NETCONF_QNAME, "data");
-    public static QName NETCONF_CREATE_SUBSCRIPTION_QNAME = QName.create(NETCONF_NOTIFICATION_URI, null,
-            "create-subscription");
-    public static QName NETCONF_CANCEL_SUBSCRIPTION_QNAME = QName.create(NETCONF_NOTIFICATION_URI, null,
-            "cancel-subscription");
-    public static QName IETF_NETCONF_MONITORING_MODULE = QName.create(NETCONF_MONITORING_URI, "2010-10-04",
-            "ietf-netconf-monitoring");
-
-    static List<Node<?>> RUNNING = Collections.<Node<?>> singletonList(new SimpleNodeTOImpl(NETCONF_RUNNING_QNAME,
+    public static QName NETCONF_RPC_REPLY_QNAME = QName.create(NETCONF_QNAME, "rpc-reply");
+    public static QName NETCONF_ERROR_OPTION_QNAME = QName.create(NETCONF_QNAME, "error-option");
+    public static QName NETCONF_RUNNING_QNAME = QName.create(NETCONF_QNAME, "running");
+    static List<Node<?>> RUNNING = Collections.<Node<?>> singletonList(new SimpleNodeTOImpl<>(NETCONF_RUNNING_QNAME,
             null, null));
-
+    public static QName NETCONF_SOURCE_QNAME = QName.create(NETCONF_QNAME, "source");
     public static CompositeNode CONFIG_SOURCE_RUNNING = new CompositeNodeTOImpl(NETCONF_SOURCE_QNAME, null, RUNNING);
+    public static QName NETCONF_CANDIDATE_QNAME = QName.create(NETCONF_QNAME, "candidate");
+    public static QName NETCONF_TARGET_QNAME = QName.create(NETCONF_QNAME, "target");
+    public static QName NETCONF_CONFIG_QNAME = QName.create(NETCONF_QNAME, "config");
+    public static QName NETCONF_COMMIT_QNAME = QName.create(NETCONF_QNAME, "commit");
+    public static QName NETCONF_OPERATION_QNAME = QName.create(NETCONF_QNAME, "operation");
+    public static QName NETCONF_EDIT_CONFIG_QNAME = QName.create(NETCONF_QNAME, "edit-config");
+    public static QName NETCONF_GET_CONFIG_QNAME = QName.create(NETCONF_QNAME, "get-config");
+    public static QName NETCONF_TYPE_QNAME = QName.create(NETCONF_QNAME, "type");
+    public static QName NETCONF_FILTER_QNAME = QName.create(NETCONF_QNAME, "filter");
+    public static QName NETCONF_GET_QNAME = QName.create(NETCONF_QNAME, "get");
+    public static QName NETCONF_RPC_QNAME = QName.create(NETCONF_QNAME, "rpc");
+    public static URI NETCONF_ROLLBACK_ON_ERROR_URI = URI
+            .create("urn:ietf:params:netconf:capability:rollback-on-error:1.0");
+    public static String ROLLBACK_ON_ERROR_OPTION = "rollback-on-error";
 
-    static AtomicInteger messageId = new AtomicInteger(0);
-
-    static Node<?> toFilterStructure(final InstanceIdentifier identifier) {
+    public static Node<?> toFilterStructure(final InstanceIdentifier identifier) {
         Node<?> previous = null;
         if (identifier.getPath().isEmpty()) {
             return null;
         }
 
-        for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument component : Lists
+        for (final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument component : Lists
                 .reverse(identifier.getPath())) {
-            if (component instanceof NodeIdentifierWithPredicates) {
-                previous = toNode((NodeIdentifierWithPredicates)component, previous);
+            if (component instanceof InstanceIdentifier.NodeIdentifierWithPredicates) {
+                previous = toNode((InstanceIdentifier.NodeIdentifierWithPredicates)component, previous);
             } else {
                 previous = toNode(component, previous);
             }
@@ -109,9 +85,9 @@ public class NetconfMapping {
         return filter("subtree", previous);
     }
 
-    static Node<?> toNode(final NodeIdentifierWithPredicates argument, final Node<?> node) {
-        List<Node<?>> list = new ArrayList<>();
-        for (Map.Entry<QName, Object> arg : argument.getKeyValues().entrySet()) {
+    static Node<?> toNode(final InstanceIdentifier.NodeIdentifierWithPredicates argument, final Node<?> node) {
+        final List<Node<?>> list = new ArrayList<>();
+        for (final Map.Entry<QName, Object> arg : argument.getKeyValues().entrySet()) {
             list.add(new SimpleNodeTOImpl(arg.getKey(), null, arg.getValue()));
         }
         if (node != null) {
@@ -120,51 +96,31 @@ public class NetconfMapping {
         return new CompositeNodeTOImpl(argument.getNodeType(), null, list);
     }
 
-    static Node<?> toNode(final PathArgument argument, final Node<?> node) {
-        if (node != null) {
-            return new CompositeNodeTOImpl(argument.getNodeType(), null, Collections.<Node<?>> singletonList(node));
-        } else {
-            return new SimpleNodeTOImpl(argument.getNodeType(), null, null);
-        }
-    }
-
-    static CompositeNode toCompositeNode(final NetconfMessage message, final Optional<SchemaContext> ctx) {
-        // TODO: implement general normalization to normalize incoming Netconf
-        // Message
-        // for Schema Context counterpart
-        return null;
-    }
+    public static void checkValidReply(final NetconfMessage input, final NetconfMessage output) {
+        final String inputMsgId = input.getDocument().getDocumentElement().getAttribute("message-id");
+        final String outputMsgId = output.getDocument().getDocumentElement().getAttribute("message-id");
 
-    static CompositeNode toNotificationNode(final NetconfMessage message, final Optional<SchemaContext> ctx) {
-        if (ctx.isPresent()) {
-            SchemaContext schemaContext = ctx.get();
-            Set<NotificationDefinition> notifications = schemaContext.getNotifications();
-            Document document = message.getDocument();
-            return XmlDocumentUtils.notificationToDomNodes(document, Optional.fromNullable(notifications), ctx.get());
+        if(inputMsgId.equals(outputMsgId) == false) {
+            final String requestXml = XmlUtil.toString(input.getDocument());
+            final String responseXml = XmlUtil.toString(output.getDocument());
+            throw new IllegalStateException(String.format("Rpc request and reply message IDs must be same. Request: %s, response: %s", requestXml, responseXml));
         }
-        return null;
     }
 
-    static NetconfMessage toRpcMessage(final QName rpc, final CompositeNode node, final Optional<SchemaContext> ctx) {
-        CompositeNodeTOImpl rpcPayload = wrap(NETCONF_RPC_QNAME, flattenInput(node));
-        Document w3cPayload = null;
-        try {
-            w3cPayload = XmlDocumentUtils.toDocument(rpcPayload, XmlDocumentUtils.defaultValueCodecProvider());
-        } catch (UnsupportedDataTypeException e) {
-            throw new IllegalArgumentException("Unable to create message", e);
+    public static void checkSuccessReply(final NetconfMessage output) throws NetconfDocumentedException {
+        if(NetconfMessageUtil.isErrorMessage(output)) {
+            throw new IllegalStateException(String.format("Response contains error: %s", XmlUtil.toString(output.getDocument())));
         }
-        w3cPayload.getDocumentElement().setAttribute("message-id", "m-" + messageId.getAndIncrement());
-        return new NetconfMessage(w3cPayload);
     }
 
-    static CompositeNode flattenInput(final CompositeNode node) {
+    public static CompositeNode flattenInput(final CompositeNode node) {
         final QName inputQName = QName.create(node.getNodeType(), "input");
-        CompositeNode input = node.getFirstCompositeByName(inputQName);
+        final CompositeNode input = node.getFirstCompositeByName(inputQName);
         if (input == null)
             return node;
         if (input instanceof CompositeNode) {
 
-            List<Node<?>> nodes = ImmutableList.<Node<?>> builder() //
+            final List<Node<?>> nodes = ImmutableList.<Node<?>> builder() //
                     .addAll(input.getValue()) //
                     .addAll(Collections2.filter(node.getValue(), new Predicate<Node<?>>() {
                         @Override
@@ -180,43 +136,25 @@ public class NetconfMapping {
         return input;
     }
 
-    static RpcResult<CompositeNode> toRpcResult(final NetconfMessage message, final QName rpc, final Optional<SchemaContext> context) {
-        CompositeNode rawRpc;
-        if (context.isPresent())
-            if (isDataRetrieQNameReply(rpc)) {
-
-                Element xmlData = getDataSubtree(message.getDocument());
-
-                List<org.opendaylight.yangtools.yang.data.api.Node<?>> dataNodes = XmlDocumentUtils.toDomNodes(xmlData,
-                        Optional.of(context.get().getDataDefinitions()));
-
-                CompositeNodeBuilder<ImmutableCompositeNode> it = ImmutableCompositeNode.builder();
-                it.setQName(NETCONF_RPC_REPLY_QNAME);
-                it.add(ImmutableCompositeNode.create(NETCONF_DATA_QNAME, dataNodes));
-
-                rawRpc = it.toInstance();
-                // sys(xmlData)
-            } else {
-                rawRpc = toCompositeNode(message, context);
-            }
-        else {
-            rawRpc = (CompositeNode) toCompositeNode(message.getDocument());
+    static Node<?> toNode(final InstanceIdentifier.PathArgument argument, final Node<?> node) {
+        if (node != null) {
+            return new CompositeNodeTOImpl(argument.getNodeType(), null, Collections.<Node<?>> singletonList(node));
+        } else {
+            return new SimpleNodeTOImpl<Void>(argument.getNodeType(), null, null);
         }
-        // rawRpc.
-        return Rpcs.getRpcResult(true, rawRpc, Collections.<RpcError> emptySet());
     }
 
-    static Element getDataSubtree(final Document doc) {
+    public static Element getDataSubtree(final Document doc) {
         return (Element) doc.getElementsByTagNameNS(NETCONF_URI.toString(), "data").item(0);
     }
 
-    static boolean isDataRetrieQNameReply(final QName it) {
-        return NETCONF_URI == it.getNamespace()
-                && (it.getLocalName() == NETCONF_GET_CONFIG_QNAME.getLocalName() || it.getLocalName() == NETCONF_GET_QNAME
-                .getLocalName());
+    public static boolean isDataRetrievalOperation(final QName rpc) {
+        return NETCONF_URI == rpc.getNamespace()
+                && (rpc.getLocalName().equals(NETCONF_GET_CONFIG_QNAME.getLocalName()) || rpc.getLocalName().equals(
+                        NETCONF_GET_QNAME.getLocalName()));
     }
 
-    static CompositeNodeTOImpl wrap(final QName name, final Node<?> node) {
+    public static CompositeNodeTOImpl wrap(final QName name, final Node<?> node) {
         if (node != null) {
             return new CompositeNodeTOImpl(name, null, Collections.<Node<?>> singletonList(node));
         } else {
@@ -224,7 +162,7 @@ public class NetconfMapping {
         }
     }
 
-    static CompositeNodeTOImpl wrap(final QName name, final Node<?> additional, final Node<?> node) {
+    public static CompositeNodeTOImpl wrap(final QName name, final Node<?> additional, final Node<?> node) {
         if (node != null) {
             return new CompositeNodeTOImpl(name, null, ImmutableList.of(additional, node));
         } else {
@@ -233,7 +171,7 @@ public class NetconfMapping {
     }
 
     static ImmutableCompositeNode filter(final String type, final Node<?> node) {
-        CompositeNodeBuilder<ImmutableCompositeNode> it = ImmutableCompositeNode.builder(); //
+        final CompositeNodeBuilder<ImmutableCompositeNode> it = ImmutableCompositeNode.builder(); //
         it.setQName(NETCONF_FILTER_QNAME);
         it.setAttribute(NETCONF_TYPE_QNAME, type);
         if (node != null) {
@@ -243,24 +181,4 @@ public class NetconfMapping {
         }
     }
 
-    public static Node<?> toCompositeNode(final Document document) {
-        return XmlDocumentUtils.toDomNode(document);
-    }
-
-    public static void checkValidReply(final NetconfMessage input, final NetconfMessage output) {
-        String inputMsgId = input.getDocument().getDocumentElement().getAttribute("message-id");
-        String outputMsgId = output.getDocument().getDocumentElement().getAttribute("message-id");
-
-        if(inputMsgId.equals(outputMsgId) == false) {
-            String requestXml = XmlUtil.toString(input.getDocument());
-            String responseXml = XmlUtil.toString(output.getDocument());
-            throw new IllegalStateException(String.format("Rpc request and reply message IDs must be same. Request: %s, response: %s", requestXml, responseXml));
-        }
-    }
-
-    public static void checkSuccessReply(final NetconfMessage output) throws NetconfDocumentedException {
-        if(NetconfMessageUtil.isErrorMessage(output)) {
-            throw new IllegalStateException(String.format("Response contains error: %s", XmlUtil.toString(output.getDocument())));
-        }
-    }
 }
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/FailedRpcResult.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/FailedRpcResult.java
new file mode 100644 (file)
index 0000000..49b16d4
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.connect.util;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+public final class FailedRpcResult<T> implements RpcResult<T> {
+
+    private final RpcError rpcError;
+
+    public FailedRpcResult(final RpcError rpcError) {
+        this.rpcError = rpcError;
+    }
+
+    @Override
+    public boolean isSuccessful() {
+        return false;
+    }
+
+    @Override
+    public T getResult() {
+        return null;
+    }
+
+    @Override
+    public Collection<RpcError> getErrors() {
+        return Collections.singletonList(rpcError);
+    }
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/MessageCounter.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/MessageCounter.java
new file mode 100644 (file)
index 0000000..2b2f6a9
--- /dev/null
@@ -0,0 +1,21 @@
+package org.opendaylight.controller.sal.connect.util;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+
+public class MessageCounter {
+    final AtomicInteger messageId = new AtomicInteger(0);
+
+    private static final String messageIdBlueprint = "%s-%s";
+
+    public String getNewMessageId(final String prefix) {
+        Preconditions.checkArgument(Strings.isNullOrEmpty(prefix) == false, "Null or empty prefix");
+        return String.format(messageIdBlueprint, prefix, getNewMessageId());
+    }
+
+    public String getNewMessageId() {
+        return Integer.toString(messageId.getAndIncrement());
+    }
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/RemoteDeviceId.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/RemoteDeviceId.java
new file mode 100644 (file)
index 0000000..4670846
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse 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.connect.util;
+
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+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.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public class RemoteDeviceId {
+
+    private final String name;
+    private final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier path;
+    private final InstanceIdentifier<Node> bindingPath;
+    private final NodeKey key;
+
+    public RemoteDeviceId(final ModuleIdentifier identifier) {
+        this(Preconditions.checkNotNull(identifier).getInstanceName());
+    }
+
+    public RemoteDeviceId(final String name) {
+        Preconditions.checkNotNull(name);
+        this.name = name;
+        this.key = new NodeKey(new NodeId(name));
+        this.path = createBIPath(name);
+        this.bindingPath = createBindingPath(key);
+    }
+
+    private static InstanceIdentifier<Node> createBindingPath(final NodeKey key) {
+        return InstanceIdentifier.builder(Nodes.class).child(Node.class, key).build();
+    }
+
+    private static org.opendaylight.yangtools.yang.data.api.InstanceIdentifier createBIPath(final String name) {
+        final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder builder =
+                org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.builder();
+        builder.node(Nodes.QNAME).nodeWithKey(Node.QNAME, QName.create(Node.QNAME.getNamespace(), Node.QNAME.getRevision(), "id"), name);
+
+        return builder.build();
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public InstanceIdentifier<Node> getBindingPath() {
+        return bindingPath;
+    }
+
+    public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier getPath() {
+        return path;
+    }
+
+    public NodeKey getBindingKey() {
+        return key;
+    }
+
+    @Override
+    public String toString() {
+        return "RemoteDevice{" + name +'}';
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) return true;
+        if (!(o instanceof RemoteDeviceId)) return false;
+
+        final RemoteDeviceId that = (RemoteDeviceId) o;
+
+        if (!name.equals(that.name)) return false;
+        if (!bindingPath.equals(that.bindingPath)) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = name.hashCode();
+        result = 31 * result + bindingPath.hashCode();
+        return result;
+    }
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/package-info.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/package-info.java
new file mode 100644 (file)
index 0000000..21b9d3a
--- /dev/null
@@ -0,0 +1,6 @@
+/**
+ * Utility classes for remote connectors e.g. netconf connector
+ *
+ * TODO extract into separate bundle when another connector is implemented e.g. restconf connector
+ */
+package org.opendaylight.controller.sal.connect.util;
index d4dad11ec3b4f70e67ede2e1ddc4f690d59e9b52..c59c41c4374838908cf3d7440bc71cef7940b0d3 100644 (file)
@@ -3,10 +3,11 @@ module odl-sal-netconf-connector-cfg {
     namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf";
     prefix "sal-netconf";
 
-       import config { prefix config; revision-date 2013-04-05; }
-       import threadpool {prefix th;}
-       import netty {prefix netty;}
-       import opendaylight-md-sal-dom {prefix dom;}
+    import config { prefix config; revision-date 2013-04-05; }
+    import threadpool {prefix th;}
+    import netty {prefix netty;}
+    import opendaylight-md-sal-dom {prefix dom;}
+    import opendaylight-md-sal-binding {prefix md-sal-binding; revision-date 2013-10-28;}
     import odl-netconf-cfg { prefix cfg-net; revision-date 2014-04-08; }
 
     description
@@ -22,7 +23,6 @@ module odl-sal-netconf-connector-cfg {
         config:java-name-prefix NetconfConnector;
     }
 
-
     grouping server {
         leaf address {
             type string;
@@ -33,7 +33,6 @@ module odl-sal-netconf-connector-cfg {
         }
     }
 
-
     augment "/config:modules/config:module/config:configuration" {
         case sal-netconf-connector {
             when "/config:modules/config:module/config:type = 'sal-netconf-connector'";
@@ -67,6 +66,16 @@ module odl-sal-netconf-connector-cfg {
                 }
             }
 
+            container binding-registry {
+                uses config:service-ref {
+                    refine type {
+                        // FIXME BUG-944 make mandatory (remove backwards compatibility)
+                        mandatory false;
+                        config:required-identity md-sal-binding:binding-broker-osgi-registry;
+                    }
+                }
+            }
+
             // FIXME BUG-944 remove backwards compatibility
             // Deprecated, replaced by client dispatcher.
             // This dependency will be removed in near future and all configurations of netconf-connector need to be changed to use dispatcher dependency.
@@ -99,6 +108,18 @@ module odl-sal-netconf-connector-cfg {
                 }
             }
 
+            container processing-executor {
+                uses config:service-ref {
+                    refine type {
+                        // FIXME BUG-944 make mandatory (remove backwards compatibility)
+                        mandatory false;
+                        config:required-identity th:threadpool;
+                    }
+                }
+
+                description "Makes up for flaws in netty threading design";
+            }
+
             // Replaces thread group dependencies
             container client-dispatcher {
                 uses config:service-ref {
@@ -122,9 +143,16 @@ module odl-sal-netconf-connector-cfg {
             }
 
             leaf between-attempts-timeout-millis {
-                description "Timeout in milliseconds to wait between connection attempts.";
+                description "Initial timeout in milliseconds to wait between connection attempts. Will be multiplied by sleep-factor with every additional attempt";
                 type uint16;
-                default 10000;
+                default 2000;
+            }
+
+            leaf sleep-factor {
+                type decimal64 {
+                    fraction-digits 1;
+                }
+                default 1.5;
             }
         }
     }
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java
new file mode 100644 (file)
index 0000000..5ac32b5
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse 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.connect.netconf;
+
+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.timeout;
+import static org.mockito.Mockito.verify;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.controller.sal.connect.api.MessageTransformer;
+import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator;
+import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
+import org.opendaylight.controller.sal.connect.api.SchemaContextProviderFactory;
+import org.opendaylight.controller.sal.connect.api.SchemaSourceProviderFactory;
+import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionCapabilities;
+import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Futures;
+
+public class NetconfDeviceTest {
+
+    private static final NetconfMessage netconfMessage;
+    private static final CompositeNode compositeNode;
+
+    static {
+        try {
+            netconfMessage = mockClass(NetconfMessage.class);
+            compositeNode = mockClass(CompositeNode.class);
+        } catch (final Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static final  RpcResult<NetconfMessage> rpcResult = Rpcs.getRpcResult(true, netconfMessage, Collections.<RpcError>emptySet());
+    private static final  RpcResult<CompositeNode> rpcResultC = Rpcs.getRpcResult(true, compositeNode, Collections.<RpcError>emptySet());
+
+    public static final String TEST_NAMESPACE = "test:namespace";
+    public static final String TEST_MODULE = "test-module";
+    public static final String TEST_REVISION = "2013-07-22";
+
+    @Test
+    public void testNetconfDeviceWithoutMonitoring() throws Exception {
+        final RemoteDeviceHandler<NetconfSessionCapabilities> facade = getFacade();
+        final RemoteDeviceCommunicator<NetconfMessage> listener = getListener();
+
+        final NetconfDevice device = new NetconfDevice(getId(), facade, getExecutor(), getMessageTransformer(), getSchemaContextProviderFactory(), getSourceProviderFactory());
+        device.onRemoteSessionUp(getSessionCaps(false, Collections.<String>emptyList()), listener);
+
+        Mockito.verify(facade, Mockito.timeout(5000)).onDeviceDisconnected();
+    }
+
+    @Test
+    public void testNetconfDeviceReconnect() throws Exception {
+        final RemoteDeviceHandler<NetconfSessionCapabilities> facade = getFacade();
+        final RemoteDeviceCommunicator<NetconfMessage> listener = getListener();
+
+        final SchemaContextProviderFactory schemaContextProviderFactory = getSchemaContextProviderFactory();
+        final SchemaSourceProviderFactory<InputStream> sourceProviderFactory = getSourceProviderFactory();
+        final MessageTransformer<NetconfMessage> messageTransformer = getMessageTransformer();
+
+        final NetconfDevice device = new NetconfDevice(getId(), facade, getExecutor(), messageTransformer, schemaContextProviderFactory, sourceProviderFactory);
+        final NetconfSessionCapabilities sessionCaps = getSessionCaps(true,
+                Lists.newArrayList(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
+        device.onRemoteSessionUp(sessionCaps, listener);
+
+        verify(sourceProviderFactory, timeout(5000)).createSourceProvider(any(RpcImplementation.class));
+        verify(schemaContextProviderFactory, timeout(5000)).createContextProvider(any(Collection.class), any(SchemaSourceProvider.class));
+        verify(messageTransformer, timeout(5000)).onGlobalContextUpdated(any(SchemaContext.class));
+        verify(facade, timeout(5000)).onDeviceConnected(any(SchemaContextProvider.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class));
+
+        device.onRemoteSessionDown();
+        verify(facade, timeout(5000)).onDeviceDisconnected();
+
+        device.onRemoteSessionUp(sessionCaps, listener);
+
+        verify(sourceProviderFactory, timeout(5000).times(2)).createSourceProvider(any(RpcImplementation.class));
+        verify(schemaContextProviderFactory, timeout(5000).times(2)).createContextProvider(any(Collection.class), any(SchemaSourceProvider.class));
+        verify(messageTransformer, timeout(5000).times(2)).onGlobalContextUpdated(any(SchemaContext.class));
+        verify(facade, timeout(5000).times(2)).onDeviceConnected(any(SchemaContextProvider.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class));
+    }
+
+    private SchemaContextProviderFactory getSchemaContextProviderFactory() {
+        final SchemaContextProviderFactory schemaContextProviderFactory = mockClass(SchemaContextProviderFactory.class);
+        doReturn(new SchemaContextProvider() {
+            @Override
+            public SchemaContext getSchemaContext() {
+                return getSchema();
+            }
+        }).when(schemaContextProviderFactory).createContextProvider(any(Collection.class), any(SchemaSourceProvider.class));
+        return schemaContextProviderFactory;
+    }
+
+    public static SchemaContext getSchema() {
+        final YangParserImpl parser = new YangParserImpl();
+        final List<InputStream> modelsToParse = Lists.newArrayList(
+                NetconfDeviceTest.class.getResourceAsStream("/schemas/test-module.yang")
+        );
+        final Set<Module> models = parser.parseYangModelsFromStreams(modelsToParse);
+        return parser.resolveSchemaContext(models);
+    }
+
+    private RemoteDeviceHandler<NetconfSessionCapabilities> getFacade() throws Exception {
+        final RemoteDeviceHandler<NetconfSessionCapabilities> remoteDeviceHandler = mockCloseableClass(RemoteDeviceHandler.class);
+        doNothing().when(remoteDeviceHandler).onDeviceConnected(any(SchemaContextProvider.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class));
+        doNothing().when(remoteDeviceHandler).onDeviceDisconnected();
+        return remoteDeviceHandler;
+    }
+
+    private <T extends AutoCloseable> T mockCloseableClass(final Class<T> remoteDeviceHandlerClass) throws Exception {
+        final T mock = mockClass(remoteDeviceHandlerClass);
+        doNothing().when(mock).close();
+        return mock;
+    }
+
+    public SchemaSourceProviderFactory<InputStream> getSourceProviderFactory() {
+        final SchemaSourceProviderFactory<InputStream> mock = mockClass(SchemaSourceProviderFactory.class);
+
+        final SchemaSourceProvider<InputStream> schemaSourceProvider = mockClass(SchemaSourceProvider.class);
+        doReturn(Optional.<String>absent()).when(schemaSourceProvider).getSchemaSource(anyString(), any(Optional.class));
+
+        doReturn(schemaSourceProvider).when(mock).createSourceProvider(any(RpcImplementation.class));
+        return mock;
+    }
+
+    private static <T> T mockClass(final Class<T> remoteDeviceHandlerClass) {
+        final T mock = Mockito.mock(remoteDeviceHandlerClass);
+        Mockito.doReturn(remoteDeviceHandlerClass.getSimpleName()).when(mock).toString();
+        return mock;
+    }
+
+    public RemoteDeviceId getId() {
+        return new RemoteDeviceId("test-D");
+    }
+
+    public ExecutorService getExecutor() {
+        return Executors.newSingleThreadExecutor();
+    }
+
+    public MessageTransformer<NetconfMessage> getMessageTransformer() throws Exception {
+        final MessageTransformer<NetconfMessage> messageTransformer = mockClass(MessageTransformer.class);
+        doReturn(netconfMessage).when(messageTransformer).toRpcRequest(any(QName.class), any(CompositeNode.class));
+        doReturn(rpcResultC).when(messageTransformer).toRpcResult(any(NetconfMessage.class), any(QName.class));
+        doNothing().when(messageTransformer).onGlobalContextUpdated(any(SchemaContext.class));
+        return messageTransformer;
+    }
+
+    public NetconfSessionCapabilities getSessionCaps(final boolean addMonitor, final Collection<String> additionalCapabilities) {
+        final ArrayList<String> capabilities = Lists.newArrayList(
+                XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0,
+                XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_1);
+
+        if(addMonitor) {
+            capabilities.add(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString());
+        }
+
+        capabilities.addAll(additionalCapabilities);
+
+        return NetconfSessionCapabilities.fromStrings(
+                capabilities);
+    }
+
+    public RemoteDeviceCommunicator<NetconfMessage> getListener() throws Exception {
+        final RemoteDeviceCommunicator<NetconfMessage> remoteDeviceCommunicator = mockCloseableClass(RemoteDeviceCommunicator.class);
+        doReturn(Futures.immediateFuture(rpcResult)).when(remoteDeviceCommunicator).sendRequest(any(NetconfMessage.class), any(QName.class));
+        return remoteDeviceCommunicator;
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/test-module.yang b/opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/test-module.yang
new file mode 100644 (file)
index 0000000..cd732fc
--- /dev/null
@@ -0,0 +1,18 @@
+module test-module {
+    yang-version 1;
+    namespace "test:namespace";
+    prefix "tt";
+
+    description
+        "Types for testing";
+
+    revision "2013-07-22";
+
+
+    container c {
+        leaf a {
+            type string;
+        }
+    }
+
+}
index 593c104dfd63e24efe268358e79bdb5a8a9e5a56..1f7b061e921cd057d13ece0002e998973fb66db2 100644 (file)
@@ -31,6 +31,12 @@ class JsonReader {
         JsonParser parser = new JsonParser();
 
         JsonElement rootElement = parser.parse(new InputStreamReader(entityStream));
+        if( rootElement.isJsonNull() )
+        {
+            //no content, so return null to indicate no input
+            return null;
+        }
+
         if (!rootElement.isJsonObject()) {
             throw new UnsupportedFormatException("Root element of Json has to be Object");
         }
index 5b95f0de1ada80408f24670c310dce35a306ab5d..171805a1798a872ff7f42b821f3201dced670b9f 100644 (file)
@@ -9,6 +9,8 @@ package org.opendaylight.controller.sal.rest.impl;
 
 import static com.google.common.base.Preconditions.checkArgument;
 
+import java.io.BufferedInputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
 import java.util.Stack;
@@ -32,7 +34,17 @@ public class XmlReader {
     private final static XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
     private XMLEventReader eventReader;
 
-    public CompositeNodeWrapper read(InputStream entityStream) throws XMLStreamException, UnsupportedFormatException {
+    public CompositeNodeWrapper read(InputStream entityStream) throws XMLStreamException,
+                                                                      UnsupportedFormatException,
+                                                                      IOException {
+        //Get an XML stream which can be marked, and reset, so we can check and see if there is
+        //any content being provided.
+        entityStream = getMarkableStream(entityStream);
+
+        if( isInputStreamEmpty( entityStream ) ) {
+            return null;
+        }
+
         eventReader = xmlInputFactory.createXMLEventReader(entityStream);
 
         if (eventReader.hasNext()) {
@@ -91,6 +103,31 @@ public class XmlReader {
         return root;
     }
 
+    /**
+     * If the input stream is not markable, then it wraps the input stream with a buffered stream,
+     * which is mark able. That way we can check if the stream is empty safely.
+     * @param entityStream
+     * @return
+     */
+    private InputStream getMarkableStream(InputStream entityStream) {
+        if( !entityStream.markSupported() )
+        {
+            entityStream = new BufferedInputStream( entityStream );
+        }
+        return entityStream;
+    }
+
+    private boolean isInputStreamEmpty(InputStream entityStream)
+            throws IOException {
+        boolean isEmpty = false;
+        entityStream.mark( 1 );
+        if( entityStream.read() == -1 ){
+            isEmpty = true;
+        }
+        entityStream.reset();
+        return isEmpty;
+    }
+
     private boolean isSimpleNodeEvent(final XMLEvent event) throws XMLStreamException {
         checkArgument(event != null, "XML Event cannot be NULL!");
         if (event.isStartElement()) {
index ad682bc8291d50e52d674607e30142009bac459e..c0ce90e15dde780a18e40e0e216c78059c9183fc 100644 (file)
@@ -8,13 +8,6 @@
  */
 package org.opendaylight.controller.sal.restconf.impl;
 
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Splitter;
-import com.google.common.base.Strings;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-
 import java.net.URI;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
@@ -38,21 +31,11 @@ import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.sal.core.api.mount.MountInstance;
 import org.opendaylight.controller.sal.rest.api.Draft02;
 import org.opendaylight.controller.sal.rest.api.RestconfService;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.controller.sal.restconf.rpc.impl.BrokerRpcExecutor;
 import org.opendaylight.controller.sal.restconf.rpc.impl.MountPointRpcExecutor;
 import org.opendaylight.controller.sal.restconf.rpc.impl.RpcExecutor;
-import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
-import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
-import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
-import org.opendaylight.controller.sal.restconf.impl.EmptyNodeWrapper;
-import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO;
-import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode;
-import org.opendaylight.controller.sal.restconf.impl.NodeWrapper;
-import org.opendaylight.controller.sal.restconf.impl.RestCodec;
-import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
-import org.opendaylight.controller.sal.restconf.impl.StructuredData;
-import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
-import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
 import org.opendaylight.controller.sal.streams.listeners.Notificator;
 import org.opendaylight.controller.sal.streams.websockets.WebSocketServer;
@@ -84,6 +67,13 @@ import org.opendaylight.yangtools.yang.model.util.EmptyType;
 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
 
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
 public class RestconfImpl implements RestconfService {
     private final static RestconfImpl INSTANCE = new RestconfImpl();
 
@@ -401,13 +391,36 @@ public class RestconfImpl implements RestconfService {
         URI rpcNamespace = rpcName.getNamespace();
         if (Objects.equal(rpcNamespace.toString(), SAL_REMOTE_NAMESPACE) &&
             Objects.equal(rpcName.getLocalName(), SAL_REMOTE_RPC_SUBSRCIBE)) {
-
             return invokeSalRemoteRpcSubscribeRPC(payload, rpc.getRpcDefinition());
         }
 
+        validateInput( rpc.getRpcDefinition().getInput(), payload );
+
         return callRpc(rpc, payload);
     }
 
+    private void validateInput(DataSchemaNode inputSchema, CompositeNode payload) {
+        if( inputSchema != null && payload == null )
+        {
+            //expected a non null payload
+            throw new RestconfDocumentedException( "Input is required.",
+                                                   ErrorType.PROTOCOL,
+                                                   ErrorTag.MALFORMED_MESSAGE );
+        }
+        else if( inputSchema == null && payload != null )
+        {
+            //did not expect any input
+            throw new RestconfDocumentedException( "No input expected.",
+                                                   ErrorType.PROTOCOL,
+                                                   ErrorTag.MALFORMED_MESSAGE );
+        }
+        //else
+        //{
+            //TODO: Validate "mandatory" and "config" values here??? Or should those be
+        // validate in a more central location inside MD-SAL core.
+        //}
+    }
+
     private StructuredData invokeSalRemoteRpcSubscribeRPC(final CompositeNode payload,
                                                           final RpcDefinition rpc) {
         final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
@@ -455,8 +468,7 @@ public class RestconfImpl implements RestconfService {
             throw new RestconfDocumentedException(
                     "Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
-        final RpcExecutor rpc = resolveIdentifierInInvokeRpc(identifier);
-        return callRpc(rpc, null);
+        return invokeRpc( identifier, (CompositeNode)null );
     }
 
     private RpcExecutor resolveIdentifierInInvokeRpc(final String identifier) {
@@ -586,6 +598,9 @@ public class RestconfImpl implements RestconfService {
     @Override
     public Response updateConfigurationData(final String identifier, final CompositeNode payload) {
         final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
+
+        validateInput(iiWithData.getSchemaNode(), payload);
+
         MountInstance mountPoint = iiWithData.getMountPoint();
         final CompositeNode value = this.normalizeNode(payload, iiWithData.getSchemaNode(), mountPoint);
         RpcResult<TransactionStatus> status = null;
@@ -610,6 +625,12 @@ public class RestconfImpl implements RestconfService {
 
     @Override
     public Response createConfigurationData(final String identifier, final CompositeNode payload) {
+        if( payload == null ) {
+            throw new RestconfDocumentedException( "Input is required.",
+                    ErrorType.PROTOCOL,
+                    ErrorTag.MALFORMED_MESSAGE );
+        }
+
         URI payloadNS = this.namespace(payload);
         if (payloadNS == null) {
             throw new RestconfDocumentedException(
@@ -685,6 +706,12 @@ public class RestconfImpl implements RestconfService {
 
     @Override
     public Response createConfigurationData(final CompositeNode payload) {
+        if( payload == null ) {
+            throw new RestconfDocumentedException( "Input is required.",
+                    ErrorType.PROTOCOL,
+                    ErrorTag.MALFORMED_MESSAGE );
+        }
+
         URI payloadNS = this.namespace(payload);
         if (payloadNS == null) {
             throw new RestconfDocumentedException(
index 312365585eaa149aaa05125acc4530840451c8e9..4e32e7058ca6cce63f9a38760764b0b0c137ddf7 100644 (file)
@@ -11,49 +11,127 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import java.io.IOException;
 import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import javax.ws.rs.WebApplicationException;
+import java.util.Map;
 
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider;
 import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider;
+import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
 import org.opendaylight.controller.sal.restconf.impl.test.TestUtils;
 import org.opendaylight.controller.sal.restconf.impl.test.YangAndXmlAndDataSchemaLoader;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 
+import com.google.common.collect.Maps;
 import com.google.gson.stream.JsonReader;
 import com.google.gson.stream.JsonToken;
 
 public class CnSnToJsonBasicDataTypesTest extends YangAndXmlAndDataSchemaLoader {
 
+    static abstract class LeafVerifier {
+
+        Object expectedValue;
+        JsonToken expectedToken;
+
+        LeafVerifier( Object expectedValue, JsonToken expectedToken ) {
+            this.expectedValue = expectedValue;
+            this.expectedToken = expectedToken;
+        }
+
+        abstract Object getActualValue( JsonReader reader ) throws IOException;
+
+        void verify( JsonReader reader, String keyName ) throws IOException {
+            assertEquals( "Json value for key " + keyName, expectedValue, getActualValue( reader ) );
+        }
+
+        JsonToken expectedTokenType() {
+            return expectedToken;
+        }
+    }
+
+    static class BooleanVerifier extends LeafVerifier {
+
+        public BooleanVerifier( boolean expected ) {
+            super( expected, JsonToken.BOOLEAN );
+        }
+
+        @Override
+        Object getActualValue( JsonReader reader ) throws IOException {
+            return reader.nextBoolean();
+        }
+    }
+
+    static class NumberVerifier extends LeafVerifier {
+
+        public NumberVerifier( Number expected ) {
+            super( expected, JsonToken.NUMBER );
+        }
+
+        @Override
+        Object getActualValue( JsonReader reader ) throws IOException {
+            if( expectedValue instanceof Double ) {
+                return reader.nextDouble();
+            }
+            else if( expectedValue instanceof Long ) {
+                return reader.nextLong();
+            }
+            else if( expectedValue instanceof Integer ) {
+                return reader.nextInt();
+            }
+
+            return null;
+        }
+    }
+
+    static class StringVerifier extends LeafVerifier {
+
+        StringVerifier( String expected ) {
+            super( expected, JsonToken.STRING );
+        }
+
+        @Override
+        Object getActualValue( JsonReader reader ) throws IOException {
+            return reader.nextString();
+        }
+    }
+
+    static class EmptyVerifier extends LeafVerifier {
+
+        EmptyVerifier() {
+            super( null, null );
+        }
+
+        @Override
+        Object getActualValue( JsonReader reader ) throws IOException {
+            reader.beginArray();
+            reader.nextNull();
+            reader.endArray();
+            return null;
+        }
+
+    }
+
     @BeforeClass
     public static void initialize() {
         dataLoad("/cnsn-to-json/simple-data-types");
     }
 
     @Test
-    public void simpleYangDataTest() {
+    public void simpleYangDataTest() throws Exception {
 
         CompositeNode compositeNode = TestUtils.readInputToCnSn("/cnsn-to-json/simple-data-types/xml/data.xml",
                 XmlToCompositeNodeProvider.INSTANCE);
 
-        String jsonOutput = null;
-
         TestUtils.normalizeCompositeNode(compositeNode, modules, "simple-data-types:cont");
 
-        try {
-            jsonOutput = TestUtils.writeCompNodeWithSchemaContextToOutput(compositeNode, modules, dataSchemaNode,
+        String jsonOutput = TestUtils.writeCompNodeWithSchemaContextToOutput(compositeNode, modules, dataSchemaNode,
                     StructuredDataToJsonProvider.INSTANCE);
-        } catch (WebApplicationException | IOException e) {
-        }
+
         assertNotNull(jsonOutput);
 
         verifyJsonOutput(jsonOutput);
@@ -88,167 +166,84 @@ public class CnSnToJsonBasicDataTypesTest extends YangAndXmlAndDataSchemaLoader
 
     private void jsonReadContElements(JsonReader jReader) throws IOException {
         jReader.beginObject();
-        List<String> loadedLfs = new ArrayList<>();
-        boolean enumChecked = false;
-        boolean bitsChecked = false;
-        boolean lfdecimal6Checked = false;
-        boolean lfdecimal4Checked = false;
-        boolean lfdecimal3Checked = false;
-        boolean lfdecimal2Checked = false;
-        boolean lfdecimal1Checked = false;
-        boolean lfbool1Checked = false;
-        boolean lfbool2Checked = false;
-        boolean lfstrChecked = false;
-        boolean lfbinaryChecked = false;
-        boolean lfemptyChecked = false;
-        boolean lfstr1Checked = false;
-        boolean lfidentityrefChecked = false;
+
+        Map<String,LeafVerifier> expectedMap = Maps.newHashMap();
+        expectedMap.put( "lfnint8Min", new NumberVerifier( Integer.valueOf( -128 ) ) );
+        expectedMap.put( "lfnint8Max", new NumberVerifier( Integer.valueOf( 127 ) ) );
+        expectedMap.put( "lfnint16Min", new NumberVerifier( Integer.valueOf( -32768 ) ) );
+        expectedMap.put( "lfnint16Max", new NumberVerifier( Integer.valueOf( 32767 ) ) );
+        expectedMap.put( "lfnint32Min", new NumberVerifier( Integer.valueOf( -2147483648 ) ) );
+        expectedMap.put( "lfnint32Max", new NumberVerifier( Long.valueOf( 2147483647 ) ) );
+        expectedMap.put( "lfnint64Min", new NumberVerifier( Long.valueOf( -9223372036854775808L ) ) );
+        expectedMap.put( "lfnint64Max", new NumberVerifier( Long.valueOf( 9223372036854775807L ) ) );
+        expectedMap.put( "lfnuint8Max", new NumberVerifier( Integer.valueOf( 255 ) ) );
+        expectedMap.put( "lfnuint16Max", new NumberVerifier( Integer.valueOf( 65535 ) ) );
+        expectedMap.put( "lfnuint32Max", new NumberVerifier( Long.valueOf( 4294967295L ) ) );
+        expectedMap.put( "lfstr", new StringVerifier( "lfstr" ) );
+        expectedMap.put( "lfstr1", new StringVerifier( "" ) );
+        expectedMap.put( "lfbool1", new BooleanVerifier( true ) );
+        expectedMap.put( "lfbool2", new BooleanVerifier( false ) );
+        expectedMap.put( "lfbool3", new BooleanVerifier( false ) );
+        expectedMap.put( "lfdecimal1", new NumberVerifier( new Double( 43.32 ) ) );
+        expectedMap.put( "lfdecimal2", new NumberVerifier( new Double( -0.43 ) ) );
+        expectedMap.put( "lfdecimal3", new NumberVerifier( new Double( 43 ) ) );
+        expectedMap.put( "lfdecimal4", new NumberVerifier( new Double( 43E3 ) ) );
+        expectedMap.put( "lfdecimal6", new NumberVerifier( new Double( 33.12345 ) ) );
+        expectedMap.put( "lfenum", new StringVerifier( "enum3" ) );
+        expectedMap.put( "lfbits", new StringVerifier( "bit3 bit2" ) );
+        expectedMap.put( "lfbinary", new StringVerifier( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" ) );
+        expectedMap.put( "lfunion1", new StringVerifier( "324" ) );
+        expectedMap.put( "lfunion2", new StringVerifier( "33.3" ) );
+        expectedMap.put( "lfunion3", new StringVerifier( "55" ) );
+        expectedMap.put( "lfunion4", new StringVerifier( "true" ) );
+        expectedMap.put( "lfunion5", new StringVerifier( "true" ) );
+        expectedMap.put( "lfunion6", new StringVerifier( "10" ) );
+        expectedMap.put( "lfunion7", new StringVerifier( "" ) );
+        expectedMap.put( "lfunion8", new StringVerifier( "" ) );
+        expectedMap.put( "lfunion9", new StringVerifier( "" ) );
+        expectedMap.put( "lfunion10", new StringVerifier( "bt1" ) );
+        expectedMap.put( "lfunion11", new StringVerifier( "33" ) );
+        expectedMap.put( "lfunion12", new StringVerifier( "false" ) );
+        expectedMap.put( "lfunion13", new StringVerifier( "b1" ) );
+        expectedMap.put( "lfunion14", new StringVerifier( "zero" ) );
+        expectedMap.put( "lfempty", new EmptyVerifier() );
+        expectedMap.put( "identityref1", new StringVerifier( "simple-data-types:iden" ) );
 
         while (jReader.hasNext()) {
             String keyName = jReader.nextName();
-            JsonToken peek = null;
-            try {
-                peek = jReader.peek();
-            } catch (IOException e) {
-                assertTrue("Key " + keyName + " has incorrect value for specifed type", false);
-            }
+            JsonToken peek = jReader.peek();
 
-            if (keyName.startsWith("lfnint") || keyName.startsWith("lfnuint")) {
-                assertEquals("Key " + keyName + " has incorrect type", JsonToken.NUMBER, peek);
-                try {
-                    jReader.nextLong();
-                } catch (NumberFormatException e) {
-                    assertTrue("Key " + keyName + " has incorrect value - " + e.getMessage(), false);
-                }
-                loadedLfs.add(keyName.substring(3));
-            } else if (keyName.equals("identityref1")) {
-                assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
-                assertEquals("simple-data-types:iden", jReader.nextString());
-                lfidentityrefChecked = true;
-            } else if (keyName.equals("lfstr")) {
-                assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
-                assertEquals("lfstr", jReader.nextString());
-                lfstrChecked = true;
-            } else if (keyName.equals("lfstr1")) {
-                assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
-                assertEquals("", jReader.nextString());
-                lfstr1Checked = true;
-            } else if (keyName.equals("lfbool1")) {
-                assertEquals("Key " + keyName + " has incorrect type", JsonToken.BOOLEAN, peek);
-                assertEquals(true, jReader.nextBoolean());
-                lfbool1Checked = true;
-            } else if (keyName.equals("lfbool2")) {
-                assertEquals("Key " + keyName + " has incorrect type", JsonToken.BOOLEAN, peek);
-                assertEquals(false, jReader.nextBoolean());
-                lfbool2Checked = true;
-            } else if (keyName.equals("lfbool3")) {
-                assertEquals("Key " + keyName + " has incorrect type", JsonToken.BOOLEAN, peek);
-                assertEquals(false, jReader.nextBoolean());
-            } else if (keyName.equals("lfdecimal1")) {
-                assertEquals("Key " + keyName + " has incorrect type", JsonToken.NUMBER, peek);
-                assertEquals(new Double(43.32), (Double) jReader.nextDouble());
-                lfdecimal1Checked = true;
-            } else if (keyName.equals("lfdecimal2")) {
-                assertEquals("Key " + keyName + " has incorrect type", JsonToken.NUMBER, peek);
-                assertEquals(new Double(-0.43), (Double) jReader.nextDouble());
-                lfdecimal2Checked = true;
-            } else if (keyName.equals("lfdecimal3")) {
-                assertEquals("Key " + keyName + " has incorrect type", JsonToken.NUMBER, peek);
-                assertEquals(new Double(43), (Double) jReader.nextDouble());
-                lfdecimal3Checked = true;
-            } else if (keyName.equals("lfdecimal4")) {
-                assertEquals("Key " + keyName + " has incorrect type", JsonToken.NUMBER, peek);
-                assertEquals(new Double(43E3), (Double) jReader.nextDouble());
-                lfdecimal4Checked = true;
-            } else if (keyName.equals("lfdecimal6")) {
-                assertEquals("Key " + keyName + " has incorrect type", JsonToken.NUMBER, peek);
-                assertEquals(new Double(33.12345), (Double) jReader.nextDouble());
-                lfdecimal6Checked = true;
-            } else if (keyName.equals("lfenum")) {
-                assertEquals("enum3", jReader.nextString());
-                enumChecked = true;
-            } else if (keyName.equals("lfbits")) {
-                assertEquals("bit3 bit2", jReader.nextString());
-                bitsChecked = true;
-            } else if (keyName.equals("lfbinary")) {
-                assertEquals("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", jReader.nextString());
-                lfbinaryChecked = true;
-            } else if (keyName.equals("lfempty")) {
-                jReader.beginArray();
-                jReader.nextNull();
-                jReader.endArray();
-                lfemptyChecked = true;
-            } else if (keyName.startsWith("lfunion")) {
-                checkLfUnion(jReader, keyName, peek);
-            } else {
-                assertTrue("Key " + keyName + " doesn't exists in yang file.", false);
+            LeafVerifier verifier = expectedMap.remove( keyName );
+            assertNotNull( "Found unexpected leaf: " + keyName , verifier );
+
+            JsonToken expToken = verifier.expectedTokenType();
+            if( expToken != null ) {
+                assertEquals( "Json token type for key " + keyName, expToken, peek );
             }
 
+            verifier.verify( jReader, keyName );;
+        }
+
+        if( !expectedMap.isEmpty() ) {
+            fail( "Missing leaf nodes in Json output: " +expectedMap.keySet() );
         }
-        Collections.sort(loadedLfs);
-        String expectedLfsStr = "[int16Max, int16Min, int32Max, int32Min, int64Max, int64Min, int8Max, int8Min, uint16Max, uint32Max, uint8Max]";
-        String actualLfsStr = loadedLfs.toString();
-        assertEquals("Some leaves are missing", expectedLfsStr, actualLfsStr);
-        assertTrue("Enum wasn't checked", enumChecked);
-        assertTrue("Bits wasn't checked", bitsChecked);
-        assertTrue("Decimal1 wasn't checked", lfdecimal1Checked);
-        assertTrue("Decimal2 wasn't checked", lfdecimal2Checked);
-        assertTrue("Decimal3 wasn't checked", lfdecimal3Checked);
-        assertTrue("Decimal4 wasn't checked", lfdecimal4Checked);
-        assertTrue("Decimal5 wasn't checked", lfdecimal6Checked);
-        assertTrue("lfbool1 wasn't checked", lfbool1Checked);
-        assertTrue("lfbool2 wasn't checked", lfbool2Checked);
-        assertTrue("lfstr wasn't checked", lfstrChecked);
-        assertTrue("lfstr1 wasn't checked", lfstr1Checked);
-        assertTrue("lfbinary wasn't checked", lfbinaryChecked);
-        assertTrue("lfempty wasn't checked", lfemptyChecked);
-        assertTrue("lfidentityref wasn't checked", lfidentityrefChecked);
+
         jReader.endObject();
     }
 
-    private void checkLfUnion(JsonReader jReader, String keyName, JsonToken peek) throws IOException {
-        if (keyName.equals("lfunion1")) {
-            assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
-            assertEquals("324", jReader.nextString());
-        } else if (keyName.equals("lfunion2")) {
-            assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
-            assertEquals("33.3", jReader.nextString());
-        } else if (keyName.equals("lfunion3")) {
-            assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
-            assertEquals("55", jReader.nextString());
-        } else if (keyName.equals("lfunion4")) {
-            assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
-            assertEquals("true", jReader.nextString());
-        } else if (keyName.equals("lfunion5")) {
-            assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
-            assertEquals("true", jReader.nextString());
-        } else if (keyName.equals("lfunion6")) {
-            assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
-            assertEquals("false", jReader.nextString());
-        } else if (keyName.equals("lfunion7")) {
-            assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
-            assertEquals("", jReader.nextString());
-        } else if (keyName.equals("lfunion8")) {
-            assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
-            assertEquals("", jReader.nextString());
-        } else if (keyName.equals("lfunion9")) {
-            assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
-            assertEquals("", jReader.nextString());
-        } else if (keyName.equals("lfunion10")) {
-            assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
-            assertEquals("bt1", jReader.nextString());
-        } else if (keyName.equals("lfunion11")) {
-            assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
-            assertEquals("33", jReader.nextString());
-        } else if (keyName.equals("lfunion12")) {
-            assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
-            assertEquals("false", jReader.nextString());
-        } else if (keyName.equals("lfunion13")) {
-            assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
-            assertEquals("44", jReader.nextString());
-        } else if (keyName.equals("lfunion14")) {
-            assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
-            assertEquals("21", jReader.nextString());
+    @Test
+    public void testBadData() throws Exception {
+
+        try {
+            CompositeNode compositeNode = TestUtils.readInputToCnSn(
+                                               "/cnsn-to-json/simple-data-types/xml/bad-data.xml",
+                                               XmlToCompositeNodeProvider.INSTANCE);
+
+            TestUtils.normalizeCompositeNode(compositeNode, modules, "simple-data-types:cont");
+            fail( "Expected RestconfDocumentedException" );
+        }
+        catch( RestconfDocumentedException e ) {
+            assertEquals( "getErrorTag", ErrorTag.INVALID_VALUE, e.getErrors().get( 0 ).getErrorTag() );
         }
     }
 }
index fc54795fcce5a468cb0d6e780f561a3052fe2eeb..9318af529b74c6ec4097331f6e374b7aa7038e9b 100644 (file)
@@ -9,8 +9,11 @@ package org.opendaylight.controller.sal.restconf.impl.cnsn.to.xml.test;
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import java.io.IOException;
+import java.util.List;
 
 import javax.ws.rs.WebApplicationException;
 import javax.xml.transform.TransformerFactoryConfigurationError;
@@ -26,6 +29,29 @@ import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
 import org.opendaylight.yangtools.yang.data.api.MutableSimpleNode;
 import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
 import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
+import org.opendaylight.yangtools.yang.model.util.BinaryType;
+import org.opendaylight.yangtools.yang.model.util.BitsType;
+import org.opendaylight.yangtools.yang.model.util.BooleanType;
+import org.opendaylight.yangtools.yang.model.util.EmptyType;
+import org.opendaylight.yangtools.yang.model.util.EnumerationType;
+import org.opendaylight.yangtools.yang.model.util.Int16;
+import org.opendaylight.yangtools.yang.model.util.Int32;
+import org.opendaylight.yangtools.yang.model.util.Int64;
+import org.opendaylight.yangtools.yang.model.util.Int8;
+import org.opendaylight.yangtools.yang.model.util.StringType;
+import org.opendaylight.yangtools.yang.model.util.Uint16;
+import org.opendaylight.yangtools.yang.model.util.Uint32;
+import org.opendaylight.yangtools.yang.model.util.Uint64;
+import org.opendaylight.yangtools.yang.model.util.Uint8;
+import org.opendaylight.yangtools.yang.model.util.UnionType;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
 
 /**
  *
@@ -65,7 +91,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader {
     @Test
     public void snAsYangStringToXmlTest() {
         serializeToXml(
-                prepareCnStructForYangData(TypeDefinitionAwareCodec.STRING_DEFAULT_CODEC.deserialize("lfStr value"),
+                prepareCnStructForYangData(TypeDefinitionAwareCodec.from(StringType.getInstance()).deserialize("lfStr value"),
                         "lfStr"), "<lfStr>lfStr value</lfStr>");
     }
 
@@ -73,7 +99,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader {
     public void snAsYangInt8ToXmlTest() {
         String elName = "lfInt8";
         serializeToXml(
-                prepareCnStructForYangData(TypeDefinitionAwareCodec.INT8_DEFAULT_CODEC.deserialize("14"), elName), "<"
+                prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Int8.getInstance()).deserialize("14"), elName), "<"
                         + elName + ">14</" + elName + ">");
     }
 
@@ -81,7 +107,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader {
     public void snAsYangInt16ToXmlTest() {
         String elName = "lfInt16";
         serializeToXml(
-                prepareCnStructForYangData(TypeDefinitionAwareCodec.INT16_DEFAULT_CODEC.deserialize("3000"), elName),
+                prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Int16.getInstance()).deserialize("3000"), elName),
                 "<" + elName + ">3000</" + elName + ">");
     }
 
@@ -89,7 +115,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader {
     public void snAsYangInt32ToXmlTest() {
         String elName = "lfInt32";
         serializeToXml(
-                prepareCnStructForYangData(TypeDefinitionAwareCodec.INT32_DEFAULT_CODEC.deserialize("201234"), elName),
+                prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Int32.getInstance()).deserialize("201234"), elName),
                 "<" + elName + ">201234</" + elName + ">");
     }
 
@@ -97,7 +123,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader {
     public void snAsYangInt64ToXmlTest() {
         String elName = "lfInt64";
         serializeToXml(
-                prepareCnStructForYangData(TypeDefinitionAwareCodec.INT64_DEFAULT_CODEC.deserialize("5123456789"),
+                prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Int64.getInstance()).deserialize("5123456789"),
                         elName), "<" + elName + ">5123456789</" + elName + ">");
     }
 
@@ -105,7 +131,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader {
     public void snAsYangUint8ToXmlTest() {
         String elName = "lfUint8";
         serializeToXml(
-                prepareCnStructForYangData(TypeDefinitionAwareCodec.UINT8_DEFAULT_CODEC.deserialize("200"), elName),
+                prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Uint8.getInstance()).deserialize("200"), elName),
                 "<" + elName + ">200</" + elName + ">");
     }
 
@@ -113,7 +139,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader {
     public void snAsYangUint16ToXmlTest() {
         String elName = "lfUint16";
         serializeToXml(
-                prepareCnStructForYangData(TypeDefinitionAwareCodec.UINT16_DEFAULT_CODEC.deserialize("4000"), elName),
+                prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Uint16.getInstance()).deserialize("4000"), elName),
                 "<" + elName + ">4000</" + elName + ">");
     }
 
@@ -121,7 +147,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader {
     public void snAsYangUint32ToXmlTest() {
         String elName = "lfUint32";
         serializeToXml(
-                prepareCnStructForYangData(TypeDefinitionAwareCodec.UINT32_DEFAULT_CODEC.deserialize("4123456789"),
+                prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Uint32.getInstance()).deserialize("4123456789"),
                         elName), "<" + elName + ">4123456789</" + elName + ">");
     }
 
@@ -129,7 +155,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader {
     public void snAsYangUint64ToXmlTest() {
         String elName = "lfUint64";
         serializeToXml(
-                prepareCnStructForYangData(TypeDefinitionAwareCodec.UINT64_DEFAULT_CODEC.deserialize("5123456789"),
+                prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Uint64.getInstance()).deserialize("5123456789"),
                         elName), "<" + elName + ">5123456789</" + elName + ">");
     }
 
@@ -138,7 +164,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader {
         String elName = "lfBinary";
         serializeToXml(
                 prepareCnStructForYangData(
-                        TypeDefinitionAwareCodec.BINARY_DEFAULT_CODEC
+                        TypeDefinitionAwareCodec.from(BinaryType.getInstance())
                         .deserialize("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567"),
                         elName), "<" + elName + ">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567</"
                                 + elName + ">");
@@ -146,25 +172,40 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader {
 
     @Test
     public void snAsYangBitsToXmlTest() {
+        BitsTypeDefinition.Bit mockBit1 = mock( BitsTypeDefinition.Bit.class );
+        when( mockBit1.getName() ).thenReturn( "one" );
+        BitsTypeDefinition.Bit mockBit2 = mock( BitsTypeDefinition.Bit.class );
+        when( mockBit2.getName() ).thenReturn( "two" );
+        List<BitsTypeDefinition.Bit> bitList = Lists.newArrayList( mockBit1, mockBit2 );
+
         String elName = "lfBits";
         serializeToXml(
-                prepareCnStructForYangData(TypeDefinitionAwareCodec.BITS_DEFAULT_CODEC.deserialize("one two"), elName),
-                "<" + elName + ">one two</" + elName + ">", "<" + elName + ">two one</" + elName + ">");
+                prepareCnStructForYangData(TypeDefinitionAwareCodec.from(
+                        BitsType.create( mock( SchemaPath.class ), bitList ) )
+                        .deserialize("one two"), elName),
+                        "<" + elName + ">one two</" + elName + ">", "<" + elName + ">two one</" + elName + ">");
     }
 
     @Test
     public void snAsYangEnumerationToXmlTest() {
+        EnumTypeDefinition.EnumPair mockEnum = mock( EnumTypeDefinition.EnumPair.class );
+        when( mockEnum.getName() ).thenReturn( "enum2" );
+        List<EnumPair> enumList = Lists.newArrayList( mockEnum );
+
         String elName = "lfEnumeration";
         serializeToXml(
-                prepareCnStructForYangData(TypeDefinitionAwareCodec.ENUMERATION_DEFAULT_CODEC.deserialize("enum2"),
-                        elName), "<" + elName + ">enum2</" + elName + ">");
+                prepareCnStructForYangData(TypeDefinitionAwareCodec.from(
+                        EnumerationType.create( mock( SchemaPath.class ), enumList,
+                                Optional.<EnumTypeDefinition.EnumPair>absent() ) )
+                                .deserialize("enum2"),
+                                elName), "<" + elName + ">enum2</" + elName + ">");
     }
 
     @Test
     public void snAsYangEmptyToXmlTest() {
         String elName = "lfEmpty";
         serializeToXml(
-                prepareCnStructForYangData(TypeDefinitionAwareCodec.EMPTY_DEFAULT_CODEC.deserialize(null), elName), "<"
+                prepareCnStructForYangData(TypeDefinitionAwareCodec.from(EmptyType.getInstance()).deserialize(null), elName), "<"
                         + elName + "/>");
     }
 
@@ -172,29 +213,42 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader {
     public void snAsYangBooleanToXmlTest() {
         String elName = "lfBoolean";
         serializeToXml(
-                prepareCnStructForYangData(TypeDefinitionAwareCodec.BOOLEAN_DEFAULT_CODEC.deserialize("str"), elName),
+                prepareCnStructForYangData(TypeDefinitionAwareCodec.from(BooleanType.getInstance()).deserialize("str"), elName),
                 "<" + elName + ">false</" + elName + ">");
         serializeToXml(
-                prepareCnStructForYangData(TypeDefinitionAwareCodec.BOOLEAN_DEFAULT_CODEC.deserialize("true"), elName),
+                prepareCnStructForYangData(TypeDefinitionAwareCodec.from(BooleanType.getInstance()).deserialize("true"), elName),
                 "<" + elName + ">true</" + elName + ">");
     }
 
     @Test
     public void snAsYangUnionToXmlTest() {
+
+        BitsTypeDefinition.Bit mockBit1 = mock( BitsTypeDefinition.Bit.class );
+        when( mockBit1.getName() ).thenReturn( "first" );
+        BitsTypeDefinition.Bit mockBit2 = mock( BitsTypeDefinition.Bit.class );
+        when( mockBit1.getName() ).thenReturn( "second" );
+        List<BitsTypeDefinition.Bit> bitList = Lists.newArrayList( mockBit1, mockBit2 );
+
+        List<TypeDefinition<?>> types = Lists.<TypeDefinition<?>>newArrayList(
+                Int8.getInstance(),
+                BitsType.create( mock( SchemaPath.class ) , bitList ),
+                BooleanType.getInstance() );
+        UnionType unionType = UnionType.create( types );
+
         String elName = "lfUnion";
         String int8 = "15";
         serializeToXml(
-                prepareCnStructForYangData(TypeDefinitionAwareCodec.UNION_DEFAULT_CODEC.deserialize(int8), elName), "<"
+                prepareCnStructForYangData(TypeDefinitionAwareCodec.from(unionType).deserialize(int8), elName), "<"
                         + elName + ">15</" + elName + ">");
 
         String bits = "first second";
         serializeToXml(
-                prepareCnStructForYangData(TypeDefinitionAwareCodec.UNION_DEFAULT_CODEC.deserialize(bits), elName), "<"
+                prepareCnStructForYangData(TypeDefinitionAwareCodec.from(unionType).deserialize(bits), elName), "<"
                         + elName + ">first second</" + elName + ">");
 
         String bool = "str";
         serializeToXml(
-                prepareCnStructForYangData(TypeDefinitionAwareCodec.UNION_DEFAULT_CODEC.deserialize(bool), elName), "<"
+                prepareCnStructForYangData(TypeDefinitionAwareCodec.from(unionType).deserialize(bool), elName), "<"
                         + elName + ">str</" + elName + ">");
     }
 
@@ -243,11 +297,11 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader {
     }
 
     private CompositeNode prepareCnStructForYangData(final Object data, final String leafName) {
-        MutableCompositeNode cont = NodeFactory.createMutableCompositeNode(TestUtils.buildQName("cont"), null, null,
-                ModifyAction.CREATE, null);
+        MutableCompositeNode cont = NodeFactory.createMutableCompositeNode(
+                TestUtils.buildQName("cont", "basic:module", "2013-12-2"), null, null, ModifyAction.CREATE, null);
 
-        MutableSimpleNode<Object> lf1 = NodeFactory.createMutableSimpleNode(TestUtils.buildQName(leafName), cont, data,
-                ModifyAction.CREATE, null);
+        MutableSimpleNode<Object> lf1 = NodeFactory.createMutableSimpleNode(
+                TestUtils.buildQName(leafName, "basic:module", "2013-12-2"), cont, data, ModifyAction.CREATE, null);
         cont.getValue().add(lf1);
         cont.init();
 
index 3c70cca0f87806d28b73273423fa7b546b1631c6..47e329cc3ef11a4cdfc8842c9a1285a445989517 100644 (file)
@@ -9,8 +9,11 @@ package org.opendaylight.controller.sal.restconf.impl.json.to.cnsn.test;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -176,6 +179,14 @@ public class JsonToCnSnTest {
 
     }
 
+    @Test
+    public void testJsonBlankInput() throws Exception{
+        InputStream inputStream = new ByteArrayInputStream( "".getBytes() );
+        CompositeNode compositeNode =
+                JsonToCompositeNodeProvider.INSTANCE.readFrom(null, null, null, null, null, inputStream);
+        assertNull( compositeNode );
+    }
+
     /**
      * Tests whether namespace <b>stay unchanged</b> if concrete values are
      * present in composite or simple node and if the method for update is
index 51687e2a1285601e4efff6c3737001a9f12488f3..307abebdd7b1a37ebfb3cc1d716048614c3e44cc 100644 (file)
@@ -56,6 +56,6 @@ public class CodecsExceptionsCatchingTest extends JerseyTest {
         Response response = target("/config/number:cont").request(MediaType.APPLICATION_XML).put(
                 Entity.entity("<cont xmlns=\"number\"><lf>3f</lf></cont>", MediaType.APPLICATION_XML));
         String exceptionMessage = response.readEntity(String.class);
-        assertTrue(exceptionMessage.contains("Incorrect lexical representation of Integer value: 3f"));
+        assertTrue(exceptionMessage.contains("invalid-value"));
     }
 }
\ No newline at end of file
index c0c86c3f25384fdbd3ea979debef62fa7f1512ab..910ca8e20aab453d02b4e320b0493775666d3575 100644 (file)
@@ -308,7 +308,7 @@ public class InvokeRpcMethodTest {
         ListenableFuture<RpcResult<CompositeNode>> mockListener = mock( ListenableFuture.class );
         when( mockListener.get() ).thenReturn( rpcResult );
 
-        QName cancelToastQName = QName.create( "cancelToast" );
+        QName cancelToastQName = QName.create( "namespace", "2014-05-28", "cancelToast" );
 
         RpcDefinition mockRpc = mock( RpcDefinition.class );
         when( mockRpc.getQName() ).thenReturn( cancelToastQName );
index ce460fe4746d9137f0775f6536297e5cc841ab44..cfbc9fdb767c2c9c8fb8bc7e8d4954f5e5e210e8 100644 (file)
@@ -149,6 +149,8 @@ public class RestPostOperationTest extends JerseyTest {
 
         mockCommitConfigurationDataPostMethod(TransactionStatus.FAILED);
         assertEquals(500, post(uri, MediaType.APPLICATION_XML, xmlDataInterfaceAbsolutePath));
+
+        assertEquals( 400, post(uri, MediaType.APPLICATION_JSON, "" ));
     }
 
     @Test
@@ -172,6 +174,8 @@ public class RestPostOperationTest extends JerseyTest {
         assertEquals(204, post(uri, Draft02.MediaTypes.DATA + XML, xmlData4));
         uri = "/config/ietf-interfaces:interfaces/interface/0/yang-ext:mount/test-module:cont";
         assertEquals(204, post(uri, Draft02.MediaTypes.DATA + XML, xmlData3));
+
+        assertEquals( 400, post(uri, MediaType.APPLICATION_JSON, "" ));
     }
 
     private void mockInvokeRpc(CompositeNode result, boolean sucessful) {
index 3af2945526466fabf3e9c139b79eb4134107752f..77b39b73529dca04ee8b6279b824c01a4361bf25 100644 (file)
@@ -22,6 +22,7 @@ import java.util.concurrent.Future;
 import javax.ws.rs.client.Entity;
 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;
@@ -31,6 +32,7 @@ import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.sal.core.api.mount.MountInstance;
 import org.opendaylight.controller.sal.core.api.mount.MountService;
 import org.opendaylight.controller.sal.rest.impl.JsonToCompositeNodeProvider;
+import org.opendaylight.controller.sal.rest.impl.RestconfDocumentedExceptionMapper;
 import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider;
 import org.opendaylight.controller.sal.rest.impl.StructuredDataToXmlProvider;
 import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider;
@@ -86,6 +88,7 @@ public class RestPutOperationTest extends JerseyTest {
         resourceConfig = resourceConfig.registerInstances(restconfImpl, StructuredDataToXmlProvider.INSTANCE,
                 StructuredDataToJsonProvider.INSTANCE, XmlToCompositeNodeProvider.INSTANCE,
                 JsonToCompositeNodeProvider.INSTANCE);
+        resourceConfig.registerClasses( RestconfDocumentedExceptionMapper.class );
         return resourceConfig;
     }
 
@@ -100,6 +103,15 @@ public class RestPutOperationTest extends JerseyTest {
 
         mockCommitConfigurationDataPutMethod(TransactionStatus.FAILED);
         assertEquals(500, put(uri, MediaType.APPLICATION_XML, xmlData));
+
+        assertEquals( 400, put(uri, MediaType.APPLICATION_JSON, "" ));
+    }
+
+    @Test
+    public void putConfigStatusCodesEmptyBody() throws UnsupportedEncodingException {
+        String uri = "/config/ietf-interfaces:interfaces/interface/eth0";
+        Response resp = target(uri).request( MediaType.APPLICATION_JSON).put(Entity.entity( "", MediaType.APPLICATION_JSON));
+        assertEquals( 400, put(uri, MediaType.APPLICATION_JSON, "" ));
     }
 
     @Test
index 5008d28bbfb26ea0e8d9ef8ab2b1814e8736671d..5cda4a7f52014dc4a9ab3572d9073877ec12e74e 100644 (file)
@@ -9,8 +9,12 @@ package org.opendaylight.controller.sal.restconf.impl.xml.to.cnsn.test;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider;
@@ -52,4 +56,27 @@ public class XmlToCnSnTest extends YangAndXmlAndDataSchemaLoader {
         assertEquals("121", lf2.getValue());
     }
 
+    @Test
+    public void testXmlBlankInput() throws Exception{
+        InputStream inputStream = new ByteArrayInputStream( "".getBytes() );
+        CompositeNode compositeNode =
+                XmlToCompositeNodeProvider.INSTANCE.readFrom(null, null, null, null, null, inputStream);
+
+        assertNull( compositeNode );
+    }
+
+    @Test
+    public void testXmlBlankInputUnmarkableStream() throws Exception{
+        InputStream inputStream = new ByteArrayInputStream( "".getBytes() ){
+            @Override
+            public boolean markSupported() {
+                return false;
+            }
+        };
+        CompositeNode compositeNode =
+                XmlToCompositeNodeProvider.INSTANCE.readFrom(null, null, null, null, null, inputStream);
+
+        assertNull( compositeNode );
+    }
+
 }
diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/cnsn-to-json/simple-data-types/xml/bad-data.xml b/opendaylight/md-sal/sal-rest-connector/src/test/resources/cnsn-to-json/simple-data-types/xml/bad-data.xml
new file mode 100644 (file)
index 0000000..110c323
--- /dev/null
@@ -0,0 +1,3 @@
+<cont>
+    <lfnint8Min>invalid</lfnint8Min>
+</cont>
\ No newline at end of file
index 56872a337d0124be4789789ef8f58427ab378278..f73ce1b65c38a154a8ae2067318c1b261e012c36 100644 (file)
        <lfunion3>55</lfunion3>
        <lfunion4>true</lfunion4>
        <lfunion5>true</lfunion5>
-       <lfunion6>false</lfunion6>
+       <lfunion6>10</lfunion6>
        <lfunion7></lfunion7>
        <lfunion8></lfunion8>
        <lfunion9></lfunion9>
        <lfunion10>bt1</lfunion10>
        <lfunion11>33</lfunion11>
        <lfunion12>false</lfunion12>
-       <lfunion13>44</lfunion13>
-       <lfunion14>21</lfunion14>
-       <lfempty />
+       <lfunion13>b1</lfunion13>
+       <lfunion14>zero</lfunion14>
        <identityref1 xmlns:x="simple:data:types">x:iden</identityref1>
 </cont>
\ No newline at end of file
index dbcbab982a9aec997e37a2fb09e763bb3f3c5f96..7d9cc7ecbd789634e96ddbf32153e534d594dbc4 100644 (file)
@@ -313,6 +313,9 @@ public final class NodeStatisticsHandler implements AutoCloseable, FlowCapableCo
         meterStats.close();
         queueStats.close();
 
+        //Clean up queued statistics request from scheduler queue 
+        srScheduler.removeRequestsFromSchedulerQueue(this.getNodeRef());
+
         logger.debug("Statistics handler for {} shut down", targetNodeKey.getId());
     }
 
index 9ebfd6fd62f7b2ab8933d86a03a788b3154d2eb5..bea43ef68a05c1000d7a7d904d0c36f6fccc1b4a 100644 (file)
@@ -18,6 +18,7 @@ import java.util.concurrent.TimeUnit;
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
 import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction.DataTransactionListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -62,6 +63,19 @@ public class StatisticsRequestScheduler implements DataTransactionListener {
         requestQueue.put(statsRequest, null);
     }
     
+    public void removeRequestsFromSchedulerQueue(NodeRef node){
+        AbstractStatsTracker stats = null;
+        synchronized(requestQueue){
+            Iterator<Map.Entry<AbstractStatsTracker, Integer>> nodesItr = requestQueue.entrySet().iterator();
+            while(nodesItr.hasNext()){
+                stats = nodesItr.next().getKey();
+                if(stats.getNodeRef().equals(node)){
+                    nodesItr.remove();
+                }
+            }
+        }
+
+    }
     public AbstractStatsTracker getNextRequestFromSchedulerQueue(){
         //Remove first element
         AbstractStatsTracker stats = null;
@@ -79,10 +93,7 @@ public class StatisticsRequestScheduler implements DataTransactionListener {
 
     private void requestStatistics(){
         AbstractStatsTracker stats = this.getNextRequestFromSchedulerQueue();
-        if(stats != null) {
-            stats.request();
-            stats.increaseRequestCounter();
-        }
+        sendStatsRequest(stats);
     }
     @Override
     public void onStatusUpdated(DataModificationTransaction transaction, TransactionStatus status) {
@@ -106,12 +117,19 @@ public class StatisticsRequestScheduler implements DataTransactionListener {
                 break;
             }
         }
+        sendStatsRequest(stats);
+    }
+    
+    private void sendStatsRequest(AbstractStatsTracker stats){
         if(stats != null){
-            stats.request();
-            stats.increaseRequestCounter();
+            try{
+                stats.request();
+                stats.increaseRequestCounter();
+            }catch(Exception e){
+                srsLogger.warn("Statistics request was not sent successfully. Reason : {}",e.getMessage());
+            }
         }
     }
-    
     public void start(){
         timer.schedule(task, 0, REQUEST_MONITOR_INTERVAL);
     }
index 78a2043e202413786f1000954c10289ce5395696..22a3f105477fbc300739a579b17767a640660406 100644 (file)
@@ -8,7 +8,17 @@
 
 package org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig;
 
+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.mock;
+import static org.mockito.Mockito.verify;
+
 import com.google.common.collect.Sets;
+import java.util.Map;
+import javax.management.Attribute;
+import javax.management.ObjectName;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -16,17 +26,6 @@ import org.mockito.MockitoAnnotations;
 import org.opendaylight.controller.config.util.ConfigTransactionClient;
 import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
 
-import javax.management.Attribute;
-import javax.management.ObjectName;
-import java.util.Map;
-
-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.mock;
-import static org.mockito.Mockito.verify;
-
 public class ReplaceEditConfigStrategyTest {
 
     @Mock
@@ -35,7 +34,7 @@ public class ReplaceEditConfigStrategyTest {
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        doNothing().when(ta).destroyConfigBean(anyString(), anyString());
+        doNothing().when(ta).destroyModule(anyString(), anyString());
         doReturn(mockON()).when(ta).lookupConfigBean(anyString(), anyString());
         doNothing().when(ta).setAttribute(any(ObjectName.class), anyString(), any(Attribute.class));
     }
index 799674487f4de5d936067b51b3f9ae392caee897..829ac304bd667f680d813e32ff58ed9f9b0b6f37 100644 (file)
@@ -7,7 +7,7 @@
  */
 package org.opendaylight.controller.netconf.client;
 
-import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.Channel;
 import io.netty.util.concurrent.Promise;
 import java.io.IOException;
 import org.opendaylight.controller.netconf.nettyutil.AbstractChannelInitializer;
@@ -31,7 +31,7 @@ final class SshClientChannelInitializer extends AbstractChannelInitializer<Netco
     }
 
     @Override
-    public void initialize(final SocketChannel ch, final Promise<NetconfClientSession> promise) {
+    public void initialize(final Channel ch, final Promise<NetconfClientSession> promise) {
         try {
             final Invoker invoker = Invoker.subsystem("netconf");
             ch.pipeline().addFirst(new SshHandler(authenticationHandler, invoker));
@@ -42,7 +42,7 @@ final class SshClientChannelInitializer extends AbstractChannelInitializer<Netco
     }
 
     @Override
-    protected void initializeSessionNegotiator(final SocketChannel ch,
+    protected void initializeSessionNegotiator(final Channel ch,
                                                final Promise<NetconfClientSession> promise) {
         ch.pipeline().addAfter(NETCONF_MESSAGE_DECODER,  AbstractChannelInitializer.NETCONF_SESSION_NEGOTIATOR,
                 negotiatorFactory.getSessionNegotiator(new SessionListenerFactory<NetconfClientSessionListener>() {
index 4a0a089fae97e1ff3d1c8b969f6da077e3584cb7..ee8f8baf0151c4cdeb820878ca9c440e60c9b64b 100644 (file)
@@ -7,7 +7,7 @@
  */
 package org.opendaylight.controller.netconf.client;
 
-import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.Channel;
 import io.netty.util.concurrent.Promise;
 import org.opendaylight.controller.netconf.nettyutil.AbstractChannelInitializer;
 import org.opendaylight.protocol.framework.SessionListenerFactory;
@@ -24,12 +24,7 @@ class TcpClientChannelInitializer extends AbstractChannelInitializer<NetconfClie
     }
 
     @Override
-    public void initialize(final SocketChannel ch, final Promise<NetconfClientSession> promise) {
-        super.initialize(ch, promise);
-    }
-
-    @Override
-    protected void initializeSessionNegotiator(final SocketChannel ch, final Promise<NetconfClientSession> promise) {
+    protected void initializeSessionNegotiator(final Channel ch, final Promise<NetconfClientSession> promise) {
         ch.pipeline().addAfter(NETCONF_MESSAGE_DECODER, AbstractChannelInitializer.NETCONF_SESSION_NEGOTIATOR,
                 negotiatorFactory.getSessionNegotiator(new SessionListenerFactory<NetconfClientSessionListener>() {
                     @Override
index 60d8f3044aee5667ea04858377d31b109ca88ef0..afa17532d55ce6628b1659db90e666acf1e97250 100644 (file)
@@ -8,24 +8,35 @@
 
 package org.opendaylight.controller.netconf.client.test;
 
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.GlobalEventExecutor;
 import java.io.Closeable;
 import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
 import java.util.Set;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
-
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl;
 import org.opendaylight.controller.netconf.client.NetconfClientSession;
 import org.opendaylight.controller.netconf.client.NetconfClientSessionListener;
 import org.opendaylight.controller.netconf.client.SimpleNetconfClientSessionListener;
 import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Sets;
-import io.netty.util.concurrent.Future;
+import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration.NetconfClientProtocol;
+import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder;
+import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
+import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
+import org.opendaylight.protocol.framework.NeverReconnectStrategy;
 
 
 /**
@@ -95,4 +106,29 @@ public class TestingNetconfClient implements Closeable {
         Preconditions.checkState(clientSession != null, "Client was not initialized successfully");
         return Sets.newHashSet(clientSession.getServerCapabilities());
     }
+
+    public static void main(String[] args) throws Exception {
+        HashedWheelTimer hashedWheelTimer = new HashedWheelTimer();
+        NioEventLoopGroup nettyGroup = new NioEventLoopGroup();
+        NetconfClientDispatcherImpl netconfClientDispatcher = new NetconfClientDispatcherImpl(nettyGroup, nettyGroup, hashedWheelTimer);
+        LoginPassword authHandler = new LoginPassword("admin", "admin");
+        TestingNetconfClient client = new TestingNetconfClient("client", netconfClientDispatcher, getClientConfig("127.0.0.1", 1830, true, Optional.of(authHandler)));
+        System.out.println(client.getCapabilities());
+    }
+
+    private static NetconfClientConfiguration getClientConfig(String host ,int port, boolean ssh, Optional<? extends AuthenticationHandler> maybeAuthHandler) throws UnknownHostException {
+        InetSocketAddress netconfAddress = new InetSocketAddress(InetAddress.getByName(host), port);
+        final NetconfClientConfigurationBuilder b = NetconfClientConfigurationBuilder.create();
+        b.withAddress(netconfAddress);
+        b.withSessionListener(new SimpleNetconfClientSessionListener());
+        b.withReconnectStrategy(new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE,
+                NetconfClientConfigurationBuilder.DEFAULT_CONNECTION_TIMEOUT_MILLIS));
+        if (ssh) {
+            b.withProtocol(NetconfClientProtocol.SSH);
+            b.withAuthHandler(maybeAuthHandler.get());
+        } else {
+            b.withProtocol(NetconfClientProtocol.TCP);
+        }
+        return b.build();
+    }
 }
index 1d94517152f45215cc7481a963a290a2870640d2..c60506ef447d821d5458dbb399c30a1ab0e94325 100644 (file)
       <plugin>
         <groupId>org.apache.felix</groupId>
         <artifactId>maven-bundle-plugin</artifactId>
+        <version>2.3.7</version>
         <configuration>
           <instructions>
             <Bundle-Activator>org.opendaylight.controller.netconf.impl.osgi.NetconfImplActivator</Bundle-Activator>
                             io.netty.buffer,
                             io.netty.handler.codec,
                             io.netty.channel.nio,
+                            io.netty.channel.local,
                             javax.annotation,
                             javax.management,
                             javax.net.ssl,
index de3dee14437b804fd18819bb20302a962fd213e2..4dfb7498184af4e7a71f7c40744821e258e00966 100644 (file)
@@ -8,8 +8,13 @@
 
 package org.opendaylight.controller.netconf.impl;
 
+import com.google.common.annotations.VisibleForTesting;
+import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.EventLoopGroup;
+import io.netty.channel.local.LocalAddress;
+import io.netty.channel.local.LocalChannel;
+import io.netty.channel.local.LocalServerChannel;
 import io.netty.channel.socket.SocketChannel;
 import io.netty.util.concurrent.Promise;
 import java.net.InetSocketAddress;
@@ -27,6 +32,7 @@ public class NetconfServerDispatcher extends AbstractDispatcher<NetconfServerSes
         this.initializer = serverChannelInitializer;
     }
 
+    @VisibleForTesting
     public ChannelFuture createServer(InetSocketAddress address) {
 
         return super.createServer(address, new PipelineInitializer<NetconfServerSession>() {
@@ -37,6 +43,15 @@ public class NetconfServerDispatcher extends AbstractDispatcher<NetconfServerSes
         });
     }
 
+    public ChannelFuture createLocalServer(LocalAddress address) {
+        return super.createServer(address, LocalServerChannel.class, new ChannelPipelineInitializer<LocalChannel, NetconfServerSession>() {
+            @Override
+            public void initializeChannel(final LocalChannel ch, final Promise<NetconfServerSession> promise) {
+                initializer.initialize(ch, promise);
+            }
+        });
+    }
+
     public static class ServerChannelInitializer extends AbstractChannelInitializer<NetconfServerSession> {
 
         public static final String DESERIALIZER_EX_HANDLER_KEY = "deserializerExHandler";
@@ -50,16 +65,15 @@ public class NetconfServerDispatcher extends AbstractDispatcher<NetconfServerSes
         }
 
         @Override
-        protected void initializeMessageDecoder(SocketChannel ch) {
+        protected void initializeMessageDecoder(Channel ch) {
             super.initializeMessageDecoder(ch);
             ch.pipeline().addLast(DESERIALIZER_EX_HANDLER_KEY, new DeserializerExceptionHandler());
         }
 
         @Override
-        protected void initializeSessionNegotiator(SocketChannel ch, Promise<NetconfServerSession> promise) {
+        protected void initializeSessionNegotiator(Channel ch, Promise<NetconfServerSession> promise) {
             ch.pipeline().addAfter(DESERIALIZER_EX_HANDLER_KEY, AbstractChannelInitializer.NETCONF_SESSION_NEGOTIATOR,
                     negotiatorFactory.getSessionNegotiator(null, ch, promise));
         }
     }
-
 }
index 7130dc350134578372348ce829bda08243a5303c..6ab62ef29a82037cd12e01c5f71d88e34a32a5bd 100644 (file)
@@ -7,12 +7,13 @@
  */
 package org.opendaylight.controller.netconf.impl.osgi;
 
+import io.netty.channel.local.LocalAddress;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.util.HashedWheelTimer;
 import java.lang.management.ManagementFactory;
-import java.net.InetSocketAddress;
 import java.util.Dictionary;
 import java.util.Hashtable;
 import java.util.concurrent.TimeUnit;
-
 import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
 import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
 import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher;
@@ -26,9 +27,6 @@ import org.osgi.framework.ServiceRegistration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import io.netty.channel.nio.NioEventLoopGroup;
-import io.netty.util.HashedWheelTimer;
-
 public class NetconfImplActivator implements BundleActivator {
 
     private static final Logger logger = LoggerFactory.getLogger(NetconfImplActivator.class);
@@ -40,17 +38,16 @@ public class NetconfImplActivator implements BundleActivator {
     private ServiceRegistration<NetconfMonitoringService> regMonitoring;
 
     @Override
-    public void start(final BundleContext context) {
-        final InetSocketAddress address = NetconfConfigUtil.extractTCPNetconfServerAddress(context,
-                NetconfConfigUtil.DEFAULT_NETCONF_TCP_ADDRESS);
-        final NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
+    public void start(final BundleContext context)  {
+
+        NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
         startOperationServiceFactoryTracker(context, factoriesListener);
 
-        final SessionIdProvider idProvider = new SessionIdProvider();
+        SessionIdProvider idProvider = new SessionIdProvider();
         timer = new HashedWheelTimer();
-
         long connectionTimeoutMillis = NetconfConfigUtil.extractTimeoutMillis(context);
 
+
         commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer());
 
         SessionMonitoringService monitoringService = startMonitoringService(context, factoriesListener);
@@ -62,24 +59,24 @@ public class NetconfImplActivator implements BundleActivator {
 
         NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer(
                 serverNegotiatorFactory);
+        NetconfServerDispatcher dispatch = new NetconfServerDispatcher(serverChannelInitializer, eventLoopGroup, eventLoopGroup);
 
-        NetconfServerDispatcher dispatch = new NetconfServerDispatcher(serverChannelInitializer, eventLoopGroup,
-                eventLoopGroup);
-
-        logger.info("Starting TCP netconf server at {}", address);
-        dispatch.createServer(address);
+        LocalAddress address = NetconfConfigUtil.getNetconfLocalAddress();
+        logger.trace("Starting local netconf server at {}", address);
+        dispatch.createLocalServer(address);
 
         context.registerService(NetconfOperationProvider.class, factoriesListener, null);
+
     }
 
-    private void startOperationServiceFactoryTracker(final BundleContext context, final NetconfOperationServiceFactoryListenerImpl factoriesListener) {
+    private void startOperationServiceFactoryTracker(BundleContext context, NetconfOperationServiceFactoryListenerImpl factoriesListener) {
         factoriesTracker = new NetconfOperationServiceFactoryTracker(context, factoriesListener);
         factoriesTracker.open();
     }
 
-    private NetconfMonitoringServiceImpl startMonitoringService(final BundleContext context, final NetconfOperationServiceFactoryListenerImpl factoriesListener) {
-        final NetconfMonitoringServiceImpl netconfMonitoringServiceImpl = new NetconfMonitoringServiceImpl(factoriesListener);
-        final Dictionary<String, ?> dic = new Hashtable<>();
+    private NetconfMonitoringServiceImpl startMonitoringService(BundleContext context, NetconfOperationServiceFactoryListenerImpl factoriesListener) {
+        NetconfMonitoringServiceImpl netconfMonitoringServiceImpl = new NetconfMonitoringServiceImpl(factoriesListener);
+        Dictionary<String, ?> dic = new Hashtable<>();
         regMonitoring = context.registerService(NetconfMonitoringService.class, netconfMonitoringServiceImpl, dic);
 
         return netconfMonitoringServiceImpl;
index 140284e4ee1946ab2cc579fba0cf08362440e580..0969bd92a59a7cb6132a2b3b6cb5ad713af5077e 100644 (file)
@@ -16,13 +16,13 @@ import static org.mockito.Mockito.mock;
 
 import ch.ethz.ssh2.Connection;
 import io.netty.channel.ChannelFuture;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.util.concurrent.GlobalEventExecutor;
-import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.management.ManagementFactory;
 import java.net.InetSocketAddress;
-import java.nio.file.Files;
 import java.util.Collection;
 import java.util.List;
 import junit.framework.Assert;
@@ -50,16 +50,14 @@ 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.messages.NetconfMessageUtil;
+import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
 import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
-import org.opendaylight.controller.sal.authorization.AuthResultEnum;
-import org.opendaylight.controller.usermanager.IUserManager;
 import org.opendaylight.protocol.framework.NeverReconnectStrategy;
 
 public class NetconfITSecureTest extends AbstractNetconfConfigTest {
 
     private static final InetSocketAddress tlsAddress = new InetSocketAddress("127.0.0.1", 12024);
-    private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023);
 
     private DefaultCommitNotificationProducer commitNot;
     private NetconfSSHServer sshServer;
@@ -79,13 +77,10 @@ public class NetconfITSecureTest extends AbstractNetconfConfigTest {
 
 
         final NetconfServerDispatcher dispatchS = createDispatcher(factoriesListener);
-        ChannelFuture s = dispatchS.createServer(tcpAddress);
+        ChannelFuture s = dispatchS.createLocalServer(NetconfConfigUtil.getNetconfLocalAddress());
         s.await();
-
-        sshServer = NetconfSSHServer.start(tlsAddress.getPort(), tcpAddress, getAuthProvider());
-        Thread thread = new Thread(sshServer);
-        thread.setDaemon(true);
-        thread.start();
+        EventLoopGroup bossGroup  = new NioEventLoopGroup();
+        sshServer = NetconfSSHServer.start(tlsAddress.getPort(), NetconfConfigUtil.getNetconfLocalAddress(), getAuthProvider(), bossGroup);
     }
 
     private NetconfServerDispatcher createDispatcher(NetconfOperationServiceFactoryListenerImpl factoriesListener) {
@@ -140,13 +135,10 @@ public class NetconfITSecureTest extends AbstractNetconfConfigTest {
     }
 
     public AuthProvider getAuthProvider() throws Exception {
-        final IUserManager userManager = mock(IUserManager.class);
-        doReturn(AuthResultEnum.AUTH_ACCEPT).when(userManager).authenticate(anyString(), anyString());
-
-        final File privateKeyFile = Files.createTempFile("tmp-netconf-test", "pk").toFile();
-        privateKeyFile.deleteOnExit();
-        String privateKeyPEMString = PEMGenerator.generateTo(privateKeyFile);
-        return new AuthProvider(userManager, privateKeyPEMString);
+        AuthProvider mock = mock(AuthProvider.class);
+        doReturn(true).when(mock).authenticated(anyString(), anyString());
+        doReturn(PEMGenerator.generate().toCharArray()).when(mock).getPEMAsCharArray();
+        return mock;
     }
 
     public AuthenticationHandler getAuthHandler() throws IOException {
index fd43f67c056407c15dc0692d938b4fad7c30586b..60a5207daa2e74b54cbc5a1a2b1ec9dd423a3f2b 100644 (file)
@@ -8,7 +8,6 @@
 
 package org.opendaylight.controller.netconf.it;
 
-import static java.util.Collections.emptyList;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
@@ -17,6 +16,10 @@ import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 
+import com.google.common.base.Throwables;
+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;
@@ -29,10 +32,8 @@ 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 org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
@@ -66,32 +67,20 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controll
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.test.types.rev131127.TestIdentity2;
 import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry;
 import org.opendaylight.yangtools.yang.data.impl.codec.IdentityCodec;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
 import org.xml.sax.SAXException;
 
-import com.google.common.base.Throwables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-import io.netty.channel.ChannelFuture;
-
 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 InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023);
-    private static final InetSocketAddress sshAddress = new InetSocketAddress("127.0.0.1", 10830);
-    private static final String USERNAME = "netconf";
-    private static final String PASSWORD = "netconf";
 
-    private NetconfMessage getConfig, getConfigCandidate, editConfig,
-            closeSession, startExi, stopExi;
+
+    private NetconfMessage getConfig, getConfigCandidate, editConfig, closeSession;
     private DefaultCommitNotificationProducer commitNot;
     private NetconfServerDispatcher dispatch;
 
@@ -139,10 +128,6 @@ public class NetconfITTest extends AbstractNetconfConfigTest {
         this.editConfig = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/edit_config.xml");
         this.getConfig = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/getConfig.xml");
         this.getConfigCandidate = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/getConfig_candidate.xml");
-        this.startExi = XmlFileLoader
-                .xmlFileToNetconfMessage("netconfMessages/startExi.xml");
-        this.stopExi = XmlFileLoader
-                .xmlFileToNetconfMessage("netconfMessages/stopExi.xml");
         this.closeSession = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/closeSession.xml");
     }
 
@@ -166,7 +151,7 @@ public class NetconfITTest extends AbstractNetconfConfigTest {
                 yangDependencies.add(resourceAsStream);
             }
         }
-        assertEquals("Some yang files were not found", emptyList(), failedToFind);
+        assertEquals("Some yang files were not found", Collections.<String>emptyList(), failedToFind);
         return yangDependencies;
     }
 
@@ -198,6 +183,7 @@ public class NetconfITTest extends AbstractNetconfConfigTest {
     public void testTwoSessions() throws Exception {
         try (TestingNetconfClient netconfClient = new TestingNetconfClient("1", clientDispatcher, getClientConfiguration(tcpAddress, 10000)))  {
             try (TestingNetconfClient netconfClient2 = new TestingNetconfClient("2", clientDispatcher, getClientConfiguration(tcpAddress, 10000))) {
+                assertNotNull(netconfClient2.getCapabilities());
             }
         }
     }
index e88bf53ae0a7ccfc95d091521e0587a55fc1ef71..7897666ddc6763cff64f8e7f6dd225f411c9eb54 100644 (file)
@@ -8,7 +8,7 @@
 
 package org.opendaylight.controller.netconf.nettyutil;
 
-import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.Channel;
 import io.netty.util.concurrent.Promise;
 import org.opendaylight.controller.netconf.api.NetconfSession;
 import org.opendaylight.controller.netconf.nettyutil.handler.FramingMechanismHandlerFactory;
@@ -25,7 +25,7 @@ public abstract class AbstractChannelInitializer<S extends NetconfSession> {
     public static final String NETCONF_MESSAGE_FRAME_ENCODER = "frameEncoder";
     public static final String NETCONF_SESSION_NEGOTIATOR = "negotiator";
 
-    public void initialize(SocketChannel ch, Promise<S> promise) {
+    public void initialize(Channel ch, Promise<S> promise) {
         ch.pipeline().addLast(NETCONF_MESSAGE_AGGREGATOR, new NetconfEOMAggregator());
         initializeMessageDecoder(ch);
         ch.pipeline().addLast(NETCONF_MESSAGE_FRAME_ENCODER, FramingMechanismHandlerFactory.createHandler(FramingMechanism.EOM));
@@ -34,13 +34,13 @@ public abstract class AbstractChannelInitializer<S extends NetconfSession> {
         initializeSessionNegotiator(ch, promise);
     }
 
-    protected void initializeMessageEncoder(SocketChannel ch) {
+    protected void initializeMessageEncoder(Channel ch) {
         // Special encoding handler for hello message to include additional header if available,
         // it is thrown away after successful negotiation
         ch.pipeline().addLast(NETCONF_MESSAGE_ENCODER, new NetconfHelloMessageToXMLEncoder());
     }
 
-    protected void initializeMessageDecoder(SocketChannel ch) {
+    protected void initializeMessageDecoder(Channel ch) {
         // Special decoding handler for hello message to parse additional header if available,
         // it is thrown away after successful negotiation
         ch.pipeline().addLast(NETCONF_MESSAGE_DECODER, new NetconfXMLToHelloMessageDecoder());
@@ -50,6 +50,6 @@ public abstract class AbstractChannelInitializer<S extends NetconfSession> {
      * Insert session negotiator into the pipeline. It must be inserted after message decoder
      * identified by {@link AbstractChannelInitializer#NETCONF_MESSAGE_DECODER}, (or any other custom decoder processor)
      */
-    protected abstract void initializeSessionNegotiator(SocketChannel ch, Promise<S> promise);
+    protected abstract void initializeSessionNegotiator(Channel ch, Promise<S> promise);
 
 }
index 622881352eef102119eed81a74f32ee0db2ed76a..cbd3efc57f7bc0abfd27e77bec63334087265fab 100644 (file)
       <plugin>
         <groupId>org.apache.felix</groupId>
         <artifactId>maven-bundle-plugin</artifactId>
+        <version>2.3.7</version>
         <configuration>
           <instructions>
             <Bundle-Activator>org.opendaylight.controller.netconf.ssh.osgi.NetconfSSHActivator</Bundle-Activator>
             <Import-Package>com.google.common.base,
-                            ch.ethz.ssh2,
-                            ch.ethz.ssh2.signature,
-                            org.apache.commons.io,
-                            org.opendaylight.controller.netconf.util.osgi,
-                            org.opendaylight.controller.usermanager,
-                            org.opendaylight.controller.sal.authorization,
-                            org.opendaylight.controller.sal.utils,
-                            org.osgi.framework,
-                            org.osgi.util.tracker,
-                            org.slf4j,
-                            org.bouncycastle.openssl</Import-Package>
+              ch.ethz.ssh2,
+              ch.ethz.ssh2.signature,
+              org.apache.commons.io,
+              org.opendaylight.controller.netconf.util.osgi,
+              org.opendaylight.controller.usermanager,
+              org.opendaylight.controller.sal.authorization,
+              org.opendaylight.controller.sal.utils,
+              org.osgi.framework,
+              org.osgi.util.tracker,
+              org.slf4j,
+              org.bouncycastle.openssl,
+              io.netty.bootstrap, io.netty.buffer, io.netty.channel, io.netty.channel.local, io.netty.channel.nio,
+              io.netty.handler.stream, io.netty.util.concurrent, org.apache.commons.lang3,
+              org.opendaylight.controller.netconf.util.messages</Import-Package>
           </instructions>
         </configuration>
       </plugin>
index c6974d4982db051f1cb6a66d3f67394ee2d57817..08bf9836b22135a295f2e54d68fa6c73ffeadf57 100644 (file)
@@ -7,79 +7,94 @@
  */
 package org.opendaylight.controller.netconf.ssh;
 
-import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
-import org.opendaylight.controller.netconf.ssh.threads.SocketThread;
-import org.opendaylight.controller.usermanager.IUserManager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.annotation.concurrent.ThreadSafe;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.local.LocalAddress;
 import java.io.IOException;
-import java.net.InetSocketAddress;
 import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicLong;
+import javax.annotation.concurrent.ThreadSafe;
+import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
+import org.opendaylight.controller.netconf.ssh.threads.Handshaker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+/**
+ * Thread that accepts client connections. Accepted socket is forwarded to {@link org.opendaylight.controller.netconf.ssh.threads.Handshaker},
+ * which is executed in {@link #handshakeExecutor}.
+ */
 @ThreadSafe
-public final class NetconfSSHServer implements Runnable {
+public final class NetconfSSHServer extends Thread implements AutoCloseable {
 
-    private ServerSocket ss = null;
-    private static final Logger logger =  LoggerFactory.getLogger(NetconfSSHServer.class);
-    private static final AtomicLong sesssionId = new AtomicLong();
-    private final InetSocketAddress clientAddress;
-    private final AuthProvider authProvider;
-    private volatile boolean up = false;
+    private static final Logger logger = LoggerFactory.getLogger(NetconfSSHServer.class);
+    private static final AtomicLong sessionIdCounter = new AtomicLong();
 
-    private NetconfSSHServer(int serverPort,InetSocketAddress clientAddress, AuthProvider authProvider) throws IllegalStateException, IOException {
+    private final ServerSocket serverSocket;
+    private final LocalAddress localAddress;
+    private final EventLoopGroup bossGroup;
+    private final AuthProvider authProvider;
+    private final ExecutorService handshakeExecutor;
+    private volatile boolean up;
 
-        logger.trace("Creating SSH server socket on port {}",serverPort);
-        this.ss = new ServerSocket(serverPort);
-        if (!ss.isBound()){
-            throw new IllegalStateException("Socket can't be bound to requested port :"+serverPort);
+    private NetconfSSHServer(int serverPort, LocalAddress localAddress, AuthProvider authProvider, EventLoopGroup bossGroup) throws IOException {
+        super(NetconfSSHServer.class.getSimpleName());
+        this.bossGroup = bossGroup;
+        logger.trace("Creating SSH server socket on port {}", serverPort);
+        this.serverSocket = new ServerSocket(serverPort);
+        if (serverSocket.isBound() == false) {
+            throw new IllegalStateException("Socket can't be bound to requested port :" + serverPort);
         }
         logger.trace("Server socket created.");
-        this.clientAddress = clientAddress;
+        this.localAddress = localAddress;
         this.authProvider = authProvider;
         this.up = true;
+        handshakeExecutor = Executors.newFixedThreadPool(10);
     }
 
-    public static NetconfSSHServer start(int serverPort, InetSocketAddress clientAddress,AuthProvider authProvider) throws IllegalStateException, IOException {
-        return new NetconfSSHServer(serverPort, clientAddress,authProvider);
+    public static NetconfSSHServer start(int serverPort, LocalAddress localAddress, AuthProvider authProvider, EventLoopGroup bossGroup) throws IOException {
+        NetconfSSHServer netconfSSHServer = new NetconfSSHServer(serverPort, localAddress, authProvider, bossGroup);
+        netconfSSHServer.start();
+        return netconfSSHServer;
     }
 
-    public void stop() throws IOException {
+    @Override
+    public void close() throws IOException {
         up = false;
         logger.trace("Closing SSH server socket.");
-        ss.close();
+        serverSocket.close();
+        bossGroup.shutdownGracefully();
         logger.trace("SSH server socket closed.");
     }
 
-    public void removeUserManagerService(){
-        this.authProvider.removeUserManagerService();
-    }
-
-    public void addUserManagerService(IUserManager userManagerService){
-        this.authProvider.addUserManagerService(userManagerService);
-    }
-    public boolean isUp(){
-        return this.up;
-    }
     @Override
     public void run() {
         while (up) {
-            logger.trace("Starting new socket thread.");
+            Socket acceptedSocket = null;
             try {
-                SocketThread.start(ss.accept(), clientAddress, sesssionId.incrementAndGet(), authProvider);
-            }
-            catch (IOException e) {
-                if( up ) {
-                    logger.error("Exception occurred during socket thread initialization", e);
+                acceptedSocket = serverSocket.accept();
+            } catch (IOException e) {
+                if (up == false) {
+                    logger.trace("Exiting server thread", e);
+                } else {
+                    logger.warn("Exception occurred during socket.accept", e);
                 }
-                else {
-                    // We're shutting down so an exception is expected as the socket's been closed.
-                    // Log to debug.
-                    logger.debug("Shutting down - got expected exception: " + e);
+            }
+            if (acceptedSocket != null) {
+                try {
+                    Handshaker task = new Handshaker(acceptedSocket, localAddress, sessionIdCounter.incrementAndGet(), authProvider, bossGroup);
+                    handshakeExecutor.submit(task);
+                } catch (IOException e) {
+                    logger.warn("Cannot set PEMHostKey, closing connection", e);
+                    try {
+                        acceptedSocket.close();
+                    } catch (IOException e1) {
+                        logger.warn("Ignoring exception while closing socket", e);
+                    }
                 }
             }
         }
+        logger.debug("Server thread is exiting");
     }
 }
index 2e9a0b9d8bbd256154ff82689e85e556b60c9e90..5d39dd1eb8adad115e030c0136cbae702d2bde65 100644 (file)
@@ -7,41 +7,75 @@
  */
 package org.opendaylight.controller.netconf.ssh.authentication;
 
-import java.io.IOException;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.annotations.VisibleForTesting;
 import org.opendaylight.controller.sal.authorization.AuthResultEnum;
 import org.opendaylight.controller.usermanager.IUserManager;
-import static com.google.common.base.Preconditions.checkNotNull;
+import org.osgi.framework.BundleContext;
+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;
 
-public class AuthProvider implements AuthProviderInterface {
+public class AuthProvider {
+    private static final Logger logger = LoggerFactory.getLogger(AuthProvider.class);
 
-    private IUserManager um;
     private final String pem;
+    private IUserManager nullableUserManager;
 
-    public AuthProvider(IUserManager ium, String pemCertificate) throws IllegalArgumentException, IOException {
+    public AuthProvider(String pemCertificate, final BundleContext bundleContext) {
         checkNotNull(pemCertificate, "Parameter 'pemCertificate' is null");
-        checkNotNull(ium, "No user manager service available.");
-        this.um = ium;
         pem = pemCertificate;
+
+        ServiceTrackerCustomizer<IUserManager, IUserManager> customizer = new ServiceTrackerCustomizer<IUserManager, IUserManager>() {
+            @Override
+            public IUserManager addingService(final ServiceReference<IUserManager> reference) {
+                logger.trace("Service {} added", reference);
+                nullableUserManager = bundleContext.getService(reference);
+                return nullableUserManager;
+            }
+
+            @Override
+            public void modifiedService(final ServiceReference<IUserManager> reference, final IUserManager service) {
+                logger.trace("Replacing modified service {} in netconf SSH.", reference);
+                nullableUserManager = service;
+            }
+
+            @Override
+            public void removedService(final ServiceReference<IUserManager> reference, final IUserManager service) {
+                logger.trace("Removing service {} from netconf SSH. " +
+                        "SSH won't authenticate users until IUserManager service will be started.", reference);
+                synchronized (AuthProvider.this) {
+                    nullableUserManager = null;
+                }
+            }
+        };
+        ServiceTracker<IUserManager, IUserManager> listenerTracker = new ServiceTracker<>(bundleContext, IUserManager.class, customizer);
+        listenerTracker.open();
     }
 
-    @Override
-    public boolean authenticated(String username, String password) {
-        AuthResultEnum authResult = this.um.authenticate(username, password);
+    /**
+     * Authenticate user. This implementation tracks IUserManager and delegates the decision to it. If the service is not
+     * available, IllegalStateException is thrown.
+     */
+    public synchronized boolean authenticated(String username, String password) {
+        if (nullableUserManager == null) {
+            logger.warn("Cannot authenticate user '{}', user manager service is missing", username);
+            throw new IllegalStateException("User manager service is not available");
+        }
+        AuthResultEnum authResult = nullableUserManager.authenticate(username, password);
+        logger.debug("Authentication result for user '{}' : {}", username, authResult);
         return authResult.equals(AuthResultEnum.AUTH_ACCEPT) || authResult.equals(AuthResultEnum.AUTH_ACCEPT_LOC);
     }
 
-    @Override
     public char[] getPEMAsCharArray() {
         return pem.toCharArray();
     }
 
-    @Override
-    public void removeUserManagerService() {
-        this.um = null;
-    }
-
-    @Override
-    public void addUserManagerService(IUserManager userManagerService) {
-        this.um = userManagerService;
+    @VisibleForTesting
+    void setNullableUserManager(IUserManager nullableUserManager) {
+        this.nullableUserManager = nullableUserManager;
     }
 }
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/AuthProviderInterface.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/AuthProviderInterface.java
deleted file mode 100644 (file)
index fad0f79..0000000
+++ /dev/null
@@ -1,19 +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.ssh.authentication;
-
-import org.opendaylight.controller.usermanager.IUserManager;
-
-public interface AuthProviderInterface {
-
-    public boolean authenticated(String username, String password) throws IllegalStateException;
-    public char[] getPEMAsCharArray() throws Exception;
-    public void removeUserManagerService();
-    public void addUserManagerService(IUserManager userManagerService);
-}
index 348fe006f3a7d4cfefcb8c502a9115da422a0a61..53ab8219ee9c2a9f98e144f8c125f45b86edb002 100644 (file)
@@ -8,8 +8,11 @@
 
 package org.opendaylight.controller.netconf.ssh.authentication;
 
+import com.google.common.annotations.VisibleForTesting;
+import java.io.FileInputStream;
 import java.security.NoSuchAlgorithmException;
 import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
 import org.bouncycastle.openssl.PEMWriter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -26,17 +29,55 @@ public class PEMGenerator {
     private static final Logger logger = LoggerFactory.getLogger(PEMGenerator.class);
     private static final int KEY_SIZE = 4096;
 
+
+    public static String readOrGeneratePK(File privateKeyFile) throws IOException {
+        if (privateKeyFile.exists() == false) {
+            // generate & save to file
+            try {
+                return generateTo(privateKeyFile);
+            } catch (Exception e) {
+                logger.error("Exception occurred while generating PEM string to {}", privateKeyFile, e);
+                throw new IllegalStateException("Error generating RSA key from file " + privateKeyFile);
+            }
+        } else {
+            // read from file
+            try (FileInputStream fis = new FileInputStream(privateKeyFile)) {
+                return IOUtils.toString(fis);
+            } catch (final IOException e) {
+                logger.error("Error reading RSA key from file {}", privateKeyFile, e);
+                throw new IOException("Error reading RSA key from file " + privateKeyFile, e);
+            }
+        }
+    }
+
+    /**
+     * Generate private key to a file and return its content as string.
+     *
+     * @param privateFile path where private key should be generated
+     * @return String representation of private key
+     * @throws IOException
+     * @throws NoSuchAlgorithmException
+     */
+    @VisibleForTesting
     public static String generateTo(File privateFile) throws IOException, NoSuchAlgorithmException {
+        logger.info("Generating private key to {}", privateFile.getAbsolutePath());
+        String privatePEM = generate();
+        FileUtils.write(privateFile, privatePEM);
+        return privatePEM;
+    }
+
+    @VisibleForTesting
+    public static String generate() throws NoSuchAlgorithmException, IOException {
         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;
+        return toString(keypair.getPrivate());
     }
 
+    /**
+     * Get string representation of a key.
+     */
     private static String toString(Key key) throws IOException {
         try (StringWriter writer = new StringWriter()) {
             try (PEMWriter pemWriter = new PEMWriter(writer)) {
@@ -45,4 +86,5 @@ public class PEMGenerator {
             return writer.toString();
         }
     }
+
 }
index d74308cfadbae8e658e58f9b189d93baaea83c2e..a26843fae17a97621b48221a932eb9b823a6ce83 100644 (file)
@@ -7,24 +7,24 @@
  */
 package org.opendaylight.controller.netconf.ssh.osgi;
 
-import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
 
+import com.google.common.base.Optional;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.local.LocalAddress;
+import io.netty.channel.nio.NioEventLoopGroup;
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
 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.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil.InfixProp;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.util.tracker.ServiceTracker;
-import org.osgi.util.tracker.ServiceTrackerCustomizer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -32,112 +32,56 @@ import org.slf4j.LoggerFactory;
  * 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
  * and listens for client connections. Each client connection creation is handled in separate
- * {@link org.opendaylight.controller.netconf.ssh.threads.SocketThread} thread.
+ * {@link org.opendaylight.controller.netconf.ssh.threads.Handshaker} thread.
  * This thread creates two additional threads {@link org.opendaylight.controller.netconf.ssh.threads.IOThread}
  * forwarding data from/to client.IOThread closes servers session and server connection when it gets -1 on input stream.
  * {@link org.opendaylight.controller.netconf.ssh.threads.IOThread}'s run method waits for -1 on input stream to finish.
  * All threads are daemons.
- **/
-public class NetconfSSHActivator implements BundleActivator{
+ */
+public class NetconfSSHActivator implements BundleActivator {
+    private static final Logger logger = LoggerFactory.getLogger(NetconfSSHActivator.class);
 
     private NetconfSSHServer server;
-    private static final Logger logger =  LoggerFactory.getLogger(NetconfSSHActivator.class);
-    private IUserManager iUserManager;
-    private BundleContext context = null;
-
-    private ServiceTrackerCustomizer<IUserManager, IUserManager> customizer = new ServiceTrackerCustomizer<IUserManager, IUserManager>(){
-        @Override
-        public IUserManager addingService(final ServiceReference<IUserManager> reference) {
-            logger.trace("Service {} added, let there be SSH bridge.", reference);
-            iUserManager =  context.getService(reference);
-            try {
-                onUserManagerFound(iUserManager);
-            } catch (final Exception e) {
-                logger.trace("Can't start SSH server due to {}",e);
-            }
-            return iUserManager;
-        }
-        @Override
-        public void modifiedService(final ServiceReference<IUserManager> reference, final IUserManager service) {
-            logger.trace("Replacing modified service {} in netconf SSH.", reference);
-            server.addUserManagerService(service);
-        }
-        @Override
-        public void removedService(final ServiceReference<IUserManager> reference, final IUserManager service) {
-            logger.trace("Removing service {} from netconf SSH. " +
-                    "SSH won't authenticate users until IUserManager service will be started.", reference);
-            removeUserManagerService();
-        }
-    };
-
 
     @Override
-    public void start(final BundleContext context) {
-        this.context = context;
-        listenForManagerService();
+    public void start(final BundleContext bundleContext) throws IOException {
+        server = startSSHServer(bundleContext);
     }
 
     @Override
     public void stop(BundleContext context) throws IOException {
-        if (server != null){
-            server.stop();
-            logger.trace("Netconf SSH bridge is down ...");
+        if (server != null) {
+            server.close();
         }
     }
-    private void startSSHServer() throws IOException {
-        checkNotNull(this.iUserManager, "No user manager service available.");
-        logger.trace("Starting netconf SSH  bridge.");
-        final InetSocketAddress sshSocketAddress = NetconfConfigUtil.extractSSHNetconfAddress(context,
-                NetconfConfigUtil.DEFAULT_NETCONF_SSH_ADDRESS);
-        final InetSocketAddress tcpSocketAddress = NetconfConfigUtil.extractTCPNetconfClientAddress(context,
-               NetconfConfigUtil.DEFAULT_NETCONF_TCP_ADDRESS);
 
-        String path =  FilenameUtils.separatorsToSystem(NetconfConfigUtil.getPrivateKeyPath(context));
+    private static NetconfSSHServer startSSHServer(BundleContext bundleContext) throws IOException {
+        Optional<InetSocketAddress> maybeSshSocketAddress = NetconfConfigUtil.extractNetconfServerAddress(bundleContext,
+                InfixProp.ssh);
 
-        if (path.isEmpty()) {
-            throw new IllegalStateException("Missing netconf.ssh.pk.path key in configuration file.");
+        if (maybeSshSocketAddress.isPresent() == false) {
+            logger.trace("SSH bridge not configured");
+            return null;
         }
+        InetSocketAddress sshSocketAddress = maybeSshSocketAddress.get();
+        logger.trace("Starting netconf SSH  bridge at {}", sshSocketAddress);
 
-        final File privateKeyFile = new File(path);
-        final String privateKeyPEMString;
-        if (privateKeyFile.exists() == false) {
-            // generate & save to file
-            try {
-                privateKeyPEMString = PEMGenerator.generateTo(privateKeyFile);
-            } catch (Exception e) {
-                logger.error("Exception occurred while generating PEM string {}", e);
-                throw new IllegalStateException("Error generating RSA key from file " + path);
-            }
-        } else {
-            // read from file
-            try (FileInputStream fis = new FileInputStream(path)) {
-                privateKeyPEMString = IOUtils.toString(fis);
-            } catch (final IOException e) {
-                logger.error("Error reading RSA key from file '{}'", path);
-                throw new IOException("Error reading RSA key from file " + path, e);
-            }
-        }
-        final AuthProvider authProvider = new AuthProvider(iUserManager, privateKeyPEMString);
-        this.server = NetconfSSHServer.start(sshSocketAddress.getPort(), tcpSocketAddress, authProvider);
+        LocalAddress localAddress = NetconfConfigUtil.getNetconfLocalAddress();
+
+        String path = FilenameUtils.separatorsToSystem(NetconfConfigUtil.getPrivateKeyPath(bundleContext));
+        checkState(StringUtils.isNotBlank(path), "Path to ssh private key is blank. Reconfigure %s", NetconfConfigUtil.getPrivateKeyKey());
+        String privateKeyPEMString = PEMGenerator.readOrGeneratePK(new File(path));
+
+        final AuthProvider authProvider = new AuthProvider(privateKeyPEMString, bundleContext);
+        EventLoopGroup bossGroup  = new NioEventLoopGroup();
+        NetconfSSHServer server = NetconfSSHServer.start(sshSocketAddress.getPort(), localAddress, authProvider, bossGroup);
 
         final Thread serverThread = new Thread(server, "netconf SSH server thread");
         serverThread.setDaemon(true);
         serverThread.start();
         logger.trace("Netconf SSH  bridge up and running.");
+        return server;
     }
 
-    private void onUserManagerFound(final IUserManager userManager) throws Exception{
-        if (server!=null && server.isUp()){
-           server.addUserManagerService(userManager);
-        } else {
-           startSSHServer();
-        }
-    }
-    private void removeUserManagerService(){
-        this.server.removeUserManagerService();
-    }
-    private void listenForManagerService(){
-        final ServiceTracker<IUserManager, IUserManager> listenerTracker = new ServiceTracker<>(context, IUserManager.class,customizer);
-        listenerTracker.open();
-    }
+
 }
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java
new file mode 100644 (file)
index 0000000..d999d37
--- /dev/null
@@ -0,0 +1,406 @@
+/*
+ * 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.threads;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import ch.ethz.ssh2.AuthenticationResult;
+import ch.ethz.ssh2.PtySettings;
+import ch.ethz.ssh2.ServerAuthenticationCallback;
+import ch.ethz.ssh2.ServerConnection;
+import ch.ethz.ssh2.ServerConnectionCallback;
+import ch.ethz.ssh2.ServerSession;
+import ch.ethz.ssh2.ServerSessionCallback;
+import ch.ethz.ssh2.SimpleServerSessionCallback;
+import com.google.common.base.Supplier;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufProcessor;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.local.LocalAddress;
+import io.netty.channel.local.LocalChannel;
+import io.netty.handler.stream.ChunkedStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import javax.annotation.concurrent.NotThreadSafe;
+import javax.annotation.concurrent.ThreadSafe;
+import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * One instance represents per connection, responsible for ssh handshake.
+ * Once auth succeeds and correct subsystem is chosen, backend connection with
+ * netty netconf server is made. This task finishes right after negotiation is done.
+ */
+@ThreadSafe
+public class Handshaker implements Runnable {
+    private static final Logger logger = LoggerFactory.getLogger(Handshaker.class);
+
+    private final ServerConnection ganymedConnection;
+    private final String session;
+
+
+    public Handshaker(Socket socket, LocalAddress localAddress, long sessionId, AuthProvider authProvider,
+                      EventLoopGroup bossGroup) throws IOException {
+
+        this.session = "Session " + sessionId;
+
+        String remoteAddressWithPort = socket.getRemoteSocketAddress().toString().replace("/", "");
+        logger.debug("{} started with {}", session, remoteAddressWithPort);
+        String remoteAddress, remotePort;
+        if (remoteAddressWithPort.contains(":")) {
+            String[] split = remoteAddressWithPort.split(":");
+            remoteAddress = split[0];
+            remotePort = split[1];
+        } else {
+            remoteAddress = remoteAddressWithPort;
+            remotePort = "";
+        }
+        ServerAuthenticationCallbackImpl serverAuthenticationCallback = new ServerAuthenticationCallbackImpl(
+                authProvider, session);
+
+        ganymedConnection = new ServerConnection(socket);
+
+        ServerConnectionCallbackImpl serverConnectionCallback = new ServerConnectionCallbackImpl(
+                serverAuthenticationCallback, remoteAddress, remotePort, session,
+                getGanymedAutoCloseable(ganymedConnection), localAddress, bossGroup);
+
+        // initialize ganymed
+        ganymedConnection.setPEMHostKey(authProvider.getPEMAsCharArray(), null);
+        ganymedConnection.setAuthenticationCallback(serverAuthenticationCallback);
+        ganymedConnection.setServerConnectionCallback(serverConnectionCallback);
+    }
+
+
+    private static AutoCloseable getGanymedAutoCloseable(final ServerConnection ganymedConnection) {
+        return new AutoCloseable() {
+            @Override
+            public void close() throws Exception {
+                ganymedConnection.close();
+            }
+        };
+    }
+
+    @Override
+    public void run() {
+        // let ganymed process handshake
+        logger.trace("{} SocketThread is started", session);
+        try {
+            // TODO this should be guarded with a timer to prevent resource exhaustion
+            ganymedConnection.connect();
+        } catch (IOException e) {
+            logger.warn("{} SocketThread error ", session, e);
+        }
+        logger.trace("{} SocketThread is exiting", session);
+    }
+}
+
+/**
+ * Netty client handler that forwards bytes from backed server to supplied output stream.
+ * When backend server closes the connection, remoteConnection.close() is called to tear
+ * down ssh connection.
+ */
+class SSHClientHandler extends ChannelInboundHandlerAdapter {
+    private static final Logger logger = LoggerFactory.getLogger(SSHClientHandler.class);
+    private final AutoCloseable remoteConnection;
+    private final OutputStream remoteOutputStream;
+    private final String session;
+    private ChannelHandlerContext channelHandlerContext;
+
+    public SSHClientHandler(AutoCloseable remoteConnection, OutputStream remoteOutputStream,
+                            String session) {
+        this.remoteConnection = remoteConnection;
+        this.remoteOutputStream = remoteOutputStream;
+        this.session = session;
+    }
+
+    @Override
+    public void channelActive(ChannelHandlerContext ctx) {
+        this.channelHandlerContext = ctx;
+        logger.debug("{} Client active", session);
+    }
+
+    @Override
+    public void channelRead(ChannelHandlerContext ctx, Object msg) {
+        ByteBuf bb = (ByteBuf) msg;
+        // we can block the server here so that slow client does not cause memory pressure
+        try {
+            bb.forEachByte(new ByteBufProcessor() {
+                @Override
+                public boolean process(byte value) throws Exception {
+                    remoteOutputStream.write(value);
+                    return true;
+                }
+            });
+        } finally {
+            bb.release();
+        }
+    }
+
+    @Override
+    public void channelReadComplete(ChannelHandlerContext ctx) throws IOException {
+        logger.trace("{} Flushing", session);
+        remoteOutputStream.flush();
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+        // Close the connection when an exception is raised.
+        logger.warn("{} Unexpected exception from downstream", session, cause);
+        ctx.close();
+    }
+
+    @Override
+    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+        logger.trace("{} channelInactive() called, closing remote client ctx", session);
+        remoteConnection.close();//this should close socket and all threads created for this client
+        this.channelHandlerContext = null;
+    }
+
+    public ChannelHandlerContext getChannelHandlerContext() {
+        return checkNotNull(channelHandlerContext, "Channel is not active");
+    }
+}
+
+/**
+ * Ganymed handler that gets unencrypted input and output streams, connects them to netty.
+ * Checks that 'netconf' subsystem is chosen by user.
+ * Launches new ClientInputStreamPoolingThread thread once session is established.
+ * Writes custom header to netty server, to inform it about IP address and username.
+ */
+class ServerConnectionCallbackImpl implements ServerConnectionCallback {
+    private static final Logger logger = LoggerFactory.getLogger(ServerConnectionCallbackImpl.class);
+    public static final String NETCONF_SUBSYSTEM = "netconf";
+
+    private final Supplier<String> currentUserSupplier;
+    private final String remoteAddress;
+    private final String remotePort;
+    private final String session;
+    private final AutoCloseable ganymedConnection;
+    private final LocalAddress localAddress;
+    private final EventLoopGroup bossGroup;
+
+    ServerConnectionCallbackImpl(Supplier<String> currentUserSupplier, String remoteAddress, String remotePort, String session,
+                                 AutoCloseable ganymedConnection, LocalAddress localAddress, EventLoopGroup bossGroup) {
+        this.currentUserSupplier = currentUserSupplier;
+        this.remoteAddress = remoteAddress;
+        this.remotePort = remotePort;
+        this.session = session;
+        this.ganymedConnection = ganymedConnection;
+        // initialize netty local connection
+        this.localAddress = localAddress;
+        this.bossGroup = bossGroup;
+    }
+
+    private static ChannelFuture initializeNettyConnection(LocalAddress localAddress, EventLoopGroup bossGroup,
+                                                           final SSHClientHandler sshClientHandler) {
+        Bootstrap clientBootstrap = new Bootstrap();
+        clientBootstrap.group(bossGroup).channel(LocalChannel.class);
+
+        clientBootstrap.handler(new ChannelInitializer<LocalChannel>() {
+            @Override
+            public void initChannel(LocalChannel ch) throws Exception {
+                ch.pipeline().addLast(sshClientHandler);
+            }
+        });
+        // asynchronously initialize local connection to netconf server
+        return clientBootstrap.connect(localAddress);
+    }
+
+    @Override
+    public ServerSessionCallback acceptSession(final ServerSession serverSession) {
+        String currentUser = currentUserSupplier.get();
+        final String additionalHeader = new NetconfHelloMessageAdditionalHeader(currentUser, remoteAddress,
+                remotePort, "ssh", "client").toFormattedString();
+
+
+        return new SimpleServerSessionCallback() {
+            @Override
+            public Runnable requestSubsystem(final ServerSession ss, final String subsystem) throws IOException {
+                return new Runnable() {
+                    @Override
+                    public void run() {
+                        if (NETCONF_SUBSYSTEM.equals(subsystem)) {
+                            // connect
+                            final SSHClientHandler sshClientHandler = new SSHClientHandler(ganymedConnection, ss.getStdin(), session);
+                            ChannelFuture clientChannelFuture = initializeNettyConnection(localAddress, bossGroup, sshClientHandler);
+                            // get channel
+                            final Channel channel = clientChannelFuture.awaitUninterruptibly().channel();
+                            new ClientInputStreamPoolingThread(session, ss.getStdout(), channel, new AutoCloseable() {
+                                @Override
+                                public void close() throws Exception {
+                                    logger.trace("Closing both ganymed and local connection");
+                                    try {
+                                        ganymedConnection.close();
+                                    } catch (Exception e) {
+                                        logger.warn("Ignoring exception while closing ganymed", e);
+                                    }
+                                    try {
+                                        channel.close();
+                                    } catch (Exception e) {
+                                        logger.warn("Ignoring exception while closing channel", e);
+                                    }
+                                }
+                            }, sshClientHandler.getChannelHandlerContext()).start();
+
+                            // write additional header
+                            channel.writeAndFlush(Unpooled.copiedBuffer(additionalHeader.getBytes()));
+                        } else {
+                            logger.debug("{} Wrong subsystem requested:'{}', closing ssh session", serverSession, subsystem);
+                            String reason = "Only netconf subsystem is supported, requested:" + subsystem;
+                            closeSession(ss, reason);
+                        }
+                    }
+                };
+            }
+
+            public void closeSession(ServerSession ss, String reason) {
+                logger.trace("{} Closing session - {}", serverSession, reason);
+                try {
+                    ss.getStdin().write(reason.getBytes());
+                } catch (IOException e) {
+                    logger.warn("{} Exception while closing session", serverSession, e);
+                }
+                ss.close();
+            }
+
+            @Override
+            public Runnable requestPtyReq(final ServerSession ss, final PtySettings pty) throws IOException {
+                return new Runnable() {
+                    @Override
+                    public void run() {
+                        closeSession(ss, "PTY request not supported");
+                    }
+                };
+            }
+
+            @Override
+            public Runnable requestShell(final ServerSession ss) throws IOException {
+                return new Runnable() {
+                    @Override
+                    public void run() {
+                        closeSession(ss, "Shell not supported");
+                    }
+                };
+            }
+        };
+    }
+}
+
+/**
+ * Only thread that is required during ssh session, forwards client's input to netty.
+ * When user closes connection, onEndOfInput.close() is called to tear down the local channel.
+ */
+class ClientInputStreamPoolingThread extends Thread {
+    private static final Logger logger = LoggerFactory.getLogger(ClientInputStreamPoolingThread.class);
+
+    private final InputStream fromClientIS;
+    private final Channel serverChannel;
+    private final AutoCloseable onEndOfInput;
+    private final ChannelHandlerContext channelHandlerContext;
+
+    ClientInputStreamPoolingThread(String session, InputStream fromClientIS, Channel serverChannel, AutoCloseable onEndOfInput,
+                                   ChannelHandlerContext channelHandlerContext) {
+        super(ClientInputStreamPoolingThread.class.getSimpleName() + " " + session);
+        this.fromClientIS = fromClientIS;
+        this.serverChannel = serverChannel;
+        this.onEndOfInput = onEndOfInput;
+        this.channelHandlerContext = channelHandlerContext;
+    }
+
+    @Override
+    public void run() {
+        ChunkedStream chunkedStream = new ChunkedStream(fromClientIS);
+        try {
+            ByteBuf byteBuf;
+            while ((byteBuf = chunkedStream.readChunk(channelHandlerContext/*only needed for ByteBuf alloc */)) != null) {
+                serverChannel.writeAndFlush(byteBuf);
+            }
+        } catch (Exception e) {
+            logger.warn("Exception", e);
+        } finally {
+            logger.trace("End of input");
+            // tear down connection
+            try {
+                onEndOfInput.close();
+            } catch (Exception e) {
+                logger.warn("Ignoring exception while closing socket", e);
+            }
+        }
+    }
+}
+
+/**
+ * Authentication handler for ganymed.
+ * Provides current user name after authenticating using supplied AuthProvider.
+ */
+@NotThreadSafe
+class ServerAuthenticationCallbackImpl implements ServerAuthenticationCallback, Supplier<String> {
+    private static final Logger logger = LoggerFactory.getLogger(ServerAuthenticationCallbackImpl.class);
+    private final AuthProvider authProvider;
+    private final String session;
+    private String currentUser;
+
+    ServerAuthenticationCallbackImpl(AuthProvider authProvider, String session) {
+        this.authProvider = authProvider;
+        this.session = session;
+    }
+
+    @Override
+    public String initAuthentication(ServerConnection sc) {
+        logger.trace("{} Established connection", session);
+        return "Established connection" + "\r\n";
+    }
+
+    @Override
+    public String[] getRemainingAuthMethods(ServerConnection sc) {
+        return new String[]{ServerAuthenticationCallback.METHOD_PASSWORD};
+    }
+
+    @Override
+    public AuthenticationResult authenticateWithNone(ServerConnection sc, String username) {
+        return AuthenticationResult.FAILURE;
+    }
+
+    @Override
+    public AuthenticationResult authenticateWithPassword(ServerConnection sc, String username, String password) {
+        checkState(currentUser == null);
+        try {
+            if (authProvider.authenticated(username, password)) {
+                currentUser = username;
+                logger.trace("{} user {} authenticated", session, currentUser);
+                return AuthenticationResult.SUCCESS;
+            }
+        } catch (Exception e) {
+            logger.warn("{} Authentication failed", session, e);
+        }
+        return AuthenticationResult.FAILURE;
+    }
+
+    @Override
+    public AuthenticationResult authenticateWithPublicKey(ServerConnection sc, String username, String algorithm,
+                                                          byte[] publicKey, byte[] signature) {
+        return AuthenticationResult.FAILURE;
+    }
+
+    @Override
+    public String get() {
+        return currentUser;
+    }
+}
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/IOThread.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/IOThread.java
deleted file mode 100644 (file)
index c53a625..0000000
+++ /dev/null
@@ -1,70 +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.ssh.threads;
-
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import javax.annotation.concurrent.ThreadSafe;
-
-import org.apache.commons.io.IOUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import ch.ethz.ssh2.ServerConnection;
-import ch.ethz.ssh2.ServerSession;
-
-@ThreadSafe
-public class IOThread extends Thread {
-
-    private static final Logger logger =  LoggerFactory.getLogger(IOThread.class);
-
-    private final InputStream inputStream;
-    private final OutputStream outputStream;
-    private final ServerSession servSession;
-    private final ServerConnection servconnection;
-    private String customHeader;
-
-
-    public IOThread (InputStream is, OutputStream os, String id,ServerSession ss, ServerConnection conn){
-        this.inputStream = is;
-        this.outputStream = os;
-        this.servSession = ss;
-        this.servconnection = conn;
-        super.setName(id);
-        logger.trace("IOThread {} created", super.getName());
-    }
-
-    public IOThread (InputStream is, OutputStream os, String id,ServerSession ss, ServerConnection conn,String header){
-        this.inputStream = is;
-        this.outputStream = os;
-        this.servSession = ss;
-        this.servconnection = conn;
-        this.customHeader = header;
-        super.setName(id);
-        logger.trace("IOThread {} created", super.getName());
-    }
-
-    @Override
-    public void run() {
-        logger.trace("thread {} started", super.getName());
-        try {
-            if (this.customHeader!=null && !this.customHeader.equals("")){
-                this.outputStream.write(this.customHeader.getBytes());
-                logger.trace("adding {} header", this.customHeader);
-            }
-            IOUtils.copy(this.inputStream, this.outputStream);
-        } catch (Exception e) {
-            logger.error("inputstream -> outputstream copy error ",e);
-        }
-        logger.trace("closing server session");
-        servSession.close();
-        servconnection.close();
-        logger.trace("thread {} is closing",super.getName());
-    }
-}
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/SocketThread.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/SocketThread.java
deleted file mode 100644 (file)
index 04639cb..0000000
+++ /dev/null
@@ -1,208 +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.ssh.threads;
-
-
-import ch.ethz.ssh2.AuthenticationResult;
-import ch.ethz.ssh2.PtySettings;
-import ch.ethz.ssh2.ServerAuthenticationCallback;
-import ch.ethz.ssh2.ServerConnection;
-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 final Socket socket;
-    private final InetSocketAddress clientAddress;
-    private ServerConnection conn = null;
-    private final long sessionId;
-    private String currentUser;
-    private final String remoteAddressWithPort;
-    private final AuthProvider authProvider;
-
-
-    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));
-        netconf_ssh_socket_thread.setDaemon(true);
-        netconf_ssh_socket_thread.start();
-    }
-
-    private SocketThread(Socket socket,
-                         InetSocketAddress clientAddress,
-                         long sessionId,
-                         AuthProvider authProvider) throws IOException {
-
-        this.socket = socket;
-        this.clientAddress = clientAddress;
-        this.sessionId = sessionId;
-        this.remoteAddressWithPort = socket.getRemoteSocketAddress().toString().replaceFirst("/", "");
-        this.authProvider = authProvider;
-
-    }
-
-    @Override
-    public void run() {
-        conn = new ServerConnection(socket);
-        try {
-            conn.setPEMHostKey(authProvider.getPEMAsCharArray(), "netconf");
-        } catch (Exception e) {
-            logger.warn("Server authentication setup failed.", e);
-        }
-        conn.setAuthenticationCallback(this);
-        conn.setServerConnectionCallback(this);
-        try {
-            conn.connect();
-        } catch (IOException e) {
-            logger.error("SocketThread error ", e);
-        }
-    }
-
-    @Override
-    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() {
-                    @Override
-                    public void run() {
-                        if (subsystem.equals("netconf")) {
-                            IOThread netconf_ssh_input = null;
-                            IOThread netconf_ssh_output = null;
-                            try {
-                                String hostName = clientAddress.getHostName();
-                                int portNumber = clientAddress.getPort();
-                                final Socket echoSocket = new Socket(hostName, portNumber);
-                                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.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);
-                                netconf_ssh_output.setDaemon(false);
-                                netconf_ssh_output.start();
-
-                            } catch (Exception t) {
-                                logger.error("SSH bridge could not create echo socket: {}", t.getMessage(), t);
-
-                                try {
-                                    if (netconf_ssh_input != null) {
-                                        netconf_ssh_input.join();
-                                    }
-                                } catch (InterruptedException e1) {
-                                    Thread.currentThread().interrupt();
-                                    logger.error("netconf_ssh_input join error ", e1);
-                                }
-
-                                try {
-                                    if (netconf_ssh_output != null) {
-                                        netconf_ssh_output.join();
-                                    }
-                                } catch (InterruptedException e2) {
-                                    Thread.currentThread().interrupt();
-                                    logger.error("netconf_ssh_output join error ", e2);
-                                }
-                            }
-                        } else {
-                            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() {
-                    @Override
-                    public void run() {
-                        closeSession(ss, "PTY request not supported");
-                    }
-                };
-            }
-
-            @Override
-            public Runnable requestShell(final ServerSession ss) throws IOException {
-                return new Runnable() {
-                    @Override
-                    public void run() {
-                        closeSession(ss, "Shell not supported");
-                    }
-                };
-            }
-        };
-
-        return cb;
-    }
-
-    @Override
-    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};
-    }
-
-    @Override
-    public AuthenticationResult authenticateWithNone(ServerConnection sc, String username) {
-        return AuthenticationResult.FAILURE;
-    }
-
-    @Override
-    public AuthenticationResult authenticateWithPassword(ServerConnection sc, String username, String password) {
-
-        try {
-            if (authProvider.authenticated(username, password)) {
-                currentUser = username;
-                logger.trace("user {}@{} authenticated", currentUser, remoteAddressWithPort);
-                return AuthenticationResult.SUCCESS;
-            }
-        } catch (Exception e) {
-            logger.warn("Authentication failed due to :" + e.getLocalizedMessage());
-        }
-        return AuthenticationResult.FAILURE;
-    }
-
-    @Override
-    public AuthenticationResult authenticateWithPublicKey(ServerConnection sc, String username, String algorithm,
-                                                          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
deleted file mode 100644 (file)
index 298f91c..0000000
+++ /dev/null
@@ -1,65 +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;
-
-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();
-    }
-}
diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/SSHServerTest.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/SSHServerTest.java
deleted file mode 100644 (file)
index 663a0b4..0000000
+++ /dev/null
@@ -1,68 +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;
-
-import ch.ethz.ssh2.Connection;
-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 {
-
-    private static final String USER = "netconf";
-    private static final String PASSWORD  = "netconf";
-    private static final String HOST = "127.0.0.1";
-    private static final int PORT = 1830;
-    private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 8383);
-    private static final Logger logger =  LoggerFactory.getLogger(SSHServerTest.class);
-    private Thread sshServerThread;
-
-
-
-
-    public void startSSHServer() throws Exception{
-        logger.info("Creating SSH server");
-        StubUserManager um = new StubUserManager(USER,PASSWORD);
-        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);
-        sshServerThread.start();
-        logger.info("SSH server on");
-    }
-
-    @Test
-    public void connect(){
-        try {
-            this.startSSHServer();
-            Connection conn = new Connection(HOST,PORT);
-            Assert.assertNotNull(conn);
-            logger.info("connecting to SSH server");
-            conn.connect();
-            logger.info("authenticating ...");
-            boolean isAuthenticated = conn.authenticateWithPassword(USER,PASSWORD);
-            Assert.assertTrue(isAuthenticated);
-        } catch (Exception e) {
-            logger.error("Error while starting SSH server.", e);
-        }
-
-    }
-
-}
diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoClient.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoClient.java
new file mode 100644 (file)
index 0000000..5d0c71a
--- /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.netconf.netty;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.local.LocalAddress;
+import io.netty.channel.local.LocalChannel;
+import io.netty.channel.nio.NioEventLoopGroup;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Sends one message when a connection is open and echoes back any received
+ * data to the server.  Simply put, the echo client initiates the ping-pong
+ * traffic between the echo client and server by sending the first message to
+ * the server.
+ */
+public class EchoClient implements Runnable {
+    private static final Logger logger = LoggerFactory.getLogger(EchoClient.class);
+
+    private final ChannelHandler clientHandler;
+
+
+    public EchoClient(ChannelHandler clientHandler) {
+        this.clientHandler = clientHandler;
+    }
+
+    public void run() {
+        // Configure the client.
+        EventLoopGroup group = new NioEventLoopGroup();
+        try {
+            Bootstrap b = new Bootstrap();
+            b.group(group)
+                    .channel(LocalChannel.class)
+                    .handler(new ChannelInitializer<LocalChannel>() {
+                        @Override
+                        public void initChannel(LocalChannel ch) throws Exception {
+                            ch.pipeline().addLast(clientHandler);
+                        }
+                    });
+
+            // Start the client.
+            LocalAddress localAddress = new LocalAddress("foo");
+            ChannelFuture f = b.connect(localAddress).sync();
+
+            // Wait until the connection is closed.
+            f.channel().closeFuture().sync();
+        } catch (Exception e) {
+            logger.error("Error in client", e);
+            throw new RuntimeException("Error in client", e);
+        } finally {
+            // Shut down the event loop to terminate all threads.
+            logger.info("Client is shutting down");
+            group.shutdownGracefully();
+        }
+    }
+}
diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoClientHandler.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoClientHandler.java
new file mode 100644 (file)
index 0000000..81182a5
--- /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.netconf.netty;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.base.Charsets;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Handler implementation for the echo client.  It initiates the ping-pong
+ * traffic between the echo client and server by sending the first message to
+ * the server.
+ */
+public class EchoClientHandler extends ChannelInboundHandlerAdapter {
+    private static final Logger logger = LoggerFactory.getLogger(EchoClientHandler.class);
+
+    private ChannelHandlerContext ctx;
+
+    @Override
+    public void channelActive(ChannelHandlerContext ctx) {
+        checkState(this.ctx == null);
+        logger.info("client active");
+        this.ctx = ctx;
+    }
+
+    @Override
+    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+        ByteBuf bb = (ByteBuf) msg;
+        logger.info(">{}", bb.toString(Charsets.UTF_8));
+        bb.release();
+    }
+
+    @Override
+    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+        // Close the connection when an exception is raised.
+        logger.warn("Unexpected exception from downstream.", cause);
+        checkState(this.ctx.equals(ctx));
+        ctx.close();
+        this.ctx = null;
+    }
+
+    public void write(String message) {
+        ByteBuf byteBuf = Unpooled.copiedBuffer(message.getBytes());
+        ctx.writeAndFlush(byteBuf);
+    }
+}
diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoServer.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoServer.java
new file mode 100644 (file)
index 0000000..ec89d75
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse 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.netty;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.local.LocalAddress;
+import io.netty.channel.local.LocalChannel;
+import io.netty.channel.local.LocalServerChannel;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.handler.logging.LogLevel;
+import io.netty.handler.logging.LoggingHandler;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Echoes back any received data from a client.
+ */
+public class EchoServer implements Runnable {
+    private static final Logger logger = LoggerFactory.getLogger(EchoServer.class);
+
+    public void run() {
+        // Configure the server.
+        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
+        EventLoopGroup workerGroup = new NioEventLoopGroup();
+        try {
+            ServerBootstrap b = new ServerBootstrap();
+            b.group(bossGroup, workerGroup)
+                    .channel(LocalServerChannel.class)
+                    .option(ChannelOption.SO_BACKLOG, 100)
+                    .handler(new LoggingHandler(LogLevel.INFO))
+                    .childHandler(new ChannelInitializer<LocalChannel>() {
+                        @Override
+                        public void initChannel(LocalChannel ch) throws Exception {
+                            ch.pipeline().addLast(new EchoServerHandler());
+                        }
+                    });
+
+            // Start the server.
+            LocalAddress localAddress = NetconfConfigUtil.getNetconfLocalAddress();
+            ChannelFuture f = b.bind(localAddress).sync();
+
+            // Wait until the server socket is closed.
+            f.channel().closeFuture().sync();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        } finally {
+            // Shut down all event loops to terminate all threads.
+            bossGroup.shutdownGracefully();
+            workerGroup.shutdownGracefully();
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        new Thread(new EchoServer()).start();
+        Thread.sleep(1000);
+        EchoClientHandler clientHandler = new EchoClientHandler();
+        EchoClient echoClient = new EchoClient(clientHandler);
+        new Thread(echoClient).start();
+
+        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
+        do {
+            String message = reader.readLine();
+            if (message == null ||  "exit".equalsIgnoreCase(message)) {
+                break;
+            }
+            logger.debug("Got '{}'", message);
+            clientHandler.write(message);
+        } while (true);
+        System.exit(0);
+    }
+}
diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoServerHandler.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoServerHandler.java
new file mode 100644 (file)
index 0000000..1286ec6
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.netty;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Splitter;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandler.Sharable;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Handler implementation for the echo server.
+ */
+@Sharable
+public class EchoServerHandler extends ChannelInboundHandlerAdapter {
+
+    private static final Logger logger = LoggerFactory.getLogger(EchoServerHandler.class.getName());
+    private String fromLastNewLine = "";
+    private final Splitter splitter = Splitter.onPattern("\r?\n");
+    @Override
+    public void channelActive(ChannelHandlerContext ctx) throws Exception {
+        logger.debug("sleep start");
+        Thread.sleep(1000);
+        logger.debug("sleep done");
+    }
+
+    @Override
+    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+        ByteBuf byteBuf = (ByteBuf) msg;
+        String message = byteBuf.toString(Charsets.UTF_8);
+        logger.info("writing back '{}'", message);
+        ctx.write(msg);
+        fromLastNewLine += message;
+        for (String line : splitter.split(fromLastNewLine)) {
+            if ("quit".equals(line)) {
+                logger.info("closing server ctx");
+                ctx.flush();
+                ctx.close();
+                break;
+            }
+            fromLastNewLine = line; // last line should be preserved
+        }
+
+        // do not release byteBuf as it is handled back
+    }
+
+    @Override
+    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
+        logger.debug("flushing");
+        ctx.flush();
+    }
+}
diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/ProxyServer.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/ProxyServer.java
new file mode 100644 (file)
index 0000000..8f2c502
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.netty;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.local.LocalAddress;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.logging.LogLevel;
+import io.netty.handler.logging.LoggingHandler;
+import java.net.InetSocketAddress;
+import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
+
+public class ProxyServer implements Runnable {
+    private final ProxyHandlerFactory proxyHandlerFactory;
+
+    public ProxyServer(ProxyHandlerFactory proxyHandlerFactory) {
+        this.proxyHandlerFactory = proxyHandlerFactory;
+    }
+
+    public void run() {
+        // Configure the server.
+        final EventLoopGroup bossGroup = new NioEventLoopGroup();
+        EventLoopGroup workerGroup = new NioEventLoopGroup();
+        try {
+            final LocalAddress localAddress = NetconfConfigUtil.getNetconfLocalAddress();
+            ServerBootstrap serverBootstrap = new ServerBootstrap();
+            serverBootstrap.group(bossGroup, workerGroup)
+                    .channel(NioServerSocketChannel.class)
+                    .option(ChannelOption.SO_BACKLOG, 100)
+                    .handler(new LoggingHandler(LogLevel.INFO))
+                    .childHandler(new ChannelInitializer<SocketChannel>() {
+                        @Override
+                        public void initChannel(SocketChannel ch) throws Exception {
+                            ch.pipeline().addLast(proxyHandlerFactory.create(bossGroup, localAddress));
+                        }
+                    });
+
+            // Start the server.
+            InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8080);
+            ChannelFuture f = serverBootstrap.bind(address).sync();
+
+            // Wait until the server socket is closed.
+            f.channel().closeFuture().sync();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        } finally {
+            // Shut down all event loops to terminate all threads.
+            bossGroup.shutdownGracefully();
+            workerGroup.shutdownGracefully();
+        }
+    }
+    public static interface ProxyHandlerFactory {
+        ChannelHandler create(EventLoopGroup bossGroup, LocalAddress localAddress);
+    }
+
+    public static void main(String[] args) {
+        ProxyHandlerFactory proxyHandlerFactory = new ProxyHandlerFactory() {
+            @Override
+            public ChannelHandler create(EventLoopGroup bossGroup, LocalAddress localAddress) {
+                return new ProxyServerHandler(bossGroup, localAddress);
+            }
+        };
+        start(proxyHandlerFactory);
+    }
+
+    public static void start(ProxyHandlerFactory proxyHandlerFactory) {
+        new Thread(new EchoServer()).start();
+        new Thread(new ProxyServer(proxyHandlerFactory)).start();
+    }
+}
diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/ProxyServerHandler.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/ProxyServerHandler.java
new file mode 100644 (file)
index 0000000..ecab212
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.netty;
+
+import com.google.common.base.Charsets;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.local.LocalAddress;
+import io.netty.channel.local.LocalChannel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ProxyServerHandler extends ChannelInboundHandlerAdapter {
+    private static final Logger logger = LoggerFactory.getLogger(ProxyServerHandler.class.getName());
+    private final Bootstrap clientBootstrap;
+    private final LocalAddress localAddress;
+
+
+    private Channel clientChannel;
+
+    public ProxyServerHandler(EventLoopGroup bossGroup, LocalAddress localAddress) {
+        clientBootstrap = new Bootstrap();
+        clientBootstrap.group(bossGroup).channel(LocalChannel.class);
+        this.localAddress = localAddress;
+    }
+
+    @Override
+    public void channelActive(ChannelHandlerContext remoteCtx) {
+        final ProxyClientHandler clientHandler = new ProxyClientHandler(remoteCtx);
+        clientBootstrap.handler(new ChannelInitializer<LocalChannel>() {
+            @Override
+            public void initChannel(LocalChannel ch) throws Exception {
+                ch.pipeline().addLast(clientHandler);
+            }
+        });
+        ChannelFuture clientChannelFuture = clientBootstrap.connect(localAddress).awaitUninterruptibly();
+        clientChannel = clientChannelFuture.channel();
+        clientChannel.writeAndFlush(Unpooled.copiedBuffer("connected\n".getBytes()));
+    }
+
+    @Override
+    public void channelInactive(ChannelHandlerContext ctx) {
+        logger.info("channelInactive - closing client connection");
+        clientChannel.close();
+    }
+
+    @Override
+    public void channelRead(ChannelHandlerContext ctx, final Object msg) {
+        logger.debug("Writing to client {}", msg);
+        clientChannel.write(msg);
+    }
+
+    @Override
+    public void channelReadComplete(ChannelHandlerContext ctx) {
+        logger.debug("flushing");
+        clientChannel.flush();
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+        // Close the connection when an exception is raised.
+        logger.warn("Unexpected exception from downstream.", cause);
+        ctx.close();
+    }
+}
+
+class ProxyClientHandler extends ChannelInboundHandlerAdapter {
+    private static final Logger logger = LoggerFactory.getLogger(ProxyClientHandler.class);
+
+    private final ChannelHandlerContext remoteCtx;
+
+
+    public ProxyClientHandler(ChannelHandlerContext remoteCtx) {
+        this.remoteCtx = remoteCtx;
+    }
+
+    @Override
+    public void channelActive(ChannelHandlerContext ctx) {
+        logger.info("client active");
+    }
+
+    @Override
+    public void channelRead(ChannelHandlerContext ctx, Object msg) {
+        ByteBuf bb = (ByteBuf) msg;
+        logger.info(">{}", bb.toString(Charsets.UTF_8));
+        remoteCtx.write(msg);
+    }
+
+    @Override
+    public void channelReadComplete(ChannelHandlerContext ctx) {
+        logger.debug("Flushing server ctx");
+        remoteCtx.flush();
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+        // Close the connection when an exception is raised.
+        logger.warn("Unexpected exception from downstream", cause);
+        ctx.close();
+    }
+
+    // called both when local or remote connection dies
+    @Override
+    public void channelInactive(ChannelHandlerContext ctx) {
+        logger.debug("channelInactive() called, closing remote client ctx");
+        remoteCtx.close();
+    }
+}
diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/SSHTest.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/SSHTest.java
new file mode 100644 (file)
index 0000000..4e32e82
--- /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.netconf.netty;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import io.netty.channel.nio.NioEventLoopGroup;
+import org.junit.Test;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SSHTest {
+    public static final Logger logger = LoggerFactory.getLogger(SSHTest.class);
+
+    @Test
+    public void test() throws Exception {
+        new Thread(new EchoServer(), "EchoServer").start();
+        AuthProvider authProvider = mock(AuthProvider.class);
+        doReturn(PEMGenerator.generate().toCharArray()).when(authProvider).getPEMAsCharArray();
+        doReturn(true).when(authProvider).authenticated(anyString(), anyString());
+        NetconfSSHServer thread = NetconfSSHServer.start(1831, NetconfConfigUtil.getNetconfLocalAddress(), authProvider, new NioEventLoopGroup());
+        Thread.sleep(2000);
+        logger.info("Closing socket");
+        thread.close();
+        thread.join();
+    }
+}
diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/ssh/authentication/SSHServerTest.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/ssh/authentication/SSHServerTest.java
new file mode 100644 (file)
index 0000000..5e368bc
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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 static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+
+import ch.ethz.ssh2.Connection;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import junit.framework.Assert;
+import org.apache.commons.io.IOUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.netconf.StubUserManager;
+import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
+import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class SSHServerTest {
+
+    private static final String USER = "netconf";
+    private static final String PASSWORD = "netconf";
+    private static final String HOST = "127.0.0.1";
+    private static final int PORT = 1830;
+    private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 8383);
+    private static final Logger logger = LoggerFactory.getLogger(SSHServerTest.class);
+    private Thread sshServerThread;
+
+    @Mock
+    private BundleContext mockedContext;
+
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        doReturn(null).when(mockedContext).createFilter(anyString());
+        doNothing().when(mockedContext).addServiceListener(any(ServiceListener.class), anyString());
+        doReturn(new ServiceReference[0]).when(mockedContext).getServiceReferences(anyString(), anyString());
+
+        logger.info("Creating SSH server");
+        StubUserManager um = new StubUserManager(USER, PASSWORD);
+        String pem;
+        try (InputStream is = getClass().getResourceAsStream("/RSA.pk")) {
+            pem = IOUtils.toString(is);
+        }
+        AuthProvider ap = new AuthProvider(pem, mockedContext);
+        ap.setNullableUserManager(um);
+        EventLoopGroup bossGroup = new NioEventLoopGroup();
+        NetconfSSHServer server = NetconfSSHServer.start(PORT, NetconfConfigUtil.getNetconfLocalAddress(),
+                ap, bossGroup);
+
+        sshServerThread = new Thread(server);
+        sshServerThread.setDaemon(true);
+        sshServerThread.start();
+        logger.info("SSH server on " + PORT);
+    }
+
+    @Test
+    public void connect() {
+        try {
+            Connection conn = new Connection(HOST, PORT);
+            Assert.assertNotNull(conn);
+            logger.info("connecting to SSH server");
+            conn.connect();
+            logger.info("authenticating ...");
+            boolean isAuthenticated = conn.authenticateWithPassword(USER, PASSWORD);
+            Assert.assertTrue(isAuthenticated);
+        } catch (Exception e) {
+            logger.error("Error while starting SSH server.", e);
+        }
+
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-ssh/src/test/resources/logback-test.xml b/opendaylight/netconf/netconf-ssh/src/test/resources/logback-test.xml
new file mode 100644 (file)
index 0000000..324c234
--- /dev/null
@@ -0,0 +1,13 @@
+<configuration>
+
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder>
+      <pattern>%date{"yyyy-MM-dd HH:mm:ss.SSS z"} [%thread] %-5level %logger{36} - %msg%n</pattern>
+    </encoder>
+  </appender>
+
+  <root level="error">
+    <appender-ref ref="STDOUT" />
+  </root>
+  <logger name="org.opendaylight.controller.netconf" level="TRACE"/>
+</configuration>
diff --git a/opendaylight/netconf/netconf-tcp/pom.xml b/opendaylight/netconf/netconf-tcp/pom.xml
new file mode 100644 (file)
index 0000000..65da6e9
--- /dev/null
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+  ~
+  ~ This program and the accompanying materials are made available under the
+  ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+  ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>netconf-subsystem</artifactId>
+    <version>0.2.5-SNAPSHOT</version>
+    <relativePath>../</relativePath>
+  </parent>
+  <artifactId>netconf-tcp</artifactId>
+  <packaging>bundle</packaging>
+  <name>${project.artifactId}</name>
+
+  <dependencies>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>netconf-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>netconf-util</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>mockito-configuration</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>2.3.7</version>
+        <configuration>
+          <instructions>
+            <Bundle-Activator>org.opendaylight.controller.netconf.tcp.osgi.NetconfTCPActivator</Bundle-Activator>
+            <Import-Package>com.google.common.base, io.netty.bootstrap, io.netty.channel, io.netty.channel.local,
+              io.netty.channel.nio, io.netty.channel.socket, io.netty.channel.socket.nio, io.netty.handler.logging,
+              io.netty.util.concurrent, org.opendaylight.controller.netconf.util.osgi, org.osgi.framework, org.slf4j</Import-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/opendaylight/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/netconf/tcp/netty/ProxyServer.java b/opendaylight/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/netconf/tcp/netty/ProxyServer.java
new file mode 100644 (file)
index 0000000..2e0022c
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.tcp.netty;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.local.LocalAddress;
+import io.netty.channel.local.LocalChannel;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.logging.LogLevel;
+import io.netty.handler.logging.LoggingHandler;
+import java.net.InetSocketAddress;
+
+public class ProxyServer implements AutoCloseable {
+    private final EventLoopGroup bossGroup = new NioEventLoopGroup();
+    private final EventLoopGroup workerGroup = new NioEventLoopGroup();
+    private final ChannelFuture channelFuture;
+
+    public ProxyServer(InetSocketAddress address, final LocalAddress localAddress) {
+        // Configure the server.
+        final Bootstrap clientBootstrap = new Bootstrap();
+        clientBootstrap.group(bossGroup).channel(LocalChannel.class);
+
+        ServerBootstrap serverBootstrap = new ServerBootstrap();
+        serverBootstrap.group(bossGroup, workerGroup)
+                .channel(NioServerSocketChannel.class)
+                .handler(new LoggingHandler(LogLevel.DEBUG))
+                .childHandler(new ChannelInitializer<SocketChannel>() {
+                    @Override
+                    public void initChannel(SocketChannel ch) throws Exception {
+                        ch.pipeline().addLast(new ProxyServerHandler(clientBootstrap, localAddress));
+                    }
+                });
+
+        // Start the server.
+        channelFuture = serverBootstrap.bind(address).syncUninterruptibly();
+    }
+
+    @Override
+    public void close() {
+        channelFuture.channel().close();
+        bossGroup.shutdownGracefully();
+        workerGroup.shutdownGracefully();
+    }
+}
diff --git a/opendaylight/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/netconf/tcp/netty/ProxyServerHandler.java b/opendaylight/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/netconf/tcp/netty/ProxyServerHandler.java
new file mode 100644 (file)
index 0000000..fa88928
--- /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.netconf.tcp.netty;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.local.LocalAddress;
+import io.netty.channel.local.LocalChannel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ProxyServerHandler extends ChannelInboundHandlerAdapter {
+    private static final Logger logger = LoggerFactory.getLogger(ProxyServerHandler.class.getName());
+    private final Bootstrap clientBootstrap;
+    private final LocalAddress localAddress;
+
+    private Channel clientChannel;
+
+    public ProxyServerHandler(Bootstrap clientBootstrap, LocalAddress localAddress) {
+        this.clientBootstrap = clientBootstrap;
+        this.localAddress = localAddress;
+    }
+
+    @Override
+    public void channelActive(ChannelHandlerContext remoteCtx) {
+        final ProxyClientHandler clientHandler = new ProxyClientHandler(remoteCtx);
+        clientBootstrap.handler(new ChannelInitializer<LocalChannel>() {
+            @Override
+            public void initChannel(LocalChannel ch) throws Exception {
+                ch.pipeline().addLast(clientHandler);
+            }
+        });
+        ChannelFuture clientChannelFuture = clientBootstrap.connect(localAddress).awaitUninterruptibly();
+        clientChannel = clientChannelFuture.channel();
+    }
+
+    @Override
+    public void channelInactive(ChannelHandlerContext ctx) {
+        logger.trace("channelInactive - closing client channel");
+        clientChannel.close();
+    }
+
+    @Override
+    public void channelRead(ChannelHandlerContext ctx, final Object msg) {
+        logger.trace("Writing to client channel");
+        clientChannel.write(msg);
+    }
+
+    @Override
+    public void channelReadComplete(ChannelHandlerContext ctx) {
+        logger.trace("Flushing client channel");
+        clientChannel.flush();
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+        // Close the connection when an exception is raised.
+        logger.warn("Unexpected exception from downstream.", cause);
+        ctx.close();
+    }
+}
+
+class ProxyClientHandler extends ChannelInboundHandlerAdapter {
+    private static final Logger logger = LoggerFactory.getLogger(ProxyClientHandler.class);
+
+    private final ChannelHandlerContext remoteCtx;
+    private ChannelHandlerContext localCtx;
+
+    public ProxyClientHandler(ChannelHandlerContext remoteCtx) {
+        this.remoteCtx = remoteCtx;
+    }
+
+    @Override
+    public void channelActive(ChannelHandlerContext ctx) {
+        checkState(this.localCtx == null);
+        logger.trace("Client channel active");
+        this.localCtx = ctx;
+    }
+
+    @Override
+    public void channelRead(ChannelHandlerContext ctx, Object msg) {
+        logger.trace("Forwarding message");
+        remoteCtx.write(msg);
+    }
+
+    @Override
+    public void channelReadComplete(ChannelHandlerContext ctx) {
+        logger.trace("Flushing remote ctx");
+        remoteCtx.flush();
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+        // Close the connection when an exception is raised.
+        logger.warn("Unexpected exception from downstream", cause);
+        checkState(this.localCtx.equals(ctx));
+        ctx.close();
+    }
+
+    // called both when local or remote connection dies
+    @Override
+    public void channelInactive(ChannelHandlerContext ctx) {
+        logger.trace("channelInactive() called, closing remote client ctx");
+        remoteCtx.close();
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/netconf/tcp/osgi/NetconfTCPActivator.java b/opendaylight/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/netconf/tcp/osgi/NetconfTCPActivator.java
new file mode 100644 (file)
index 0000000..bc94e59
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.tcp.osgi;
+
+import com.google.common.base.Optional;
+import java.net.InetSocketAddress;
+import org.opendaylight.controller.netconf.tcp.netty.ProxyServer;
+import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
+import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil.InfixProp;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Opens TCP port specified in config.ini, creates bridge between this port and local netconf server.
+ */
+public class NetconfTCPActivator implements BundleActivator {
+    private static final Logger logger = LoggerFactory.getLogger(NetconfTCPActivator.class);
+    private ProxyServer proxyServer;
+
+    @Override
+    public void start(BundleContext context) {
+        final Optional<InetSocketAddress> maybeAddress = NetconfConfigUtil.extractNetconfServerAddress(context, InfixProp.tcp);
+        if (maybeAddress.isPresent() == false) {
+            logger.debug("Netconf tcp server is not configured to start");
+            return;
+        }
+        InetSocketAddress address = maybeAddress.get();
+        if (address.getAddress().isAnyLocalAddress()) {
+            logger.warn("Unprotected netconf TCP address is configured to ANY local address. This is a security risk. " +
+                    "Consider changing {} to 127.0.0.1", NetconfConfigUtil.getNetconfServerAddressKey(InfixProp.tcp));
+        }
+        logger.info("Starting TCP netconf server at {}", address);
+        proxyServer = new ProxyServer(address, NetconfConfigUtil.getNetconfLocalAddress());
+    }
+
+    @Override
+    public void stop(BundleContext context) {
+        if (proxyServer != null) {
+            proxyServer.close();
+        }
+    }
+}
index dcbdcabbba2683b260b003624c189561a134de2a..d9d957c663766ccb847cc57a3cde920b19d6cfd2 100644 (file)
       <plugin>
         <groupId>org.apache.felix</groupId>
         <artifactId>maven-bundle-plugin</artifactId>
+        <version>2.3.7</version>
         <configuration>
           <instructions>
             <Import-Package>com.google.common.base, com.google.common.collect, io.netty.channel,
               io.netty.util.concurrent, javax.annotation, javax.xml.namespace, javax.xml.parsers, javax.xml.transform,
               javax.xml.transform.dom, javax.xml.transform.stream, javax.xml.validation, javax.xml.xpath,
               org.opendaylight.controller.netconf.api, org.opendaylight.controller.netconf.mapping.api,
-              org.osgi.framework, org.slf4j, org.w3c.dom, org.xml.sax</Import-Package>
+              org.osgi.framework, org.slf4j, org.w3c.dom, org.xml.sax,io.netty.channel.local</Import-Package>
             <Export-Package>org.opendaylight.controller.netconf.util.*</Export-Package>
           </instructions>
         </configuration>
index 0993b8ad0c7038e5b86b0fee0e5cb4d30fd701f8..333fea3493172286fdba2c807eff105760741411 100644 (file)
@@ -9,36 +9,35 @@
 package org.opendaylight.controller.netconf.util.osgi;
 
 import com.google.common.base.Optional;
+import io.netty.channel.local.LocalAddress;
+import java.net.InetSocketAddress;
 import org.osgi.framework.BundleContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.net.InetSocketAddress;
-
 public final class NetconfConfigUtil {
     private static final Logger logger = LoggerFactory.getLogger(NetconfConfigUtil.class);
 
-    public static final InetSocketAddress DEFAULT_NETCONF_TCP_ADDRESS
-            = new InetSocketAddress("127.0.0.1", 8383);
-    public static final InetSocketAddress DEFAULT_NETCONF_SSH_ADDRESS
-            = new InetSocketAddress("0.0.0.0", 1830);
-
     private static final String PREFIX_PROP = "netconf.";
 
     private NetconfConfigUtil() {
     }
 
-    private enum InfixProp {
+    public enum InfixProp {
         tcp, ssh
     }
 
     private static final String PORT_SUFFIX_PROP = ".port";
     private static final String ADDRESS_SUFFIX_PROP = ".address";
-    private static final String CLIENT_PROP = ".client";
     private static final String PRIVATE_KEY_PATH_PROP = ".pk.path";
 
     private static final String CONNECTION_TIMEOUT_MILLIS_PROP = "connectionTimeoutMillis";
     private static final long DEFAULT_TIMEOUT_MILLIS = 5000;
+    private static final LocalAddress netconfLocalAddress = new LocalAddress("netconf");
+
+    public static LocalAddress getNetconfLocalAddress() {
+        return netconfLocalAddress;
+    }
 
     public static long extractTimeoutMillis(final BundleContext bundleContext) {
         final String key = PREFIX_PROP + CONNECTION_TIMEOUT_MILLIS_PROP;
@@ -54,22 +53,6 @@ public final class NetconfConfigUtil {
         }
     }
 
-    public static InetSocketAddress extractTCPNetconfServerAddress(final BundleContext context, final InetSocketAddress defaultAddress) {
-        final Optional<InetSocketAddress> extracted = extractNetconfServerAddress(context, InfixProp.tcp);
-        final InetSocketAddress netconfTcpAddress = getNetconfAddress(defaultAddress, extracted, InfixProp.tcp);
-        logger.debug("Using {} as netconf tcp address", netconfTcpAddress);
-        if (netconfTcpAddress.getAddress().isAnyLocalAddress()) {
-            logger.warn("Unprotected netconf TCP address is configured to ANY local address. This is a security risk. " +
-                    "Consider changing {} to 127.0.0.1", PREFIX_PROP + InfixProp.tcp + ADDRESS_SUFFIX_PROP);
-        }
-        return netconfTcpAddress;
-    }
-
-    public static InetSocketAddress extractTCPNetconfClientAddress(final BundleContext context, final InetSocketAddress defaultAddress) {
-        final Optional<InetSocketAddress> extracted = extractNetconfClientAddress(context, InfixProp.tcp);
-        return getNetconfAddress(defaultAddress, extracted, InfixProp.tcp);
-    }
-
     /**
      * Get extracted address or default.
      *
@@ -93,15 +76,12 @@ public final class NetconfConfigUtil {
         return inetSocketAddress;
     }
 
-    public static InetSocketAddress extractSSHNetconfAddress(final BundleContext context, final InetSocketAddress defaultAddress) {
-        Optional<InetSocketAddress> extractedAddress = extractNetconfServerAddress(context, InfixProp.ssh);
-        InetSocketAddress netconfSSHAddress = getNetconfAddress(defaultAddress, extractedAddress, InfixProp.ssh);
-        logger.debug("Using {} as netconf SSH address", netconfSSHAddress);
-        return netconfSSHAddress;
+    public static String getPrivateKeyPath(final BundleContext context) {
+        return getPropertyValue(context, getPrivateKeyKey());
     }
 
-    public static String getPrivateKeyPath(final BundleContext context) {
-        return getPropertyValue(context, PREFIX_PROP + InfixProp.ssh + PRIVATE_KEY_PATH_PROP);
+    public static String getPrivateKeyKey() {
+        return PREFIX_PROP + InfixProp.ssh + PRIVATE_KEY_PATH_PROP;
     }
 
     private static String getPropertyValue(final BundleContext context, final String propertyName) {
@@ -112,16 +92,20 @@ public final class NetconfConfigUtil {
         return propertyValue;
     }
 
+    public static String getNetconfServerAddressKey(InfixProp infixProp) {
+        return PREFIX_PROP + infixProp + ADDRESS_SUFFIX_PROP;
+    }
+
     /**
      * @param context   from which properties are being read.
      * @param infixProp either tcp or ssh
      * @return value if address and port are present and valid, Optional.absent otherwise.
      * @throws IllegalStateException if address or port are invalid, or configuration is missing
      */
-    private static Optional<InetSocketAddress> extractNetconfServerAddress(final BundleContext context,
+    public static Optional<InetSocketAddress> extractNetconfServerAddress(final BundleContext context,
                                                                            final InfixProp infixProp) {
 
-        final Optional<String> address = getProperty(context, PREFIX_PROP + infixProp + ADDRESS_SUFFIX_PROP);
+        final Optional<String> address = getProperty(context, getNetconfServerAddressKey(infixProp));
         final Optional<String> port = getProperty(context, PREFIX_PROP + infixProp + PORT_SUFFIX_PROP);
 
         if (address.isPresent() && port.isPresent()) {
@@ -140,24 +124,6 @@ public final class NetconfConfigUtil {
         return new InetSocketAddress(address.get(), portNumber);
     }
 
-    private static Optional<InetSocketAddress> extractNetconfClientAddress(final BundleContext context,
-                                                                           final InfixProp infixProp) {
-        final Optional<String> address = getProperty(context,
-                PREFIX_PROP + infixProp + CLIENT_PROP + ADDRESS_SUFFIX_PROP);
-        final Optional<String> port = getProperty(context,
-                PREFIX_PROP + infixProp + CLIENT_PROP + PORT_SUFFIX_PROP);
-
-        if (address.isPresent() && port.isPresent()) {
-            try {
-                return Optional.of(parseAddress(address, port));
-            } catch (final RuntimeException e) {
-                logger.warn("Unable to parse client {} netconf address from {}:{}, fallback to server address",
-                        infixProp, address, port, e);
-            }
-        }
-        return extractNetconfServerAddress(context, infixProp);
-    }
-
     private static Optional<String> getProperty(final BundleContext context, final String propKey) {
         String value = context.getProperty(propKey);
         if (value != null && value.isEmpty()) {
index 4f87fd8626b2d1cfd98925c0e3be5015808b41a3..d26fcf987e5469388b5c01bac3a391b805a46536 100644 (file)
@@ -27,6 +27,7 @@
     <module>netconf-mapping-api</module>
     <module>netconf-client</module>
     <module>netconf-ssh</module>
+    <module>netconf-tcp</module>
     <module>netconf-monitoring</module>
     <module>ietf-netconf-monitoring</module>
     <module>ietf-netconf-monitoring-extension</module>
 
     </pluginManagement>
     <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+        <version>2.12</version>
+        <configuration>
+          <failsOnError>false</failsOnError>
+          <failOnViolation>false</failOnViolation>
+          <configLocation>checkstyle-logging.xml</configLocation>
+          <consoleOutput>true</consoleOutput>
+          <includeTestSourceDirectory>true</includeTestSourceDirectory>
+          <sourceDirectory>${project.basedir}</sourceDirectory>
+          <includes>**\/*.java,**\/*.xml,**\/*.ini,**\/*.sh,**\/*.bat,**\/*.yang</includes>
+          <excludes>**\/target\/,**\/bin\/,**\/target-ide\/,**\/${jmxGeneratorPath}\/,**\/${salGeneratorPath}\/</excludes>
+        </configuration>
+        <dependencies>
+          <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>checkstyle-logging</artifactId>
+            <version>${yangtools.version}</version>
+          </dependency>
+        </dependencies>
+        <executions>
+          <execution>
+            <goals>
+              <goal>check</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-compiler-plugin</artifactId>
index 4b2badd92db8baa5cde25a3dbb8a7e2f40d9967a..8253ac46d3f5405ace57da8022be90a183645a68 100644 (file)
@@ -230,4 +230,12 @@ public class TCP extends Packet {
         return (BitBufferHelper.getShort(fieldValues.get(DESTPORT)));
     }
 
+    /**
+     * Get the stored checksum value of the TCP header
+     * @return short - the checksum
+     */
+    public short getChecksum() {
+        return (BitBufferHelper.getShort(fieldValues.get(CHECKSUM)));
+    }
+
 }
index 48679c33f287ced6dab0d97141acd4b40d077a4c..3e18aedfdb38b7238ef9a72c9d49bff151aec685 100644 (file)
@@ -104,4 +104,13 @@ public class TCPTest {
         Assert.assertTrue(urgentPointer[1] == 10);
 
     }
+
+    @Test
+    public void testGetChecksum() {
+        TCP tcp = new TCP();
+        byte[] udpChecksum = { 0, -56 };
+        tcp.hdrFieldsMap.put("Checksum", udpChecksum);
+        short checksum = tcp.getChecksum();
+        Assert.assertTrue(checksum == 200);
+    }
 }