Initial code drop of netconf protocol implementation 76/1776/9
authorMaros Marsalek <mmarsale@cisco.com>
Tue, 8 Oct 2013 16:26:35 +0000 (18:26 +0200)
committerGerrit Code Review <gerrit@opendaylight.org>
Mon, 14 Oct 2013 14:45:34 +0000 (14:45 +0000)
This implementation is based on infrastructure provided by the framework artifact
and contains server as well as client side code.

Netconf subsystem structure:
Netconf-api:
Api definition for client and server.

Netconf-impl:
Netconf server implementation. Server handles basic communication
and delegates handling of rpcs to an implementation of netconf-mapping-api.

Netconf-mapping-api:
Api definition for pluggable rpcs handler. Implementations of this api
are plugged dynamically into netconf-impl using OSGi apis.

Config-netconf-connector:
Implementation of netconf-mapping-api that handles netconf rpcs and delegates
requests to configuration subsystem

Netconf-util:
Utility classes used by client and server code.

Netconf-client:
Netconf client implementation.

Netconf-it:
Integration tests for netconf. These tests verify correct cooperation of netconf server,
config-netconf-connector, config subsystem and netconf client.

Config-persister-api:
Api definition for config persister. Config persister is a component that is responsible
for storing configuration snapshots after every change to the configuration via netconf rpcs.
(Pushed to config-subsystem)

Config-persister-impl:
Implementation of config persister that receives notifications from netconf-impl about changes
to the configuration and uses pluggable adapters to store received snapshots.

Config-persister-file-adapter:
Implementation of config persister adapter that stores and restores config snapshots from a file.
(Pushed to config-subsystem)

In order to run netconf in OSGi, some configuration issues had to be resolved
in config-subsystem bundles.

Change-Id: I8e0421c924b0714a4d49962c4bb5ca01ef68ac78
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
275 files changed:
opendaylight/commons/opendaylight/pom.xml
opendaylight/config/config-api/pom.xml
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/stat/ConfigProvider.java [new file with mode: 0644]
opendaylight/config/config-persister-api/pom.xml [new file with mode: 0644]
opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/Persister.java [new file with mode: 0644]
opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/storage/StorageAdapter.java [new file with mode: 0644]
opendaylight/config/config-persister-file-adapter/pom.xml [new file with mode: 0644]
opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java [new file with mode: 0644]
opendaylight/config/config-persister-file-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapterTest.java [new file with mode: 0644]
opendaylight/config/pom.xml
opendaylight/config/yang-jmx-generator-plugin/pom.xml
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGenerator.java
opendaylight/config/yang-jmx-generator/pom.xml
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java
opendaylight/config/yang-store-impl/pom.xml
opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked1.txt [new file with mode: 0644]
opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked2.txt [new file with mode: 0644]
opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked3.txt [new file with mode: 0644]
opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked4.txt [new file with mode: 0644]
opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked5.txt [new file with mode: 0644]
opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_config_bean_response.txt [new file with mode: 0644]
opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_lookupConfigBeans.txt [new file with mode: 0644]
opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_commit.xml [new file with mode: 0644]
opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_candidate.xml [new file with mode: 0644]
opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_running.xml [new file with mode: 0644]
opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_modify_candidate.xml [new file with mode: 0644]
opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_candidate.xml [new file with mode: 0644]
opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_running.xml [new file with mode: 0644]
opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/server_error_missing_attribute.xml [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/logback.xml
opendaylight/netconf/config-netconf-connector/pom.xml [new file with mode: 0755]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/AttributeIfcSwitchStatement.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AbstractAttributeReadingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ArrayAttributeReadingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AttributeConfigElement.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AttributeReadingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/CompositeAttributeReadingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ObjectNameAttributeReadingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ObjectXmlReader.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/SimpleAttributeReadingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/AbstractAttributeMappingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ArrayAttributeMappingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/AttributeMappingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/CompositeAttributeMappingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectMapper.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectNameAttributeMappingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/SimpleAttributeMappingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/AbstractAttributeResolvingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ArrayAttributeResolvingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/AttributeResolvingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/CompositeAttributeResolvingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ObjectNameAttributeResolvingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ObjectResolver.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/SimpleAttributeResolvingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ArrayAttributeWritingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/AttributeWritingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/CompositeAttributeWritingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectNameAttributeWritingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectXmlWriter.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/RuntimeBeanEntryWritingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/SimpleAttributeWritingStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/Config.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfig.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfigElementResolved.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/ModuleConfig.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/ModuleElementResolved.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/Services.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/InstanceRuntimeRpc.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/ModuleRpcs.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/Rpcs.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/InstanceRuntime.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/ModuleRuntime.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/Runtime.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/AbstractConfigNetconfOperation.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Commit.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Datastore.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/DiscardChanges.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Validate.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/AbstractEditConfigStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/DeleteEditConfigStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfig.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigXmlParser.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditStrategyType.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/MergeEditConfigStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/NoneEditConfigStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/RemoveEditConfigStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/ReplaceEditConfigStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/CandidateDatastoreQueryStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/DatastoreQueryStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/GetConfig.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/RunningDatastoreQueryStrategy.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/runtimerpc/RuntimeRpc.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/runtimerpc/RuntimeRpcElementResolved.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/Activator.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceFactoryImpl.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImpl.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/YangStoreServiceTracker.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/transactions/TransactionProvider.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/util/Util.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/ServiceTrackerTest.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/ValidateTest.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigTest.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/ReplaceEditConfigStrategyTest.java [new file with mode: 0644]
opendaylight/netconf/config-persister-impl/pom.xml [new file with mode: 0644]
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java [new file with mode: 0644]
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/NoOpStorageAdapter.java [new file with mode: 0644]
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterImpl.java [new file with mode: 0644]
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/Util.java [new file with mode: 0644]
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java [new file with mode: 0644]
opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/client_hello.xml [new file with mode: 0644]
opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/commit.xml [new file with mode: 0644]
opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/editConfig.xml [new file with mode: 0644]
opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/PersisterImplTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-api/pom.xml [new file with mode: 0644]
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfDeserializerException.java [new file with mode: 0644]
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfDocumentedException.java [new file with mode: 0644]
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfMessage.java [new file with mode: 0644]
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfOperationRouter.java [new file with mode: 0644]
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfServerSessionPreferences.java [new file with mode: 0644]
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java [new file with mode: 0644]
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSessionListener.java [new file with mode: 0644]
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSessionPreferences.java [new file with mode: 0644]
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfTerminationReason.java [new file with mode: 0644]
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/jmx/CommitJMXNotification.java [new file with mode: 0644]
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/jmx/DefaultCommitOperationMXBean.java [new file with mode: 0644]
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/jmx/NetconfJMXNotification.java [new file with mode: 0644]
opendaylight/netconf/netconf-client/pom.xml [new file with mode: 0644]
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClient.java [new file with mode: 0644]
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientDispatcher.java [new file with mode: 0644]
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java [new file with mode: 0644]
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionListener.java [new file with mode: 0644]
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java [new file with mode: 0644]
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiatorFactory.java [new file with mode: 0644]
opendaylight/netconf/netconf-client/src/main/resources/client_hello.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/pom.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/CapabilityProviderImpl.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/DefaultCommitNotificationProducer.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerDispatcher.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListener.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListenerFactory.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiator.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiatorFactory.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/SessionIdProvider.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/CapabilityProvider.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCloseSession.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCommit.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultGetSchema.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryListener.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryListenerImpl.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryTracker.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceSnapshot.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/DeserializerExceptionHandler.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/NetconfUtil.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/resources/getConfig_candidate.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/resources/server_hello.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/MessageHeaderTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/MessageParserTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/NetconfDispatcherImplTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked1.txt [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked2.txt [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked3.txt [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked4.txt [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked5.txt [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/listener/databaseinteractions/jolokia_config_bean_response.txt [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/listener/databaseinteractions/jolokia_lookupConfigBeans.txt [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_commit.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_lock_candidate.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_lock_running.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_modify_candidate.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_unlock_candidate.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_unlock_running.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/server_error_missing_attribute.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/input.json [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/input.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputList.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputMap.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputMultiple.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputSetter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputSetterList.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputSetterMap.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/map.json [new file with mode: 0644]
opendaylight/netconf/netconf-it/pom.xml [new file with mode: 0644]
opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-it/src/test/resources/keystore.jks [new file with mode: 0644]
opendaylight/netconf/netconf-mapping-api/pom.xml [new file with mode: 0644]
opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/Capability.java [new file with mode: 0644]
opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/HandlingPriority.java [new file with mode: 0644]
opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperation.java [new file with mode: 0644]
opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationFilter.java [new file with mode: 0644]
opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationFilterChain.java [new file with mode: 0644]
opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationService.java [new file with mode: 0644]
opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceFactory.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/pom.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractChannelInitializer.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractNetconfSessionNegotiator.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/mapping/AbstractNetconfOperation.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/FramingMechanism.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageFactory.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageHeader.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageUtil.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/SendErrorExceptionUtil.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/HardcodedNamespaceResolver.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XMLNetconfUtil.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlElement.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlNetconfConstants.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlNetconfValidator.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlUtil.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/resources/org/opendaylight/controller/netconf/util/messages/server_error.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/resources/rfc4741.xsd [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/resources/xml.xsd [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/test/XmlFileLoader.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello_with_auth.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/close-session.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/closeSession.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/commit.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testClientSendsRpcReply_expectedResponse.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testClientSendsRpcReply_request.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testRpcWithoutMessageId_expectedResponse.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testRpcWithoutMessageId_request.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/databaseInteraction/client_get_request_ConfigRegistry.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/databaseInteraction/confg_subsystem_expected_reply.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/discardChanges.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_expectedResult.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_merge_threadfactory.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_merge_yang-test.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_none.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_remove.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_default.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_default_ex.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_module.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_module_ex.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/edit_config.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/get.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/getConfig.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/getConfig_candidate.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/get_schema-no-version.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/get_schema.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/handshake/client_hello_with_session_id.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/handshake/client_hello_with_wrong_namespace.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/editConfig_merge_threadfactory.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/mount12002.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/mount12003.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/unmount12002.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_differentNamespaceTO.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespaces.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespacesList.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_typeNameConfigAttributeMatching.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpc.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpcInner.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpcInnerInner.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/threadpool-edit-config.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised1.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised2.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised3.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised4.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised5.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised6.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised7.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised8.xml [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/validate.xml [new file with mode: 0644]
opendaylight/netconf/pom.xml [new file with mode: 0644]
pom.xml

index 223abcd114b4137b5c148c5ec7bb04042943ce81..296250d945a7c797a099ca50280c99056d7fbee8 100644 (file)
@@ -54,7 +54,7 @@
     <geminiweb.version>2.2.0.RELEASE</geminiweb.version>
     <checkstyle.version>2.10</checkstyle.version>
     <testvm.argLine>-Xmx1024m -XX:MaxPermSize=256m</testvm.argLine>
     <geminiweb.version>2.2.0.RELEASE</geminiweb.version>
     <checkstyle.version>2.10</checkstyle.version>
     <testvm.argLine>-Xmx1024m -XX:MaxPermSize=256m</testvm.argLine>
-    <yang.version>0.5.8</yang.version>
+    <yang.version>0.5.9-SNAPSHOT</yang.version>
     <guava.version>14.0.1</guava.version>
     <osgi.core.version>5.0.0</osgi.core.version>
     <ietf-inet-types.version>2010.09.24.1</ietf-inet-types.version>
     <guava.version>14.0.1</guava.version>
     <osgi.core.version>5.0.0</osgi.core.version>
     <ietf-inet-types.version>2010.09.24.1</ietf-inet-types.version>
@@ -70,7 +70,8 @@
     <bundle.plugin.version>2.3.7</bundle.plugin.version>
     <junit.version>4.8.1</junit.version>
     <bgpcep.version>0.2.0-SNAPSHOT</bgpcep.version>
     <bundle.plugin.version>2.3.7</bundle.plugin.version>
     <junit.version>4.8.1</junit.version>
     <bgpcep.version>0.2.0-SNAPSHOT</bgpcep.version>
-    <yangtools.version>0.5.8</yangtools.version>
+    <yangtools.version>0.5.9-SNAPSHOT</yangtools.version>
+    <yangtools.binding.version>0.6.0-SNAPSHOT</yangtools.binding.version>
     <!--versions for bits of the controller -->
     <controller.version>0.4.1-SNAPSHOT</controller.version>
     <config.version>0.2.1-SNAPSHOT</config.version>
     <!--versions for bits of the controller -->
     <controller.version>0.4.1-SNAPSHOT</controller.version>
     <config.version>0.2.1-SNAPSHOT</config.version>
        <version>${bgpcep.version}</version>
       </dependency>
 
        <version>${bgpcep.version}</version>
       </dependency>
 
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>framework</artifactId>
+            <version>${bgpcep.version}</version>
+        </dependency>
+
+        <!--Netty-->
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-handler</artifactId>
+            <version>4.0.9.Final</version>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-codec</artifactId>
+            <version>4.0.9.Final</version>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-buffer</artifactId>
+            <version>4.0.9.Final</version>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-transport</artifactId>
+            <version>4.0.9.Final</version>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-common</artifactId>
+            <version>4.0.9.Final</version>
+        </dependency>
+
       <!-- yangtools dependencies -->
       <dependency>
        <groupId>org.opendaylight.yangtools</groupId>
        <artifactId>yang-binding</artifactId>
       <!-- yangtools dependencies -->
       <dependency>
        <groupId>org.opendaylight.yangtools</groupId>
        <artifactId>yang-binding</artifactId>
-       <version>${yangtools.version}</version>
+       <version>${yangtools.binding.version}</version>
       </dependency>
       <dependency>
        <groupId>org.opendaylight.yangtools</groupId>
       </dependency>
       <dependency>
        <groupId>org.opendaylight.yangtools</groupId>
index 858a8020d0b094eebc0e1494a054ff65c629e782..4dbc31f062bc48ce26c9a2373bd0bc9b3e52756d 100644 (file)
             <artifactId>concepts</artifactId>
             <version>0.2.0-SNAPSHOT</version>
         </dependency>
             <artifactId>concepts</artifactId>
             <version>0.2.0-SNAPSHOT</version>
         </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
     </dependencies>
 
     <build>
     </dependencies>
 
     <build>
@@ -34,7 +38,8 @@
                     <instructions>
                         <Import-Package>
                             javax.management,
                     <instructions>
                         <Import-Package>
                             javax.management,
-                            org.opendaylight.protocol.concepts
+                            org.opendaylight.protocol.concepts,
+                            org.osgi.framework,
                         </Import-Package>
                         <Export-Package>
                             org.opendaylight.controller.config.api,
                         </Import-Package>
                         <Export-Package>
                             org.opendaylight.controller.config.api,
@@ -43,6 +48,7 @@
                             org.opendaylight.controller.config.api.jmx,
                             org.opendaylight.controller.config.api.jmx.constants,
                             org.opendaylight.controller.config.api.runtime,
                             org.opendaylight.controller.config.api.jmx,
                             org.opendaylight.controller.config.api.jmx.constants,
                             org.opendaylight.controller.config.api.runtime,
+                            org.opendaylight.controller.config.stat,
                         </Export-Package>
                     </instructions>
                 </configuration>
                         </Export-Package>
                     </instructions>
                 </configuration>
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/stat/ConfigProvider.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/stat/ConfigProvider.java
new file mode 100644 (file)
index 0000000..3a81061
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.config.stat;
+
+import org.osgi.framework.BundleContext;
+
+/**
+ * Subset of {@link org.osgi.framework.BundleContext}
+ */
+public interface ConfigProvider {
+    /**
+     * Returns the value of the specified property. If the key is not found in
+     * the Framework properties, the system properties are then searched. The
+     * method returns {@code null} if the property is not found.
+     *
+     * <p>
+     * All bundles must have permission to read properties whose names start
+     * with &quot;org.osgi.&quot;.
+     *
+     * @param key
+     *            The name of the requested property.
+     * @return The value of the requested property, or {@code null} if the
+     *         property is undefined.
+     * @throws SecurityException
+     *             If the caller does not have the appropriate
+     *             {@code PropertyPermission} to read the property, and the Java
+     *             Runtime Environment supports permissions.
+     */
+    String getProperty(String key);
+
+    public static class ConfigProviderImpl implements ConfigProvider {
+        private final BundleContext context;
+
+        public ConfigProviderImpl(BundleContext context) {
+            this.context = context;
+        }
+
+        @Override
+        public String getProperty(String key) {
+            return context.getProperty(key);
+        }
+
+        @Override
+        public String toString() {
+            return "ConfigProviderImpl{" + "context=" + context + '}';
+        }
+    }
+
+}
diff --git a/opendaylight/config/config-persister-api/pom.xml b/opendaylight/config/config-persister-api/pom.xml
new file mode 100644 (file)
index 0000000..867c12c
--- /dev/null
@@ -0,0 +1,48 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>config-subsystem</artifactId>
+        <groupId>org.opendaylight.controller</groupId>
+        <version>0.2.1-SNAPSHOT</version>
+        <relativePath>..</relativePath>
+    </parent>
+    <artifactId>config-persister-api</artifactId>
+    <name>${project.artifactId}</name>
+    <packaging>bundle</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-util</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Import-Package>
+                            com.google.common.base,
+                            org.w3c.dom,
+                            org.osgi.framework,
+                            org.opendaylight.controller.config.stat
+                        </Import-Package>
+                        <Export-Package>
+                            org.opendaylight.controller.config.persist.api,
+                            org.opendaylight.controller.config.persist.api.storage,
+                        </Export-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/Persister.java b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/Persister.java
new file mode 100644 (file)
index 0000000..f9c9301
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.config.persist.api;
+
+import com.google.common.base.Optional;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * Base interface for persister implementation.
+ */
+public interface Persister extends Closeable {
+
+    void persistConfig(ConfigSnapshotHolder configSnapshotHolder) throws IOException;
+
+    Optional<ConfigSnapshotHolder> loadLastConfig() throws IOException;
+
+    public static interface ConfigSnapshotHolder {
+
+        String getConfigSnapshot();
+
+        Set<String> getCapabilities();
+    }
+}
diff --git a/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/storage/StorageAdapter.java b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/storage/StorageAdapter.java
new file mode 100644 (file)
index 0000000..4475040
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.config.persist.api.storage;
+
+import org.opendaylight.controller.config.persist.api.Persister;
+import org.opendaylight.controller.config.stat.ConfigProvider;
+
+/**
+ * Plugins for {@link org.opendaylight.controller.config.persist.api.Persister}
+ * must implement this interface.
+ */
+public interface StorageAdapter extends Persister {
+
+    void setProperties(ConfigProvider configProvider);
+
+}
diff --git a/opendaylight/config/config-persister-file-adapter/pom.xml b/opendaylight/config/config-persister-file-adapter/pom.xml
new file mode 100644 (file)
index 0000000..51fcf86
--- /dev/null
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>config-subsystem</artifactId>
+        <groupId>org.opendaylight.controller</groupId>
+        <version>0.2.1-SNAPSHOT</version>
+        <relativePath>..</relativePath>
+    </parent>
+    <artifactId>config-persister-file-adapter</artifactId>
+    <name>${project.artifactId}</name>
+    <packaging>bundle</packaging>
+
+    <dependencies>
+        <!-- compile dependencies -->
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-persister-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+
+        <!-- test dependencies -->
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>mockito-configuration</artifactId>
+            <version>0.2.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- workaround for creating version according to OSGi specification (major.minor.micro[.qualifier] -->
+            <plugin>
+                <groupId>org.codehaus.groovy.maven</groupId>
+                <artifactId>gmaven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>execute</goal>
+                        </goals>
+                        <configuration>
+                            <source>
+                                System.setProperty("osgiversion", "${project.version}".replace('-', '.'))
+                            </source>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Fragment-Host>${project.groupId}.config-persister-impl;bundle-version=${osgiversion}
+                        </Fragment-Host>
+                        <Provide-Capability>org.opendaylight.controller.config.persister.storage.adapter
+                        </Provide-Capability>
+                        <Import-Package>
+                            org.osgi.framework,
+                            com.google.common.base,
+                            com.google.common.collect,
+                            com.google.common.io,
+                            javax.xml.parsers,
+                            javax.xml.transform,
+                            javax.xml.transform.dom,
+                            javax.xml.transform.stream,
+                            org.apache.commons.lang3,
+                            org.opendaylight.controller.config.persist.api,
+                            org.opendaylight.controller.config.stat,
+                            org.opendaylight.controller.config.persist.api.storage,
+                            org.slf4j,
+                            org.w3c.dom,
+                            org.xml.sax,
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java b/opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java
new file mode 100644 (file)
index 0000000..a866743
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.config.persist.storage.file;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Charsets;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+import org.apache.commons.lang3.StringUtils;
+import org.opendaylight.controller.config.persist.api.storage.StorageAdapter;
+import org.opendaylight.controller.config.stat.ConfigProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Set;
+
+/**
+ * StorageAdapter that stores configuration in a plan file.
+ */
+public class FileStorageAdapter implements StorageAdapter {
+    private static final Logger logger = LoggerFactory.getLogger(FileStorageAdapter.class);
+
+    // TODO prefix properties
+
+    private static final Charset ENCODING = Charsets.UTF_8;
+
+    public static final String FILE_STORAGE_PROP = "fileStorage";
+    public static final String NUMBER_OF_BACKUPS = "numberOfBackups";
+
+    private static final String SEPARATOR_E_PURE = "//END OF CONFIG";
+    private static final String SEPARATOR_E = newLine(SEPARATOR_E_PURE);
+
+    private static final String SEPARATOR_M_PURE = "//END OF SNAPSHOT";
+    private static final String SEPARATOR_M = newLine(SEPARATOR_M_PURE);
+
+    private static final String SEPARATOR_S = newLine("//START OF CONFIG");
+
+    private static final String SEPARATOR_SL_PURE = "//START OF CONFIG-LAST";
+    private static final String SEPARATOR_SL = newLine(SEPARATOR_SL_PURE);
+
+    private static Integer numberOfStoredBackups;
+    private File storage;
+
+    @Override
+    public void setProperties(ConfigProvider configProvider) {
+        File storage = extractStorageFileFromProperties(configProvider);
+        logger.debug("Using file {}", storage.getAbsolutePath());
+        // Create file if it does not exist
+        File parentFile = storage.getAbsoluteFile().getParentFile();
+        if (parentFile.exists() == false) {
+            logger.debug("Creating parent folders {}", parentFile);
+            parentFile.mkdirs();
+        }
+        if (storage.exists() == false) {
+            logger.debug("Storage file does not exist, creating empty file");
+            try {
+                boolean result = storage.createNewFile();
+                if (result == false)
+                    throw new RuntimeException("Unable to create storage file " + storage);
+            } catch (IOException e) {
+                throw new RuntimeException("Unable to create storage file " + storage, e);
+            }
+        }
+        if (numberOfStoredBackups == 0) {
+            throw new RuntimeException(NUMBER_OF_BACKUPS
+                    + " property should be either set to positive value, or ommited. Can not be set to 0.");
+        }
+        setFileStorage(storage);
+
+    }
+
+    @VisibleForTesting
+    void setFileStorage(File storage) {
+        this.storage = storage;
+    }
+
+    @VisibleForTesting
+    void setNumberOfBackups(Integer numberOfBackups) {
+        numberOfStoredBackups = numberOfBackups;
+    }
+
+    private static File extractStorageFileFromProperties(ConfigProvider configProvider) {
+        String fileStorageProperty = configProvider.getProperty(FILE_STORAGE_PROP);
+        Preconditions.checkNotNull(fileStorageProperty, "Unable to find " + FILE_STORAGE_PROP
+                + " in received properties :" + configProvider);
+        File result = new File(fileStorageProperty);
+        String numberOfBAckupsAsString = configProvider.getProperty(NUMBER_OF_BACKUPS);
+        if (numberOfBAckupsAsString != null) {
+            numberOfStoredBackups = Integer.valueOf(numberOfBAckupsAsString);
+        } else {
+            numberOfStoredBackups = Integer.MAX_VALUE;
+        }
+
+        return result;
+    }
+
+    private static String newLine(String string) {
+        return string + "\n";
+    }
+
+    @Override
+    public void persistConfig(ConfigSnapshotHolder holder) throws IOException {
+        Preconditions.checkNotNull(storage, "Storage file is null");
+
+        String content = Files.toString(storage, ENCODING);
+        if (numberOfStoredBackups == Integer.MAX_VALUE) {
+            resetLastConfig(content);
+            persistLastConfig(holder);
+        } else {
+            if (numberOfStoredBackups == 1) {
+                Files.write("", storage, ENCODING);
+                persistLastConfig(holder);
+            } else {
+                int count = StringUtils.countMatches(content, SEPARATOR_S);
+                if ((count + 1) < numberOfStoredBackups) {
+                    resetLastConfig(content);
+                    persistLastConfig(holder);
+                } else {
+                    String contentSubString = StringUtils.substringBefore(content, SEPARATOR_E);
+                    contentSubString = contentSubString.concat(SEPARATOR_E_PURE);
+                    content = StringUtils.substringAfter(content, contentSubString);
+                    resetLastConfig(content);
+                    persistLastConfig(holder);
+                }
+            }
+        }
+    }
+
+    private void resetLastConfig(String content) throws IOException {
+        content = content.replaceFirst(SEPARATOR_SL, SEPARATOR_S);
+        Files.write(content, storage, ENCODING);
+    }
+
+    private void persistLastConfig(ConfigSnapshotHolder holder) throws IOException {
+        Files.append(SEPARATOR_SL, storage, ENCODING);
+        String snapshotAsString = holder.getConfigSnapshot();
+        Files.append(newLine(snapshotAsString), storage, ENCODING);
+        Files.append(SEPARATOR_M, storage, ENCODING);
+        Files.append(toStringCaps(holder.getCapabilities()), storage, ENCODING);
+        Files.append(SEPARATOR_E, storage, ENCODING);
+    }
+
+    private CharSequence toStringCaps(Set<String> capabilities) {
+        StringBuilder b = new StringBuilder();
+        for (String capability : capabilities) {
+            b.append(newLine(capability));
+        }
+        return b.toString();
+    }
+
+    @Override
+    public Optional<ConfigSnapshotHolder> loadLastConfig() throws IOException {
+        Preconditions.checkNotNull(storage, "Storage file is null");
+
+        if (!storage.exists()) {
+            return Optional.absent();
+        }
+
+        final LineProcessor lineProcessor = new LineProcessor();
+        String result = Files.readLines(storage, ENCODING, lineProcessor);
+
+        try {
+            if (lineProcessor.getConfigSnapshot().isPresent() == false) {
+                return Optional.absent();
+            } else {
+                return Optional.<ConfigSnapshotHolder> of(new PersistedConfigImpl(lineProcessor.getConfigSnapshot(),
+                        lineProcessor.getCapabilities()));
+            }
+
+        } catch (ParserConfigurationException | SAXException e) {
+            throw new IOException("Unable to load last config ", e);
+        }
+    }
+
+    private static final class LineProcessor implements com.google.common.io.LineProcessor<String> {
+
+        private boolean inLastConfig, inLastSnapshot;
+        private final StringBuffer snapshotBuffer = new StringBuffer();
+        private final Set<String> caps = Sets.newHashSet();
+
+        @Override
+        public String getResult() {
+            return null;
+        }
+
+        @Override
+        public boolean processLine(String line) throws IOException {
+            if (inLastConfig && line.equals(SEPARATOR_E_PURE)) {
+                inLastConfig = false;
+                return false;
+            }
+
+            if (inLastConfig && line.equals(SEPARATOR_M_PURE)) {
+                inLastSnapshot = false;
+                return true;
+            }
+
+            if (inLastConfig) {
+                if (inLastSnapshot) {
+                    snapshotBuffer.append(line);
+                    snapshotBuffer.append(System.lineSeparator());
+                } else {
+                    caps.add(line);
+                }
+            }
+
+            if (line.equals(SEPARATOR_SL_PURE)) {
+                inLastConfig = true;
+                inLastSnapshot = true;
+            }
+
+            return true;
+        }
+
+        Optional<String> getConfigSnapshot() throws IOException, SAXException, ParserConfigurationException {
+            final String xmlContent = snapshotBuffer.toString();
+            if (xmlContent == null || xmlContent.equals("")) {
+                return Optional.absent();
+            } else
+                return Optional.of(xmlContent);
+        }
+
+        Set<String> getCapabilities() throws IOException, SAXException, ParserConfigurationException {
+            return caps;
+        }
+
+    }
+
+    @Override
+    public void close() throws IOException {
+
+    }
+
+    @Override
+    public String toString() {
+        return "FileStorageAdapter [storage=" + storage + "]";
+    }
+
+    private class PersistedConfigImpl implements ConfigSnapshotHolder {
+
+        private final String snapshot;
+        private final Set<String> caps;
+
+        public PersistedConfigImpl(Optional<String> configSnapshot, Set<String> capabilities) {
+            this.snapshot = configSnapshot.get();
+            this.caps = capabilities;
+        }
+
+        @Override
+        public String getConfigSnapshot() {
+            return snapshot;
+        }
+
+        @Override
+        public Set<String> getCapabilities() {
+            return caps;
+        }
+    }
+
+}
diff --git a/opendaylight/config/config-persister-file-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapterTest.java b/opendaylight/config/config-persister-file-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapterTest.java
new file mode 100644 (file)
index 0000000..acb44ba
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.config.persist.storage.file;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Sets;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.controller.config.persist.api.Persister;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.*;
+
+public class FileStorageAdapterTest {
+
+    private static int i;
+    private File file;
+
+    @Before
+    public void setUp() throws Exception {
+        file = Files.createTempFile("testFilePersist", ".txt").toFile();
+        if (!file.exists())
+            return;
+        com.google.common.io.Files.write("", file, Charsets.UTF_8);
+        i = 1;
+    }
+
+    @Test
+    public void testFileAdapter() throws Exception {
+        FileStorageAdapter storage = new FileStorageAdapter();
+        storage.setFileStorage(file);
+        storage.setNumberOfBackups(Integer.MAX_VALUE);
+        final Persister.ConfigSnapshotHolder holder = new Persister.ConfigSnapshotHolder() {
+            @Override
+            public String getConfigSnapshot() {
+                return createConfig();
+            }
+
+            @Override
+            public Set<String> getCapabilities() {
+                return createCaps();
+            }
+        };
+        storage.persistConfig(holder);
+
+        storage.persistConfig(holder);
+
+        Collection<String> readLines = Collections2.filter(com.google.common.io.Files.readLines(file, Charsets.UTF_8),
+                new Predicate<String>() {
+
+                    @Override
+                    public boolean apply(String input) {
+                        if (input.equals(""))
+                            return false;
+                        return true;
+                    }
+                });
+        assertEquals(14, readLines.size());
+
+        Optional<Persister.ConfigSnapshotHolder> lastConf = storage.loadLastConfig();
+        assertTrue(lastConf.isPresent());
+        assertEquals("<config>2</config>",
+                lastConf.get().getConfigSnapshot().replaceAll("\\s", ""));
+        assertEquals(createCaps(), lastConf.get().getCapabilities());
+    }
+
+    private Set<String> createCaps() {
+        Set<String> caps = Sets.newHashSet();
+
+        caps.add("cap1");
+        caps.add("cap2");
+        caps.add("capaaaa as dasfasdf s2");
+        return caps;
+    }
+
+    @Test
+    public void testFileAdapterOneBackup() throws Exception {
+        FileStorageAdapter storage = new FileStorageAdapter();
+        storage.setFileStorage(file);
+        storage.setNumberOfBackups(1);
+        final Persister.ConfigSnapshotHolder holder = new Persister.ConfigSnapshotHolder() {
+            @Override
+            public String getConfigSnapshot() {
+                return createConfig();
+            }
+
+            @Override
+            public Set<String> getCapabilities() {
+                return createCaps();
+            }
+        };
+        storage.persistConfig(holder);
+
+        storage.persistConfig(holder);
+
+        Collection<String> readLines = Collections2.filter(com.google.common.io.Files.readLines(file, Charsets.UTF_8),
+                new Predicate<String>() {
+
+                    @Override
+                    public boolean apply(String input) {
+                        if (input.equals(""))
+                            return false;
+                        return true;
+                    }
+                });
+        assertEquals(7, readLines.size());
+
+        Optional<Persister.ConfigSnapshotHolder> lastConf = storage.loadLastConfig();
+        assertTrue(lastConf.isPresent());
+        assertEquals("<config>2</config>",
+                lastConf.get().getConfigSnapshot().replaceAll("\\s", ""));
+    }
+
+    @Test
+    public void testFileAdapterOnlyTwoBackups() throws Exception {
+        FileStorageAdapter storage = new FileStorageAdapter();
+        storage.setFileStorage(file);
+        storage.setNumberOfBackups(2);
+        final Persister.ConfigSnapshotHolder holder = new Persister.ConfigSnapshotHolder() {
+            @Override
+            public String getConfigSnapshot() {
+                return createConfig();
+            }
+
+            @Override
+            public Set<String> getCapabilities() {
+                return createCaps();
+            }
+        };
+        storage.persistConfig(holder);
+
+        storage.persistConfig(holder);
+        storage.persistConfig(holder);
+
+        Collection<String> readLines = Collections2.filter(com.google.common.io.Files.readLines(file, Charsets.UTF_8),
+                new Predicate<String>() {
+
+                    @Override
+                    public boolean apply(String input) {
+                        if (input.equals(""))
+                            return false;
+                        return true;
+                    }
+                });
+
+        assertEquals(14, readLines.size());
+
+        Optional<Persister.ConfigSnapshotHolder> lastConf = storage.loadLastConfig();
+        assertTrue(lastConf.isPresent());
+        assertEquals("<config>3</config>",
+               lastConf.get().getConfigSnapshot().replaceAll("\\s", ""));
+        assertFalse(readLines.contains(holder.getConfigSnapshot()));
+    }
+
+    @Test
+    public void testNoLastConfig() throws Exception {
+        File file = Files.createTempFile("testFilePersist", ".txt").toFile();
+        if (!file.exists())
+            return;
+        FileStorageAdapter storage = new FileStorageAdapter();
+        storage.setFileStorage(file);
+
+        Optional<Persister.ConfigSnapshotHolder> elementOptional = storage.loadLastConfig();
+        assertThat(elementOptional.isPresent(), is(false));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testNoProperties() throws Exception {
+        FileStorageAdapter storage = new FileStorageAdapter();
+        storage.loadLastConfig();
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testNoProperties2() throws Exception {
+        FileStorageAdapter storage = new FileStorageAdapter();
+        storage.persistConfig(new Persister.ConfigSnapshotHolder() {
+            @Override
+            public String getConfigSnapshot() {
+                return Mockito.mock(String.class);
+            }
+
+            @Override
+            public Set<String> getCapabilities() {
+                return Collections.<String> emptySet();
+            }
+        } );
+    }
+
+    static String createConfig() {
+        return "<config>" + i++ + "</config>";
+    }
+
+}
index 6484c3027451e85d61e779aced14da7280a777bb..1812fbb4493e2d7583179fb384f9843da60ec6e3 100755 (executable)
@@ -22,6 +22,8 @@
         <module>config-api</module>
         <module>config-manager</module>
         <module>config-util</module>
         <module>config-api</module>
         <module>config-manager</module>
         <module>config-util</module>
+        <module>config-persister-api</module>
+        <module>config-persister-file-adapter</module>
         <module>yang-jmx-generator</module>
         <module>yang-jmx-generator-plugin</module>
         <module>yang-jmx-generator-it</module>
         <module>yang-jmx-generator</module>
         <module>yang-jmx-generator-plugin</module>
         <module>yang-jmx-generator-it</module>
@@ -40,7 +42,6 @@
         <jacoco.version>0.6.2.201302030002</jacoco.version>
         <slf4j.version>1.7.2</slf4j.version>
         <jolokia.version>1.1.1</jolokia.version>
         <jacoco.version>0.6.2.201302030002</jacoco.version>
         <slf4j.version>1.7.2</slf4j.version>
         <jolokia.version>1.1.1</jolokia.version>
-        <osgi.core.version>5.0.0</osgi.core.version>
         <opendaylight.yang.version>0.5.9-SNAPSHOT</opendaylight.yang.version>
         <opendaylight.binding.version>0.6.0-SNAPSHOT</opendaylight.binding.version>
         <jmxGeneratorPath>${project.build.directory}/generated-sources/config</jmxGeneratorPath>
         <opendaylight.yang.version>0.5.9-SNAPSHOT</opendaylight.yang.version>
         <opendaylight.binding.version>0.6.0-SNAPSHOT</opendaylight.binding.version>
         <jmxGeneratorPath>${project.build.directory}/generated-sources/config</jmxGeneratorPath>
                     <artifactId>gmaven-plugin</artifactId>
                     <version>1.0</version>
                 </plugin>
                     <artifactId>gmaven-plugin</artifactId>
                     <version>1.0</version>
                 </plugin>
+                <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
+                <plugin>
+                    <groupId>org.eclipse.m2e</groupId>
+                    <artifactId>lifecycle-mapping</artifactId>
+                    <version>1.0.0</version>
+                    <configuration>
+                        <lifecycleMappingMetadata>
+                            <pluginExecutions>
+                                <pluginExecution>
+                                    <pluginExecutionFilter>
+                                        <groupId>
+                                            org.opendaylight.yangtools
+                                        </groupId>
+                                        <artifactId>
+                                            yang-maven-plugin
+                                        </artifactId>
+                                        <versionRange>
+                                            [0.5.7-SNAPSHOT,)
+                                        </versionRange>
+                                        <goals>
+                                            <goal>
+                                                generate-sources
+                                            </goal>
+                                        </goals>
+                                    </pluginExecutionFilter>
+                                    <action>
+                                        <ignore></ignore>
+                                    </action>
+                                </pluginExecution>
+                            </pluginExecutions>
+                        </lifecycleMappingMetadata>
+                    </configuration>
+                </plugin>
             </plugins>
         </pluginManagement>
     </build>
             </plugins>
         </pluginManagement>
     </build>
     <pluginRepositories>
         <pluginRepository>
             <id>nexus.opendaylight.org</id>
     <pluginRepositories>
         <pluginRepository>
             <id>nexus.opendaylight.org</id>
-            <url>http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot</url>
+            <url>http://nexus.opendaylight.org/content/repositories/opendaylight.release</url>
             <releases>
                 <enabled>true</enabled>
             </releases>
             <releases>
                 <enabled>true</enabled>
             </releases>
index 1079ce9fd74449687b5c03dc9fe0e5d9e6e8483b..81d55efeaf4b82405d5a448628b2bea8148236fd 100644 (file)
@@ -9,51 +9,61 @@
     </parent>
     <artifactId>yang-jmx-generator-plugin</artifactId>
     <dependencies>
     </parent>
     <artifactId>yang-jmx-generator-plugin</artifactId>
     <dependencies>
+
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
+
         <dependency>
             <groupId>org.opendaylight.controller</groupId>
             <artifactId>yang-jmx-generator</artifactId>
             <version>0.2.1-SNAPSHOT</version>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.controller</groupId>
             <artifactId>yang-jmx-generator</artifactId>
             <version>0.2.1-SNAPSHOT</version>
         </dependency>
+
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>yang-maven-plugin-spi</artifactId>
             <version>${opendaylight.yang.version}</version>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>yang-maven-plugin-spi</artifactId>
             <version>${opendaylight.yang.version}</version>
         </dependency>
+
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
-            <artifactId>binding-generator-impl</artifactId>
+            <artifactId>binding-type-provider</artifactId>
             <version>${opendaylight.binding.version}</version>
         </dependency>
             <version>${opendaylight.binding.version}</version>
         </dependency>
+
         <dependency>
             <groupId>org.eclipse.jdt</groupId>
             <artifactId>core</artifactId>
             <version>3.3.0-v_771</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.eclipse.jdt</groupId>
             <artifactId>core</artifactId>
             <version>3.3.0-v_771</version>
             <scope>test</scope>
         </dependency>
+
         <dependency>
             <groupId>org.freemarker</groupId>
             <artifactId>freemarker</artifactId>
             <version>2.3.20</version>
         </dependency>
         <dependency>
             <groupId>org.freemarker</groupId>
             <artifactId>freemarker</artifactId>
             <version>2.3.20</version>
         </dependency>
+
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>config-api</artifactId>
             <version>0.2.1-SNAPSHOT</version>
         </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>config-api</artifactId>
             <version>0.2.1-SNAPSHOT</version>
         </dependency>
+
         <dependency>
             <groupId>commons-io</groupId>
             <artifactId>commons-io</artifactId>
         </dependency>
         <dependency>
             <groupId>commons-io</groupId>
             <artifactId>commons-io</artifactId>
         </dependency>
+
         <dependency>
             <!--FIXME two implementations of slf4j on classpath, logback classic from parent-->
             <groupId>com.googlecode.slf4j-maven-plugin-log</groupId>
             <artifactId>slf4j-maven-plugin-log</artifactId>
             <version>1.0.0</version>
         </dependency>
         <dependency>
             <!--FIXME two implementations of slf4j on classpath, logback classic from parent-->
             <groupId>com.googlecode.slf4j-maven-plugin-log</groupId>
             <artifactId>slf4j-maven-plugin-log</artifactId>
             <version>1.0.0</version>
         </dependency>
+
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
             <scope>test</scope>
             <type>test-jar</type>
         </dependency>
             <scope>test</scope>
             <type>test-jar</type>
         </dependency>
+
         <dependency>
             <groupId>org.eclipse</groupId>
             <artifactId>jdt</artifactId>
             <version>3.3.0-v20070607-1300</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.eclipse</groupId>
             <artifactId>jdt</artifactId>
             <version>3.3.0-v20070607-1300</version>
             <scope>test</scope>
         </dependency>
+
         <dependency>
             <groupId>org.opendaylight.bgpcep</groupId>
             <artifactId>mockito-configuration</artifactId>
             <version>0.2.0-SNAPSHOT</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.bgpcep</groupId>
             <artifactId>mockito-configuration</artifactId>
             <version>0.2.0-SNAPSHOT</version>
             <scope>test</scope>
         </dependency>
+
         <dependency>
         <dependency>
-          <groupId>org.apache.commons</groupId>
-          <artifactId>commons-lang3</artifactId>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
         </dependency>
     </dependencies>
 
         </dependency>
     </dependencies>
 
index 743ffba739c97399a6a73556ab01a3061eed6816..e40ca8b18b176869509f8c897b169bf5450b33c2 100644 (file)
@@ -7,17 +7,11 @@
  */
 package org.opendaylight.controller.config.yangjmxgenerator.plugin;
 
  */
 package org.opendaylight.controller.config.yangjmxgenerator.plugin;
 
-import java.io.File;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
 import org.apache.commons.io.FileUtils;
 import org.apache.maven.plugin.logging.Log;
 import org.apache.maven.project.MavenProject;
 import org.apache.commons.io.FileUtils;
 import org.apache.maven.plugin.logging.Log;
 import org.apache.maven.project.MavenProject;
@@ -35,11 +29,12 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.impl.StaticLoggerBinder;
 
 import org.slf4j.LoggerFactory;
 import org.slf4j.impl.StaticLoggerBinder;
 
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * This class interfaces with yang-maven-plugin. Gets parsed yang modules in
 
 /**
  * This class interfaces with yang-maven-plugin. Gets parsed yang modules in
index dce382ddb7eac55f8529d6ac46c0c89730b2c3a5..67a1c80b9ca7fe43979c7c3e793f26add451bd87 100644 (file)
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
+
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
-            <artifactId>binding-generator-util</artifactId>
+            <artifactId>binding-generator-spi</artifactId>
             <version>${opendaylight.binding.version}</version>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
             <version>${opendaylight.binding.version}</version>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
-            <artifactId>binding-generator-spi</artifactId>
+            <artifactId>binding-generator-util</artifactId>
             <version>${opendaylight.binding.version}</version>
         </dependency>
             <version>${opendaylight.binding.version}</version>
         </dependency>
+
+
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
index 6d1eca11933cea8216588d6a94fab9955b173c49..d07e20bebba013c82e32ab0ba1b3c988550f0ecb 100644 (file)
@@ -7,20 +7,9 @@
  */
 package org.opendaylight.controller.config.yangjmxgenerator;
 
  */
 package org.opendaylight.controller.config.yangjmxgenerator;
 
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
 import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
 import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
 import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
 import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
@@ -28,22 +17,12 @@ import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute
 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
-import org.opendaylight.yangtools.yang.model.api.SchemaNode;
-import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.UsesNode;
+import org.opendaylight.yangtools.yang.model.api.*;
 
 
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Optional;
-import com.google.common.collect.Lists;
+import java.util.*;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
 
 /**
  * Holds information about runtime bean to be generated. There are two kinds of
 
 /**
  * Holds information about runtime bean to be generated. There are two kinds of
@@ -295,8 +274,8 @@ public class RuntimeBeanEntry {
                                         + "Error occured in " + rpcDefinition);
                     }
                     List<JavaAttribute> parameters = new ArrayList<>();
                                         + "Error occured in " + rpcDefinition);
                     }
                     List<JavaAttribute> parameters = new ArrayList<>();
-                    for (DataSchemaNode childNode : rpcDefinition.getInput()
-                            .getChildNodes()) {
+                    for (DataSchemaNode childNode : sortAttributes(rpcDefinition.getInput()
+                            .getChildNodes())) {
                         if (childNode.isAddedByUses() == false) { // skip
                                                                   // refined
                                                                   // context-instance
                         if (childNode.isAddedByUses() == false) { // skip
                                                                   // refined
                                                                   // context-instance
@@ -318,6 +297,17 @@ public class RuntimeBeanEntry {
                 attributes, rpcs);
     }
 
                 attributes, rpcs);
     }
 
+    private static Collection<DataSchemaNode> sortAttributes(Set<DataSchemaNode> childNodes) {
+        final TreeSet<DataSchemaNode> dataSchemaNodes = new TreeSet<>(new Comparator<DataSchemaNode>() {
+            @Override
+            public int compare(DataSchemaNode o1, DataSchemaNode o2) {
+                return o1.getQName().getLocalName().compareTo(o2.getQName().getLocalName());
+            }
+        });
+        dataSchemaNodes.addAll(childNodes);
+        return dataSchemaNodes;
+    }
+
     private static boolean isInnerStateBean(DataSchemaNode child) {
         for (UnknownSchemaNode unknownSchemaNode : child
                 .getUnknownSchemaNodes()) {
     private static boolean isInnerStateBean(DataSchemaNode child) {
         for (UnknownSchemaNode unknownSchemaNode : child
                 .getUnknownSchemaNodes()) {
index 69531c61b6743600f6b4411200194123be0a3b1b..97285c1f22ee8de7978f92a0ece595f0d10e62b1 100644 (file)
@@ -32,7 +32,7 @@
         </dependency>
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
-            <artifactId>binding-generator-impl</artifactId>
+            <artifactId>binding-type-provider</artifactId>
             <version>${opendaylight.binding.version}</version>
         </dependency>
         <dependency>
             <version>${opendaylight.binding.version}</version>
         </dependency>
         <dependency>
diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked1.txt b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked1.txt
new file mode 100644 (file)
index 0000000..aad7239
--- /dev/null
@@ -0,0 +1,35 @@
+
+#24
+<rpc message-id="101" xm
+#28
+lns="urn:ietf:params:xml:ns:
+#2
+ne
+#33
+tconf:base:1.0">
+  <my-own-method
+#3
+ xm
+#13
+lns="http://e
+#34
+xample.net/me/my-own/1.0">
+    <my
+#8
+-first-p
+#21
+arameter>14</my-first
+#26
+-parameter>
+    <another-p
+#23
+arameter>fred</another-
+#31
+parameter>
+ </my-own-method>
+ <
+#2
+/r
+#3
+pc>
+##
diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked2.txt b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked2.txt
new file mode 100644 (file)
index 0000000..a36a85e
--- /dev/null
@@ -0,0 +1,48 @@
+
+#22
+<rpc message-id="101" 
+#24
+xmlns="urn:ietf:params:x
+#15
+ml:ns:netconf:b
+#54
+ase:1.0">
+  <get-config>
+      <source>
+            <r
+#2
+un
+#9
+ning/>
+  
+#18
+  </source>    <fi
+#33
+lter type="subtree">
+      <top x
+#4
+mlns
+#31
+="http://example.com/schema/1.2
+#15
+/config">
+     
+#19
+   <users>
+        
+#8
+     <us
+#3
+er/
+#5
+>
+   
+#77
+     </users>
+           </top>
+               </filter>
+                 </g
+#17
+et-config>
+</rpc>
+##
diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked3.txt b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked3.txt
new file mode 100644 (file)
index 0000000..d9dc43d
--- /dev/null
@@ -0,0 +1,43 @@
+
+#43
+<rpc message-id="101" xmlns="urn:ietf:param
+#14
+s:xml:ns:netco
+#14
+nf:base:1.0">
+
+#26
+<get-config>
+    <source>
+
+#35
+     <running/>
+         </source>
+
+#39
+    <filter type="subtree">
+          <
+#40
+top xmlns="http://example.com/schema/1.2
+#26
+/config">
+        <users>
+
+#36
+     <user>
+                 <name>f
+#56
+red</name>
+          </user>
+                  </users>
+
+#28
+      </top>
+          </fil
+#1
+t
+#28
+er>
+  </get-config>
+  </rpc>
+##
diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked4.txt b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked4.txt
new file mode 100644 (file)
index 0000000..0b8a102
--- /dev/null
@@ -0,0 +1,40 @@
+
+#17
+<rpc message-id="
+#25
+101" xmlns="urn:ietf:para
+#19
+ms:xml:ns:netconf:b
+#3
+ase
+#19
+:1.0">
+  <get-confi
+#61
+g>
+    <source>
+          <running/>
+              </source>
+
+#43
+  <filter type="subtree">
+        <!-- requ
+#13
+est a text ve
+#22
+rsion of the configura
+#9
+tion -->
+
+#16
+   <config-text 
+#45
+xmlns="http://example.com/text/1.2/config"/>
+
+#22
+    </filter>
+      </
+#18
+get-config>
+</rpc>
+##
diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked5.txt b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked5.txt
new file mode 100644 (file)
index 0000000..f8f3c4d
--- /dev/null
@@ -0,0 +1,42 @@
+
+#43
+<rpc message-id="101" xmlns="urn:ietf:param
+#41
+s:xml:ns:netconf:base:1.0">
+  <edit-confi
+#29
+g>
+    <target>
+          <ru
+#13
+nning/>
+    <
+#4
+/tar
+#18
+get>
+    <config>
+
+#41
+  <top xmlns="http://example.com/schema/1
+#32
+.2/config">
+        <interface>
+
+#29
+        <name>Ethernet0/0</na
+#30
+me>
+          <mtu>1500</mtu>
+
+#61
+</interface>
+      </top>
+          </config>
+            </e
+#9
+dit-confi
+#9
+g>
+</rpc>
+##
diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_config_bean_response.txt b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_config_bean_response.txt
new file mode 100644 (file)
index 0000000..2ae32ef
--- /dev/null
@@ -0,0 +1,18 @@
+curl http://localhost:17777/jolokia/read/org.opendaylight.controller:instanceName=fixed1,type=ConfigBean,interfaceName=testing-threadpool | jsonpp
+{
+    "request": {
+        "mbean": "org.opendaylight.controller:instanceName=fixed1,interfaceName=testing-threadpool,type=ConfigBean",
+        "type": "read"
+    },
+    "status": 200,
+    "timestamp": 1362416252,
+    "value": {
+        "ExportedInterfaces": [
+            "testing-threadpool",
+            "modifiable-threadpool"
+        ],
+        "ImplementationName": "fixed",
+        "ThreadCount": 10,
+        "TriggerNewInstanceCreation": false
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_lookupConfigBeans.txt b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_lookupConfigBeans.txt
new file mode 100644 (file)
index 0000000..2ae705a
--- /dev/null
@@ -0,0 +1,18 @@
+$ curl 'http://localhost:17777/jolokia/exec/org.opendaylight.controller:type=ConfigRegistry/lookupConfigBeans()' | jsonpp
+{
+    "request": {
+        "mbean": "org.opendaylight.controller:type=ConfigRegistry",
+        "operation": "lookupConfigBeans()",
+        "type": "exec"
+    },
+    "status": 200,
+    "timestamp": 1362417043,
+    "value": [
+        {
+            "objectName": "org.opendaylight.controller:instanceName=fixed1,interfaceName=modifiable-threadpool,type=ConfigBean"
+        },
+        {
+            "objectName": "org.opendaylight.controller:instanceName=fixed1,interfaceName=testing-threadpool,type=ConfigBean"
+        }
+    ]
+}
diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_commit.xml b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_commit.xml
new file mode 100644 (file)
index 0000000..6eca609
--- /dev/null
@@ -0,0 +1,4 @@
+<rpc message-id="104"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <commit/>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_candidate.xml b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_candidate.xml
new file mode 100644 (file)
index 0000000..6a9ed63
--- /dev/null
@@ -0,0 +1,8 @@
+<rpc message-id="102"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <lock>
+        <target>
+            <candidate/>
+        </target>
+    </lock>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_running.xml b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_running.xml
new file mode 100644 (file)
index 0000000..2d66c45
--- /dev/null
@@ -0,0 +1,8 @@
+<rpc message-id="101"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <lock>
+        <target>
+            <running/>
+        </target>
+    </lock>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_modify_candidate.xml b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_modify_candidate.xml
new file mode 100644 (file)
index 0000000..ce67845
--- /dev/null
@@ -0,0 +1,20 @@
+<rpc message-id="103"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <default-operation>none</default-operation>
+        <test-option>test-then-set</test-option>
+        <error-option>stop-on-error</error-option>
+        <nc:config
+                xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+                xmlns="uri-for-my-data-model-namespace">
+            <some-existing-node>
+                <my-new-node nc:operation="create">
+                    <my-new-leaf>7</my-new-leaf>
+                </my-new-node>
+            </some-existing-node>
+        </nc:config>
+    </edit-config>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_candidate.xml b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_candidate.xml
new file mode 100644 (file)
index 0000000..dd6fe1b
--- /dev/null
@@ -0,0 +1,8 @@
+<rpc message-id="105"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <unlock>
+        <target>
+            <candidate/>
+        </target>
+    </unlock>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_running.xml b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_running.xml
new file mode 100644 (file)
index 0000000..f94af46
--- /dev/null
@@ -0,0 +1,8 @@
+<rpc message-id="106"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <unlock>
+        <target>
+            <running/>
+        </target>
+    </unlock>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/server_error_missing_attribute.xml b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/server_error_missing_attribute.xml
new file mode 100644 (file)
index 0000000..c70184e
--- /dev/null
@@ -0,0 +1,11 @@
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <rpc-error>
+        <error-type>rpc</error-type>
+        <error-tag>missing-attribute</error-tag>
+        <error-severity>error</error-severity>
+        <error-info>
+            <bad-attribute>message-id</bad-attribute>
+            <bad-element>rpc</bad-element>
+        </error-info>
+    </rpc-error>
+</rpc-reply>
index a11f93b83d92b72b10235cbecc1d9df2ede34282..1b747f6ce8d6951dcc552c6943740253539adb57 100644 (file)
           <version>0.1.1-SNAPSHOT</version>
         </dependency>
 
           <version>0.1.1-SNAPSHOT</version>
         </dependency>
 
-        <!-- config
+        <!-- config-->
         <dependency>
           <groupId>org.opendaylight.controller</groupId>
           <artifactId>config-api</artifactId>
         <dependency>
           <groupId>org.opendaylight.controller</groupId>
           <artifactId>config-api</artifactId>
           <artifactId>logback-config</artifactId>
           <version>${config.version}</version>
         </dependency>
           <artifactId>logback-config</artifactId>
           <version>${config.version}</version>
         </dependency>
-        -->
+        <dependency>
+          <groupId>org.opendaylight.controller</groupId>
+          <artifactId>config-persister-api</artifactId>
+          <version>${config.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>org.opendaylight.controller</groupId>
+          <artifactId>config-persister-file-adapter</artifactId>
+          <version>${config.version}</version>
+        </dependency>
+
+
+       <!-- Netconf -->
+        <dependency>
+          <groupId>org.opendaylight.controller</groupId>
+          <artifactId>netconf-api</artifactId>
+          <version>${config.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>org.opendaylight.controller</groupId>
+          <artifactId>netconf-impl</artifactId>
+          <version>${config.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>org.opendaylight.controller</groupId>
+          <artifactId>netconf-util</artifactId>
+          <version>${config.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>org.opendaylight.controller</groupId>
+          <artifactId>netconf-client</artifactId>
+          <version>${config.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>org.opendaylight.controller</groupId>
+          <artifactId>netconf-mapping-api</artifactId>
+          <version>${config.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>org.opendaylight.controller</groupId>
+          <artifactId>config-netconf-connector</artifactId>
+          <version>${config.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>org.opendaylight.controller</groupId>
+          <artifactId>config-persister-impl</artifactId>
+          <version>${config.version}</version>
+        </dependency>
+
+
       </dependencies>
     </profile>
   </profiles>
       </dependencies>
     </profile>
   </profiles>
      <groupId>org.opendaylight.bgpcep</groupId>
      <artifactId>util</artifactId>
     </dependency>
      <groupId>org.opendaylight.bgpcep</groupId>
      <artifactId>util</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.bgpcep</groupId>
+      <artifactId>framework</artifactId>
+    </dependency>
 
 
-    <!-- testing dependencies I'm pretty sure we should trim -->
+    <!--Netty-->
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-handler</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-codec</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-buffer</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-transport</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-common</artifactId>
+    </dependency>
+
+
+      <!-- testing dependencies I'm pretty sure we should trim -->
     <dependency>
      <groupId>org.opendaylight.controller</groupId>
      <artifactId>clustering.test</artifactId>
     <dependency>
      <groupId>org.opendaylight.controller</groupId>
      <artifactId>clustering.test</artifactId>
      <artifactId>yang-binding</artifactId>
      <version>0.6.0-SNAPSHOT</version>
     </dependency>
      <artifactId>yang-binding</artifactId>
      <version>0.6.0-SNAPSHOT</version>
     </dependency>
+    <dependency>
+     <groupId>org.opendaylight.yangtools</groupId>
+     <artifactId>binding-type-provider</artifactId>
+     <version>0.6.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+     <groupId>org.opendaylight.yangtools</groupId>
+     <artifactId>binding-generator-util</artifactId>
+     <version>${yangtools.binding.version}</version>
+    </dependency>
+    <dependency>
+     <groupId>org.opendaylight.yangtools</groupId>
+     <artifactId>binding-model-api</artifactId>
+     <version>${yangtools.binding.version}</version>
+    </dependency>
+    <dependency>
+     <groupId>org.opendaylight.yangtools</groupId>
+     <artifactId>binding-generator-spi</artifactId>
+     <version>${yangtools.binding.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-lang</groupId>
+      <artifactId>commons-lang</artifactId>
+      <version>2.4</version>
+    </dependency>
+
+      <dependency>
+     <groupId>org.opendaylight.yangtools.thirdparty</groupId>
+     <artifactId>antlr-runtime-osgi</artifactId>
+     <version>4.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+     <groupId>org.opendaylight.yangtools.thirdparty</groupId>
+     <artifactId>xtend-lib-osgi</artifactId>
+     <version>2.4.2-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+     <groupId>org.opendaylight.yangtools</groupId>
+     <artifactId>yang-parser-api</artifactId>
+     <version>${yangtools.version}</version>
+    </dependency>
+    <dependency>
+     <groupId>org.opendaylight.yangtools</groupId>
+     <artifactId>yang-model-util</artifactId>
+     <version>${yangtools.version}</version>
+    </dependency>
+    <dependency>
+     <groupId>org.opendaylight.yangtools</groupId>
+     <artifactId>yang-parser-impl</artifactId>
+     <version>${yangtools.version}</version>
+    </dependency>
     <dependency>
      <groupId>org.opendaylight.yangtools</groupId>
      <artifactId>yang-common</artifactId>
     <dependency>
      <groupId>org.opendaylight.yangtools</groupId>
      <artifactId>yang-common</artifactId>
index ac68652455ce02de50013d28a61d322831e7107c..4ad7cf184b3f021d0dff58fe07f027ca49503fb6 100644 (file)
@@ -13,6 +13,17 @@ osgi.bundles=\
     reference\:file\:../lib/jersey-json-1.17.jar@2:start,\
     reference\:file\:../lib/jersey-server-1.17.jar@2:start
 
     reference\:file\:../lib/jersey-json-1.17.jar@2:start,\
     reference\:file\:../lib/jersey-server-1.17.jar@2:start
 
+# Netconf startup configuration
+netconf.tcp.address=127.0.0.1
+netconf.tcp.port=8383
+
+#netconf.tls.address=127.0.0.1
+#netconf.tls.port=8384
+#netconf.tls.keystore=
+#netconf.tls.keystore.password=
+
+netconf.config.persister.storageAdapterClass=org.opendaylight.controller.netconf.persist.impl.NoOpStorageAdapter
+
 # Set Default start level for framework
 osgi.bundles.defaultStartLevel=4
 # Extra packages to import from the boot class loader
 # Set Default start level for framework
 osgi.bundles.defaultStartLevel=4
 # Extra packages to import from the boot class loader
index 50a87fb568c54ec8e6f6d4e7895c793386d46cf9..7d04a12a14d6143fca2aa7ef9f105a4ec1f3aa56 100644 (file)
@@ -42,6 +42,9 @@
   <!-- OSGi logging bridge -->
   <logger name="org.opendaylight.controller.logging.bridge" level="WARN"/>
 
   <!-- OSGi logging bridge -->
   <logger name="org.opendaylight.controller.logging.bridge" level="WARN"/>
 
+  <!-- Netty -->
+  <logger name="io.netty" level="WARN"/>
+
   <!-- Openflow Protocol Plugin -->
   <logger name="org.opendaylight.controller.protocol_plugin.openflow" level="INFO"/>
   <logger name="org.opendaylight.controller.protocol_plugin.openflow.internal.DiscoveryService" level="INFO"/>
   <!-- Openflow Protocol Plugin -->
   <logger name="org.opendaylight.controller.protocol_plugin.openflow" level="INFO"/>
   <logger name="org.opendaylight.controller.protocol_plugin.openflow.internal.DiscoveryService" level="INFO"/>
diff --git a/opendaylight/netconf/config-netconf-connector/pom.xml b/opendaylight/netconf/config-netconf-connector/pom.xml
new file mode 100755 (executable)
index 0000000..0386378
--- /dev/null
@@ -0,0 +1,157 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>netconf-subsystem</artifactId>
+        <groupId>org.opendaylight.controller</groupId>
+        <version>0.2.1-SNAPSHOT</version>
+    </parent>
+    <artifactId>config-netconf-connector</artifactId>
+    <name>${project.artifactId}</name>
+    <packaging>bundle</packaging>
+
+    <dependencies>
+        <!-- compile dependencies -->
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+
+            <artifactId>yang-jmx-generator</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+
+            <artifactId>config-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-util</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+
+            <artifactId>yang-store-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-mapping-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-util</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+
+            <artifactId>yang-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+
+            <artifactId>config-manager</artifactId>
+            <version>${config.version}</version>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+
+            <artifactId>config-manager</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-impl</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+
+            <artifactId>yang-store-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>mockito-configuration</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.netconf.confignetconfconnector.osgi.Activator
+                        </Bundle-Activator>
+                        <Private-Package>
+                            org.opendaylight.controller.netconf.confignetconfconnector.mapping.*,
+                            org.opendaylight.controller.netconf.confignetconfconnector.operations.*,
+                            org.opendaylight.controller.netconf.confignetconfconnector.transactions,
+                            org.opendaylight.controller.netconf.confignetconfconnector.util,
+                            org.opendaylight.controller.netconf.confignetconfconnector.osgi,
+                            org.opendaylight.controller.config.util,
+                        </Private-Package>
+                        <Import-Package>
+                            com.google.common.base,
+                            com.google.common.collect,
+                            javax.annotation,
+                            javax.management,
+                            javax.management.openmbean,
+                            org.opendaylight.controller.config.api,
+                            org.opendaylight.controller.config.api.jmx,
+                            org.opendaylight.controller.config.yang.store.api,
+                            org.opendaylight.controller.config.yangjmxgenerator,
+                            org.opendaylight.controller.config.yangjmxgenerator.attribute,
+                            org.opendaylight.controller.netconf.api,
+                            org.opendaylight.controller.netconf.mapping.api,
+                            org.opendaylight.controller.netconf.util.mapping,
+                            org.opendaylight.controller.netconf.util.xml,
+                            org.opendaylight.yangtools.yang.common,
+                            org.opendaylight.yangtools.yang.model.api,
+                            org.osgi.framework,
+                            org.osgi.util.tracker,
+                            org.slf4j,
+                            org.w3c.dom
+                        </Import-Package>
+                        <Export-Package>
+                        </Export-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/AttributeIfcSwitchStatement.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/AttributeIfcSwitchStatement.java
new file mode 100644 (file)
index 0000000..5e3f542
--- /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.netconf.confignetconfconnector.mapping.attributes;
+
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.*;
+
+public abstract class AttributeIfcSwitchStatement<T> {
+
+    public T switchAttribute(AttributeIfc attributeIfc) {
+
+        if (attributeIfc instanceof JavaAttribute) {
+            return caseJavaAttribute((JavaAttribute) attributeIfc);
+        } else if (attributeIfc instanceof DependencyAttribute) {
+            return caseDependencyAttribute((DependencyAttribute) attributeIfc);
+        } else if (attributeIfc instanceof ListAttribute) {
+            return caseListAttribute((ListAttribute) attributeIfc);
+        } else if (attributeIfc instanceof TOAttribute) {
+            return caseTOAttribute((TOAttribute) attributeIfc);
+        }
+
+        throw new IllegalArgumentException("Unknown attribute type " + attributeIfc.getClass() + ", " + attributeIfc);
+    }
+
+    protected abstract T caseJavaAttribute(JavaAttribute attributeIfc);
+
+    protected abstract T caseDependencyAttribute(DependencyAttribute attributeIfc);
+
+    protected abstract T caseTOAttribute(TOAttribute attributeIfc);
+
+    protected abstract T caseListAttribute(ListAttribute attributeIfc);
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AbstractAttributeReadingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AbstractAttributeReadingStrategy.java
new file mode 100644 (file)
index 0000000..2ba1b61
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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.confignetconfconnector.mapping.attributes.fromxml;
+
+import java.util.List;
+
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+
+public abstract class AbstractAttributeReadingStrategy<A extends AttributeIfc> implements AttributeReadingStrategy {
+
+    private final A attributeIfc;
+
+    public AbstractAttributeReadingStrategy(A attributeIfc) {
+        this.attributeIfc = attributeIfc;
+    }
+
+    public A getAttributeIfc() {
+        return attributeIfc;
+    }
+
+    @Override
+    public AttributeConfigElement readElement(List<XmlElement> configNodes) {
+        if (configNodes.size() == 0)
+            return AttributeConfigElement.createNullValue(attributeIfc);
+
+        return readElementHook(configNodes);
+    }
+
+    abstract AttributeConfigElement readElementHook(List<XmlElement> configNodes);
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ArrayAttributeReadingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ArrayAttributeReadingStrategy.java
new file mode 100644 (file)
index 0000000..f07e740
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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.confignetconfconnector.mapping.attributes.fromxml;
+
+import com.google.common.collect.Lists;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+
+import java.util.List;
+
+public class ArrayAttributeReadingStrategy extends AbstractAttributeReadingStrategy<AttributeIfc> {
+
+    private final AttributeReadingStrategy innerStrategy;
+
+    /**
+     * @param attributeIfc
+     * @param innerStrategy
+     */
+    public ArrayAttributeReadingStrategy(AttributeIfc attributeIfc, AttributeReadingStrategy innerStrategy) {
+        super(attributeIfc);
+        this.innerStrategy = innerStrategy;
+    }
+
+    @Override
+    AttributeConfigElement readElementHook(List<XmlElement> configNodes) {
+        List<Object> innerList = Lists.newArrayList();
+        for (int i = 0; i < configNodes.size(); i++) {
+            innerList.add(innerStrategy.readElement(Lists.newArrayList(configNodes.get(i))).getValue());
+        }
+        return AttributeConfigElement.create(getAttributeIfc(), innerList);
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AttributeConfigElement.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AttributeConfigElement.java
new file mode 100644 (file)
index 0000000..fa249da
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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.confignetconfconnector.mapping.attributes.fromxml;
+
+import javax.management.openmbean.OpenType;
+
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving.AttributeResolvingStrategy;
+
+import com.google.common.base.Optional;
+
+/**
+ * Parsed xml element containing configuration for one attribute of an instance
+ * of some module. Contains default value extracted from yang file.
+ */
+public class AttributeConfigElement {
+    private final Object dafaultValue;
+    private final Object value;
+
+    private Optional<?> resolvedValue;
+    private Object resolvedDefaultValue;
+    private String jmxName;
+
+    public AttributeConfigElement(Object dafaultValue, Object value) {
+        this.dafaultValue = dafaultValue;
+        this.value = value;
+    }
+
+    public void setJmxName(String jmxName) {
+        this.jmxName = jmxName;
+    }
+
+    public String getJmxName() {
+        return jmxName;
+    }
+
+    public void resolveValue(AttributeResolvingStrategy<?, ? extends OpenType<?>> attributeResolvingStrategy,
+            String attrName) {
+        resolvedValue = attributeResolvingStrategy.parseAttribute(attrName, value);
+        Optional<?> resolvedDefault = attributeResolvingStrategy.parseAttribute(attrName, dafaultValue);
+        resolvedDefaultValue = resolvedDefault.isPresent() ? resolvedDefault.get() : null;
+
+    }
+
+    public static AttributeConfigElement create(AttributeIfc attributeIfc, Object value) {
+        return new AttributeConfigElement(attributeIfc.getNullableDefault(), value);
+    }
+
+    public static AttributeConfigElement createNullValue(AttributeIfc attributeIfc) {
+        return new AttributeConfigElement(attributeIfc.getNullableDefault(), null);
+    }
+
+    public Object getValue() {
+        return value;
+    }
+
+    public Optional<?> getResolvedValue() {
+        return resolvedValue;
+    }
+
+    public Object getResolvedDefaultValue() {
+        return resolvedDefaultValue;
+    }
+
+    @Override
+    public String toString() {
+        return "AttributeConfigElement [dafaultValue=" + dafaultValue + ", value=" + value + "]";
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AttributeReadingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AttributeReadingStrategy.java
new file mode 100644 (file)
index 0000000..6dae839
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml;
+
+import java.util.List;
+
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+
+public interface AttributeReadingStrategy {
+
+    public abstract AttributeConfigElement readElement(List<XmlElement> element);
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/CompositeAttributeReadingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/CompositeAttributeReadingStrategy.java
new file mode 100644 (file)
index 0000000..1cfb74d
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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.confignetconfconnector.mapping.attributes.fromxml;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class CompositeAttributeReadingStrategy extends AbstractAttributeReadingStrategy<TOAttribute> {
+
+    private final Map<String, AttributeReadingStrategy> innerStrategies;
+
+    public CompositeAttributeReadingStrategy(TOAttribute attributeIfc,
+            Map<String, AttributeReadingStrategy> innerStrategies) {
+        super(attributeIfc);
+        this.innerStrategies = innerStrategies;
+    }
+
+    @Override
+    AttributeConfigElement readElementHook(List<XmlElement> configNodes) {
+
+        Preconditions.checkState(configNodes.size() == 1, "This element should be present only once %s", configNodes);
+
+        XmlElement complexElement = configNodes.get(0);
+
+        Map<String, Object> innerMap = Maps.newHashMap();
+
+        Map<String, AttributeIfc> inner = getAttributeIfc().getYangPropertiesToTypesMap();
+
+        List<XmlElement> recognisedChildren = Lists.newArrayList();
+        for (Entry<String, AttributeIfc> innerAttrEntry : inner.entrySet()) {
+            List<XmlElement> childItem = complexElement.getChildElementsWithSameNamespace(innerAttrEntry.getKey());
+            recognisedChildren.addAll(childItem);
+
+            AttributeConfigElement resolvedInner = innerStrategies.get(innerAttrEntry.getKey()).readElement(childItem);
+
+            innerMap.put(innerAttrEntry.getKey(), resolvedInner.getValue());
+        }
+
+        complexElement.checkUnrecognisedElements(recognisedChildren);
+
+        return AttributeConfigElement.create(getAttributeIfc(), innerMap);
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ObjectNameAttributeReadingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ObjectNameAttributeReadingStrategy.java
new file mode 100644 (file)
index 0000000..bdb9391
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.confignetconfconnector.mapping.attributes.fromxml;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.ObjectNameAttributeMappingStrategy;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+
+import java.util.List;
+
+public class ObjectNameAttributeReadingStrategy extends AbstractAttributeReadingStrategy<AttributeIfc> {
+
+    public ObjectNameAttributeReadingStrategy(DependencyAttribute attributeIfc) {
+        super(attributeIfc);
+    }
+
+    @Override
+    AttributeConfigElement readElementHook(List<XmlElement> configNodes) {
+
+        XmlElement firstChild = configNodes.get(0);
+        Preconditions.checkState(configNodes.size() == 1, "This element should be present only once " + firstChild
+                + " but was " + configNodes.size());
+
+        Preconditions.checkNotNull(firstChild, "Element %s should be present", firstChild);
+        return AttributeConfigElement.create(getAttributeIfc(), resolve(firstChild));
+    }
+
+    private ObjectNameAttributeMappingStrategy.MappedDependency resolve(XmlElement firstChild) {
+        XmlElement typeElement = firstChild.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.TYPE_KEY);
+        String serviceName = typeElement.getTextContent();
+        XmlElement nameElement = firstChild.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.NAME_KEY);
+        String dependencyName = nameElement.getTextContent();
+
+        return new ObjectNameAttributeMappingStrategy.MappedDependency(serviceName, dependencyName);
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ObjectXmlReader.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ObjectXmlReader.java
new file mode 100644 (file)
index 0000000..8663de7
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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.confignetconfconnector.mapping.attributes.fromxml;
+
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.*;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement;
+
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.SimpleType;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class ObjectXmlReader extends AttributeIfcSwitchStatement<AttributeReadingStrategy> {
+
+    private String key;
+
+    public Map<String, AttributeReadingStrategy> prepareReading(Map<String, AttributeIfc> yangToAttrConfig) {
+        Map<String, AttributeReadingStrategy> strategies = Maps.newHashMap();
+
+        for (Entry<String, AttributeIfc> attributeEntry : yangToAttrConfig.entrySet()) {
+            AttributeReadingStrategy strat = prepareReadingStrategy(attributeEntry.getKey(), attributeEntry.getValue());
+            strategies.put(attributeEntry.getKey(), strat);
+        }
+        return strategies;
+    }
+
+    private AttributeReadingStrategy prepareReadingStrategy(String key, AttributeIfc attributeIfc) {
+        this.key = key;
+        return switchAttribute(attributeIfc);
+    }
+
+    @Override
+    protected AttributeReadingStrategy caseJavaAttribute(JavaAttribute attributeIfc) {
+        if (attributeIfc.getOpenType() instanceof SimpleType<?>)
+            return new SimpleAttributeReadingStrategy(attributeIfc);
+        else if (attributeIfc.getOpenType() instanceof ArrayType<?>) {
+            SimpleAttributeReadingStrategy innerStrategy = new SimpleAttributeReadingStrategy(
+                    ((ArrayType<?>) attributeIfc.getOpenType()).getElementOpenType());
+            return new ArrayAttributeReadingStrategy(attributeIfc, innerStrategy);
+        }
+        throw new IllegalStateException(JavaAttribute.class + " can only provide open type " + SimpleType.class
+                + " or " + ArrayType.class);
+    }
+
+    @Override
+    protected AttributeReadingStrategy caseDependencyAttribute(DependencyAttribute attributeIfc) {
+        return new ObjectNameAttributeReadingStrategy(attributeIfc);
+    }
+
+    @Override
+    protected AttributeReadingStrategy caseTOAttribute(TOAttribute attributeIfc) {
+        Map<String, AttributeIfc> inner = attributeIfc.getYangPropertiesToTypesMap();
+        Map<String, AttributeReadingStrategy> innerStrategies = Maps.newHashMap();
+
+        for (Entry<String, AttributeIfc> innerAttrEntry : inner.entrySet()) {
+            AttributeReadingStrategy innerStrat = prepareReadingStrategy(innerAttrEntry.getKey(),
+                    innerAttrEntry.getValue());
+            innerStrategies.put(innerAttrEntry.getKey(), innerStrat);
+        }
+
+        return new CompositeAttributeReadingStrategy(attributeIfc, innerStrategies);
+    }
+
+    @Override
+    protected AttributeReadingStrategy caseListAttribute(ListAttribute attributeIfc) {
+        AttributeIfc innerAttr = attributeIfc.getInnerAttribute();
+        AttributeReadingStrategy innerStrategy = prepareReadingStrategy(key, innerAttr);
+        return new ArrayAttributeReadingStrategy(attributeIfc, innerStrategy);
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/SimpleAttributeReadingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/SimpleAttributeReadingStrategy.java
new file mode 100644 (file)
index 0000000..c5c287f
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+
+import javax.management.openmbean.OpenType;
+import java.util.List;
+
+public class SimpleAttributeReadingStrategy extends AbstractAttributeReadingStrategy<AttributeIfc> {
+
+    public SimpleAttributeReadingStrategy(AttributeIfc attributeIfc) {
+        super(attributeIfc);
+    }
+
+    /**
+     * @param elementOpenType
+     */
+    public SimpleAttributeReadingStrategy(OpenType<?> elementOpenType) {
+        super(new AttributeIfcWrapper(elementOpenType));
+    }
+
+    @Override
+    AttributeConfigElement readElementHook(List<XmlElement> configNodes) {
+        XmlElement xmlElement = configNodes.get(0);
+        Preconditions.checkState(configNodes.size() == 1, "This element should be present only once " + xmlElement
+                + " but was " + configNodes.size());
+
+        String textContent = xmlElement.getTextContent();
+
+        Preconditions.checkNotNull(textContent, "This element should contain text %s", xmlElement);
+        return AttributeConfigElement.create(getAttributeIfc(), textContent);
+    }
+
+    /**
+     * Wrapper for JavaAttribute inner element attributes (in case JavaAttribute
+     * is array)
+     */
+    static class AttributeIfcWrapper implements AttributeIfc {
+
+        private final OpenType<?> elementOpenType;
+
+        public AttributeIfcWrapper(OpenType<?> elementOpenType) {
+            this.elementOpenType = elementOpenType;
+        }
+
+        @Override
+        public String getAttributeYangName() {
+            return null;
+        }
+
+        @Override
+        public String getNullableDescription() {
+            return null;
+        }
+
+        @Override
+        public String getNullableDefault() {
+            return null;
+        }
+
+        @Override
+        public String getUpperCaseCammelCase() {
+            return null;
+        }
+
+        @Override
+        public String getLowerCaseCammelCase() {
+            return null;
+        }
+
+        @Override
+        public OpenType<?> getOpenType() {
+            return elementOpenType;
+        }
+
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/AbstractAttributeMappingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/AbstractAttributeMappingStrategy.java
new file mode 100644 (file)
index 0000000..0d5fcb9
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping;
+
+import javax.management.openmbean.OpenType;
+
+public abstract class AbstractAttributeMappingStrategy<T, O extends OpenType<?>> implements
+        AttributeMappingStrategy<T, O> {
+
+    private final O attrOpenType;
+
+    public AbstractAttributeMappingStrategy(O attributeIfc) {
+        this.attrOpenType = attributeIfc;
+    }
+
+    @Override
+    public O getOpenType() {
+        return attrOpenType;
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ArrayAttributeMappingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ArrayAttributeMappingStrategy.java
new file mode 100644 (file)
index 0000000..30436bb
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping;
+
+import java.lang.reflect.Array;
+import java.util.List;
+
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.OpenType;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+
+public class ArrayAttributeMappingStrategy extends AbstractAttributeMappingStrategy<List<Object>, ArrayType<?>> {
+
+    private final AttributeMappingStrategy<?, ? extends OpenType<?>> innerElementStrategy;
+
+    public ArrayAttributeMappingStrategy(ArrayType<?> arrayType,
+            AttributeMappingStrategy<?, ? extends OpenType<?>> innerElementStrategy) {
+        super(arrayType);
+        this.innerElementStrategy = innerElementStrategy;
+    }
+
+    @Override
+    public Optional<List<Object>> mapAttribute(Object value) {
+        if (value == null)
+            return Optional.absent();
+
+        Preconditions.checkArgument(value.getClass().isArray(), "Value has to be instanceof Array ");
+
+        List<Object> retVal = Lists.newArrayList();
+
+        for (int i = 0; i < Array.getLength(value); i++) {
+            Object innerValue = Array.get(value, i);
+            // String expectedClassName =
+            // getOpenType().getElementOpenType().getClassName();
+            // String realClassName = value.getClass().getName();
+
+            // Preconditions.checkState(realClassName.contains(expectedClassName),
+            // "Element in collection/array should be of type " +
+            // expectedClassName + " but was "
+            // + realClassName + " for attribute: " + getOpenType());
+
+            Optional<?> mapAttribute = innerElementStrategy.mapAttribute(innerValue);
+
+            if (mapAttribute.isPresent())
+                retVal.add(mapAttribute.get());
+        }
+
+        return Optional.of(retVal);
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/AttributeMappingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/AttributeMappingStrategy.java
new file mode 100644 (file)
index 0000000..51dfc58
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * 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.confignetconfconnector.mapping.attributes.mapping;
+
+import com.google.common.base.Optional;
+
+import javax.management.openmbean.OpenType;
+
+public interface AttributeMappingStrategy<T, O extends OpenType<?>> {
+
+    O getOpenType();
+
+    Optional<T> mapAttribute(Object o);
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/CompositeAttributeMappingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/CompositeAttributeMappingStrategy.java
new file mode 100644 (file)
index 0000000..252b13b
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenType;
+import java.util.Map;
+import java.util.Set;
+
+public class CompositeAttributeMappingStrategy extends
+        AbstractAttributeMappingStrategy<Map<String, Object>, CompositeType> {
+
+    private final Map<String, AttributeMappingStrategy<?, ? extends OpenType<?>>> innerStrategies;
+    private final Map<String, String> jmxToJavaNameMapping;
+
+    public CompositeAttributeMappingStrategy(CompositeType compositeType,
+            Map<String, AttributeMappingStrategy<?, ? extends OpenType<?>>> innerStrategies,
+            Map<String, String> jmxToJavaNameMapping) {
+        super(compositeType);
+        this.innerStrategies = innerStrategies;
+        this.jmxToJavaNameMapping = jmxToJavaNameMapping;
+    }
+
+    @Override
+    public Optional<Map<String, Object>> mapAttribute(Object value) {
+        if (value == null)
+            return Optional.absent();
+
+        Util.checkType(value, CompositeDataSupport.class);
+
+        CompositeDataSupport compositeData = (CompositeDataSupport) value;
+        CompositeType currentType = compositeData.getCompositeType();
+        CompositeType expectedType = getOpenType();
+
+        Set<String> expectedCompositeTypeKeys = expectedType.keySet();
+        Set<String> currentCompositeTypeKeys = currentType.keySet();
+        Preconditions.checkArgument(expectedCompositeTypeKeys.equals(currentCompositeTypeKeys),
+                "Composite type mismatch, expected composite type with attributes " + expectedCompositeTypeKeys
+                        + " but was " + currentCompositeTypeKeys);
+
+        Map<String, Object> retVal = Maps.newHashMap();
+
+        for (String jmxName : jmxToJavaNameMapping.keySet()) {
+            String innerAttrJmxName = jmxName;
+            Object innerValue = compositeData.get(innerAttrJmxName);
+
+            AttributeMappingStrategy<?, ? extends OpenType<?>> attributeMappingStrategy = innerStrategies
+                    .get(innerAttrJmxName);
+            Optional<?> mapAttribute = attributeMappingStrategy.mapAttribute(innerValue);
+            if (mapAttribute.isPresent())
+                retVal.put(jmxToJavaNameMapping.get(innerAttrJmxName), mapAttribute.get());
+        }
+
+        return Optional.of(retVal);
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectMapper.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectMapper.java
new file mode 100644 (file)
index 0000000..52b6801
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * 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.confignetconfconnector.mapping.attributes.mapping;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.*;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services;
+
+import com.google.common.collect.Maps;
+
+public class ObjectMapper extends AttributeIfcSwitchStatement<AttributeMappingStrategy<?, ? extends OpenType<?>>> {
+
+    private final Services dependencyTracker;
+
+    public ObjectMapper(Services depTracker) {
+        this.dependencyTracker = depTracker;
+    }
+
+    public Map<String, AttributeMappingStrategy<?, ? extends OpenType<?>>> prepareMapping(
+            Map<String, AttributeIfc> configDefinition) {
+        Map<String, AttributeMappingStrategy<?, ? extends OpenType<?>>> strategies = Maps.newHashMap();
+
+        for (Entry<String, AttributeIfc> attrEntry : configDefinition.entrySet()) {
+            strategies.put(attrEntry.getKey(), prepareStrategy(attrEntry.getValue()));
+        }
+
+        return strategies;
+    }
+
+    private AttributeMappingStrategy<?, ? extends OpenType<?>> prepareStrategy(AttributeIfc attributeIfc) {
+        return switchAttribute(attributeIfc);
+    }
+
+    private Map<String, String> createJmxToYangMapping(TOAttribute attributeIfc) {
+        Map<String, String> retVal = Maps.newHashMap();
+        for (Entry<String, AttributeIfc> entry : attributeIfc.getJmxPropertiesToTypesMap().entrySet()) {
+            retVal.put(entry.getKey(), (entry.getValue()).getAttributeYangName());
+        }
+        return retVal;
+    }
+
+    @Override
+    protected AttributeMappingStrategy<?, ? extends OpenType<?>> caseJavaAttribute(JavaAttribute attributeIfc) {
+
+        if (attributeIfc.getOpenType() instanceof SimpleType<?>)
+            return new SimpleAttributeMappingStrategy((SimpleType<?>) attributeIfc.getOpenType());
+        else if (attributeIfc.getOpenType() instanceof ArrayType<?>) {
+            ArrayType<?> arrayType = (ArrayType<?>) attributeIfc.getOpenType();
+            AttributeMappingStrategy<?, ? extends OpenType<?>> innerStrategy = new SimpleAttributeMappingStrategy(
+                    (SimpleType<?>) arrayType.getElementOpenType());
+            return new ArrayAttributeMappingStrategy(arrayType, innerStrategy);
+        }
+        throw new IllegalStateException(JavaAttribute.class + " can only provide open type " + SimpleType.class
+                + " or " + ArrayType.class);
+    }
+
+    @Override
+    protected AttributeMappingStrategy<?, ? extends OpenType<?>> caseDependencyAttribute(
+            DependencyAttribute attributeIfc) {
+        String serviceName = attributeIfc.getDependency().getSie().getQName().getLocalName();
+        return new ObjectNameAttributeMappingStrategy((SimpleType<?>) attributeIfc.getOpenType(), dependencyTracker,
+                serviceName);
+    }
+
+    @Override
+    protected AttributeMappingStrategy<?, ? extends OpenType<?>> caseTOAttribute(TOAttribute attributeIfc) {
+        Map<String, AttributeMappingStrategy<?, ? extends OpenType<?>>> innerStrategies = Maps.newHashMap();
+
+        for (Entry<String, AttributeIfc> innerAttrEntry : attributeIfc.getJmxPropertiesToTypesMap().entrySet()) {
+            innerStrategies.put(innerAttrEntry.getKey(), prepareStrategy(innerAttrEntry.getValue()));
+        }
+
+        return new CompositeAttributeMappingStrategy((CompositeType) attributeIfc.getOpenType(), innerStrategies,
+                createJmxToYangMapping(attributeIfc));
+    }
+
+    @Override
+    protected AttributeMappingStrategy<?, ? extends OpenType<?>> caseListAttribute(ListAttribute attributeIfc) {
+        return new ArrayAttributeMappingStrategy(attributeIfc.getOpenType(),
+                prepareStrategy(attributeIfc.getInnerAttribute()));
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectNameAttributeMappingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectNameAttributeMappingStrategy.java
new file mode 100644 (file)
index 0000000..1febf02
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+
+import javax.management.ObjectName;
+import javax.management.openmbean.SimpleType;
+
+public class ObjectNameAttributeMappingStrategy extends
+        AbstractAttributeMappingStrategy<ObjectNameAttributeMappingStrategy.MappedDependency, SimpleType<?>> {
+
+    private final Services tracker;
+    private final String serviceName;
+
+    public ObjectNameAttributeMappingStrategy(SimpleType<?> openType, Services dependencyTracker, String serviceName) {
+        super(openType);
+        this.tracker = dependencyTracker;
+        this.serviceName = serviceName;
+    }
+
+    @Override
+    public Optional<MappedDependency> mapAttribute(Object value) {
+        if (value == null)
+            return Optional.absent();
+
+        String expectedClass = getOpenType().getClassName();
+        String realClass = value.getClass().getName();
+        Preconditions.checkArgument(realClass.equals(expectedClass), "Type mismatch, expected " + expectedClass
+                + " but was " + realClass);
+        Util.checkType(value, ObjectName.class);
+
+        ObjectName on = (ObjectName) value;
+        String refName = tracker.addServiceEntry(serviceName, on);
+
+        return Optional.of(new MappedDependency(serviceName, refName));
+    }
+
+    public static class MappedDependency {
+        private final String serviceName, refName;
+
+        public MappedDependency(String serviceName, String refName) {
+            this.serviceName = serviceName;
+            this.refName = refName;
+        }
+
+        public String getServiceName() {
+            return serviceName;
+        }
+
+        public String getRefName() {
+            return refName;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuffer sb = new StringBuffer("MappedDependency{");
+            sb.append("serviceName='").append(serviceName).append('\'');
+            sb.append(", refName='").append(refName).append('\'');
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/SimpleAttributeMappingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/SimpleAttributeMappingStrategy.java
new file mode 100644 (file)
index 0000000..d92b7c4
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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.confignetconfconnector.mapping.attributes.mapping;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+
+import javax.management.openmbean.SimpleType;
+import java.util.Date;
+import java.util.Map;
+
+public class SimpleAttributeMappingStrategy extends AbstractAttributeMappingStrategy<String, SimpleType<?>> {
+
+    public SimpleAttributeMappingStrategy(SimpleType<?> openType) {
+        super(openType);
+    }
+
+    @Override
+    public Optional<String> mapAttribute(Object value) {
+        if (value == null)
+            return Optional.absent();
+
+        String expectedClass = getOpenType().getClassName();
+        String realClass = value.getClass().getName();
+        Preconditions.checkArgument(realClass.equals(expectedClass), "Type mismatch, expected " + expectedClass
+                + " but was " + realClass);
+
+        WriterPlugin prefferedPlugin = writerPlugins.get(value.getClass().getCanonicalName());
+        prefferedPlugin = prefferedPlugin == null ? writerPlugins.get(DEFAULT_WRITER_PLUGIN) : prefferedPlugin;
+        return Optional.of(prefferedPlugin.writeObject(value));
+    }
+
+    private static final String DEFAULT_WRITER_PLUGIN = "default";
+    private static final Map<String, WriterPlugin> writerPlugins = Maps.newHashMap();
+    static {
+        writerPlugins.put(DEFAULT_WRITER_PLUGIN, new DefaultWriterPlugin());
+        writerPlugins.put(Date.class.getCanonicalName(), new DatePlugin());
+    }
+
+    /**
+     * Custom writer plugins must implement this interface.
+     */
+    static interface WriterPlugin {
+        String writeObject(Object value);
+    }
+
+    static class DefaultWriterPlugin implements WriterPlugin {
+
+        @Override
+        public String writeObject(Object value) {
+            return value.toString();
+        }
+    }
+
+    static class DatePlugin implements WriterPlugin {
+
+        @Override
+        public String writeObject(Object value) {
+            Preconditions.checkArgument(value instanceof Date, "Attribute must be Date");
+            return Util.writeDate((Date) value);
+        }
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/AbstractAttributeResolvingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/AbstractAttributeResolvingStrategy.java
new file mode 100644 (file)
index 0000000..2808d5d
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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.confignetconfconnector.mapping.attributes.resolving;
+
+import javax.management.openmbean.OpenType;
+
+abstract class AbstractAttributeResolvingStrategy<T, O extends OpenType<?>> implements AttributeResolvingStrategy<T, O> {
+    private final O openType;
+
+    public AbstractAttributeResolvingStrategy(O openType) {
+        this.openType = openType;
+    }
+
+    @Override
+    public O getOpenType() {
+        return openType;
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ArrayAttributeResolvingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ArrayAttributeResolvingStrategy.java
new file mode 100644 (file)
index 0000000..adee8be
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * 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.confignetconfconnector.mapping.attributes.resolving;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenType;
+import java.lang.reflect.Array;
+import java.util.List;
+
+final class ArrayAttributeResolvingStrategy extends AbstractAttributeResolvingStrategy<Object, ArrayType<?>> {
+
+    private final AttributeResolvingStrategy<?, ? extends OpenType<?>> innerTypeResolvingStrategy;
+
+    private static final Logger logger = LoggerFactory.getLogger(ArrayAttributeResolvingStrategy.class);
+
+    public ArrayAttributeResolvingStrategy(AttributeResolvingStrategy<?, ? extends OpenType<?>> innerTypeResolved,
+            ArrayType<?> openType) {
+        super(openType);
+        this.innerTypeResolvingStrategy = innerTypeResolved;
+    }
+
+    @Override
+    public Optional<Object> parseAttribute(String attrName, Object value) {
+        if (value == null) {
+            return Optional.absent();
+        }
+
+        Util.checkType(value, List.class);
+        List<?> valueList = (List<?>) value;
+
+        Class<?> innerTypeClass = null;
+
+        if (innerTypeResolvingStrategy.getOpenType() instanceof CompositeType) {
+            innerTypeClass = CompositeDataSupport.class;
+        } else
+            try {
+                innerTypeClass = Class.forName(getOpenType().getElementOpenType().getClassName());
+            } catch (ClassNotFoundException e) {
+                throw new RuntimeException("Unable to locate class for "
+                        + getOpenType().getElementOpenType().getClassName(), e);
+            }
+
+        Object parsedArray = null;
+
+        if (getOpenType().isPrimitiveArray()) {
+            Class<?> primitiveType = getPrimitiveType(innerTypeClass);
+            parsedArray = Array.newInstance(primitiveType, valueList.size());
+        } else
+            parsedArray = Array.newInstance(innerTypeClass, valueList.size());
+
+        int i = 0;
+        for (Object innerValue : valueList) {
+            Optional<?> parsedElement = innerTypeResolvingStrategy.parseAttribute(attrName + "_" + i, innerValue);
+
+            if (!parsedElement.isPresent())
+                continue;
+
+            Array.set(parsedArray, i, parsedElement.get());
+            // parsedArray[i] = parsedElement.get();
+            i++;
+        }
+
+        logger.debug("Attribute {} : {} parsed to type {} as {}", attrName, value, getOpenType(),
+                toStringArray(parsedArray));
+
+        return Optional.of(parsedArray);
+    }
+
+    private static String toStringArray(Object array) {
+        StringBuilder build = new StringBuilder(array.toString());
+        build.append(" [");
+        for (int i = 0; i < Array.getLength(array); i++) {
+            build.append(Array.get(array, i).toString());
+            build.append(",");
+        }
+        build.append("]]");
+        return build.toString();
+    }
+
+    private static Class<?> getPrimitiveType(Class<?> innerTypeClass) {
+        try {
+            return (Class<?>) innerTypeClass.getField("TYPE").get(null);
+        } catch (Exception e) {
+            throw new RuntimeException("Unable to determine primitive type to " + innerTypeClass);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "ResolvedArrayTypeAttributeType [innerTypeResolved=" + innerTypeResolvingStrategy + "]";
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/AttributeResolvingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/AttributeResolvingStrategy.java
new file mode 100644 (file)
index 0000000..0bb274a
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving;
+
+import com.google.common.base.Optional;
+
+import javax.management.openmbean.OpenType;
+
+/**
+ * Create real object from String or Map that corresponds to given opentype.
+ */
+public interface AttributeResolvingStrategy<T, O extends OpenType<?>> {
+    O getOpenType();
+
+    Optional<T> parseAttribute(String attrName, Object value);
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/CompositeAttributeResolvingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/CompositeAttributeResolvingStrategy.java
new file mode 100644 (file)
index 0000000..a85f306
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import java.util.Map;
+
+final class CompositeAttributeResolvingStrategy extends
+        AbstractAttributeResolvingStrategy<CompositeDataSupport, CompositeType> {
+    private final Map<String, AttributeResolvingStrategy<?, ? extends OpenType<?>>> innerTypes;
+    private final Map<String, String> yangToJavaAttrMapping;
+
+    private static final Logger logger = LoggerFactory.getLogger(CompositeAttributeResolvingStrategy.class);
+
+    CompositeAttributeResolvingStrategy(Map<String, AttributeResolvingStrategy<?, ? extends OpenType<?>>> innerTypes,
+            CompositeType openType, Map<String, String> yangToJavaAttrMapping) {
+        super(openType);
+        this.innerTypes = innerTypes;
+        this.yangToJavaAttrMapping = yangToJavaAttrMapping;
+    }
+
+    @Override
+    public String toString() {
+        return "ResolvedCompositeAttribute [" + innerTypes + "]";
+    }
+
+    @Override
+    public Optional<CompositeDataSupport> parseAttribute(String attrName, Object value) {
+
+        if (value == null) {
+            return Optional.absent();
+        }
+
+        Util.checkType(value, Map.class);
+        Map<?, ?> valueMap = (Map<?, ?>) value;
+
+        Map<String, Object> items = Maps.newHashMap();
+        Map<String, OpenType<?>> openTypes = Maps.newHashMap();
+
+        for (Object innerAttrName : innerTypes.keySet()) {
+            Preconditions.checkState(innerAttrName instanceof String, "Attribute name must be string");
+            String innerAttrNameStr = (String) innerAttrName;
+
+            AttributeResolvingStrategy<?, ? extends OpenType<?>> attributeResolvingStrategy = innerTypes
+                    .get(innerAttrName);
+
+            Object valueToParse = valueMap.get(innerAttrName);
+
+            Optional<?> parsedInnerValue = attributeResolvingStrategy.parseAttribute(innerAttrNameStr, valueToParse);
+
+            openTypes.put(innerAttrNameStr, attributeResolvingStrategy.getOpenType());
+
+            items.put(yangToJavaAttrMapping.get(innerAttrNameStr),
+                    parsedInnerValue.isPresent() ? parsedInnerValue.get() : null);
+        }
+
+        CompositeDataSupport parsedValue = null;
+        try {
+            parsedValue = new CompositeDataSupport(getOpenType(), items);
+        } catch (OpenDataException e) {
+            throw new RuntimeException("An error occured during restoration of composite type " + this
+                    + " for attribute " + attrName + " from value " + value, e);
+        }
+
+        logger.debug("Attribute {} : {} parsed to type {} as {}", attrName, value, getOpenType(), parsedValue);
+
+        return Optional.of(parsedValue);
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ObjectNameAttributeResolvingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ObjectNameAttributeResolvingStrategy.java
new file mode 100644 (file)
index 0000000..5469015
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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.confignetconfconnector.mapping.attributes.resolving;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.ObjectNameAttributeMappingStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services.ServiceInstance;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.management.ObjectName;
+import javax.management.openmbean.SimpleType;
+
+public class ObjectNameAttributeResolvingStrategy extends AbstractAttributeResolvingStrategy<ObjectName, SimpleType<?>> {
+
+    private final Services serviceTracker;
+    private static final Logger logger = LoggerFactory.getLogger(ObjectNameAttributeResolvingStrategy.class);
+
+    ObjectNameAttributeResolvingStrategy(Services serviceTracker) {
+        super(SimpleType.OBJECTNAME);
+        this.serviceTracker = serviceTracker;
+    }
+
+    @Override
+    public Optional<ObjectName> parseAttribute(String attrName, Object value) {
+        if (value == null) {
+            return Optional.absent();
+        }
+
+        Util.checkType(value, ObjectNameAttributeMappingStrategy.MappedDependency.class);
+
+        ObjectNameAttributeMappingStrategy.MappedDependency mappedDep = (ObjectNameAttributeMappingStrategy.MappedDependency) value;
+        ServiceInstance byRefName = serviceTracker.getByServiceAndRefName(mappedDep.getServiceName(),
+                mappedDep.getRefName());
+        ObjectName on = ObjectNameUtil.createReadOnlyModuleON(byRefName.getModuleName(), byRefName.getInstanceName());
+        logger.debug("Attribute {} : {} parsed to type {}", attrName, value, getOpenType());
+        return Optional.of(on);
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ObjectResolver.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ObjectResolver.java
new file mode 100644 (file)
index 0000000..3860471
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * 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.confignetconfconnector.mapping.attributes.resolving;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.*;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+
+public class ObjectResolver extends AttributeIfcSwitchStatement<AttributeResolvingStrategy<?, ? extends OpenType<?>>> {
+
+    private final Services serviceTracker;
+    private OpenType<?> openType;
+
+    public ObjectResolver(Services serviceTracker) {
+        this.serviceTracker = serviceTracker;
+    }
+
+    public Map<String, AttributeResolvingStrategy<?, ? extends OpenType<?>>> prepareResolving(
+            Map<String, AttributeIfc> configDefinition) {
+        Map<String, AttributeResolvingStrategy<?, ? extends OpenType<?>>> strategies = Maps.newHashMap();
+
+        for (Entry<String, AttributeIfc> attrEntry : configDefinition.entrySet()) {
+            strategies.put(attrEntry.getKey(),
+                    prepareStrategy(attrEntry.getValue(), attrEntry.getValue().getOpenType()));
+        }
+
+        return strategies;
+    }
+
+    private AttributeResolvingStrategy<?, ? extends OpenType<?>> prepareStrategy(AttributeIfc attributeIfc,
+            OpenType<?> openType) {
+
+        this.openType = openType;
+        return switchAttribute(attributeIfc);
+    }
+
+    private Map<String, String> createYangToJmxMapping(TOAttribute attributeIfc) {
+        Map<String, String> retVal = Maps.newHashMap();
+        for (Entry<String, AttributeIfc> entry : attributeIfc.getYangPropertiesToTypesMap().entrySet()) {
+            retVal.put(entry.getKey(), (entry.getValue()).getLowerCaseCammelCase());
+        }
+        return retVal;
+    }
+
+    @Override
+    protected AttributeResolvingStrategy<?, ? extends OpenType<?>> caseJavaAttribute(JavaAttribute attributeIfc) {
+        if (attributeIfc.getOpenType() instanceof SimpleType<?>)
+            return new SimpleAttributeResolvingStrategy((SimpleType<?>) openType);
+        else if (attributeIfc.getOpenType() instanceof ArrayType<?>) {
+            ArrayType<?> arrayType = (ArrayType<?>) openType;
+            SimpleType<?> innerType = (SimpleType<?>) arrayType.getElementOpenType();
+            AttributeResolvingStrategy<?, ? extends OpenType<?>> strat = new SimpleAttributeResolvingStrategy(innerType);
+            return new ArrayAttributeResolvingStrategy(strat, arrayType);
+        }
+        throw new IllegalStateException(JavaAttribute.class + " can only provide open type " + SimpleType.class
+                + " or " + ArrayType.class);
+    }
+
+    @Override
+    protected AttributeResolvingStrategy<?, ? extends OpenType<?>> caseDependencyAttribute(
+            DependencyAttribute attributeIfc) {
+        return new ObjectNameAttributeResolvingStrategy(serviceTracker);
+    }
+
+    @Override
+    protected AttributeResolvingStrategy<?, ? extends OpenType<?>> caseTOAttribute(TOAttribute attributeIfc) {
+        CompositeType compositeType = (CompositeType) openType;
+        Map<String, AttributeResolvingStrategy<?, ? extends OpenType<?>>> innerMap = Maps.newHashMap();
+        for (String innerName : compositeType.keySet()) {
+            Preconditions.checkState(attributeIfc instanceof TOAttribute, "Unexpected state, " + attributeIfc
+                    + " should be instance of " + TOAttribute.class.getName());
+            AttributeIfc innerAttributeIfc = attributeIfc.getJmxPropertiesToTypesMap().get(innerName);
+            innerMap.put(innerAttributeIfc.getAttributeYangName(),
+                    prepareStrategy(innerAttributeIfc, compositeType.getType(innerName)));
+        }
+        return new CompositeAttributeResolvingStrategy(innerMap, compositeType, createYangToJmxMapping(attributeIfc));
+    }
+
+    @Override
+    protected AttributeResolvingStrategy<?, ? extends OpenType<?>> caseListAttribute(ListAttribute attributeIfc) {
+        ArrayType<?> arrayType = (ArrayType<?>) openType;
+        OpenType<?> innerType = arrayType.getElementOpenType();
+        AttributeIfc inner = attributeIfc.getInnerAttribute();
+        return new ArrayAttributeResolvingStrategy(prepareStrategy(inner, innerType), arrayType);
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/SimpleAttributeResolvingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/SimpleAttributeResolvingStrategy.java
new file mode 100644 (file)
index 0000000..3b88157
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.management.openmbean.SimpleType;
+import java.lang.reflect.Method;
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.Map;
+
+final class SimpleAttributeResolvingStrategy extends AbstractAttributeResolvingStrategy<Object, SimpleType<?>> {
+
+    private static final Logger logger = LoggerFactory.getLogger(SimpleAttributeResolvingStrategy.class);
+
+    SimpleAttributeResolvingStrategy(SimpleType<?> simpleType) {
+        super(simpleType);
+    }
+
+    @Override
+    public String toString() {
+        return "ResolvedSimpleAttribute [" + getOpenType().getClassName() + "]";
+    }
+
+    @Override
+    public Optional<Object> parseAttribute(String attrName, Object value) {
+        if (value == null) {
+            return Optional.absent();
+        }
+
+        Class<?> cls;
+        try {
+            cls = Class.forName(getOpenType().getClassName());
+        } catch (ClassNotFoundException e) {
+            throw new RuntimeException("Unable to locate class for " + getOpenType().getClassName(), e);
+        }
+
+        Util.checkType(value, String.class);
+
+        Resolver prefferedPlugin = resolverPlugins.get(cls.getCanonicalName());
+        prefferedPlugin = prefferedPlugin == null ? resolverPlugins.get(DEFAULT_RESOLVERS) : prefferedPlugin;
+
+        Object parsedValue = prefferedPlugin.resolveObject(cls, attrName, (String) value);
+        logger.debug("Attribute {} : {} parsed to type {} with value {}", attrName, value, getOpenType(), parsedValue);
+        return Optional.of(parsedValue);
+    }
+
+    private static final String DEFAULT_RESOLVERS = "default";
+    private static final Map<String, Resolver> resolverPlugins = Maps.newHashMap();
+
+    static {
+        resolverPlugins.put(DEFAULT_RESOLVERS, new DefaultResolver());
+        resolverPlugins.put(String.class.getCanonicalName(), new StringResolver());
+        resolverPlugins.put(Date.class.getCanonicalName(), new DateResolver());
+        resolverPlugins.put(Character.class.getCanonicalName(), new CharResolver());
+        resolverPlugins.put(BigInteger.class.getCanonicalName(), new BigIntegerResolver());
+    }
+
+    static interface Resolver {
+        Object resolveObject(Class<?> type, String attrName, String value);
+    }
+
+    static class DefaultResolver implements Resolver {
+
+        @Override
+        public Object resolveObject(Class<?> type, String attrName, String value) {
+            try {
+                Object parsedValue = parseObject(type, value);
+                return parsedValue;
+            } catch (Exception e) {
+                throw new RuntimeException("Unable to resolve attribute " + attrName + " from " + value, e);
+            }
+        }
+
+        protected Object parseObject(Class<?> type, String value) throws Exception {
+            Method method = type.getMethod("valueOf", String.class);
+            Object parsedValue = method.invoke(null, value);
+            return parsedValue;
+        }
+    }
+
+    static class StringResolver extends DefaultResolver {
+
+        @Override
+        protected Object parseObject(Class<?> type, String value) throws Exception {
+            return value;
+        }
+    }
+
+    static class BigIntegerResolver extends DefaultResolver {
+
+        @Override
+        protected Object parseObject(Class<?> type, String value) throws Exception {
+            return new BigInteger(value);
+        }
+    }
+
+    static class CharResolver extends DefaultResolver {
+
+        @Override
+        protected Object parseObject(Class<?> type, String value) throws Exception {
+            return new Character(value.charAt(0));
+        }
+    }
+
+    static class DateResolver extends DefaultResolver {
+
+        @Override
+        protected Object parseObject(Class<?> type, String value) throws Exception {
+            return Util.readDate(value);
+        }
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ArrayAttributeWritingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ArrayAttributeWritingStrategy.java
new file mode 100644 (file)
index 0000000..89dc251
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.toxml;
+
+import java.util.List;
+
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.w3c.dom.Element;
+
+public class ArrayAttributeWritingStrategy implements AttributeWritingStrategy {
+
+    private final AttributeWritingStrategy innnerStrategy;
+
+    public ArrayAttributeWritingStrategy(AttributeWritingStrategy innerStrategy) {
+        this.innnerStrategy = innerStrategy;
+    }
+
+    @Override
+    public void writeElement(Element parentElement, String namespace, Object value) {
+        Util.checkType(value, List.class);
+
+        for (Object innerObject : ((List<?>) value)) {
+            innnerStrategy.writeElement(parentElement, namespace, innerObject);
+        }
+
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/AttributeWritingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/AttributeWritingStrategy.java
new file mode 100644 (file)
index 0000000..d0c2b88
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.toxml;
+
+import org.w3c.dom.Element;
+
+public interface AttributeWritingStrategy {
+
+    void writeElement(Element parentElement, String namespace, Object value);
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/CompositeAttributeWritingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/CompositeAttributeWritingStrategy.java
new file mode 100644 (file)
index 0000000..d1326bd
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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.confignetconfconnector.mapping.attributes.toxml;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class CompositeAttributeWritingStrategy implements AttributeWritingStrategy {
+
+    protected final String key;
+    protected final Document document;
+    protected final Map<String, AttributeWritingStrategy> innerStrats;
+
+    public CompositeAttributeWritingStrategy(Document document, String key,
+            Map<String, AttributeWritingStrategy> innerStrats) {
+        this.document = document;
+        this.key = key;
+        this.innerStrats = innerStrats;
+    }
+
+    @Override
+    public void writeElement(Element parentElement, String namespace, Object value) {
+        Util.checkType(value, Map.class);
+
+        Element innerNode = document.createElement(key);
+        XmlUtil.addNamespaceAttr(innerNode, namespace);
+
+        Map<?, ?> map = (Map<?, ?>) value;
+
+        for (Entry<?, ?> innerObjectEntry : map.entrySet()) {
+            Util.checkType(innerObjectEntry.getKey(), String.class);
+
+            String innerKey = (String) innerObjectEntry.getKey();
+            Object innerValue = innerObjectEntry.getValue();
+
+            innerStrats.get(innerKey).writeElement(innerNode, namespace, innerValue);
+        }
+        parentElement.appendChild(innerNode);
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectNameAttributeWritingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectNameAttributeWritingStrategy.java
new file mode 100644 (file)
index 0000000..b88b672
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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.confignetconfconnector.mapping.attributes.toxml;
+
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.ObjectNameAttributeMappingStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class ObjectNameAttributeWritingStrategy implements AttributeWritingStrategy {
+
+    private final Document document;
+    private final String key;
+
+    /**
+     * @param document
+     * @param key
+     */
+    public ObjectNameAttributeWritingStrategy(Document document, String key) {
+        this.document = document;
+        this.key = key;
+    }
+
+    @Override
+    public void writeElement(Element parentElement, String namespace, Object value) {
+        Util.checkType(value, ObjectNameAttributeMappingStrategy.MappedDependency.class);
+        Element innerNode = document.createElement(key);
+        XmlUtil.addNamespaceAttr(innerNode, namespace);
+
+        String moduleName = ((ObjectNameAttributeMappingStrategy.MappedDependency) value).getServiceName();
+        String refName = ((ObjectNameAttributeMappingStrategy.MappedDependency) value).getRefName();
+
+        final Element typeElement = XmlUtil.createTextElement(document, XmlNetconfConstants.TYPE_KEY, moduleName);
+        innerNode.appendChild(typeElement);
+
+        final Element nameElement = XmlUtil.createTextElement(document, XmlNetconfConstants.NAME_KEY, refName);
+        innerNode.appendChild(nameElement);
+
+        parentElement.appendChild(innerNode);
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectXmlWriter.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectXmlWriter.java
new file mode 100644 (file)
index 0000000..b49d20e
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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.confignetconfconnector.mapping.attributes.toxml;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.*;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement;
+import org.w3c.dom.Document;
+
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.SimpleType;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class ObjectXmlWriter extends AttributeIfcSwitchStatement<AttributeWritingStrategy> {
+
+    private Document document;
+    private String key;
+
+    public Map<String, AttributeWritingStrategy> prepareWriting(Map<String, AttributeIfc> yangToAttrConfig,
+            Document document) {
+
+        Map<String, AttributeWritingStrategy> preparedWriting = Maps.newHashMap();
+
+        for (Entry<String, AttributeIfc> mappedAttributeEntry : yangToAttrConfig.entrySet()) {
+            String key = mappedAttributeEntry.getKey();
+            AttributeIfc value = mappedAttributeEntry.getValue();
+            AttributeWritingStrategy strat = prepareWritingStrategy(key, value, document);
+            preparedWriting.put(key, strat);
+        }
+
+        return preparedWriting;
+    }
+
+    private AttributeWritingStrategy prepareWritingStrategy(String key, AttributeIfc expectedAttr, Document document) {
+        Preconditions.checkNotNull(expectedAttr, "Mbean attributes mismatch, unable to find expected attribute for %s",
+                key);
+        this.document = document;
+        this.key = key;
+        return switchAttribute(expectedAttr);
+    }
+
+    @Override
+    protected AttributeWritingStrategy caseJavaAttribute(JavaAttribute attributeIfc) {
+
+        if (attributeIfc.getOpenType() instanceof SimpleType<?>)
+            return new SimpleAttributeWritingStrategy(document, key);
+        else if (attributeIfc.getOpenType() instanceof ArrayType<?>) {
+            AttributeWritingStrategy innerStrategy = new SimpleAttributeWritingStrategy(document, key);
+            return new ArrayAttributeWritingStrategy(innerStrategy);
+        }
+        throw new IllegalStateException(JavaAttribute.class + " can only provide open type " + SimpleType.class
+                + " or " + ArrayType.class);
+    }
+
+    @Override
+    protected AttributeWritingStrategy caseDependencyAttribute(DependencyAttribute attributeIfc) {
+        return new ObjectNameAttributeWritingStrategy(document, key);
+    }
+
+    @Override
+    protected AttributeWritingStrategy caseTOAttribute(TOAttribute attributeIfc) {
+        Map<String, AttributeWritingStrategy> innerStrats = Maps.newHashMap();
+        String currentKey = key;
+        for (Entry<String, AttributeIfc> innerAttrEntry : attributeIfc.getYangPropertiesToTypesMap().entrySet()) {
+
+            AttributeWritingStrategy innerStrategy = prepareWritingStrategy(innerAttrEntry.getKey(),
+                    innerAttrEntry.getValue(), document);
+            innerStrats.put(innerAttrEntry.getKey(), innerStrategy);
+        }
+
+        return new CompositeAttributeWritingStrategy(document, currentKey, innerStrats);
+    }
+
+    @Override
+    protected AttributeWritingStrategy caseListAttribute(ListAttribute attributeIfc) {
+        AttributeIfc inner = attributeIfc.getInnerAttribute();
+        AttributeWritingStrategy innerStrategy = prepareWritingStrategy(key, inner, document);
+        return new ArrayAttributeWritingStrategy(innerStrategy);
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/RuntimeBeanEntryWritingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/RuntimeBeanEntryWritingStrategy.java
new file mode 100644 (file)
index 0000000..a67b348
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.toxml;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class RuntimeBeanEntryWritingStrategy extends CompositeAttributeWritingStrategy {
+
+    public RuntimeBeanEntryWritingStrategy(Document document, String key,
+            Map<String, AttributeWritingStrategy> innerStrats) {
+        super(document, key, innerStrats);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.opendaylight.controller.config.netconf.mapping.attributes.toxml.
+     * AttributeWritingStrategy#writeElement(org.w3c.dom.Element,
+     * java.lang.Object)
+     */
+    @Override
+    public void writeElement(Element parentElement, String namespace, Object value) {
+        Util.checkType(value, Map.class);
+
+        Element innerNode = document.createElement(key);
+
+        Map<?, ?> map = (Map<?, ?>) value;
+
+        for (Entry<?, ?> runtimeBeanInstanceMappingEntry : map.entrySet()) {
+
+            // wrap runtime attributes with number assigned to current runtime
+            // bean
+            Util.checkType(runtimeBeanInstanceMappingEntry.getValue(), Map.class);
+            Map<?, ?> innerMap = (Map<?, ?>) runtimeBeanInstanceMappingEntry.getValue();
+            Element runtimeInstanceNode = document.createElement("_"
+                    + (String) runtimeBeanInstanceMappingEntry.getKey());
+            innerNode.appendChild(runtimeInstanceNode);
+
+            for (Entry<?, ?> innerObjectEntry : innerMap.entrySet()) {
+
+                Util.checkType(innerObjectEntry.getKey(), String.class);
+
+                String innerKey = (String) innerObjectEntry.getKey();
+                Object innerValue = innerObjectEntry.getValue();
+
+                innerStrats.get(innerKey).writeElement(runtimeInstanceNode, namespace, innerValue);
+            }
+        }
+        parentElement.appendChild(innerNode);
+
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/SimpleAttributeWritingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/SimpleAttributeWritingStrategy.java
new file mode 100644 (file)
index 0000000..514183b
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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.confignetconfconnector.mapping.attributes.toxml;
+
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class SimpleAttributeWritingStrategy implements AttributeWritingStrategy {
+
+    private final Document document;
+    private final String key;
+
+    /**
+     * @param document
+     * @param key
+     */
+    public SimpleAttributeWritingStrategy(Document document, String key) {
+        this.document = document;
+        this.key = key;
+    }
+
+    @Override
+    public void writeElement(Element parentElement, String namespace, Object value) {
+        Util.checkType(value, String.class);
+        Element innerNode = XmlUtil.createTextElement(document, key, (String) value);
+        XmlUtil.addNamespaceAttr(innerNode, namespace);
+        parentElement.appendChild(innerNode);
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/Config.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/Config.java
new file mode 100644 (file)
index 0000000..b17a8a8
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * 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.confignetconfconnector.mapping.config;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+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.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.management.ObjectName;
+import java.util.*;
+import java.util.Map.Entry;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.lang.String.format;
+
+public class Config {
+
+    private final Map<String, Map<String, ModuleConfig>> moduleConfigs;
+
+    public Config(Map<String, Map<String, ModuleConfig>> moduleConfigs) {
+        this.moduleConfigs = moduleConfigs;
+    }
+
+    private Map<String, Map<String, Collection<ObjectName>>> getMappedInstances(Set<ObjectName> instancesToMap,
+            Services serviceTracker) {
+        Multimap<String, ObjectName> moduleToInstances = mapInstancesToModules(instancesToMap);
+
+        Map<String, Map<String, Collection<ObjectName>>> retVal = Maps.newLinkedHashMap();
+
+        for (String namespace : moduleConfigs.keySet()) {
+
+            Map<String, Collection<ObjectName>> innerRetVal = Maps.newHashMap();
+
+            for (Entry<String, ModuleConfig> mbeEntry : moduleConfigs.get(namespace).entrySet()) {
+
+                String moduleName = mbeEntry.getKey();
+                Collection<ObjectName> instances = moduleToInstances.get(moduleName);
+
+                if (instances == null)
+                    continue;
+
+                innerRetVal.put(moduleName, instances);
+
+                // All found instances add to service tracker in advance
+                // This way all instances will be serialized as all available
+                // services when get-config is triggered
+                // (even if they are not used as services by other onstances)
+                // = more user friendly
+                addServices(serviceTracker, instances, mbeEntry.getValue().getProvidedServices());
+
+            }
+
+            retVal.put(namespace, innerRetVal);
+        }
+        return retVal;
+    }
+
+    private void addServices(Services serviceTracker, Collection<ObjectName> instances,
+            Collection<String> providedServices) {
+        for (ObjectName instanceOn : instances) {
+            for (String serviceName : providedServices) {
+                serviceTracker.addServiceEntry(serviceName, instanceOn);
+            }
+        }
+    }
+
+    private static Multimap<String, ObjectName> mapInstancesToModules(Set<ObjectName> instancesToMap) {
+        Multimap<String, ObjectName> retVal = HashMultimap.create();
+
+        for (ObjectName objectName : instancesToMap) {
+            String factoryName = ObjectNameUtil.getFactoryName(objectName);
+            retVal.put(factoryName, objectName);
+        }
+        return retVal;
+    }
+
+    // public Element toXml(Set<ObjectName> instancesToMap, String namespace,
+    // Document document) {
+    // return toXml(instancesToMap, Optional.of(namespace), document);
+    // }
+
+    public Element toXml(Set<ObjectName> instancesToMap, Optional<String> maybeNamespace, Document document,
+            Element dataElement) {
+        Services serviceTracker = new Services();
+
+        Map<String, Map<String, Collection<ObjectName>>> moduleToInstances = getMappedInstances(instancesToMap,
+                serviceTracker);
+
+        Element root = dataElement;
+        if (maybeNamespace.isPresent()) {
+            XmlUtil.addNamespaceAttr(root, maybeNamespace.get());
+        }
+
+        Element modulesElement = document.createElement(XmlNetconfConstants.MODULES_KEY);
+        XmlUtil.addNamespaceAttr(modulesElement,
+                XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+        root.appendChild(modulesElement);
+        for (String moduleNamespace : moduleToInstances.keySet()) {
+            for (Entry<String, Collection<ObjectName>> moduleMappingEntry : moduleToInstances.get(moduleNamespace)
+                    .entrySet()) {
+
+                ModuleConfig mapping = moduleConfigs.get(moduleNamespace).get(moduleMappingEntry.getKey());
+
+                if (moduleMappingEntry.getValue().isEmpty()) {
+                    addEmptyModulesCommented(document, modulesElement, moduleNamespace, moduleMappingEntry);
+                } else {
+                    for (ObjectName objectName : moduleMappingEntry.getValue()) {
+                        modulesElement
+                                .appendChild(mapping.toXml(objectName, serviceTracker, document, moduleNamespace));
+                    }
+                }
+
+            }
+        }
+
+        root.appendChild(serviceTracker.toXml(serviceTracker.getMappedServices(), document));
+
+        return root;
+    }
+
+    private void addEmptyModulesCommented(Document document, Element root, String moduleNamespace,
+            Entry<String, Collection<ObjectName>> moduleMappingEntry) {
+        Element emptyModule = document.createElement(XmlNetconfConstants.MODULE_KEY);
+
+        Element typeElement = XmlUtil.createTextElement(document, XmlNetconfConstants.TYPE_KEY,
+                moduleMappingEntry.getKey());
+        emptyModule.appendChild(typeElement);
+
+        root.appendChild(document.createComment(XmlUtil.toString(emptyModule, false)));
+    }
+
+    // TODO refactor, replace string representing namespace with namespace class
+    // TODO refactor, replace Map->Multimap with e.g. ConfigElementResolved
+    // class
+    public Map<String, Multimap<String, ModuleElementResolved>> fromXml(XmlElement xml) {
+        Map<String, Multimap<String, ModuleElementResolved>> retVal = Maps.newHashMap();
+
+        List<XmlElement> recognisedChildren = Lists.newArrayList();
+
+        Services serviceTracker = fromXmlServices(xml, recognisedChildren);
+        List<XmlElement> moduleElements = fromXmlModules(xml, recognisedChildren);
+
+        xml.checkUnrecognisedElements(recognisedChildren);
+
+        for (XmlElement moduleElement : moduleElements) {
+            resolveModule(retVal, serviceTracker, moduleElement);
+        }
+
+        return retVal;
+    }
+
+    private List<XmlElement> fromXmlModules(XmlElement xml, List<XmlElement> recognisedChildren) {
+        Optional<XmlElement> modulesElement = xml.getOnlyChildElementOptionally(XmlNetconfConstants.MODULES_KEY,
+                XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+        List<XmlElement> moduleElements;
+        if (modulesElement.isPresent()) {
+            moduleElements = modulesElement.get().getChildElementsWithSameNamespace(XmlNetconfConstants.MODULE_KEY);
+            recognisedChildren.add(modulesElement.get());
+            modulesElement.get().checkUnrecognisedElements(moduleElements);
+        } else {
+            moduleElements = Lists.newArrayList();
+        }
+        return moduleElements;
+    }
+
+    private void resolveModule(Map<String, Multimap<String, ModuleElementResolved>> retVal, Services serviceTracker,
+            XmlElement moduleElement) {
+        XmlElement typeElement = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.TYPE_KEY);
+        Entry<String, String> prefixToNamespace = typeElement.findNamespaceOfTextContent();
+        String moduleNamespace = prefixToNamespace.getValue();
+        XmlElement nameElement = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.NAME_KEY);
+        String instanceName = nameElement.getTextContent();
+        String factoryNameWithPrefix = typeElement.getTextContent();
+        String prefixOrEmptyString = prefixToNamespace.getKey();
+        String factoryName = getFactoryName(factoryNameWithPrefix, prefixOrEmptyString);
+
+        ModuleConfig moduleMapping = getModuleMapping(moduleNamespace, instanceName, factoryName);
+
+        Multimap<String, ModuleElementResolved> innerMap = retVal.get(moduleNamespace);
+        if (innerMap == null) {
+            innerMap = HashMultimap.create();
+            retVal.put(moduleNamespace, innerMap);
+        }
+
+        ModuleElementResolved moduleElementResolved = moduleMapping.fromXml(moduleElement, serviceTracker,
+                instanceName, moduleNamespace);
+
+        innerMap.put(factoryName, moduleElementResolved);
+    }
+
+    private Services fromXmlServices(XmlElement xml, List<XmlElement> recognisedChildren) {
+        Optional<XmlElement> servicesElement = xml.getOnlyChildElementOptionally(XmlNetconfConstants.SERVICES_KEY,
+                XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+
+        Map<String, Map<String, String>> mappedServices;
+        if (servicesElement.isPresent()) {
+            mappedServices = Services.fromXml(servicesElement.get());
+            recognisedChildren.add(servicesElement.get());
+        } else {
+            mappedServices = new HashMap<>();
+        }
+
+        return Services.resolveServices(mappedServices);
+    }
+
+    private String getFactoryName(String factoryNameWithPrefix, String prefixOrEmptyString) {
+        checkState(
+                factoryNameWithPrefix.startsWith(prefixOrEmptyString),
+                format("Internal error: text " + "content '%s' of type node does not start with prefix '%s'",
+                        factoryNameWithPrefix, prefixOrEmptyString));
+
+        int factoryNameAfterPrefixIndex;
+        if (prefixOrEmptyString.isEmpty()) {
+            factoryNameAfterPrefixIndex = 0;
+        } else {
+            factoryNameAfterPrefixIndex = prefixOrEmptyString.length() + 1;
+        }
+        return factoryNameWithPrefix.substring(factoryNameAfterPrefixIndex);
+    }
+
+    private ModuleConfig getModuleMapping(String moduleNamespace, String instanceName, String factoryName) {
+        Map<String, ModuleConfig> mappingsFromNamespace = moduleConfigs.get(moduleNamespace);
+
+        Preconditions.checkNotNull(mappingsFromNamespace,
+                "Namespace %s, defined in: module %s of type %s not found, available namespaces: %s", moduleNamespace,
+                instanceName, factoryName, moduleConfigs.keySet());
+
+        ModuleConfig moduleMapping = mappingsFromNamespace.get(factoryName);
+        checkState(moduleMapping != null, "Cannot find mapping for module type " + factoryName);
+        return moduleMapping;
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfig.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfig.java
new file mode 100644 (file)
index 0000000..d1ba0b0
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * 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.confignetconfconnector.mapping.config;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeReadingStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.ObjectXmlReader;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.AttributeMappingStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.ObjectMapper;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving.AttributeResolvingStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving.ObjectResolver;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.toxml.AttributeWritingStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.toxml.ObjectXmlWriter;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.management.ObjectName;
+import javax.management.openmbean.OpenType;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public final class InstanceConfig {
+    private static final Logger logger = LoggerFactory.getLogger(InstanceConfig.class);
+
+    private final Map<String, AttributeIfc> yangToAttrConfig;
+    private final Map<String, AttributeIfc> jmxToAttrConfig;
+    private final ConfigRegistryClient configRegistryClient;
+
+    public InstanceConfig(ConfigRegistryClient configRegistryClient, Map<String, AttributeIfc> yangNamesToAttributes) {
+        this.yangToAttrConfig = yangNamesToAttributes;
+        this.jmxToAttrConfig = reverseMap(yangNamesToAttributes);
+        this.configRegistryClient = configRegistryClient;
+    }
+
+    private Map<String, Object> getMappedConfiguration(ObjectName on, Services depTracker) {
+
+        // TODO make field, mappingStrategies can be instantiated only once
+        Map<String, AttributeMappingStrategy<?, ? extends OpenType<?>>> mappingStrategies = new ObjectMapper(depTracker)
+                .prepareMapping(jmxToAttrConfig);
+
+        Map<String, Object> toXml = Maps.newHashMap();
+
+        for (Entry<String, AttributeIfc> configDefEntry : jmxToAttrConfig.entrySet()) {
+
+            // Skip children runtime beans as they are mapped by InstanceRuntime
+            if (configDefEntry.getValue() instanceof RuntimeBeanEntry)
+                continue;
+
+            Object value = configRegistryClient.getAttributeCurrentValue(on, configDefEntry.getKey());
+            try {
+                AttributeMappingStrategy<?, ? extends OpenType<?>> attributeMappingStrategy = mappingStrategies
+                        .get(configDefEntry.getKey());
+                Optional<?> a = attributeMappingStrategy.mapAttribute(value);
+                if (a.isPresent() == false)
+                    continue;
+
+                toXml.put(configDefEntry.getValue().getAttributeYangName(), a.get());
+            } catch (Exception e) {
+                throw new IllegalStateException("Unable to map value " + value + " to attribute "
+                        + configDefEntry.getKey(), e);
+            }
+        }
+
+        return toXml;
+    }
+
+    public Element toXml(ObjectName on, Services depTracker, String namespace, Document document, Element rootElement) {
+
+        Element cfgElement = rootElement;
+
+        Map<String, AttributeWritingStrategy> strats = new ObjectXmlWriter().prepareWriting(yangToAttrConfig, document);
+
+        Map<String, Object> mappedConfig = getMappedConfiguration(on, depTracker);
+
+        for (Entry<String, ?> mappingEntry : mappedConfig.entrySet()) {
+            try {
+                strats.get(mappingEntry.getKey()).writeElement(cfgElement, namespace, mappingEntry.getValue());
+            } catch (Exception e) {
+                throw new IllegalStateException("Unable to write value " + mappingEntry.getValue() + " for attribute "
+                        + mappingEntry.getValue(), e);
+            }
+        }
+
+        return cfgElement;
+    }
+
+    private void resolveConfiguration(InstanceConfigElementResolved mappedConfig, Services depTracker) {
+
+        // TODO make field, resolvingStrategies can be instantiated only once
+        Map<String, AttributeResolvingStrategy<?, ? extends OpenType<?>>> resolvingStrategies = new ObjectResolver(
+                depTracker).prepareResolving(yangToAttrConfig);
+
+        for (Entry<String, AttributeConfigElement> configDefEntry : mappedConfig.getConfiguration().entrySet()) {
+            try {
+
+                AttributeResolvingStrategy<?, ? extends OpenType<?>> attributeResolvingStrategy = resolvingStrategies
+                        .get(configDefEntry.getKey());
+
+                configDefEntry.getValue().resolveValue(attributeResolvingStrategy, configDefEntry.getKey());
+                configDefEntry.getValue().setJmxName(
+                        yangToAttrConfig.get(configDefEntry.getKey()).getUpperCaseCammelCase());
+            } catch (Exception e) {
+                throw new IllegalStateException("Unable to resolve value " + configDefEntry.getValue()
+                        + " to attribute " + configDefEntry.getKey(), e);
+            }
+        }
+    }
+
+    public InstanceConfigElementResolved fromXml(XmlElement moduleElement, Services services, String moduleNamespace) {
+        Map<String, AttributeConfigElement> retVal = Maps.newHashMap();
+
+        Map<String, AttributeReadingStrategy> strats = new ObjectXmlReader().prepareReading(yangToAttrConfig);
+        List<XmlElement> recognisedChildren = Lists.newArrayList();
+
+        XmlElement type = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.TYPE_KEY);
+        XmlElement name = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.NAME_KEY);
+        List<XmlElement> typeAndName = Lists.newArrayList(type, name);
+
+        for (Entry<String, AttributeReadingStrategy> readStratEntry : strats.entrySet()) {
+            List<XmlElement> configNodes = getConfigNodes(moduleElement, moduleNamespace, readStratEntry.getKey(),
+                    recognisedChildren, typeAndName);
+            AttributeConfigElement readElement = readStratEntry.getValue().readElement(configNodes);
+            retVal.put(readStratEntry.getKey(), readElement);
+        }
+
+        recognisedChildren.addAll(typeAndName);
+        moduleElement.checkUnrecognisedElements(recognisedChildren);
+
+        // TODO: add check for conflicts between global and local edit strategy
+        String perInstanceEditStrategy = moduleElement.getAttribute(XmlNetconfConstants.OPERATION_ATTR_KEY,
+                XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+
+        InstanceConfigElementResolved instanceConfigElementResolved = perInstanceEditStrategy.equals("") ? new InstanceConfigElementResolved(
+                retVal) : new InstanceConfigElementResolved(perInstanceEditStrategy, retVal);
+
+        resolveConfiguration(instanceConfigElementResolved, services);
+        return instanceConfigElementResolved;
+    }
+
+    private List<XmlElement> getConfigNodes(XmlElement moduleElement, String moduleNamespace, String name,
+            List<XmlElement> recognisedChildren, List<XmlElement> typeAndName) {
+        List<XmlElement> foundConfigNodes = moduleElement.getChildElementsWithinNamespace(name, moduleNamespace);
+        if (foundConfigNodes.isEmpty()) {
+            logger.debug("No config nodes {}:{} found in {}", moduleNamespace, name, moduleElement);
+            logger.debug("Trying lookup of config nodes without specified namespace");
+            foundConfigNodes = moduleElement.getChildElementsWithinNamespace(name,
+                    XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+            // In case module type or name element is not present in config it
+            // would be matched with config type or name
+            // We need to remove config type and name from available module
+            // config elements
+            foundConfigNodes.removeAll(typeAndName);
+            logger.debug("Found {} config nodes {} without specified namespace in {}", foundConfigNodes.size(), name,
+                    moduleElement);
+        } else {
+            List<XmlElement> foundWithoutNamespaceNodes = moduleElement.getChildElementsWithinNamespace(name,
+                    XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+            foundWithoutNamespaceNodes.removeAll(typeAndName);
+            Preconditions.checkState(foundWithoutNamespaceNodes.isEmpty(),
+                    "Element %s present multiple times with different namespaces: %s, %s", name, foundConfigNodes,
+                    foundWithoutNamespaceNodes);
+        }
+
+        recognisedChildren.addAll(foundConfigNodes);
+        return foundConfigNodes;
+    }
+
+    private static Map<String, AttributeIfc> reverseMap(Map<String, AttributeIfc> yangNameToAttr) {
+        Map<String, AttributeIfc> reversednameToAtr = Maps.newHashMap();
+
+        for (Entry<String, AttributeIfc> entry : yangNameToAttr.entrySet()) {
+            reversednameToAtr.put(entry.getValue().getUpperCaseCammelCase(), entry.getValue());
+        }
+
+        return reversednameToAtr;
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfigElementResolved.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfigElementResolved.java
new file mode 100644 (file)
index 0000000..6624fc1
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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.confignetconfconnector.mapping.config;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfigStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfigXmlParser;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditStrategyType;
+
+import java.util.Map;
+
+/**
+ * Parsed xml element containing whole configuration for an instance of some
+ * module. Contains preferred edit strategy type.
+ */
+public class InstanceConfigElementResolved {
+
+    private final EditStrategyType editStrategy;
+    private final Map<String, AttributeConfigElement> configuration;
+
+    public InstanceConfigElementResolved(String strat, Map<String, AttributeConfigElement> configuration) {
+        EditStrategyType valueOf = checkStrategy(strat);
+        this.editStrategy = valueOf;
+        this.configuration = configuration;
+    }
+
+    EditStrategyType checkStrategy(String strat) {
+        EditStrategyType valueOf = EditStrategyType.valueOf(strat);
+        if (EditStrategyType.defaultStrategy().isEnforcing()) {
+            Preconditions
+                    .checkArgument(
+                            valueOf == EditStrategyType.defaultStrategy(),
+                            "With "
+                                    + EditStrategyType.defaultStrategy()
+                                    + " as "
+                                    + EditConfigXmlParser.DEFAULT_OPERATION_KEY
+                                    + " operations on module elements are not permitted since the default option is restrictive");
+        }
+        return valueOf;
+    }
+
+    public InstanceConfigElementResolved(Map<String, AttributeConfigElement> configuration) {
+        editStrategy = EditStrategyType.defaultStrategy();
+        this.configuration = configuration;
+    }
+
+    public EditConfigStrategy getEditStrategy() {
+        return editStrategy.getFittingStrategy();
+    }
+
+    public Map<String, AttributeConfigElement> getConfiguration() {
+        return configuration;
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/ModuleConfig.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/ModuleConfig.java
new file mode 100644 (file)
index 0000000..3a0c547
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * 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.confignetconfconnector.mapping.config;
+
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+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.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.management.ObjectName;
+import java.util.Collection;
+import java.util.Collections;
+
+public class ModuleConfig {
+
+    private final String moduleName;
+    private final InstanceConfig instanceConfig;
+    private final Collection<String> providedServices;
+
+    public ModuleConfig(String moduleName, InstanceConfig mbeanMapping, Collection<String> providedServices) {
+        this.moduleName = moduleName;
+        this.instanceConfig = mbeanMapping;
+        this.providedServices = providedServices;
+    }
+
+    public ModuleConfig(String key, InstanceConfig instanceConfig) {
+        this(key, instanceConfig, Collections.<String> emptyList());
+    }
+
+    public InstanceConfig getMbeanMapping() {
+        return instanceConfig;
+    }
+
+    public Collection<String> getProvidedServices() {
+        return providedServices;
+    }
+
+    public Element toXml(ObjectName instanceON, Services depTracker, Document document, String namespace) {
+        Element root = document.createElement(XmlNetconfConstants.MODULE_KEY);
+        // Xml.addNamespaceAttr(document, root, namespace);
+
+        final String prefix = getPrefix(namespace);
+        Element typeElement = XmlUtil.createPrefixedTextElement(document, XmlNetconfConstants.TYPE_KEY, prefix,
+                moduleName);
+        XmlUtil.addPrefixedNamespaceAttr(typeElement, prefix, namespace);
+        // Xml.addNamespaceAttr(document, typeElement,
+        // XMLUtil.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+        root.appendChild(typeElement);
+
+        Element nameElement = XmlUtil.createTextElement(document, XmlNetconfConstants.NAME_KEY,
+                ObjectNameUtil.getInstanceName(instanceON));
+        // Xml.addNamespaceAttr(document, nameElement,
+        // XMLUtil.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+        root.appendChild(nameElement);
+
+        root = instanceConfig.toXml(instanceON, depTracker, namespace, document, root);
+
+        return root;
+    }
+
+    private String getPrefix(String namespace) {
+        // if(namespace.contains(":")==false)
+        return "prefix";
+        // return namespace.substring(namespace.lastIndexOf(':') + 1,
+        // namespace.length());
+
+    }
+
+    public ModuleElementResolved fromXml(XmlElement moduleElement, Services depTracker, String instanceName,
+            String moduleNamespace) {
+
+        InstanceConfigElementResolved ice = instanceConfig.fromXml(moduleElement, depTracker, moduleNamespace);
+        return new ModuleElementResolved(instanceName, ice);
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/ModuleElementResolved.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/ModuleElementResolved.java
new file mode 100644 (file)
index 0000000..6d2936c
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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.confignetconfconnector.mapping.config;
+
+public class ModuleElementResolved {
+
+    private final String instanceName;
+    private final InstanceConfigElementResolved instanceConfigElementResolved;
+
+    public ModuleElementResolved(String instanceName, InstanceConfigElementResolved instanceConfigElementResolved) {
+        this.instanceName = instanceName;
+        this.instanceConfigElementResolved = instanceConfigElementResolved;
+    }
+
+    public String getInstanceName() {
+        return instanceName;
+    }
+
+    public InstanceConfigElementResolved getInstanceConfigElementResolved() {
+        return instanceConfigElementResolved;
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/Services.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/Services.java
new file mode 100644 (file)
index 0000000..7e4b6c2
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * 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.confignetconfconnector.mapping.config;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+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.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.annotation.Nullable;
+import javax.management.ObjectName;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class Services {
+
+    private static final String PROVIDER_KEY = "provider";
+    private static final String NAME_KEY = "name";
+    public static final String TYPE_KEY = "type";
+    public static final String SERVICE_KEY = "service";
+
+    private long suffix = 1;
+
+    private final Map<ServiceInstance, String> instanceToRef = Maps.newHashMap();
+    private final Map<String/* ServiceName */, Map<String/* refName */, ServiceInstance>> serviceNameToRefNameToInstance = Maps
+            .newHashMap();
+
+    public String addServiceEntry(String serviceName, ObjectName on) {
+
+        String moduleName = on.getKeyProperty("moduleFactoryName");
+        String instanceName = on.getKeyProperty("instanceName");
+
+        return addServiceEntry(serviceName, moduleName, instanceName);
+    }
+
+    public String addServiceEntry(String serviceName, String moduleName, String instanceName) {
+        ServiceInstance serviceInstance = new ServiceInstance(moduleName, instanceName);
+        serviceInstance.setServiceName(serviceName);
+
+        String refName = instanceToRef.get(serviceInstance);
+
+        Map<String, ServiceInstance> refNameToInstance = serviceNameToRefNameToInstance.get(serviceName);
+        if (refNameToInstance == null) {
+            refNameToInstance = Maps.newHashMap();
+            serviceNameToRefNameToInstance.put(serviceName, refNameToInstance);
+        }
+
+        if (refName != null) {
+            if (serviceNameToRefNameToInstance.get(serviceName).containsKey(moduleName) == false) {
+                refNameToInstance.put(refName, serviceInstance);
+            }
+            return refName;
+        } else {
+            refName = "ref_" + instanceName;
+
+            final Set<String> refNamesAsSet = toSet(instanceToRef.values());
+            if (refNamesAsSet.contains(refName)) {
+                refName = findAvailableRefName(refName, refNamesAsSet);
+            }
+
+            instanceToRef.put(serviceInstance, refName);
+            refNameToInstance.put(refName, serviceInstance);
+
+            return refName;
+        }
+    }
+
+    private Set<String> toSet(Collection<String> values) {
+        Set<String> refNamesAsSet = Sets.newHashSet();
+
+        for (String refName : values) {
+            boolean resultAdd = refNamesAsSet.add(refName);
+            Preconditions.checkState(resultAdd,
+                    "Error occurred building services element, reference name {} was present twice", refName);
+        }
+
+        return refNamesAsSet;
+    }
+
+    public ServiceInstance getByServiceAndRefName(String serviceName, String refName) {
+        Map<String, ServiceInstance> refNameToInstance = serviceNameToRefNameToInstance.get(serviceName);
+        Preconditions.checkArgument(refNameToInstance != null, "No serviceInstances mapped to " + serviceName + " , "
+                + serviceNameToRefNameToInstance.keySet());
+
+        ServiceInstance serviceInstance = refNameToInstance.get(refName);
+        Preconditions.checkArgument(serviceInstance != null, "No serviceInstance mapped to " + refName
+                + " under service name " + serviceName + " , " + refNameToInstance.keySet());
+        return serviceInstance;
+    }
+
+    // TODO hide getMappedServices, call it explicitly in toXml
+
+    public Map<String, Map<String, String>> getMappedServices() {
+        Map<String, Map<String, String>> retVal = Maps.newHashMap();
+
+        for (String serviceName : serviceNameToRefNameToInstance.keySet()) {
+
+            Map<String, String> innerRetVal = Maps.transformValues(serviceNameToRefNameToInstance.get(serviceName),
+                    new Function<ServiceInstance, String>() {
+                        @Nullable
+                        @Override
+                        public String apply(@Nullable ServiceInstance serviceInstance) {
+                            return serviceInstance.toString();
+                        }
+                    });
+            retVal.put(serviceName, innerRetVal);
+        }
+
+        return retVal;
+    }
+
+    // TODO hide resolveServices, call it explicitly in fromXml
+
+    public static Services resolveServices(Map<String, Map<String, String>> mappedServices) {
+        Services tracker = new Services();
+
+        for (Entry<String, Map<String, String>> serviceEntry : mappedServices.entrySet()) {
+
+            String serviceName = serviceEntry.getKey();
+            for (Entry<String, String> refEntry : serviceEntry.getValue().entrySet()) {
+
+                Map<String, ServiceInstance> refNameToInstance = tracker.serviceNameToRefNameToInstance
+                        .get(serviceName);
+                if (refNameToInstance == null) {
+                    refNameToInstance = Maps.newHashMap();
+                    tracker.serviceNameToRefNameToInstance.put(serviceName, refNameToInstance);
+                }
+
+                String refName = refEntry.getKey();
+                Preconditions.checkState(false == refNameToInstance.containsKey(refName),
+                        "Duplicate reference name to service " + refName + " under service " + serviceName);
+                ServiceInstance serviceInstance = ServiceInstance.fromString(refEntry.getValue());
+                refNameToInstance.put(refName, serviceInstance);
+
+                tracker.instanceToRef.put(serviceInstance, refEntry.getKey());
+            }
+        }
+        return tracker;
+    }
+
+    public static Map<String, Map<String, String>> fromXml(XmlElement xml) {
+        Map<String, Map<String, String>> retVal = Maps.newHashMap();
+
+        List<XmlElement> services = xml.getChildElements(SERVICE_KEY);
+        xml.checkUnrecognisedElements(services);
+
+        for (XmlElement service : services) {
+
+            XmlElement typeElement = service.getOnlyChildElement(TYPE_KEY);
+            String serviceName = typeElement.getTextContent();
+
+            Map<String, String> innerMap = Maps.newHashMap();
+            retVal.put(serviceName, innerMap);
+
+            List<XmlElement> instances = service.getChildElements(XmlNetconfConstants.INSTANCE_KEY);
+            service.checkUnrecognisedElements(instances, typeElement);
+
+            for (XmlElement instance : instances) {
+                XmlElement nameElement = instance.getOnlyChildElement(NAME_KEY);
+                String refName = nameElement.getTextContent();
+
+                XmlElement providerElement = instance.getOnlyChildElement(PROVIDER_KEY);
+                String providerName = providerElement.getTextContent();
+
+                instance.checkUnrecognisedElements(nameElement, providerElement);
+
+                innerMap.put(refName, providerName);
+            }
+        }
+
+        return retVal;
+    }
+
+    private String findAvailableRefName(String refName, Set<String> refNamesAsSet) {
+        String intitialRefName = refName;
+
+        while (true) {
+            refName = intitialRefName + "_" + suffix++;
+            if (refNamesAsSet.contains(refName) == false)
+                return refName;
+        }
+    }
+
+    public Element toXml(Map<String, Map<String, String>> mappedServices, Document document) {
+        Element root = document.createElement(XmlNetconfConstants.SERVICES_KEY);
+        XmlUtil.addNamespaceAttr(root, XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+
+        for (Entry<String, Map<String, String>> serviceEntry : mappedServices.entrySet()) {
+            Element serviceElement = document.createElement(SERVICE_KEY);
+            root.appendChild(serviceElement);
+
+            Element typeElement = XmlUtil.createTextElement(document, TYPE_KEY, serviceEntry.getKey());
+            serviceElement.appendChild(typeElement);
+
+            for (Entry<String, String> instanceEntry : serviceEntry.getValue().entrySet()) {
+                Element instanceElement = document.createElement(XmlNetconfConstants.INSTANCE_KEY);
+                serviceElement.appendChild(instanceElement);
+
+                Element nameElement = XmlUtil.createTextElement(document, NAME_KEY, instanceEntry.getKey());
+                instanceElement.appendChild(nameElement);
+
+                Element providerElement = XmlUtil.createTextElement(document, PROVIDER_KEY, instanceEntry.getValue());
+                instanceElement.appendChild(providerElement);
+            }
+        }
+
+        return root;
+    }
+
+    public static final class ServiceInstance {
+        public ServiceInstance(String moduleName, String instanceName) {
+            this.moduleName = moduleName;
+            this.instanceName = instanceName;
+        }
+
+        public static ServiceInstance fromString(String instanceId) {
+            instanceId = instanceId.trim();
+            Matcher matcher = p.matcher(instanceId);
+            Preconditions.checkArgument(matcher.matches(), "Unexpected format for provider, expected " + p.toString()
+                    + " but was " + instanceId);
+            String factoryName = matcher.group(1);
+            String instanceName = matcher.group(2);
+            return new ServiceInstance(factoryName, instanceName);
+        }
+
+        private final String moduleName, instanceName;
+        private String serviceName;
+
+        public String getServiceName() {
+            return serviceName;
+        }
+
+        public void setServiceName(String serviceName) {
+            this.serviceName = serviceName;
+        }
+
+        public String getModuleName() {
+            return moduleName;
+        }
+
+        public String getInstanceName() {
+            return instanceName;
+        }
+
+        private static final String blueprint = "/" + XmlNetconfConstants.CONFIG_KEY + "/"
+                + XmlNetconfConstants.MODULES_KEY + "/" + XmlNetconfConstants.MODULE_KEY + "["
+                + XmlNetconfConstants.NAME_KEY + "='%s']/" + XmlNetconfConstants.INSTANCE_KEY + "["
+                + XmlNetconfConstants.NAME_KEY + "='%s']";
+
+        private static final String blueprintR = "/" + XmlNetconfConstants.CONFIG_KEY + "/"
+                + XmlNetconfConstants.MODULES_KEY + "/" + XmlNetconfConstants.MODULE_KEY + "\\["
+                + XmlNetconfConstants.NAME_KEY + "='%s'\\]/" + XmlNetconfConstants.INSTANCE_KEY + "\\["
+                + XmlNetconfConstants.NAME_KEY + "='%s'\\]";
+
+        private static final Pattern p = Pattern.compile(String.format(blueprintR, "(.+)", "(.+)"));
+
+        @Override
+        public String toString() {
+            return String.format(blueprint, moduleName, instanceName);
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((instanceName == null) ? 0 : instanceName.hashCode());
+            result = prime * result + ((moduleName == null) ? 0 : moduleName.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            ServiceInstance other = (ServiceInstance) obj;
+            if (instanceName == null) {
+                if (other.instanceName != null)
+                    return false;
+            } else if (!instanceName.equals(other.instanceName))
+                return false;
+            if (moduleName == null) {
+                if (other.moduleName != null)
+                    return false;
+            } else if (!moduleName.equals(other.moduleName))
+                return false;
+            return true;
+        }
+
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/InstanceRuntimeRpc.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/InstanceRuntimeRpc.java
new file mode 100644 (file)
index 0000000..e91357e
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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.confignetconfconnector.mapping.rpc;
+
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry.Rpc;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeReadingStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.ObjectXmlReader;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving.AttributeResolvingStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving.ObjectResolver;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+
+import javax.management.openmbean.OpenType;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public final class InstanceRuntimeRpc {
+
+    private final Map<String, AttributeIfc> yangToAttrConfig;
+    private final Rpc rpc;
+
+    public InstanceRuntimeRpc(Rpc rpc) {
+        this.yangToAttrConfig = map(rpc.getParameters());
+        this.rpc = rpc;
+    }
+
+    private Map<String, AttributeIfc> map(List<JavaAttribute> parameters) {
+        Map<String, AttributeIfc> mapped = Maps.newHashMap();
+        for (JavaAttribute javaAttribute : parameters) {
+            mapped.put(javaAttribute.getAttributeYangName(), javaAttribute);
+        }
+        return mapped;
+    }
+
+    private void resolveConfiguration(Map<String, AttributeConfigElement> mappedConfig) {
+
+        // TODO make field, resolvingStrategies can be instantiated only once
+        Map<String, AttributeResolvingStrategy<?, ? extends OpenType<?>>> resolvingStrategies = new ObjectResolver(null)
+                .prepareResolving(yangToAttrConfig);
+        // TODO make constructor for object resolver without service tracker
+        for (Entry<String, AttributeConfigElement> configDefEntry : mappedConfig.entrySet()) {
+            try {
+
+                AttributeResolvingStrategy<?, ? extends OpenType<?>> attributeResolvingStrategy = resolvingStrategies
+                        .get(configDefEntry.getKey());
+
+                configDefEntry.getValue().resolveValue(attributeResolvingStrategy, configDefEntry.getKey());
+                configDefEntry.getValue().setJmxName(
+                        yangToAttrConfig.get(configDefEntry.getKey()).getUpperCaseCammelCase());
+            } catch (Exception e) {
+                throw new IllegalStateException("Unable to resolve value " + configDefEntry.getValue()
+                        + " to attribute " + configDefEntry.getKey(), e);
+            }
+        }
+    }
+
+    public Map<String, AttributeConfigElement> fromXml(XmlElement configRootNode) {
+        Map<String, AttributeConfigElement> retVal = Maps.newHashMap();
+
+        Map<String, AttributeReadingStrategy> strats = new ObjectXmlReader().prepareReading(yangToAttrConfig);
+
+        for (Entry<String, AttributeReadingStrategy> readStratEntry : strats.entrySet()) {
+            List<XmlElement> configNodes = configRootNode.getChildElements(readStratEntry.getKey());
+            AttributeConfigElement readElement = readStratEntry.getValue().readElement(configNodes);
+            retVal.put(readStratEntry.getKey(), readElement);
+        }
+
+        resolveConfiguration(retVal);
+        return retVal;
+    }
+
+    public String getName() {
+        return rpc.getName();
+    }
+
+    public String getReturnType() {
+        return rpc.getReturnType();
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/ModuleRpcs.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/ModuleRpcs.java
new file mode 100644 (file)
index 0000000..a081890
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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.confignetconfconnector.mapping.rpc;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry.Rpc;
+
+import java.util.Map;
+
+public final class ModuleRpcs {
+
+    Map<String, String> yangToJavaNames = Maps.newHashMap();
+    Map<String, Map<String, InstanceRuntimeRpc>> rpcMapping = Maps.newHashMap();
+
+    public void addNameMapping(RuntimeBeanEntry runtimeEntry) {
+        String yangName = runtimeEntry.getYangName();
+        Preconditions.checkState(yangToJavaNames.containsKey(yangName) == false,
+                "RuntimeBean %s found twice in same namespace", yangName);
+        yangToJavaNames.put(yangName, runtimeEntry.getJavaNamePrefix());
+    }
+
+    public void addRpc(RuntimeBeanEntry runtimeEntry, Rpc rpc) {
+        String yangName = runtimeEntry.getYangName();
+        Map<String, InstanceRuntimeRpc> map = rpcMapping.get(yangName);
+        if (map == null) {
+            map = Maps.newHashMap();
+            rpcMapping.put(yangName, map);
+        }
+
+        Preconditions.checkState(map.containsKey(rpc.getYangName()) == false, "Rpc %s for runtime bean %s added twice",
+                rpc.getYangName(), yangName);
+        map.put(rpc.getYangName(), new InstanceRuntimeRpc(rpc));
+    }
+
+    public String getRbeJavaName(String yangName) {
+        String javaName = yangToJavaNames.get(yangName);
+        Preconditions.checkState(javaName != null,
+                "No runtime bean entry found under yang name %s, available yang names %s", yangName,
+                yangToJavaNames.keySet());
+        return javaName;
+    }
+
+    public InstanceRuntimeRpc getRpc(String rbeName, String rpcName) {
+        Map<String, InstanceRuntimeRpc> rpcs = rpcMapping.get(rbeName);
+        Preconditions.checkState(rpcs != null, "No rpcs found for runtime bean %s", rbeName);
+        InstanceRuntimeRpc rpc = rpcs.get(rpcName);
+        Preconditions.checkState(rpc != null, "No rpc found for runtime bean %s with name %s", rbeName, rpcName);
+        return rpc;
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/Rpcs.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/Rpcs.java
new file mode 100644 (file)
index 0000000..556541a
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.runtimerpc.RuntimeRpcElementResolved;
+
+import java.util.Map;
+
+public class Rpcs {
+    private final Map<String, Map<String, ModuleRpcs>> mappedRpcs;
+
+    public Rpcs(Map<String, Map<String, ModuleRpcs>> mappedRpcs) {
+        super();
+        this.mappedRpcs = mappedRpcs;
+    }
+
+    public ModuleRpcs getRpcMapping(RuntimeRpcElementResolved id) {
+        Map<String, ModuleRpcs> modules = mappedRpcs.get(id.getNamespace());
+        Preconditions.checkState(modules != null, "No modules found for namespace %s", id.getNamespace());
+        ModuleRpcs rpcMapping = modules.get(id.getModuleName());
+        Preconditions.checkState(modules != null, "No module %s found for namespace %s", id.getModuleName(),
+                id.getNamespace());
+
+        return rpcMapping;
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/InstanceRuntime.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/InstanceRuntime.java
new file mode 100644 (file)
index 0000000..9d348d0
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * 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.confignetconfconnector.mapping.runtime;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Sets;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.InstanceConfig;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.management.ObjectName;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+public class InstanceRuntime {
+
+    /**
+     *
+     */
+    private static final String KEY_ATTRIBUTE_KEY = "key";
+
+    private final InstanceConfig instanceMapping;
+    private final Map<String, InstanceRuntime> childrenMappings;
+    private final Map<String, String> jmxToYangChildRbeMapping;
+
+    public InstanceRuntime(InstanceConfig instanceMapping, Map<String, InstanceRuntime> childrenMappings,
+            Map<String, String> jmxToYangChildRbeMapping) {
+        this.instanceMapping = instanceMapping;
+        this.childrenMappings = childrenMappings;
+        this.jmxToYangChildRbeMapping = jmxToYangChildRbeMapping;
+    }
+
+    /**
+     * Finds all children runtime beans, same properties and values as current
+     * root + any number of additional properties
+     */
+    private Set<ObjectName> findChildren(ObjectName innerRootBean, Set<ObjectName> childRbeOns) {
+        final Hashtable<String, String> wantedProperties = innerRootBean.getKeyPropertyList();
+
+        return Sets.newHashSet(Collections2.filter(childRbeOns, new Predicate<ObjectName>() {
+
+            @Override
+            public boolean apply(ObjectName on) {
+                Hashtable<String, String> localProperties = on.getKeyPropertyList();
+                for (Entry<String, String> propertyEntry : wantedProperties.entrySet()) {
+                    if (!localProperties.containsKey(propertyEntry.getKey()))
+                        return false;
+                    if (!localProperties.get(propertyEntry.getKey()).equals(propertyEntry.getValue()))
+                        return false;
+                    if (localProperties.size() <= wantedProperties.size())
+                        return false;
+                }
+                return true;
+            }
+        }));
+    }
+
+    /**
+     * Finds next level root runtime beans, beans that have the same properties
+     * as current root + one additional
+     */
+    private Set<ObjectName> getRootBeans(Set<ObjectName> childRbeOns, final String string, final int keyListSize) {
+        return Sets.newHashSet(Collections2.filter(childRbeOns, new Predicate<ObjectName>() {
+
+            @Override
+            public boolean apply(ObjectName on) {
+                if (on.getKeyPropertyList().size() != keyListSize + 1)
+                    return false;
+                if (!on.getKeyPropertyList().containsKey(string))
+                    return false;
+                return true;
+            }
+        }));
+    }
+
+    public Element toXml(ObjectName rootOn, Set<ObjectName> childRbeOns, Document document) {
+        return toXml(rootOn, childRbeOns, document, null, null);
+    }
+
+    public Element toXml(ObjectName rootOn, Set<ObjectName> childRbeOns, Document document, String instanceIndex,
+            String keyName) {
+        Element xml = document.createElement(keyName == null ? XmlNetconfConstants.DATA_KEY : keyName);
+        // TODO namespace
+        xml = instanceMapping.toXml(rootOn, null, "namespace", document, xml);
+
+        if (instanceIndex != null) {
+            xml.setAttribute(KEY_ATTRIBUTE_KEY, instanceIndex);
+        }
+
+        for (Entry<String, InstanceRuntime> childMappingEntry : childrenMappings.entrySet()) {
+            Set<ObjectName> innerRootBeans = getRootBeans(childRbeOns, childMappingEntry.getKey(), rootOn
+                    .getKeyPropertyList().size());
+
+            for (ObjectName objectName : innerRootBeans) {
+                Set<ObjectName> innerChildRbeOns = findChildren(objectName, childRbeOns);
+                String runtimeInstanceIndex = objectName.getKeyProperty(childMappingEntry.getKey());
+
+                String elementName = jmxToYangChildRbeMapping.get(childMappingEntry.getKey());
+                xml.appendChild(childMappingEntry.getValue().toXml(objectName, innerChildRbeOns, document,
+                        runtimeInstanceIndex, elementName));
+            }
+        }
+
+        return xml;
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/ModuleRuntime.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/ModuleRuntime.java
new file mode 100644 (file)
index 0000000..07da65e
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.mapping.runtime;
+
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.management.ObjectName;
+import java.util.Collection;
+import java.util.Set;
+
+public class ModuleRuntime {
+
+    private final String moduleName;
+    private final InstanceRuntime instanceRuntime;
+
+    public ModuleRuntime(String moduleName, InstanceRuntime instanceRuntime) {
+        this.moduleName = moduleName;
+        this.instanceRuntime = instanceRuntime;
+    }
+
+    public InstanceRuntime getMbeanMapping() {
+        return instanceRuntime;
+    }
+
+    private ObjectName findRoot(Collection<ObjectName> runtimeBeanOns) {
+        for (ObjectName objectName : runtimeBeanOns) {
+            if (objectName.getKeyPropertyList().size() == 3)
+                return objectName;
+        }
+        throw new IllegalStateException("Root runtime bean not found among " + runtimeBeanOns);
+    }
+
+    public Element toXml(String namespace, Multimap<String, ObjectName> instances, Document document) {
+        Element root = document.createElement(XmlNetconfConstants.MODULE_KEY);
+        XmlUtil.addNamespaceAttr(root, namespace);
+
+        Element nameElement = XmlUtil.createTextElement(document, XmlNetconfConstants.NAME_KEY, moduleName);
+        root.appendChild(nameElement);
+
+        for (String instanceName : instances.keySet()) {
+            Element instance = document.createElement(XmlNetconfConstants.INSTANCE_KEY);
+
+            Element innerNameElement = XmlUtil.createTextElement(document, XmlNetconfConstants.NAME_KEY, instanceName);
+            instance.appendChild(innerNameElement);
+
+            Collection<ObjectName> runtimeBeanOns = instances.get(instanceName);
+            ObjectName rootName = findRoot(runtimeBeanOns);
+
+            Set<ObjectName> childrenRuntimeBeans = Sets.newHashSet(runtimeBeanOns);
+            childrenRuntimeBeans.remove(rootName);
+
+            instance.appendChild(instanceRuntime.toXml(rootName, childrenRuntimeBeans, document));
+
+            root.appendChild(instance);
+        }
+
+        return root;
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/Runtime.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/Runtime.java
new file mode 100644 (file)
index 0000000..da28126
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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.confignetconfconnector.mapping.runtime;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.management.ObjectName;
+import java.util.Map;
+import java.util.Set;
+
+public class Runtime {
+
+    private final Map<String, Map<String, ModuleRuntime>> moduleRuntimes;
+
+    public Runtime(Map<String, Map<String, ModuleRuntime>> moduleRuntimes) {
+        this.moduleRuntimes = moduleRuntimes;
+    }
+
+    private Map<String, Multimap<String, ObjectName>> mapInstancesToModules(Set<ObjectName> instancesToMap) {
+        Map<String, Multimap<String, ObjectName>> retVal = Maps.newHashMap();
+
+        for (ObjectName objectName : instancesToMap) {
+            String moduleName = ObjectNameUtil.getFactoryName(objectName);
+
+            Multimap<String, ObjectName> multimap = retVal.get(moduleName);
+            if (multimap == null) {
+                multimap = HashMultimap.create();
+                retVal.put(moduleName, multimap);
+            }
+
+            String instanceName = ObjectNameUtil.getInstanceName(objectName);
+
+            multimap.put(instanceName, objectName);
+        }
+
+        return retVal;
+    }
+
+    public Element toXml(Set<ObjectName> instancesToMap, Document document) {
+        Element root = document.createElement(XmlNetconfConstants.DATA_KEY);
+
+        Element modulesElement = document.createElement(XmlNetconfConstants.MODULES_KEY);
+        XmlUtil.addNamespaceAttr(modulesElement,
+                XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+        root.appendChild(modulesElement);
+
+        Map<String, Multimap<String, ObjectName>> moduleToInstances = mapInstancesToModules(instancesToMap);
+
+        for (String localNamespace : moduleRuntimes.keySet()) {
+            for (String moduleName : moduleRuntimes.get(localNamespace).keySet()) {
+                Multimap<String, ObjectName> instanceToRbe = moduleToInstances.get(moduleName);
+
+                if (instanceToRbe == null)
+                    continue;
+
+                ModuleRuntime moduleRuntime = moduleRuntimes.get(localNamespace).get(moduleName);
+                Element innerXml = moduleRuntime.toXml(localNamespace, instanceToRbe, document);
+                modulesElement.appendChild(innerXml);
+            }
+        }
+
+        return root;
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/AbstractConfigNetconfOperation.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/AbstractConfigNetconfOperation.java
new file mode 100644 (file)
index 0000000..6689759
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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.confignetconfconnector.operations;
+
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
+import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public abstract class AbstractConfigNetconfOperation extends AbstractNetconfOperation {
+
+    protected final ConfigRegistryClient configRegistryClient;
+
+    protected AbstractConfigNetconfOperation(ConfigRegistryClient configRegistryClient,
+            String netconfSessionIdForReporting) {
+        super(netconfSessionIdForReporting);
+        this.configRegistryClient = configRegistryClient;
+    }
+
+    @Override
+    protected HandlingPriority canHandle(String operationName, String operationNamespace) {
+        // TODO check namespace
+        return operationName.equals(getOperationName()) ? HandlingPriority.HANDLE_WITH_DEFAULT_PRIORITY
+                : HandlingPriority.CANNOT_HANDLE;
+    }
+
+    protected abstract String getOperationName();
+
+    @Override
+    protected Element handle(Document document, XmlElement operationElement, NetconfOperationRouter opRouter)
+            throws NetconfDocumentedException {
+        return handle(document, operationElement);
+    }
+
+    protected abstract Element handle(Document document, XmlElement operationElement) throws NetconfDocumentedException;
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Commit.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Commit.java
new file mode 100644 (file)
index 0000000..8820d58
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.operations;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class Commit extends AbstractConfigNetconfOperation {
+
+    private static final Logger logger = LoggerFactory.getLogger(Commit.class);
+
+    private final TransactionProvider transactionProvider;
+
+    public Commit(TransactionProvider transactionProvider, ConfigRegistryClient configRegistryClient,
+            String netconfSessionIdForReporting) {
+        super(configRegistryClient, netconfSessionIdForReporting);
+        this.transactionProvider = transactionProvider;
+    }
+
+    private static void checkXml(XmlElement xml) {
+        xml.checkName(XmlNetconfConstants.COMMIT);
+        xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+    }
+
+    @Override
+    protected String getOperationName() {
+        return XmlNetconfConstants.COMMIT;
+    }
+
+    @Override
+    protected Element handle(Document document, XmlElement xml) throws NetconfDocumentedException {
+        checkXml(xml);
+
+        CommitStatus status;
+        try {
+            status = this.transactionProvider.commitTransaction();
+        } catch (final IllegalStateException e) {
+            logger.warn("Commit failed: ", e);
+            final Map<String, String> errorInfo = new HashMap<>();
+            errorInfo.put(ErrorTag.operation_failed.name(),
+                    "Operation failed. Use 'get-config' or 'edit-config' before triggering 'commit' operation");
+            throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application, ErrorTag.operation_failed,
+                    ErrorSeverity.error, errorInfo);
+        } catch (final NetconfDocumentedException e) {
+            throw new NetconfDocumentedException(
+                    "Unable to retrieve config snapshot after commit for persister, details: " + e.getMessage(),
+                    ErrorType.application, ErrorTag.operation_failed, ErrorSeverity.error, e.getErrorInfo());
+        }
+        logger.info("Datastore {} committed successfully: {}", Datastore.candidate, status);
+
+        return document.createElement(XmlNetconfConstants.OK);
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Datastore.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Datastore.java
new file mode 100644 (file)
index 0000000..d736595
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.operations;
+
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig.CandidateDatastoreQueryStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig.DatastoreQueryStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig.RunningDatastoreQueryStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+
+public enum Datastore {
+
+    running, candidate;
+
+    /**
+     * @param source
+     * @param transactionProvider
+     * @return
+     */
+    public static DatastoreQueryStrategy getInstanceQueryStrategy(Datastore source,
+            TransactionProvider transactionProvider) {
+        switch (source) {
+        case running:
+            return new RunningDatastoreQueryStrategy();
+        case candidate:
+            return new CandidateDatastoreQueryStrategy(transactionProvider);
+        default:
+            throw new UnsupportedOperationException("Unimplemented datastore query strategy for " + source);
+        }
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/DiscardChanges.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/DiscardChanges.java
new file mode 100644 (file)
index 0000000..b8fa5dd
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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.confignetconfconnector.operations;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class DiscardChanges extends AbstractConfigNetconfOperation {
+
+    public static final String DISCARD = "discard-changes";
+
+    private static final Logger logger = LoggerFactory.getLogger(DiscardChanges.class);
+
+    private final TransactionProvider transactionProvider;
+
+    public DiscardChanges(final TransactionProvider transactionProvider, ConfigRegistryClient configRegistryClient,
+            String netconfSessionIdForReporting) {
+        super(configRegistryClient, netconfSessionIdForReporting);
+        this.transactionProvider = transactionProvider;
+    }
+
+    private static void fromXml(XmlElement xml) {
+        xml.checkName(DISCARD);
+        xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+    }
+
+    @Override
+    protected String getOperationName() {
+        return DISCARD;
+    }
+
+    @Override
+    protected Element handle(Document document, XmlElement xml) throws NetconfDocumentedException {
+        try {
+            fromXml(xml);
+        } catch (final IllegalArgumentException e) {
+            logger.warn("Rpc error: {}", ErrorTag.bad_attribute, e);
+            final Map<String, String> errorInfo = new HashMap<>();
+            errorInfo.put(ErrorTag.bad_attribute.name(), e.getMessage());
+            throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.rpc, ErrorTag.bad_attribute,
+                    ErrorSeverity.error, errorInfo);
+        }
+
+        try {
+            this.transactionProvider.abortTransaction();
+        } catch (final IllegalStateException e) {
+            logger.warn("Abort failed: ", e);
+            final Map<String, String> errorInfo = new HashMap<>();
+            errorInfo
+                    .put(ErrorTag.operation_failed.name(),
+                            "Operation failed. Use 'get-config' or 'edit-config' before triggering 'discard-changes' operation");
+            throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application, ErrorTag.operation_failed,
+                    ErrorSeverity.error, errorInfo);
+        }
+        logger.info("Changes discarded successfully from datastore {}", Datastore.candidate);
+
+        return document.createElement(XmlNetconfConstants.OK);
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Validate.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Validate.java
new file mode 100644 (file)
index 0000000..b8cae43
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * 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.confignetconfconnector.operations;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Validate extends AbstractConfigNetconfOperation {
+
+    public static final String VALIDATE = "validate";
+
+    private static final Logger logger = LoggerFactory.getLogger(Validate.class);
+
+    private final TransactionProvider transactionProvider;
+
+    public Validate(final TransactionProvider transactionProvider, ConfigRegistryClient configRegistryClient,
+            String netconfSessionIdForReporting) {
+        super(configRegistryClient, netconfSessionIdForReporting);
+        this.transactionProvider = transactionProvider;
+    }
+
+    private void checkXml(XmlElement xml) {
+        xml.checkName(VALIDATE);
+        xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+
+        XmlElement sourceElement = xml.getOnlyChildElement(XmlNetconfConstants.SOURCE_KEY,
+                XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+        XmlElement sourceChildNode = sourceElement.getOnlyChildElement();
+
+        sourceChildNode.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+        String datastoreValue = sourceChildNode.getName();
+        Datastore sourceDatastore = Datastore.valueOf(datastoreValue);
+
+        Preconditions.checkState(sourceDatastore == Datastore.candidate, "Only " + Datastore.candidate
+                + " is supported as source for " + VALIDATE + " but was " + datastoreValue);
+    }
+
+    @Override
+    protected String getOperationName() {
+        return VALIDATE;
+    }
+
+    @Override
+    protected Element handle(Document document, XmlElement xml) throws NetconfDocumentedException {
+        try {
+            checkXml(xml);
+        } catch (IllegalStateException e) {
+            logger.warn("Rpc error: {}", ErrorTag.missing_attribute, e);
+            final Map<String, String> errorInfo = new HashMap<>();
+            errorInfo.put(ErrorTag.missing_attribute.name(), "Missing value of datastore attribute");
+            throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.rpc, ErrorTag.missing_attribute,
+                    ErrorSeverity.error, errorInfo);
+        } catch (final IllegalArgumentException e) {
+            logger.warn("Rpc error: {}", ErrorTag.bad_attribute, e);
+            final Map<String, String> errorInfo = new HashMap<>();
+            errorInfo.put(ErrorTag.bad_attribute.name(), e.getMessage());
+            throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.rpc, ErrorTag.bad_attribute,
+                    ErrorSeverity.error, errorInfo);
+        }
+
+        try {
+            transactionProvider.validateTransaction();
+        } catch (ValidationException e) {
+            logger.warn("Validation failed", e);
+            final Map<String, String> errorInfo = new HashMap<>();
+            errorInfo.put(ErrorTag.operation_failed.name(), "Validation failed");
+            throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application, ErrorTag.operation_failed,
+                    ErrorSeverity.error, errorInfo);
+        } catch (IllegalStateException e) {
+            logger.warn("Validation failed", e);
+            final Map<String, String> errorInfo = new HashMap<>();
+            errorInfo
+                    .put(ErrorTag.operation_failed.name(),
+                            "Datastore is not present. Use 'get-config' or 'edit-config' before triggering 'operations' operation");
+            throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application, ErrorTag.operation_failed,
+                    ErrorSeverity.error, errorInfo);
+
+        }
+
+        logger.info("Datastore {} validated successfully", Datastore.candidate);
+
+        return document.createElement(XmlNetconfConstants.OK);
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/AbstractEditConfigStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/AbstractEditConfigStrategy.java
new file mode 100644 (file)
index 0000000..d8ea7d7
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.confignetconfconnector.operations.editconfig;
+
+import java.util.Map;
+
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractEditConfigStrategy implements EditConfigStrategy {
+
+    private static final Logger logger = LoggerFactory.getLogger(AbstractEditConfigStrategy.class);
+
+    @Override
+    public void executeConfiguration(String module, String instance, Map<String, AttributeConfigElement> configuration,
+            ConfigTransactionClient ta) {
+
+        try {
+            ObjectName on = ta.lookupConfigBean(module, instance);
+            logger.debug("ServiceInstance for {} {} located successfully under {}", module, instance, on);
+            executeStrategy(configuration, ta, on);
+        } catch (InstanceNotFoundException e) {
+            handleMissingInstance(configuration, ta, module, instance);
+        }
+
+    }
+
+    abstract void handleMissingInstance(Map<String, AttributeConfigElement> configuration, ConfigTransactionClient ta,
+            String module, String instance);
+
+    abstract void executeStrategy(Map<String, AttributeConfigElement> configuration, ConfigTransactionClient ta,
+            ObjectName objectName);
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/DeleteEditConfigStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/DeleteEditConfigStrategy.java
new file mode 100644 (file)
index 0000000..ffe107f
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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.confignetconfconnector.operations.editconfig;
+
+import java.util.Map;
+
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DeleteEditConfigStrategy extends AbstractEditConfigStrategy {
+
+    private static final Logger logger = LoggerFactory.getLogger(DeleteEditConfigStrategy.class);
+
+    @Override
+    void handleMissingInstance(Map<String, AttributeConfigElement> configuration, ConfigTransactionClient ta,
+            String module, String instance) {
+        throw new IllegalStateException("Unable to delete " + module + ":" + instance + " , ServiceInstance not found");
+    }
+
+    @Override
+    void executeStrategy(Map<String, AttributeConfigElement> configuration, ConfigTransactionClient ta, ObjectName on) {
+        try {
+            ta.destroyModule(on);
+            logger.debug("ServiceInstance {} deleted successfully", on);
+        } catch (InstanceNotFoundException e) {
+            throw new IllegalStateException("Unable to delete " + on, e);
+        }
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfig.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfig.java
new file mode 100644 (file)
index 0000000..de21f31
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * 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.confignetconfconnector.operations.editconfig;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.*;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.AbstractConfigNetconfOperation;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.management.ObjectName;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class EditConfig extends AbstractConfigNetconfOperation {
+
+    private static final Logger logger = LoggerFactory.getLogger(EditConfig.class);
+
+    private final YangStoreSnapshot yangStoreSnapshot;
+
+    private final TransactionProvider transactionProvider;
+    private EditConfigXmlParser editConfigXmlParser;
+
+    public EditConfig(YangStoreSnapshot yangStoreSnapshot, TransactionProvider transactionProvider,
+            ConfigRegistryClient configRegistryClient, String netconfSessionIdForReporting) {
+        super(configRegistryClient, netconfSessionIdForReporting);
+        this.yangStoreSnapshot = yangStoreSnapshot;
+        this.transactionProvider = transactionProvider;
+        this.editConfigXmlParser = new EditConfigXmlParser();
+    }
+
+    @VisibleForTesting
+    Element getResponseInternal(final Document document,
+            final EditConfigXmlParser.EditConfigExecution editConfigExecution) throws NetconfDocumentedException {
+        if (editConfigExecution.shouldTest()) {
+            executeTests(configRegistryClient, editConfigExecution);
+        }
+
+        if (editConfigExecution.shouldSet()) {
+            executeSet(configRegistryClient, editConfigExecution);
+        }
+
+        logger.info("Operation {} successful", EditConfigXmlParser.EDIT_CONFIG);
+
+        return document.createElement(XmlNetconfConstants.OK);
+    }
+
+    private void executeSet(ConfigRegistryClient configRegistryClient,
+            EditConfigXmlParser.EditConfigExecution editConfigExecution) throws NetconfDocumentedException {
+        try {
+            set(configRegistryClient, editConfigExecution);
+        } catch (IllegalStateException | JmxAttributeValidationException | ValidationException e) {
+            logger.warn("Set phase for {} failed", EditConfigXmlParser.EDIT_CONFIG, e);
+            final Map<String, String> errorInfo = new HashMap<>();
+            errorInfo.put(ErrorTag.operation_failed.name(), e.getMessage());
+            throw new NetconfDocumentedException("Test phase: " + e.getMessage(), e, ErrorType.application,
+                    ErrorTag.operation_failed, ErrorSeverity.error, errorInfo);
+        }
+        logger.debug("Set phase for {} operation successful", EditConfigXmlParser.EDIT_CONFIG);
+    }
+
+    private void executeTests(ConfigRegistryClient configRegistryClient,
+            EditConfigXmlParser.EditConfigExecution editConfigExecution) throws NetconfDocumentedException {
+        try {
+            test(configRegistryClient, editConfigExecution.resolvedXmlElements);
+        } catch (IllegalStateException | JmxAttributeValidationException | ValidationException e) {
+            logger.warn("Test phase for {} failed", EditConfigXmlParser.EDIT_CONFIG, e);
+            final Map<String, String> errorInfo = new HashMap<>();
+            errorInfo.put(ErrorTag.operation_failed.name(), e.getMessage());
+            throw new NetconfDocumentedException("Test phase: " + e.getMessage(), e, ErrorType.application,
+                    ErrorTag.operation_failed, ErrorSeverity.error, errorInfo);
+        }
+        logger.debug("Test phase for {} operation successful", EditConfigXmlParser.EDIT_CONFIG);
+    }
+
+    private void test(ConfigRegistryClient configRegistryClient,
+            Map<String, Multimap<String, ModuleElementResolved>> resolvedModules) {
+        ObjectName taON = transactionProvider.getTestTransaction();
+        try {
+
+            // default strategy = replace wipes config
+            if (EditStrategyType.defaultStrategy() == EditStrategyType.replace) {
+                transactionProvider.wipeTestTransaction(taON);
+            }
+            setOnTransaction(configRegistryClient, resolvedModules, taON);
+            transactionProvider.validateTestTransaction(taON);
+        } finally {
+            transactionProvider.abortTestTransaction(taON);
+        }
+    }
+
+    private void set(ConfigRegistryClient configRegistryClient,
+            EditConfigXmlParser.EditConfigExecution editConfigExecution) {
+        ObjectName taON = transactionProvider.getOrCreateTransaction();
+
+        // default strategy = replace wipes config
+        if (EditStrategyType.defaultStrategy() == EditStrategyType.replace) {
+            transactionProvider.wipeTransaction();
+        }
+        setOnTransaction(configRegistryClient, editConfigExecution.resolvedXmlElements, taON);
+    }
+
+    private void setOnTransaction(ConfigRegistryClient configRegistryClient,
+            Map<String, Multimap<String, ModuleElementResolved>> resolvedXmlElements, ObjectName taON) {
+        ConfigTransactionClient ta = configRegistryClient.getConfigTransactionClient(taON);
+
+        for (Multimap<String, ModuleElementResolved> modulesToResolved : resolvedXmlElements.values()) {
+            for (Entry<String, ModuleElementResolved> moduleToResolved : modulesToResolved.entries()) {
+                String moduleName = moduleToResolved.getKey();
+
+                ModuleElementResolved moduleElementResolved = moduleToResolved.getValue();
+                String instanceName = moduleElementResolved.getInstanceName();
+
+                InstanceConfigElementResolved ice = moduleElementResolved.getInstanceConfigElementResolved();
+                EditConfigStrategy strategy = ice.getEditStrategy();
+                strategy.executeConfiguration(moduleName, instanceName, ice.getConfiguration(), ta);
+            }
+        }
+    }
+
+    public static Config getConfigMapping(ConfigRegistryClient configRegistryClient,
+            Map<String, Map<String, ModuleMXBeanEntry>> mBeanEntries) {
+        Map<String, Map<String, ModuleConfig>> factories = transform(configRegistryClient, mBeanEntries);
+        return new Config(factories);
+    }
+
+    // TODO refactor
+    private static Map<String, Map<String, ModuleConfig>> transform(final ConfigRegistryClient configRegistryClient,
+            Map<String, Map<String, ModuleMXBeanEntry>> mBeanEntries) {
+        return Maps.transformEntries(mBeanEntries,
+                new Maps.EntryTransformer<String, Map<String, ModuleMXBeanEntry>, Map<String, ModuleConfig>>() {
+
+                    @Override
+                    public Map<String, ModuleConfig> transformEntry(String arg0, Map<String, ModuleMXBeanEntry> arg1) {
+                        return Maps.transformEntries(arg1,
+                                new Maps.EntryTransformer<String, ModuleMXBeanEntry, ModuleConfig>() {
+
+                                    @Override
+                                    public ModuleConfig transformEntry(String key, ModuleMXBeanEntry value) {
+                                        return new ModuleConfig(key, new InstanceConfig(configRegistryClient, value
+                                                .getAttributes()));
+                                    }
+                                });
+                    }
+                });
+    }
+
+    @Override
+    protected String getOperationName() {
+        return EditConfigXmlParser.EDIT_CONFIG;
+    }
+
+    @Override
+    protected Element handle(Document document, XmlElement xml) throws NetconfDocumentedException {
+
+        EditConfigXmlParser.EditConfigExecution editConfigExecution;
+        Config cfg = getConfigMapping(configRegistryClient, yangStoreSnapshot.getModuleMXBeanEntryMap());
+        try {
+            editConfigExecution = editConfigXmlParser.fromXml(xml, cfg);
+        } catch (IllegalStateException e) {
+            logger.warn("Error parsing xml", e);
+            final Map<String, String> errorInfo = new HashMap<>();
+            errorInfo.put(ErrorTag.missing_attribute.name(), "Missing value for 'target' attribute");
+            throw new NetconfDocumentedException(e.getMessage(), ErrorType.rpc, ErrorTag.missing_attribute,
+                    ErrorSeverity.error, errorInfo);
+        } catch (final IllegalArgumentException e) {
+            logger.warn("Error parsing xml", e);
+            final Map<String, String> errorInfo = new HashMap<>();
+            errorInfo.put(ErrorTag.bad_attribute.name(), e.getMessage());
+            throw new NetconfDocumentedException(e.getMessage(), ErrorType.rpc, ErrorTag.bad_attribute,
+                    ErrorSeverity.error, errorInfo);
+        } catch (final UnsupportedOperationException e) {
+            logger.warn("Unsupported", e);
+            final Map<String, String> errorInfo = new HashMap<>();
+            errorInfo.put(ErrorTag.operation_not_supported.name(), "Unsupported option for 'edit-config'");
+            throw new NetconfDocumentedException(e.getMessage(), ErrorType.application,
+                    ErrorTag.operation_not_supported, ErrorSeverity.error, errorInfo);
+        }
+
+        return getResponseInternal(document, editConfigExecution);
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigStrategy.java
new file mode 100644 (file)
index 0000000..6b7b622
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * 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.confignetconfconnector.operations.editconfig;
+
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+
+import java.util.Map;
+
+public interface EditConfigStrategy {
+
+    void executeConfiguration(String module, String instance, Map<String, AttributeConfigElement> configuration,
+            ConfigTransactionClient ta);
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigXmlParser.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigXmlParser.java
new file mode 100644 (file)
index 0000000..d835dfd
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * 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.confignetconfconnector.operations.editconfig;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Multimap;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Config;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.ModuleElementResolved;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.Datastore;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+import java.util.Map;
+
+public class EditConfigXmlParser {
+
+    private static final Logger logger = LoggerFactory.getLogger(EditConfigXmlParser.class);
+
+    public static final String EDIT_CONFIG = "edit-config";
+    public static final String DEFAULT_OPERATION_KEY = "default-operation";
+    static final String ERROR_OPTION_KEY = "error-option";
+    static final String DEFAULT_ERROR_OPTION = "stop-on-error";
+    static final String TARGET_KEY = "target";
+    static final String TEST_OPTION_KEY = "test-option";
+
+    public EditConfigXmlParser() {
+    }
+
+    EditConfigXmlParser.EditConfigExecution fromXml(final XmlElement xml, final Config cfgMapping)
+            throws NetconfDocumentedException {
+
+        EditStrategyType.resetDefaultStrategy();
+
+        xml.checkName(EditConfigXmlParser.EDIT_CONFIG);
+        xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+
+        XmlElement targetElement = xml.getOnlyChildElementWithSameNamespace(EditConfigXmlParser.TARGET_KEY);
+        XmlElement targetChildNode = targetElement.getOnlyChildElementWithSameNamespace();
+        String datastoreValue = targetChildNode.getName();
+        Datastore targetDatastore = Datastore.valueOf(datastoreValue);
+        logger.debug("Setting {} to '{}'", EditConfigXmlParser.TARGET_KEY, targetDatastore);
+
+        // check target
+        Preconditions.checkArgument(targetDatastore == Datastore.candidate,
+                "Only %s datastore supported for edit config but was: %s", Datastore.candidate, targetDatastore);
+
+        // Test option
+        TestOption testOption;
+        Optional<XmlElement> testOptionElementOpt = xml
+                .getOnlyChildElementWithSameNamespaceOptionally(EditConfigXmlParser.TEST_OPTION_KEY);
+        if (testOptionElementOpt.isPresent()) {
+            String testOptionValue = testOptionElementOpt.get().getTextContent();
+            testOption = EditConfigXmlParser.TestOption.getFromXmlName(testOptionValue);
+        } else {
+            testOption = EditConfigXmlParser.TestOption.getDefault();
+        }
+        logger.debug("Setting {} to '{}'", EditConfigXmlParser.TEST_OPTION_KEY, testOption);
+
+        // Error option
+        Optional<XmlElement> errorOptionElement = xml
+                .getOnlyChildElementWithSameNamespaceOptionally(EditConfigXmlParser.ERROR_OPTION_KEY);
+        if (errorOptionElement.isPresent()) {
+            String errorOptionParsed = errorOptionElement.get().getTextContent();
+            if (false == errorOptionParsed.equals(EditConfigXmlParser.DEFAULT_ERROR_OPTION))
+                throw new UnsupportedOperationException("Only " + EditConfigXmlParser.DEFAULT_ERROR_OPTION
+                        + " supported for " + EditConfigXmlParser.ERROR_OPTION_KEY + ", was " + errorOptionParsed);
+        }
+
+        // Default op
+        Optional<XmlElement> defaultContent = xml
+                .getOnlyChildElementWithSameNamespaceOptionally(EditConfigXmlParser.DEFAULT_OPERATION_KEY);
+        if (defaultContent.isPresent())
+            EditStrategyType.setDefaultStrategy(EditStrategyType.valueOf(defaultContent.get().getTextContent()));
+
+        XmlElement configElement = xml.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.CONFIG_KEY);
+
+        return new EditConfigXmlParser.EditConfigExecution(xml, cfgMapping, configElement, testOption);
+    }
+
+    private void removeMountpointsFromConfig(XmlElement configElement, XmlElement mountpointsElement) {
+        configElement.getDomElement().removeChild(mountpointsElement.getDomElement());
+    }
+
+    @VisibleForTesting
+    static enum TestOption {
+        testOnly, set, testThenSet;
+
+        static TestOption getFromXmlName(String testOptionXmlName) {
+            switch (testOptionXmlName) {
+            case "test-only":
+                return testOnly;
+            case "test-then-set":
+                return testThenSet;
+            case "set":
+                return set;
+            default:
+                throw new IllegalArgumentException("Unsupported test option " + testOptionXmlName + " supported: "
+                        + Arrays.toString(TestOption.values()));
+            }
+        }
+
+        public static TestOption getDefault() {
+            return testThenSet;
+        }
+
+    }
+
+    @VisibleForTesting
+    static class EditConfigExecution {
+        XmlElement editConfigXml;
+        Map<String, Multimap<String, ModuleElementResolved>> resolvedXmlElements;
+        TestOption testOption;
+
+        EditConfigExecution(XmlElement xml, Config configResolver, XmlElement configElement, TestOption testOption) {
+            this.editConfigXml = xml;
+            this.resolvedXmlElements = configResolver.fromXml(configElement);
+            this.testOption = testOption;
+        }
+
+        boolean shouldTest() {
+            return testOption == TestOption.testOnly || testOption == TestOption.testThenSet;
+        }
+
+        boolean shouldSet() {
+            return testOption == TestOption.set || testOption == TestOption.testThenSet;
+        }
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditStrategyType.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditStrategyType.java
new file mode 100644 (file)
index 0000000..a7a0518
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import com.google.common.base.Preconditions;
+
+public enum EditStrategyType {
+    // can be default
+    merge, replace, none,
+    // additional per element
+    delete, remove;
+
+    private static final Set<EditStrategyType> defaultStrats = EnumSet.of(merge, replace, none);
+
+    private static EditStrategyType defaultStrat = merge;
+
+    public static EditStrategyType defaultStrategy() {
+        return defaultStrat;
+    }
+
+    public static void setDefaultStrategy(EditStrategyType strat) {
+        Preconditions.checkArgument(strat.canBeDefault(), "Default edit strategy can be only of value " + defaultStrats
+                + ", but was " + strat);
+        defaultStrat = strat;
+    }
+
+    public static void resetDefaultStrategy() {
+        setDefaultStrategy(EditStrategyType.merge);
+    }
+
+    public boolean isEnforcing() {
+        switch (this) {
+        case merge:
+        case none:
+        case remove:
+        case delete:
+            return false;
+        case replace:
+            return true;
+
+        default:
+            throw new IllegalStateException("Default edit strategy can be only of value " + defaultStrats + " but was "
+                    + this);
+        }
+    }
+
+    private static final EnumSet<EditStrategyType> defaults;
+
+    static {
+        defaults = EnumSet.of(merge, replace, none);
+    }
+
+    private boolean canBeDefault() {
+        return defaults.contains(this);
+    }
+
+    public EditConfigStrategy getFittingStrategy() {
+        switch (this) {
+        case merge:
+            return new MergeEditConfigStrategy();
+        case replace:
+            return new ReplaceEditConfigStrategy();
+        case delete:
+            return new DeleteEditConfigStrategy();
+        case remove:
+            return new RemoveEditConfigStrategy();
+        case none:
+            return new NoneEditConfigStrategy();
+        default:
+            throw new UnsupportedOperationException("Unimplemented edit config strategy" + this);
+        }
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/MergeEditConfigStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/MergeEditConfigStrategy.java
new file mode 100644 (file)
index 0000000..2a4a784
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.management.Attribute;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MergeEditConfigStrategy extends AbstractEditConfigStrategy {
+
+    private static final Logger logger = LoggerFactory.getLogger(MergeEditConfigStrategy.class);
+
+    @Override
+    void handleMissingInstance(Map<String, AttributeConfigElement> configuration, ConfigTransactionClient ta,
+            String module, String instance) {
+        ObjectName on;
+        try {
+            on = ta.createModule(module, instance);
+            logger.info("New instance for {} {} created under name {}", module, instance, on);
+            executeStrategy(configuration, ta, on);
+        } catch (InstanceAlreadyExistsException e1) {
+            throw new IllegalStateException("Unable to create instance for " + module + " : " + instance);
+        }
+    }
+
+    @Override
+    void executeStrategy(Map<String, AttributeConfigElement> configuration, ConfigTransactionClient ta, ObjectName on) {
+        for (Entry<String, AttributeConfigElement> configAttributeEntry : configuration.entrySet()) {
+            try {
+                AttributeConfigElement ace = configAttributeEntry.getValue();
+
+                if (!ace.getResolvedValue().isPresent()) {
+                    logger.debug("Skipping attribute {} for {}", configAttributeEntry.getKey(), on);
+                    continue;
+                }
+
+                Object value = ace.getResolvedValue().get();
+                ta.setAttribute(on, ace.getJmxName(), new Attribute(ace.getJmxName(), value));
+                logger.debug("Attribute {} set to {} for {}", configAttributeEntry.getKey(), value, on);
+            } catch (Exception e) {
+                throw new IllegalStateException("Unable to set attributes for " + on + ", Error with attribute "
+                        + configAttributeEntry.getKey() + ":" + configAttributeEntry.getValue(), e);
+            }
+        }
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/NoneEditConfigStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/NoneEditConfigStrategy.java
new file mode 100644 (file)
index 0000000..db11ce3
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig;
+
+import java.util.Map;
+
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NoneEditConfigStrategy implements EditConfigStrategy {
+
+    private static final Logger logger = LoggerFactory.getLogger(NoneEditConfigStrategy.class);
+
+    @Override
+    public void executeConfiguration(String module, String instance, Map<String, AttributeConfigElement> configuration,
+            ConfigTransactionClient ta) {
+        logger.debug("Skipping configuration element for {}:{}", module, instance);
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/RemoveEditConfigStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/RemoveEditConfigStrategy.java
new file mode 100644 (file)
index 0000000..76ca094
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig;
+
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+
+public class RemoveEditConfigStrategy extends DeleteEditConfigStrategy {
+
+    private static final Logger logger = LoggerFactory.getLogger(RemoveEditConfigStrategy.class);
+
+    @Override
+    void handleMissingInstance(Map<String, AttributeConfigElement> configuration, ConfigTransactionClient ta,
+            String module, String instance) {
+        logger.warn("Unable to delete {}:{}, ServiceInstance not found", module, instance);
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/ReplaceEditConfigStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/ReplaceEditConfigStrategy.java
new file mode 100644 (file)
index 0000000..0091d6c
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.management.Attribute;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ReplaceEditConfigStrategy extends AbstractEditConfigStrategy {
+
+    private static final Logger logger = LoggerFactory.getLogger(ReplaceEditConfigStrategy.class);
+
+    @Override
+    void handleMissingInstance(Map<String, AttributeConfigElement> configuration, ConfigTransactionClient ta,
+            String module, String instance) {
+        try {
+            ObjectName on = ta.createModule(module, instance);
+            logger.debug("New instance for {} {} created under name {}", module, instance, on);
+            executeStrategy(configuration, ta, on);
+        } catch (InstanceAlreadyExistsException e) {
+            logger.warn("Error creating instance {}:{}, replace failed", module, instance, e);
+            throw new IllegalStateException("Unable to create new instance for " + module + " : " + instance, e);
+        }
+    }
+
+    @Override
+    void executeStrategy(Map<String, AttributeConfigElement> configuration, ConfigTransactionClient ta, ObjectName on) {
+        for (Entry<String, AttributeConfigElement> configAttributeEntry : configuration.entrySet()) {
+            try {
+                AttributeConfigElement ace = configAttributeEntry.getValue();
+
+                if (!ace.getResolvedValue().isPresent()) {
+                    Object value = ace.getResolvedDefaultValue();
+                    ta.setAttribute(on, ace.getJmxName(), new Attribute(ace.getJmxName(), value));
+                    logger.debug("Attribute {} set to default value {} for {}", configAttributeEntry.getKey(), value,
+                            on);
+                } else {
+                    Object value = ace.getResolvedValue().get();
+                    ta.setAttribute(on, ace.getJmxName(), new Attribute(ace.getJmxName(), value));
+                    logger.debug("Attribute {} set to value {} for {}", configAttributeEntry.getKey(), value, on);
+                }
+
+            } catch (Exception e) {
+                throw new IllegalStateException("Unable to set attributes for " + on + ", Error with attribute "
+                        + configAttributeEntry.getKey() + ":" + configAttributeEntry.getValue(), e);
+            }
+        }
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java
new file mode 100644 (file)
index 0000000..b93843d
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * 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.confignetconfconnector.operations.get;
+
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.InstanceConfig;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.runtime.InstanceRuntime;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.runtime.ModuleRuntime;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.runtime.Runtime;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.AbstractConfigNetconfOperation;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.management.ObjectName;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class Get extends AbstractConfigNetconfOperation {
+
+    public static final String GET = "get";
+
+    private final YangStoreSnapshot yangStoreSnapshot;
+    private static final Logger logger = LoggerFactory.getLogger(Get.class);
+
+    public Get(YangStoreSnapshot yangStoreSnapshot, ConfigRegistryClient configRegistryClient,
+            String netconfSessionIdForReporting) {
+        super(configRegistryClient, netconfSessionIdForReporting);
+        this.yangStoreSnapshot = yangStoreSnapshot;
+    }
+
+    private Map<String, Map<String, ModuleRuntime>> createModuleRuntimes(ConfigRegistryClient configRegistryClient,
+            Map<String, Map<String, ModuleMXBeanEntry>> mBeanEntries) {
+        Map<String, Map<String, ModuleRuntime>> retVal = Maps.newHashMap();
+
+        for (String namespace : mBeanEntries.keySet()) {
+
+            Map<String, ModuleRuntime> innerMap = Maps.newHashMap();
+            Map<String, ModuleMXBeanEntry> entriesFromNamespace = mBeanEntries.get(namespace);
+            for (String module : entriesFromNamespace.keySet()) {
+
+                ModuleMXBeanEntry mbe = entriesFromNamespace.get(module);
+
+                Map<RuntimeBeanEntry, InstanceConfig> cache = Maps.newHashMap();
+                RuntimeBeanEntry root = null;
+                for (RuntimeBeanEntry rbe : mbe.getRuntimeBeans()) {
+                    cache.put(rbe, new InstanceConfig(configRegistryClient, rbe.getYangPropertiesToTypesMap()));
+                    if (rbe.isRoot())
+                        root = rbe;
+                }
+
+                if (root == null)
+                    continue;
+
+                InstanceRuntime rootInstanceRuntime = createInstanceRuntime(root, cache);
+                ModuleRuntime moduleRuntime = new ModuleRuntime(module, rootInstanceRuntime);
+                innerMap.put(module, moduleRuntime);
+            }
+
+            retVal.put(namespace, innerMap);
+        }
+        return retVal;
+    }
+
+    private InstanceRuntime createInstanceRuntime(RuntimeBeanEntry root, Map<RuntimeBeanEntry, InstanceConfig> cache) {
+        Map<String, InstanceRuntime> children = Maps.newHashMap();
+        for (RuntimeBeanEntry child : root.getChildren()) {
+            children.put(child.getJavaNamePrefix(), createInstanceRuntime(child, cache));
+        }
+
+        return new InstanceRuntime(cache.get(root), children, createJmxToYangMap(root.getChildren()));
+    }
+
+    private Map<String, String> createJmxToYangMap(List<RuntimeBeanEntry> children) {
+        Map<String, String> jmxToYangNamesForChildRbe = Maps.newHashMap();
+        for (RuntimeBeanEntry rbe : children) {
+            jmxToYangNamesForChildRbe.put(rbe.getJavaNamePrefix(), rbe.getYangName());
+        }
+        return jmxToYangNamesForChildRbe;
+    }
+
+    private static void checkXml(XmlElement xml) {
+        xml.checkName(GET);
+        xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+
+        // Filter option - unsupported
+        if (xml.getChildElements(XmlNetconfConstants.FILTER).size() != 0)
+            throw new UnsupportedOperationException("Unsupported option " + XmlNetconfConstants.FILTER + " for " + GET);
+    }
+
+    @Override
+    protected String getOperationName() {
+        return GET;
+    }
+
+    @Override
+    protected Element handle(Document document, XmlElement xml) throws NetconfDocumentedException {
+        try {
+            checkXml(xml);
+        } catch (final IllegalArgumentException e) {
+            logger.warn("Error parsing xml", e);
+            final Map<String, String> errorInfo = new HashMap<>();
+            errorInfo.put(ErrorTag.bad_attribute.name(), e.getMessage());
+            throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.rpc, ErrorTag.bad_attribute,
+                    ErrorSeverity.error, errorInfo);
+        } catch (final UnsupportedOperationException e) {
+            logger.warn("Unsupported", e);
+            final Map<String, String> errorInfo = new HashMap<>();
+            errorInfo.put(ErrorTag.operation_not_supported.name(), "Unsupported option for 'get'");
+            throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application,
+                    ErrorTag.operation_not_supported, ErrorSeverity.error, errorInfo);
+        }
+        final Set<ObjectName> runtimeBeans = configRegistryClient.lookupRuntimeBeans();
+        final Map<String, Map<String, ModuleRuntime>> moduleMappings = createModuleRuntimes(configRegistryClient,
+                yangStoreSnapshot.getModuleMXBeanEntryMap());
+        final Runtime runtime = new Runtime(moduleMappings);
+
+        final Element element = runtime.toXml(runtimeBeans, document);
+
+        logger.info("{} operation successful", GET);
+
+        return element;
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/CandidateDatastoreQueryStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/CandidateDatastoreQueryStrategy.java
new file mode 100644 (file)
index 0000000..8e0a9d7
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig;
+
+import java.util.Set;
+
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+
+public class CandidateDatastoreQueryStrategy implements DatastoreQueryStrategy {
+
+    private final TransactionProvider transactionProvider;
+
+    public CandidateDatastoreQueryStrategy(TransactionProvider transactionProvider) {
+        this.transactionProvider = transactionProvider;
+    }
+
+    @Override
+    public Set<ObjectName> queryInstances(ConfigRegistryClient configRegistryClient) {
+        ObjectName on = transactionProvider.getOrCreateTransaction();
+        ConfigTransactionClient proxy = configRegistryClient.getConfigTransactionClient(on);
+        return proxy.lookupConfigBeans();
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/DatastoreQueryStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/DatastoreQueryStrategy.java
new file mode 100644 (file)
index 0000000..4ae5670
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig;
+
+import java.util.Set;
+
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+
+public interface DatastoreQueryStrategy {
+
+    /**
+     * @param configRegistryClient
+     * @return
+     */
+    Set<ObjectName> queryInstances(ConfigRegistryClient configRegistryClient);
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/GetConfig.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/GetConfig.java
new file mode 100644 (file)
index 0000000..9b8c150
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * 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.confignetconfconnector.operations.getconfig;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Config;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.InstanceConfig;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.ModuleConfig;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.AbstractConfigNetconfOperation;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.Datastore;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.management.ObjectName;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class GetConfig extends AbstractConfigNetconfOperation {
+
+    public static final String GET_CONFIG = "get-config";
+
+    private final YangStoreSnapshot yangStoreSnapshot;
+    private final Optional<String> maybeNamespace;
+
+    private final TransactionProvider transactionProvider;
+
+    private static final Logger logger = LoggerFactory.getLogger(GetConfig.class);
+
+    public GetConfig(YangStoreSnapshot yangStoreSnapshot, Optional<String> maybeNamespace,
+            TransactionProvider transactionProvider, ConfigRegistryClient configRegistryClient,
+            String netconfSessionIdForReporting) {
+        super(configRegistryClient, netconfSessionIdForReporting);
+        this.yangStoreSnapshot = yangStoreSnapshot;
+        this.maybeNamespace = maybeNamespace;
+        this.transactionProvider = transactionProvider;
+    }
+
+    public static Datastore fromXml(XmlElement xml) {
+        xml.checkName(GET_CONFIG);
+        xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+
+        XmlElement sourceElement = xml.getOnlyChildElement(XmlNetconfConstants.SOURCE_KEY,
+                XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+        XmlElement sourceNode = sourceElement.getOnlyChildElement();
+        String sourceParsed = sourceNode.getName();
+        logger.debug("Setting source datastore to '{}'", sourceParsed);
+        Datastore sourceDatastore = Datastore.valueOf(sourceParsed);
+
+        // Filter option - unsupported
+        if (xml.getChildElements(XmlNetconfConstants.FILTER).size() != 0)
+            throw new UnsupportedOperationException("Unsupported option " + XmlNetconfConstants.FILTER + " for "
+                    + GET_CONFIG);
+
+        return sourceDatastore;
+
+    }
+
+    private Element getResponseInternal(final Document document, final ConfigRegistryClient configRegistryClient,
+            final Datastore source) throws NetconfDocumentedException {
+        Element dataElement = document.createElement(XmlNetconfConstants.DATA_KEY);
+        final Set<ObjectName> instances = Datastore.getInstanceQueryStrategy(source, this.transactionProvider)
+                .queryInstances(configRegistryClient);
+        final Config configMapping = new Config(transform(configRegistryClient,
+                yangStoreSnapshot.getModuleMXBeanEntryMap()));
+        dataElement = configMapping.toXml(instances, this.maybeNamespace, document, dataElement);
+
+        logger.info("{} operation successful", GET_CONFIG);
+
+        return dataElement;
+    }
+
+    // TODO refactor ... duplicate code
+    private Map<String, Map<String, ModuleConfig>> transform(final ConfigRegistryClient configRegistryClient,
+            Map<String, Map<String, ModuleMXBeanEntry>> mBeanEntries) {
+        return Maps.transformEntries(mBeanEntries,
+                new Maps.EntryTransformer<String, Map<String, ModuleMXBeanEntry>, Map<String, ModuleConfig>>() {
+
+                    @Override
+                    public Map<String, ModuleConfig> transformEntry(String arg0, Map<String, ModuleMXBeanEntry> arg1) {
+                        return Maps.transformEntries(arg1,
+                                new Maps.EntryTransformer<String, ModuleMXBeanEntry, ModuleConfig>() {
+
+                                    @Override
+                                    public ModuleConfig transformEntry(String key, ModuleMXBeanEntry value) {
+                                        return new ModuleConfig(key, new InstanceConfig(configRegistryClient, value
+                                                .getAttributes()), value.getProvidedServices().values());
+                                    }
+                                });
+                    }
+                });
+    }
+
+    @Override
+    protected String getOperationName() {
+        return GET_CONFIG;
+    }
+
+    @Override
+    public Element handle(Document document, XmlElement xml) throws NetconfDocumentedException {
+        Datastore source;
+        try {
+            source = fromXml(xml);
+        } catch (final IllegalArgumentException e) {
+            logger.warn("Rpc error: {}", ErrorTag.bad_attribute, e);
+            final Map<String, String> errorInfo = new HashMap<>();
+            errorInfo.put(ErrorTag.bad_attribute.name(), e.getMessage());
+            throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.rpc, ErrorTag.bad_attribute,
+                    ErrorSeverity.error, errorInfo);
+        } catch (final IllegalStateException e) {
+            logger.warn("Rpc error: {}", ErrorTag.missing_attribute, e);
+            final Map<String, String> errorInfo = new HashMap<>();
+            errorInfo.put(ErrorTag.missing_attribute.name(), "Missing datasource attribute value");
+            throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.rpc, ErrorTag.missing_attribute,
+                    ErrorSeverity.error, errorInfo);
+        } catch (final UnsupportedOperationException e) {
+            logger.warn("Unsupported", e);
+            final Map<String, String> errorInfo = new HashMap<>();
+            errorInfo.put(ErrorTag.operation_not_supported.name(), "Unsupported option for get");
+            throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application,
+                    ErrorTag.operation_not_supported, ErrorSeverity.error, errorInfo);
+        }
+        return getResponseInternal(document, configRegistryClient, source);
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/RunningDatastoreQueryStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/RunningDatastoreQueryStrategy.java
new file mode 100644 (file)
index 0000000..c3f8257
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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.confignetconfconnector.operations.getconfig;
+
+import java.util.Set;
+
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+
+public class RunningDatastoreQueryStrategy implements DatastoreQueryStrategy {
+
+    @Override
+    public Set<ObjectName> queryInstances(ConfigRegistryClient configRegistryClient) {
+        return configRegistryClient.lookupConfigBeans();
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/runtimerpc/RuntimeRpc.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/runtimerpc/RuntimeRpc.java
new file mode 100644 (file)
index 0000000..7bdfa27
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * 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.confignetconfconnector.operations.runtimerpc;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry.Rpc;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.SimpleTypeResolver;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.SimpleAttributeMappingStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.InstanceRuntimeRpc;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.ModuleRpcs;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.Rpcs;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.AbstractConfigNetconfOperation;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.Commit;
+import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.management.ObjectName;
+import javax.management.openmbean.SimpleType;
+import java.util.Map;
+
+public class RuntimeRpc extends AbstractConfigNetconfOperation {
+
+    private static final Logger logger = LoggerFactory.getLogger(Commit.class);
+    public static final String CONTEXT_INSTANCE = "context-instance";
+
+    private final YangStoreSnapshot yangStoreSnapshot;
+
+    public RuntimeRpc(final YangStoreSnapshot yangStoreSnapshot, ConfigRegistryClient configRegistryClient,
+            String netconfSessionIdForReporting) {
+        super(configRegistryClient, netconfSessionIdForReporting);
+        this.yangStoreSnapshot = yangStoreSnapshot;
+    }
+
+    private String getStringRepresentation(final Object result) {
+        final SimpleType<?> simpleType = SimpleTypeResolver.getSimpleType(result.getClass().getName());
+        final Optional<String> mappedAttributeOpt = new SimpleAttributeMappingStrategy(simpleType).mapAttribute(result);
+        return mappedAttributeOpt.isPresent() ? mappedAttributeOpt.get() : "";
+    }
+
+    private Object executeOperation(final ConfigRegistryClient configRegistryClient, final ObjectName on,
+            final String name, final Map<String, AttributeConfigElement> attributes) {
+        final Object[] params = new Object[attributes.size()];
+        final String[] signature = new String[attributes.size()];
+
+        int i = 0;
+        for (final String attrName : attributes.keySet()) {
+            final AttributeConfigElement attribute = attributes.get(attrName);
+            final Optional<?> resolvedValueOpt = attribute.getResolvedValue();
+
+            params[i] = resolvedValueOpt.isPresent() ? resolvedValueOpt.get() : attribute.getResolvedDefaultValue();
+            signature[i] = resolvedValueOpt.isPresent() ? resolvedValueOpt.get().getClass().getName() : attribute
+                    .getResolvedDefaultValue().getClass().getName();
+            i++;
+        }
+
+        return configRegistryClient.invokeMethod(on, name, params, signature);
+    }
+
+    public NetconfOperationExecution fromXml(final XmlElement xml) throws NetconfDocumentedException {
+        final String namespace = xml.getNamespace();
+        final XmlElement contextInstanceElement = xml.getOnlyChildElement(CONTEXT_INSTANCE);
+        final String operationName = xml.getName();
+
+        final RuntimeRpcElementResolved id = RuntimeRpcElementResolved.fromXpath(
+                contextInstanceElement.getTextContent(), operationName, namespace);
+
+        final Rpcs rpcs = mapRpcs(yangStoreSnapshot.getModuleMXBeanEntryMap());
+
+        final ModuleRpcs rpcMapping = rpcs.getRpcMapping(id);
+        final InstanceRuntimeRpc instanceRuntimeRpc = rpcMapping.getRpc(id.getRuntimeBeanName(), operationName);
+
+        // TODO move to Rpcs after xpath attribute is redesigned
+
+        final ObjectName on = id.getObjectName(rpcMapping);
+        Map<String, AttributeConfigElement> attributes = instanceRuntimeRpc.fromXml(xml);
+        attributes = sortAttributes(attributes, xml);
+
+        return new NetconfOperationExecution(on, instanceRuntimeRpc.getName(), attributes,
+                instanceRuntimeRpc.getReturnType(), namespace);
+    }
+
+    @Override
+    public HandlingPriority canHandle(Document message) {
+        XmlElement requestElement = getRequestElementWithCheck(message);
+
+        XmlElement operationElement = requestElement.getOnlyChildElement();
+        final String netconfOperationName = operationElement.getName();
+        final String netconfOperationNamespace = operationElement.getNamespace();
+
+        final Optional<XmlElement> contextInstanceElement = operationElement
+                .getOnlyChildElementOptionally(CONTEXT_INSTANCE);
+
+        if (contextInstanceElement.isPresent() == false)
+            return HandlingPriority.CANNOT_HANDLE;
+
+        final RuntimeRpcElementResolved id = RuntimeRpcElementResolved.fromXpath(contextInstanceElement.get()
+                .getTextContent(), netconfOperationName, netconfOperationNamespace);
+
+        // TODO reuse rpcs instance in fromXml method
+        final Rpcs rpcs = mapRpcs(yangStoreSnapshot.getModuleMXBeanEntryMap());
+
+        try {
+
+            final ModuleRpcs rpcMapping = rpcs.getRpcMapping(id);
+            final InstanceRuntimeRpc instanceRuntimeRpc = rpcMapping.getRpc(id.getRuntimeBeanName(),
+                    netconfOperationName);
+            Preconditions.checkState(instanceRuntimeRpc != null, "No rpc found for %s:%s", netconfOperationNamespace,
+                    netconfOperationName);
+
+        } catch (IllegalStateException e) {
+            logger.debug("Cannot handle runtime operation {}:{}", netconfOperationNamespace, netconfOperationName, e);
+            return HandlingPriority.CANNOT_HANDLE;
+        }
+
+        return HandlingPriority.HANDLE_WITH_DEFAULT_PRIORITY;
+    }
+
+    @Override
+    protected HandlingPriority canHandle(String netconfOperationName, String namespace) {
+        throw new UnsupportedOperationException(
+                "This should not be used since it is not possible to provide check with these attributes");
+    }
+
+    @Override
+    protected String getOperationName() {
+        throw new UnsupportedOperationException("Runtime rpc does not have a stable name");
+    }
+
+    @Override
+    protected Element handle(Document document, XmlElement xml) throws NetconfDocumentedException {
+
+        // TODO exception handling
+        // TODO check for namespaces and unknown elements
+
+        final NetconfOperationExecution execution = fromXml(xml);
+
+        logger.debug("Invoking operation {} on {} with arguments {}", execution.operationName, execution.on,
+                execution.attributes);
+        final Object result = executeOperation(configRegistryClient, execution.on, execution.operationName,
+                execution.attributes);
+
+        logger.info("Operation {} called successfully on {} with arguments {} with result {}", execution.operationName,
+                execution.on, execution.attributes, result);
+
+        if (execution.returnType.equals("void")) {
+            return document.createElement("ok");
+        } else {
+            final Element output = XmlUtil.createTextElement(document, "result", getStringRepresentation(result));
+            XmlUtil.addNamespaceAttr(output, execution.namespace);
+            return output;
+        }
+    }
+
+    private static class NetconfOperationExecution {
+
+        private final ObjectName on;
+        private final String operationName;
+        private final Map<String, AttributeConfigElement> attributes;
+        private final String returnType;
+        private final String namespace;
+
+        public NetconfOperationExecution(final ObjectName on, final String name,
+                final Map<String, AttributeConfigElement> attributes, final String returnType, final String namespace) {
+            this.on = on;
+            this.operationName = name;
+            this.attributes = attributes;
+            this.returnType = returnType;
+            this.namespace = namespace;
+        }
+
+    }
+
+    private static Map<String, AttributeConfigElement> sortAttributes(
+            final Map<String, AttributeConfigElement> attributes, final XmlElement xml) {
+        final Map<String, AttributeConfigElement> sorted = Maps.newLinkedHashMap();
+
+        for (XmlElement xmlElement : xml.getChildElements()) {
+            final String name = xmlElement.getName();
+            if (CONTEXT_INSTANCE.equals(name) == false) { // skip context
+                                                          // instance child node
+                                                          // because it
+                                                          // specifies
+                // ObjectName
+                final AttributeConfigElement value = attributes.get(name);
+                if (value == null) {
+                    throw new IllegalArgumentException("Cannot find yang mapping for node " + xmlElement);
+                }
+                sorted.put(name, value);
+            }
+        }
+
+        return sorted;
+    }
+
+    private static Rpcs mapRpcs(final Map<String, Map<String, ModuleMXBeanEntry>> mBeanEntries) {
+
+        final Map<String, Map<String, ModuleRpcs>> map = Maps.newHashMap();
+
+        for (final String namespace : mBeanEntries.keySet()) {
+
+            Map<String, ModuleRpcs> namespaceToModules = map.get(namespace);
+            if (namespaceToModules == null) {
+                namespaceToModules = Maps.newHashMap();
+                map.put(namespace, namespaceToModules);
+            }
+
+            for (final String moduleName : mBeanEntries.get(namespace).keySet()) {
+
+                ModuleRpcs rpcMapping = namespaceToModules.get(moduleName);
+                if (rpcMapping == null) {
+                    rpcMapping = new ModuleRpcs();
+                    namespaceToModules.put(moduleName, rpcMapping);
+                }
+
+                final ModuleMXBeanEntry entry = mBeanEntries.get(namespace).get(moduleName);
+
+                for (final RuntimeBeanEntry runtimeEntry : entry.getRuntimeBeans()) {
+                    rpcMapping.addNameMapping(runtimeEntry);
+                    for (final Rpc rpc : runtimeEntry.getRpcs()) {
+                        rpcMapping.addRpc(runtimeEntry, rpc);
+                    }
+                }
+            }
+        }
+
+        return new Rpcs(map);
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/runtimerpc/RuntimeRpcElementResolved.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/runtimerpc/RuntimeRpcElementResolved.java
new file mode 100644 (file)
index 0000000..838e5d8
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.operations.runtimerpc;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.ModuleRpcs;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+
+import javax.management.ObjectName;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Represents parsed xpath to runtime bean instance
+ */
+public final class RuntimeRpcElementResolved {
+    private final String moduleName;
+    private final String instanceName;
+    private final String namespace;
+    private final String runtimeBeanName;
+    private final Map<String, String> additionalAttributes;
+
+    private RuntimeRpcElementResolved(String namespace, String moduleName, String instanceName, String runtimeBeanName,
+            Map<String, String> additionalAttributes) {
+        this.moduleName = moduleName;
+        this.instanceName = instanceName;
+        this.additionalAttributes = additionalAttributes;
+        this.namespace = namespace;
+        this.runtimeBeanName = runtimeBeanName;
+    }
+
+    public String getModuleName() {
+        return moduleName;
+    }
+
+    public String getInstanceName() {
+        return instanceName;
+    }
+
+    public String getNamespace() {
+        return namespace;
+    }
+
+    public String getRuntimeBeanName() {
+        return runtimeBeanName;
+    }
+
+    public ObjectName getObjectName(ModuleRpcs rpcMapping) {
+        Map<String, String> additionalAttributesJavaNames = Maps
+                .newHashMapWithExpectedSize(additionalAttributes.size());
+        for (String attributeYangName : additionalAttributes.keySet()) {
+            String attributeJavaName = rpcMapping.getRbeJavaName(attributeYangName);
+            Preconditions.checkState(attributeJavaName != null,
+                    "Cannot find java name for runtime bean wtih yang name %s", attributeYangName);
+            additionalAttributesJavaNames.put(attributeJavaName, additionalAttributes.get(attributeYangName));
+        }
+        return ObjectNameUtil.createRuntimeBeanName(moduleName, instanceName, additionalAttributesJavaNames);
+    }
+
+    private static final String xpathPatternBlueprint = "/" + XmlNetconfConstants.DATA_KEY + "/"
+            + XmlNetconfConstants.MODULES_KEY + "/" + XmlNetconfConstants.MODULE_KEY + "\\["
+            + XmlNetconfConstants.NAME_KEY + "='(.+)'\\]/" + XmlNetconfConstants.INSTANCE_KEY + "\\["
+            + XmlNetconfConstants.NAME_KEY + "='([^']+)'\\](.*)";
+    private static final Pattern xpathPattern = Pattern.compile(xpathPatternBlueprint);
+    private static final String additionalPatternBlueprint = "(.+)\\[(.+)='(.+)'\\]";
+    private static final Pattern additionalPattern = Pattern.compile(additionalPatternBlueprint);
+
+    public static RuntimeRpcElementResolved fromXpath(String xpath, String elementName, String namespace) {
+        Matcher matcher = xpathPattern.matcher(xpath);
+        Preconditions.checkState(matcher.matches(),
+                "Node %s with value '%s' not in required form on rpc element %s, required format is %s",
+                RuntimeRpc.CONTEXT_INSTANCE, xpath, elementName, xpathPatternBlueprint);
+
+        String moduleName = matcher.group(1);
+        String instanceName = matcher.group(2);
+        String additionalString = matcher.group(3);
+        HashMap<String, String> additionalAttributes = Maps.<String, String> newHashMap();
+        String runtimeBeanYangName = moduleName;
+        for (String additionalKeyValue : additionalString.split("/")) {
+            if (Strings.isNullOrEmpty(additionalKeyValue))
+                continue;
+            matcher = additionalPattern.matcher(additionalKeyValue);
+            Preconditions
+                    .checkState(
+                            matcher.matches(),
+                            "Attribute %s not in required form on rpc element %s, required format for additional attributes is  %s",
+                            additionalKeyValue, elementName, additionalPatternBlueprint);
+            String name = matcher.group(1);
+            runtimeBeanYangName = name;
+            additionalAttributes.put(name, matcher.group(3));
+        }
+
+        return new RuntimeRpcElementResolved(namespace, moduleName, instanceName, runtimeBeanYangName,
+                additionalAttributes);
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/Activator.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/Activator.java
new file mode 100644 (file)
index 0000000..83029c4
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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.confignetconfconnector.osgi;
+
+import org.opendaylight.controller.config.yang.store.api.YangStoreService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Hashtable;
+
+import static com.google.common.base.Preconditions.checkState;
+
+public class Activator implements BundleActivator, YangStoreServiceTracker.YangStoreTrackerListener {
+
+    private static final Logger logger = LoggerFactory.getLogger(Activator.class);
+
+    private BundleContext context;
+    ServiceRegistration osgiRegistration;
+
+    @Override
+    public void start(BundleContext context) throws Exception {
+        this.context = context;
+        YangStoreServiceTracker tracker = new YangStoreServiceTracker(context, this);
+        tracker.open();
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+    }
+
+    @Override
+    public synchronized void onYangStoreAdded(YangStoreService yangStoreService) {
+        checkState(osgiRegistration == null, "More than one onYangStoreAdded received");
+        NetconfOperationServiceFactoryImpl factory = new NetconfOperationServiceFactoryImpl(yangStoreService);
+        logger.debug("Registering into OSGi");
+        osgiRegistration = context.registerService(new String[]{NetconfOperationServiceFactory.class.getName()}, factory,
+                new Hashtable<String, Object>());
+    }
+
+    @Override
+    public synchronized void onYangStoreRemoved() {
+        osgiRegistration.unregister();
+        osgiRegistration = null;
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java
new file mode 100644 (file)
index 0000000..ec1915d
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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.confignetconfconnector.osgi;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.Commit;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.DiscardChanges;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.Validate;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.get.Get;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig.GetConfig;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.runtimerpc.RuntimeRpc;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+
+import java.util.Set;
+
+final class NetconfOperationProvider {
+    private final YangStoreSnapshot yangStoreSnapshot;
+    private final Set<NetconfOperation> operations;
+    private final ConfigRegistryClient configRegistryClient;
+    private final TransactionProvider transactionProvider;
+
+    NetconfOperationProvider(YangStoreSnapshot yangStoreSnapshot, ConfigRegistryClient configRegistryClient,
+            TransactionProvider transactionProvider, String netconfSessionIdForReporting) {
+
+        this.yangStoreSnapshot = yangStoreSnapshot;
+        this.configRegistryClient = configRegistryClient;
+        this.transactionProvider = transactionProvider;
+        operations = setUpOperations(yangStoreSnapshot, configRegistryClient, transactionProvider,
+                netconfSessionIdForReporting);
+    }
+
+    Set<NetconfOperation> getOperations() {
+        return operations;
+    }
+
+    private static Set<NetconfOperation> setUpOperations(YangStoreSnapshot yangStoreSnapshot,
+            ConfigRegistryClient configRegistryClient, TransactionProvider transactionProvider,
+            String netconfSessionIdForReporting) {
+        Set<NetconfOperation> ops = Sets.newHashSet();
+
+        GetConfig getConfigOp = new GetConfig(yangStoreSnapshot, Optional.<String> absent(), transactionProvider,
+                configRegistryClient, netconfSessionIdForReporting);
+
+        ops.add(getConfigOp);
+        ops.add(new EditConfig(yangStoreSnapshot, transactionProvider, configRegistryClient,
+                netconfSessionIdForReporting));
+        ops.add(new Commit(transactionProvider, configRegistryClient, netconfSessionIdForReporting));
+        ops.add(new Get(yangStoreSnapshot, configRegistryClient, netconfSessionIdForReporting));
+        ops.add(new DiscardChanges(transactionProvider, configRegistryClient, netconfSessionIdForReporting));
+        ops.add(new Validate(transactionProvider, configRegistryClient, netconfSessionIdForReporting));
+        ops.add(new RuntimeRpc(yangStoreSnapshot, configRegistryClient, netconfSessionIdForReporting));
+
+        return ops;
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceFactoryImpl.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceFactoryImpl.java
new file mode 100644 (file)
index 0000000..35695f7
--- /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.netconf.confignetconfconnector.osgi;
+
+import org.opendaylight.controller.config.util.ConfigRegistryJMXClient;
+import org.opendaylight.controller.config.yang.store.api.YangStoreException;
+import org.opendaylight.controller.config.yang.store.api.YangStoreService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.management.MBeanServer;
+import java.lang.management.ManagementFactory;
+
+public class NetconfOperationServiceFactoryImpl implements NetconfOperationServiceFactory {
+
+    public static final int ATTEMPT_TIMEOUT_MS = 1000;
+
+    private final YangStoreService yangStoreService;
+    private final ConfigRegistryJMXClient jmxClient;
+
+    private static final Logger logger = LoggerFactory.getLogger(NetconfOperationServiceFactoryImpl.class);
+
+    public NetconfOperationServiceFactoryImpl(YangStoreService yangStoreService) {
+        this(yangStoreService, ManagementFactory.getPlatformMBeanServer());
+    }
+
+    public NetconfOperationServiceFactoryImpl(YangStoreService yangStoreService, MBeanServer mBeanServer) {
+        this.yangStoreService = yangStoreService;
+
+        // Config registry might not be present yet, but will be eventually
+        while(true) {
+
+            final ConfigRegistryJMXClient configRegistryJMXClient;
+            try {
+                configRegistryJMXClient = new ConfigRegistryJMXClient(mBeanServer);
+            } catch (IllegalStateException e) {
+                logger.debug("Jmx client could not be created, reattempting");
+                try {
+                    Thread.sleep(ATTEMPT_TIMEOUT_MS);
+                } catch (InterruptedException e1) {
+                    throw new RuntimeException(e1);
+                }
+                continue;
+            }
+
+            jmxClient = configRegistryJMXClient;
+            break;
+        }
+    }
+
+    @Override
+    public NetconfOperationServiceImpl createService(long netconfSessionId, String netconfSessionIdForReporting) {
+        try {
+            return new NetconfOperationServiceImpl(yangStoreService, jmxClient, netconfSessionIdForReporting);
+        } catch (YangStoreException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImpl.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImpl.java
new file mode 100644 (file)
index 0000000..8497edb
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * 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.confignetconfconnector.osgi;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
+import org.opendaylight.controller.config.util.ConfigRegistryJMXClient;
+import org.opendaylight.controller.config.yang.store.api.YangStoreException;
+import org.opendaylight.controller.config.yang.store.api.YangStoreService;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.opendaylight.controller.netconf.mapping.api.Capability;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationFilter;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Manages life cycle of {@link YangStoreSnapshot}.
+ */
+public class NetconfOperationServiceImpl implements NetconfOperationService {
+
+    private final YangStoreSnapshot yangStoreSnapshot;
+    private final NetconfOperationProvider operationProvider;
+    private final Set<Capability> capabilities;
+    private final TransactionProvider transactionProvider;
+
+    public NetconfOperationServiceImpl(YangStoreService yangStoreService, ConfigRegistryJMXClient jmxClient,
+            String netconfSessionIdForReporting) throws YangStoreException {
+
+        yangStoreSnapshot = yangStoreService.getYangStoreSnapshot();
+        transactionProvider = new TransactionProvider(jmxClient, netconfSessionIdForReporting);
+        operationProvider = new NetconfOperationProvider(yangStoreSnapshot, jmxClient, transactionProvider,
+                netconfSessionIdForReporting);
+        capabilities = setupCapabilities(yangStoreSnapshot);
+    }
+
+    @Override
+    public void close() {
+        yangStoreSnapshot.close();
+        transactionProvider.close();
+    }
+
+    @Override
+    public Set<Capability> getCapabilities() {
+        return capabilities;
+    }
+
+    @Override
+    public Set<NetconfOperation> getNetconfOperations() {
+        return operationProvider.getOperations();
+    }
+
+    @Override
+    public Set<NetconfOperationFilter> getFilters() {
+        return Collections.emptySet();
+    }
+
+    private static Set<Capability> setupCapabilities(YangStoreSnapshot yangStoreSnapshot) {
+        Set<Capability> capabilities = Sets.newHashSet();
+
+        capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:candidate:1.0"));
+        capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:rollback-on-error:1.0"));
+        capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:operations:1.0"));
+        capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:operations:1.1"));
+        capabilities
+                .add(new BasicCapability(
+                        "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&amp;revision=2010-10-04"));
+
+        final Collection<Map.Entry<Module, String>> modulesAndContents = yangStoreSnapshot.getModuleMap().values();
+        for (Map.Entry<Module, String> moduleAndContent : modulesAndContents) {
+            capabilities.add(new YangStoreCapability(moduleAndContent));
+        }
+
+        return capabilities;
+    }
+
+    private static class BasicCapability implements Capability {
+
+        private final String capability;
+
+        private BasicCapability(String capability) {
+            this.capability = capability;
+        }
+
+        @Override
+        public String getCapabilityUri() {
+            return capability;
+        }
+
+        @Override
+        public Optional<String> getModuleName() {
+            return Optional.absent();
+        }
+
+        @Override
+        public Optional<String> getRevision() {
+            return Optional.absent();
+        }
+
+        @Override
+        public Optional<String> getCapabilitySchema() {
+            return Optional.absent();
+        }
+    }
+
+    private static class YangStoreCapability extends BasicCapability {
+
+        private final String content;
+        private final String revision;
+        private final String moduleName;
+
+        public YangStoreCapability(Map.Entry<Module, String> moduleAndContent) {
+            super(getAsString(moduleAndContent.getKey()));
+            this.content = moduleAndContent.getValue();
+            Module module = moduleAndContent.getKey();
+            this.moduleName = module.getName();
+            this.revision = Util.writeDate(module.getRevision());
+        }
+
+        @Override
+        public Optional<String> getCapabilitySchema() {
+            return Optional.of(content);
+        }
+
+        private static String getAsString(Module module) {
+            final StringBuffer capabilityContent = new StringBuffer();
+            capabilityContent.append(module.getNamespace());
+            capabilityContent.append("?module=");
+            capabilityContent.append(module.getName());
+            capabilityContent.append("&revision=");
+            capabilityContent.append(Util.writeDate(module.getRevision()));
+            return capabilityContent.toString();
+        }
+
+        @Override
+        public Optional<String> getModuleName() {
+            return Optional.of(moduleName);
+        }
+
+        @Override
+        public Optional<String> getRevision() {
+            return Optional.of(revision);
+        }
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/YangStoreServiceTracker.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/YangStoreServiceTracker.java
new file mode 100644 (file)
index 0000000..3b1e89d
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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.confignetconfconnector.osgi;
+
+import org.opendaylight.controller.config.yang.store.api.YangStoreService;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+class YangStoreServiceTracker extends ServiceTracker<YangStoreService, YangStoreService> {
+    private final YangStoreTrackerListener listener;
+
+    YangStoreServiceTracker(BundleContext context, final YangStoreTrackerListener listener) {
+        super(context, YangStoreService.class, null);
+        this.listener = listener;
+    }
+
+    @Override
+    public synchronized YangStoreService addingService(final ServiceReference<YangStoreService> reference) {
+        final YangStoreService yangStoreService = super.addingService(reference);
+        listener.onYangStoreAdded(yangStoreService);
+        return yangStoreService;
+    }
+
+    @Override
+    public synchronized void removedService(final ServiceReference<YangStoreService> reference,
+            final YangStoreService service) {
+        listener.onYangStoreRemoved();
+    }
+
+    static interface YangStoreTrackerListener {
+        void onYangStoreAdded(YangStoreService yangStoreService);
+        void onYangStoreRemoved();
+    }
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/transactions/TransactionProvider.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/transactions/TransactionProvider.java
new file mode 100644 (file)
index 0000000..b3483a7
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * 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.confignetconfconnector.transactions;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class TransactionProvider implements AutoCloseable {
+    private static final Logger logger = LoggerFactory.getLogger(TransactionProvider.class);
+
+    private final ConfigRegistryClient configRegistryClient;
+
+    private final String netconfSessionIdForReporting;
+    private ObjectName transaction;
+    private final List<ObjectName> allOpenedTransactions = new ArrayList<>();
+
+    public TransactionProvider(ConfigRegistryClient configRegistryClient, String netconfSessionIdForReporting) {
+        this.configRegistryClient = configRegistryClient;
+        this.netconfSessionIdForReporting = netconfSessionIdForReporting;
+    }
+
+    @Override
+    public synchronized void close() {
+        for (ObjectName tx : allOpenedTransactions) {
+            if (isStillOpenTransaction(tx)) {
+                try {
+                    configRegistryClient.getConfigTransactionClient(tx).abortConfig();
+                } catch (Exception e) {
+                    logger.debug("Ignoring {} while closing transaction {}", e.toString(), tx, e);
+                }
+            }
+        }
+        allOpenedTransactions.clear();
+    }
+
+    public Optional<ObjectName> getTransaction() {
+
+        if (transaction == null)
+            return Optional.absent();
+
+        // Transaction was already closed somehow
+        if (isStillOpenTransaction(transaction) == false) {
+            logger.warn("Fixing illegal state: transaction {} was closed in {}", transaction,
+                    netconfSessionIdForReporting);
+            transaction = null;
+            return Optional.absent();
+        }
+        return Optional.of(transaction);
+    }
+
+    private boolean isStillOpenTransaction(ObjectName transaction) {
+        boolean isStillOpenTransaction = configRegistryClient.getOpenConfigs().contains(transaction);
+        return isStillOpenTransaction;
+    }
+
+    public synchronized ObjectName getOrCreateTransaction() {
+        Optional<ObjectName> ta = getTransaction();
+
+        if (ta.isPresent())
+            return ta.get();
+        transaction = configRegistryClient.beginConfig();
+        allOpenedTransactions.add(transaction);
+        return transaction;
+    }
+
+    /**
+     * Used for editConfig test option
+     */
+    public synchronized ObjectName getTestTransaction() {
+        ObjectName testTx = configRegistryClient.beginConfig();
+        allOpenedTransactions.add(testTx);
+        return testTx;
+    }
+
+    /**
+     * Commit and notification send must be atomic
+     */
+    public synchronized CommitStatus commitTransaction() throws NetconfDocumentedException {
+        final Optional<ObjectName> taON = getTransaction();
+        Preconditions.checkState(taON.isPresent(), "No transaction found for session " + netconfSessionIdForReporting);
+        CommitStatus status = configRegistryClient.commitConfig(taON.get());
+        allOpenedTransactions.remove(transaction);
+        transaction = null;
+        return status;
+    }
+
+    public synchronized void abortTransaction() {
+        Optional<ObjectName> taON = getTransaction();
+        Preconditions.checkState(taON.isPresent(), "No transaction found for session " + netconfSessionIdForReporting);
+
+        ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get());
+        transactionClient.abortConfig();
+        allOpenedTransactions.remove(transaction);
+        transaction = null;
+    }
+
+    public synchronized void abortTestTransaction(ObjectName testTx) {
+        ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(testTx);
+        allOpenedTransactions.remove(testTx);
+        transactionClient.abortConfig();
+    }
+
+    public void validateTransaction() throws ValidationException {
+        Optional<ObjectName> taON = getTransaction();
+        Preconditions.checkState(taON.isPresent(), "No transaction found for session " + netconfSessionIdForReporting);
+
+        ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get());
+        transactionClient.validateConfig();
+    }
+
+    public void validateTestTransaction(ObjectName taON) {
+        ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON);
+        transactionClient.validateConfig();
+    }
+
+    public void wipeTestTransaction(ObjectName taON) {
+        wipeInternal(taON, true, null);
+    }
+
+    /**
+     * Wiping means removing all module instances keeping the transaction open.
+     */
+    synchronized void wipeInternal(ObjectName taON, boolean isTest, String moduleName) {
+        ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON);
+
+        Set<ObjectName> lookupConfigBeans = moduleName == null ? transactionClient.lookupConfigBeans()
+                : transactionClient.lookupConfigBeans(moduleName);
+        for (ObjectName instance : lookupConfigBeans) {
+            try {
+                transactionClient.destroyModule(instance);
+            } catch (InstanceNotFoundException e) {
+                if (isTest)
+                    logger.debug("Unable to clean configuration in transactiom {}", taON, e);
+                else
+                    logger.warn("Unable to clean configuration in transactiom {}", taON, e);
+
+                throw new IllegalStateException("Unable to clean configuration in transactiom " + taON, e);
+            }
+        }
+        logger.debug("Transaction {} wiped clean", taON);
+    }
+
+    public void wipeTransaction() {
+        Optional<ObjectName> taON = getTransaction();
+        Preconditions.checkState(taON.isPresent(), "No transaction found for session " + netconfSessionIdForReporting);
+        wipeInternal(taON.get(), false, null);
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/util/Util.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/util/Util.java
new file mode 100644 (file)
index 0000000..2671959
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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.confignetconfconnector.util;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.config.yang.store.api.YangStoreException;
+import org.opendaylight.controller.config.yang.store.api.YangStoreService;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public final class Util {
+
+    /**
+     * Used for date <-> xml serialization
+     */
+    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+
+    public static String writeDate(final Date date) {
+        return dateFormat.format(date);
+    }
+
+    public static Date readDate(final String s) throws ParseException {
+        return dateFormat.parse(s);
+    }
+
+    public static void checkType(final Object value, final Class<?> clazz) {
+        Preconditions.checkArgument(clazz.isAssignableFrom(value.getClass()), "Unexpected type " + value.getClass()
+                + " should be " + clazz);
+    }
+
+    // TODO: add message and proper error types
+    public static YangStoreSnapshot getYangStore(final YangStoreService yangStoreService)
+            throws NetconfDocumentedException {
+        try {
+            return yangStoreService.getYangStoreSnapshot();
+        } catch (final YangStoreException e) {
+            throw new NetconfDocumentedException("TODO", e, ErrorType.application, ErrorTag.bad_attribute,
+                    ErrorSeverity.error);
+        }
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java
new file mode 100644 (file)
index 0000000..c72cb74
--- /dev/null
@@ -0,0 +1,659 @@
+/*
+ * 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.confignetconfconnector;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.matchers.JUnitMatchers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.runtime.RootRuntimeBeanRegistrator;
+import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.manager.impl.jmx.RootRuntimeBeanRegistratorImpl;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.opendaylight.controller.config.yang.store.impl.MbeParser;
+import org.opendaylight.controller.config.yang.test.impl.*;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.Commit;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.DiscardChanges;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.get.Get;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig.GetConfig;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.runtimerpc.RuntimeRpc;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession;
+import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.util.*;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+public class NetconfMappingTest extends AbstractConfigTest {
+    private static final Logger logger = LoggerFactory.getLogger(NetconfMappingTest.class);
+
+    private static final String INSTANCE_NAME = "test1";
+    private static final String NETCONF_SESSION_ID = "foo";
+    private NetconfTestImplModuleFactory factory;
+    private DepTestImplModuleFactory factory2;
+
+    @Mock
+    YangStoreSnapshot yangStoreSnapshot;
+    @Mock
+    NetconfOperationRouter netconfOperationRouter;
+
+    private TransactionProvider transactionProvider;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        doReturn(getMbes()).when(this.yangStoreSnapshot).getModuleMXBeanEntryMap();
+        this.factory = new NetconfTestImplModuleFactory();
+        this.factory2 = new DepTestImplModuleFactory();
+        super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(this.factory, this.factory2));
+
+        transactionProvider = new TransactionProvider(this.configRegistryClient, NETCONF_SESSION_ID);
+    }
+
+    private ObjectName createModule(final String instanceName) throws InstanceAlreadyExistsException {
+        final ConfigTransactionJMXClient transaction = this.configRegistryClient.createTransaction();
+
+        final ObjectName on = transaction.createModule(this.factory.getImplementationName(), instanceName);
+        final NetconfTestImplModuleMXBean mxBean = transaction.newMXBeanProxy(on, NetconfTestImplModuleMXBean.class);
+        setModule(mxBean, transaction);
+
+        transaction.commit();
+        return on;
+    }
+
+    @Test
+    public void testConfigNetconf() throws Exception {
+
+        createModule(INSTANCE_NAME);
+
+        edit("netconfMessages/editConfig.xml");
+        checkBinaryLeafEdited(getConfigCandidate());
+
+        // default-operation:none, should not affect binary leaf
+        edit("netconfMessages/editConfig_none.xml");
+        checkBinaryLeafEdited(getConfigCandidate());
+
+        // check after edit
+        commit();
+        Element response = getConfigRunning();
+        // System.out.println(Xml.toString(response));
+
+        checkBinaryLeafEdited(response);
+        checkTypeConfigAttribute(response);
+
+        edit("netconfMessages/editConfig_remove.xml");
+
+        commit();
+        response = getConfigCandidate();
+        final String responseFromCandidate = XmlUtil.toString(response).replaceAll("\\s+", "");
+        // System.out.println(responseFromCandidate);
+        response = getConfigRunning();
+        final String responseFromRunning = XmlUtil.toString(response).replaceAll("\\s+", "");
+        // System.out.println(responseFromRunning);
+        assertEquals(responseFromCandidate, responseFromRunning);
+
+        final String expectedResult = XmlFileLoader.fileToString("netconfMessages/editConfig_expectedResult.xml")
+                .replaceAll("\\s+", "");
+
+        assertEquals(expectedResult, responseFromRunning);
+        assertEquals(expectedResult, responseFromCandidate);
+
+        edit("netconfMessages/editConfig_none.xml");
+        doNothing().when(netconfOperationRouter).close();
+        closeSession();
+        verify(netconfOperationRouter).close();
+        verifyNoMoreInteractions(netconfOperationRouter);
+    }
+
+    private void closeSession() throws NetconfDocumentedException, ParserConfigurationException, SAXException,
+            IOException {
+        DefaultCloseSession closeOp = new DefaultCloseSession(NETCONF_SESSION_ID);
+        executeOp(closeOp, "netconfMessages/closeSession.xml");
+    }
+
+    private void edit(String resource) throws ParserConfigurationException, SAXException, IOException,
+            NetconfDocumentedException {
+        EditConfig editOp = new EditConfig(yangStoreSnapshot, transactionProvider, configRegistryClient,
+                NETCONF_SESSION_ID);
+        executeOp(editOp, resource);
+    }
+
+    private void commit() throws ParserConfigurationException, SAXException, IOException, NetconfDocumentedException {
+        Commit commitOp = new Commit(transactionProvider, configRegistryClient, NETCONF_SESSION_ID);
+        executeOp(commitOp, "netconfMessages/commit.xml");
+    }
+
+    private Element getConfigCandidate() throws ParserConfigurationException, SAXException, IOException,
+            NetconfDocumentedException {
+        GetConfig getConfigOp = new GetConfig(yangStoreSnapshot, Optional.<String> absent(), transactionProvider,
+                configRegistryClient, NETCONF_SESSION_ID);
+        return executeOp(getConfigOp, "netconfMessages/getConfig_candidate.xml");
+    }
+
+    private Element getConfigRunning() throws ParserConfigurationException, SAXException, IOException,
+            NetconfDocumentedException {
+        GetConfig getConfigOp = new GetConfig(yangStoreSnapshot, Optional.<String> absent(), transactionProvider,
+                configRegistryClient, NETCONF_SESSION_ID);
+        return executeOp(getConfigOp, "netconfMessages/getConfig.xml");
+    }
+
+    @Test(expected = NetconfDocumentedException.class)
+    public void testConfigNetconfReplaceDefaultEx() throws Exception {
+
+        createModule(INSTANCE_NAME);
+
+        edit("netconfMessages/editConfig.xml");
+        edit("netconfMessages/editConfig_replace_default_ex.xml");
+    }
+
+    @Test
+    public void testConfigNetconfReplaceDefault() throws Exception {
+
+        createModule(INSTANCE_NAME);
+
+        edit("netconfMessages/editConfig.xml");
+        commit();
+        Element response = getConfigRunning();
+        final int allInstances = response.getElementsByTagName("module").getLength();
+
+        edit("netconfMessages/editConfig_replace_default.xml");
+
+        commit();
+        response = getConfigRunning();
+
+        final int afterReplace = response.getElementsByTagName("module").getLength();
+        assertEquals(4, allInstances);
+        assertEquals(2, afterReplace);
+    }
+
+    @Test(expected = NetconfDocumentedException.class)
+    public void testSameAttrDifferentNamespaces() throws Exception {
+        try {
+            edit("netconfMessages/namespaces/editConfig_sameAttrDifferentNamespaces.xml");
+        } catch (NetconfDocumentedException e) {
+            String message = e.getMessage();
+            assertThat(message,
+                    JUnitMatchers
+                            .containsString("Element simple-long-2 present multiple times with different namespaces"));
+            assertThat(message,
+                    JUnitMatchers.containsString("urn:opendaylight:params:xml:ns:yang:controller:test:impl"));
+            assertThat(message,
+                    JUnitMatchers
+                            .containsString(XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG));
+            throw e;
+        }
+    }
+
+    @Test(expected = NetconfDocumentedException.class)
+    public void testDifferentNamespaceInTO() throws Exception {
+        try {
+            edit("netconfMessages/namespaces/editConfig_differentNamespaceTO.xml");
+        } catch (NetconfDocumentedException e) {
+            String message = e.getMessage();
+            assertThat(message, JUnitMatchers.containsString("Unrecognised elements"));
+            assertThat(message, JUnitMatchers.containsString("simple-int2"));
+            assertThat(message, JUnitMatchers.containsString("dto_d"));
+            throw e;
+        }
+    }
+
+    @Test(expected = NetconfDocumentedException.class)
+    public void testSameAttrDifferentNamespacesList() throws Exception {
+        try {
+            edit("netconfMessages/namespaces/editConfig_sameAttrDifferentNamespacesList.xml");
+        } catch (NetconfDocumentedException e) {
+            String message = e.getMessage();
+            assertThat(message,
+                    JUnitMatchers.containsString("Element binaryLeaf present multiple times with different namespaces"));
+            assertThat(message,
+                    JUnitMatchers.containsString("urn:opendaylight:params:xml:ns:yang:controller:test:impl"));
+            assertThat(message,
+                    JUnitMatchers
+                            .containsString(XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG));
+            throw e;
+        }
+    }
+
+    @Test
+    public void testTypeNameConfigAttributeMatching() throws Exception {
+        edit("netconfMessages/editConfig.xml");
+        commit();
+        edit("netconfMessages/namespaces/editConfig_typeNameConfigAttributeMatching.xml");
+        commit();
+
+        Element response = getConfigRunning();
+        checkTypeConfigAttribute(response);
+    }
+
+    // TODO add <modules operation="replace"> functionality
+    @Test(expected = NetconfDocumentedException.class)
+    public void testConfigNetconfReplaceModuleEx() throws Exception {
+
+        createModule(INSTANCE_NAME);
+
+        edit("netconfMessages/editConfig.xml");
+        edit("netconfMessages/editConfig_replace_module_ex.xml");
+    }
+
+    @Test
+    public void testUnrecognisedConfigElements() throws Exception {
+
+        String format = "netconfMessages/unrecognised/editConfig_unrecognised%d.xml";
+        final int TESTS_COUNT = 8;
+
+        for (int i = 0; i < TESTS_COUNT; i++) {
+            String file = String.format(format, i + 1);
+            try {
+                edit(file);
+            } catch (NetconfDocumentedException e) {
+                Assert.assertThat(e.getMessage(), JUnitMatchers.containsString("Unrecognised elements"));
+                Assert.assertThat(e.getMessage(), JUnitMatchers.containsString("unknownAttribute"));
+                continue;
+            }
+            fail("Unrecognised test should throw exception " + file);
+        }
+    }
+
+    @Test
+    @Ignore
+    // FIXME
+    public void testConfigNetconfReplaceModule() throws Exception {
+
+        createModule(INSTANCE_NAME);
+
+        edit("netconfMessages/editConfig.xml");
+        commit();
+        Element response = getConfigRunning();
+        final int allInstances = response.getElementsByTagName("instance").getLength();
+
+        edit("netconfMessages/editConfig_replace_module.xml");
+
+        commit();
+        response = getConfigRunning();
+        final int afterReplace = response.getElementsByTagName("instance").getLength();
+
+        Assert.assertEquals(4 + 4 /* Instances from services */, allInstances);
+        Assert.assertEquals(3 + 3, afterReplace);
+    }
+
+    @Test(expected = NetconfDocumentedException.class)
+    public void testEx() throws Exception {
+
+        commit();
+    }
+
+    @Test(expected = NetconfDocumentedException.class)
+    public void testEx2() throws Exception {
+        discard();
+    }
+
+    private void discard() throws ParserConfigurationException, SAXException, IOException, NetconfDocumentedException {
+        DiscardChanges discardOp = new DiscardChanges(transactionProvider, configRegistryClient, NETCONF_SESSION_ID);
+        executeOp(discardOp, "netconfMessages/discardChanges.xml");
+    }
+
+    private void checkBinaryLeafEdited(final Element response) {
+        final NodeList children = response.getElementsByTagName("binaryLeaf");
+        assertEquals(3, children.getLength());
+        final StringBuffer buf = new StringBuffer();
+        for (int i = 0; i < 3; i++) {
+            final Element e = (Element) children.item(i);
+            buf.append(XmlElement.fromDomElement(e).getTextContent());
+        }
+        assertEquals("810", buf.toString());
+
+    }
+
+    private void checkTypeConfigAttribute(Element response) {
+
+        XmlElement modulesElement = XmlElement.fromDomElement(response).getOnlyChildElement("data")
+                .getOnlyChildElement("modules");
+
+        XmlElement configAttributeType = null;
+        for (XmlElement moduleElement : modulesElement.getChildElements("module")) {
+            for (XmlElement type : moduleElement.getChildElements("type")) {
+                if (type.getAttribute(XmlUtil.XMLNS_ATTRIBUTE_KEY).equals("") == false) {
+                    configAttributeType = type;
+                }
+            }
+        }
+
+        assertEquals("configAttributeType", configAttributeType.getTextContent());
+    }
+
+    private Map<String, Map<String, ModuleMXBeanEntry>> getMbes() throws Exception {
+        final List<InputStream> yangDependencies = getYangs();
+
+        final Map<String, Map<String, ModuleMXBeanEntry>> mBeanEntries = Maps.newHashMap();
+        mBeanEntries.putAll(new MbeParser().parseYangFiles(yangDependencies).getModuleMXBeanEntryMap());
+
+        return mBeanEntries;
+    }
+
+    @Test
+    public void testConfigNetconfRuntime() throws Exception {
+
+        ModuleIdentifier id = new ModuleIdentifier(NetconfTestImplModuleFactory.NAME, "instance");
+        RootRuntimeBeanRegistrator rootReg = new RootRuntimeBeanRegistratorImpl(internalJmxRegistrator, id);
+        NetconfTestImplRuntimeRegistrator registrator = new NetconfTestImplRuntimeRegistrator(rootReg);
+
+        NetconfTestImplRuntimeRegistration a = registerRoot(registrator);
+        InnerRunningDataRuntimeRegistration reg = registerInner(a);
+        registerInner2(reg);
+
+        id = new ModuleIdentifier(NetconfTestImplModuleFactory.NAME, "instance2");
+        rootReg = new RootRuntimeBeanRegistratorImpl(internalJmxRegistrator, id);
+        registrator = new NetconfTestImplRuntimeRegistrator(rootReg);
+
+        a = registerRoot(registrator);
+        registerAdditional(a);
+        registerAdditional(a);
+        registerAdditional(a);
+        registerAdditional(a);
+        reg = registerInner(a);
+        registerInner2(reg);
+        reg = registerInner(a);
+        registerInner2(reg);
+        registerInner2(reg);
+        reg = registerInner(a);
+        registerInner2(reg);
+        registerInner2(reg);
+        registerInner2(reg);
+        reg = registerInner(a);
+        registerInner2(reg);
+        registerInner2(reg);
+        registerInner2(reg);
+        registerInner2(reg);
+
+        Element response = get();
+
+        assertEquals(2, getElementsSize(response, "instance"));
+        assertEquals(2, getElementsSize(response, "asdf"));
+        assertEquals(5, getElementsSize(response, "inner-running-data"));
+        assertEquals(5, getElementsSize(response, "deep2"));
+        assertEquals(11, getElementsSize(response, "inner-inner-running-data"));
+        assertEquals(11, getElementsSize(response, "deep3"));
+        assertEquals(4, getElementsSize(response, "inner-running-data-additional"));
+        assertEquals(4, getElementsSize(response, "deep4"));
+        // TODO assert keys
+
+        RuntimeRpc netconf = new RuntimeRpc(yangStoreSnapshot, configRegistryClient, NETCONF_SESSION_ID);
+
+        response = executeOp(netconf, "netconfMessages/rpc.xml");
+        assertThat(XmlUtil.toString(response), JUnitMatchers.containsString("testarg1".toUpperCase()));
+
+        response = executeOp(netconf, "netconfMessages/rpcInner.xml");
+        assertThat(XmlUtil.toString(response), JUnitMatchers.containsString("ok"));
+
+        response = executeOp(netconf, "netconfMessages/rpcInnerInner.xml");
+        assertThat(XmlUtil.toString(response), JUnitMatchers.containsString("true"));
+    }
+
+    private Element get() throws NetconfDocumentedException, ParserConfigurationException, SAXException, IOException {
+        Get getOp = new Get(yangStoreSnapshot, configRegistryClient, NETCONF_SESSION_ID);
+        return executeOp(getOp, "netconfMessages/get.xml");
+    }
+
+    private int getElementsSize(Element response, String elementName) {
+        return response.getElementsByTagName(elementName).getLength();
+    }
+
+    private Object registerAdditional(final NetconfTestImplRuntimeRegistration a) {
+        class InnerRunningDataAdditionalRuntimeMXBeanTest implements InnerRunningDataAdditionalRuntimeMXBean {
+
+            private final int simpleInt;
+            private final String simpleString;
+
+            public InnerRunningDataAdditionalRuntimeMXBeanTest(final int simpleInt, final String simpleString) {
+                this.simpleInt = simpleInt;
+                this.simpleString = simpleString;
+            }
+
+            @Override
+            public Integer getSimpleInt3() {
+                return this.simpleInt;
+            }
+
+            @Override
+            public Deep4 getDeep4() {
+                final Deep4 d = new Deep4();
+                d.setBoool(false);
+                return d;
+            }
+
+            @Override
+            public String getSimpleString() {
+                return this.simpleString;
+            }
+
+            @Override
+            public void noArgInner() {
+            }
+
+        }
+
+        final int simpleInt = counter++;
+        return a.register(new InnerRunningDataAdditionalRuntimeMXBeanTest(simpleInt, "randomString_" + simpleInt));
+    }
+
+    private void registerInner2(final InnerRunningDataRuntimeRegistration reg) {
+        class InnerInnerRunningDataRuntimeMXBeanTest implements InnerInnerRunningDataRuntimeMXBean {
+
+            private final int simpleInt;
+
+            public InnerInnerRunningDataRuntimeMXBeanTest(final int simpleInt) {
+                this.simpleInt = simpleInt;
+            }
+
+            @Override
+            public List<NotStateBean> getNotStateBean() {
+                final NotStateBean notStateBean = new NotStateBean();
+                final NotStateBeanInternal notStateBeanInternal = new NotStateBeanInternal();
+                notStateBean.setNotStateBeanInternal(Lists.newArrayList(notStateBeanInternal));
+                return Lists.newArrayList(notStateBean);
+            }
+
+            @Override
+            public Integer getSimpleInt3() {
+                return this.simpleInt;
+            }
+
+            @Override
+            public Deep3 getDeep3() {
+                return new Deep3();
+            }
+
+            @Override
+            public Boolean noArgInnerInner(Integer integer, Boolean aBoolean) {
+                return aBoolean;
+            }
+
+        }
+
+        reg.register(new InnerInnerRunningDataRuntimeMXBeanTest(counter++));
+
+    }
+
+    private static int counter = 1000;
+
+    private InnerRunningDataRuntimeRegistration registerInner(final NetconfTestImplRuntimeRegistration a) {
+
+        class InnerRunningDataRuntimeMXBeanTest implements InnerRunningDataRuntimeMXBean {
+
+            private final int simpleInt;
+
+            public InnerRunningDataRuntimeMXBeanTest(final int simpleInt) {
+                this.simpleInt = simpleInt;
+            }
+
+            @Override
+            public Integer getSimpleInt3() {
+                return this.simpleInt;
+            }
+
+            @Override
+            public Deep2 getDeep2() {
+                return new Deep2();
+            }
+
+        }
+        return a.register(new InnerRunningDataRuntimeMXBeanTest(counter++));
+    }
+
+    private NetconfTestImplRuntimeRegistration registerRoot(final NetconfTestImplRuntimeRegistrator registrator) {
+        final NetconfTestImplRuntimeRegistration a = registrator.register(new NetconfTestImplRuntimeMXBean() {
+
+            @Override
+            public Long getCreatedSessions() {
+                return 11L;
+            }
+
+            @Override
+            public Asdf getAsdf() {
+                final Asdf asdf = new Asdf();
+                asdf.setSimpleInt(55);
+                asdf.setSimpleString("asdf");
+                return asdf;
+            }
+
+            @Override
+            public String noArg(final String arg1) {
+                return arg1.toUpperCase();
+            }
+
+        });
+        return a;
+    }
+
+    private Element executeOp(final NetconfOperation op, final String filename) throws ParserConfigurationException,
+            SAXException, IOException, NetconfDocumentedException {
+
+        final Document request = XmlFileLoader.xmlFileToDocument(filename);
+
+        logger.debug("Executing netconf operation\n{}", XmlUtil.toString(request));
+        HandlingPriority priority = op.canHandle(request);
+
+        Preconditions.checkState(priority != HandlingPriority.CANNOT_HANDLE);
+
+        final Document response = op.handle(request, netconfOperationRouter);
+        logger.debug("Got response\n{}", XmlUtil.toString(response));
+        return response.getDocumentElement();
+    }
+
+    private List<InputStream> getYangs() throws FileNotFoundException {
+        List<String> paths = Arrays.asList("/META-INF/yang/config.yang", "/META-INF/yang/rpc-context.yang",
+                "/META-INF/yang/config-test.yang", "/META-INF/yang/config-test-impl.yang",
+                "/META-INF/yang/ietf-inet-types.yang");
+        final Collection<InputStream> yangDependencies = new ArrayList<>();
+        for (String path : paths) {
+            final InputStream is = Preconditions
+                    .checkNotNull(getClass().getResourceAsStream(path), path + " not found");
+            yangDependencies.add(is);
+        }
+        return Lists.newArrayList(yangDependencies);
+    }
+
+    private void setModule(final NetconfTestImplModuleMXBean mxBean, final ConfigTransactionJMXClient transaction)
+            throws InstanceAlreadyExistsException {
+        mxBean.setSimpleInt((long) 44);
+        mxBean.setBinaryLeaf(new byte[] { 8, 7, 9 });
+        final DtoD dtob = getDtoD();
+        mxBean.setDtoD(dtob);
+        //
+        final DtoC dtoa = getDtoC();
+        mxBean.setDtoC(dtoa);
+        mxBean.setSimpleBoolean(false);
+        //
+        final Peers p1 = new Peers();
+        p1.setCoreSize(44L);
+        p1.setPort("port1");
+        p1.setSimpleInt3(456);
+        final Peers p2 = new Peers();
+        p2.setCoreSize(44L);
+        p2.setPort("port23");
+        p2.setSimpleInt3(456);
+        mxBean.setPeers(Lists.<Peers> newArrayList(p1, p2));
+        // //
+        mxBean.setSimpleLong(454545L);
+        mxBean.setSimpleLong2(44L);
+        mxBean.setSimpleBigInteger(BigInteger.valueOf(999L));
+        mxBean.setSimpleByte(new Byte((byte) 4));
+        mxBean.setSimpleShort(new Short((short) 4));
+        mxBean.setSimpleTest(545);
+
+        mxBean.setComplexList(Lists.<ComplexList> newArrayList());
+        mxBean.setSimpleList(Lists.<Integer> newArrayList());
+
+        final ObjectName testingDepOn = transaction.createModule(this.factory2.getImplementationName(), "dep");
+        mxBean.setTestingDep(testingDepOn);
+    }
+
+    private static DtoD getDtoD() {
+        final DtoD dtob = new DtoD();
+        dtob.setSimpleInt1((long) 444);
+        dtob.setSimpleInt2((long) 4444);
+        dtob.setSimpleInt3(454);
+        final ComplexDtoBInner dtobInner = new ComplexDtoBInner();
+        final Deep deep = new Deep();
+        deep.setSimpleInt3(4);
+        dtobInner.setDeep(deep);
+        dtobInner.setSimpleInt3(44);
+        dtobInner.setSimpleList(Lists.newArrayList(4));
+        dtob.setComplexDtoBInner(Lists.newArrayList(dtobInner));
+        dtob.setSimpleList(Lists.newArrayList(4));
+        return dtob;
+    }
+
+    private static DtoC getDtoC() {
+        final DtoC dtoa = new DtoC();
+        // dtoa.setSimpleArg((long) 55);
+        final DtoAInner dtoAInner = new DtoAInner();
+        final DtoAInnerInner dtoAInnerInner = new DtoAInnerInner();
+        dtoAInnerInner.setSimpleArg(456L);
+        dtoAInner.setDtoAInnerInner(dtoAInnerInner);
+        dtoAInner.setSimpleArg(44L);
+        dtoa.setDtoAInner(dtoAInner);
+        return dtoa;
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/ServiceTrackerTest.java b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/ServiceTrackerTest.java
new file mode 100644 (file)
index 0000000..e07f9ce
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector;
+
+import org.junit.Test;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services.ServiceInstance;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class ServiceTrackerTest {
+
+    @Test
+    public void test() {
+        Services.ServiceInstance serviceInstance = new ServiceInstance("module", "serviceInstance");
+
+        String string = serviceInstance.toString();
+
+        Services.ServiceInstance serviceInstance2 = Services.ServiceInstance.fromString(string);
+
+        assertEquals(serviceInstance, serviceInstance2);
+    }
+
+    @Test
+    public void testOneInstanceMultipleServices() {
+        Services services = new Services();
+        services.addServiceEntry("s1", "module", "instance");
+        assertEquals(1, services.getMappedServices().size());
+
+        services.addServiceEntry("s2", "module", "instance");
+        assertEquals(2, services.getMappedServices().size());
+    }
+
+    @Test
+    public void testMultipleInstancesOneName() throws Exception {
+        Services services = new Services();
+        services.addServiceEntry("s1", "module", "instance");
+        assertEquals(1, services.getMappedServices().size());
+
+        services.addServiceEntry("s1", "module2", "instance");
+        assertEquals(1, services.getMappedServices().size());
+        assertEquals(2, services.getMappedServices().get("s1").size());
+        assertTrue(services.getMappedServices().get("s1").containsKey("ref_instance"));
+        assertTrue(services.getMappedServices().get("s1").containsKey("ref_instance_1"));
+    }
+
+    @Test
+    public void testMultipleInstancesOneName2() throws Exception {
+        Services services = new Services();
+        services.addServiceEntry("s1", "module", "instance_1");
+
+        services.addServiceEntry("s2", "module2", "instance");
+        services.addServiceEntry("s2", "module3", "instance");
+        services.addServiceEntry("s1", "module3", "instance");
+
+        assertEquals(2, services.getMappedServices().get("s1").size());
+        assertEquals(2, services.getMappedServices().get("s2").size());
+        assertTrue(services.getMappedServices().get("s1").containsKey("ref_instance_2"));
+        assertTrue(services.getMappedServices().get("s1").containsKey("ref_instance_1"));
+        assertTrue(services.getMappedServices().get("s2").containsKey("ref_instance"));
+        assertTrue(services.getMappedServices().get("s2").containsKey("ref_instance_2"));
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/ValidateTest.java b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/ValidateTest.java
new file mode 100644 (file)
index 0000000..8d14e96
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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.confignetconfconnector.operations;
+
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+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.w3c.dom.Element;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.*;
+
+public class ValidateTest {
+
+    public static final String NETCONF_SESSION_ID_FOR_REPORTING = "foo";
+
+    @Test(expected = NetconfDocumentedException.class)
+    public void test() throws Exception {
+        final XmlElement xml = XmlElement.fromString("<abc></abc>");
+        final Validate validate = new Validate(null, null, NETCONF_SESSION_ID_FOR_REPORTING);
+        validate.handle(null, xml);
+    }
+
+    @Test(expected = NetconfDocumentedException.class)
+    public void testNoSource() throws Exception {
+        final XmlElement xml = XmlElement.fromString("<validate xmlns=\""
+                + XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0 + "\"/>");
+        final Validate validate = new Validate(null, null, NETCONF_SESSION_ID_FOR_REPORTING);
+        validate.handle(null, xml);
+    }
+
+    @Test(expected = NetconfDocumentedException.class)
+    public void testNoNamespace() throws Exception {
+        final XmlElement xml = XmlElement.fromString("<validate/>");
+        final Validate validate = new Validate(null, null, NETCONF_SESSION_ID_FOR_REPORTING);
+        validate.handle(null, xml);
+    }
+
+    @Test(expected = NetconfDocumentedException.class)
+    public void testRunningSource() throws Exception {
+
+        final XmlElement xml = XmlElement.fromString("<validate xmlns=\""
+                + XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0
+                + "\"><source><running></running></source></validate>");
+        final Validate validate = new Validate(null, null, NETCONF_SESSION_ID_FOR_REPORTING);
+        validate.handle(null, xml);
+    }
+
+    @Test(expected = NetconfDocumentedException.class)
+    public void testNoTransaction() throws Exception {
+        final XmlElement xml = XmlElement.fromString("<validate xmlns=\""
+                + XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0
+                + "\"><source><candidate/></source></validate>");
+        final TransactionProvider transactionProvider = mock(TransactionProvider.class);
+        doThrow(IllegalStateException.class).when(transactionProvider).validateTransaction();
+        final Validate validate = new Validate(transactionProvider, null, NETCONF_SESSION_ID_FOR_REPORTING);
+        validate.handle(null, xml);
+    }
+
+    @Test(expected = NetconfDocumentedException.class)
+    public void testValidationException() throws Exception {
+        final XmlElement xml = XmlElement.fromString("<validate xmlns=\""
+                + XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0
+                + "\">><source><candidate/></source></validate>");
+        final TransactionProvider transactionProvider = mock(TransactionProvider.class);
+        doThrow(ValidationException.class).when(transactionProvider).validateTransaction();
+        final Validate validate = new Validate(transactionProvider, null, NETCONF_SESSION_ID_FOR_REPORTING);
+        validate.handle(null, xml);
+    }
+
+    @Test
+    public void testValidation() throws Exception {
+        final XmlElement xml = XmlElement.fromString("<validate xmlns=\""
+                + XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0
+                + "\"><source><candidate/></source></validate>");
+        final TransactionProvider transactionProvider = mock(TransactionProvider.class);
+        final Element okElement = XmlUtil.readXmlToElement("<ok/>");
+        doNothing().when(transactionProvider).validateTransaction();
+        final Validate validate = new Validate(transactionProvider, null, NETCONF_SESSION_ID_FOR_REPORTING);
+        Element ok = validate.handle(XmlUtil.newDocument(), xml);
+        assertEquals(XmlUtil.toString(okElement), XmlUtil.toString(ok));
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigTest.java b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigTest.java
new file mode 100644 (file)
index 0000000..f43caf6
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 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.confignetconfconnector.operations.editconfig;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Config;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.InstanceConfigElementResolved;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.ModuleElementResolved;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfigStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfigXmlParser;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfigXmlParser.EditConfigExecution;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.ValidateTest;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+
+import javax.management.ObjectName;
+import java.util.Map;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyMap;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.*;
+
+public class EditConfigTest {
+
+    @Mock
+    private YangStoreSnapshot yangStoreSnapshot;
+    @Mock
+    private TransactionProvider provider;
+    @Mock
+    private ConfigRegistryClient configRegistry;
+    @Mock
+    private ConfigTransactionClient configTransactionClient;
+    @Mock
+    private ObjectName mockOn;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        doReturn("mockON").when(mockOn).toString();
+        doReturn(mockOn).when(provider).getTestTransaction();
+        doNothing().when(provider).validateTestTransaction(any(ObjectName.class));
+
+        doReturn(mockOn).when(provider).getTestTransaction();
+        doNothing().when(provider).abortTestTransaction(any(ObjectName.class));
+        doReturn(mockOn).when(provider).getOrCreateTransaction();
+
+        doReturn(configTransactionClient).when(configRegistry).getConfigTransactionClient(any(ObjectName.class));
+        doReturn("mockConfigTransactionClient").when(configTransactionClient).toString();
+
+        doReturn(mockOn).when(configTransactionClient).lookupConfigBean(anyString(), anyString());
+    }
+
+    @Test
+    public void test() throws NetconfDocumentedException {
+        EditConfig edit = new EditConfig(yangStoreSnapshot, provider, configRegistry,
+                ValidateTest.NETCONF_SESSION_ID_FOR_REPORTING);
+        EditConfigStrategy editStrat = mock(EditConfigStrategy.class);
+        doNothing().when(editStrat).executeConfiguration(anyString(), anyString(), anyMap(),
+                any(ConfigTransactionClient.class));
+        Map<String, Multimap<String, ModuleElementResolved>> resolvedXmlElements = getMapping(editStrat);
+
+        Config cfg = mock(Config.class);
+        XmlElement xmlElement = mock(XmlElement.class);
+        doReturn(resolvedXmlElements).when(cfg).fromXml(xmlElement);
+
+        EditConfigExecution editConfigExecution = new EditConfigExecution(null, cfg, xmlElement,
+                EditConfigXmlParser.TestOption.testThenSet);
+
+        edit.getResponseInternal(XmlUtil.newDocument(), editConfigExecution);
+
+        verify(provider).getTestTransaction();
+        verify(provider).validateTestTransaction(mockOn);
+        verify(provider).abortTestTransaction(mockOn);
+
+        verify(provider).getOrCreateTransaction();
+
+        // For every instance execute strat
+        verify(editStrat, times(2/* Test */+ 2/* Set */)).executeConfiguration(anyString(), anyString(), anyMap(),
+                any(ConfigTransactionClient.class));
+    }
+
+    private Map<String, Multimap<String, ModuleElementResolved>> getMapping(EditConfigStrategy editStrat) {
+        Map<String, Multimap<String, ModuleElementResolved>> result = Maps.newHashMap();
+
+        Multimap<String, ModuleElementResolved> innerMultimap = HashMultimap.create();
+        Map<String, AttributeConfigElement> attributes = getSimpleAttributes();
+
+        InstanceConfigElementResolved ice1 = mock(InstanceConfigElementResolved.class);
+        doReturn(attributes).when(ice1).getConfiguration();
+        doReturn(editStrat).when(ice1).getEditStrategy();
+        innerMultimap.put("m1", new ModuleElementResolved("i1", ice1));
+
+        InstanceConfigElementResolved ice2 = mock(InstanceConfigElementResolved.class);
+        doReturn(attributes).when(ice2).getConfiguration();
+        doReturn(editStrat).when(ice2).getEditStrategy();
+        innerMultimap.put("m1", new ModuleElementResolved("i2", ice2));
+
+        result.put("n1", innerMultimap);
+
+        return result;
+    }
+
+    static Map<String, AttributeConfigElement> getSimpleAttributes() {
+        Map<String, AttributeConfigElement> attributes = Maps.newHashMap();
+        AttributeConfigElement ace1 = mock(AttributeConfigElement.class);
+        doReturn("abcd").when(ace1).getResolvedDefaultValue();
+        doReturn(Optional.<String> of("abc")).when(ace1).getResolvedValue();
+        doReturn("mockedAce1").when(ace1).toString();
+        doReturn("jmxNameAce1").when(ace1).getJmxName();
+        attributes.put("a1", ace1);
+        return attributes;
+    }
+
+}
diff --git a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/ReplaceEditConfigStrategyTest.java b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/ReplaceEditConfigStrategyTest.java
new file mode 100644 (file)
index 0000000..13421a2
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.*;
+
+import java.util.Map;
+
+import javax.management.Attribute;
+import javax.management.ObjectName;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
+
+import com.google.common.collect.Sets;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfigTest;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.ReplaceEditConfigStrategy;
+
+public class ReplaceEditConfigStrategyTest {
+
+    @Mock
+    private ConfigTransactionClient ta;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        doNothing().when(ta).destroyConfigBean(anyString(), anyString());
+        doReturn(mockON()).when(ta).lookupConfigBean(anyString(), anyString());
+        doNothing().when(ta).setAttribute(any(ObjectName.class), anyString(), any(Attribute.class));
+    }
+
+    @Test
+    public void test() throws Exception {
+        ReplaceEditConfigStrategy strat = new ReplaceEditConfigStrategy();
+
+        Map<String, AttributeConfigElement> map = EditConfigTest.getSimpleAttributes();
+
+        doReturn(Sets.newHashSet(mockON(), mockON())).when(ta).lookupConfigBeans();
+
+        strat.executeConfiguration("m1", "i1", map, ta);
+
+        verify(ta).lookupConfigBean(anyString(), anyString());
+        verify(ta).setAttribute(any(ObjectName.class), anyString(), any(Attribute.class));
+    }
+
+    ObjectName mockON() {
+        ObjectName mock = mock(ObjectName.class);
+        doReturn("mockON").when(mock).toString();
+        return mock;
+    }
+
+}
diff --git a/opendaylight/netconf/config-persister-impl/pom.xml b/opendaylight/netconf/config-persister-impl/pom.xml
new file mode 100644 (file)
index 0000000..c86202b
--- /dev/null
@@ -0,0 +1,98 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>netconf-subsystem</artifactId>
+        <groupId>org.opendaylight.controller</groupId>
+        <version>0.2.1-SNAPSHOT</version>
+    </parent>
+    <artifactId>config-persister-impl</artifactId>
+    <name>${project.artifactId}</name>
+    <packaging>bundle</packaging>
+
+    <dependencies>
+        <!-- compile dependencies -->
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>config-persister-api</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-client</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-util</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>config-persister-file-adapter</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+        </dependency>
+
+        <!-- test dependencies -->
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>mockito-configuration</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.netconf.persist.impl.osgi.ConfigPersisterActivator
+                        </Bundle-Activator>
+                        <Require-Capability>org.opendaylight.controller.config.persister.storage.adapter
+                        </Require-Capability>
+                        <Import-Package>
+                            com.google.common.base,
+                            com.google.common.collect,
+                            javax.management,
+                            javax.xml.parsers,
+                            org.opendaylight.controller.config.persist.api,
+                            org.opendaylight.controller.config.stat,
+                            org.opendaylight.controller.config.persist.api.storage,
+                            org.opendaylight.controller.netconf.api,
+                            org.opendaylight.controller.netconf.api.jmx,
+                            org.opendaylight.controller.netconf.client,
+                            org.opendaylight.controller.netconf.util.osgi,
+                            org.opendaylight.controller.netconf.util.xml,
+                            org.osgi.framework,
+                            org.slf4j,
+                            org.w3c.dom,
+                            org.xml.sax,
+                        </Import-Package>
+                        <Export-Package>
+                        </Export-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java
new file mode 100644 (file)
index 0000000..c01a225
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.persist.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
+import org.opendaylight.controller.config.persist.api.Persister;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.jmx.CommitJMXNotification;
+import org.opendaylight.controller.netconf.api.jmx.DefaultCommitOperationMXBean;
+import org.opendaylight.controller.netconf.api.jmx.NetconfJMXNotification;
+import org.opendaylight.controller.netconf.client.NetconfClient;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+import javax.annotation.concurrent.ThreadSafe;
+import javax.management.*;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Responsible for listening for notifications from netconf containing latest
+ * committed configuration that should be persisted, and also for loading last
+ * configuration.
+ */
+@ThreadSafe
+public class ConfigPersisterNotificationHandler implements NotificationListener, Closeable {
+
+    private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterNotificationHandler.class);
+
+    private final InetSocketAddress address;
+
+    private NetconfClient netconfClient;
+
+    private final Persister persister;
+    private final MBeanServerConnection mbeanServer;
+    private Long currentSessionId;
+
+    private final ObjectName on = DefaultCommitOperationMXBean.objectName;
+
+    public static final long DEFAULT_TIMEOUT = 40000L;
+    private final long timeout;
+
+    public ConfigPersisterNotificationHandler(Persister persister, InetSocketAddress address,
+            MBeanServerConnection mbeanServer) {
+        this(persister, address, mbeanServer, DEFAULT_TIMEOUT);
+    }
+
+    public ConfigPersisterNotificationHandler(Persister persister, InetSocketAddress address,
+            MBeanServerConnection mbeanServer, long timeout) {
+        this.persister = persister;
+        this.address = address;
+        this.mbeanServer = mbeanServer;
+        this.timeout = timeout;
+    }
+
+    public void init() throws InterruptedException {
+        Optional<Persister.ConfigSnapshotHolder> maybeConfig = loadLastConfig();
+
+        if (maybeConfig.isPresent()) {
+            logger.debug("Last config found {}", persister);
+
+            registerToNetconf(maybeConfig.get().getCapabilities());
+
+            final String configSnapshot = maybeConfig.get().getConfigSnapshot();
+            try {
+                pushLastConfig(XmlUtil.readXmlToElement(configSnapshot));
+            } catch (SAXException | IOException e) {
+                throw new IllegalStateException("Unable to load last config", e);
+            }
+
+        } else {
+            // this ensures that netconf is initialized, this is first
+            // connection
+            // this means we can register as listener for commit
+            registerToNetconf(Collections.<String>emptySet());
+
+            logger.info("No last config provided by backend storage {}", persister);
+        }
+        registerAsJMXListener();
+    }
+
+    private synchronized long registerToNetconf(Set<String> expectedCaps) throws InterruptedException {
+
+        Set<String> currentCapabilities = Sets.newHashSet();
+
+        // TODO think about moving capability subset check to netconf client
+        // could be utilized by integration tests
+
+        long pollingStart = System.currentTimeMillis();
+        int delay = 5000;
+
+        int attempt = 0;
+
+        while (true) {
+            attempt++;
+
+            try {
+                netconfClient = new NetconfClient(this.toString(), address, delay);
+                // TODO is this correct ex to catch ?
+            } catch (IllegalStateException e) {
+                logger.debug("Netconf {} was not initialized or is not stable, attempt {}", address, attempt, e);
+                Thread.sleep(delay);
+                continue;
+            }
+            currentCapabilities = netconfClient.getCapabilities();
+
+            if (isSubset(currentCapabilities, expectedCaps)) {
+                logger.debug("Hello from netconf stable with {} capabilities", currentCapabilities);
+                currentSessionId = netconfClient.getSessionId();
+                logger.info("Session id received from netconf server: {}", currentSessionId);
+                return currentSessionId;
+            }
+
+            if (System.currentTimeMillis() > pollingStart + timeout) {
+                break;
+            }
+
+            logger.debug("Polling hello from netconf, attempt {}, capabilities {}", attempt, currentCapabilities);
+
+            try {
+                netconfClient.close();
+            } catch (IOException e) {
+                throw new RuntimeException("Error closing temporary client " + netconfClient);
+            }
+
+            Thread.sleep(delay);
+        }
+
+        throw new RuntimeException("Netconf server did not provide required capabilities " + expectedCaps
+                + " in time, provided capabilities " + currentCapabilities);
+
+    }
+
+    private boolean isSubset(Set<String> currentCapabilities, Set<String> expectedCaps) {
+        for (String exCap : expectedCaps) {
+            if (currentCapabilities.contains(exCap) == false)
+                return false;
+        }
+        return true;
+    }
+
+    private void registerAsJMXListener() {
+        try {
+            mbeanServer.addNotificationListener(on, this, null, null);
+        } catch (InstanceNotFoundException | IOException e) {
+            throw new RuntimeException("Cannot register as JMX listener to netconf", e);
+        }
+    }
+
+    @Override
+    public void handleNotification(Notification notification, Object handback) {
+        if (notification instanceof NetconfJMXNotification == false)
+            return;
+
+        // Socket should not be closed at this point
+        // Activator unregisters this as JMX listener before close is called
+
+        logger.debug("Received notification {}", notification);
+        if (notification instanceof CommitJMXNotification) {
+            try {
+                handleAfterCommitNotification((CommitJMXNotification) notification);
+            } catch (Exception e) {
+                // TODO: notificationBroadcast support logs only DEBUG
+                logger.warn("Exception occured during notification handling: ", e);
+                throw e;
+            }
+        } else
+            throw new IllegalStateException("Unknown config registry notification type " + notification);
+    }
+
+    private void handleAfterCommitNotification(final CommitJMXNotification notification) {
+        try {
+            final XmlElement configElement = XmlElement.fromDomElement(notification.getConfigSnapshot());
+            persister.persistConfig(new Persister.ConfigSnapshotHolder() {
+                @Override
+                public String getConfigSnapshot() {
+                    return XmlUtil.toString(configElement.getDomElement());
+                }
+
+                @Override
+                public Set<String> getCapabilities() {
+                    return notification.getCapabilities();
+                }
+            });
+            logger.debug("Configuration persisted successfully");
+        } catch (IOException e) {
+            throw new RuntimeException("Unable to persist configuration snapshot", e);
+        }
+    }
+
+    private Optional<Persister.ConfigSnapshotHolder> loadLastConfig() {
+        Optional<Persister.ConfigSnapshotHolder> maybeConfigElement;
+        try {
+            maybeConfigElement = persister.loadLastConfig();
+        } catch (IOException e) {
+            throw new RuntimeException("Unable to load configuration", e);
+        }
+        return maybeConfigElement;
+    }
+
+    private synchronized void pushLastConfig(Element persistedConfig) {
+        StringBuilder response = new StringBuilder("editConfig response = {");
+
+        Element configElement = persistedConfig;
+        NetconfMessage message = createEditConfigMessage(configElement, "/netconfOp/editConfig.xml");
+        NetconfMessage responseMessage = netconfClient.sendMessage(message);
+
+        XmlElement element = XmlElement.fromDomDocument(responseMessage.getDocument());
+        Preconditions.checkState(element.getName().equals(XmlNetconfConstants.RPC_REPLY_KEY));
+        element = element.getOnlyChildElement();
+
+        checkIsOk(element, responseMessage);
+        response.append(XmlUtil.toString(responseMessage.getDocument()));
+        response.append("}");
+        responseMessage = netconfClient.sendMessage(getNetconfMessageFromResource("/netconfOp/commit.xml"));
+
+        element = XmlElement.fromDomDocument(responseMessage.getDocument());
+        Preconditions.checkState(element.getName().equals(XmlNetconfConstants.RPC_REPLY_KEY));
+        element = element.getOnlyChildElement();
+
+        checkIsOk(element, responseMessage);
+        response.append("commit response = {");
+        response.append(XmlUtil.toString(responseMessage.getDocument()));
+        response.append("}");
+        logger.debug("Last configuration loaded successfully");
+    }
+
+    private void checkIsOk(XmlElement element, NetconfMessage responseMessage) {
+        if (element.getName().equals(XmlNetconfConstants.OK)) {
+            return;
+        } else {
+            if (element.getName().equals(XmlNetconfConstants.RPC_ERROR)) {
+                logger.warn("Can not load last configuration, operation failed");
+                throw new IllegalStateException("Can not load last configuration, operation failed: "
+                        + XmlUtil.toString(responseMessage.getDocument()));
+            }
+            logger.warn("Can not load last configuration. Operation failed.");
+            throw new IllegalStateException("Can not load last configuration. Operation failed: "
+                    + XmlUtil.toString(responseMessage.getDocument()));
+        }
+    }
+
+    private NetconfMessage createEditConfigMessage(Element dataElement, String editConfigResourcename) {
+        try (InputStream stream = getClass().getResourceAsStream(editConfigResourcename)) {
+            Preconditions.checkNotNull(stream, "Unable to load resource " + editConfigResourcename);
+
+            Document doc = XmlUtil.readXmlToDocument(stream);
+
+            doc.getDocumentElement();
+            XmlElement editConfigElement = XmlElement.fromDomDocument(doc).getOnlyChildElement();
+            XmlElement configWrapper = editConfigElement.getOnlyChildElement(XmlNetconfConstants.CONFIG_KEY);
+            editConfigElement.getDomElement().removeChild(configWrapper.getDomElement());
+            for (XmlElement el : XmlElement.fromDomElement(dataElement).getChildElements()) {
+                configWrapper.appendChild((Element) doc.importNode(el.getDomElement(), true));
+            }
+            editConfigElement.appendChild(configWrapper.getDomElement());
+            return new NetconfMessage(doc);
+        } catch (IOException | SAXException e) {
+            throw new RuntimeException("Unable to parse message from resources " + editConfigResourcename, e);
+        }
+    }
+
+    private NetconfMessage getNetconfMessageFromResource(String resource) {
+        try (InputStream stream = getClass().getResourceAsStream(resource)) {
+            Preconditions.checkNotNull(stream, "Unable to load resource " + resource);
+            return new NetconfMessage(XmlUtil.readXmlToDocument(stream));
+        } catch (SAXException | IOException e) {
+            throw new RuntimeException("Unable to parse message from resources " + resource, e);
+        }
+    }
+
+    @Override
+    public synchronized void close() {
+        // TODO persister is received from constructor, should not be closed
+        // here
+        try {
+            persister.close();
+        } catch (Exception e) {
+            logger.warn("Unable to close config persister {}", persister, e);
+        }
+
+        if (netconfClient != null) {
+            try {
+                netconfClient.close();
+            } catch (Exception e) {
+                logger.warn("Unable to close connection to netconf {}", netconfClient, e);
+            }
+        }
+
+        // unregister from JMX
+        try {
+            if (mbeanServer.isRegistered(on)) {
+                mbeanServer.removeNotificationListener(on, this);
+            }
+        } catch (Exception e) {
+            logger.warn("Unable to unregister {} as listener for {}", this, on, e);
+        }
+    }
+}
diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/NoOpStorageAdapter.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/NoOpStorageAdapter.java
new file mode 100644 (file)
index 0000000..08b0d1a
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.persist.impl;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.config.persist.api.storage.StorageAdapter;
+import org.opendaylight.controller.config.stat.ConfigProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+public class NoOpStorageAdapter implements StorageAdapter {
+    private static final Logger logger = LoggerFactory.getLogger(NoOpStorageAdapter.class);
+
+    @Override
+    public void setProperties(ConfigProvider configProvider) {
+        logger.debug("setProperties called with {}", configProvider);
+    }
+
+    @Override
+    public void persistConfig(ConfigSnapshotHolder holder) throws IOException {
+        logger.debug("persistConfig called with {}", holder);
+    }
+
+    @Override
+    public Optional<ConfigSnapshotHolder> loadLastConfig() throws IOException {
+        logger.debug("loadLastConfig called");
+        return Optional.absent();
+    }
+
+    @Override
+    public void close() throws IOException {
+        logger.debug("close called");
+    }
+}
diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterImpl.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterImpl.java
new file mode 100644 (file)
index 0000000..03892f0
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.persist.impl;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import org.opendaylight.controller.config.persist.api.Persister;
+import org.opendaylight.controller.config.persist.api.storage.StorageAdapter;
+import org.opendaylight.controller.config.stat.ConfigProvider;
+
+import java.io.IOException;
+
+/**
+ * {@link Persister} implementation that delegates persisting functionality to
+ * underlying {@link Persister} called Storage Adapter.
+ *
+ * Storage adapters are low level persisters that do the heavy lifting for this
+ * class. Instances of storage adapters can be injected directly via constructor
+ * or instantiated from a full name of its class provided in a properties file.
+ *
+ * Name of storage adapter class should be located under
+ * {@link #STORAGE_ADAPTER_CLASS_PROP} key.
+ */
+public final class PersisterImpl implements Persister {
+
+    public static final String STORAGE_ADAPTER_CLASS_PROP = "netconf.config.persister.storageAdapterClass";
+    private final StorageAdapter storage;
+
+    public PersisterImpl(StorageAdapter storage) {
+        this.storage = storage;
+    }
+
+    public static Optional<PersisterImpl> createFromProperties(ConfigProvider configProvider) {
+        String storageAdapterClass = configProvider.getProperty(STORAGE_ADAPTER_CLASS_PROP);
+        StorageAdapter storage;
+        if (storageAdapterClass == null || storageAdapterClass.equals("")) {
+            return Optional.absent();
+        }
+
+        try {
+            storage = StorageAdapter.class.cast(resolveClass(storageAdapterClass, StorageAdapter.class).newInstance());
+            storage.setProperties(configProvider);
+
+        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
+            throw new IllegalArgumentException("Unable to instantiate storage adapter from " + storageAdapterClass, e);
+        }
+        return Optional.of(new PersisterImpl(storage));
+    }
+
+    private static Class<?> resolveClass(String storageAdapterClass, Class<?> baseType) throws ClassNotFoundException {
+        Class<?> clazz = Class.forName(storageAdapterClass);
+
+        if (!isImplemented(baseType, clazz))
+            throw new IllegalArgumentException("Storage adapter " + clazz + " has to implement " + baseType);
+        return clazz;
+    }
+
+    private static boolean isImplemented(Class<?> expectedIface, Class<?> byClazz) {
+        for (Class<?> iface : byClazz.getInterfaces()) {
+            if (iface.equals(expectedIface))
+                return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void persistConfig(ConfigSnapshotHolder holder) throws IOException {
+        storage.persistConfig(holder);
+    }
+
+    @Override
+    public Optional<ConfigSnapshotHolder> loadLastConfig() throws IOException {
+        return storage.loadLastConfig();
+    }
+
+    @VisibleForTesting
+    StorageAdapter getStorage() {
+        return storage;
+    }
+
+    @Override
+    public void close() throws IOException {
+        storage.close();
+    }
+
+    @Override
+    public String toString() {
+        return "PersisterImpl [storage=" + storage + "]";
+    }
+}
diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/Util.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/Util.java
new file mode 100644 (file)
index 0000000..b173091
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.persist.impl;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+
+public final class Util {
+
+    public static ScheduledExecutorService getExecutorServiceWithThreadName(final String threadNamePrefix,
+            int threadCount) {
+        return Executors.newScheduledThreadPool(threadCount, new ThreadFactory() {
+
+            private int i = 1;
+
+            @Override
+            public Thread newThread(Runnable r) {
+                Thread thread = new Thread(r);
+                thread.setName(threadNamePrefix + ":" + i++);
+                return thread;
+            }
+        });
+    }
+}
diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java
new file mode 100644 (file)
index 0000000..cf1b0af
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.persist.impl.osgi;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.netconf.persist.impl.ConfigPersisterNotificationHandler;
+import org.opendaylight.controller.netconf.persist.impl.NoOpStorageAdapter;
+import org.opendaylight.controller.netconf.persist.impl.PersisterImpl;
+import org.opendaylight.controller.config.stat.ConfigProvider;
+import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
+import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil.TLSConfiguration;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.management.MBeanServer;
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+
+public class ConfigPersisterActivator implements BundleActivator {
+
+    private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterActivator.class);
+
+    private final static MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
+
+    private ConfigPersisterNotificationHandler configPersisterNotificationHandler;
+
+    private Thread initializationThread;
+
+    @Override
+    public void start(BundleContext context) throws Exception {
+        logger.debug("ConfigPersister activator started");
+
+        ConfigProvider configProvider = new ConfigProvider.ConfigProviderImpl(context);
+        Optional<PersisterImpl> maybePersister = PersisterImpl.createFromProperties(configProvider);
+        if (maybePersister.isPresent() == false) {
+            throw new IllegalStateException("No persister is defined in " + PersisterImpl.STORAGE_ADAPTER_CLASS_PROP
+                    + " property. For noop persister use " + NoOpStorageAdapter.class.getCanonicalName()
+                    + " . Persister is not operational");
+        }
+
+        Optional<TLSConfiguration> maybeTLSConfiguration = NetconfConfigUtil.extractTLSConfiguration(configProvider);
+        Optional<InetSocketAddress> maybeTCPAddress = NetconfConfigUtil.extractTCPNetconfAddress(configProvider);
+
+        InetSocketAddress address;
+        if (maybeTLSConfiguration.isPresent()) {
+            throw new UnsupportedOperationException("TLS is currently not supported for persister");
+        } else if (maybeTCPAddress.isPresent()) {
+            address = maybeTCPAddress.get();
+        } else {
+            throw new IllegalStateException("Netconf is not configured, persister is not operational");
+        }
+
+        PersisterImpl persister = maybePersister.get();
+        configPersisterNotificationHandler = new ConfigPersisterNotificationHandler(persister, address,
+                platformMBeanServer);
+        Runnable initializationRunnable = new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    configPersisterNotificationHandler.init();
+                } catch (InterruptedException e) {
+                    logger.info("Interrupted while waiting for netconf connection");
+                }
+            }
+        };
+        initializationThread = new Thread(initializationRunnable, "ConfigPersister-registrator");
+        initializationThread.start();
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        initializationThread.interrupt();
+        configPersisterNotificationHandler.close();
+    }
+}
diff --git a/opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/client_hello.xml b/opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/client_hello.xml
new file mode 100644 (file)
index 0000000..4e34591
--- /dev/null
@@ -0,0 +1,5 @@
+<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <capabilities>
+        <capability>urn:ietf:params:netconf:base:1.0</capability>
+    </capabilities>
+</hello>
diff --git a/opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/commit.xml b/opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/commit.xml
new file mode 100644 (file)
index 0000000..ae1f6e8
--- /dev/null
@@ -0,0 +1,3 @@
+<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="persister_commit" notify="false">
+    <commit></commit>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/editConfig.xml b/opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/editConfig.xml
new file mode 100644 (file)
index 0000000..8ec1c8c
--- /dev/null
@@ -0,0 +1,12 @@
+<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="persister_edit">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <default-operation>replace</default-operation>
+
+        <config>
+        </config>
+
+    </edit-config>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/PersisterImplTest.java b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/PersisterImplTest.java
new file mode 100644 (file)
index 0000000..36e8825
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.persist.impl;
+
+import com.google.common.base.Optional;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.persist.api.Persister;
+import org.opendaylight.controller.config.persist.api.storage.StorageAdapter;
+import org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter;
+import org.opendaylight.controller.config.stat.ConfigProvider;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.junit.Assert.*;
+import static org.junit.matchers.JUnitMatchers.containsString;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.*;
+
+public class PersisterImplTest {
+    @Mock
+    ConfigProvider mockedConfigProvider;
+
+    @Before
+    public void setUpMocks() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testFromProperties() throws Exception {
+        doReturn(MockAdapter.class.getName()).when(mockedConfigProvider).getProperty(
+                PersisterImpl.STORAGE_ADAPTER_CLASS_PROP);
+
+        PersisterImpl persisterImpl = PersisterImpl.createFromProperties(mockedConfigProvider).get();
+        persisterImpl.persistConfig(null);
+        persisterImpl.loadLastConfig();
+        persisterImpl.persistConfig(null);
+        persisterImpl.loadLastConfig();
+
+        assertEquals(2, MockAdapter.persist);
+        assertEquals(2, MockAdapter.load);
+        assertEquals(1, MockAdapter.props);
+    }
+
+    @Test
+    public void testFromProperties2() throws Exception {
+        mockedConfigProvider = mock(ConfigProvider.class);
+        doReturn(FileStorageAdapter.class.getName()).when(mockedConfigProvider).getProperty(
+                PersisterImpl.STORAGE_ADAPTER_CLASS_PROP);
+        doReturn("target" + File.separator + "generated-test-sources" + File.separator + "testFile").when(
+                mockedConfigProvider).getProperty(FileStorageAdapter.FILE_STORAGE_PROP);
+        doReturn("mockedConfigProvider").when(mockedConfigProvider).toString();
+        doReturn(null).when(mockedConfigProvider).getProperty("numberOfBackups");
+
+        PersisterImpl persisterImpl = PersisterImpl.createFromProperties(mockedConfigProvider).get();
+        assertTrue(persisterImpl.getStorage() instanceof FileStorageAdapter);
+    }
+
+    @Test
+    public void testFromProperties3() throws Exception {
+        mockedConfigProvider = mock(ConfigProvider.class);
+        doReturn(FileStorageAdapter.class.getName()).when(mockedConfigProvider).getProperty(
+                PersisterImpl.STORAGE_ADAPTER_CLASS_PROP);
+        doReturn("target" + File.separator + "generated-test-sources" + File.separator + "testFile").when(
+                mockedConfigProvider).getProperty(FileStorageAdapter.FILE_STORAGE_PROP);
+        doReturn("mockedConfigProvider").when(mockedConfigProvider).toString();
+        doReturn("0").when(mockedConfigProvider).getProperty("numberOfBackups");
+        try {
+            PersisterImpl.createFromProperties(mockedConfigProvider).get();
+            fail();
+        } catch (RuntimeException e) {
+            assertThat(
+                    e.getMessage(),
+                    containsString("numberOfBackups property should be either set to positive value, or ommited. Can not be set to 0."));
+        }
+    }
+
+    @Test
+    public void test() throws Exception {
+        StorageAdapter storage = mock(StorageAdapter.class);
+        doReturn(null).when(storage).loadLastConfig();
+        doNothing().when(storage).persistConfig(any(Persister.ConfigSnapshotHolder.class));
+        PersisterImpl persister = new PersisterImpl(storage);
+        persister.loadLastConfig();
+        persister.persistConfig(null);
+
+        verify(storage).loadLastConfig();
+        verify(storage).persistConfig(any(Persister.ConfigSnapshotHolder.class));
+    }
+
+    public static class MockAdapter implements StorageAdapter {
+
+        static int persist = 0;
+
+        @Override
+        public void persistConfig(ConfigSnapshotHolder holder) throws IOException {
+            persist++;
+        }
+
+        static int load = 0;
+
+        @Override
+        public Optional<ConfigSnapshotHolder> loadLastConfig() throws IOException {
+            load++;
+            return null;// ?
+        }
+
+        static int props = 0;
+
+        @Override
+        public void setProperties(ConfigProvider configProvider) {
+            props++;
+        }
+
+        @Override
+        public void close() throws IOException {
+            // TODO Auto-generated method stub
+
+        }
+
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-api/pom.xml b/opendaylight/netconf/netconf-api/pom.xml
new file mode 100644 (file)
index 0000000..57e77cc
--- /dev/null
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <parent>
+        <artifactId>netconf-subsystem</artifactId>
+        <groupId>org.opendaylight.controller</groupId>
+        <version>0.2.1-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>netconf-api</artifactId>
+    <name>${project.artifactId}</name>
+    <packaging>bundle</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>config-api</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>framework</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Private-Package>
+                        </Private-Package>
+                        <Import-Package>
+                            javax.management,
+                            org.opendaylight.controller.config.api.jmx,
+                            org.opendaylight.protocol.framework,
+                            org.w3c.dom
+                        </Import-Package>
+                        <Export-Package>
+                            org.opendaylight.controller.netconf.api,
+                            org.opendaylight.controller.netconf.api.jmx,
+                        </Export-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+
+</project>
diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfDeserializerException.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfDeserializerException.java
new file mode 100644 (file)
index 0000000..f42db69
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.api;
+
+import org.opendaylight.protocol.framework.DeserializerException;
+
+/**
+ * This exception is thrown by
+ * {@link NetconfSessionListener#onMessage(NetconfMessage)} to indicate fatal
+ * communication problem after which the session should be closed.
+ */
+public class NetconfDeserializerException extends DeserializerException {
+    private static final long serialVersionUID = 1L;
+
+    public NetconfDeserializerException(final String message) {
+        super(message);
+    }
+
+    public NetconfDeserializerException(final String message, final Exception e) {
+        super(message, e);
+    }
+}
diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfDocumentedException.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfDocumentedException.java
new file mode 100644 (file)
index 0000000..1107572
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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.api;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.opendaylight.protocol.framework.DocumentedException;
+
+/**
+ * Checked exception to communicate an error that needs to be sent to the
+ * netconf client.
+ */
+public class NetconfDocumentedException extends DocumentedException {
+
+    private static final long serialVersionUID = 1L;
+
+    public enum ErrorType {
+        transport, rpc, protocol, application;
+
+        public String getTagValue() {
+            return name();
+        }
+    }
+
+    public enum ErrorTag {
+        missing_attribute("missing-attribute"), unknown_element("unknown-element"), operation_not_supported(
+                "operation-not-supported"), bad_attribute("bad-attribute"), data_missing("data-missing"), operation_failed(
+                "operation-failed"), invalid_value("invalid-value"), malformed_message("malformed-message");
+
+        private final String tagValue;
+
+        ErrorTag(final String tagValue) {
+            this.tagValue = tagValue;
+        }
+
+        public String getTagValue() {
+            return this.tagValue;
+        }
+    }
+
+    public enum ErrorSeverity {
+        error, warning;
+
+        public String getTagValue() {
+            return name();
+        }
+    }
+
+    private final ErrorType errorType;
+    private final ErrorTag errorTag;
+    private final ErrorSeverity errorSeverity;
+    private final Map<String, String> errorInfo;
+
+    public NetconfDocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag,
+            final ErrorSeverity errorSeverity) {
+        this(message, errorType, errorTag, errorSeverity, Collections.<String, String> emptyMap());
+    }
+
+    public NetconfDocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag,
+            final ErrorSeverity errorSeverity, final Map<String, String> errorInfo) {
+        super(message);
+        this.errorType = errorType;
+        this.errorTag = errorTag;
+        this.errorSeverity = errorSeverity;
+        this.errorInfo = errorInfo;
+    }
+
+    public NetconfDocumentedException(final String message, final Exception cause, final ErrorType errorType,
+            final ErrorTag errorTag, final ErrorSeverity errorSeverity) {
+        this(message, cause, errorType, errorTag, errorSeverity, Collections.<String, String> emptyMap());
+    }
+
+    public NetconfDocumentedException(final String message, final Exception cause, final ErrorType errorType,
+            final ErrorTag errorTag, final ErrorSeverity errorSeverity, final Map<String, String> errorInfo) {
+        super(message, cause);
+        this.errorType = errorType;
+        this.errorTag = errorTag;
+        this.errorSeverity = errorSeverity;
+        this.errorInfo = errorInfo;
+    }
+
+    public ErrorType getErrorType() {
+        return this.errorType;
+    }
+
+    public ErrorTag getErrorTag() {
+        return this.errorTag;
+    }
+
+    public ErrorSeverity getErrorSeverity() {
+        return this.errorSeverity;
+    }
+
+    public Map<String, String> getErrorInfo() {
+        return this.errorInfo;
+    }
+
+    @Override
+    public String toString() {
+        return "NetconfDocumentedException{" + "message=" + getMessage() + ", errorType=" + this.errorType
+                + ", errorTag=" + this.errorTag + ", errorSeverity=" + this.errorSeverity + ", errorInfo="
+                + this.errorInfo + '}';
+    }
+}
diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfMessage.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfMessage.java
new file mode 100644 (file)
index 0000000..33d41b0
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.api;
+
+import org.opendaylight.protocol.framework.ProtocolMessage;
+import org.w3c.dom.Document;
+
+/**
+ * NetconfMessage represents a wrapper around org.w3c.dom.Document. Needed for
+ * implementing ProtocolMessage interface.
+ */
+public final class NetconfMessage implements ProtocolMessage {
+
+    private static final long serialVersionUID = 462175939836367285L;
+
+    private final Document doc;
+
+    public NetconfMessage(final Document doc) {
+        this.doc = doc;
+    }
+
+    public Document getDocument() {
+        return this.doc;
+    }
+}
diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfOperationRouter.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfOperationRouter.java
new file mode 100644 (file)
index 0000000..49ca0c0
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.api;
+
+import org.w3c.dom.Document;
+
+public interface NetconfOperationRouter extends AutoCloseable {
+
+    Document onNetconfMessage(Document message) throws NetconfDocumentedException;
+
+    @Override
+    void close();
+
+}
diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfServerSessionPreferences.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfServerSessionPreferences.java
new file mode 100644 (file)
index 0000000..d56213c
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.api;
+
+/**
+ * The only input for the start of a NETCONF session is hello-message.
+ */
+public final class NetconfServerSessionPreferences extends NetconfSessionPreferences {
+
+    private final long sessionId;
+
+    public NetconfServerSessionPreferences(final NetconfMessage helloMessage, long sessionId) {
+        super(helloMessage);
+        this.sessionId = sessionId;
+    }
+
+    public long getSessionId() {
+        return sessionId;
+    }
+}
diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java
new file mode 100644 (file)
index 0000000..a61d693
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.api;
+
+import org.opendaylight.protocol.framework.AbstractProtocolSession;
+
+public abstract class NetconfSession extends AbstractProtocolSession<NetconfMessage> {
+
+    public abstract void sendMessage(NetconfMessage netconfMessage);
+}
diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSessionListener.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSessionListener.java
new file mode 100644 (file)
index 0000000..54cb471
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.api;
+
+import org.opendaylight.protocol.framework.SessionListener;
+
+public interface NetconfSessionListener extends
+        SessionListener<NetconfMessage, NetconfSession, NetconfTerminationReason> {
+
+}
diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSessionPreferences.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSessionPreferences.java
new file mode 100644 (file)
index 0000000..1ec46c8
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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.api;
+
+public class NetconfSessionPreferences {
+    protected final NetconfMessage helloMessage;
+
+    public NetconfSessionPreferences(final NetconfMessage helloMessage) {
+        this.helloMessage = helloMessage;
+    }
+
+    /**
+     * @return the helloMessage
+     */
+    public NetconfMessage getHelloMessage() {
+        return this.helloMessage;
+    }
+}
diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfTerminationReason.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfTerminationReason.java
new file mode 100644 (file)
index 0000000..9de3071
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.api;
+
+import org.opendaylight.protocol.framework.TerminationReason;
+
+public class NetconfTerminationReason implements TerminationReason {
+
+    private final String reason;
+
+    public NetconfTerminationReason(String reason) {
+        this.reason = reason;
+    }
+
+    @Override
+    public String getErrorMessage() {
+        return reason;
+    }
+}
diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/jmx/CommitJMXNotification.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/jmx/CommitJMXNotification.java
new file mode 100644 (file)
index 0000000..0c41740
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.api.jmx;
+
+import org.w3c.dom.Element;
+
+import javax.management.NotificationBroadcasterSupport;
+import java.util.Set;
+
+public class CommitJMXNotification extends NetconfJMXNotification {
+
+    private final Element configSnapshot;
+
+    private static final String afterCommitMessageTemplate = "Commit successful: %s";
+    private final Set<String> capabilities;
+
+    CommitJMXNotification(NotificationBroadcasterSupport source, String message, Element cfgSnapshot,
+            Set<String> capabilities) {
+        super(TransactionProviderJMXNotificationType.commit, source, String.format(afterCommitMessageTemplate, message));
+        this.configSnapshot = cfgSnapshot;
+        this.capabilities = capabilities;
+    }
+
+    public Element getConfigSnapshot() {
+        return configSnapshot;
+    }
+
+    public Set<String> getCapabilities() {
+        return capabilities;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuffer sb = new StringBuffer("CommitJMXNotification{");
+        sb.append("configSnapshot=").append(configSnapshot);
+        sb.append(", capabilities=").append(getCapabilities());
+        sb.append('}');
+        return sb.toString();
+    }
+
+    /**
+     *
+     */
+    private static final long serialVersionUID = -8587623362011695514L;
+
+}
diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/jmx/DefaultCommitOperationMXBean.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/jmx/DefaultCommitOperationMXBean.java
new file mode 100644 (file)
index 0000000..a170d29
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.api.jmx;
+
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+
+import javax.management.ObjectName;
+
+public interface DefaultCommitOperationMXBean {
+
+    static String typeName = "NetconfNotificationProvider";
+    public static ObjectName objectName = ObjectNameUtil.createONWithDomainAndType(typeName);
+
+}
diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/jmx/NetconfJMXNotification.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/jmx/NetconfJMXNotification.java
new file mode 100644 (file)
index 0000000..5aa4bff
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.api.jmx;
+
+import java.util.Set;
+
+import javax.management.Notification;
+import javax.management.NotificationBroadcasterSupport;
+
+import org.w3c.dom.Element;
+
+public abstract class NetconfJMXNotification extends Notification {
+
+    /**
+     *
+     */
+    private static final long serialVersionUID = 6754474563863772845L;
+
+    private static long sequenceNumber = 1;
+
+    private final TransactionProviderJMXNotificationType type;
+
+    protected NetconfJMXNotification(TransactionProviderJMXNotificationType type,
+            NotificationBroadcasterSupport source, String message) {
+        super(type.toString(), source, sequenceNumber++, System.nanoTime(), message);
+        this.type = type;
+    }
+
+    @Override
+    public String toString() {
+        return "TransactionProviderJMXNotification [type=" + type + "]";
+    }
+
+    /**
+     * Sends this notification using source that created it
+     */
+    public void send() {
+        ((NotificationBroadcasterSupport) getSource()).sendNotification(this);
+    }
+
+    /**
+     * Creates notification about successful commit execution.
+     *
+     * Intended for config-persister.
+     *
+     * @param transactionName
+     * @param cfgSnapshot
+     */
+    public static CommitJMXNotification afterCommit(NotificationBroadcasterSupport source, String message,
+            Element cfgSnapshot, Set<String> capabilities) {
+        return new CommitJMXNotification(source, message, cfgSnapshot, capabilities);
+    }
+
+    static enum TransactionProviderJMXNotificationType {
+        commit;
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-client/pom.xml b/opendaylight/netconf/netconf-client/pom.xml
new file mode 100644 (file)
index 0000000..ac949f5
--- /dev/null
@@ -0,0 +1,77 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>netconf-subsystem</artifactId>
+        <groupId>org.opendaylight.controller</groupId>
+        <version>0.2.1-SNAPSHOT</version>
+    </parent>
+    <artifactId>netconf-client</artifactId>
+    <name>${project.artifactId}</name>
+    <packaging>bundle</packaging>
+
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-util</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>framework</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Export-Package>
+                            org.opendaylight.controller.netconf.client,
+                        </Export-Package>
+                        <Import-Package>
+                            com.google.common.base,
+                            com.google.common.collect,
+                            io.netty.channel,
+                            io.netty.channel.socket,
+                            io.netty.util,
+                            io.netty.util.concurrent,
+                            javax.annotation,
+                            javax.net.ssl,
+                            javax.xml.namespace,
+                            javax.xml.parsers,
+                            javax.xml.xpath,
+                            org.opendaylight.controller.netconf.api,
+                            org.opendaylight.controller.netconf.util,
+                            org.opendaylight.controller.netconf.util.xml,
+                            org.opendaylight.protocol.framework,
+                            org.slf4j,
+                            org.w3c.dom,
+                            org.xml.sax
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClient.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClient.java
new file mode 100644 (file)
index 0000000..b5a06ca
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * 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.client;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.GlobalEventExecutor;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.protocol.framework.NeverReconnectStrategy;
+import org.opendaylight.protocol.framework.ReconnectStrategy;
+import org.opendaylight.protocol.framework.TimedReconnectStrategy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.SSLContext;
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.Set;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+
+public class NetconfClient implements Closeable {
+
+    private static final Logger logger = LoggerFactory.getLogger(NetconfClient.class);
+
+    public static final int DEFAULT_CONNECT_TIMEOUT = 5000;
+    private final NetconfClientDispatcher dispatch;
+    private final String label;
+    private final NetconfClientSession clientSession;
+    private final NetconfClientSessionListener sessionListener;
+    private final long sessionId;
+    private final InetSocketAddress address;
+
+    // TODO test reconnecting constructor
+    public NetconfClient(String clientLabelForLogging, InetSocketAddress address, int connectionAttempts,
+            int attemptMsTimeout) {
+        this(clientLabelForLogging, address, getReconnectStrategy(connectionAttempts, attemptMsTimeout), Optional
+                .<SSLContext> absent());
+    }
+
+    private NetconfClient(String clientLabelForLogging, InetSocketAddress address, ReconnectStrategy strat,
+            Optional<SSLContext> maybeSSLContext) {
+        this.label = clientLabelForLogging;
+        dispatch = new NetconfClientDispatcher(maybeSSLContext);
+
+        sessionListener = new NetconfClientSessionListener();
+        Future<NetconfClientSession> clientFuture = dispatch.createClient(address, sessionListener, strat);
+        this.address = address;
+        clientSession = get(clientFuture);
+        this.sessionId = clientSession.getSessionId();
+    }
+
+    private NetconfClientSession get(Future<NetconfClientSession> clientFuture) {
+        try {
+            return clientFuture.get();
+        } catch (InterruptedException | CancellationException e) {
+            throw new RuntimeException("Netconf client interrupted", e);
+        } catch (ExecutionException e) {
+            throw new IllegalStateException("Unable to create netconf client", e);
+        }
+    }
+
+    public NetconfClient(String clientLabelForLogging, InetSocketAddress address, int connectTimeoutMs,
+            Optional<SSLContext> maybeSSLContext) {
+        this(clientLabelForLogging, address,
+                new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, connectTimeoutMs), maybeSSLContext);
+    }
+
+    public NetconfClient(String clientLabelForLogging, InetSocketAddress address, int connectTimeoutMs) {
+        this(clientLabelForLogging, address,
+                new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, connectTimeoutMs), Optional
+                        .<SSLContext> absent());
+    }
+
+    public NetconfClient(String clientLabelForLogging, InetSocketAddress address) {
+        this(clientLabelForLogging, address, new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE,
+                DEFAULT_CONNECT_TIMEOUT), Optional.<SSLContext> absent());
+    }
+
+    public NetconfClient(String clientLabelForLogging, InetSocketAddress address, Optional<SSLContext> maybeSSLContext) {
+        this(clientLabelForLogging, address, new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE,
+                DEFAULT_CONNECT_TIMEOUT), maybeSSLContext);
+    }
+
+    public NetconfMessage sendMessage(NetconfMessage message) {
+        return sendMessage(message, 5, 1000);
+    }
+
+    public NetconfMessage sendMessage(NetconfMessage message, int attempts, int attemptMsDelay) {
+        Preconditions.checkState(clientSession.isUp(), "Session was not up yet");
+        clientSession.sendMessage(message);
+        try {
+            return sessionListener.getLastMessage(attempts, attemptMsDelay);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(this + " Cannot read message from " + address, e);
+        } catch (IllegalStateException e) {
+            throw new IllegalStateException(this + " Cannot read message from " + address, e);
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+        clientSession.close();
+        dispatch.close();
+    }
+
+    private static ReconnectStrategy getReconnectStrategy(int connectionAttempts, int attemptMsTimeout) {
+        return new TimedReconnectStrategy(GlobalEventExecutor.INSTANCE, attemptMsTimeout, 1000, 1.0, null,
+                Long.valueOf(connectionAttempts), null);
+    }
+
+    @Override
+    public String toString() {
+        final StringBuffer sb = new StringBuffer("NetconfClient{");
+        sb.append("label=").append(label);
+        sb.append(", sessionId=").append(sessionId);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    public long getSessionId() {
+        return sessionId;
+    }
+
+    public Set<String> getCapabilities() {
+        Preconditions.checkState(clientSession != null, "Client was not initialized successfully");
+        return Sets.newHashSet(clientSession.getServerCapabilities());
+    }
+}
diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientDispatcher.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientDispatcher.java
new file mode 100644 (file)
index 0000000..4df8235
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * 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.client;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.Promise;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfSession;
+import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
+import org.opendaylight.controller.netconf.util.AbstractChannelInitializer;
+import org.opendaylight.protocol.framework.AbstractDispatcher;
+import org.opendaylight.protocol.framework.ReconnectStrategy;
+import org.opendaylight.protocol.framework.SessionListener;
+import org.opendaylight.protocol.framework.SessionListenerFactory;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import java.net.InetSocketAddress;
+
+public class NetconfClientDispatcher extends AbstractDispatcher<NetconfClientSession, NetconfClientSessionListener> {
+
+    private final Optional<SSLContext> maybeContext;
+    private final NetconfClientSessionNegotiatorFactory negotatorFactory;
+
+    public NetconfClientDispatcher(final Optional<SSLContext> maybeContext) {
+        this.maybeContext = Preconditions.checkNotNull(maybeContext);
+        this.negotatorFactory = new NetconfClientSessionNegotiatorFactory(new HashedWheelTimer());
+    }
+
+    public Future<NetconfClientSession> createClient(InetSocketAddress address,
+            final NetconfClientSessionListener sessionListener, ReconnectStrategy strat) {
+
+        return super.createClient(address, strat, new PipelineInitializer<NetconfClientSession>() {
+
+            @Override
+            public void initializeChannel(final SocketChannel ch, final Promise<NetconfClientSession> promise) {
+                initialize(ch, promise);
+            }
+
+            private void initialize(SocketChannel ch, Promise<NetconfClientSession> promise) {
+                new ClientChannelInitializer(maybeContext, negotatorFactory, sessionListener).initialize(ch, promise);
+            }
+        });
+    }
+
+    private static class ClientChannelInitializer extends AbstractChannelInitializer {
+
+        private final NetconfClientSessionNegotiatorFactory negotiatorFactory;
+        private final NetconfClientSessionListener sessionListener;
+
+        private ClientChannelInitializer(Optional<SSLContext> maybeContext,
+                NetconfClientSessionNegotiatorFactory negotiatorFactory, NetconfClientSessionListener sessionListener) {
+            super(maybeContext);
+            this.negotiatorFactory = negotiatorFactory;
+            this.sessionListener = sessionListener;
+        }
+
+        @Override
+        protected void initializeAfterDecoder(SocketChannel ch, Promise<? extends NetconfSession> promise) {
+            ch.pipeline().addLast("negotiator", negotiatorFactory.getSessionNegotiator(new SessionListenerFactory() {
+                @Override
+                public SessionListener<NetconfMessage, NetconfClientSession, NetconfTerminationReason> getSessionListener() {
+                    return sessionListener;
+                }
+            }, ch, promise));
+        }
+
+        @Override
+        protected void initSslEngine(SSLEngine sslEngine) {
+            sslEngine.setUseClientMode(true);
+        }
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java
new file mode 100644 (file)
index 0000000..f0180cf
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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.client;
+
+import io.netty.channel.Channel;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfSession;
+import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.protocol.framework.SessionListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Collection;
+
+public class NetconfClientSession extends NetconfSession {
+
+    private final SessionListener sessionListener;
+    private final long sessionId;
+    private final Channel channel;
+
+    private static final Logger logger = LoggerFactory.getLogger(NetconfClientSession.class);
+    private final Collection<String> capabilities;
+    private boolean up;
+
+    public NetconfClientSession(SessionListener sessionListener, Channel channel, long sessionId,
+            Collection<String> capabilities) {
+        this.sessionListener = sessionListener;
+        this.channel = channel;
+        this.sessionId = sessionId;
+        this.capabilities = capabilities;
+        logger.debug("Client Session {} created", toString());
+    }
+
+    @Override
+    public void close() {
+        channel.close();
+        sessionListener.onSessionTerminated(this, new NetconfTerminationReason("Client Session closed"));
+    }
+
+    @Override
+    protected void handleMessage(NetconfMessage netconfMessage) {
+        logger.debug("Client Session {} received message {}", toString(),
+                XmlUtil.toString(netconfMessage.getDocument()));
+        sessionListener.onMessage(this, netconfMessage);
+    }
+
+    @Override
+    public void sendMessage(NetconfMessage netconfMessage) {
+        channel.writeAndFlush(netconfMessage);
+    }
+
+    @Override
+    protected void endOfInput() {
+        logger.debug("Client Session {} end of input detected while session was in state {}", toString(), isUp() ? "up"
+                : "initialized");
+        if (isUp()) {
+            this.sessionListener.onSessionDown(this, new IOException("End of input detected. Close the session."));
+        }
+    }
+
+    @Override
+    protected void sessionUp() {
+        logger.debug("Client Session {} up", toString());
+        sessionListener.onSessionUp(this);
+        this.up = true;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuffer sb = new StringBuffer("ClientNetconfSession{");
+        sb.append("sessionId=").append(sessionId);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    public boolean isUp() {
+        return up;
+    }
+
+    public long getSessionId() {
+        return sessionId;
+    }
+
+    public Collection<String> getServerCapabilities() {
+        return capabilities;
+    }
+}
diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionListener.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionListener.java
new file mode 100644 (file)
index 0000000..d3c1b22
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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.client;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
+import org.opendaylight.protocol.framework.SessionListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class NetconfClientSessionListener implements
+        SessionListener<NetconfMessage, NetconfClientSession, NetconfTerminationReason> {
+
+    private static final Logger logger = LoggerFactory.getLogger(NetconfClientSessionListener.class);
+    private AtomicBoolean up = new AtomicBoolean(false);
+
+    @Override
+    public void onSessionUp(NetconfClientSession clientSession) {
+        up.set(true);
+    }
+
+    @Override
+    public void onSessionDown(NetconfClientSession clientSession, Exception e) {
+        logger.debug("Client Session {} down, reason: {}", clientSession, e.getMessage());
+        up.set(false);
+    }
+
+    @Override
+    public void onSessionTerminated(NetconfClientSession clientSession,
+            NetconfTerminationReason netconfTerminationReason) {
+        logger.debug("Client Session {} terminated, reason: {}", clientSession,
+                netconfTerminationReason.getErrorMessage());
+        up.set(false);
+    }
+
+    @Override
+    public synchronized void onMessage(NetconfClientSession session, NetconfMessage message) {
+        synchronized (messages) {
+            this.messages.add(message);
+        }
+    }
+
+    private int lastReadMessage = -1;
+    private List<NetconfMessage> messages = Lists.newArrayList();
+
+    public NetconfMessage getLastMessage(int attempts, int attemptMsDelay) throws InterruptedException {
+        Preconditions.checkState(up.get(), "Session was not up yet");
+
+        for (int i = 0; i < attempts; i++) {
+            synchronized (messages) {
+                if (messages.size() - 1 > lastReadMessage) {
+                    lastReadMessage++;
+                    return messages.get(lastReadMessage);
+                }
+            }
+
+            if (up.get() == false)
+                throw new IllegalStateException("Session ended while trying to read message");
+            Thread.sleep(attemptMsDelay);
+        }
+
+        throw new IllegalStateException("No netconf message to read");
+    }
+}
diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java
new file mode 100644 (file)
index 0000000..17a55c5
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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.client;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import io.netty.channel.Channel;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.Promise;
+import org.opendaylight.controller.netconf.api.NetconfSessionPreferences;
+import org.opendaylight.controller.netconf.util.AbstractNetconfSessionNegotiator;
+import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.protocol.framework.SessionListener;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import javax.annotation.Nullable;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import java.util.Collection;
+import java.util.List;
+
+public class NetconfClientSessionNegotiator extends
+        AbstractNetconfSessionNegotiator<NetconfSessionPreferences, NetconfClientSession> {
+
+    protected NetconfClientSessionNegotiator(NetconfSessionPreferences sessionPreferences,
+            Promise<NetconfClientSession> promise, Channel channel, Timer timer, SessionListener sessionListener) {
+        super(sessionPreferences, promise, channel, timer, sessionListener);
+    }
+
+    private static Collection<String> getCapabilities(Document doc) {
+        XmlElement responseElement = XmlElement.fromDomDocument(doc);
+        XmlElement capabilitiesElement = responseElement
+                .getOnlyChildElementWithSameNamespace(XmlNetconfConstants.CAPABILITIES);
+        List<XmlElement> caps = capabilitiesElement.getChildElements(XmlNetconfConstants.CAPABILITY);
+        return Collections2.transform(caps, new Function<XmlElement, String>() {
+
+            @Nullable
+            @Override
+            public String apply(@Nullable XmlElement input) {
+                return input.getTextContent();
+            }
+        });
+    }
+
+    private static final XPathExpression sessionIdXPath = XMLNetconfUtil
+            .compileXPath("/netconf:hello/netconf:session-id");
+
+    private long extractSessionId(Document doc) {
+        final Node sessionIdNode = (Node) XmlUtil.evaluateXPath(sessionIdXPath, doc, XPathConstants.NODE);
+        String textContent = sessionIdNode.getTextContent();
+        if (textContent == null || textContent.equals("")) {
+            throw new IllegalStateException("Session id not received from server");
+        }
+
+        return Long.valueOf(textContent);
+    }
+
+    @Override
+    protected NetconfClientSession getSession(SessionListener sessionListener, Channel channel, Document doc) {
+        return new NetconfClientSession(sessionListener, channel, extractSessionId(doc), getCapabilities(doc));
+    }
+}
diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiatorFactory.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiatorFactory.java
new file mode 100644 (file)
index 0000000..db0b953
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.client;
+
+import com.google.common.base.Preconditions;
+import io.netty.channel.Channel;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.Promise;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfSessionPreferences;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.protocol.framework.SessionListenerFactory;
+import org.opendaylight.protocol.framework.SessionNegotiator;
+import org.opendaylight.protocol.framework.SessionNegotiatorFactory;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class NetconfClientSessionNegotiatorFactory implements SessionNegotiatorFactory {
+
+    private final Timer timer;
+
+    public NetconfClientSessionNegotiatorFactory(Timer timer) {
+        this.timer = timer;
+    }
+
+    private static NetconfMessage loadHelloMessageTemplate() {
+        final String helloMessagePath = "/client_hello.xml";
+        try (InputStream is = NetconfClientSessionNegotiatorFactory.class.getResourceAsStream(helloMessagePath)) {
+            Preconditions.checkState(is != null, "Input stream from %s was null", helloMessagePath);
+            return new NetconfMessage(XmlUtil.readXmlToDocument(is));
+        } catch (SAXException | IOException e) {
+            throw new RuntimeException("Unable to load hello message", e);
+        }
+    }
+
+    @Override
+    public SessionNegotiator getSessionNegotiator(SessionListenerFactory sessionListenerFactory, Channel channel,
+            Promise promise) {
+        // Hello message needs to be recreated every time
+        NetconfSessionPreferences proposal = new NetconfSessionPreferences(loadHelloMessageTemplate());
+        return new NetconfClientSessionNegotiator(proposal, promise, channel, timer,
+                sessionListenerFactory.getSessionListener());
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-client/src/main/resources/client_hello.xml b/opendaylight/netconf/netconf-client/src/main/resources/client_hello.xml
new file mode 100644 (file)
index 0000000..1d85b68
--- /dev/null
@@ -0,0 +1,5 @@
+<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <capabilities>
+        <capability>urn:ietf:params:netconf:base:1.0</capability>
+    </capabilities>
+</hello>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-impl/pom.xml b/opendaylight/netconf/netconf-impl/pom.xml
new file mode 100644 (file)
index 0000000..b52e85b
--- /dev/null
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <parent>
+        <artifactId>netconf-subsystem</artifactId>
+        <groupId>org.opendaylight.controller</groupId>
+        <version>0.2.1-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>netconf-impl</artifactId>
+    <name>${project.artifactId}</name>
+    <packaging>bundle</packaging>
+
+
+    <dependencies>
+        <!-- compile dependencies -->
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-util</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>config-util</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-mapping-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>util</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>framework</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+
+        <!-- test dependencies -->
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>mockito-configuration</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.4</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+
+            <artifactId>yang-store-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>xmlunit</groupId>
+            <artifactId>xmlunit</artifactId>
+            <version>1.4</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-util</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-client</artifactId>
+            <version>${project.version}</version>
+            <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.netconf.impl.osgi.NetconfImplActivator</Bundle-Activator>
+                        <Import-Package>
+                            com.google.common.base,
+                            com.google.common.collect,
+                            io.netty.channel,
+                            io.netty.channel.socket,
+                            io.netty.util,
+                            io.netty.util.concurrent,
+                            javax.management,
+                            javax.net.ssl,
+                            javax.xml.namespace,
+                            javax.xml.xpath,
+                            org.opendaylight.controller.netconf.api,
+                            org.opendaylight.controller.netconf.api.jmx,
+                            org.opendaylight.controller.netconf.mapping.api,
+                            org.opendaylight.controller.netconf.util,
+                            org.opendaylight.controller.netconf.util.mapping,
+                            org.opendaylight.controller.netconf.util.osgi,
+                            org.opendaylight.controller.netconf.util.xml,
+                            org.opendaylight.protocol.framework,
+                            org.osgi.framework,
+                            org.osgi.util.tracker,
+                            org.slf4j,
+                            org.w3c.dom,
+                            org.xml.sax,
+                            org.opendaylight.controller.netconf.util.messages,
+                            org.opendaylight.controller.config.stat
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+
+</project>
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/CapabilityProviderImpl.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/CapabilityProviderImpl.java
new file mode 100644 (file)
index 0000000..1d2e039
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshot;
+import org.opendaylight.controller.netconf.mapping.api.Capability;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+public class CapabilityProviderImpl implements CapabilityProvider {
+    private final NetconfOperationServiceSnapshot netconfOperationServiceSnapshot;
+    private final Set<String> capabilityURIs;
+
+    public CapabilityProviderImpl(NetconfOperationServiceSnapshot netconfOperationServiceSnapshot) {
+        this.netconfOperationServiceSnapshot = netconfOperationServiceSnapshot;
+        Map<String, Capability> urisToCapabilitiesInternalMap = getCapabilitiesInternal(netconfOperationServiceSnapshot);
+        capabilityURIs = Collections.unmodifiableSet(urisToCapabilitiesInternalMap.keySet());
+    }
+
+    private static Map<String, Capability> getCapabilitiesInternal(
+            NetconfOperationServiceSnapshot netconfOperationServiceSnapshot) {
+        Map<String, Capability> capabilityMap = Maps.newHashMap();
+
+        for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) {
+            final Set<Capability> caps = netconfOperationService.getCapabilities();
+
+            for (Capability cap : caps) {
+                // TODO check for duplicates ?
+                capabilityMap.put(cap.getCapabilityUri(), cap);
+            }
+        }
+
+        return capabilityMap;
+    }
+
+    @Override
+    public synchronized String getSchemaForCapability(String moduleName, Optional<String> revision) {
+
+        Map<String, Map<String, String>> mappedModulesToRevisionToSchema = Maps.newHashMap();
+
+        for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) {
+            final Set<Capability> caps = netconfOperationService.getCapabilities();
+
+            for (Capability cap : caps) {
+                if (cap.getModuleName().isPresent() == false)
+                    continue;
+                if (cap.getRevision().isPresent() == false)
+                    continue;
+                if (cap.getCapabilitySchema().isPresent() == false)
+                    continue;
+
+                final String currentModuleName = cap.getModuleName().get();
+                Map<String, String> revisionMap = mappedModulesToRevisionToSchema.get(currentModuleName);
+                if (revisionMap == null) {
+                    revisionMap = Maps.newHashMap();
+                    mappedModulesToRevisionToSchema.put(currentModuleName, revisionMap);
+                }
+
+                String currentRevision = cap.getRevision().get();
+                revisionMap.put(currentRevision, cap.getCapabilitySchema().get());
+            }
+        }
+
+        Map<String, String> revisionMapRequest = mappedModulesToRevisionToSchema.get(moduleName);
+        Preconditions.checkState(revisionMapRequest != null, "Capability for module %s not present, " + ""
+                + "available modules : %s", moduleName, capabilityURIs);
+
+        if (revision.isPresent()) {
+            String schema = revisionMapRequest.get(revision.get());
+
+            Preconditions.checkState(schema != null,
+                    "Capability for module %s:%s not present, available revisions for module: %s", moduleName,
+                    revision.get(), revisionMapRequest.keySet());
+
+            return schema;
+        } else {
+            Preconditions.checkState(revisionMapRequest.size() == 1,
+                    "Expected 1 capability for module %s, available revisions : %s", moduleName,
+                    revisionMapRequest.keySet());
+            return revisionMapRequest.values().iterator().next();
+        }
+    }
+
+    @Override
+    public synchronized Set<String> getCapabilities() {
+        return capabilityURIs;
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/DefaultCommitNotificationProducer.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/DefaultCommitNotificationProducer.java
new file mode 100644 (file)
index 0000000..305fb57
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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.impl;
+
+import org.opendaylight.controller.netconf.api.jmx.CommitJMXNotification;
+import org.opendaylight.controller.netconf.api.jmx.DefaultCommitOperationMXBean;
+import org.opendaylight.controller.netconf.api.jmx.NetconfJMXNotification;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Element;
+
+import javax.management.*;
+import java.util.Set;
+
+public class DefaultCommitNotificationProducer extends NotificationBroadcasterSupport implements
+        DefaultCommitOperationMXBean, AutoCloseable {
+
+    private static final Logger logger = LoggerFactory.getLogger(DefaultCommitNotificationProducer.class);
+
+    private final MBeanServer mbeanServer;
+
+    private final ObjectName on = DefaultCommitOperationMXBean.objectName;
+
+    public DefaultCommitNotificationProducer(MBeanServer mBeanServer) {
+        this.mbeanServer = mBeanServer;
+        registerMBean(this, mbeanServer, on);
+    }
+
+    private static void registerMBean(final Object instance, final MBeanServer mbs, final ObjectName on) {
+        try {
+            mbs.registerMBean(instance, on);
+        } catch (InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException e) {
+            throw new RuntimeException("Unable to register " + instance + " as " + on, e);
+        }
+    }
+
+    public void sendCommitNotification(String message, Element cfgSnapshot, Set<String> capabilities) {
+        CommitJMXNotification notif = NetconfJMXNotification.afterCommit(this, message, cfgSnapshot, capabilities);
+        logger.debug("Notification about commit {} sent", notif);
+        sendNotification(notif);
+    }
+
+    @Override
+    public void close() {
+        try {
+            mbeanServer.unregisterMBean(on);
+        } catch (InstanceNotFoundException | MBeanRegistrationException e) {
+            logger.warn("Ignoring exception while unregistering {} as {}", this, on, e);
+        }
+    }
+}
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerDispatcher.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerDispatcher.java
new file mode 100644 (file)
index 0000000..324da56
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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.impl;
+
+import com.google.common.base.Optional;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.util.concurrent.Promise;
+import org.opendaylight.controller.netconf.api.NetconfSession;
+import org.opendaylight.controller.netconf.impl.util.DeserializerExceptionHandler;
+import org.opendaylight.controller.netconf.util.AbstractChannelInitializer;
+import org.opendaylight.protocol.framework.AbstractDispatcher;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import java.net.InetSocketAddress;
+
+public class NetconfServerDispatcher extends AbstractDispatcher<NetconfSession, NetconfServerSessionListener> {
+
+    private final ServerChannelInitializer initializer;
+
+    public NetconfServerDispatcher(final Optional<SSLContext> maybeContext,
+            NetconfServerSessionNegotiatorFactory serverNegotiatorFactory,
+            NetconfServerSessionListenerFactory listenerFactory) {
+        this.initializer = new ServerChannelInitializer(maybeContext, serverNegotiatorFactory, listenerFactory);
+    }
+
+    // FIXME change headers for all new source code files
+
+    // TODO test create server with same address twice
+    public ChannelFuture createServer(InetSocketAddress address) {
+
+        return super.createServer(address, new PipelineInitializer<NetconfSession>() {
+            @Override
+            public void initializeChannel(final SocketChannel ch, final Promise<NetconfSession> promise) {
+                initializer.initialize(ch, promise);
+            }
+        });
+    }
+
+    private static class ServerChannelInitializer extends AbstractChannelInitializer {
+
+        private final NetconfServerSessionNegotiatorFactory negotiatorFactory;
+        private final NetconfServerSessionListenerFactory listenerFactory;
+
+        private ServerChannelInitializer(Optional<SSLContext> maybeContext,
+                NetconfServerSessionNegotiatorFactory negotiatorFactory,
+                NetconfServerSessionListenerFactory listenerFactory) {
+            super(maybeContext);
+            this.negotiatorFactory = negotiatorFactory;
+            this.listenerFactory = listenerFactory;
+        }
+
+        @Override
+        protected void initializeAfterDecoder(SocketChannel ch, Promise<? extends NetconfSession> promise) {
+            ch.pipeline().addLast("deserializerExHandler", new DeserializerExceptionHandler());
+            ch.pipeline().addLast("negotiator", negotiatorFactory.getSessionNegotiator(listenerFactory, ch, promise));
+        }
+
+        @Override
+        protected void initSslEngine(SSLEngine sslEngine) {
+            sslEngine.setUseClientMode(false);
+        }
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java
new file mode 100644 (file)
index 0000000..1ae3fab
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.impl;
+
+import io.netty.channel.Channel;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfSession;
+import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.protocol.framework.SessionListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+public class NetconfServerSession extends NetconfSession {
+
+    private final SessionListener sessionListener;
+    private final Channel channel;
+
+    private static final Logger logger = LoggerFactory.getLogger(NetconfServerSession.class);
+    private final long sessionId;
+    private boolean up = false;
+
+    public NetconfServerSession(SessionListener sessionListener, Channel channel, long sessionId) {
+        this.sessionListener = sessionListener;
+        this.channel = channel;
+        this.sessionId = sessionId;
+        logger.debug("Session {} created", toString());
+    }
+
+    @Override
+    public void close() {
+        channel.close();
+        sessionListener.onSessionTerminated(this, new NetconfTerminationReason("Session closed"));
+    }
+
+    @Override
+    protected void handleMessage(NetconfMessage netconfMessage) {
+        logger.debug("Session {} received message {}", toString(), XmlUtil.toString(netconfMessage.getDocument()));
+        sessionListener.onMessage(this, netconfMessage);
+    }
+
+    public void sendMessage(NetconfMessage netconfMessage) {
+        channel.writeAndFlush(netconfMessage);
+    }
+
+    @Override
+    protected void endOfInput() {
+        logger.debug("Session {} end of input detected while session was in state {}", toString(), isUp() ? "up"
+                : "initialized");
+        if (isUp()) {
+            this.sessionListener.onSessionDown(this, new IOException("End of input detected. Close the session."));
+        }
+    }
+
+    @Override
+    protected void sessionUp() {
+        logger.debug("Session {} up", toString());
+        sessionListener.onSessionUp(this);
+        this.up = true;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuffer sb = new StringBuffer("ServerNetconfSession{");
+        sb.append("sessionId=").append(sessionId);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    public boolean isUp() {
+        return up;
+    }
+
+    public long getSessionId() {
+        return sessionId;
+    }
+}
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListener.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListener.java
new file mode 100644 (file)
index 0000000..4f71ab9
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * 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.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+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.impl.osgi.NetconfOperationRouterImpl;
+import org.opendaylight.controller.netconf.util.messages.SendErrorExceptionUtil;
+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.protocol.framework.SessionListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import static com.google.common.base.Preconditions.checkState;
+
+public class NetconfServerSessionListener implements
+        SessionListener<NetconfMessage, NetconfServerSession, NetconfTerminationReason> {
+
+    static final Logger logger = LoggerFactory.getLogger(NetconfServerSessionListener.class);
+    public static final String MESSAGE_ID = "message-id";
+
+    private NetconfOperationRouterImpl operationRouter;
+
+    public NetconfServerSessionListener(NetconfOperationRouterImpl operationRouter) {
+        this.operationRouter = operationRouter;
+    }
+
+    @Override
+    public void onSessionUp(NetconfServerSession netconfNetconfServerSession) {
+
+    }
+
+    @Override
+    public void onSessionDown(NetconfServerSession netconfNetconfServerSession, Exception e) {
+        logger.debug("Session {} down, reason: {}", netconfNetconfServerSession, e.getMessage());
+
+        operationRouter.close();
+    }
+
+    @Override
+    public void onSessionTerminated(NetconfServerSession netconfNetconfServerSession,
+            NetconfTerminationReason netconfTerminationReason) {
+        logger.debug("Session {} terminated, reason: {}", netconfNetconfServerSession,
+                netconfTerminationReason.getErrorMessage());
+
+        operationRouter.close();
+    }
+
+    @Override
+    public void onMessage(NetconfServerSession session, NetconfMessage netconfMessage) {
+        try {
+
+            Preconditions.checkState(operationRouter != null, "Cannot handle message, session up was not yet received");
+            // FIXME: there is no validation since the document may contain yang
+            // schemas
+            final NetconfMessage message = processDocument(netconfMessage);
+            logger.debug("Respondign with message {}", XmlUtil.toString(message.getDocument()));
+            session.sendMessage(message);
+
+            if (isCloseSession(netconfMessage)) {
+                closeNetconfSession(session);
+            }
+
+        } catch (final RuntimeException e) {
+            logger.error("Unexpected exception", e);
+            // TODO: should send generic error or close session?
+            throw new RuntimeException("Unable to process incoming message " + netconfMessage, e);
+        } catch (NetconfDocumentedException e) {
+            SendErrorExceptionUtil.sendErrorMessage(session, e, netconfMessage);
+        }
+    }
+
+    private void closeNetconfSession(NetconfServerSession session) {
+        // destroy NetconfOperationService
+        session.close();
+        logger.info("Session {} closed successfully", session.getSessionId());
+    }
+
+    private NetconfMessage processDocument(final NetconfMessage netconfMessage) throws NetconfDocumentedException {
+
+        final Document incommingDocument = netconfMessage.getDocument();
+        final Node rootNode = incommingDocument.getDocumentElement();
+
+        if (rootNode.getNodeName().equals(XmlNetconfConstants.RPC_KEY)) {
+            final String messageId = rootNode.getAttributes().getNamedItem(MESSAGE_ID).getTextContent();
+            checkState(messageId != null);
+            final Document responseDocument = XmlUtil.newDocument();
+            Document rpcReply = operationRouter.onNetconfMessage(incommingDocument);
+            responseDocument.appendChild(responseDocument.importNode(rpcReply.getDocumentElement(), true));
+            return new NetconfMessage(responseDocument);
+        } else {
+            // unknown command, send RFC 4741 p.70 unknown-element
+            /*
+             * Tag: unknown-element Error-type: rpc, protocol, application
+             * Severity: error Error-info: <bad-element> : name of the
+             * unexpected element Description: An unexpected element is present.
+             */
+            // TODO add message to error info
+            throw new NetconfDocumentedException("Unknown tag " + rootNode.getNodeName(),
+                    NetconfDocumentedException.ErrorType.protocol, NetconfDocumentedException.ErrorTag.unknown_element,
+                    NetconfDocumentedException.ErrorSeverity.error, ImmutableMap.of("bad-element",
+                            rootNode.getNodeName()));
+        }
+    }
+
+    private static boolean isCloseSession(final NetconfMessage incommingDocument) {
+        final Document document = incommingDocument.getDocument();
+        XmlElement rpcElement = XmlElement.fromDomDocument(document);
+        if (rpcElement.getOnlyChildElementOptionally("close-session").isPresent())
+            return true;
+
+        return false;
+    }
+}
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListenerFactory.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListenerFactory.java
new file mode 100644 (file)
index 0000000..eea6dc1
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.impl;
+
+import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouterImpl;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListener;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshot;
+import org.opendaylight.protocol.framework.SessionListenerFactory;
+
+public class NetconfServerSessionListenerFactory implements SessionListenerFactory<NetconfServerSessionListener> {
+
+    private final NetconfOperationServiceFactoryListener factoriesListener;
+
+    private final DefaultCommitNotificationProducer commitNotifier;
+
+    private final SessionIdProvider idProvider;
+
+    public NetconfServerSessionListenerFactory(NetconfOperationServiceFactoryListener factoriesListener,
+            DefaultCommitNotificationProducer commitNotifier, SessionIdProvider idProvider) {
+        this.factoriesListener = factoriesListener;
+        this.commitNotifier = commitNotifier;
+        this.idProvider = idProvider;
+    }
+
+    @Override
+    public NetconfServerSessionListener getSessionListener() {
+        NetconfOperationServiceSnapshot netconfOperationServiceSnapshot = factoriesListener.getSnapshot(idProvider
+                .getCurrentSessionId());
+
+        CapabilityProvider capabilityProvider = new CapabilityProviderImpl(netconfOperationServiceSnapshot);
+
+        NetconfOperationRouterImpl operationRouter = new NetconfOperationRouterImpl(netconfOperationServiceSnapshot,
+                capabilityProvider, commitNotifier);
+
+        return new NetconfServerSessionListener(operationRouter);
+    }
+}
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiator.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiator.java
new file mode 100644 (file)
index 0000000..e14ae3e
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.impl;
+
+import org.opendaylight.controller.netconf.api.NetconfServerSessionPreferences;
+import org.opendaylight.controller.netconf.util.AbstractNetconfSessionNegotiator;
+import org.opendaylight.protocol.framework.SessionListener;
+import org.w3c.dom.Document;
+
+import io.netty.channel.Channel;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.Promise;
+
+public class NetconfServerSessionNegotiator extends
+        AbstractNetconfSessionNegotiator<NetconfServerSessionPreferences, NetconfServerSession> {
+
+    protected NetconfServerSessionNegotiator(NetconfServerSessionPreferences sessionPreferences,
+            Promise<NetconfServerSession> promise, Channel channel, Timer timer, SessionListener sessionListener) {
+        super(sessionPreferences, promise, channel, timer, sessionListener);
+    }
+
+    @Override
+    protected NetconfServerSession getSession(SessionListener sessionListener, Channel channel, Document doc) {
+        return new NetconfServerSession(sessionListener, channel, sessionPreferences.getSessionId());
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiatorFactory.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiatorFactory.java
new file mode 100644 (file)
index 0000000..e747230
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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.impl;
+
+import io.netty.channel.Channel;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.Promise;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfServerSessionPreferences;
+import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListener;
+import org.opendaylight.controller.netconf.impl.util.NetconfUtil;
+import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.protocol.framework.SessionListenerFactory;
+import org.opendaylight.protocol.framework.SessionNegotiator;
+import org.opendaylight.protocol.framework.SessionNegotiatorFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+
+public class NetconfServerSessionNegotiatorFactory implements SessionNegotiatorFactory {
+
+    private final Timer timer;
+
+    private static final Document helloMessageTemplate = loadHelloMessageTemplate();
+    private final SessionIdProvider idProvider;
+    private final NetconfOperationServiceFactoryListener factoriesListener;
+
+    public NetconfServerSessionNegotiatorFactory(Timer timer, NetconfOperationServiceFactoryListener factoriesListener,
+            SessionIdProvider idProvider) {
+        this.timer = timer;
+        this.factoriesListener = factoriesListener;
+        this.idProvider = idProvider;
+    }
+
+    private static Document loadHelloMessageTemplate() {
+        return NetconfUtil.createMessage(
+                NetconfServerSessionNegotiatorFactory.class.getResourceAsStream("/server_hello.xml")).getDocument();
+    }
+
+    @Override
+    public SessionNegotiator getSessionNegotiator(SessionListenerFactory sessionListenerFactory, Channel channel,
+            Promise promise) {
+        long sessionId = idProvider.getNextSessionId();
+
+        NetconfServerSessionPreferences proposal = new NetconfServerSessionPreferences(createHelloMessage(sessionId),
+                sessionId);
+        return new NetconfServerSessionNegotiator(proposal, promise, channel, timer,
+                sessionListenerFactory.getSessionListener());
+    }
+
+    private static final XPathExpression sessionIdXPath = XMLNetconfUtil
+            .compileXPath("/netconf:hello/netconf:session-id");
+    private static final XPathExpression capabilitiesXPath = XMLNetconfUtil
+            .compileXPath("/netconf:hello/netconf:capabilities");
+
+    private NetconfMessage createHelloMessage(long sessionId) {
+        Document helloMessageTemplate = getHelloTemplateClone();
+
+        // change session ID
+        final Node sessionIdNode = (Node) XmlUtil.evaluateXPath(sessionIdXPath, helloMessageTemplate,
+                XPathConstants.NODE);
+        sessionIdNode.setTextContent(String.valueOf(sessionId));
+
+        // add capabilities from yang store
+        final Element capabilitiesElement = (Element) XmlUtil.evaluateXPath(capabilitiesXPath, helloMessageTemplate,
+                XPathConstants.NODE);
+
+        CapabilityProvider capabilityProvider = new CapabilityProviderImpl(factoriesListener.getSnapshot(sessionId));
+
+        for (String capability : capabilityProvider.getCapabilities()) {
+            final Element capabilityElement = helloMessageTemplate.createElement(XmlNetconfConstants.CAPABILITY);
+            capabilityElement.setTextContent(capability);
+            capabilitiesElement.appendChild(capabilityElement);
+        }
+        return new NetconfMessage(helloMessageTemplate);
+    }
+
+    private synchronized Document getHelloTemplateClone() {
+        return (Document) this.helloMessageTemplate.cloneNode(true);
+    }
+}
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/SessionIdProvider.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/SessionIdProvider.java
new file mode 100644 (file)
index 0000000..60fbfd8
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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.impl;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+public final class SessionIdProvider {
+
+    private final AtomicLong sessionCounter = new AtomicLong(0);
+
+    public long getNextSessionId() {
+        return sessionCounter.incrementAndGet();
+    }
+
+    public long getCurrentSessionId() {
+        return sessionCounter.get();
+    }
+}
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/CapabilityProvider.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/CapabilityProvider.java
new file mode 100644 (file)
index 0000000..b785b2d
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * 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.impl.mapping;
+
+import com.google.common.base.Optional;
+
+import java.util.Set;
+
+public interface CapabilityProvider {
+
+    String getSchemaForCapability(String moduleName, Optional<String> revision);
+
+    Set<String> getCapabilities();
+
+}
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCloseSession.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCloseSession.java
new file mode 100644 (file)
index 0000000..1438515
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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.impl.mapping.operations;
+
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
+import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class DefaultCloseSession extends AbstractNetconfOperation {
+    public static final String CLOSE_SESSION = "close-session";
+
+    public DefaultCloseSession(String netconfSessionIdForReporting) {
+        super(netconfSessionIdForReporting);
+    }
+
+    @Override
+    protected HandlingPriority canHandle(String operationName, String netconfOperationNamespace) {
+        if (operationName.equals(CLOSE_SESSION) == false)
+            return HandlingPriority.CANNOT_HANDLE;
+        if (netconfOperationNamespace.equals(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0) == false)
+            return HandlingPriority.CANNOT_HANDLE;
+
+        return HandlingPriority.HANDLE_WITH_MAX_PRIORITY;
+    }
+
+    /**
+     * Close netconf operation router associated to this session, which in turn
+     * closes NetconfOperationServiceSnapshot with all NetconfOperationService
+     * instances
+     */
+    @Override
+    protected Element handle(Document document, XmlElement operationElement, NetconfOperationRouter opRouter)
+            throws NetconfDocumentedException {
+        opRouter.close();
+        return document.createElement(XmlNetconfConstants.OK);
+    }
+}
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCommit.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCommit.java
new file mode 100644 (file)
index 0000000..8637c0d
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * 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.impl.mapping.operations;
+
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
+import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationFilter;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationFilterChain;
+import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation.OperationNameAndNamespace;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.io.InputStream;
+import java.util.Map;
+
+public class DefaultCommit implements NetconfOperationFilter {
+
+    private static final Logger logger = LoggerFactory.getLogger(DefaultCommit.class);
+
+    private static final String NOTIFY_ATTR = "notify";
+
+    private final DefaultCommitNotificationProducer notificationProducer;
+    private final CapabilityProvider cap;
+    private final String netconfSessionIdForReporting;
+
+    public DefaultCommit(DefaultCommitNotificationProducer notifier, CapabilityProvider cap,
+            String netconfSessionIdForReporting) {
+        this.notificationProducer = notifier;
+        this.cap = cap;
+        this.netconfSessionIdForReporting = netconfSessionIdForReporting;
+        this.getConfigMessage = loadGetConfigMessage();
+    }
+
+    private final Document getConfigMessage;
+    public static final String GET_CONFIG_CANDIDATE_XML_LOCATION = "/getConfig_candidate.xml";
+
+    private static Document loadGetConfigMessage() {
+        try (InputStream asStream = DefaultCommit.class.getResourceAsStream(GET_CONFIG_CANDIDATE_XML_LOCATION)) {
+            return XmlUtil.readXmlToDocument(asStream);
+        } catch (Exception e) {
+            throw new IllegalStateException("Unable to load getConfig message for notifications from "
+                    + GET_CONFIG_CANDIDATE_XML_LOCATION);
+        }
+    }
+
+    @Override
+    public Document doFilter(Document message, NetconfOperationRouter operationRouter,
+            NetconfOperationFilterChain filterChain) throws NetconfDocumentedException {
+        OperationNameAndNamespace operationNameAndNamespace = new OperationNameAndNamespace(message);
+        if (canHandle(operationNameAndNamespace)) {
+            if (isCommitWithoutNotification(message)) {
+                message = removePersisterAttributes(message);
+                logger.debug("Skipping commit notification");
+                // fall back to filter chain
+            } else {
+                Document innerResult = filterChain.execute(message, operationRouter);
+                Element cfgSnapshot = getConfigSnapshot(operationRouter);
+                logger.debug("Config snapshot retrieved successfully {}", cfgSnapshot);
+                notificationProducer.sendCommitNotification("ok", cfgSnapshot, cap.getCapabilities());
+                return innerResult;
+            }
+        }
+        return filterChain.execute(message, operationRouter);
+    }
+
+    @Override
+    public int getSoringOrder() {
+        return 0;
+    }
+
+    @Override
+    public int compareTo(NetconfOperationFilter o) {
+        return Integer.compare(getSoringOrder(), o.getSoringOrder());
+    }
+
+    private boolean canHandle(OperationNameAndNamespace operationNameAndNamespace) {
+        if (operationNameAndNamespace.getOperationName().equals("commit") == false)
+            return false;
+        return operationNameAndNamespace.getNamespace().equals(
+                XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+    }
+
+    private Document removePersisterAttributes(Document message) {
+        final Element documentElement = message.getDocumentElement();
+        documentElement.removeAttribute(NOTIFY_ATTR);
+        return message;
+    }
+
+    private boolean isCommitWithoutNotification(Document message) {
+        XmlElement xmlElement = XmlElement.fromDomElementWithExpected(message.getDocumentElement(),
+                XmlNetconfConstants.RPC_KEY, XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+
+        String attr = xmlElement.getAttribute(NOTIFY_ATTR);
+
+        if (attr == null || attr.equals(""))
+            return false;
+        else if (attr.equals(Boolean.toString(false))) {
+            logger.debug("Commit operation received with notify=false attribute {}", message);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private Element getConfigSnapshot(NetconfOperationRouter opRouter) throws NetconfDocumentedException {
+        final Document responseDocument = opRouter.onNetconfMessage(getConfigMessage);
+
+        XmlElement dataElement;
+        try {
+            XmlElement xmlElement = XmlElement.fromDomElementWithExpected(responseDocument.getDocumentElement(),
+                    XmlNetconfConstants.RPC_REPLY_KEY, XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+            dataElement = xmlElement.getOnlyChildElement(XmlNetconfConstants.DATA_KEY);
+        } catch (IllegalArgumentException e) {
+            final String msg = "Unexpected response from get-config operation";
+            logger.warn(msg, e);
+            Map<String, String> info = Maps.newHashMap();
+            info.put(NetconfDocumentedException.ErrorTag.operation_failed.toString(), e.getMessage());
+            throw new NetconfDocumentedException(msg, e, NetconfDocumentedException.ErrorType.application,
+                    NetconfDocumentedException.ErrorTag.operation_failed,
+                    NetconfDocumentedException.ErrorSeverity.error, info);
+        }
+
+        return dataElement.getDomElement();
+    }
+
+    @Override
+    public String toString() {
+        return "DefaultCommit{" + netconfSessionIdForReporting + '}';
+    }
+}
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultGetSchema.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultGetSchema.java
new file mode 100644 (file)
index 0000000..3e65ed8
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * 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.impl.mapping.operations;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
+import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
+import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public final class DefaultGetSchema extends AbstractNetconfOperation {
+
+    private final CapabilityProvider cap;
+
+    private static final Logger logger = LoggerFactory.getLogger(DefaultGetSchema.class);
+
+    public DefaultGetSchema(CapabilityProvider cap, String netconfSessionIdForReporting) {
+        super(netconfSessionIdForReporting);
+        this.cap = cap;
+    }
+
+    public static final String GET_SCHEMA = "get-schema";
+    public static final String IDENTIFIER = "identifier";
+    public static final String VERSION = "version";
+
+    @Override
+    protected HandlingPriority canHandle(String netconfOperationName, String namespace) {
+        if (netconfOperationName.equals("get-schema") == false)
+            return HandlingPriority.CANNOT_HANDLE;
+        if (namespace.equals(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_YANG_IETF_NETCONF_MONITORING) == false)
+            return HandlingPriority.CANNOT_HANDLE;
+
+        return HandlingPriority.HANDLE_WITH_DEFAULT_PRIORITY;
+    }
+
+    @Override
+    protected Element handle(Document document, XmlElement xml, NetconfOperationRouter router)
+            throws NetconfDocumentedException {
+        GetSchemaEntry entry;
+
+        try {
+            entry = new GetSchemaEntry(xml);
+        } catch (final IllegalArgumentException e) {
+            logger.warn("Error parsing xml", e);
+            final Map<String, String> errorInfo = new HashMap<>();
+            errorInfo.put(NetconfDocumentedException.ErrorTag.bad_attribute.name(), e.getMessage());
+            throw new NetconfDocumentedException(e.getMessage(), e, NetconfDocumentedException.ErrorType.rpc,
+                    NetconfDocumentedException.ErrorTag.bad_attribute, NetconfDocumentedException.ErrorSeverity.error,
+                    errorInfo);
+        } catch (final IllegalStateException e) {
+            logger.warn("Error parsing xml", e);
+            final Map<String, String> errorInfo = new HashMap<>();
+            errorInfo.put(NetconfDocumentedException.ErrorTag.bad_attribute.name(), e.getMessage());
+            throw new NetconfDocumentedException(e.getMessage(), e, NetconfDocumentedException.ErrorType.rpc,
+                    NetconfDocumentedException.ErrorTag.bad_attribute, NetconfDocumentedException.ErrorSeverity.error,
+                    errorInfo);
+        }
+
+        String schema;
+        try {
+            schema = cap.getSchemaForCapability(entry.identifier, entry.version);
+        } catch (IllegalStateException e) {
+            Map<String, String> errorInfo = Maps.newHashMap();
+            errorInfo.put(entry.identifier, e.getMessage());
+            logger.warn("Rpc error: {}", NetconfDocumentedException.ErrorTag.operation_failed, e);
+            throw new NetconfDocumentedException(e.getMessage(), NetconfDocumentedException.ErrorType.application,
+                    NetconfDocumentedException.ErrorTag.operation_failed,
+                    NetconfDocumentedException.ErrorSeverity.error, errorInfo);
+        }
+
+        Element getSchemaResult;
+        getSchemaResult = XmlUtil.createTextElement(document, XmlNetconfConstants.DATA_KEY, schema);
+        XmlUtil.addNamespaceAttr(getSchemaResult,
+                XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_YANG_IETF_NETCONF_MONITORING);
+
+        logger.info("{} operation successful", GET_SCHEMA);
+
+        return getSchemaResult;
+    }
+
+    private static final class GetSchemaEntry {
+        private final String identifier;
+        private final Optional<String> version;
+
+        GetSchemaEntry(XmlElement getSchemaElement) {
+            getSchemaElement.checkName(GET_SCHEMA);
+            getSchemaElement.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_YANG_IETF_NETCONF_MONITORING);
+
+            XmlElement identifierElement = getSchemaElement.getOnlyChildElementWithSameNamespace(IDENTIFIER);
+            identifier = identifierElement.getTextContent();
+            Optional<XmlElement> versionElement = getSchemaElement
+                    .getOnlyChildElementWithSameNamespaceOptionally(VERSION);
+            if (versionElement.isPresent()) {
+                version = Optional.of(versionElement.get().getTextContent());
+            } else {
+                version = Optional.absent();
+            }
+
+        }
+    }
+}
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java
new file mode 100644 (file)
index 0000000..96da1a6
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.impl.osgi;
+
+import com.google.common.base.Optional;
+import io.netty.util.HashedWheelTimer;
+import org.opendaylight.controller.config.stat.ConfigProvider;
+import org.opendaylight.controller.netconf.impl.*;
+import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
+import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil.TLSConfiguration;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.SSLContext;
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+
+public class NetconfImplActivator implements BundleActivator {
+
+    private static final Logger logger = LoggerFactory.getLogger(NetconfImplActivator.class);
+
+    private Optional<InetSocketAddress> maybeTCPAddress;
+    private Optional<TLSConfiguration> maybeTLSConfiguration;
+
+    private NetconfOperationServiceFactoryTracker factoriesTracker;
+    private DefaultCommitNotificationProducer commitNot;
+    private NetconfServerDispatcher dispatch;
+
+    @Override
+    public void start(final BundleContext context) throws Exception {
+        final ConfigProvider configProvider = new ConfigProvider.ConfigProviderImpl(context);
+        maybeTCPAddress = NetconfConfigUtil.extractTCPNetconfAddress(configProvider);
+        maybeTLSConfiguration = NetconfConfigUtil.extractTLSConfiguration(configProvider);
+        if (maybeTCPAddress.isPresent() == false && maybeTLSConfiguration.isPresent() == false) {
+            throw new IllegalStateException("TCP nor TLS is configured, netconf not available.");
+        }
+        NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
+        factoriesTracker = new NetconfOperationServiceFactoryTracker(context, factoriesListener);
+        factoriesTracker.open();
+
+        SessionIdProvider idProvider = new SessionIdProvider();
+        NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
+                new HashedWheelTimer(), factoriesListener, idProvider);
+
+        commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer());
+
+        NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
+                factoriesListener, commitNot, idProvider);
+
+        if (maybeTCPAddress.isPresent()) {
+            Optional<SSLContext> maybeSSLContext = Optional.absent();
+            InetSocketAddress address = maybeTCPAddress.get();
+            dispatch = new NetconfServerDispatcher(maybeSSLContext, serverNegotiatorFactory, listenerFactory);
+
+            logger.info("Starting TCP netconf server at {}", address);
+            dispatch.createServer(address);
+        }
+        if (maybeTLSConfiguration.isPresent()) {
+            Optional<SSLContext> maybeSSLContext = Optional.of(maybeTLSConfiguration.get().getSslContext());
+            InetSocketAddress address = maybeTLSConfiguration.get().getAddress();
+            dispatch = new NetconfServerDispatcher(maybeSSLContext, serverNegotiatorFactory, listenerFactory);
+
+            logger.info("Starting TLS netconf server at {}", address);
+            dispatch.createServer(address);
+        }
+    }
+
+    @Override
+    public void stop(final BundleContext context) throws Exception {
+        logger.info("Shutting down netconf because YangStoreService service was removed");
+
+        commitNot.close();
+        dispatch.close();
+    }
+}
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java
new file mode 100644 (file)
index 0000000..c7e1740
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * 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.impl.osgi;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
+import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
+import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession;
+import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCommit;
+import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultGetSchema;
+import org.opendaylight.controller.netconf.mapping.api.*;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+
+import java.util.*;
+
+public class NetconfOperationRouterImpl implements NetconfOperationRouter {
+
+    private static final Logger logger = LoggerFactory.getLogger(NetconfOperationRouterImpl.class);
+
+    private final NetconfOperationServiceSnapshot netconfOperationServiceSnapshot;
+
+    private final Set<NetconfOperation> allNetconfOperations;
+    private final TreeSet<NetconfOperationFilter> allSortedFilters;
+
+    private final CapabilityProvider capabilityProvider;
+
+    public NetconfOperationRouterImpl(NetconfOperationServiceSnapshot netconfOperationServiceSnapshot,
+            CapabilityProvider capabilityProvider, DefaultCommitNotificationProducer commitNotifier) {
+
+        this.netconfOperationServiceSnapshot = netconfOperationServiceSnapshot;
+
+        this.capabilityProvider = capabilityProvider;
+
+        Set<NetconfOperation> defaultNetconfOperations = Sets.newHashSet();
+        defaultNetconfOperations.add(new DefaultGetSchema(capabilityProvider, netconfOperationServiceSnapshot
+                .getNetconfSessionIdForReporting()));
+        defaultNetconfOperations.add(new DefaultCloseSession(netconfOperationServiceSnapshot
+                .getNetconfSessionIdForReporting()));
+
+        allNetconfOperations = getAllNetconfOperations(defaultNetconfOperations, netconfOperationServiceSnapshot);
+
+        DefaultCommit defaultCommit = new DefaultCommit(commitNotifier, capabilityProvider,
+                netconfOperationServiceSnapshot.getNetconfSessionIdForReporting());
+        Set<NetconfOperationFilter> defaultFilters = Sets.<NetconfOperationFilter> newHashSet(defaultCommit);
+        allSortedFilters = getAllNetconfFilters(defaultFilters, netconfOperationServiceSnapshot);
+    }
+
+    private static Set<NetconfOperation> getAllNetconfOperations(Set<NetconfOperation> defaultNetconfOperations,
+            NetconfOperationServiceSnapshot netconfOperationServiceSnapshot) {
+        Set<NetconfOperation> result = new HashSet<>();
+        result.addAll(defaultNetconfOperations);
+
+        for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) {
+            final Set<NetconfOperation> netOpsFromService = netconfOperationService.getNetconfOperations();
+            for (NetconfOperation netconfOperation : netOpsFromService) {
+                Preconditions.checkState(result.contains(netconfOperation) == false,
+                        "Netconf operation %s already present", netconfOperation);
+                result.add(netconfOperation);
+            }
+        }
+        return Collections.unmodifiableSet(result);
+    }
+
+    private static TreeSet<NetconfOperationFilter> getAllNetconfFilters(Set<NetconfOperationFilter> defaultFilters,
+            NetconfOperationServiceSnapshot netconfOperationServiceSnapshot) {
+        TreeSet<NetconfOperationFilter> result = new TreeSet<>(defaultFilters);
+        for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) {
+            final Set<NetconfOperationFilter> filtersFromService = netconfOperationService.getFilters();
+            for (NetconfOperationFilter filter : filtersFromService) {
+                Preconditions.checkState(result.contains(filter) == false, "Filter %s already present", filter);
+                result.add(filter);
+            }
+        }
+        return result;
+    }
+
+    public CapabilityProvider getCapabilityProvider() {
+        return capabilityProvider;
+    }
+
+    @Override
+    public synchronized Document onNetconfMessage(Document message) throws NetconfDocumentedException {
+        NetconfOperationExecution netconfOperationExecution = getNetconfOperationWithHighestPriority(message);
+        logger.debug("Forwarding netconf message {} to {}", XmlUtil.toString(message),
+                netconfOperationExecution.operationWithHighestPriority);
+
+        final LinkedList<NetconfOperationFilterChain> chain = new LinkedList<>();
+        chain.push(netconfOperationExecution);
+
+        for (Iterator<NetconfOperationFilter> it = allSortedFilters.descendingIterator(); it.hasNext();) {
+            final NetconfOperationFilter filter = it.next();
+            final NetconfOperationFilterChain prevItem = chain.getFirst();
+            NetconfOperationFilterChain currentItem = new NetconfOperationFilterChain() {
+                @Override
+                public Document execute(Document message, NetconfOperationRouter operationRouter)
+                        throws NetconfDocumentedException {
+                    logger.trace("Entering {}", filter);
+                    return filter.doFilter(message, operationRouter, prevItem);
+                }
+            };
+            chain.push(currentItem);
+        }
+        return chain.getFirst().execute(message, this);
+    }
+
+    private NetconfOperationExecution getNetconfOperationWithHighestPriority(Document message) {
+
+        // TODO test
+        TreeMap<HandlingPriority, Set<NetconfOperation>> sortedPriority = getSortedNetconfOperationsWithCanHandle(message);
+
+        Preconditions.checkState(sortedPriority.isEmpty() == false, "No %s available to handle message %s",
+                NetconfOperation.class.getName(), XmlUtil.toString(message));
+
+        HandlingPriority highestFoundPriority = sortedPriority.lastKey();
+
+        int netconfOperationsWithHighestPriority = sortedPriority.get(highestFoundPriority).size();
+
+        Preconditions.checkState(netconfOperationsWithHighestPriority == 1,
+                "Multiple %s available to handle message %s", NetconfOperation.class.getName(), message);
+
+        return new NetconfOperationExecution(sortedPriority, highestFoundPriority);
+    }
+
+    private TreeMap<HandlingPriority, Set<NetconfOperation>> getSortedNetconfOperationsWithCanHandle(Document message) {
+        TreeMap<HandlingPriority, Set<NetconfOperation>> sortedPriority = Maps.newTreeMap();
+
+        for (NetconfOperation netconfOperation : allNetconfOperations) {
+            final HandlingPriority handlingPriority = netconfOperation.canHandle(message);
+
+            if (handlingPriority.equals(HandlingPriority.CANNOT_HANDLE) == false) {
+                Set<NetconfOperation> netconfOperations = sortedPriority.get(handlingPriority);
+                netconfOperations = checkIfNoOperationsOnPriority(sortedPriority, handlingPriority, netconfOperations);
+                netconfOperations.add(netconfOperation);
+            }
+        }
+        return sortedPriority;
+    }
+
+    private Set<NetconfOperation> checkIfNoOperationsOnPriority(
+            TreeMap<HandlingPriority, Set<NetconfOperation>> sortedPriority, HandlingPriority handlingPriority,
+            Set<NetconfOperation> netconfOperations) {
+        if (netconfOperations == null) {
+            netconfOperations = Sets.newHashSet();
+            sortedPriority.put(handlingPriority, netconfOperations);
+        }
+        return netconfOperations;
+    }
+
+    @Override
+    public void close() {
+        netconfOperationServiceSnapshot.close();
+    }
+
+    private class NetconfOperationExecution implements NetconfOperationFilterChain {
+        private final NetconfOperation operationWithHighestPriority;
+
+        private NetconfOperationExecution(NetconfOperation operationWithHighestPriority) {
+            this.operationWithHighestPriority = operationWithHighestPriority;
+        }
+
+        public NetconfOperationExecution(TreeMap<HandlingPriority, Set<NetconfOperation>> sortedPriority,
+                HandlingPriority highestFoundPriority) {
+            operationWithHighestPriority = sortedPriority.get(highestFoundPriority).iterator().next();
+            sortedPriority.remove(highestFoundPriority);
+        }
+
+        @Override
+        public Document execute(Document message, NetconfOperationRouter router) throws NetconfDocumentedException {
+            return operationWithHighestPriority.handle(message, router);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "NetconfOperationRouterImpl{" + "netconfOperationServiceSnapshot=" + netconfOperationServiceSnapshot
+                + '}';
+    }
+}
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryListener.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryListener.java
new file mode 100644 (file)
index 0000000..385ad09
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.impl.osgi;
+
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+
+public interface NetconfOperationServiceFactoryListener {
+
+    void onAddNetconfOperationServiceFactory(NetconfOperationServiceFactory service);
+
+    void onRemoveNetconfOperationServiceFactory(NetconfOperationServiceFactory service);
+
+    NetconfOperationServiceSnapshot getSnapshot(long sessionId);
+}
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryListenerImpl.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryListenerImpl.java
new file mode 100644 (file)
index 0000000..134c38b
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.impl.osgi;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+
+public class NetconfOperationServiceFactoryListenerImpl implements NetconfOperationServiceFactoryListener {
+    private final Set<NetconfOperationServiceFactory> allFactories = new HashSet<>();
+
+    @Override
+    public synchronized void onAddNetconfOperationServiceFactory(NetconfOperationServiceFactory service) {
+        allFactories.add(service);
+    }
+
+    @Override
+    public synchronized void onRemoveNetconfOperationServiceFactory(NetconfOperationServiceFactory service) {
+        allFactories.remove(service);
+    }
+
+    @Override
+    public synchronized NetconfOperationServiceSnapshot getSnapshot(long sessionId) {
+        return new NetconfOperationServiceSnapshot(allFactories, sessionId);
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryTracker.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryTracker.java
new file mode 100644 (file)
index 0000000..4d2bcb3
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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.impl.osgi;
+
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+class NetconfOperationServiceFactoryTracker extends
+        ServiceTracker<NetconfOperationServiceFactory, NetconfOperationServiceFactory> {
+    private final NetconfOperationServiceFactoryListener operationRouter;
+
+    NetconfOperationServiceFactoryTracker(BundleContext context,
+            final NetconfOperationServiceFactoryListener operationRouter) {
+        super(context, NetconfOperationServiceFactory.class, null);
+        this.operationRouter = operationRouter;
+    }
+
+    @Override
+    public NetconfOperationServiceFactory addingService(ServiceReference<NetconfOperationServiceFactory> reference) {
+        NetconfOperationServiceFactory netconfOperationServiceFactory = super.addingService(reference);
+        operationRouter.onAddNetconfOperationServiceFactory(netconfOperationServiceFactory);
+        return netconfOperationServiceFactory;
+    }
+
+    @Override
+    public void removedService(ServiceReference<NetconfOperationServiceFactory> reference,
+            NetconfOperationServiceFactory netconfOperationServiceFactory) {
+        operationRouter.onRemoveNetconfOperationServiceFactory(netconfOperationServiceFactory);
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceSnapshot.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceSnapshot.java
new file mode 100644 (file)
index 0000000..cb4f532
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.impl.osgi;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NetconfOperationServiceSnapshot implements AutoCloseable {
+    private static final Logger logger = LoggerFactory.getLogger(NetconfOperationServiceSnapshot.class);
+
+    private final Set<NetconfOperationService> services;
+    private final String netconfSessionIdForReporting;
+
+    public NetconfOperationServiceSnapshot(Set<NetconfOperationServiceFactory> factories, long sessionId) {
+        Set<NetconfOperationService> services = new HashSet<>();
+        netconfSessionIdForReporting = getNetconfSessionIdForReporting(sessionId);
+        for (NetconfOperationServiceFactory factory : factories) {
+            services.add(factory.createService(sessionId, netconfSessionIdForReporting));
+        }
+        this.services = Collections.unmodifiableSet(services);
+    }
+
+    private static String getNetconfSessionIdForReporting(long sessionId) {
+        return "netconf session id " + sessionId;
+    }
+
+    public String getNetconfSessionIdForReporting() {
+        return netconfSessionIdForReporting;
+    }
+
+    public Set<NetconfOperationService> getServices() {
+        return services;
+    }
+
+    @Override
+    public void close() {
+        RuntimeException firstException = null;
+        for (NetconfOperationService service : services) {
+            try {
+                service.close();
+            } catch (RuntimeException e) {
+                logger.warn("Got exception while closing {}", service, e);
+                if (firstException == null) {
+                    firstException = e;
+                } else {
+                    firstException.addSuppressed(e);
+                }
+            }
+        }
+        if (firstException != null) {
+            throw firstException;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "NetconfOperationServiceSnapshot{" + netconfSessionIdForReporting + '}';
+    }
+}
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/DeserializerExceptionHandler.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/DeserializerExceptionHandler.java
new file mode 100644 (file)
index 0000000..b27cd20
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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.impl.util;
+
+import java.util.Map;
+
+import org.opendaylight.controller.netconf.api.NetconfDeserializerException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.util.messages.SendErrorExceptionUtil;
+
+import com.google.common.collect.Maps;
+
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+
+public final class DeserializerExceptionHandler implements ChannelHandler {
+
+    @Override
+    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
+        // NOOP
+    }
+
+    @Override
+    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+        // NOOP
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+        if (cause instanceof NetconfDeserializerException) {
+            handleDeserializerException(ctx, cause);
+        }
+    }
+
+    private void handleDeserializerException(ChannelHandlerContext ctx, Throwable cause) {
+
+        Map<String, String> info = Maps.newHashMap();
+        info.put("cause", cause.getMessage());
+        NetconfDocumentedException ex = new NetconfDocumentedException(cause.getMessage(),
+                NetconfDocumentedException.ErrorType.rpc, NetconfDocumentedException.ErrorTag.malformed_message,
+                NetconfDocumentedException.ErrorSeverity.error, info);
+
+        SendErrorExceptionUtil.sendErrorMessage(ctx.channel(), ex);
+    }
+}
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/NetconfUtil.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/NetconfUtil.java
new file mode 100644 (file)
index 0000000..0aa6fb3
--- /dev/null
@@ -0,0 +1,37 @@
+package org.opendaylight.controller.netconf.impl.util;
+
+import java.io.*;
+
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+// TODO purge nulls
+public class NetconfUtil {
+
+    private static final Logger logger = LoggerFactory.getLogger(NetconfUtil.class);
+
+    public static NetconfMessage createMessage(final File f) {
+        try {
+            return createMessage(new FileInputStream(f));
+        } catch (final FileNotFoundException e) {
+            logger.warn("File {} not found.", f, e);
+        }
+        return null;
+    }
+
+    public static NetconfMessage createMessage(final InputStream is) {
+        Document doc = null;
+        try {
+            doc = XmlUtil.readXmlToDocument(is);
+        } catch (final IOException e) {
+            logger.warn("Error ocurred while parsing stream.", e);
+        } catch (final SAXException e) {
+            logger.warn("Error ocurred while final parsing stream.", e);
+        }
+        return (doc == null) ? null : new NetconfMessage(doc);
+    }
+}
diff --git a/opendaylight/netconf/netconf-impl/src/main/resources/getConfig_candidate.xml b/opendaylight/netconf/netconf-impl/src/main/resources/getConfig_candidate.xml
new file mode 100644 (file)
index 0000000..3ac18b5
--- /dev/null
@@ -0,0 +1,7 @@
+<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="commitNotification">
+    <get-config>
+        <source>
+            <candidate/>
+        </source>
+    </get-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-impl/src/main/resources/server_hello.xml b/opendaylight/netconf/netconf-impl/src/main/resources/server_hello.xml
new file mode 100644 (file)
index 0000000..6a3f911
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <capabilities>
+        <capability>urn:ietf:params:netconf:base:1.0</capability>
+    </capabilities>
+    <session-id>1</session-id>
+</hello>
diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java
new file mode 100644 (file)
index 0000000..54a3482
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * 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.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
+import io.netty.channel.ChannelFuture;
+import io.netty.util.HashedWheelTimer;
+import org.apache.commons.io.IOUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.util.ConfigRegistryJMXClient;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.opendaylight.controller.config.yang.store.api.YangStoreService;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.client.NetconfClient;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
+import org.opendaylight.controller.netconf.mapping.api.*;
+import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+
+import javax.management.ObjectName;
+import javax.net.ssl.SSLContext;
+import java.io.DataOutputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+public class ConcurrentClientsTest {
+
+    private static final int CONCURRENCY = 16;
+    @Mock
+    private YangStoreService yangStoreService;
+    @Mock
+    private ConfigRegistryJMXClient jmxClient;
+
+    private final InetSocketAddress netconfAddress = new InetSocketAddress("127.0.0.1", 8303);
+
+    static final Logger logger = LoggerFactory.getLogger(ConcurrentClientsTest.class);
+
+    private DefaultCommitNotificationProducer commitNot;
+    private NetconfServerDispatcher dispatch;
+
+    @Before
+    public void setUp() throws Exception {
+        { // init mocks
+            MockitoAnnotations.initMocks(this);
+            final YangStoreSnapshot yStore = mock(YangStoreSnapshot.class);
+            doReturn(yStore).when(this.yangStoreService).getYangStoreSnapshot();
+            doReturn(Collections.emptyMap()).when(yStore).getModuleMXBeanEntryMap();
+            doReturn(Collections.emptyMap()).when(yStore).getModuleMap();
+
+            final ConfigTransactionJMXClient mockedTCl = mock(ConfigTransactionJMXClient.class);
+            doReturn(mockedTCl).when(this.jmxClient).getConfigTransactionClient(any(ObjectName.class));
+
+            doReturn(Collections.emptySet()).when(jmxClient).lookupConfigBeans();
+        }
+
+        NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
+        factoriesListener.onAddNetconfOperationServiceFactory(mockOpF());
+
+        SessionIdProvider idProvider = new SessionIdProvider();
+        NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
+                new HashedWheelTimer(5000, TimeUnit.MILLISECONDS), factoriesListener, idProvider);
+
+        commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer());
+
+        NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
+                factoriesListener, commitNot, idProvider);
+        dispatch = new NetconfServerDispatcher(Optional.<SSLContext> absent(), serverNegotiatorFactory, listenerFactory);
+
+        ChannelFuture s = dispatch.createServer(netconfAddress);
+        s.await();
+    }
+
+    private NetconfOperationServiceFactory mockOpF() {
+        return new NetconfOperationServiceFactory() {
+            @Override
+            public NetconfOperationService createService(long netconfSessionId, String netconfSessionIdForReporting) {
+                return new NetconfOperationService() {
+                    @Override
+                    public Set<Capability> getCapabilities() {
+                        return Collections.emptySet();
+                    }
+
+                    @Override
+                    public Set<NetconfOperation> getNetconfOperations() {
+                        return Sets.<NetconfOperation> newHashSet(new NetconfOperation() {
+                            @Override
+                            public HandlingPriority canHandle(Document message) {
+                                return HandlingPriority.getHandlingPriority(Integer.MAX_VALUE);
+                            }
+
+                            @Override
+                            public Document handle(Document message, NetconfOperationRouter operationRouter)
+                                    throws NetconfDocumentedException {
+                                try {
+                                    return XmlUtil.readXmlToDocument("<test/>");
+                                } catch (Exception e) {
+                                    throw new RuntimeException(e);
+                                }
+                            }
+                        });
+                    }
+
+                    @Override
+                    public Set<NetconfOperationFilter> getFilters() {
+                        return Collections.emptySet();
+                    }
+
+                    @Override
+                    public void close() {
+                    }
+                };
+            }
+        };
+    }
+
+    @After
+    public void cleanUp() throws Exception {
+        commitNot.close();
+        dispatch.close();
+    }
+
+    @Test
+    public void multipleClients() throws Exception {
+        List<TestingThread> threads = new ArrayList<>();
+
+        final int attempts = 5;
+        for (int i = 0; i < CONCURRENCY; i++) {
+            TestingThread thread = new TestingThread(String.valueOf(i), attempts);
+            threads.add(thread);
+            thread.start();
+        }
+
+        for (TestingThread thread : threads) {
+            thread.join();
+            assertTrue(thread.success);
+        }
+    }
+
+    @Test
+    public void synchronizationTest() throws Exception {
+        new BlockingThread("foo").run2();
+    }
+
+    @Test
+    public void multipleBlockingClients() throws Exception {
+        List<BlockingThread> threads = new ArrayList<>();
+        for (int i = 0; i < CONCURRENCY; i++) {
+            BlockingThread thread = new BlockingThread(String.valueOf(i));
+            threads.add(thread);
+            thread.start();
+        }
+
+        for (BlockingThread thread : threads) {
+            thread.join();
+            assertTrue(thread.success);
+        }
+    }
+
+    class BlockingThread extends Thread {
+        Boolean success;
+
+        public BlockingThread(String name) {
+            super("client-" + name);
+        }
+
+        @Override
+        public void run() {
+            try {
+                run2();
+                success = true;
+            } catch (Exception e) {
+                success = false;
+                throw new RuntimeException(e);
+            }
+        }
+
+        private void run2() throws Exception {
+            InputStream clientHello = checkNotNull(XmlFileLoader
+                    .getResourceAsStream("netconfMessages/client_hello.xml"));
+            InputStream getConfig = checkNotNull(XmlFileLoader.getResourceAsStream("netconfMessages/getConfig.xml"));
+
+            Socket clientSocket = new Socket(netconfAddress.getHostString(), netconfAddress.getPort());
+            DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
+            InputStreamReader inFromServer = new InputStreamReader(clientSocket.getInputStream());
+
+            StringBuffer sb = new StringBuffer();
+            while (sb.toString().endsWith("]]>]]>") == false) {
+                sb.append((char) inFromServer.read());
+            }
+            logger.info(sb.toString());
+
+            outToServer.write(IOUtils.toByteArray(clientHello));
+            outToServer.write("]]>]]>".getBytes());
+            outToServer.flush();
+            // Thread.sleep(100);
+            outToServer.write(IOUtils.toByteArray(getConfig));
+            outToServer.write("]]>]]>".getBytes());
+            outToServer.flush();
+            Thread.sleep(100);
+            sb = new StringBuffer();
+            while (sb.toString().endsWith("]]>]]>") == false) {
+                sb.append((char) inFromServer.read());
+            }
+            logger.info(sb.toString());
+            clientSocket.close();
+        }
+    }
+
+    class TestingThread extends Thread {
+
+        private final String clientId;
+        private final int attempts;
+        private Boolean success;
+
+        TestingThread(String clientId, int attempts) {
+            this.clientId = clientId;
+            this.attempts = attempts;
+            setName("client-" + clientId);
+        }
+
+        @Override
+        public void run() {
+            try {
+                final NetconfClient netconfClient = new NetconfClient(clientId, netconfAddress);
+                long sessionId = netconfClient.getSessionId();
+                logger.info("Client with sessionid {} hello exchanged", sessionId);
+
+                final NetconfMessage getMessage = XmlFileLoader
+                        .xmlFileToNetconfMessage("netconfMessages/getConfig.xml");
+                NetconfMessage result = netconfClient.sendMessage(getMessage);
+                logger.info("Client with sessionid {} got result {}", sessionId, result);
+                netconfClient.close();
+                logger.info("Client with session id {} ended", sessionId);
+                success = true;
+            } catch (final Exception e) {
+                success = false;
+                throw new RuntimeException(e);
+            }
+        }
+    }
+}
diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/MessageHeaderTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/MessageHeaderTest.java
new file mode 100644 (file)
index 0000000..aa3c5d4
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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.impl;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.netconf.util.messages.NetconfMessageHeader;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+public class MessageHeaderTest {
+
+    private NetconfMessageHeader header = null;
+
+    @Before
+    public void setUp() {
+        this.header = new NetconfMessageHeader();
+    }
+
+    @Test
+    public void testFromBytes() {
+        final byte[] raw = new byte[] { (byte) 0x0a, (byte) 0x23, (byte) 0x35, (byte) 0x38, (byte) 0x0a };
+        this.header.fromBytes(raw);
+        assertEquals(58, this.header.getLength());
+    }
+
+    @Test
+    public void testToBytes() {
+        this.header.setLength(123);
+        assertArrayEquals(new byte[] { (byte) 0x0a, (byte) 0x23, (byte) 0x31, (byte) 0x32, (byte) 0x33, (byte) 0x0a },
+                this.header.toBytes());
+    }
+}
diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/MessageParserTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/MessageParserTest.java
new file mode 100644 (file)
index 0000000..39f45e0
--- /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.impl;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.custommonkey.xmlunit.XMLAssert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.util.messages.FramingMechanism;
+import org.opendaylight.controller.netconf.util.messages.NetconfMessageFactory;
+import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.protocol.framework.DeserializerException;
+import org.opendaylight.protocol.framework.DocumentedException;
+import org.opendaylight.protocol.util.ByteArray;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+public class MessageParserTest {
+
+    private NetconfMessageFactory parser = null;
+
+    @Before
+    public void setUp() {
+        this.parser = new NetconfMessageFactory();
+    }
+
+    @Test
+    public void testPutEOM() throws IOException, SAXException, ParserConfigurationException {
+        final NetconfMessage msg = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/client_hello.xml");
+        final byte[] bytes = this.parser.put(msg);
+        assertArrayEquals(NetconfMessageFactory.endOfMessage, ByteArray.subByte(bytes, bytes.length
+                - NetconfMessageFactory.endOfMessage.length, NetconfMessageFactory.endOfMessage.length));
+    }
+
+    @Ignore
+    @Test
+    // TODO not working on WINDOWS
+    // arrays first differed at element [4]; expected:<49> but was:<53>
+    // at
+    // org.junit.internal.ComparisonCriteria.arrayEquals(ComparisonCriteria.java:52)
+    public void testPutChunk() throws IOException, SAXException, ParserConfigurationException {
+        final NetconfMessage msg = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/client_hello.xml");
+        this.parser.setFramingMechanism(FramingMechanism.CHUNK);
+        final byte[] bytes = this.parser.put(msg);
+        final byte[] header = new byte[] { (byte) 0x0a, (byte) 0x23, (byte) 0x32, (byte) 0x31, (byte) 0x31, (byte) 0x0a };
+        assertArrayEquals(header, ByteArray.subByte(bytes, 0, header.length));
+        assertArrayEquals(NetconfMessageFactory.endOfChunk, ByteArray.subByte(bytes, bytes.length
+                - NetconfMessageFactory.endOfChunk.length, NetconfMessageFactory.endOfChunk.length));
+    }
+
+    @Test
+    public void testParseEOM() throws IOException, SAXException, DeserializerException, DocumentedException,
+            ParserConfigurationException {
+        final Document msg = XmlFileLoader.xmlFileToDocument("netconfMessages/client_hello.xml");
+        final byte[] bytes = this.parser.put(new NetconfMessage(msg));
+        final Document doc = this.parser
+                .parse(ByteArray.subByte(bytes, 0, bytes.length - NetconfMessageFactory.endOfMessage.length))
+                .iterator().next().getDocument();
+        assertEquals(XmlUtil.toString(msg), XmlUtil.toString(doc));
+        XMLAssert.assertXMLEqual(msg, doc);
+    }
+
+    @Test
+    public void testParseChunk() throws IOException, SAXException, DeserializerException, DocumentedException,
+            ParserConfigurationException {
+        final Document msg = XmlFileLoader.xmlFileToDocument("netconfMessages/client_hello.xml");
+        this.parser.setFramingMechanism(FramingMechanism.CHUNK);
+        final byte[] bytes = this.parser.put(new NetconfMessage(msg));
+        final Document doc = this.parser
+                .parse(ByteArray.subByte(bytes, 6, bytes.length - NetconfMessageFactory.endOfChunk.length - 6))
+                .iterator().next().getDocument();
+        assertEquals(XmlUtil.toString(msg), XmlUtil.toString(doc));
+        XMLAssert.assertXMLEqual(msg, doc);
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/NetconfDispatcherImplTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/NetconfDispatcherImplTest.java
new file mode 100644 (file)
index 0000000..ecbde59
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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.impl;
+
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+
+import javax.net.ssl.SSLContext;
+
+import org.junit.Test;
+import org.opendaylight.controller.netconf.impl.*;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListener;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
+
+import com.google.common.base.Optional;
+
+import io.netty.channel.ChannelFuture;
+import io.netty.util.HashedWheelTimer;
+
+public class NetconfDispatcherImplTest {
+
+    @Test
+    public void test() throws Exception {
+
+        DefaultCommitNotificationProducer commitNot = new DefaultCommitNotificationProducer(
+                ManagementFactory.getPlatformMBeanServer());
+        NetconfOperationServiceFactoryListener factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
+
+        SessionIdProvider idProvider = new SessionIdProvider();
+        NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
+                new HashedWheelTimer(), factoriesListener, idProvider);
+
+        NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
+                factoriesListener, commitNot, idProvider);
+        NetconfServerDispatcher dispatch = new NetconfServerDispatcher(Optional.<SSLContext> absent(),
+                serverNegotiatorFactory, listenerFactory);
+
+        InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 8333);
+        ChannelFuture s = dispatch.createServer(addr);
+
+        commitNot.close();
+        dispatch.close();
+    }
+}
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked1.txt b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked1.txt
new file mode 100644 (file)
index 0000000..aad7239
--- /dev/null
@@ -0,0 +1,35 @@
+
+#24
+<rpc message-id="101" xm
+#28
+lns="urn:ietf:params:xml:ns:
+#2
+ne
+#33
+tconf:base:1.0">
+  <my-own-method
+#3
+ xm
+#13
+lns="http://e
+#34
+xample.net/me/my-own/1.0">
+    <my
+#8
+-first-p
+#21
+arameter>14</my-first
+#26
+-parameter>
+    <another-p
+#23
+arameter>fred</another-
+#31
+parameter>
+ </my-own-method>
+ <
+#2
+/r
+#3
+pc>
+##
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked2.txt b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked2.txt
new file mode 100644 (file)
index 0000000..a36a85e
--- /dev/null
@@ -0,0 +1,48 @@
+
+#22
+<rpc message-id="101" 
+#24
+xmlns="urn:ietf:params:x
+#15
+ml:ns:netconf:b
+#54
+ase:1.0">
+  <get-config>
+      <source>
+            <r
+#2
+un
+#9
+ning/>
+  
+#18
+  </source>    <fi
+#33
+lter type="subtree">
+      <top x
+#4
+mlns
+#31
+="http://example.com/schema/1.2
+#15
+/config">
+     
+#19
+   <users>
+        
+#8
+     <us
+#3
+er/
+#5
+>
+   
+#77
+     </users>
+           </top>
+               </filter>
+                 </g
+#17
+et-config>
+</rpc>
+##
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked3.txt b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked3.txt
new file mode 100644 (file)
index 0000000..d9dc43d
--- /dev/null
@@ -0,0 +1,43 @@
+
+#43
+<rpc message-id="101" xmlns="urn:ietf:param
+#14
+s:xml:ns:netco
+#14
+nf:base:1.0">
+
+#26
+<get-config>
+    <source>
+
+#35
+     <running/>
+         </source>
+
+#39
+    <filter type="subtree">
+          <
+#40
+top xmlns="http://example.com/schema/1.2
+#26
+/config">
+        <users>
+
+#36
+     <user>
+                 <name>f
+#56
+red</name>
+          </user>
+                  </users>
+
+#28
+      </top>
+          </fil
+#1
+t
+#28
+er>
+  </get-config>
+  </rpc>
+##
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked4.txt b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked4.txt
new file mode 100644 (file)
index 0000000..0b8a102
--- /dev/null
@@ -0,0 +1,40 @@
+
+#17
+<rpc message-id="
+#25
+101" xmlns="urn:ietf:para
+#19
+ms:xml:ns:netconf:b
+#3
+ase
+#19
+:1.0">
+  <get-confi
+#61
+g>
+    <source>
+          <running/>
+              </source>
+
+#43
+  <filter type="subtree">
+        <!-- requ
+#13
+est a text ve
+#22
+rsion of the configura
+#9
+tion -->
+
+#16
+   <config-text 
+#45
+xmlns="http://example.com/text/1.2/config"/>
+
+#22
+    </filter>
+      </
+#18
+get-config>
+</rpc>
+##
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked5.txt b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked5.txt
new file mode 100644 (file)
index 0000000..f8f3c4d
--- /dev/null
@@ -0,0 +1,42 @@
+
+#43
+<rpc message-id="101" xmlns="urn:ietf:param
+#41
+s:xml:ns:netconf:base:1.0">
+  <edit-confi
+#29
+g>
+    <target>
+          <ru
+#13
+nning/>
+    <
+#4
+/tar
+#18
+get>
+    <config>
+
+#41
+  <top xmlns="http://example.com/schema/1
+#32
+.2/config">
+        <interface>
+
+#29
+        <name>Ethernet0/0</na
+#30
+me>
+          <mtu>1500</mtu>
+
+#61
+</interface>
+      </top>
+          </config>
+            </e
+#9
+dit-confi
+#9
+g>
+</rpc>
+##
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/listener/databaseinteractions/jolokia_config_bean_response.txt b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/listener/databaseinteractions/jolokia_config_bean_response.txt
new file mode 100644 (file)
index 0000000..2ae32ef
--- /dev/null
@@ -0,0 +1,18 @@
+curl http://localhost:17777/jolokia/read/org.opendaylight.controller:instanceName=fixed1,type=ConfigBean,interfaceName=testing-threadpool | jsonpp
+{
+    "request": {
+        "mbean": "org.opendaylight.controller:instanceName=fixed1,interfaceName=testing-threadpool,type=ConfigBean",
+        "type": "read"
+    },
+    "status": 200,
+    "timestamp": 1362416252,
+    "value": {
+        "ExportedInterfaces": [
+            "testing-threadpool",
+            "modifiable-threadpool"
+        ],
+        "ImplementationName": "fixed",
+        "ThreadCount": 10,
+        "TriggerNewInstanceCreation": false
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/listener/databaseinteractions/jolokia_lookupConfigBeans.txt b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/listener/databaseinteractions/jolokia_lookupConfigBeans.txt
new file mode 100644 (file)
index 0000000..2ae705a
--- /dev/null
@@ -0,0 +1,18 @@
+$ curl 'http://localhost:17777/jolokia/exec/org.opendaylight.controller:type=ConfigRegistry/lookupConfigBeans()' | jsonpp
+{
+    "request": {
+        "mbean": "org.opendaylight.controller:type=ConfigRegistry",
+        "operation": "lookupConfigBeans()",
+        "type": "exec"
+    },
+    "status": 200,
+    "timestamp": 1362417043,
+    "value": [
+        {
+            "objectName": "org.opendaylight.controller:instanceName=fixed1,interfaceName=modifiable-threadpool,type=ConfigBean"
+        },
+        {
+            "objectName": "org.opendaylight.controller:instanceName=fixed1,interfaceName=testing-threadpool,type=ConfigBean"
+        }
+    ]
+}
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_commit.xml b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_commit.xml
new file mode 100644 (file)
index 0000000..6eca609
--- /dev/null
@@ -0,0 +1,4 @@
+<rpc message-id="104"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <commit/>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_lock_candidate.xml b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_lock_candidate.xml
new file mode 100644 (file)
index 0000000..6a9ed63
--- /dev/null
@@ -0,0 +1,8 @@
+<rpc message-id="102"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <lock>
+        <target>
+            <candidate/>
+        </target>
+    </lock>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_lock_running.xml b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_lock_running.xml
new file mode 100644 (file)
index 0000000..2d66c45
--- /dev/null
@@ -0,0 +1,8 @@
+<rpc message-id="101"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <lock>
+        <target>
+            <running/>
+        </target>
+    </lock>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_modify_candidate.xml b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_modify_candidate.xml
new file mode 100644 (file)
index 0000000..ce67845
--- /dev/null
@@ -0,0 +1,20 @@
+<rpc message-id="103"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <default-operation>none</default-operation>
+        <test-option>test-then-set</test-option>
+        <error-option>stop-on-error</error-option>
+        <nc:config
+                xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+                xmlns="uri-for-my-data-model-namespace">
+            <some-existing-node>
+                <my-new-node nc:operation="create">
+                    <my-new-leaf>7</my-new-leaf>
+                </my-new-node>
+            </some-existing-node>
+        </nc:config>
+    </edit-config>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_unlock_candidate.xml b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_unlock_candidate.xml
new file mode 100644 (file)
index 0000000..dd6fe1b
--- /dev/null
@@ -0,0 +1,8 @@
+<rpc message-id="105"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <unlock>
+        <target>
+            <candidate/>
+        </target>
+    </unlock>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_unlock_running.xml b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_unlock_running.xml
new file mode 100644 (file)
index 0000000..f94af46
--- /dev/null
@@ -0,0 +1,8 @@
+<rpc message-id="106"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <unlock>
+        <target>
+            <running/>
+        </target>
+    </unlock>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/server_error_missing_attribute.xml b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/server_error_missing_attribute.xml
new file mode 100644 (file)
index 0000000..c70184e
--- /dev/null
@@ -0,0 +1,11 @@
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <rpc-error>
+        <error-type>rpc</error-type>
+        <error-tag>missing-attribute</error-tag>
+        <error-severity>error</error-severity>
+        <error-info>
+            <bad-attribute>message-id</bad-attribute>
+            <bad-element>rpc</bad-element>
+        </error-info>
+    </rpc-error>
+</rpc-reply>
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/input.json b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/input.json
new file mode 100644 (file)
index 0000000..5e03681
--- /dev/null
@@ -0,0 +1,10 @@
+{\r
+   "value":null,\r
+   "status":200,\r
+   "request": {\r
+                "type":"exec",\r
+                "mbean":"java.util.logging:type=Logging",\r
+                "operation":"setLoggerLevel",\r
+                "arguments":["global","INFO"]\r
+              }\r
+}
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/input.xml b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/input.xml
new file mode 100644 (file)
index 0000000..b04ace3
--- /dev/null
@@ -0,0 +1,7 @@
+<jmxbean>\r
+    <mBean>org.opendaylight.controller:type=AppDeployer</mBean>\r
+    <type>EXEC</type>\r
+    <operation>lookupConfigBeans</operation>\r
+    <arguments>abc,bcd.aas</arguments>\r
+    <arguments>64</arguments>\r
+</jmxbean>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputList.xml b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputList.xml
new file mode 100644 (file)
index 0000000..5fc9353
--- /dev/null
@@ -0,0 +1,9 @@
+<jmxbean>\r
+    <mBean>org.opendaylight.controller:type=AppDeployer</mBean>\r
+    <type>EXEC</type>\r
+    <operation>lookupConfigBeans</operation>\r
+    <arguments>\r
+        <elements>22</elements>\r
+        <elements>69</elements>\r
+    </arguments>\r
+</jmxbean>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputMap.xml b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputMap.xml
new file mode 100644 (file)
index 0000000..9bf5b58
--- /dev/null
@@ -0,0 +1,14 @@
+<jmxbean>\r
+    <mBean>org.opendaylight.controller:type=AppDeployer</mBean>\r
+    <type>EXEC</type>\r
+    <operation>lookupConfigBeans</operation>\r
+    <arguments>\r
+        <map>\r
+            <topology-registry>single</topology-registry>\r
+            <bgp-update>mock</bgp-update>\r
+        </map>\r
+        <array>2</array>\r
+        <array>22</array>\r
+        <anotherArg>arg</anotherArg>\r
+    </arguments>\r
+</jmxbean>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputMultiple.xml b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputMultiple.xml
new file mode 100644 (file)
index 0000000..23a3489
--- /dev/null
@@ -0,0 +1,29 @@
+<config>\r
+    <jmxbean>\r
+        <mBean>org.opendaylight.controller:type=AppDeployer</mBean>\r
+        <type>EXEC</type>\r
+        <operation>lookupConfigBeans</operation>\r
+        <attribute>abc,bcd.aas</attribute>\r
+        <attribute>22</attribute>\r
+    </jmxbean>\r
+    <jmxbean>\r
+        <mBean>org.opendaylight.controller:type=AppDeployer</mBean>\r
+        <type>WRITE</type>\r
+        <attribute>attribute</attribute>\r
+        <value>22</value>\r
+    </jmxbean>\r
+    <jmxbean>\r
+        <mBean>org.opendaylight.controller:type=AppDeployer</mBean>\r
+        <type>EXEC</type>\r
+        <operation>lookupConfigBeans</operation>\r
+        <arguments>\r
+            <map>\r
+                <topology-registry>single</topology-registry>\r
+                <bgp-update>mock</bgp-update>\r
+            </map>\r
+            <array>2</array>\r
+            <array>22</array>\r
+            <anotherArg>arg</anotherArg>\r
+        </arguments>\r
+    </jmxbean>\r
+</config>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputSetter.xml b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputSetter.xml
new file mode 100644 (file)
index 0000000..abad54d
--- /dev/null
@@ -0,0 +1,6 @@
+<jmxbean>\r
+    <mBean>org.opendaylight.controller:type=AppDeployer</mBean>\r
+    <type>WRITE</type>\r
+    <attribute>attribute</attribute>\r
+    <value>22</value>\r
+</jmxbean>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputSetterList.xml b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputSetterList.xml
new file mode 100644 (file)
index 0000000..7162b8f
--- /dev/null
@@ -0,0 +1,8 @@
+<jmxbean>\r
+    <mBean>org.opendaylight.controller:type=AppDeployer</mBean>\r
+    <type>WRITE</type>\r
+    <attribute>attribute</attribute>\r
+    <value>22</value>\r
+    <value>222</value>\r
+    <value>223</value>\r
+</jmxbean>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputSetterMap.xml b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputSetterMap.xml
new file mode 100644 (file)
index 0000000..00a0536
--- /dev/null
@@ -0,0 +1,9 @@
+<jmxbean>\r
+    <mBean>org.opendaylight.controller:type=AppDeployer</mBean>\r
+    <type>WRITE</type>\r
+    <attribute>setAtr</attribute>\r
+    <value>\r
+        <abc>1</abc>\r
+        <bcd>2</bcd>\r
+    </value>\r
+</jmxbean>\r
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/map.json b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/map.json
new file mode 100644 (file)
index 0000000..60fabb6
--- /dev/null
@@ -0,0 +1,134 @@
+{\r
+   "timestamp":1362488209,\r
+   "status":200,\r
+   "request":{\r
+      "mbean":"org.opendaylight.controller:type=ConfigRegistry",\r
+      "attribute":"AvailableInterfacesAndImplementations",\r
+      "type":"read"\r
+   },\r
+   "value":{\r
+      "topology-registry":[\r
+         "single"\r
+      ],\r
+      "bgp-update":[\r
+         "mock",\r
+         "bgp-impl"\r
+      ],\r
+      "positioning-service":[\r
+         "combine",\r
+         "onehop",\r
+         "ondemand",\r
+         "pxe",\r
+         "precompute"\r
+      ],\r
+      "serializer":[\r
+         "serializer-impl"\r
+      ],\r
+      "network-topology-factory":[\r
+         "mock-xml",\r
+         "bgp-network-topology-factory",\r
+         "transient"\r
+      ],\r
+      "dwe-topology":[\r
+         "ebgp",\r
+         "defaultmetric",\r
+         "igp",\r
+         "network"\r
+      ],\r
+      "thread-factory":[\r
+         "naming-thread-factory"\r
+      ],\r
+      "bgp-parser":[\r
+         "parser"\r
+      ],\r
+      "pcep-dispatcher":[\r
+         "dispatcher"\r
+      ],\r
+      "threadpool":[\r
+         "flexible",\r
+         "fixed",\r
+         "scheduled"\r
+      ],\r
+      "scheduled-threadpool":[\r
+         "scheduled"\r
+      ],\r
+      "positioning-onehop":[\r
+         "onehop"\r
+      ],\r
+      "bgp-dispatcher":[\r
+         "bgp-dispatcher-impl"\r
+      ],\r
+      "cost-combiner":[\r
+         "pxe"\r
+      ],\r
+      "apsp-provider":[\r
+         "jgrapht",\r
+         "parallel",\r
+         "single-threaded"\r
+      ],\r
+      "topology":[\r
+         "ebgp",\r
+         "defaultmetric",\r
+         "igp",\r
+         "network"\r
+      ],\r
+      "soap-resource":[\r
+         "positioning-adaptor-pxe"\r
+      ],\r
+      "database-provider-factory":[\r
+         "transient"\r
+      ],\r
+      "bgp-proposal-checker":[\r
+         "bgp-proposal-checker-impl"\r
+      ],\r
+      "bgp-proposal":[\r
+         "bgp-proposal-impl"\r
+      ],\r
+      "listenable-network-topology-factory":[\r
+         "transient"\r
+      ],\r
+      "event-bus":[\r
+         "sync",\r
+         "async"\r
+      ],\r
+      "topology-registry-provider":[\r
+         "single"\r
+      ],\r
+      "topology-provider-factory":[\r
+         "transient"\r
+      ],\r
+      "rest-resource":[\r
+         "topology-resource-holder",\r
+         "alto-resource-holder",\r
+         "topology-visual-holder",\r
+         "network-resource-holder",\r
+         "path-resource-holder"\r
+      ],\r
+      "listenable-database-provider-factory":[\r
+         "transient"\r
+      ],\r
+      "topology-validator":[\r
+         "accept-all",\r
+         "threshold"\r
+      ],\r
+      "replicator":[\r
+         "replicator-impl"\r
+      ],\r
+      "server":[\r
+         "soap",\r
+         "rest"\r
+      ],\r
+      "combiner-pxe":[\r
+         "pxe"\r
+      ],\r
+      "rest":[\r
+         "rest"\r
+      ],\r
+      "soap":[\r
+         "soap"\r
+      ],\r
+      "path-service":[\r
+         "cariden"\r
+      ]\r
+   }\r
+}
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-it/pom.xml b/opendaylight/netconf/netconf-it/pom.xml
new file mode 100644 (file)
index 0000000..a3377d7
--- /dev/null
@@ -0,0 +1,165 @@
+<?xml version="1.0"?>
+<project
+        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+        xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>netconf-subsystem</artifactId>
+        <groupId>org.opendaylight.controller</groupId>
+        <version>0.2.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>netconf-it</artifactId>
+    <name>${project.artifactId}</name>
+
+    <dependencies>
+        <!-- compile dependencies -->
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-api</artifactId>
+            <version>${config.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-util</artifactId>
+            <version>${config.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>yang-store-api</artifactId>
+            <version>${config.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-api</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>util</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-client</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-netconf-connector</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>yang-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-manager</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-persister-impl</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-manager</artifactId>
+            <version>${config.version}</version>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-impl</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-mapping-api</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-util</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>yang-store-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>yang-store-impl</artifactId>
+            <version>${config.version}</version>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>logback-config</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>mockito-configuration</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <forkCount>1</forkCount>
+                    <reuseForks>false</reuseForks>
+                    <perCoreThreadCount>false</perCoreThreadCount>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>default-test</id>
+                        <configuration>
+                            <skip>true</skip>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>integration-tests</id>
+                        <phase>integration-test</phase>
+                        <goals>
+                            <goal>test</goal>
+                        </goals>
+                        <configuration>
+                            <includes>
+                                <include>**/org/opendaylight/controller/netconf/it/*.java</include>
+                            </includes>
+                            <skip>false</skip>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java
new file mode 100644 (file)
index 0000000..8a51b7c
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * 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.it;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import io.netty.channel.ChannelFuture;
+import io.netty.util.HashedWheelTimer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.manager.impl.jmx.BaseJMXRegistrator;
+import org.opendaylight.controller.config.manager.impl.jmx.RootRuntimeBeanRegistratorImpl;
+import org.opendaylight.controller.netconf.persist.impl.ConfigPersisterNotificationHandler;
+import org.opendaylight.controller.config.persist.api.Persister;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.opendaylight.controller.config.yang.store.api.YangStoreException;
+import org.opendaylight.controller.config.yang.store.impl.HardcodedYangStoreService;
+import org.opendaylight.controller.config.yang.test.impl.*;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.client.NetconfClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl;
+import org.opendaylight.controller.netconf.impl.*;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
+import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.protocol.util.SSLUtil;
+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 javax.management.ObjectName;
+import javax.net.ssl.SSLContext;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.internal.util.Checks.checkNotNull;
+
+public class NetconfITTest extends AbstractConfigTest {
+
+    // 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 tlsAddress = new InetSocketAddress("127.0.0.1", 12024);
+
+    private NetconfMessage getConfig, getConfigCandidate, editConfig, closeSession;
+    private DefaultCommitNotificationProducer commitNot;
+    private NetconfServerDispatcher dispatch, dispatchS;
+
+    @Before
+    public void setUp() throws Exception {
+        super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(getModuleFactories().toArray(
+                new ModuleFactory[0])));
+
+        loadMessages();
+
+        NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
+        factoriesListener.onAddNetconfOperationServiceFactory(new NetconfOperationServiceFactoryImpl(getYangStore()));
+
+        commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer());
+
+        dispatch = createDispatcher(Optional.<SSLContext> absent(), factoriesListener);
+        ChannelFuture s = dispatch.createServer(tcpAddress);
+        s.await();
+
+        dispatchS = createDispatcher(Optional.of(getSslContext()), factoriesListener);
+        s = dispatchS.createServer(tlsAddress);
+        s.await();
+    }
+
+    private NetconfServerDispatcher createDispatcher(Optional<SSLContext> sslC,
+            NetconfOperationServiceFactoryListenerImpl factoriesListener) {
+        SessionIdProvider idProvider = new SessionIdProvider();
+        NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
+                new HashedWheelTimer(5000, TimeUnit.MILLISECONDS), factoriesListener, idProvider);
+
+        NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
+                factoriesListener, commitNot, idProvider);
+
+        return new NetconfServerDispatcher(sslC, serverNegotiatorFactory, listenerFactory);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        commitNot.close();
+        dispatch.close();
+        dispatchS.close();
+    }
+
+    private SSLContext getSslContext() throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
+            IOException, UnrecoverableKeyException, KeyManagementException {
+        final InputStream keyStore = getClass().getResourceAsStream("/keystore.jks");
+        final InputStream trustStore = getClass().getResourceAsStream("/keystore.jks");
+        SSLContext sslContext = SSLUtil.initializeSecureContext("password", keyStore, trustStore, "SunX509");
+        keyStore.close();
+        trustStore.close();
+        return sslContext;
+    }
+
+    private void loadMessages() throws IOException, SAXException, ParserConfigurationException {
+        this.editConfig = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/edit_config.xml");
+        this.getConfig = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/getConfig.xml");
+        this.getConfigCandidate = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/getConfig_candidate.xml");
+        this.closeSession = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/closeSession.xml");
+    }
+
+    private HardcodedYangStoreService getYangStore() throws YangStoreException, IOException {
+        final Collection<InputStream> yangDependencies = getBasicYangs();
+        return new HardcodedYangStoreService(yangDependencies);
+    }
+
+    private Collection<InputStream> getBasicYangs() throws IOException {
+        List<String> paths = Arrays.asList("/META-INF/yang/config.yang", "/META-INF/yang/rpc-context.yang",
+                "/META-INF/yang/config-test.yang", "/META-INF/yang/config-test-impl.yang",
+                "/META-INF/yang/ietf-inet-types.yang");
+        final Collection<InputStream> yangDependencies = new ArrayList<>();
+        for (String path : paths) {
+            final InputStream is = checkNotNull(getClass().getResourceAsStream(path), path + " not found");
+            yangDependencies.add(is);
+        }
+        return yangDependencies;
+    }
+
+    protected List<ModuleFactory> getModuleFactories() {
+        return getModuleFactoriesS();
+    }
+
+    static List<ModuleFactory> getModuleFactoriesS() {
+        return Lists.newArrayList(new TestImplModuleFactory(), new DepTestImplModuleFactory(),
+                new NetconfTestImplModuleFactory());
+    }
+
+    @Test
+    public void testTwoSessions() throws Exception {
+        try (NetconfClient netconfClient = new NetconfClient("1", tcpAddress, 4000)) {
+            try (NetconfClient netconfClient2 = new NetconfClient("2", tcpAddress, 4000)) {
+            }
+        }
+    }
+
+    @Test(timeout = 10000)
+    public void testPersister() throws Exception {
+        Persister persister = mock(Persister.class);
+        doReturn("mockPersister").when(persister).toString();
+        doReturn(Optional.absent()).when(persister).loadLastConfig();
+        ConfigPersisterNotificationHandler h = new ConfigPersisterNotificationHandler(persister, tcpAddress, ManagementFactory.getPlatformMBeanServer());
+        h.init();
+    }
+
+    @Test
+    public void testSecure() throws Exception {
+        try (NetconfClient netconfClient = new NetconfClient("1", tlsAddress, 4000, Optional.of(getSslContext()))) {
+
+        }
+    }
+
+    @Ignore
+    @Test
+    public void waitingTest() throws Exception {
+        final ConfigTransactionJMXClient transaction = this.configRegistryClient.createTransaction();
+        transaction.createModule(DepTestImplModuleFactory.NAME, "eb");
+        transaction.commit();
+        Thread.currentThread().suspend();
+    }
+
+    @Test
+    public void rpcReplyContainsAllAttributesTest() throws Exception {
+        try (NetconfClient netconfClient = createSession(tcpAddress, "1")) {
+            final String rpc = "<rpc message-id=\"5\" a=\"a\" b=\"44\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+                    + "<get/>" + "</rpc>";
+            final Document doc = XmlUtil.readXmlToDocument(rpc);
+            final NetconfMessage message = netconfClient.sendMessage(new NetconfMessage(doc));
+            assertNotNull(message);
+            final NamedNodeMap expectedAttributes = doc.getDocumentElement().getAttributes();
+            final NamedNodeMap returnedAttributes = message.getDocument().getDocumentElement().getAttributes();
+
+            assertSameAttributes(expectedAttributes, returnedAttributes);
+        }
+    }
+
+    private void assertSameAttributes(final NamedNodeMap expectedAttributes, final NamedNodeMap returnedAttributes) {
+        assertNotNull("Expecting 4 attributes", returnedAttributes);
+        assertEquals(expectedAttributes.getLength(), returnedAttributes.getLength());
+
+        for (int i = 0; i < expectedAttributes.getLength(); i++) {
+            final Node expAttr = expectedAttributes.item(i);
+            final Node attr = returnedAttributes.item(i);
+            assertEquals(expAttr.getNodeName(), attr.getNodeName());
+            assertEquals(expAttr.getNamespaceURI(), attr.getNamespaceURI());
+            assertEquals(expAttr.getTextContent(), attr.getTextContent());
+        }
+    }
+
+    @Test
+    public void rpcReplyErrorContainsAllAttributesTest() throws Exception {
+        try (NetconfClient netconfClient = createSession(tcpAddress, "1")) {
+            final String rpc = "<rpc message-id=\"1\" a=\"adada\" b=\"4\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+                    + "<commit/>" + "</rpc>";
+            final Document doc = XmlUtil.readXmlToDocument(rpc);
+            final NetconfMessage message = netconfClient.sendMessage(new NetconfMessage(doc));
+            final NamedNodeMap expectedAttributes = doc.getDocumentElement().getAttributes();
+            final NamedNodeMap returnedAttributes = message.getDocument().getDocumentElement().getAttributes();
+
+            assertSameAttributes(expectedAttributes, returnedAttributes);
+        }
+    }
+
+    @Test
+    public void rpcOutputContainsCorrectNamespace() throws Exception {
+        final ConfigTransactionJMXClient transaction = this.configRegistryClient.createTransaction();
+        ObjectName dep = transaction.createModule(DepTestImplModuleFactory.NAME, "instanceD");
+        ObjectName impl = transaction.createModule(NetconfTestImplModuleFactory.NAME, "instance");
+        NetconfTestImplModuleMXBean proxy = configRegistryClient
+                .newMXBeanProxy(impl, NetconfTestImplModuleMXBean.class);
+        proxy.setTestingDep(dep);
+        registerRuntimeBean();
+
+        transaction.commit();
+
+        try (NetconfClient netconfClient = createSession(tcpAddress, "1")) {
+            final String expectedNamespace = "urn:opendaylight:params:xml:ns:yang:controller:test:impl";
+
+            final String rpc = "<rpc message-id=\"5\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+                    + "<no-arg xmlns=\""
+                    + expectedNamespace
+                    + "\">    "
+                    + "<context-instance>/data/modules/module[name='impl-netconf']/instance[name='instance']</context-instance>"
+                    + "<arg1>argument1</arg1>" + "</no-arg>" + "</rpc>";
+            final Document doc = XmlUtil.readXmlToDocument(rpc);
+            final NetconfMessage message = netconfClient.sendMessage(new NetconfMessage(doc));
+
+            final Element rpcReply = message.getDocument().getDocumentElement();
+            final XmlElement resultElement = XmlElement.fromDomElement(rpcReply).getOnlyChildElement();
+            assertEquals("result", resultElement.getName());
+            final String namespace = resultElement.getNamespaceAttribute();
+            assertEquals(expectedNamespace, namespace);
+        }
+    }
+
+    private void registerRuntimeBean() {
+        BaseJMXRegistrator baseJMXRegistrator = new BaseJMXRegistrator(ManagementFactory.getPlatformMBeanServer());
+        RootRuntimeBeanRegistratorImpl runtimeBeanRegistrator = baseJMXRegistrator
+                .createRuntimeBeanRegistrator(new ModuleIdentifier(NetconfTestImplModuleFactory.NAME, "instance"));
+        NetconfTestImplRuntimeRegistrator reg = new NetconfTestImplRuntimeRegistrator(runtimeBeanRegistrator);
+        reg.register(new NetconfTestImplRuntimeMXBean() {
+            @Override
+            public Asdf getAsdf() {
+                return null;
+            }
+
+            @Override
+            public Long getCreatedSessions() {
+                return null;
+            }
+
+            @Override
+            public String noArg(String arg1) {
+                return "from no arg";
+            }
+        });
+    }
+
+    @Test
+    public void testCloseSession() throws Exception {
+        try (NetconfClient netconfClient = createSession(tcpAddress, "1")) {
+
+            // edit config
+            Document rpcReply = netconfClient.sendMessage(this.editConfig).getDocument();
+            assertIsOK(rpcReply);
+
+            rpcReply = netconfClient.sendMessage(this.closeSession).getDocument();
+
+            assertIsOK(rpcReply);
+        }
+    }
+
+    @Test
+    public void testEditConfig() throws Exception {
+        try (NetconfClient netconfClient = createSession(tcpAddress, "1")) {
+            // send edit_config.xml
+            final Document rpcReply = netconfClient.sendMessage(this.editConfig).getDocument();
+            assertIsOK(rpcReply);
+        }
+    }
+
+    @Test
+    public void testValidate() throws Exception {
+        try (NetconfClient netconfClient = createSession(tcpAddress, "1")) {
+            // begin transaction
+            Document rpcReply = netconfClient.sendMessage(getConfigCandidate).getDocument();
+            assertEquals("data", XmlElement.fromDomDocument(rpcReply).getOnlyChildElement().getName());
+
+            // operations empty
+            rpcReply = netconfClient.sendMessage(XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/validate.xml"))
+                    .getDocument();
+            assertIsOK(rpcReply);
+        }
+    }
+
+    private void assertIsOK(final Document rpcReply) {
+        assertEquals("rpc-reply", rpcReply.getDocumentElement().getTagName());
+        assertEquals("ok", XmlElement.fromDomDocument(rpcReply).getOnlyChildElement().getName());
+    }
+
+    @Ignore
+    @Test
+    // TODO can only send NetconfMessage - it must be valid xml
+    public void testClientHelloWithAuth() throws Exception {
+        final String fileName = "netconfMessages/client_hello_with_auth.xml";
+        // final InputStream resourceAsStream =
+        // AbstractListenerTest.class.getResourceAsStream(fileName);
+        // assertNotNull(resourceAsStream);
+        try (NetconfClient netconfClient = new NetconfClient("test", tcpAddress, 5000)) {
+            // IOUtils.copy(resourceAsStream, netconfClient.getStream());
+            // netconfClient.getOutputStream().write(NetconfMessageFactory.endOfMessage);
+            // server should not write anything back
+            // assertEquals(null, netconfClient.readMessage());
+            assertGetConfigWorks(netconfClient);
+        }
+    }
+
+    private Document assertGetConfigWorks(final NetconfClient netconfClient) throws InterruptedException {
+        return assertGetConfigWorks(netconfClient, this.getConfig);
+    }
+
+    private Document assertGetConfigWorks(final NetconfClient netconfClient, final NetconfMessage getConfigMessage)
+            throws InterruptedException {
+        final NetconfMessage rpcReply = netconfClient.sendMessage(getConfigMessage);
+        assertNotNull(rpcReply);
+        assertEquals("data", XmlElement.fromDomDocument(rpcReply.getDocument()).getOnlyChildElement().getName());
+        return rpcReply.getDocument();
+    }
+
+    @Test
+    public void testGetConfig() throws Exception {
+        try (NetconfClient netconfClient = createSession(tcpAddress, "1")) {
+            assertGetConfigWorks(netconfClient);
+        }
+    }
+
+    @Test
+    public void createYangTestBasedOnYuma() throws Exception {
+        try (NetconfClient netconfClient = createSession(tcpAddress, "1")) {
+            Document rpcReply = netconfClient.sendMessage(
+                    XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/editConfig_merge_yang-test.xml"))
+                    .getDocument();
+            assertEquals("rpc-reply", rpcReply.getDocumentElement().getTagName());
+            assertIsOK(rpcReply);
+            assertGetConfigWorks(netconfClient, this.getConfigCandidate);
+            rpcReply = netconfClient.sendMessage(XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/commit.xml"))
+                    .getDocument();
+            assertIsOK(rpcReply);
+
+            final ObjectName on = new ObjectName(
+                    "org.opendaylight.controller:instanceName=impl-dep-instance,type=Module,moduleFactoryName=impl-dep");
+            Set<ObjectName> cfgBeans = configRegistryClient.lookupConfigBeans();
+            assertEquals(cfgBeans, Sets.newHashSet(on));
+        }
+    }
+
+    private NetconfClient createSession(final InetSocketAddress address, final String expected) throws Exception {
+        final NetconfClient netconfClient = new NetconfClient("test " + address.toString(), address, 5000);
+
+        assertEquals(expected, Long.toString(netconfClient.getSessionId()));
+
+        return netconfClient;
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-it/src/test/resources/keystore.jks b/opendaylight/netconf/netconf-it/src/test/resources/keystore.jks
new file mode 100644 (file)
index 0000000..201d375
Binary files /dev/null and b/opendaylight/netconf/netconf-it/src/test/resources/keystore.jks differ
diff --git a/opendaylight/netconf/netconf-mapping-api/pom.xml b/opendaylight/netconf/netconf-mapping-api/pom.xml
new file mode 100644 (file)
index 0000000..5b6003f
--- /dev/null
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <parent>
+        <artifactId>netconf-subsystem</artifactId>
+        <groupId>org.opendaylight.controller</groupId>
+        <version>0.2.1-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>netconf-mapping-api</artifactId>
+    <name>${project.artifactId}</name>
+
+    <packaging>bundle</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+    </dependencies>
+
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Private-Package>
+                        </Private-Package>
+                        <Import-Package>
+                            com.google.common.base,
+                            org.opendaylight.controller.netconf.api,
+                            org.w3c.dom
+                        </Import-Package>
+                        <Export-Package>
+                            org.opendaylight.controller.netconf.mapping.api,
+                        </Export-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/Capability.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/Capability.java
new file mode 100644 (file)
index 0000000..6351c61
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.mapping.api;
+
+import com.google.common.base.Optional;
+
+/**
+ * Contains capability URI announced by server hello message and optionally its
+ * corresponding yang schema that can be retrieved by get-schema rpc.
+ */
+public interface Capability {
+
+    public String getCapabilityUri();
+
+    public Optional<String> getModuleName();
+
+    public Optional<String> getRevision();
+
+    public Optional<String> getCapabilitySchema();
+}
diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/HandlingPriority.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/HandlingPriority.java
new file mode 100644 (file)
index 0000000..5a0688c
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * 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.mapping.api;
+
+import com.google.common.base.Optional;
+
+public class HandlingPriority implements Comparable<HandlingPriority> {
+
+    public static final HandlingPriority CANNOT_HANDLE = new HandlingPriority();
+    public static final HandlingPriority HANDLE_WITH_DEFAULT_PRIORITY = new HandlingPriority(Integer.MIN_VALUE);
+    public static final HandlingPriority HANDLE_WITH_MAX_PRIORITY = new HandlingPriority(Integer.MAX_VALUE);
+
+    private Integer priority;
+
+    public static HandlingPriority getHandlingPriority(int priority) {
+        return new HandlingPriority(priority);
+    }
+
+    private HandlingPriority(int priority) {
+        this.priority = priority;
+    }
+
+    private HandlingPriority() {
+    }
+
+    /**
+     * @return priority number or Optional.absent otherwise
+     */
+    public Optional<Integer> getPriority() {
+        return Optional.of(priority).or(Optional.<Integer> absent());
+    }
+
+    // TODO test
+
+    @Override
+    public int compareTo(HandlingPriority o) {
+        if (this == o)
+            return 0;
+        if (this == CANNOT_HANDLE)
+            return -1;
+        if (o == CANNOT_HANDLE)
+            return 1;
+
+        if (priority > o.priority)
+            return 1;
+        if (priority == o.priority)
+            return 0;
+        if (priority < o.priority)
+            return -1;
+
+        throw new IllegalStateException("Unexpected state");
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (!(o instanceof HandlingPriority))
+            return false;
+
+        HandlingPriority that = (HandlingPriority) o;
+
+        if (priority != null ? !priority.equals(that.priority) : that.priority != null)
+            return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return priority != null ? priority.hashCode() : 0;
+    }
+}
diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperation.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperation.java
new file mode 100644 (file)
index 0000000..58857b4
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.mapping.api;
+
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.w3c.dom.Document;
+
+public interface NetconfOperation {
+
+    HandlingPriority canHandle(Document message);
+
+    Document handle(Document message, NetconfOperationRouter operationRouter) throws NetconfDocumentedException;
+}
diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationFilter.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationFilter.java
new file mode 100644 (file)
index 0000000..22a7307
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.mapping.api;
+
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.w3c.dom.Document;
+
+/**
+ * Filters wrap each netconf operation, if there is one found. Filters are
+ * sorted and applied from the greatest to smallest sorting order.
+ */
+public interface NetconfOperationFilter extends Comparable<NetconfOperationFilter> {
+
+    Document doFilter(Document message, NetconfOperationRouter operationRouter, NetconfOperationFilterChain filterChain)
+            throws NetconfDocumentedException;
+
+    int getSoringOrder();
+
+}
diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationFilterChain.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationFilterChain.java
new file mode 100644 (file)
index 0000000..7a0eb91
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.mapping.api;
+
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.w3c.dom.Document;
+
+public interface NetconfOperationFilterChain {
+
+    Document execute(Document message, NetconfOperationRouter operationRouter) throws NetconfDocumentedException;
+
+}
diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationService.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationService.java
new file mode 100644 (file)
index 0000000..858636a
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.mapping.api;
+
+import java.util.Set;
+
+/**
+ *
+ */
+public interface NetconfOperationService extends AutoCloseable {
+
+    /**
+     * Get capabilities announced by server hello message.
+     */
+    Set<Capability> getCapabilities();
+
+    /**
+     * Get set of netconf operations that are handled by this service.
+     */
+    Set<NetconfOperation> getNetconfOperations();
+
+    Set<NetconfOperationFilter> getFilters();
+
+    /**
+     * Called when netconf session is destroyed.
+     */
+    @Override
+    void close();
+
+}
diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceFactory.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceFactory.java
new file mode 100644 (file)
index 0000000..46b9cd2
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.mapping.api;
+
+/**
+ * Factory that must be registered in OSGi service registry in order to be used
+ * by netconf-impl. Responsible for creating per-session instances of
+ * {@link NetconfOperationService}.
+ */
+public interface NetconfOperationServiceFactory {
+
+    NetconfOperationService createService(long netconfSessionId, String netconfSessionIdForReporting);
+
+}
diff --git a/opendaylight/netconf/netconf-util/pom.xml b/opendaylight/netconf/netconf-util/pom.xml
new file mode 100644 (file)
index 0000000..cb198e7
--- /dev/null
@@ -0,0 +1,117 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>netconf-subsystem</artifactId>
+        <groupId>org.opendaylight.controller</groupId>
+        <version>0.2.1-SNAPSHOT</version>
+    </parent>
+    <artifactId>netconf-util</artifactId>
+    <name>${project.artifactId}</name>
+    <packaging>bundle</packaging>
+
+
+    <dependencies>
+        <!-- compile dependencies -->
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-mapping-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>config-api</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>framework</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>util</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Export-Package>
+                            org.opendaylight.controller.netconf.util,
+                            org.opendaylight.controller.netconf.util.xml,
+                            org.opendaylight.controller.netconf.util.osgi,
+                            org.opendaylight.controller.netconf.util.mapping,
+                            org.opendaylight.controller.netconf.util.messages,
+                        </Export-Package>
+                        <Import-Package>
+                            org.opendaylight.controller.config.stat,
+                            com.google.common.base,
+                            com.google.common.collect,
+                            io.netty.buffer,
+                            io.netty.channel,
+                            io.netty.channel.socket,
+                            io.netty.handler.codec,
+                            io.netty.handler.ssl,
+                            io.netty.util,
+                            io.netty.util.concurrent,
+                            javax.annotation,
+                            javax.net.ssl,
+                            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.opendaylight.protocol.framework,
+                            org.opendaylight.protocol.util,
+                            org.osgi.framework,
+                            org.slf4j,
+                            org.w3c.dom,
+                            org.xml.sax,
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>2.4</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractChannelInitializer.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractChannelInitializer.java
new file mode 100644 (file)
index 0000000..317a126
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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.util;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfSession;
+import org.opendaylight.controller.netconf.util.messages.NetconfMessageFactory;
+import org.opendaylight.protocol.framework.ProtocolHandlerFactory;
+import org.opendaylight.protocol.framework.ProtocolMessageDecoder;
+import org.opendaylight.protocol.framework.ProtocolMessageEncoder;
+
+import com.google.common.base.Optional;
+
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.handler.ssl.SslHandler;
+import io.netty.util.concurrent.Promise;
+
+public abstract class AbstractChannelInitializer {
+
+    private final Optional<SSLContext> maybeContext;
+    private final NetconfHandlerFactory handlerFactory;
+
+    public AbstractChannelInitializer(Optional<SSLContext> maybeContext) {
+        this.maybeContext = maybeContext;
+        this.handlerFactory = new NetconfHandlerFactory(new NetconfMessageFactory());
+    }
+
+    public void initialize(SocketChannel ch, Promise<? extends NetconfSession> promise) {
+        if (maybeContext.isPresent()) {
+            initSsl(ch);
+        }
+
+        ch.pipeline().addLast("frameDecoder", NetconfMessageFactory.getDelimiterFrameDecoder());
+        ch.pipeline().addLast(handlerFactory.getDecoders());
+        initializeAfterDecoder(ch, promise);
+        ch.pipeline().addLast(handlerFactory.getEncoders());
+    }
+
+    protected abstract void initializeAfterDecoder(SocketChannel ch, Promise<? extends NetconfSession> promise);
+
+    private void initSsl(SocketChannel ch) {
+        SSLEngine sslEngine = maybeContext.get().createSSLEngine();
+        initSslEngine(sslEngine);
+        final SslHandler handler = new SslHandler(sslEngine);
+        ch.pipeline().addLast("ssl", handler);
+    }
+
+    protected abstract void initSslEngine(SSLEngine sslEngine);
+
+    private static final class NetconfHandlerFactory extends ProtocolHandlerFactory<NetconfMessage> {
+
+        public NetconfHandlerFactory(final NetconfMessageFactory msgFactory) {
+            super(msgFactory);
+        }
+
+        @Override
+        public ChannelHandler[] getEncoders() {
+            return new ChannelHandler[] { new ProtocolMessageEncoder(this.msgFactory) };
+        }
+
+        @Override
+        public ChannelHandler[] getDecoders() {
+            return new ChannelHandler[] { new ProtocolMessageDecoder(this.msgFactory) };
+        }
+    }
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractNetconfSessionNegotiator.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractNetconfSessionNegotiator.java
new file mode 100644 (file)
index 0000000..9069d85
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * 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.util;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import io.netty.channel.Channel;
+import io.netty.handler.ssl.SslHandler;
+import io.netty.util.Timeout;
+import io.netty.util.Timer;
+import io.netty.util.TimerTask;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.GenericFutureListener;
+import io.netty.util.concurrent.Promise;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfSession;
+import org.opendaylight.controller.netconf.api.NetconfSessionPreferences;
+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.protocol.framework.AbstractSessionNegotiator;
+import org.opendaylight.protocol.framework.SessionListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+
+import java.util.concurrent.TimeUnit;
+
+public abstract class AbstractNetconfSessionNegotiator<P extends NetconfSessionPreferences, S extends NetconfSession>
+        extends AbstractSessionNegotiator<NetconfMessage, S> {
+
+    // TODO what time ?
+    private static final long INITIAL_HOLDTIMER = 1;
+    private static final Logger logger = LoggerFactory.getLogger(AbstractNetconfSessionNegotiator.class);
+
+    protected final P sessionPreferences;
+
+    private final SessionListener sessionListener;
+
+    /**
+     * Possible states for Finite State Machine
+     */
+    private enum State {
+        IDLE, OPEN_WAIT, FAILED, ESTABLISHED
+    }
+
+    private State state = State.IDLE;
+    private final Timer timer;
+
+    protected AbstractNetconfSessionNegotiator(P sessionPreferences, Promise<S> promise, Channel channel, Timer timer,
+            SessionListener sessionListener) {
+        super(promise, channel);
+        this.sessionPreferences = sessionPreferences;
+        this.timer = timer;
+        this.sessionListener = sessionListener;
+    }
+
+    @Override
+    protected void startNegotiation() throws Exception {
+        final Optional<SslHandler> sslHandler = getSslHandler(channel);
+        if (sslHandler.isPresent()) {
+            Future<Channel> future = sslHandler.get().handshakeFuture();
+            future.addListener(new GenericFutureListener<Future<? super Channel>>() {
+                @Override
+                public void operationComplete(Future<? super Channel> future) throws Exception {
+                    Preconditions.checkState(future.isSuccess(), "Ssl handshake was not successful");
+                    logger.debug("Ssl handshake complete");
+                    start();
+                }
+            });
+        } else
+            start();
+    }
+
+    private static Optional<SslHandler> getSslHandler(Channel channel) {
+        final SslHandler sslHandler = channel.pipeline().get(SslHandler.class);
+        return sslHandler == null ? Optional.<SslHandler> absent() : Optional.of(sslHandler);
+    }
+
+    private void start() {
+        final NetconfMessage helloMessage = this.sessionPreferences.getHelloMessage();
+        logger.debug("Session negotiation started with hello message {}", XmlUtil.toString(helloMessage.getDocument()));
+
+        sendMessage(helloMessage);
+        changeState(State.OPEN_WAIT);
+
+        this.timer.newTimeout(new TimerTask() {
+            @Override
+            public void run(final Timeout timeout) throws Exception {
+                synchronized (this) {
+                    if (state != State.ESTABLISHED) {
+                        final IllegalStateException cause = new IllegalStateException(
+                                "Session was not established after " + timeout);
+                        negotiationFailed(cause);
+                        changeState(State.FAILED);
+                    }
+                }
+            }
+        }, INITIAL_HOLDTIMER, TimeUnit.MINUTES);
+    }
+
+    private void sendMessage(NetconfMessage message) {
+        this.channel.writeAndFlush(message);
+    }
+
+    @Override
+    protected void handleMessage(NetconfMessage netconfMessage) {
+        final Document doc = netconfMessage.getDocument();
+
+        if (isHelloMessage(doc)) {
+            changeState(State.ESTABLISHED);
+            S session = getSession(sessionListener, channel, doc);
+            negotiationSuccessful(session);
+        } else {
+            final IllegalStateException cause = new IllegalStateException(
+                    "Received message was not hello as expected, but was " + XmlUtil.toString(doc));
+            negotiationFailed(cause);
+        }
+    }
+
+    protected abstract S getSession(SessionListener sessionListener, Channel channel, Document doc);
+
+    private boolean isHelloMessage(Document doc) {
+        try {
+            XmlElement.fromDomElementWithExpected(doc.getDocumentElement(), "hello",
+                    XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+
+        } catch (IllegalArgumentException | IllegalStateException e) {
+            return false;
+        }
+        return true;
+    }
+
+    private void changeState(final State newState) {
+        logger.debug("Changing state from : {} to : {}", state, newState);
+        Preconditions.checkState(isStateChangePermitted(state, newState), "Cannot change state from %s to %s", state,
+                newState);
+        this.state = newState;
+    }
+
+    private static boolean isStateChangePermitted(State state, State newState) {
+        if (state == State.IDLE && newState == State.OPEN_WAIT)
+            return true;
+        if (state == State.OPEN_WAIT && newState == State.ESTABLISHED)
+            return true;
+        if (state == State.OPEN_WAIT && newState == State.FAILED)
+            return true;
+
+        return false;
+    }
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/mapping/AbstractNetconfOperation.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/mapping/AbstractNetconfOperation.java
new file mode 100644 (file)
index 0000000..5850e64
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * 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.util.mapping;
+
+import java.util.Map;
+
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+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.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public abstract class AbstractNetconfOperation implements NetconfOperation {
+    private final String netconfSessionIdForReporting;
+
+    protected AbstractNetconfOperation(String netconfSessionIdForReporting) {
+        this.netconfSessionIdForReporting = netconfSessionIdForReporting;
+    }
+
+    public String getNetconfSessionIdForReporting() {
+        return netconfSessionIdForReporting;
+    }
+
+    @Override
+    public HandlingPriority canHandle(Document message) {
+        OperationNameAndNamespace operationNameAndNamespace = new OperationNameAndNamespace(message);
+        return canHandle(operationNameAndNamespace.getOperationName(), operationNameAndNamespace.getNamespace());
+    }
+
+    public static class OperationNameAndNamespace {
+        private final String operationName, namespace;
+
+        public OperationNameAndNamespace(Document message) {
+            XmlElement requestElement = getRequestElementWithCheck(message);
+
+            XmlElement operationElement = requestElement.getOnlyChildElement();
+            operationName = operationElement.getName();
+            namespace = operationElement.getNamespace();
+        }
+
+        public String getOperationName() {
+            return operationName;
+        }
+
+        public String getNamespace() {
+            return namespace;
+        }
+    }
+
+    protected static XmlElement getRequestElementWithCheck(Document message) {
+        return XmlElement.fromDomElementWithExpected(message.getDocumentElement(), XmlNetconfConstants.RPC_KEY,
+                XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+    }
+
+    protected abstract HandlingPriority canHandle(String operationName, String netconfOperationNamespace);
+
+    @Override
+    public Document handle(Document message, NetconfOperationRouter opRouter) throws NetconfDocumentedException {
+
+        XmlElement requestElement = getRequestElementWithCheck(message);
+
+        Document document = XmlUtil.newDocument();
+
+        XmlElement operationElement = requestElement.getOnlyChildElement();
+        Map<String, Attr> attributes = requestElement.getAttributes();
+
+        Element response = handle(document, operationElement, opRouter);
+
+        Element rpcReply = document.createElementNS(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0,
+                XmlNetconfConstants.RPC_REPLY_KEY);
+        rpcReply.appendChild(response);
+
+        for (String attrName : attributes.keySet()) {
+            rpcReply.setAttribute(attrName, attributes.get(attrName).getNodeValue());
+        }
+
+        document.appendChild(rpcReply);
+        return document;
+    }
+
+    protected abstract Element handle(Document document, XmlElement operationElement, NetconfOperationRouter opRouter)
+            throws NetconfDocumentedException;
+
+    @Override
+    public String toString() {
+        return getClass() + "{" + netconfSessionIdForReporting + '}';
+    }
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/FramingMechanism.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/FramingMechanism.java
new file mode 100644 (file)
index 0000000..c118ca8
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.util.messages;
+
+/**
+ * Known NETCONF framing mechanisms.
+ */
+public enum FramingMechanism {
+    /**
+     * @see <a href="http://tools.ietf.org/html/rfc6242#section-4.2">Chunked
+     *      framing mechanism</a>
+     */
+    CHUNK,
+    /**
+     * @see <a
+     *      href="http://tools.ietf.org/html/rfc6242#section-4.3">End-of-message
+     *      framing mechanism</a>
+     */
+    EOM
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageFactory.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageFactory.java
new file mode 100644 (file)
index 0000000..ca3079b
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * 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.util.messages;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandler;
+import io.netty.handler.codec.DelimiterBasedFrameDecoder;
+import org.opendaylight.controller.netconf.api.NetconfDeserializerException;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.protocol.framework.DeserializerException;
+import org.opendaylight.protocol.framework.DocumentedException;
+import org.opendaylight.protocol.framework.ProtocolMessageFactory;
+import org.opendaylight.protocol.util.ByteArray;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * NetconfMessageFactory for (de)serializing DOM documents.
+ */
+public final class NetconfMessageFactory implements ProtocolMessageFactory<NetconfMessage> {
+
+    private static final Logger logger = LoggerFactory.getLogger(NetconfMessageFactory.class);
+
+    public static final byte[] endOfMessage = "]]>]]>".getBytes(Charsets.UTF_8);
+
+    public static final byte[] endOfChunk = "\n##\n".getBytes(Charsets.UTF_8);
+
+    private static final int MAX_CHUNK_SIZE = 1024; // Bytes
+
+    private FramingMechanism framing = FramingMechanism.EOM;
+
+    private final Optional<String> clientId;
+
+    public NetconfMessageFactory() {
+        clientId = Optional.absent();
+    }
+
+    public NetconfMessageFactory(Optional<String> clientId) {
+        this.clientId = clientId;
+    }
+
+    public static ChannelHandler getDelimiterFrameDecoder() {
+        return new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, Unpooled.copiedBuffer(endOfMessage));
+    }
+
+    @Override
+    public List<NetconfMessage> parse(byte[] bytes) throws DeserializerException, DocumentedException {
+        String s = Charsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString();
+        logger.debug("Parsing message \n{}", s);
+        if (bytes[0] == '[') {
+            // yuma sends auth information in the first line. Ignore until ]\n
+            // is found.
+            int endOfAuthHeader = ByteArray.findByteSequence(bytes, new byte[] { ']', '\n' });
+            if (endOfAuthHeader > -1) {
+                bytes = Arrays.copyOfRange(bytes, endOfAuthHeader + 2, bytes.length);
+            }
+        }
+        List<NetconfMessage> messages = Lists.newArrayList();
+        try {
+            Document doc = XmlUtil.readXmlToDocument(new ByteArrayInputStream(bytes));
+            messages.add(new NetconfMessage(doc));
+        } catch (final SAXException | IOException | IllegalStateException e) {
+            throw new NetconfDeserializerException("Could not parse message from " + new String(bytes), e);
+        }
+        return messages;
+    }
+
+    @Override
+    public byte[] put(NetconfMessage netconfMessage) {
+        if (clientId.isPresent()) {
+            Comment comment = netconfMessage.getDocument().createComment("clientId:" + clientId.get());
+            netconfMessage.getDocument().appendChild(comment);
+        }
+        byte[] bytes = (this.framing == FramingMechanism.EOM) ? this.putEOM(netconfMessage) : this
+                .putChunked(netconfMessage);
+        String content = xmlToString(netconfMessage.getDocument());
+
+        logger.trace("Putting message \n{}", content);
+        return bytes;
+    }
+
+    private byte[] putEOM(NetconfMessage msg) {
+        // create byte buffer from the String XML
+        // all Netconf messages are encoded using UTF-8
+        final ByteBuffer msgBytes = Charsets.UTF_8.encode(xmlToString(msg.getDocument()));
+        final ByteBuffer result = ByteBuffer.allocate(msgBytes.limit() + endOfMessage.length);
+        result.put(msgBytes);
+        // put end of message
+        result.put(endOfMessage);
+        return result.array();
+    }
+
+    private byte[] putChunked(NetconfMessage msg) {
+        final ByteBuffer msgBytes = Charsets.UTF_8.encode(xmlToString(msg.getDocument()));
+        final NetconfMessageHeader h = new NetconfMessageHeader();
+        if (msgBytes.limit() > MAX_CHUNK_SIZE)
+            logger.warn("Netconf message too long, should be split.");
+        h.setLength(msgBytes.limit());
+        final byte[] headerBytes = h.toBytes();
+        final ByteBuffer result = ByteBuffer.allocate(headerBytes.length + msgBytes.limit() + endOfChunk.length);
+        result.put(headerBytes);
+        result.put(msgBytes);
+        result.put(endOfChunk);
+        return result.array();
+    }
+
+    private String xmlToString(Document doc) {
+        return XmlUtil.toString(doc, false);
+    }
+
+    /**
+     * For Hello message the framing is always EOM, but the framing mechanism
+     * may change.
+     *
+     * @param fm
+     *            new framing mechanism
+     */
+    public void setFramingMechanism(final FramingMechanism fm) {
+        logger.debug("Framing mechanism changed to {}", fm);
+        this.framing = fm;
+    }
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageHeader.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageHeader.java
new file mode 100644 (file)
index 0000000..ccf7a3f
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.util.messages;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Preconditions;
+import org.opendaylight.protocol.util.ByteArray;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Netconf message header is used only when chunked framing mechanism is
+ * supported. The header consists of only the length field.
+ */
+public final class NetconfMessageHeader {
+
+    private long length;
+
+    // \n#<length>\n
+    private static final byte[] headerBegin = new byte[] { (byte) 0x0a, (byte) 0x23 };
+
+    private static final byte headerEnd = (byte) 0x0a;
+
+    public static final int MIN_HEADER_LENGTH = 4; // bytes
+
+    public static final int MAX_HEADER_LENGTH = 13; // bytes
+
+    private boolean parsed = false;
+
+    public NetconfMessageHeader() {
+
+    }
+
+    public NetconfMessageHeader fromBytes(final byte[] bytes) {
+        // the length is variable therefore bytes between headerBegin and
+        // headerEnd mark the length
+        // the length should be only numbers and therefore easily parsed with
+        // ASCII
+        this.length = Long.parseLong(Charsets.US_ASCII.decode(
+                ByteBuffer.wrap(ByteArray.subByte(bytes, headerBegin.length, bytes.length - headerBegin.length - 1)))
+                .toString());
+        Preconditions.checkState(this.length < Integer.MAX_VALUE && this.length > 0);
+        this.parsed = true;
+        return this;
+    }
+
+    public byte[] toBytes() {
+        final byte[] l = String.valueOf(this.length).getBytes(Charsets.US_ASCII);
+        final byte[] h = new byte[headerBegin.length + l.length + 1];
+        System.arraycopy(headerBegin, 0, h, 0, headerBegin.length);
+        System.arraycopy(l, 0, h, headerBegin.length, l.length);
+        System.arraycopy(new byte[] { headerEnd }, 0, h, headerBegin.length + l.length, 1);
+        return h;
+    }
+
+    // FIXME: improve precision to long
+    public int getLength() {
+        return (int) this.length;
+    }
+
+    public void setLength(final int length) {
+        this.length = length;
+    }
+
+    /**
+     * @return the parsed
+     */
+    public boolean isParsed() {
+        return this.parsed;
+    }
+
+    /**
+     * @param parsed
+     *            the parsed to set
+     */
+    public void setParsed() {
+        this.parsed = false;
+    }
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageUtil.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageUtil.java
new file mode 100644 (file)
index 0000000..46053e7
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.util.messages;
+
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.w3c.dom.Document;
+
+public class NetconfMessageUtil {
+
+    public static boolean isOKMessage(NetconfMessage message) {
+        return isOKMessage(message.getDocument());
+    }
+
+    public static boolean isOKMessage(Document document) {
+        return isOKMessage(XmlElement.fromDomDocument(document));
+    }
+
+    public static boolean isOKMessage(XmlElement xmlElement) {
+        return xmlElement.getOnlyChildElement().getName().equals(XmlNetconfConstants.OK);
+    }
+
+    public static boolean isErrorMEssage(NetconfMessage message) {
+        return isErrorMessage(message.getDocument());
+    }
+
+    public static boolean isErrorMessage(Document document) {
+        return isErrorMessage(XmlElement.fromDomDocument(document));
+    }
+
+    public static boolean isErrorMessage(XmlElement xmlElement) {
+        return xmlElement.getOnlyChildElement().getName().equals(XmlNetconfConstants.RPC_ERROR);
+
+    }
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/SendErrorExceptionUtil.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/SendErrorExceptionUtil.java
new file mode 100644 (file)
index 0000000..639b428
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * 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.util.messages;
+
+import com.google.common.base.Preconditions;
+import io.netty.channel.Channel;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfSession;
+import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.*;
+
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import java.io.InputStream;
+import java.util.Map.Entry;
+
+public class SendErrorExceptionUtil {
+    private static final Logger logger = LoggerFactory.getLogger(SendErrorExceptionUtil.class);
+
+    public static void sendErrorMessage(final NetconfSession session,
+            final NetconfDocumentedException sendErrorException) {
+        logger.info("Sending error {}", sendErrorException.getMessage(), sendErrorException);
+        final Document errorDocument = createDocument(sendErrorException);
+        session.sendMessage(new NetconfMessage(errorDocument));
+    }
+
+    public static void sendErrorMessage(Channel channel, NetconfDocumentedException sendErrorException) {
+        logger.info("Sending error {}", sendErrorException.getMessage(), sendErrorException);
+        final Document errorDocument = createDocument(sendErrorException);
+        channel.writeAndFlush(new NetconfMessage(errorDocument));
+    }
+
+    public static void sendErrorMessage(NetconfSession session, NetconfDocumentedException sendErrorException,
+            NetconfMessage incommingMessage) {
+        final Document errorDocument = createDocument(sendErrorException);
+        logger.info("Sending error {}", XmlUtil.toString(errorDocument));
+        tryToCopyAttributes(incommingMessage.getDocument(), errorDocument, sendErrorException);
+        session.sendMessage(new NetconfMessage(errorDocument));
+    }
+
+    private static void tryToCopyAttributes(final Document incommingDocument, final Document errorDocument,
+            final NetconfDocumentedException sendErrorException) {
+        try {
+            final Element incommingRpc = incommingDocument.getDocumentElement();
+            Preconditions.checkState(incommingRpc.getTagName().equals(XmlNetconfConstants.RPC_KEY), "Missing "
+                    + XmlNetconfConstants.RPC_KEY + " " + "element");
+
+            final Element rpcReply = errorDocument.getDocumentElement();
+            Preconditions.checkState(rpcReply.getTagName().equals(XmlNetconfConstants.RPC_REPLY_KEY), "Missing "
+                    + XmlNetconfConstants.RPC_REPLY_KEY + " element");
+
+            final NamedNodeMap incomingAttributes = incommingRpc.getAttributes();
+            for (int i = 0; i < incomingAttributes.getLength(); i++) {
+                final Attr attr = (Attr) incomingAttributes.item(i);
+                // skip namespace
+                if (attr.getNodeName().equals(XmlUtil.XMLNS_ATTRIBUTE_KEY))
+                    continue;
+                rpcReply.setAttributeNode((Attr) errorDocument.importNode(attr, true));
+            }
+        } catch (final Exception e) {
+            logger.warn("Unable to copy incomming attributes to {}, returned rpc-error might be invalid for client",
+                    sendErrorException, e);
+        }
+    }
+
+    private static XPathExpression rpcErrorExpression = XMLNetconfUtil
+            .compileXPath("/netconf:rpc-reply/netconf:rpc-error");
+    private static XPathExpression errorTypeExpression = XMLNetconfUtil.compileXPath("netconf:error-type");
+    private static XPathExpression errorTagExpression = XMLNetconfUtil.compileXPath("netconf:error-tag");
+    private static XPathExpression errorSeverityExpression = XMLNetconfUtil.compileXPath("netconf:error-severity");
+
+    private static Document createDocument(final NetconfDocumentedException sendErrorException) {
+
+        final InputStream errIS = SendErrorExceptionUtil.class.getResourceAsStream("server_error.xml");
+        Document originalErrorDocument;
+        try {
+            originalErrorDocument = XmlUtil.readXmlToDocument(errIS);
+        } catch (final Exception e) {
+            throw new IllegalStateException(e);
+        }
+
+        final Document errorDocument = XmlUtil.createDocumentCopy(originalErrorDocument);
+        final Node rootNode = errorDocument.getFirstChild();
+
+        final Node rpcErrorNode = (Node) XmlUtil.evaluateXPath(rpcErrorExpression, rootNode, XPathConstants.NODE);
+
+        final Node errorTypeNode = (Node) XmlUtil.evaluateXPath(errorTypeExpression, rpcErrorNode, XPathConstants.NODE);
+        errorTypeNode.setTextContent(sendErrorException.getErrorType().getTagValue());
+
+        final Node errorTagNode = (Node) XmlUtil.evaluateXPath(errorTagExpression, rpcErrorNode, XPathConstants.NODE);
+        errorTagNode.setTextContent(sendErrorException.getErrorTag().getTagValue());
+
+        final Node errorSeverityNode = (Node) XmlUtil.evaluateXPath(errorSeverityExpression, rpcErrorNode,
+                XPathConstants.NODE);
+        errorSeverityNode.setTextContent(sendErrorException.getErrorSeverity().getTagValue());
+
+        if (sendErrorException.getErrorInfo() != null && sendErrorException.getErrorInfo().isEmpty() == false) {
+            /*
+             * <error-info> <bad-attribute>message-id</bad-attribute>
+             * <bad-element>rpc</bad-element> </error-info>
+             */
+            final Node errorInfoNode = errorDocument.createElementNS(
+                    XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, "error-info");
+
+            errorInfoNode.setPrefix(rootNode.getPrefix());
+            rpcErrorNode.appendChild(errorInfoNode);
+            for (final Entry<String, String> errorInfoEntry : sendErrorException.getErrorInfo().entrySet()) {
+                final Node node = errorDocument.createElementNS(
+                        XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, errorInfoEntry.getKey());
+                node.setTextContent(errorInfoEntry.getValue());
+                errorInfoNode.appendChild(node);
+            }
+
+        }
+        return errorDocument;
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java
new file mode 100644 (file)
index 0000000..5c9d823
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * 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.util.osgi;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.config.stat.ConfigProvider;
+import org.opendaylight.protocol.util.SSLUtil;
+
+import javax.net.ssl.SSLContext;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+public class NetconfConfigUtil {
+    private static final String PREFIX_PROP = "netconf.";
+
+    private enum InfixProp {
+        tcp, tls
+    }
+
+    private static final String PORT_SUFFIX_PROP = ".port";
+    private static final String ADDRESS_SUFFIX_PROP = ".address";
+
+    private static final String NETCONF_TLS_KEYSTORE_PROP = PREFIX_PROP + InfixProp.tls + ".keystore";
+    private static final String NETCONF_TLS_KEYSTORE_PASSWORD_PROP = NETCONF_TLS_KEYSTORE_PROP + ".password";
+
+    public static Optional<InetSocketAddress> extractTCPNetconfAddress(ConfigProvider configProvider) {
+        return extractSomeNetconfAddress(configProvider, InfixProp.tcp);
+    }
+
+    public static Optional<TLSConfiguration> extractTLSConfiguration(ConfigProvider configProvider) {
+        Optional<InetSocketAddress> address = extractSomeNetconfAddress(configProvider, InfixProp.tls);
+        if (address.isPresent()) {
+            String keystoreFileName = configProvider.getProperty(NETCONF_TLS_KEYSTORE_PROP);
+            File keystoreFile = new File(keystoreFileName);
+            checkState(keystoreFile.exists() && keystoreFile.isFile() && keystoreFile.canRead(),
+                    "Keystore file %s does not exist or is not readable file", keystoreFileName);
+            keystoreFile = keystoreFile.getAbsoluteFile();
+            String keystorePassword = configProvider.getProperty(NETCONF_TLS_KEYSTORE_PASSWORD_PROP);
+            checkNotNull(keystoreFileName, "Property %s must be defined for tls netconf server",
+                    NETCONF_TLS_KEYSTORE_PROP);
+            keystorePassword = keystorePassword != null ? keystorePassword : "";
+            return Optional.of(new TLSConfiguration(address.get(), keystoreFile, keystorePassword));
+        } else {
+            return Optional.absent();
+        }
+    }
+
+    public static class TLSConfiguration {
+        private final InetSocketAddress address;
+        private final File keystoreFile;
+        private final String keystorePassword;
+        private final SSLContext sslContext;
+
+        TLSConfiguration(InetSocketAddress address, File keystoreFile, String keystorePassword) {
+            this.address = address;
+            this.keystoreFile = keystoreFile;
+            this.keystorePassword = keystorePassword;
+            try {
+                try (InputStream keyStoreIS = new FileInputStream(keystoreFile)) {
+                    try (InputStream trustStoreIS = new FileInputStream(keystoreFile)) {
+                        sslContext = SSLUtil.initializeSecureContext("password", keyStoreIS, trustStoreIS, "SunX509");
+                    }
+                }
+            } catch (Exception e) {
+                throw new RuntimeException("Cannot initialize ssl context for netconf file " + keystoreFile, e);
+            }
+        }
+
+        public SSLContext getSslContext() {
+            return sslContext;
+        }
+
+        public InetSocketAddress getAddress() {
+            return address;
+        }
+
+        public File getKeystoreFile() {
+            return keystoreFile;
+        }
+
+        public String getKeystorePassword() {
+            return keystorePassword;
+        }
+    }
+
+    /**
+     * @param configProvider
+     *            from which properties are being read.
+     * @param infixProp
+     *            either tcp or tls
+     * @return absent if address is missing, value if address and port are
+     *         valid.
+     * @throws IllegalStateException
+     *             if address or port are invalid
+     */
+    private static Optional<InetSocketAddress> extractSomeNetconfAddress(ConfigProvider configProvider,
+            InfixProp infixProp) {
+        String address = configProvider.getProperty(PREFIX_PROP + infixProp + ADDRESS_SUFFIX_PROP);
+        if (address == null) {
+            return Optional.absent();
+        }
+        String portKey = PREFIX_PROP + infixProp + PORT_SUFFIX_PROP;
+        String portString = configProvider.getProperty(portKey);
+        checkNotNull(portString, "Netconf port must be specified in properties file with " + portKey);
+        try {
+            int port = Integer.valueOf(portString);
+            return Optional.of(new InetSocketAddress(address, port));
+        } catch (RuntimeException e) {
+            throw new IllegalStateException("Cannot create " + infixProp + " netconf address from address:" + address
+                    + " and port:" + portString, e);
+        }
+    }
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/HardcodedNamespaceResolver.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/HardcodedNamespaceResolver.java
new file mode 100644 (file)
index 0000000..23fe7cd
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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.util.xml;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.xml.namespace.NamespaceContext;
+
+import com.google.common.collect.ImmutableMap;
+
+// http://www.ibm.com/developerworks/library/x-nmspccontext/
+public class HardcodedNamespaceResolver implements NamespaceContext {
+    private final Map<String/* prefix */, String/* namespace */> prefixesToNamespaces;
+
+    public HardcodedNamespaceResolver(String prefix, String namespace) {
+        this(ImmutableMap.of(prefix, namespace));
+    }
+
+    public HardcodedNamespaceResolver(Map<String, String> prefixesToNamespaces) {
+        this.prefixesToNamespaces = Collections.unmodifiableMap(prefixesToNamespaces);
+    }
+
+    /**
+     * This method returns the uri for all prefixes needed. Wherever possible it
+     * uses XMLConstants.
+     *
+     * @param prefix
+     * @return uri
+     */
+    @Override
+    public String getNamespaceURI(String prefix) {
+        if (prefixesToNamespaces.containsKey(prefix)) {
+            return prefixesToNamespaces.get(prefix);
+        } else {
+            throw new IllegalStateException("Prefix mapping not found for " + prefix);
+        }
+    }
+
+    @Override
+    public String getPrefix(String namespaceURI) {
+        // Not needed in this context.
+        return null;
+    }
+
+    @Override
+    public Iterator<?> getPrefixes(String namespaceURI) {
+        // Not needed in this context.
+        return null;
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XMLNetconfUtil.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XMLNetconfUtil.java
new file mode 100644 (file)
index 0000000..d930322
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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.util.xml;
+
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+public class XMLNetconfUtil {
+
+    public static XPathExpression compileXPath(String xPath) {
+        final XPathFactory xPathfactory = XPathFactory.newInstance();
+        final XPath xpath = xPathfactory.newXPath();
+        xpath.setNamespaceContext(new HardcodedNamespaceResolver("netconf",
+                XmlNetconfConstants.RFC4741_TARGET_NAMESPACE));
+        try {
+            return xpath.compile(xPath);
+        } catch (final XPathExpressionException e) {
+            throw new IllegalStateException("Error while compiling xpath expression " + xPath, e);
+        }
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlElement.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlElement.java
new file mode 100644 (file)
index 0000000..5a7fde4
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * 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.util.xml;
+
+import java.io.IOException;
+import java.util.*;
+
+import javax.annotation.Nullable;
+
+import org.w3c.dom.*;
+import org.xml.sax.SAXException;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+public class XmlElement {
+
+    public final Element element;
+
+    private XmlElement(Element element) {
+        this.element = element;
+    }
+
+    public static XmlElement fromDomElement(Element e) {
+        return new XmlElement(e);
+    }
+
+    public static XmlElement fromDomDocument(Document xml) {
+        return new XmlElement(xml.getDocumentElement());
+    }
+
+    public static XmlElement fromString(String s) {
+        try {
+            return new XmlElement(XmlUtil.readXmlToElement(s));
+        } catch (IOException | SAXException e) {
+            throw new IllegalArgumentException("Unable to create from " + s, e);
+        }
+    }
+
+    public static XmlElement fromDomElementWithExpected(Element element, String expectedName) {
+        XmlElement xmlElement = XmlElement.fromDomElement(element);
+        xmlElement.checkName(expectedName);
+        return xmlElement;
+    }
+
+    public static XmlElement fromDomElementWithExpected(Element element, String expectedName, String expectedNamespace) {
+        XmlElement xmlElement = XmlElement.fromDomElementWithExpected(element, expectedName);
+        xmlElement.checkNamespace(expectedNamespace);
+        return xmlElement;
+    }
+
+    private static Map<String, String> extractNamespaces(Element typeElement) {
+        Map<String, String> namespaces = new HashMap<>();
+        NamedNodeMap attributes = typeElement.getAttributes();
+        for (int i = 0; i < attributes.getLength(); i++) {
+            Node attribute = attributes.item(i);
+            String attribKey = attribute.getNodeName();
+            if (attribKey.startsWith(XmlUtil.XMLNS_ATTRIBUTE_KEY)) {
+                String prefix;
+                if (attribKey.equals(XmlUtil.XMLNS_ATTRIBUTE_KEY)) {
+                    prefix = "";
+                } else {
+                    Preconditions.checkState(attribKey.startsWith(XmlUtil.XMLNS_ATTRIBUTE_KEY + ":"));
+                    prefix = attribKey.substring(XmlUtil.XMLNS_ATTRIBUTE_KEY.length() + 1);
+                }
+                namespaces.put(prefix, attribute.getNodeValue());
+            }
+        }
+        return namespaces;
+    }
+
+    public void checkName(String expectedName) {
+        Preconditions.checkArgument(getName().equals(expectedName), "Expected %s xml element but was %s", expectedName,
+                getName());
+    }
+
+    public void checkNamespaceAttribute(String expectedNamespace) {
+        Preconditions.checkArgument(getNamespaceAttribute().equals(expectedNamespace),
+                "Unexpected namespace %s for element %s, should be %s", getNamespaceAttribute(), expectedNamespace);
+    }
+
+    public void checkNamespace(String expectedNamespace) {
+        Preconditions.checkArgument(getNamespace().equals(expectedNamespace),
+                "Unexpected namespace %s for element %s, should be %s", getNamespace(), expectedNamespace);
+    }
+
+    public String getName() {
+        return element.getTagName();
+    }
+
+    public String getAttribute(String attributeName) {
+        return element.getAttribute(attributeName);
+    }
+
+    public String getAttribute(String attributeName, String namespace) {
+        return element.getAttributeNS(namespace, attributeName);
+    }
+
+    public void appendChild(Element element) {
+        this.element.appendChild(element);
+        // Element newElement = (Element) element.cloneNode(true);
+        // newElement.appendChild(configElement);
+        // return XmlElement.fromDomElement(newElement);
+    }
+
+    public Element getDomElement() {
+        return element;
+    }
+
+    public Map<String, Attr> getAttributes() {
+
+        Map<String, Attr> mappedAttributes = Maps.newHashMap();
+
+        NamedNodeMap attributes = element.getAttributes();
+        for (int i = 0; i < attributes.getLength(); i++) {
+            Attr attr = (Attr) attributes.item(i);
+            mappedAttributes.put(attr.getNodeName(), attr);
+        }
+
+        return mappedAttributes;
+    }
+
+    /**
+     * Non recursive
+     */
+    private List<XmlElement> getChildElementsInternal(ElementFilteringStrategy strat) {
+        NodeList childNodes = element.getChildNodes();
+        final List<XmlElement> result = new ArrayList<>();
+        for (int i = 0; i < childNodes.getLength(); i++) {
+            Node item = childNodes.item(i);
+            if (item instanceof Element == false)
+                continue;
+            if (strat.accept((Element) item))
+                result.add(new XmlElement((Element) item));
+        }
+
+        return result;
+    }
+
+    public List<XmlElement> getChildElements() {
+        return getChildElementsInternal(new ElementFilteringStrategy() {
+            @Override
+            public boolean accept(Element e) {
+                return true;
+            }
+        });
+    }
+
+    public List<XmlElement> getChildElementsWithinNamespace(final String childName, String namespace) {
+        return Lists.newArrayList(Collections2.filter(getChildElementsWithinNamespace(namespace),
+                new Predicate<XmlElement>() {
+                    @Override
+                    public boolean apply(@Nullable XmlElement xmlElement) {
+                        return xmlElement.getName().equals(childName);
+                    }
+                }));
+    }
+
+    public List<XmlElement> getChildElementsWithinNamespace(final String namespace) {
+        return getChildElementsInternal(new ElementFilteringStrategy() {
+            @Override
+            public boolean accept(Element e) {
+                return XmlElement.fromDomElement(e).getNamespace().equals(namespace);
+            }
+
+        });
+    }
+
+    public List<XmlElement> getChildElements(final String tagName) {
+        return getChildElementsInternal(new ElementFilteringStrategy() {
+            @Override
+            public boolean accept(Element e) {
+                return e.getTagName().equals(tagName);
+            }
+        });
+    }
+
+    public XmlElement getOnlyChildElement(String childName) {
+        List<XmlElement> nameElements = getChildElements(childName);
+        Preconditions.checkState(nameElements.size() == 1, "One element " + childName + " expected in " + toString());
+        return nameElements.get(0);
+    }
+
+    public Optional<XmlElement> getOnlyChildElementOptionally(String childName) {
+        try {
+            return Optional.of(getOnlyChildElement(childName));
+        } catch (Exception e) {
+            return Optional.absent();
+        }
+    }
+
+    public Optional<XmlElement> getOnlyChildElementOptionally(String childName, String namespace) {
+        try {
+            return Optional.of(getOnlyChildElement(childName, namespace));
+        } catch (Exception e) {
+            return Optional.absent();
+        }
+    }
+
+    public XmlElement getOnlyChildElementWithSameNamespace(String childName) {
+        return getOnlyChildElement(childName, getNamespace());
+    }
+
+    public Optional<XmlElement> getOnlyChildElementWithSameNamespaceOptionally(String childName) {
+        try {
+            return Optional.of(getOnlyChildElement(childName, getNamespace()));
+        } catch (Exception e) {
+            return Optional.absent();
+        }
+    }
+
+    public XmlElement getOnlyChildElementWithSameNamespace() {
+        XmlElement childElement = getOnlyChildElement();
+        childElement.checkNamespace(getNamespace());
+        return childElement;
+    }
+
+    public Optional<XmlElement> getOnlyChildElementWithSameNamespaceOptionally() {
+        try {
+            XmlElement childElement = getOnlyChildElement();
+            childElement.checkNamespace(getNamespace());
+            return Optional.of(childElement);
+        } catch (Exception e) {
+            return Optional.absent();
+        }
+    }
+
+    public XmlElement getOnlyChildElement(final String childName, String namespace) {
+        List<XmlElement> children = getChildElementsWithinNamespace(namespace);
+        children = Lists.newArrayList(Collections2.filter(children, new Predicate<XmlElement>() {
+            @Override
+            public boolean apply(@Nullable XmlElement xmlElement) {
+                return xmlElement.getName().equals(childName);
+            }
+        }));
+        Preconditions.checkState(children.size() == 1, "One element %s:%s expected in %s but was %s", namespace,
+                childName, toString(), children.size());
+        return children.get(0);
+    }
+
+    public XmlElement getOnlyChildElement() {
+        List<XmlElement> children = getChildElements();
+        Preconditions.checkState(children.size() == 1, "One element expected in %s but was %s", toString(),
+                children.size());
+        return children.get(0);
+    }
+
+    public String getTextContent() {
+        Node textChild = element.getFirstChild();
+        Preconditions.checkState(textChild instanceof Text, getName() + " should contain text");
+        String content = textChild.getTextContent();
+        // Trim needed
+        return content.trim();
+    }
+
+    public String getNamespaceAttribute() {
+        String attribute = element.getAttribute(XmlUtil.XMLNS_ATTRIBUTE_KEY);
+        Preconditions.checkState(attribute != null && !attribute.equals(""), "Element %s must specify a %s attribute",
+                toString(), XmlUtil.XMLNS_ATTRIBUTE_KEY);
+        return attribute;
+    }
+
+    public String getNamespace() {
+        String namespaceURI = element.getNamespaceURI();
+        Preconditions.checkState(namespaceURI != null, "No namespace defined for %s", this);
+        return namespaceURI.toString();
+    }
+
+    @Override
+    public String toString() {
+        final StringBuffer sb = new StringBuffer("XmlElement{");
+        sb.append("name='").append(getName()).append('\'');
+        if (element.getNamespaceURI() != null) {
+            sb.append(", namespace='").append(getNamespace()).append('\'');
+        }
+        sb.append('}');
+        return sb.toString();
+    }
+
+    /**
+     * Search for element's attributes defining namespaces. Look for the one
+     * namespace that matches prefix of element's text content. E.g.
+     *
+     * <pre>
+     * &lt;type
+     * xmlns:th-java="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl"&gt;th-java:threadfactory-naming&lt;/type&gt;
+     * </pre>
+     *
+     * returns {"th-java","urn:.."}. If no prefix is matched, then default
+     * namespace is returned with empty string as key. If no default namespace
+     * is found value will be null.
+     */
+    public Map.Entry<String/* prefix */, String/* namespace */> findNamespaceOfTextContent() {
+        Map<String, String> namespaces = extractNamespaces(element);
+        String textContent = getTextContent();
+        int indexOfColon = textContent.indexOf(":");
+        String prefix;
+        if (indexOfColon > -1) {
+            prefix = textContent.substring(0, indexOfColon);
+        } else {
+            prefix = "";
+        }
+        if (namespaces.containsKey(prefix) == false) {
+            throw new IllegalArgumentException("Cannot find namespace for " + element + ". Prefix from content is "
+                    + prefix + ". Found namespaces " + namespaces);
+        }
+        return Maps.immutableEntry(prefix, namespaces.get(prefix));
+    }
+
+    public List<XmlElement> getChildElementsWithSameNamespace(final String childName) {
+        List<XmlElement> children = getChildElementsWithinNamespace(getNamespace());
+        return Lists.newArrayList(Collections2.filter(children, new Predicate<XmlElement>() {
+            @Override
+            public boolean apply(@Nullable XmlElement xmlElement) {
+                return xmlElement.getName().equals(childName);
+            }
+        }));
+    }
+
+    public void checkUnrecognisedElements(List<XmlElement> recognisedElements,
+            XmlElement... additionalRecognisedElements) {
+        List<XmlElement> childElements = getChildElements();
+        childElements.removeAll(recognisedElements);
+        for (XmlElement additionalRecognisedElement : additionalRecognisedElements) {
+            childElements.remove(additionalRecognisedElement);
+        }
+        Preconditions.checkState(childElements.isEmpty(), "Unrecognised elements %s in %s", childElements, this);
+    }
+
+    public void checkUnrecognisedElements(XmlElement... additionalRecognisedElements) {
+        checkUnrecognisedElements(Collections.<XmlElement> emptyList(), additionalRecognisedElements);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        XmlElement that = (XmlElement) o;
+
+        if (!element.isEqualNode(that.element))
+            return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return element.hashCode();
+    }
+
+    private static interface ElementFilteringStrategy {
+        boolean accept(Element e);
+    }
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlNetconfConstants.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlNetconfConstants.java
new file mode 100644 (file)
index 0000000..0791812
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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.util.xml;
+
+public class XmlNetconfConstants {
+
+    public static final String MOUNTPOINTS = "mountpoints";
+    public static final String MOUNTPOINT = "mountpoint";
+    public static final String ID = "id";
+    public static final String CAPABILITY = "capability";
+    public static final String CAPABILITIES = "capabilities";
+    public static final String COMMIT = "commit";
+    public static final String TYPE_KEY = "type";
+    public static final String MODULE_KEY = "module";
+    public static final String INSTANCE_KEY = "instance";
+    public static final String OPERATION_ATTR_KEY = "operation";
+    public static final String SERVICES_KEY = "services";
+    public static final String CONFIG_KEY = "config";
+    public static final String MODULES_KEY = "modules";
+    public static final String CONFIGURATION_KEY = "configuration";
+    public static final String DATA_KEY = "data";
+    public static final String OK = "ok";
+    public static final String FILTER = "filter";
+    public static final String SOURCE_KEY = "source";
+    public static final String RPC_KEY = "rpc";
+    public static final String RPC_REPLY_KEY = "rpc-reply";
+    public static final String RPC_ERROR = "rpc-error";
+    public static final String NAME_KEY = "name";
+    //
+    //
+    public static final String RFC4741_TARGET_NAMESPACE = "urn:ietf:params:xml:ns:netconf:base:1.0";
+    public static final String URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0 = "urn:ietf:params:xml:ns:netconf:base:1.0";
+    public static final String URN_IETF_PARAMS_XML_NS_YANG_IETF_NETCONF_MONITORING = "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring";
+    // TODO where to store namespace of config ?
+    public static final String URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG = "urn:opendaylight:params:xml:ns:yang:controller:config";
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlNetconfValidator.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlNetconfValidator.java
new file mode 100644 (file)
index 0000000..de0ebcc
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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.util.xml;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.Validator;
+
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+import com.google.common.base.Preconditions;
+
+public class XmlNetconfValidator {
+    static final Schema schema;
+
+    static {
+        final InputStream xmlSchema = XmlNetconfValidator.class.getResourceAsStream("/xml.xsd");
+        Preconditions.checkNotNull(xmlSchema, "Cannot find xml.xsd");
+
+        final InputStream rfc4714Schema = XmlNetconfValidator.class.getResourceAsStream("/rfc4741.xsd");
+        Preconditions.checkNotNull(rfc4714Schema, "Cannot find rfc4741.xsd");
+        schema = XmlUtil.loadSchema(xmlSchema, rfc4714Schema);
+    }
+
+    public static void validate(Document inputDocument) throws SAXException, IOException {
+        final Validator validator = schema.newValidator();
+        final Source source = new DOMSource(inputDocument);
+        validator.validate(source);
+    }
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlUtil.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlUtil.java
new file mode 100644 (file)
index 0000000..a2d43b2
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * 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.util.xml;
+
+import java.io.*;
+
+import javax.xml.XMLConstants;
+import javax.xml.namespace.QName;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.*;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+import com.google.common.base.Charsets;
+
+public class XmlUtil {
+
+    public static final String XMLNS_ATTRIBUTE_KEY = "xmlns";
+
+    public static Element readXmlToElement(String xmlContent) throws SAXException, IOException {
+        Document doc = readXmlToDocument(xmlContent);
+        return doc.getDocumentElement();
+    }
+
+    public static Element readXmlToElement(InputStream xmlContent) throws SAXException, IOException {
+        Document doc = readXmlToDocument(xmlContent);
+        return doc.getDocumentElement();
+    }
+
+    public static Document readXmlToDocument(String xmlContent) throws SAXException, IOException {
+        return readXmlToDocument(new ByteArrayInputStream(xmlContent.getBytes(Charsets.UTF_8)));
+    }
+
+    public static Document readXmlToDocument(InputStream xmlContent) throws SAXException, IOException {
+        DocumentBuilderFactory factory = getDocumentBuilderFactory();
+        DocumentBuilder dBuilder;
+        try {
+            dBuilder = factory.newDocumentBuilder();
+        } catch (ParserConfigurationException e) {
+            throw new RuntimeException(e);
+        }
+        Document doc = dBuilder.parse(xmlContent);
+
+        doc.getDocumentElement().normalize();
+        return doc;
+    }
+
+    public static Element readXmlToElement(File xmlFile) throws SAXException, IOException {
+        return readXmlToDocument(new FileInputStream(xmlFile)).getDocumentElement();
+    }
+
+    private static final DocumentBuilderFactory getDocumentBuilderFactory() {
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        factory.setNamespaceAware(true);
+        factory.setCoalescing(true);
+        // factory.setValidating(true);
+        factory.setIgnoringElementContentWhitespace(true);
+        factory.setIgnoringComments(true);
+        return factory;
+    }
+
+    public static Document newDocument() {
+        DocumentBuilderFactory factory = getDocumentBuilderFactory();
+        try {
+            DocumentBuilder builder = factory.newDocumentBuilder();
+            Document document = builder.newDocument();
+            return document;
+        } catch (ParserConfigurationException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static Element createTextElement(Document document, String name, String content) {
+        Element typeElement = document.createElement(name);
+        typeElement.appendChild(document.createTextNode(content));
+        return typeElement;
+    }
+
+    public static void addNamespaceAttr(Element root, String namespace) {
+        root.setAttribute(XMLNS_ATTRIBUTE_KEY, namespace);
+    }
+
+    public static void addPrefixedNamespaceAttr(Element root, String prefix, String namespace) {
+        root.setAttribute(concat(XMLNS_ATTRIBUTE_KEY, prefix), namespace);
+    }
+
+    public static Element createPrefixedTextElement(Document document, String key, String prefix, String moduleName) {
+        return createTextElement(document, key, concat(prefix, moduleName));
+    }
+
+    private static String concat(String prefix, String value) {
+        return prefix + ":" + value;
+    }
+
+    public static String toString(Document document) {
+        return toString(document.getDocumentElement());
+    }
+
+    public static String toString(Element xml) {
+        return toString(xml, false);
+    }
+
+    public static String toString(XmlElement xmlElement) {
+        return toString(xmlElement.getDomElement(), false);
+    }
+
+    public static String toString(Element xml, boolean addXmlDeclaration) {
+        try {
+            Transformer transformer = TransformerFactory.newInstance().newTransformer();
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, addXmlDeclaration == true ? "no" : "yes");
+
+            StreamResult result = new StreamResult(new StringWriter());
+            DOMSource source = new DOMSource(xml);
+            transformer.transform(source, result);
+
+            String xmlString = result.getWriter().toString();
+            return xmlString;
+        } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
+            throw new RuntimeException("Unable to serialize xml element " + xml, e);
+        }
+    }
+
+    public static String toString(Document doc, boolean addXmlDeclaration) {
+        return toString(doc.getDocumentElement(), addXmlDeclaration);
+    }
+
+    public static Schema loadSchema(InputStream... fromStreams) {
+        Source[] sources = new Source[fromStreams.length];
+        int i = 0;
+        for (InputStream stream : fromStreams) {
+            sources[i++] = new StreamSource(stream);
+        }
+
+        final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+        try {
+            return schemaFactory.newSchema(sources);
+        } catch (SAXException e) {
+            throw new IllegalStateException("Failed to instantiate XML schema", e);
+        }
+    }
+
+    public static Object evaluateXPath(XPathExpression expr, Object rootNode, QName returnType) {
+        try {
+            return expr.evaluate(rootNode, returnType);
+        } catch (XPathExpressionException e) {
+            throw new IllegalStateException("Error while evaluating xpath expression " + expr, e);
+        }
+    }
+
+    public static Document createDocumentCopy(Document original) {
+        final Document copiedDocument = newDocument();
+        final Node copiedRoot = copiedDocument.importNode(original.getDocumentElement(), true);
+        copiedDocument.appendChild(copiedRoot);
+        return copiedDocument;
+    }
+}
diff --git a/opendaylight/netconf/netconf-util/src/main/resources/org/opendaylight/controller/netconf/util/messages/server_error.xml b/opendaylight/netconf/netconf-util/src/main/resources/org/opendaylight/controller/netconf/util/messages/server_error.xml
new file mode 100644 (file)
index 0000000..ff0c0c5
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <rpc-error>
+        <error-type>*** transport/rpc/protocol/application</error-type>
+        <error-tag>*** RFC 4741 Appendix A. NETCONF Error List</error-tag>
+        <error-severity>*** error/warning</error-severity>
+
+        <!--
+        <error-info>
+            <bad-attribute>message-id</bad-attribute>
+            <bad-element>rpc</bad-element>
+        </error-info>
+        -->
+
+    </rpc-error>
+</rpc-reply>
diff --git a/opendaylight/netconf/netconf-util/src/main/resources/rfc4741.xsd b/opendaylight/netconf/netconf-util/src/main/resources/rfc4741.xsd
new file mode 100644 (file)
index 0000000..990049d
--- /dev/null
@@ -0,0 +1,585 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
+           targetNamespace="urn:ietf:params:xml:ns:netconf:base:1.0"
+           elementFormDefault="qualified"
+           attributeFormDefault="unqualified"
+           xml:lang="en">
+    <!--
+       import standard XML definitions
+       -->
+    <xs:import namespace="http://www.w3.org/XML/1998/namespace"
+               schemaLocation="http://www.w3.org/2001/xml.xsd">
+
+        <xs:annotation>
+            <xs:documentation>
+                This import accesses the xml: attribute groups for the
+                xml:lang as declared on the error-message element.
+            </xs:documentation>
+        </xs:annotation>
+    </xs:import>
+    <!--
+       message-id attribute
+       -->
+    <xs:simpleType name="messageIdType">
+        <xs:restriction base="xs:string">
+            <xs:maxLength value="4095"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <!--
+       Types used for session-id
+     -->
+    <xs:simpleType name="SessionId">
+        <xs:restriction base="xs:unsignedInt">
+            <xs:minInclusive value="1"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="SessionIdOrZero">
+        <xs:restriction base="xs:unsignedInt"/>
+    </xs:simpleType>
+    <!--
+       <rpc> element
+       -->
+    <xs:complexType name="rpcType">
+        <xs:sequence>
+            <xs:element ref="rpcOperation"/>
+
+
+        </xs:sequence>
+        <xs:attribute name="message-id" type="messageIdType"
+                      use="required"/>
+        <!--
+             Arbitrary attributes can be supplied with <rpc> element.
+           -->
+        <xs:anyAttribute processContents="lax"/>
+    </xs:complexType>
+    <xs:element name="rpc" type="rpcType"/>
+    <!--
+       data types and elements used to construct rpc-errors
+       -->
+    <xs:simpleType name="ErrorType">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="transport"/>
+            <xs:enumeration value="rpc"/>
+            <xs:enumeration value="protocol"/>
+            <xs:enumeration value="application"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="ErrorTag">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="in-use"/>
+            <xs:enumeration value="invalid-value"/>
+            <xs:enumeration value="too-big"/>
+            <xs:enumeration value="missing-attribute"/>
+            <xs:enumeration value="bad-attribute"/>
+            <xs:enumeration value="unknown-attribute"/>
+            <xs:enumeration value="missing-element"/>
+            <xs:enumeration value="bad-element"/>
+            <xs:enumeration value="unknown-element"/>
+            <xs:enumeration value="unknown-namespace"/>
+            <xs:enumeration value="access-denied"/>
+            <xs:enumeration value="lock-denied"/>
+            <xs:enumeration value="resource-denied"/>
+            <xs:enumeration value="rollback-failed"/>
+            <xs:enumeration value="data-exists"/>
+            <xs:enumeration value="data-missing"/>
+            <xs:enumeration value="operation-not-supported"/>
+            <xs:enumeration value="operation-failed"/>
+            <xs:enumeration value="partial-operation"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="ErrorSeverity">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="error"/>
+            <xs:enumeration value="warning"/>
+        </xs:restriction>
+
+
+    </xs:simpleType>
+    <xs:complexType name="errorInfoType">
+        <xs:sequence>
+            <xs:choice>
+                <xs:element name="session-id" type="SessionIdOrZero"/>
+                <xs:sequence minOccurs="0" maxOccurs="unbounded">
+                    <xs:sequence>
+                        <xs:element name="bad-attribute" type="xs:QName"
+                                    minOccurs="0" maxOccurs="1"/>
+                        <xs:element name="bad-element" type="xs:QName"
+                                    minOccurs="0" maxOccurs="1"/>
+                        <xs:element name="ok-element" type="xs:QName"
+                                    minOccurs="0" maxOccurs="1"/>
+                        <xs:element name="err-element" type="xs:QName"
+                                    minOccurs="0" maxOccurs="1"/>
+                        <xs:element name="noop-element" type="xs:QName"
+                                    minOccurs="0" maxOccurs="1"/>
+                        <xs:element name="bad-namespace" type="xs:QName"
+                                    minOccurs="0" maxOccurs="1"/>
+                    </xs:sequence>
+                </xs:sequence>
+            </xs:choice>
+            <!-- elements from any other namespace are also allowed
+                  to follow the NETCONF elements -->
+            <xs:any namespace="##other"
+                    minOccurs="0" maxOccurs="unbounded"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="rpcErrorType">
+        <xs:sequence>
+            <xs:element name="error-type" type="ErrorType"/>
+            <xs:element name="error-tag" type="ErrorTag"/>
+            <xs:element name="error-severity" type="ErrorSeverity"/>
+            <xs:element name="error-app-tag" type="xs:string"
+                        minOccurs="0"/>
+            <xs:element name="error-path" type="xs:string" minOccurs="0"/>
+            <xs:element name="error-message" minOccurs="0">
+                <xs:complexType>
+                    <xs:simpleContent>
+                        <xs:extension base="xs:string">
+                            <xs:attribute ref="xml:lang" use="optional"/>
+                        </xs:extension>
+                    </xs:simpleContent>
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="error-info" type="errorInfoType"
+                        minOccurs="0"/>
+        </xs:sequence>
+
+
+    </xs:complexType>
+    <!--
+       <rpc-reply> element
+       -->
+    <xs:complexType name="rpcReplyType">
+        <xs:choice>
+            <xs:element name="ok"/>
+            <xs:group ref="rpcResponse"/>
+        </xs:choice>
+        <xs:attribute name="message-id" type="messageIdType"
+                      use="optional"/>
+        <!--
+             Any attributes supplied with <rpc> element must be returned
+             on <rpc-reply>.
+           -->
+        <xs:anyAttribute processContents="lax"/>
+    </xs:complexType>
+    <xs:group name="rpcResponse">
+        <xs:sequence>
+            <xs:element name="rpc-error" type="rpcErrorType"
+                        minOccurs="0" maxOccurs="unbounded"/>
+            <xs:element name="data" type="dataInlineType" minOccurs="0"/>
+        </xs:sequence>
+    </xs:group>
+    <xs:element name="rpc-reply" type="rpcReplyType"/>
+    <!--
+       Type for <test-option> parameter to <edit-config>
+       -->
+    <xs:simpleType name="testOptionType">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="test-then-set"/>
+            <xs:enumeration value="set"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <!--
+       Type for <error-option> parameter to <edit-config>
+       -->
+    <xs:simpleType name="errorOptionType">
+        <xs:restriction base="xs:string">
+            <xs:annotation>
+                <xs:documentation>
+                    Use of the rollback-on-error value requires
+                    the :rollback-on-error capability.
+                </xs:documentation>
+            </xs:annotation>
+            <xs:enumeration value="stop-on-error"/>
+            <xs:enumeration value="continue-on-error"/>
+            <xs:enumeration value="rollback-on-error"/>
+
+
+        </xs:restriction>
+    </xs:simpleType>
+    <!--
+       rpcOperationType: used as a base type for all
+       NETCONF operations
+       -->
+    <xs:complexType name="rpcOperationType"/>
+    <xs:element name="rpcOperation"
+                type="rpcOperationType" abstract="true"/>
+    <!--
+       Type for <config> element
+       -->
+    <xs:complexType name="configInlineType">
+        <xs:complexContent>
+            <xs:extension base="xs:anyType"/>
+        </xs:complexContent>
+    </xs:complexType>
+    <!--
+       Type for <data> element
+       -->
+    <xs:complexType name="dataInlineType">
+        <xs:complexContent>
+            <xs:extension base="xs:anyType"/>
+        </xs:complexContent>
+    </xs:complexType>
+    <!--
+       Type for <filter> element
+       -->
+    <xs:simpleType name="FilterType">
+        <xs:restriction base="xs:string">
+            <xs:annotation>
+                <xs:documentation>
+                    Use of the xpath value requires the :xpath capability.
+                </xs:documentation>
+            </xs:annotation>
+            <xs:enumeration value="subtree"/>
+            <xs:enumeration value="xpath"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:complexType name="filterInlineType">
+        <xs:complexContent>
+            <xs:extension base="xs:anyType">
+                <xs:attribute name="type"
+                              type="FilterType" default="subtree"/>
+                <!-- if type="xpath", the xpath expression
+                   appears in the select element -->
+                <xs:attribute name="select"/>
+            </xs:extension>
+
+
+        </xs:complexContent>
+    </xs:complexType>
+    <!--
+       configuration datastore names
+       -->
+    <xs:annotation>
+        <xs:documentation>
+            The startup datastore can be used only if the :startup
+            capability is advertised. The candidate datastore can
+            be used only if the :candidate datastore is advertised.
+        </xs:documentation>
+    </xs:annotation>
+    <xs:complexType name="configNameType"/>
+    <xs:element name="config-name"
+                type="configNameType" abstract="true"/>
+    <xs:element name="startup" type="configNameType"
+                substitutionGroup="config-name"/>
+    <xs:element name="candidate" type="configNameType"
+                substitutionGroup="config-name"/>
+    <xs:element name="running" type="configNameType"
+                substitutionGroup="config-name"/>
+    <!--
+       operation attribute used in <edit-config>
+       -->
+    <xs:simpleType name="editOperationType">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="merge"/>
+            <xs:enumeration value="replace"/>
+            <xs:enumeration value="create"/>
+            <xs:enumeration value="delete"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:attribute name="operation"
+                  type="editOperationType" default="merge"/>
+    <!--
+       <default-operation> element
+       -->
+    <xs:simpleType name="defaultOperationType">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="merge"/>
+            <xs:enumeration value="replace"/>
+            <xs:enumeration value="none"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <!--
+       <url> element
+       -->
+    <xs:complexType name="configURIType">
+
+
+        <xs:annotation>
+            <xs:documentation>
+                Use of the url element requires the :url capability.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:simpleContent>
+            <xs:extension base="xs:anyURI"/>
+        </xs:simpleContent>
+    </xs:complexType>
+    <!--
+       Type for <source> element (except <get-config>)
+       -->
+    <xs:complexType name="rpcOperationSourceType">
+        <xs:choice>
+            <xs:element name="config" type="configInlineType"/>
+            <xs:element ref="config-name"/>
+            <xs:element name="url" type="configURIType"/>
+        </xs:choice>
+    </xs:complexType>
+    <!--
+       Type for <source> element in <get-config>
+       -->
+    <xs:complexType name="getConfigSourceType">
+        <xs:choice>
+            <xs:element ref="config-name"/>
+            <xs:element name="url" type="configURIType"/>
+        </xs:choice>
+    </xs:complexType>
+    <!--
+       Type for <target> element
+       -->
+    <xs:complexType name="rpcOperationTargetType">
+        <xs:choice>
+            <xs:element ref="config-name"/>
+            <xs:element name="url" type="configURIType"/>
+        </xs:choice>
+    </xs:complexType>
+    <!--
+       <get-config> operation
+       -->
+    <xs:complexType name="getConfigType">
+        <xs:complexContent>
+            <xs:extension base="rpcOperationType">
+                <xs:sequence>
+                    <xs:element name="source"
+                                type="getConfigSourceType"/>
+                    <xs:element name="filter"
+                                type="filterInlineType" minOccurs="0"/>
+
+
+                </xs:sequence>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="get-config" type="getConfigType"
+                substitutionGroup="rpcOperation"/>
+    <!--
+       <edit-config> operation
+       -->
+    <xs:complexType name="editConfigType">
+        <xs:complexContent>
+            <xs:extension base="rpcOperationType">
+                <xs:sequence>
+                    <xs:annotation>
+                        <xs:documentation>
+                            Use of the test-option element requires the
+                            :validate capability. Use of the url element
+                            requires the :url capability.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:element name="target"
+                                type="rpcOperationTargetType"/>
+                    <xs:element name="default-operation"
+                                type="defaultOperationType"
+                                minOccurs="0"/>
+                    <xs:element name="test-option"
+                                type="testOptionType"
+                                minOccurs="0"/>
+                    <xs:element name="error-option"
+                                type="errorOptionType"
+                                minOccurs="0"/>
+                    <xs:choice>
+                        <xs:element name="config"
+                                    type="configInlineType"/>
+                        <xs:element name="url"
+                                    type="configURIType"/>
+                    </xs:choice>
+                </xs:sequence>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="edit-config" type="editConfigType"
+                substitutionGroup="rpcOperation"/>
+    <!--
+       <copy-config> operation
+       -->
+    <xs:complexType name="copyConfigType">
+        <xs:complexContent>
+
+
+            <xs:extension base="rpcOperationType">
+                <xs:sequence>
+                    <xs:element name="target" type="rpcOperationTargetType"/>
+                    <xs:element name="source" type="rpcOperationSourceType"/>
+                </xs:sequence>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="copy-config" type="copyConfigType"
+                substitutionGroup="rpcOperation"/>
+    <!--
+       <delete-config> operation
+       -->
+    <xs:complexType name="deleteConfigType">
+        <xs:complexContent>
+            <xs:extension base="rpcOperationType">
+                <xs:sequence>
+                    <xs:element name="target" type="rpcOperationTargetType"/>
+                </xs:sequence>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="delete-config" type="deleteConfigType"
+                substitutionGroup="rpcOperation"/>
+    <!--
+       <get> operation
+       -->
+    <xs:complexType name="getType">
+        <xs:complexContent>
+            <xs:extension base="rpcOperationType">
+                <xs:sequence>
+                    <xs:element name="filter"
+                                type="filterInlineType" minOccurs="0"/>
+                </xs:sequence>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="get" type="getType"
+                substitutionGroup="rpcOperation"/>
+    <!--
+       <lock> operation
+       -->
+    <xs:complexType name="lockType">
+        <xs:complexContent>
+            <xs:extension base="rpcOperationType">
+                <xs:sequence>
+                    <xs:element name="target"
+                                type="rpcOperationTargetType"/>
+
+
+                </xs:sequence>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="lock" type="lockType"
+                substitutionGroup="rpcOperation"/>
+    <!--
+       <unlock> operation
+       -->
+    <xs:complexType name="unlockType">
+        <xs:complexContent>
+            <xs:extension base="rpcOperationType">
+                <xs:sequence>
+                    <xs:element name="target" type="rpcOperationTargetType"/>
+                </xs:sequence>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="unlock" type="unlockType"
+                substitutionGroup="rpcOperation"/>
+    <!--
+       <operations> operation
+       -->
+    <xs:complexType name="validateType">
+        <xs:annotation>
+            <xs:documentation>
+                The validate operation requires the :validate capability.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexContent>
+            <xs:extension base="rpcOperationType">
+                <xs:sequence>
+                    <xs:element name="source" type="rpcOperationSourceType"/>
+                </xs:sequence>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="validate" type="validateType"
+                substitutionGroup="rpcOperation"/>
+    <!--
+       <commit> operation
+       -->
+    <xs:simpleType name="confirmTimeoutType">
+        <xs:restriction base="xs:unsignedInt">
+            <xs:minInclusive value="1"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:complexType name="commitType">
+
+
+        <xs:annotation>
+            <xs:documentation>
+                The commit operation requires the :candidate capability.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexContent>
+            <xs:extension base="rpcOperationType">
+                <xs:sequence>
+                    <xs:annotation>
+                        <xs:documentation>
+                            Use of the confirmed and confirm-timeout elements
+                            requires the :confirmed-commit capability.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:element name="confirmed" minOccurs="0"/>
+                    <xs:element name="confirm-timeout"
+                                type="confirmTimeoutType"
+                                minOccurs="0"/>
+                </xs:sequence>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="commit" type="commitType"
+                substitutionGroup="rpcOperation"/>
+    <!--
+       <discard-changes> operation
+       -->
+    <xs:complexType name="discardChangesType">
+        <xs:annotation>
+            <xs:documentation>
+                The discard-changes operation requires the
+                :candidate capability.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexContent>
+            <xs:extension base="rpcOperationType"/>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="discard-changes"
+                type="discardChangesType"
+                substitutionGroup="rpcOperation"/>
+    <!--
+       <close-session> operation
+       -->
+    <xs:complexType name="closeSessionType">
+        <xs:complexContent>
+            <xs:extension base="rpcOperationType"/>
+        </xs:complexContent>
+
+
+    </xs:complexType>
+    <xs:element name="close-session" type="closeSessionType"
+                substitutionGroup="rpcOperation"/>
+    <!--
+       <kill-session> operation
+       -->
+    <xs:complexType name="killSessionType">
+        <xs:complexContent>
+            <xs:extension base="rpcOperationType">
+                <xs:sequence>
+                    <xs:element name="session-id"
+                                type="SessionId" minOccurs="1"/>
+                </xs:sequence>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="kill-session" type="killSessionType"
+                substitutionGroup="rpcOperation"/>
+    <!--
+       <hello> element
+       -->
+    <xs:element name="hello">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="capabilities">
+                    <xs:complexType>
+                        <xs:sequence>
+                            <xs:element name="capability" type="xs:anyURI"
+                                        maxOccurs="unbounded"/>
+                        </xs:sequence>
+                    </xs:complexType>
+                </xs:element>
+                <xs:element name="session-id"
+                            type="SessionId" minOccurs="0"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+</xs:schema>
diff --git a/opendaylight/netconf/netconf-util/src/main/resources/xml.xsd b/opendaylight/netconf/netconf-util/src/main/resources/xml.xsd
new file mode 100644 (file)
index 0000000..0f7db9d
--- /dev/null
@@ -0,0 +1,327 @@
+<?xml version='1.0'?>
+<?xml-stylesheet href="../2008/09/xsd.xsl" type="text/xsl"?>
+<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace"
+           xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           xmlns="http://www.w3.org/1999/xhtml"
+           xml:lang="en">
+
+    <xs:annotation>
+        <xs:documentation>
+            <div>
+                <h1>About the XML namespace</h1>
+
+                <div class="bodytext">
+                    <p>
+                        This schema document describes the XML namespace, in a form
+                        suitable for import by other schema documents.
+                    </p>
+                    <p>
+                        See
+                        <a href="http://www.w3.org/XML/1998/namespace.html">
+                            http://www.w3.org/XML/1998/namespace.html
+                        </a>
+                        and
+                        <a href="http://www.w3.org/TR/REC-xml">
+                            http://www.w3.org/TR/REC-xml
+                        </a>
+                        for information
+                        about this namespace.
+                    </p>
+                    <p>
+                        Note that local names in this namespace are intended to be
+                        defined only by the World Wide Web Consortium or its subgroups.
+                        The names currently defined in this namespace are listed below.
+                        They should not be used with conflicting semantics by any Working
+                        Group, specification, or document instance.
+                    </p>
+                    <p>
+                        See further below in this document for more information about
+                        <a
+                                href="#usage">how to refer to this schema document from your own
+                            XSD schema documents
+                        </a>
+                        and about<a href="#nsversioning">the
+                        namespace-versioning policy governing this schema document</a>.
+                    </p>
+                </div>
+            </div>
+        </xs:documentation>
+    </xs:annotation>
+
+    <xs:attribute name="lang">
+        <xs:annotation>
+            <xs:documentation>
+                <div>
+
+                    <h3>lang (as an attribute name)</h3>
+                    <p>
+                        denotes an attribute whose value
+                        is a language code for the natural language of the content of
+                        any element; its value is inherited. This name is reserved
+                        by virtue of its definition in the XML specification.
+                    </p>
+
+                </div>
+                <div>
+                    <h4>Notes</h4>
+                    <p>
+                        Attempting to install the relevant ISO 2- and 3-letter
+                        codes as the enumerated possible values is probably never
+                        going to be a realistic possibility.
+                    </p>
+                    <p>
+                        See BCP 47 at
+                        <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">
+                            http://www.rfc-editor.org/rfc/bcp/bcp47.txt
+                        </a>
+                        and the IANA language subtag registry at
+                        <a href="http://www.iana.org/assignments/language-subtag-registry">
+                            http://www.iana.org/assignments/language-subtag-registry
+                        </a>
+                        for further information.
+                    </p>
+                    <p>
+                        The union allows for the 'un-declaration' of xml:lang with
+                        the empty string.
+                    </p>
+                </div>
+            </xs:documentation>
+        </xs:annotation>
+        <xs:simpleType>
+            <xs:union memberTypes="xs:language">
+                <xs:simpleType>
+                    <xs:restriction base="xs:string">
+                        <xs:enumeration value=""/>
+                    </xs:restriction>
+                </xs:simpleType>
+            </xs:union>
+        </xs:simpleType>
+    </xs:attribute>
+
+    <xs:attribute name="space">
+        <xs:annotation>
+            <xs:documentation>
+                <div>
+
+                    <h3>space (as an attribute name)</h3>
+                    <p>
+                        denotes an attribute whose
+                        value is a keyword indicating what whitespace processing
+                        discipline is intended for the content of the element; its
+                        value is inherited. This name is reserved by virtue of its
+                        definition in the XML specification.
+                    </p>
+
+                </div>
+            </xs:documentation>
+        </xs:annotation>
+        <xs:simpleType>
+            <xs:restriction base="xs:NCName">
+                <xs:enumeration value="default"/>
+                <xs:enumeration value="preserve"/>
+            </xs:restriction>
+        </xs:simpleType>
+    </xs:attribute>
+
+    <xs:attribute name="base" type="xs:anyURI">
+        <xs:annotation>
+            <xs:documentation>
+                <div>
+
+                    <h3>base (as an attribute name)</h3>
+                    <p>
+                        denotes an attribute whose value
+                        provides a URI to be used as the base for interpreting any
+                        relative URIs in the scope of the element on which it
+                        appears; its value is inherited. This name is reserved
+                        by virtue of its definition in the XML Base specification.
+                    </p>
+
+                    <p>
+                        See
+                        <a
+                                href="http://www.w3.org/TR/xmlbase/">http://www.w3.org/TR/xmlbase/
+                        </a>
+                        for information about this attribute.
+                    </p>
+                </div>
+            </xs:documentation>
+        </xs:annotation>
+    </xs:attribute>
+
+    <xs:attribute name="id" type="xs:ID">
+        <xs:annotation>
+            <xs:documentation>
+                <div>
+
+                    <h3>id (as an attribute name)</h3>
+                    <p>
+                        denotes an attribute whose value
+                        should be interpreted as if declared to be of type ID.
+                        This name is reserved by virtue of its definition in the
+                        xml:id specification.
+                    </p>
+
+                    <p>
+                        See
+                        <a
+                                href="http://www.w3.org/TR/xml-id/">http://www.w3.org/TR/xml-id/
+                        </a>
+                        for information about this attribute.
+                    </p>
+                </div>
+            </xs:documentation>
+        </xs:annotation>
+    </xs:attribute>
+
+    <xs:attributeGroup name="specialAttrs">
+        <xs:attribute ref="xml:base"/>
+        <xs:attribute ref="xml:lang"/>
+        <xs:attribute ref="xml:space"/>
+        <xs:attribute ref="xml:id"/>
+    </xs:attributeGroup>
+
+    <xs:annotation>
+        <xs:documentation>
+            <div>
+
+                <h3>Father (in any context at all)</h3>
+
+                <div class="bodytext">
+                    <p>
+                        denotes Jon Bosak, the chair of
+                        the original XML Working Group. This name is reserved by
+                        the following decision of the W3C XML Plenary and
+                        XML Coordination groups:
+                    </p>
+                    <blockquote>
+                        <p>
+                            In appreciation for his vision, leadership and
+                            dedication the W3C XML Plenary on this 10th day of
+                            February, 2000, reserves for Jon Bosak in perpetuity
+                            the XML name "xml:Father".
+                        </p>
+                    </blockquote>
+                </div>
+            </div>
+        </xs:documentation>
+    </xs:annotation>
+
+    <xs:annotation>
+        <xs:documentation>
+            <div xml:id="usage" id="usage">
+                <h2>
+                    <a name="usage">About this schema document</a>
+                </h2>
+
+                <div class="bodytext">
+                    <p>
+                        This schema defines attributes and an attribute group suitable
+                        for use by schemas wishing to allow<code>xml:base</code>,
+                        <code>xml:lang</code>,
+                        <code>xml:space</code>
+                        or
+                        <code>xml:id</code>
+                        attributes on elements they define.
+                    </p>
+                    <p>
+                        To enable this, such a schema must import this schema for
+                        the XML namespace, e.g. as follows:
+                    </p>
+                    <pre>
+                        &lt;schema . . .>
+                        . . .
+                        &lt;import namespace="http://www.w3.org/XML/1998/namespace"
+                        schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+                    </pre>
+                    <p>
+                        or
+                    </p>
+                    <pre>
+                        &lt;import namespace="http://www.w3.org/XML/1998/namespace"
+                        schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
+                    </pre>
+                    <p>
+                        Subsequently, qualified reference to any of the attributes or the
+                        group defined below will have the desired effect, e.g.
+                    </p>
+                    <pre>
+                        &lt;type . . .>
+                        . . .
+                        &lt;attributeGroup ref="xml:specialAttrs"/>
+                    </pre>
+                    <p>
+                        will define a type which will schema-validate an instance element
+                        with any of those attributes.
+                    </p>
+                </div>
+            </div>
+        </xs:documentation>
+    </xs:annotation>
+
+    <xs:annotation>
+        <xs:documentation>
+            <div id="nsversioning" xml:id="nsversioning">
+                <h2>
+                    <a name="nsversioning">Versioning policy for this schema document</a>
+                </h2>
+                <div class="bodytext">
+                    <p>
+                        In keeping with the XML Schema WG's standard versioning
+                        policy, this schema document will persist at
+                        <a href="http://www.w3.org/2009/01/xml.xsd">
+                            http://www.w3.org/2009/01/xml.xsd</a>.
+                    </p>
+                    <p>
+                        At the date of issue it can also be found at
+                        <a href="http://www.w3.org/2001/xml.xsd">
+                            http://www.w3.org/2001/xml.xsd</a>.
+                    </p>
+                    <p>
+                        The schema document at that URI may however change in the future,
+                        in order to remain compatible with the latest version of XML
+                        Schema itself, or with the XML namespace itself. In other words,
+                        if the XML Schema or XML namespaces change, the version of this
+                        document at
+                        <a href="http://www.w3.org/2001/xml.xsd">
+                            http://www.w3.org/2001/xml.xsd
+                        </a>
+                        will change accordingly; the version at
+                        <a href="http://www.w3.org/2009/01/xml.xsd">
+                            http://www.w3.org/2009/01/xml.xsd
+                        </a>
+                        will not change.
+                    </p>
+                    <p>
+                        Previous dated (and unchanging) versions of this schema
+                        document are at:
+                    </p>
+                    <ul>
+                        <li>
+                            <a href="http://www.w3.org/2009/01/xml.xsd">
+                                http://www.w3.org/2009/01/xml.xsd
+                            </a>
+                        </li>
+                        <li>
+                            <a href="http://www.w3.org/2007/08/xml.xsd">
+                                http://www.w3.org/2007/08/xml.xsd
+                            </a>
+                        </li>
+                        <li>
+                            <a href="http://www.w3.org/2004/10/xml.xsd">
+                                http://www.w3.org/2004/10/xml.xsd
+                            </a>
+                        </li>
+                        <li>
+                            <a href="http://www.w3.org/2001/03/xml.xsd">
+                                http://www.w3.org/2001/03/xml.xsd
+                            </a>
+                        </li>
+                    </ul>
+                </div>
+            </div>
+        </xs:documentation>
+    </xs:annotation>
+
+</xs:schema>
+
diff --git a/opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/test/XmlFileLoader.java b/opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/test/XmlFileLoader.java
new file mode 100644 (file)
index 0000000..8b60719
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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.util.test;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Preconditions;
+import com.google.common.io.CharStreams;
+import com.google.common.io.InputSupplier;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+public class XmlFileLoader {
+
+    public static NetconfMessage xmlFileToNetconfMessage(final String fileName) throws IOException, SAXException,
+            ParserConfigurationException {
+        return new NetconfMessage(xmlFileToDocument(fileName));
+    }
+
+    public static Element xmlFileToElement(final String fileName) throws IOException, SAXException,
+            ParserConfigurationException {
+        return xmlFileToDocument(fileName).getDocumentElement();
+    }
+
+    public static String xmlFileToString(final String fileName) throws IOException, SAXException,
+            ParserConfigurationException {
+        return XmlUtil.toString(xmlFileToDocument(fileName));
+    }
+
+    public static Document xmlFileToDocument(final String fileName) throws IOException, SAXException,
+            ParserConfigurationException {
+        try (InputStream resourceAsStream = XmlFileLoader.class.getClassLoader().getResourceAsStream(fileName)) {
+            Preconditions.checkNotNull(resourceAsStream);
+            final Document doc = XmlUtil.readXmlToDocument(resourceAsStream);
+            return doc;
+        }
+    }
+
+    public static String fileToString(final String fileName) throws IOException {
+        try (InputStream resourceAsStream = XmlFileLoader.class.getClassLoader().getResourceAsStream(fileName)) {
+            Preconditions.checkNotNull(resourceAsStream);
+
+            InputSupplier<? extends InputStream> supplier = new InputSupplier<InputStream>() {
+                @Override
+                public InputStream getInput() throws IOException {
+                    return resourceAsStream;
+                }
+            };
+
+            InputSupplier<InputStreamReader> readerSupplier = CharStreams.newReaderSupplier(supplier, Charsets.UTF_8);
+
+            return CharStreams.toString(readerSupplier);
+        }
+    }
+
+    public static InputStream getResourceAsStream(final String fileName) {
+        return XmlFileLoader.class.getClassLoader().getResourceAsStream(fileName);
+    }
+}
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello.xml
new file mode 100644 (file)
index 0000000..4e34591
--- /dev/null
@@ -0,0 +1,5 @@
+<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <capabilities>
+        <capability>urn:ietf:params:netconf:base:1.0</capability>
+    </capabilities>
+</hello>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello_with_auth.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello_with_auth.xml
new file mode 100644 (file)
index 0000000..174640c
--- /dev/null
@@ -0,0 +1,8 @@
+[tomas;10.0.0.0/10000;tcp;1000;1000;;/home/tomas;;]
+        <?xml version="1.0" encoding="UTF-8"?>
+<hello
+        xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <capabilities>
+        <capability>urn:ietf:params:netconf:base:1.0</capability>
+    </capabilities>
+</hello>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/close-session.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/close-session.xml
new file mode 100644 (file)
index 0000000..32d6446
--- /dev/null
@@ -0,0 +1,3 @@
+<rpc message-id="103" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <close-session/>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/closeSession.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/closeSession.xml
new file mode 100644 (file)
index 0000000..2b0c0d0
--- /dev/null
@@ -0,0 +1,3 @@
+<rpc message-id="1" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <close-session xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/commit.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/commit.xml
new file mode 100644 (file)
index 0000000..ffdf132
--- /dev/null
@@ -0,0 +1,3 @@
+<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101">
+    <commit></commit>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testClientSendsRpcReply_expectedResponse.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testClientSendsRpcReply_expectedResponse.xml
new file mode 100644 (file)
index 0000000..4d85d33
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <rpc-error>
+        <error-type>protocol</error-type>
+        <error-tag>unknown-element</error-tag>
+        <error-severity>error</error-severity>
+        <error-info>
+            <bad-element>rpc-reply</bad-element>
+        </error-info>
+    </rpc-error>
+</rpc-reply>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testClientSendsRpcReply_request.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testClientSendsRpcReply_request.xml
new file mode 100644 (file)
index 0000000..72e7780
--- /dev/null
@@ -0,0 +1,4 @@
+<rpc-reply message-id="*** replaced by message id ***"
+           xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <ok/>
+</rpc-reply>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testRpcWithoutMessageId_expectedResponse.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testRpcWithoutMessageId_expectedResponse.xml
new file mode 100644 (file)
index 0000000..c28b93d
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <rpc-error>
+        <error-type>rpc</error-type>
+        <error-tag>missing-attribute</error-tag>
+        <error-severity>error</error-severity>
+        <error-info>
+            <bad-attribute>message-id</bad-attribute>
+            <bad-element>rpc</bad-element>
+        </error-info>
+    </rpc-error>
+</rpc-reply>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testRpcWithoutMessageId_request.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testRpcWithoutMessageId_request.xml
new file mode 100644 (file)
index 0000000..419d006
--- /dev/null
@@ -0,0 +1,4 @@
+<rpc
+        xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <get/>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/databaseInteraction/client_get_request_ConfigRegistry.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/databaseInteraction/client_get_request_ConfigRegistry.xml
new file mode 100644 (file)
index 0000000..dfed1a3
--- /dev/null
@@ -0,0 +1,8 @@
+<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <get-config>
+        <source>
+            <running/>
+        </source>
+        <filter type="xpath" select="/top/jmxbean[objectName='org.opendaylight.controller:type=ConfigRegistry']"/>
+    </get-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/databaseInteraction/confg_subsystem_expected_reply.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/databaseInteraction/confg_subsystem_expected_reply.xml
new file mode 100644 (file)
index 0000000..ef8696d
--- /dev/null
@@ -0,0 +1,29 @@
+<rpc-reply message-id="101"
+           xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <data>
+        <jmxbean
+                objectName="org.opendaylight.controller:instanceName=fixed1,interfaceName=testing-threadpool,type=ConfigBean">
+            <ExportedInterfaces>
+                <Entries>
+                    <Entry>testing-threadpool</Entry>
+                    <Entry>modifiable-threadpool</Entry>
+                </Entries>
+            </ExportedInterfaces>
+            <ImplementationName>fixed</ImplementationName>
+            <ThreadCount>10</ThreadCount>
+            <TriggerNewInstanceCreation>false</TriggerNewInstanceCreation>
+        </jmxbean>
+        <jmxbean
+                objectName="org.opendaylight.controller:instanceName=fixed1,interfaceName=modifiable-threadpool,type=ConfigBean">
+            <ExportedInterfaces>
+                <Entries>
+                    <Entry>testing-threadpool</Entry>
+                    <Entry>modifiable-threadpool</Entry>
+                </Entries>
+            </ExportedInterfaces>
+            <ImplementationName>fixed</ImplementationName>
+            <ThreadCount>10</ThreadCount>
+            <TriggerNewInstanceCreation>false</TriggerNewInstanceCreation>
+        </jmxbean>
+    </data>
+</rpc-reply>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/discardChanges.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/discardChanges.xml
new file mode 100644 (file)
index 0000000..738a665
--- /dev/null
@@ -0,0 +1,3 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <discard-changes></discard-changes>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig.xml
new file mode 100644 (file)
index 0000000..904c0a6
--- /dev/null
@@ -0,0 +1,117 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <test-option>
+            set
+        </test-option>
+        <default-operation>merge</default-operation>
+        <config>
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+                <module>
+                    <name>dep</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+                <module>
+                    <name>dep2</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+                <module>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-netconf
+                    </type>
+
+                    <name>test1</name>
+
+                    <simple-long-2>44</simple-long-2>
+                    <binaryLeaf>8</binaryLeaf>
+                    <binaryLeaf>1</binaryLeaf>
+                    <binaryLeaf>0</binaryLeaf>
+                    <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">configAttributeType</type>
+                    <dto_d xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        <simple-int1>444</simple-int1>
+                        <simple-int2>4444</simple-int2>
+                        <simple-int3>454</simple-int3>
+                        <complex-dto-bInner>
+                            <simple-int3>44</simple-int3>
+                            <deep>
+                                <simple-int3>4</simple-int3>
+                            </deep>
+                            <simple-list>4</simple-list>
+                        </complex-dto-bInner>
+                        <simple-list>4</simple-list>
+                    </dto_d>
+                    <simpleInt>44</simpleInt>
+                    <simple-test>545</simple-test>
+                    <simple-long>454545</simple-long>
+                    <simpleBoolean>false</simpleBoolean>
+                    <dto-c>
+                        <dto-a-inner>
+                            <dto-a-inner-inner>
+                                <simple-arg>456</simple-arg>
+                            </dto-a-inner-inner>
+                            <simple-arg>44</simple-arg>
+                        </dto-a-inner>
+                    </dto-c>
+                    <simple-short>4</simple-short>
+                    <simple-BigInteger>999</simple-BigInteger>
+                    <simple-byte>4</simple-byte>
+                    <peers>
+                        <port>port1</port>
+                        <simple-int3>456</simple-int3>
+                        <core-size>44</core-size>
+                    </peers>
+                    <peers>
+                        <port>port23</port>
+                        <simple-int3>456</simple-int3>
+                        <core-size>44</core-size>
+                    </peers>
+                    <testing-dep>
+                        <type>testing</type>
+                        <name>ref_dep</name>
+                    </testing-dep>
+                </module>
+
+                <module>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-netconf
+                    </type>
+                    <name>test2</name>
+                    <testing-dep>
+                        <type>testing</type>
+                        <name>ref_dep</name>
+                    </testing-dep>
+                </module>
+            </modules>
+
+            <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                <service>
+                    <type>testing</type>
+                    <instance>
+                        <name>ref_dep</name>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+                        </provider>
+                    </instance>
+                    <instance>
+                        <name>ref_dep_2</name>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+                        </provider>
+                    </instance>
+                    <instance>
+                        <name>ref_test1</name>
+                        <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+                        </provider>
+                    </instance>
+                </service>
+            </services>
+        </config>
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_expectedResult.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_expectedResult.xml
new file mode 100644 (file)
index 0000000..7f884dc
--- /dev/null
@@ -0,0 +1,19 @@
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" a="64" id="a" message-id="101" xmlnx="a:b:c:d">
+    <data>
+        <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+            <!--<module>
+            <type>impl</type>
+            </module>
+            -->
+            <!--<module>
+            <type>impl-dep</type>
+            </module>
+            -->
+            <!--<module>
+            <type>impl-netconf</type>
+            </module>
+            -->
+        </modules>
+        <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config"/>
+    </data>
+</rpc-reply>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_merge_threadfactory.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_merge_threadfactory.xml
new file mode 100644 (file)
index 0000000..ad7c84f
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rpc message-id="6"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+        <target>
+            <candidate/>
+        </target>
+        <default-operation>merge</default-operation>
+        <test-option>set</test-option>
+        <config>
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                <module
+                        xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+                        nc:operation="merge">
+                    <name>threadfactory-naming-instance</name>
+                    <type
+                            xmlns:th-java="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+                        th-java:threadfactory-naming
+                    </type>
+                    <name-prefix xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+                        prefixDefinedInXML
+                    </name-prefix>
+                </module>
+            </modules>
+        </config>
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_merge_yang-test.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_merge_yang-test.xml
new file mode 100644 (file)
index 0000000..5711a68
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rpc message-id="6"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+        <target>
+            <candidate/>
+        </target>
+        <default-operation>merge</default-operation>
+        <test-option>set</test-option>
+        <config>
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                <module
+                        xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+                        nc:operation="merge">
+                    <name>impl-dep-instance</name>
+                    <type
+                            xmlns:th-java="urn:opendaylight:params:xml:ns:yang:controller:test:impl">th-java:impl-dep
+                    </type>
+                </module>
+            </modules>
+        </config>
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_none.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_none.xml
new file mode 100644 (file)
index 0000000..42021c5
--- /dev/null
@@ -0,0 +1,109 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <default-operation>none</default-operation>
+        <error-option>stop-on-error</error-option>
+        <config>
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                <module>
+                    <name>dep</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+                <module>
+                    <name>dep2</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+                <module>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-netconf
+                    </type>
+                    <name>test1</name>
+                    <simple-long-2>44</simple-long-2>
+                    <binaryLeaf>8</binaryLeaf>
+                    <binaryLeaf>7</binaryLeaf>
+                    <binaryLeaf>9</binaryLeaf>
+                    <dto_d>
+                        <simple-int1>444</simple-int1>
+                        <simple-int2>4444</simple-int2>
+                        <simple-int3>454</simple-int3>
+                        <complex-dto-bInner>
+                            <simple-int3>44</simple-int3>
+                            <deep>
+                                <simple-int3>4</simple-int3>
+                            </deep>
+                            <simple-list>4</simple-list>
+                        </complex-dto-bInner>
+                        <simple-list>4</simple-list>
+                    </dto_d>
+                    <simpleInt>44</simpleInt>
+                    <simple-test>545</simple-test>
+                    <simple-long>454545</simple-long>
+                    <simpleBoolean>false</simpleBoolean>
+                    <dto-c>
+                        <dto-a-inner>
+                            <dto-a-inner-inner>
+                                <simple-arg>456</simple-arg>
+                            </dto-a-inner-inner>
+                            <simple-arg>44</simple-arg>
+                        </dto-a-inner>
+                    </dto-c>
+                    <simple-short>4</simple-short>
+                    <simple-BigInteger>999</simple-BigInteger>
+                    <simple-byte>4</simple-byte>
+                    <peers>
+                        <port>port1</port>
+                        <simple-int3>456</simple-int3>
+                        <core-size>44</core-size>
+                    </peers>
+                    <peers>
+                        <port>port23</port>
+                        <simple-int3>456</simple-int3>
+                        <core-size>44</core-size>
+                    </peers>
+                    <testing-dep>
+                        <type>testing</type>
+                        <name>ref_dep</name>
+                    </testing-dep>
+                </module>
+                <module>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-netconf
+                    </type>
+                    <name>test2</name>
+                    <testing-dep>
+                        <type>testing</type>
+                        <name>ref_dep</name>
+                    </testing-dep>
+                </module>
+            </modules>
+            <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                <service>
+                    <type>testing</type>
+                    <instance>
+                        <name>ref_dep</name>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+                        </provider>
+                    </instance>
+                    <instance>
+                        <name>ref_dep_2</name>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+                        </provider>
+                    </instance>
+                    <instance>
+                        <name>ref_test1</name>
+                        <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+                        </provider>
+                    </instance>
+                </service>
+            </services>
+        </config>
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_remove.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_remove.xml
new file mode 100644 (file)
index 0000000..9d06d98
--- /dev/null
@@ -0,0 +1,43 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <default-operation>none</default-operation>
+        <config>
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                <module xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+                        nc:operation="remove">
+                    <name>dep</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+                <module xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+                        nc:operation="remove">
+                    <name>dep2</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+                <module xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+                        nc:operation="remove">
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-netconf
+                    </type>
+                    <name>test1</name>
+                </module>
+
+                <module xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+                        nc:operation="remove">
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-netconf
+                    </type>
+                    <name>test2</name>
+                </module>
+            </modules>
+        </config>
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_default.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_default.xml
new file mode 100644 (file)
index 0000000..3c06b85
--- /dev/null
@@ -0,0 +1,27 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <default-operation>replace</default-operation>
+        <config>
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+                <module>
+                    <name>dep</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+                <module>
+                    <name>dep2</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+            </modules>
+            <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config"/>
+        </config>
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_default_ex.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_default_ex.xml
new file mode 100644 (file)
index 0000000..91183cb
--- /dev/null
@@ -0,0 +1,21 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <default-operation>replace</default-operation>
+        <config xmlns="top:level:namespace">
+            <modules>
+                <module xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl" operation="remove">
+                    <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">impl-dep</type>
+                    <name>dep</name>
+                </module>
+                <module xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl" operation="remove">
+                    <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">impl-dep</type>
+                    <name>dep2</name>
+                </module>
+            </modules>
+            <services/>
+        </config>
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_module.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_module.xml
new file mode 100644 (file)
index 0000000..ed3ff6a
--- /dev/null
@@ -0,0 +1,16 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <config xmlns="top:level:namespace">
+            <modules>
+                <module xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl"
+                        operation="replace">
+                    <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">impl-dep</type>
+                    <name>dep</name>
+                </module>
+            </modules>
+        </config>
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_module_ex.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_module_ex.xml
new file mode 100644 (file)
index 0000000..7c7679c
--- /dev/null
@@ -0,0 +1,16 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <default-operation>replace</default-operation>
+        <config xmlns="top:level:namespace">
+            <modules operation="replace">
+                <module xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl" operation="merge">
+                    <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">impl-dep</type>
+                    <name>dep</name>
+                </module>
+            </modules>
+        </config>
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/edit_config.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/edit_config.xml
new file mode 100644 (file)
index 0000000..05866a6
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rpc message-id="1"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+        <target>
+            <candidate/>
+        </target>
+        <config/>
+    </edit-config>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/get.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/get.xml
new file mode 100644 (file)
index 0000000..7daadbd
--- /dev/null
@@ -0,0 +1,3 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <get/>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/getConfig.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/getConfig.xml
new file mode 100644 (file)
index 0000000..39efb49
--- /dev/null
@@ -0,0 +1,7 @@
+<rpc id="a" a="64" xmlnx="a:b:c:d" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101">
+    <get-config>
+        <source>
+            <running/>
+        </source>
+    </get-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/getConfig_candidate.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/getConfig_candidate.xml
new file mode 100644 (file)
index 0000000..5e65bfd
--- /dev/null
@@ -0,0 +1,7 @@
+<rpc id="a" a="64" xmlnx="a:b:c:d" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101">
+    <get-config>
+        <source>
+            <candidate/>
+        </source>
+    </get-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/get_schema-no-version.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/get_schema-no-version.xml
new file mode 100644 (file)
index 0000000..fa59e59
--- /dev/null
@@ -0,0 +1,6 @@
+<rpc message-id="2"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <get-schema xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">
+        <identifier>threadpool-api</identifier>
+    </get-schema>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/get_schema.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/get_schema.xml
new file mode 100644 (file)
index 0000000..79f046e
--- /dev/null
@@ -0,0 +1,10 @@
+<rpc message-id="2"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <get-schema xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">
+        <identifier>threadpool-api</identifier>
+        <version>2010-09-24</version>
+        <format
+                xmlns:ncm="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">ncm:yang
+        </format>
+    </get-schema>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/handshake/client_hello_with_session_id.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/handshake/client_hello_with_session_id.xml
new file mode 100644 (file)
index 0000000..e950e44
--- /dev/null
@@ -0,0 +1,6 @@
+<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <capabilities>
+        <capability>urn:ietf:params:netconf:base:1.0</capability>
+    </capabilities>
+    <session-id>666</session-id>
+</hello>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/handshake/client_hello_with_wrong_namespace.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/handshake/client_hello_with_wrong_namespace.xml
new file mode 100644 (file)
index 0000000..3f32f60
--- /dev/null
@@ -0,0 +1,5 @@
+<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.1">
+    <capabilities>
+        <capability>urn:ietf:params:netconf:base:1.0</capability>
+    </capabilities>
+</hello>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/editConfig_merge_threadfactory.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/editConfig_merge_threadfactory.xml
new file mode 100644 (file)
index 0000000..e1e0237
--- /dev/null
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rpc message-id="6"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+        <target>
+            <candidate/>
+        </target>
+        <default-operation>merge</default-operation>
+        <config>
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                <module
+                        xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+                        nc:operation="merge">
+                    <name>threadfactory-naming-instance</name>
+                    <type
+                            xmlns:th-java="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+                        th-java:threadfactory-naming
+                    </type>
+                    <name-prefix xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+                        prefixDefinedInXML
+                    </name-prefix>
+                </module>
+            </modules>
+
+            <mountpoints>
+                <mountpoint>
+                    <id>localhost:12002</id>
+                    <config>
+                        <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                            <module
+                                    xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+                                    nc:operation="merge">
+                                <name>threadfactory-naming-instance</name>
+                                <type
+                                        xmlns:th-java="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+                                    th-java:threadfactory-naming
+                                </type>
+                                <name-prefix xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+                                    prefixDefinedInXML
+                                </name-prefix>
+                            </module>
+                        </modules>
+                    </config>
+                </mountpoint>
+                <mountpoint>
+                    <id>localhost:12003</id>
+                    <config>
+                        <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                            <module
+                                    xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+                                    nc:operation="replace">
+                                <name>threadfactory-naming-instance</name>
+                                <type
+                                        xmlns:th-java="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+                                    th-java:threadfactory-naming
+                                </type>
+                                <name-prefix xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+                                    prefixDefinedInXML
+                                </name-prefix>
+                            </module>
+                        </modules>
+                    </config>
+                </mountpoint>
+            </mountpoints>
+        </config>
+
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/mount12002.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/mount12002.xml
new file mode 100644 (file)
index 0000000..f524de3
--- /dev/null
@@ -0,0 +1,7 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <mount>
+        <mountpoint-id>
+            localhost:12002
+        </mountpoint-id>
+    </mount>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/mount12003.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/mount12003.xml
new file mode 100644 (file)
index 0000000..64ee057
--- /dev/null
@@ -0,0 +1,7 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <mount>
+        <mountpoint-id>
+            localhost:12003
+        </mountpoint-id>
+    </mount>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/unmount12002.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/unmount12002.xml
new file mode 100644 (file)
index 0000000..c82db72
--- /dev/null
@@ -0,0 +1,7 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <unmount>
+        <mountpoint-id>
+            localhost:12002
+        </mountpoint-id>
+    </unmount>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_differentNamespaceTO.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_differentNamespaceTO.xml
new file mode 100644 (file)
index 0000000..efa4690
--- /dev/null
@@ -0,0 +1,117 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <test-option>
+            set
+        </test-option>
+        <default-operation>merge</default-operation>
+        <config>
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+                <module>
+                    <name>dep</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+                <module>
+                    <name>dep2</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+                <module>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-netconf
+                    </type>
+
+                    <name>test1</name>
+
+                    <simple-long-2>44</simple-long-2>
+                    <binaryLeaf>8</binaryLeaf>
+                    <binaryLeaf>1</binaryLeaf>
+                    <binaryLeaf>0</binaryLeaf>
+                    <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">configAttributeType</type>
+                    <dto_d>
+                        <simple-int1>444</simple-int1>
+                        <simple-int2 xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">4444</simple-int2>
+                        <simple-int3>454</simple-int3>
+                        <complex-dto-bInner>
+                            <simple-int3>44</simple-int3>
+                            <deep>
+                                <simple-int3>4</simple-int3>
+                            </deep>
+                            <simple-list>4</simple-list>
+                        </complex-dto-bInner>
+                        <simple-list>4</simple-list>
+                    </dto_d>
+                    <simpleInt>44</simpleInt>
+                    <simple-test>545</simple-test>
+                    <simple-long>454545</simple-long>
+                    <simpleBoolean>false</simpleBoolean>
+                    <dto-c>
+                        <dto-a-inner>
+                            <dto-a-inner-inner>
+                                <simple-arg>456</simple-arg>
+                            </dto-a-inner-inner>
+                            <simple-arg>44</simple-arg>
+                        </dto-a-inner>
+                    </dto-c>
+                    <simple-short>4</simple-short>
+                    <simple-BigInteger>999</simple-BigInteger>
+                    <simple-byte>4</simple-byte>
+                    <peers>
+                        <port>port1</port>
+                        <simple-int3>456</simple-int3>
+                        <core-size>44</core-size>
+                    </peers>
+                    <peers>
+                        <port>port23</port>
+                        <simple-int3>456</simple-int3>
+                        <core-size>44</core-size>
+                    </peers>
+                    <testing-dep>
+                        <type>testing</type>
+                        <name>ref_dep</name>
+                    </testing-dep>
+                </module>
+
+                <module>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-netconf
+                    </type>
+                    <name>test2</name>
+                    <testing-dep>
+                        <type>testing</type>
+                        <name>ref_dep</name>
+                    </testing-dep>
+                </module>
+            </modules>
+
+            <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                <service>
+                    <type>testing</type>
+                    <instance>
+                        <name>ref_dep</name>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+                        </provider>
+                    </instance>
+                    <instance>
+                        <name>ref_dep_2</name>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+                        </provider>
+                    </instance>
+                    <instance>
+                        <name>ref_test1</name>
+                        <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+                        </provider>
+                    </instance>
+                </service>
+            </services>
+        </config>
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespaces.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespaces.xml
new file mode 100644 (file)
index 0000000..3dbb297
--- /dev/null
@@ -0,0 +1,118 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <test-option>
+            set
+        </test-option>
+        <default-operation>merge</default-operation>
+        <config>
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+                <module>
+                    <name>dep</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+                <module>
+                    <name>dep2</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+                <module>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-netconf
+                    </type>
+
+                    <name>test1</name>
+
+                    <simple-long-2>44</simple-long-2>
+                    <simple-long-2 xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">44</simple-long-2>
+                    <binaryLeaf>8</binaryLeaf>
+                    <binaryLeaf>1</binaryLeaf>
+                    <binaryLeaf>0</binaryLeaf>
+                    <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">configAttributeType</type>
+                    <dto_d xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        <simple-int1>444</simple-int1>
+                        <simple-int2>4444</simple-int2>
+                        <simple-int3>454</simple-int3>
+                        <complex-dto-bInner>
+                            <simple-int3>44</simple-int3>
+                            <deep>
+                                <simple-int3>4</simple-int3>
+                            </deep>
+                            <simple-list>4</simple-list>
+                        </complex-dto-bInner>
+                        <simple-list>4</simple-list>
+                    </dto_d>
+                    <simpleInt>44</simpleInt>
+                    <simple-test>545</simple-test>
+                    <simple-long>454545</simple-long>
+                    <simpleBoolean>false</simpleBoolean>
+                    <dto-c>
+                        <dto-a-inner>
+                            <dto-a-inner-inner>
+                                <simple-arg>456</simple-arg>
+                            </dto-a-inner-inner>
+                            <simple-arg>44</simple-arg>
+                        </dto-a-inner>
+                    </dto-c>
+                    <simple-short>4</simple-short>
+                    <simple-BigInteger>999</simple-BigInteger>
+                    <simple-byte>4</simple-byte>
+                    <peers>
+                        <port>port1</port>
+                        <simple-int3>456</simple-int3>
+                        <core-size>44</core-size>
+                    </peers>
+                    <peers>
+                        <port>port23</port>
+                        <simple-int3>456</simple-int3>
+                        <core-size>44</core-size>
+                    </peers>
+                    <testing-dep>
+                        <type>testing</type>
+                        <name>ref_dep</name>
+                    </testing-dep>
+                </module>
+
+                <module>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-netconf
+                    </type>
+                    <name>test2</name>
+                    <testing-dep>
+                        <type>testing</type>
+                        <name>ref_dep</name>
+                    </testing-dep>
+                </module>
+            </modules>
+
+            <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                <service>
+                    <type>testing</type>
+                    <instance>
+                        <name>ref_dep</name>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+                        </provider>
+                    </instance>
+                    <instance>
+                        <name>ref_dep_2</name>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+                        </provider>
+                    </instance>
+                    <instance>
+                        <name>ref_test1</name>
+                        <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+                        </provider>
+                    </instance>
+                </service>
+            </services>
+        </config>
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespacesList.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespacesList.xml
new file mode 100644 (file)
index 0000000..504ccff
--- /dev/null
@@ -0,0 +1,117 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <test-option>
+            set
+        </test-option>
+        <default-operation>merge</default-operation>
+        <config>
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+                <module>
+                    <name>dep</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+                <module>
+                    <name>dep2</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+                <module>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-netconf
+                    </type>
+
+                    <name>test1</name>
+
+                    <simple-long-2>44</simple-long-2>
+                    <binaryLeaf>8</binaryLeaf>
+                    <binaryLeaf xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">1</binaryLeaf>
+                    <binaryLeaf>0</binaryLeaf>
+                    <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">configAttributeType</type>
+                    <dto_d xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        <simple-int1>444</simple-int1>
+                        <simple-int2>4444</simple-int2>
+                        <simple-int3>454</simple-int3>
+                        <complex-dto-bInner>
+                            <simple-int3>44</simple-int3>
+                            <deep>
+                                <simple-int3>4</simple-int3>
+                            </deep>
+                            <simple-list>4</simple-list>
+                        </complex-dto-bInner>
+                        <simple-list>4</simple-list>
+                    </dto_d>
+                    <simpleInt>44</simpleInt>
+                    <simple-test>545</simple-test>
+                    <simple-long>454545</simple-long>
+                    <simpleBoolean>false</simpleBoolean>
+                    <dto-c>
+                        <dto-a-inner>
+                            <dto-a-inner-inner>
+                                <simple-arg>456</simple-arg>
+                            </dto-a-inner-inner>
+                            <simple-arg>44</simple-arg>
+                        </dto-a-inner>
+                    </dto-c>
+                    <simple-short>4</simple-short>
+                    <simple-BigInteger>999</simple-BigInteger>
+                    <simple-byte>4</simple-byte>
+                    <peers>
+                        <port>port1</port>
+                        <simple-int3>456</simple-int3>
+                        <core-size>44</core-size>
+                    </peers>
+                    <peers>
+                        <port>port23</port>
+                        <simple-int3>456</simple-int3>
+                        <core-size>44</core-size>
+                    </peers>
+                    <testing-dep>
+                        <type>testing</type>
+                        <name>ref_dep</name>
+                    </testing-dep>
+                </module>
+
+                <module>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-netconf
+                    </type>
+                    <name>test2</name>
+                    <testing-dep>
+                        <type>testing</type>
+                        <name>ref_dep</name>
+                    </testing-dep>
+                </module>
+            </modules>
+
+            <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                <service>
+                    <type>testing</type>
+                    <instance>
+                        <name>ref_dep</name>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+                        </provider>
+                    </instance>
+                    <instance>
+                        <name>ref_dep_2</name>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+                        </provider>
+                    </instance>
+                    <instance>
+                        <name>ref_test1</name>
+                        <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+                        </provider>
+                    </instance>
+                </service>
+            </services>
+        </config>
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_typeNameConfigAttributeMatching.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_typeNameConfigAttributeMatching.xml
new file mode 100644 (file)
index 0000000..8398fdb
--- /dev/null
@@ -0,0 +1,116 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <test-option>
+            set
+        </test-option>
+        <default-operation>merge</default-operation>
+        <config>
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+                <module>
+                    <name>dep</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+                <module>
+                    <name>dep2</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+                <module>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-netconf
+                    </type>
+
+                    <name>test1</name>
+
+                    <simple-long-2>44</simple-long-2>
+                    <binaryLeaf>8</binaryLeaf>
+                    <binaryLeaf>1</binaryLeaf>
+                    <binaryLeaf>0</binaryLeaf>
+                    <dto_d xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        <simple-int1>444</simple-int1>
+                        <simple-int2>4444</simple-int2>
+                        <simple-int3>454</simple-int3>
+                        <complex-dto-bInner>
+                            <simple-int3>44</simple-int3>
+                            <deep>
+                                <simple-int3>4</simple-int3>
+                            </deep>
+                            <simple-list>4</simple-list>
+                        </complex-dto-bInner>
+                        <simple-list>4</simple-list>
+                    </dto_d>
+                    <simpleInt>44</simpleInt>
+                    <simple-test>545</simple-test>
+                    <simple-long>454545</simple-long>
+                    <simpleBoolean>false</simpleBoolean>
+                    <dto-c>
+                        <dto-a-inner>
+                            <dto-a-inner-inner>
+                                <simple-arg>456</simple-arg>
+                            </dto-a-inner-inner>
+                            <simple-arg>44</simple-arg>
+                        </dto-a-inner>
+                    </dto-c>
+                    <simple-short>4</simple-short>
+                    <simple-BigInteger>999</simple-BigInteger>
+                    <simple-byte>4</simple-byte>
+                    <peers>
+                        <port>port1</port>
+                        <simple-int3>456</simple-int3>
+                        <core-size>44</core-size>
+                    </peers>
+                    <peers>
+                        <port>port23</port>
+                        <simple-int3>456</simple-int3>
+                        <core-size>44</core-size>
+                    </peers>
+                    <testing-dep>
+                        <type>testing</type>
+                        <name>ref_dep</name>
+                    </testing-dep>
+                </module>
+
+                <module>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-netconf
+                    </type>
+                    <name>test2</name>
+                    <testing-dep>
+                        <type>testing</type>
+                        <name>ref_dep</name>
+                    </testing-dep>
+                </module>
+            </modules>
+
+            <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                <service>
+                    <type>testing</type>
+                    <instance>
+                        <name>ref_dep</name>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+                        </provider>
+                    </instance>
+                    <instance>
+                        <name>ref_dep_2</name>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+                        </provider>
+                    </instance>
+                    <instance>
+                        <name>ref_test1</name>
+                        <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+                        </provider>
+                    </instance>
+                </service>
+            </services>
+        </config>
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpc.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpc.xml
new file mode 100644 (file)
index 0000000..b2a2ee3
--- /dev/null
@@ -0,0 +1,8 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <no-arg xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+        <context-instance>/data/modules/module[name='impl-netconf']/instance[name='instance']</context-instance>
+        <arg1>
+            testarg1
+        </arg1>
+    </no-arg>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpcInner.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpcInner.xml
new file mode 100644 (file)
index 0000000..8bc504a
--- /dev/null
@@ -0,0 +1,7 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <noArgInner xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+        <context-instance>
+            /data/modules/module[name='impl-netconf']/instance[name='instance2']/inner-running-data-additional[key='randomString_1003']
+        </context-instance>
+    </noArgInner>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpcInnerInner.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpcInnerInner.xml
new file mode 100644 (file)
index 0000000..2356398
--- /dev/null
@@ -0,0 +1,16 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <noArgInnerInner
+            xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+        <context-instance>
+            /data/modules/module[name='impl-netconf']/instance[name='instance2']/inner-running-data[key='1015']/inner-inner-running-data[key='1017']
+        </context-instance>
+
+        <arg1>
+            456
+        </arg1>
+        <arg2>
+            true
+        </arg2>
+
+    </noArgInnerInner>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/threadpool-edit-config.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/threadpool-edit-config.xml
new file mode 100644 (file)
index 0000000..40f86e3
--- /dev/null
@@ -0,0 +1,23 @@
+<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <config>
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                <module
+                        xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+                        nc:operation="merge">
+                    <name>threadfactory-naming-instance</name>
+                    <type
+                            xmlns:th-java="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+                        th-java:threadfactory-naming
+                    </type>
+                    <name-prefix xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+                        prefixDefinedInXML
+                    </name-prefix>
+                </module>
+            </modules>
+        </config>
+    </edit-config>
+</rpc>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised1.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised1.xml
new file mode 100644 (file)
index 0000000..6b267c5
--- /dev/null
@@ -0,0 +1,36 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <test-option>
+            set
+        </test-option>
+        <default-operation>merge</default-operation>
+        <config>
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+                <module>
+                    <name>dep</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                    <unknownAttribute>error</unknownAttribute>
+                </module>
+
+
+            </modules>
+
+            <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                <service>
+                    <type>testing</type>
+                    <instance>
+                        <name>ref_dep</name>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+                        </provider>
+                    </instance>
+                </service>
+            </services>
+        </config>
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised2.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised2.xml
new file mode 100644 (file)
index 0000000..8ca7ee7
--- /dev/null
@@ -0,0 +1,37 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <test-option>
+            set
+        </test-option>
+        <default-operation>merge</default-operation>
+        <config>
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+                <module>
+                    <name>dep</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+
+            </modules>
+
+            <unknownAttribute>error</unknownAttribute>
+
+            <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                <service>
+                    <type>testing</type>
+                    <instance>
+                        <name>ref_dep</name>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+                        </provider>
+                    </instance>
+                </service>
+            </services>
+        </config>
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised3.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised3.xml
new file mode 100644 (file)
index 0000000..d3705cc
--- /dev/null
@@ -0,0 +1,38 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <test-option>
+            set
+        </test-option>
+        <default-operation>merge</default-operation>
+        <config>
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+                <module>
+                    <name>dep</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+
+            </modules>
+
+
+            <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                <unknownAttribute>error</unknownAttribute>
+                l
+                <service>
+                    <type>testing</type>
+                    <instance>
+                        <name>ref_dep</name>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+                        </provider>
+                    </instance>
+                </service>
+            </services>
+        </config>
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised4.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised4.xml
new file mode 100644 (file)
index 0000000..a485574
--- /dev/null
@@ -0,0 +1,39 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <test-option>
+            set
+        </test-option>
+        <default-operation>merge</default-operation>
+        <config>
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+                <module>
+                    <name>dep</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+
+            </modules>
+
+
+            <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                l
+                <service>
+                    <type>testing</type>
+                    <unknownAttribute>error</unknownAttribute>
+
+                    <instance>
+                        <name>ref_dep</name>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+                        </provider>
+                    </instance>
+                </service>
+            </services>
+        </config>
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised5.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised5.xml
new file mode 100644 (file)
index 0000000..654a183
--- /dev/null
@@ -0,0 +1,39 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <test-option>
+            set
+        </test-option>
+        <default-operation>merge</default-operation>
+        <config>
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+                <module>
+                    <name>dep</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+
+            </modules>
+
+
+            <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                l
+                <service>
+                    <type>testing</type>
+
+                    <instance>
+                        <name>ref_dep</name>
+                        <unknownAttribute>error</unknownAttribute>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+                        </provider>
+                    </instance>
+                </service>
+            </services>
+        </config>
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised6.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised6.xml
new file mode 100644 (file)
index 0000000..fc43a77
--- /dev/null
@@ -0,0 +1,36 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <test-option>
+            set
+        </test-option>
+        <default-operation>merge</default-operation>
+        <config>
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+                <unknownAttribute>error</unknownAttribute>
+                <module>
+                    <name>dep</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+
+            </modules>
+
+            <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                <service>
+                    <type>testing</type>
+                    <instance>
+                        <name>ref_dep</name>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+                        </provider>
+                    </instance>
+                </service>
+            </services>
+        </config>
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised7.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised7.xml
new file mode 100644 (file)
index 0000000..1bea536
--- /dev/null
@@ -0,0 +1,117 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <test-option>
+            set
+        </test-option>
+        <default-operation>merge</default-operation>
+        <config>
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+                <module>
+                    <name>dep</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+                <module>
+                    <name>dep2</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+                <module>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-netconf
+                    </type>
+
+                    <name>test1</name>
+
+                    <simple-long-2>44</simple-long-2>
+                    <binaryLeaf>8</binaryLeaf>
+                    <binaryLeaf>1</binaryLeaf>
+                    <binaryLeaf>0</binaryLeaf>
+                    <dto_d>
+                        <unknownAttribute>error</unknownAttribute>
+                        <simple-int1>444</simple-int1>
+                        <simple-int2>4444</simple-int2>
+                        <simple-int3>454</simple-int3>
+                        <complex-dto-bInner>
+                            <simple-int3>44</simple-int3>
+                            <deep>
+                                <simple-int3>4</simple-int3>
+                            </deep>
+                            <simple-list>4</simple-list>
+                        </complex-dto-bInner>
+                        <simple-list>4</simple-list>
+                    </dto_d>
+                    <simpleInt>44</simpleInt>
+                    <simple-test>545</simple-test>
+                    <simple-long>454545</simple-long>
+                    <simpleBoolean>false</simpleBoolean>
+                    <dto-c>
+                        <dto-a-inner>
+                            <dto-a-inner-inner>
+                                <simple-arg>456</simple-arg>
+                            </dto-a-inner-inner>
+                            <simple-arg>44</simple-arg>
+                        </dto-a-inner>
+                    </dto-c>
+                    <simple-short>4</simple-short>
+                    <simple-BigInteger>999</simple-BigInteger>
+                    <simple-byte>4</simple-byte>
+                    <peers>
+                        <port>port1</port>
+                        <simple-int3>456</simple-int3>
+                        <core-size>44</core-size>
+                    </peers>
+                    <peers>
+                        <port>port23</port>
+                        <simple-int3>456</simple-int3>
+                        <core-size>44</core-size>
+                    </peers>
+                    <testing-dep>
+                        <type>testing</type>
+                        <name>ref_dep</name>
+                    </testing-dep>
+                </module>
+
+                <module>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-netconf
+                    </type>
+                    <name>test2</name>
+                    <testing-dep>
+                        <type>testing</type>
+                        <name>ref_dep</name>
+                    </testing-dep>
+                </module>
+            </modules>
+
+            <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                <service>
+                    <type>testing</type>
+                    <instance>
+                        <name>ref_dep</name>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+                        </provider>
+                    </instance>
+                    <instance>
+                        <name>ref_dep_2</name>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+                        </provider>
+                    </instance>
+                    <instance>
+                        <name>ref_test1</name>
+                        <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+                        </provider>
+                    </instance>
+                </service>
+            </services>
+        </config>
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised8.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised8.xml
new file mode 100644 (file)
index 0000000..d0afb27
--- /dev/null
@@ -0,0 +1,117 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <test-option>
+            set
+        </test-option>
+        <default-operation>merge</default-operation>
+        <config>
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+                <module>
+                    <name>dep</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+                <module>
+                    <name>dep2</name>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-dep
+                    </type>
+                </module>
+
+                <module>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-netconf
+                    </type>
+
+                    <name>test1</name>
+
+                    <simple-long-2>44</simple-long-2>
+                    <binaryLeaf>8</binaryLeaf>
+                    <binaryLeaf>1</binaryLeaf>
+                    <binaryLeaf>0</binaryLeaf>
+                    <dto_d>
+                        <simple-int1>444</simple-int1>
+                        <simple-int2>4444</simple-int2>
+                        <simple-int3>454</simple-int3>
+                        <complex-dto-bInner>
+                            <simple-int3>44</simple-int3>
+                            <deep>
+                                <simple-int3>4</simple-int3>
+                            </deep>
+                            <simple-list>4</simple-list>
+                        </complex-dto-bInner>
+                        <simple-list>4</simple-list>
+                    </dto_d>
+                    <simpleInt>44</simpleInt>
+                    <simple-test>545</simple-test>
+                    <simple-long>454545</simple-long>
+                    <simpleBoolean>false</simpleBoolean>
+                    <dto-c>
+                        <dto-a-inner>
+                            <dto-a-inner-inner>
+                                <simple-arg>456</simple-arg>
+                            </dto-a-inner-inner>
+                            <simple-arg>44</simple-arg>
+                        </dto-a-inner>
+                    </dto-c>
+                    <simple-short>4</simple-short>
+                    <simple-BigInteger>999</simple-BigInteger>
+                    <simple-byte>4</simple-byte>
+                    <peers>
+                        <port>port1</port>
+                        <simple-int3>456</simple-int3>
+                        <core-size>44</core-size>
+                    </peers>
+                    <peers>
+                        <port>port23</port>
+                        <simple-int3>456</simple-int3>
+                        <core-size>44</core-size>
+                        <unknownAttribute>error</unknownAttribute>
+                    </peers>
+                    <testing-dep>
+                        <type>testing</type>
+                        <name>ref_dep</name>
+                    </testing-dep>
+                </module>
+
+                <module>
+                    <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        test-impl:impl-netconf
+                    </type>
+                    <name>test2</name>
+                    <testing-dep>
+                        <type>testing</type>
+                        <name>ref_dep</name>
+                    </testing-dep>
+                </module>
+            </modules>
+
+            <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                <service>
+                    <type>testing</type>
+                    <instance>
+                        <name>ref_dep</name>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep']
+                        </provider>
+                    </instance>
+                    <instance>
+                        <name>ref_dep_2</name>
+                        <provider>/config/modules/module[name='impl-dep']/instance[name='dep2']
+                        </provider>
+                    </instance>
+                    <instance>
+                        <name>ref_test1</name>
+                        <provider>/config/modules/module[name='impl-netconf']/instance[name='test1']
+                        </provider>
+                    </instance>
+                </service>
+            </services>
+        </config>
+    </edit-config>
+</rpc>
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/validate.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/validate.xml
new file mode 100644 (file)
index 0000000..dab34b4
--- /dev/null
@@ -0,0 +1,7 @@
+<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101">
+    <validate>
+        <source>
+            <candidate/>
+        </source>
+    </validate>
+</rpc>
diff --git a/opendaylight/netconf/pom.xml b/opendaylight/netconf/pom.xml
new file mode 100644 (file)
index 0000000..cff6dfa
--- /dev/null
@@ -0,0 +1,148 @@
+<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>commons.opendaylight</artifactId>
+        <version>1.4.1-SNAPSHOT</version>
+        <relativePath>../commons/opendaylight</relativePath>
+    </parent>
+
+    <groupId>org.opendaylight.controller</groupId>
+    <version>0.2.1-SNAPSHOT</version>
+    <artifactId>netconf-subsystem</artifactId>
+    <packaging>pom</packaging>
+    <name>${project.artifactId}</name>
+    <prerequisites>
+        <maven>3.0.4</maven>
+    </prerequisites>
+
+
+    <modules>
+        <module>netconf-api</module>
+        <module>netconf-impl</module>
+        <module>config-netconf-connector</module>
+        <module>netconf-util</module>
+        <module>netconf-it</module>
+        <module>config-persister-impl</module>
+        <module>netconf-mapping-api</module>
+        <module>netconf-client</module>
+    </modules>
+
+    <properties>
+        <osgi.version>5.0.0</osgi.version>
+        <maven.bundle.version>2.3.7</maven.bundle.version>
+        <slf4j.version>1.7.2</slf4j.version>
+        <java.version.source>1.7</java.version.source>
+        <java.version.target>1.7</java.version.target>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.osgi</groupId>
+                <artifactId>org.osgi.core</artifactId>
+                <version>${osgi.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.bgpcep</groupId>
+                <artifactId>mockito-configuration</artifactId>
+                <version>${bgpcep.version}</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>config-api</artifactId>
+                <version>${config.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>config-manager</artifactId>
+                <version>${config.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>yang-jmx-generator</artifactId>
+                <version>${config.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>config-util</artifactId>
+                <version>${config.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>yang-store-api</artifactId>
+                <version>${config.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>yang-store-impl</artifactId>
+                <version>${config.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>yang-test</artifactId>
+                <version>${config.version}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>2.5.1</version>
+                <configuration>
+                    <source>${java.version.source}</source>
+                    <target>${java.version.target}</target>
+                    <testSource>${java.version.source}</testSource>
+                    <testTarget>${java.version.target}</testTarget>
+                </configuration>
+            </plugin>
+        </plugins>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.felix</groupId>
+                    <artifactId>maven-bundle-plugin</artifactId>
+                    <version>${maven.bundle.version}</version>
+                    <extensions>true</extensions>
+                    <configuration>
+                        <instructions>
+                            <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+                        </instructions>
+                    </configuration>
+                </plugin>
+            </plugins>
+
+        </pluginManagement>
+    </build>
+    <!--
+    <repositories>
+        <repository>
+            <id>opendaylight-snapshot</id>
+            <url>http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot</url>
+            <snapshots>
+                <updatePolicy>daily</updatePolicy>
+            </snapshots>
+        </repository>
+    </repositories>
+    -->
+</project>
diff --git a/pom.xml b/pom.xml
index 4abcd81c365b630a8679e92af33802ebcb9ab270..692c0c6b2148cad65a168384b585efde7a733600 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -81,6 +81,7 @@
     <!-- md-sal -->
     <module>opendaylight/md-sal</module>
     <module>opendaylight/config</module>
     <!-- md-sal -->
     <module>opendaylight/md-sal</module>
     <module>opendaylight/config</module>
+    <module>opendaylight/netconf</module>
     <!-- config -->
 
     <!--  Web bundles -->
     <!-- config -->
 
     <!--  Web bundles -->