BUG-2314 Migrate netconf-connector to NormalizedNode 83/15783/9
authorMaros Marsalek <mmarsale@cisco.com>
Tue, 24 Feb 2015 12:17:00 +0000 (13:17 +0100)
committerTony Tkacik <ttkacik@cisco.com>
Sun, 8 Mar 2015 20:23:22 +0000 (21:23 +0100)
CompositeNodes are wiped out from sal-netconf-connector.

The inital operations performed on a remote netconf device
(e.g. Schema download) is performed using a special schema
context that contains base netconf, netconf monitoring
and netconf notification schemas.

With this schema context, it is possible to also use
normalized nodes.

YangInstanceIdentifier -> NormalizedNode filter structure
is handled by a class named InstanceIdToFilter that was
extracted from deprecated DataNormalizer.

Change-Id: Ibcc399c3ef9413aa4f96dba5b4bb2611db7123a8
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
37 files changed:
opendaylight/md-sal/sal-netconf-connector/pom.xml
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/MessageTransformer.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceHandler.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemas.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NotificationHandler.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataBroker.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDatastoreAdapter.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceNotificationService.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceRpc.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalFacade.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalProvider.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceTopologyAdapter.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/AbstractWriteTx.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/ReadOnlyTx.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteCandidateRunningTx.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteCandidateTx.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteRunningTx.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaYangSourceProvider.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/InstanceIdToNodes.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfBaseOps.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfRpcFutureCallback.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NodeContainerProxy.java
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemasTest.java
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfToNotificationTest.java
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfToRpcRequestTest.java
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTxTest.java
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/ReadOnlyTxTest.java
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformerTest.java
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/util/InstanceIdToNodesTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/config-test-rpc.yang
opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/filter-test.yang [new file with mode: 0644]
opendaylight/netconf/pom.xml

index 61c83a6..40cf5a3 100644 (file)
   <packaging>bundle</packaging>
 
   <dependencies>
-    <dependency>
-      <groupId>${project.groupId}</groupId>
-      <artifactId>netconf-client</artifactId>
-      <version>${netconf.version}</version>
-    </dependency>
     <dependency>
       <groupId>${project.groupId}</groupId>
       <artifactId>netconf-config-dispatcher</artifactId>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>sal-binding-api</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>binding-generator-impl</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>sal-binding-config</artifactId>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
     </dependency>
-    <dependency>
+      <dependency>
+          <groupId>xmlunit</groupId>
+          <artifactId>xmlunit</artifactId>
+      </dependency>
+      <dependency>
       <groupId>${project.groupId}</groupId>
       <artifactId>config-api</artifactId>
       <version>${netconf.version}</version>
index 44b2435..4465e93 100644 (file)
@@ -29,7 +29,6 @@ import org.opendaylight.controller.sal.connect.netconf.NetconfStateSchemas;
 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCommunicator;
 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceSalFacade;
-import org.opendaylight.controller.sal.connect.netconf.schema.mapping.NetconfMessageTransformer;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.controller.sal.core.api.Broker;
 import org.opendaylight.protocol.framework.ReconnectStrategy;
@@ -105,13 +104,13 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
         final BindingAwareBroker bindingBroker = getBindingRegistryDependency();
 
         final RemoteDeviceHandler<NetconfSessionPreferences> salFacade
-                = new NetconfDeviceSalFacade(id, domBroker, bindingBroker, bundleContext, globalProcessingExecutor);
+                = new NetconfDeviceSalFacade(id, domBroker, bindingBroker, bundleContext);
 
         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO =
                 new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaContextFactory, new NetconfStateSchemas.NetconfStateSchemasResolverImpl());
 
         final NetconfDevice device =
-                new NetconfDevice(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, new NetconfMessageTransformer(), getReconnectOnChangedSchema());
+                new NetconfDevice(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, getReconnectOnChangedSchema());
 
         final NetconfDeviceCommunicator listener = userCapabilities.isPresent() ?
                 new NetconfDeviceCommunicator(id, device, userCapabilities.get()) : new NetconfDeviceCommunicator(id, device);
index 7a392a8..6f81ce0 100644 (file)
@@ -7,17 +7,16 @@
  */
 package org.opendaylight.controller.sal.connect.api;
 
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 
-public interface MessageTransformer<M> extends SchemaContextListener {
+public interface MessageTransformer<M> {
 
-    CompositeNode toNotification(M message);
+    ContainerNode toNotification(M message);
 
-    M toRpcRequest(QName rpc, CompositeNode node);
+    M toRpcRequest(SchemaPath rpc, ContainerNode node);
 
-    RpcResult<CompositeNode> toRpcResult(M message, QName rpc);
+    DOMRpcResult toRpcResult(M message, SchemaPath rpc);
 
 }
index c5a0ae2..02f45e5 100644 (file)
@@ -7,20 +7,20 @@
  */
 package org.opendaylight.controller.sal.connect.api;
 
-import org.opendaylight.controller.sal.core.api.RpcImplementation;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 public interface RemoteDeviceHandler<PREF> extends AutoCloseable {
 
     void onDeviceConnected(SchemaContext remoteSchemaContext,
-                           PREF netconfSessionPreferences, RpcImplementation deviceRpc);
+                           PREF netconfSessionPreferences, DOMRpcService deviceRpc);
 
     void onDeviceDisconnected();
 
     void onDeviceFailed(Throwable throwable);
 
-    void onNotification(CompositeNode domNotification);
+    void onNotification(ContainerNode domNotification);
 
     void close();
 }
index 281f82b..b57a891 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.controller.sal.connect.netconf;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
@@ -26,6 +27,9 @@ import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.sal.connect.api.MessageTransformer;
 import org.opendaylight.controller.sal.connect.api.RemoteDevice;
@@ -36,13 +40,15 @@ import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCom
 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceRpc;
 import org.opendaylight.controller.sal.connect.netconf.schema.NetconfRemoteSchemaYangSourceProvider;
+import org.opendaylight.controller.sal.connect.netconf.schema.mapping.NetconfMessageTransformer;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.extension.rev131210.$YangModuleInfoImpl;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfCapabilityChange;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.fields.unavailable.capabilities.UnavailableCapability;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
@@ -63,6 +69,26 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionPreferenc
 
     private static final Logger logger = LoggerFactory.getLogger(NetconfDevice.class);
 
+    /**
+     * Initial schema context contains schemas for netconf monitoring and netconf notifications
+     */
+    public static final SchemaContext INIT_SCHEMA_CTX;
+
+    static {
+        try {
+            final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create();
+            moduleInfoBackedContext.addModuleInfos(
+                    Lists.newArrayList(
+                            $YangModuleInfoImpl.getInstance(),
+                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.$YangModuleInfoImpl.getInstance(),
+                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.$YangModuleInfoImpl.getInstance()));
+            INIT_SCHEMA_CTX = moduleInfoBackedContext.tryToCreateSchemaContext().get();
+        } catch (final RuntimeException e) {
+            logger.error("Unable to prepare schema context for netconf initialization", e);
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
     public static final Function<QName, SourceIdentifier> QNAME_TO_SOURCE_ID_FUNCTION = new Function<QName, SourceIdentifier>() {
         @Override
         public SourceIdentifier apply(final QName input) {
@@ -77,28 +103,37 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionPreferenc
     private final RemoteDeviceHandler<NetconfSessionPreferences> salFacade;
     private final ListeningExecutorService processingExecutor;
     private final SchemaSourceRegistry schemaRegistry;
-    private final MessageTransformer<NetconfMessage> messageTransformer;
     private final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver;
     private final NotificationHandler notificationHandler;
     private final List<SchemaSourceRegistration<? extends SchemaSourceRepresentation>> sourceRegistrations = Lists.newArrayList();
 
+    // Message transformer is constructed once the schemas are available
+    private MessageTransformer<NetconfMessage> messageTransformer;
+
     public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id, final RemoteDeviceHandler<NetconfSessionPreferences> salFacade,
-                         final ExecutorService globalProcessingExecutor, final MessageTransformer<NetconfMessage> messageTransformer) {
-        this(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, messageTransformer, false);
+                         final ExecutorService globalProcessingExecutor) {
+        this(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, false);
     }
 
+    /**
+     * Create rpc implementation capable of handling RPC for monitoring and notifications even before the schemas of remote device are downloaded
+     */
+    static NetconfDeviceRpc getRpcForInitialization(final NetconfDeviceCommunicator listener) {
+        return new NetconfDeviceRpc(INIT_SCHEMA_CTX, listener, new NetconfMessageTransformer(INIT_SCHEMA_CTX));
+    }
+
+
     // FIXME reduce parameters
     public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id, final RemoteDeviceHandler<NetconfSessionPreferences> salFacade,
-                         final ExecutorService globalProcessingExecutor, final MessageTransformer<NetconfMessage> messageTransformer, final boolean reconnectOnSchemasChange) {
+                         final ExecutorService globalProcessingExecutor, final boolean reconnectOnSchemasChange) {
         this.id = id;
         this.reconnectOnSchemasChange = reconnectOnSchemasChange;
         this.schemaRegistry = schemaResourcesDTO.getSchemaRegistry();
-        this.messageTransformer = messageTransformer;
         this.schemaContextFactory = schemaResourcesDTO.getSchemaContextFactory();
         this.salFacade = salFacade;
         this.stateSchemasResolver = schemaResourcesDTO.getStateSchemasResolver();
         this.processingExecutor = MoreExecutors.listeningDecorator(globalProcessingExecutor);
-        this.notificationHandler = new NotificationHandler(salFacade, messageTransformer, id);
+        this.notificationHandler = new NotificationHandler(salFacade, id);
     }
 
     @Override
@@ -111,24 +146,23 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionPreferenc
         // http://netty.io/wiki/thread-model.html
         logger.debug("{}: Session to remote device established with {}", id, remoteSessionCapabilities);
 
-        final NetconfDeviceRpc deviceRpc = setUpDeviceRpc(listener);
-
-        final DeviceSourcesResolver task = new DeviceSourcesResolver(deviceRpc, remoteSessionCapabilities, id, stateSchemasResolver);
+        final NetconfDeviceRpc initRpc = getRpcForInitialization(listener);
+        final DeviceSourcesResolver task = new DeviceSourcesResolver(remoteSessionCapabilities, id, stateSchemasResolver, initRpc);
         final ListenableFuture<DeviceSources> sourceResolverFuture = processingExecutor.submit(task);
 
         if(shouldListenOnSchemaChange(remoteSessionCapabilities)) {
-           registerToBaseNetconfStream(deviceRpc, listener);
+           registerToBaseNetconfStream(initRpc, listener);
         }
 
         final FutureCallback<DeviceSources> resolvedSourceCallback = new FutureCallback<DeviceSources>() {
             @Override
             public void onSuccess(final DeviceSources result) {
-                addProvidedSourcesToSchemaRegistry(deviceRpc, result);
+                addProvidedSourcesToSchemaRegistry(initRpc, result);
                 setUpSchema(result);
             }
 
             private void setUpSchema(final DeviceSources result) {
-                processingExecutor.submit(new RecursiveSchemaSetup(result, remoteSessionCapabilities, deviceRpc, listener));
+                processingExecutor.submit(new RecursiveSchemaSetup(result, remoteSessionCapabilities, listener));
             }
 
             @Override
@@ -139,33 +173,32 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionPreferenc
         };
 
         Futures.addCallback(sourceResolverFuture, resolvedSourceCallback);
-
     }
 
     private void registerToBaseNetconfStream(final NetconfDeviceRpc deviceRpc, final NetconfDeviceCommunicator listener) {
-       final ListenableFuture<RpcResult<CompositeNode>> rpcResultListenableFuture =
-                deviceRpc.invokeRpc(NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_QNAME, NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_CONTENT);
+       final CheckedFuture<DOMRpcResult, DOMRpcException> rpcResultListenableFuture =
+                deviceRpc.invokeRpc(NetconfMessageTransformUtil.toPath(NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_QNAME), NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_CONTENT);
 
         final NotificationHandler.NotificationFilter filter = new NotificationHandler.NotificationFilter() {
             @Override
-            public Optional<CompositeNode> filterNotification(final CompositeNode notification) {
+            public Optional<NormalizedNode<?, ?>> filterNotification(final NormalizedNode<?, ?> notification) {
                 if (isCapabilityChanged(notification)) {
                     logger.info("{}: Schemas change detected, reconnecting", id);
                     // Only disconnect is enough, the reconnecting nature of the connector will take care of reconnecting
                     listener.disconnect();
                     return Optional.absent();
                 }
-                return Optional.of(notification);
+                return Optional.<NormalizedNode<?, ?>>of(notification);
             }
 
-            private boolean isCapabilityChanged(final CompositeNode notification) {
+            private boolean isCapabilityChanged(final NormalizedNode<?, ?> notification) {
                 return notification.getNodeType().equals(NetconfCapabilityChange.QNAME);
             }
         };
 
-        Futures.addCallback(rpcResultListenableFuture, new FutureCallback<RpcResult<CompositeNode>>() {
+        Futures.addCallback(rpcResultListenableFuture, new FutureCallback<DOMRpcResult>() {
             @Override
-            public void onSuccess(final RpcResult<CompositeNode> result) {
+            public void onSuccess(final DOMRpcResult domRpcResult) {
                 notificationHandler.addNotificationFilter(filter);
             }
 
@@ -180,10 +213,13 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionPreferenc
         return remoteSessionCapabilities.isNotificationsSupported() && reconnectOnSchemasChange;
     }
 
-    private void handleSalInitializationSuccess(final SchemaContext result, final NetconfSessionPreferences remoteSessionCapabilities, final NetconfDeviceRpc deviceRpc) {
-        updateMessageTransformer(result);
+    @VisibleForTesting
+    void handleSalInitializationSuccess(final SchemaContext result, final NetconfSessionPreferences remoteSessionCapabilities, final DOMRpcService deviceRpc) {
+        messageTransformer = new NetconfMessageTransformer(result);
+
+        updateTransformer(messageTransformer);
+        notificationHandler.onRemoteSchemaUp(messageTransformer);
         salFacade.onDeviceConnected(result, remoteSessionCapabilities, deviceRpc);
-        notificationHandler.onRemoteSchemaUp();
 
         logger.info("{}: Netconf connector initialized successfully", id);
     }
@@ -196,17 +232,14 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionPreferenc
     }
 
     /**
-     * Set the schema context inside transformer to null as is in initial state
+     * Set the transformer to null as is in initial state
      */
     private void resetMessageTransformer() {
-        updateMessageTransformer(null);
+        updateTransformer(null);
     }
 
-    /**
-     * Update initial message transformer to use retrieved schema
-     */
-    private void updateMessageTransformer(final SchemaContext currentSchemaContext) {
-        messageTransformer.onGlobalContextUpdated(currentSchemaContext);
+    private void updateTransformer(final MessageTransformer<NetconfMessage> transformer) {
+        messageTransformer = transformer;
     }
 
     private void addProvidedSourcesToSchemaRegistry(final NetconfDeviceRpc deviceRpc, final DeviceSources deviceSources) {
@@ -217,10 +250,6 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionPreferenc
         }
     }
 
-    private NetconfDeviceRpc setUpDeviceRpc(final RemoteDeviceCommunicator<NetconfMessage> listener) {
-       return new NetconfDeviceRpc(listener, messageTransformer);
-    }
-
     @Override
     public void onRemoteSessionDown() {
         notificationHandler.onRemoteSchemaDown();
@@ -233,7 +262,7 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionPreferenc
     }
 
     @Override
-    public void onRemoteSessionFailed(Throwable throwable) {
+    public void onRemoteSessionFailed(final Throwable throwable) {
         salFacade.onDeviceFailed(throwable);
     }
 
@@ -273,18 +302,24 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionPreferenc
      * Schema building callable.
      */
     private static class DeviceSourcesResolver implements Callable<DeviceSources> {
+
         private final NetconfDeviceRpc deviceRpc;
         private final NetconfSessionPreferences remoteSessionCapabilities;
         private final RemoteDeviceId id;
         private final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver;
 
-        public DeviceSourcesResolver(final NetconfDeviceRpc deviceRpc, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id, final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver) {
+        DeviceSourcesResolver(final NetconfDeviceRpc deviceRpc, final NetconfSessionPreferences remoteSessionCapabilities,
+                                     final RemoteDeviceId id, final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver) {
             this.deviceRpc = deviceRpc;
             this.remoteSessionCapabilities = remoteSessionCapabilities;
             this.id = id;
             this.stateSchemasResolver = stateSchemasResolver;
         }
 
+        public DeviceSourcesResolver(final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id, final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver, final NetconfDeviceRpc rpcForMonitoring) {
+            this(rpcForMonitoring, remoteSessionCapabilities, id, stateSchemasResolver);
+        }
+
         @Override
         public DeviceSources call() throws Exception {
 
@@ -307,10 +342,10 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionPreferenc
             }
 
 
-            // TODO should we perform this ? We have a mechanism to fix initialization of devices not reporting or required modules in hello
-            // That is overriding capabilities in configuration using attribute yang-module-capabilities
-            // This is more user friendly even though it clashes with attribute yang-module-capabilities
-            // Some devices do not report all required models in hello message, but provide them
+            // Here all the sources reported in netconf monitoring are merged with those reported in hello.
+            // It is necessary to perform this since submodules are not mentioned in hello but still required.
+            // This clashes with the option of a user to specify supported yang models manually in configuration for netconf-connector
+            // and as a result one is not able to fully override yang models of a device. It is only possible to add additional models.
             final Set<SourceIdentifier> providedSourcesNotRequired = Sets.difference(providedSources, requiredSources);
             if (!providedSourcesNotRequired.isEmpty()) {
                 logger.warn("{}: Netconf device provides additional yang models not reported in hello message capabilities: {}",
@@ -326,7 +361,6 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionPreferenc
 
     /**
      * Contains RequiredSources - sources from capabilities.
-     *
      */
     private static final class DeviceSources {
         private final Collection<SourceIdentifier> requiredSources;
@@ -353,14 +387,12 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionPreferenc
     private final class RecursiveSchemaSetup implements Runnable {
         private final DeviceSources deviceSources;
         private final NetconfSessionPreferences remoteSessionCapabilities;
-        private final NetconfDeviceRpc deviceRpc;
         private final RemoteDeviceCommunicator<NetconfMessage> listener;
-        private NetconfDeviceCapabilities capabilities;
+        private final NetconfDeviceCapabilities capabilities;
 
-        public RecursiveSchemaSetup(final DeviceSources deviceSources, final NetconfSessionPreferences remoteSessionCapabilities, final NetconfDeviceRpc deviceRpc, final RemoteDeviceCommunicator<NetconfMessage> listener) {
+        public RecursiveSchemaSetup(final DeviceSources deviceSources, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceCommunicator<NetconfMessage> listener) {
             this.deviceSources = deviceSources;
             this.remoteSessionCapabilities = remoteSessionCapabilities;
-            this.deviceRpc = deviceRpc;
             this.listener = listener;
             this.capabilities = remoteSessionCapabilities.getNetconfDeviceCapabilities();
         }
@@ -390,10 +422,10 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionPreferenc
                 @Override
                 public void onSuccess(final SchemaContext result) {
                     logger.debug("{}: Schema context built successfully from {}", id, requiredSources);
-                    Collection<QName> filteredQNames = Sets.difference(remoteSessionCapabilities.getModuleBasedCaps(), capabilities.getUnresolvedCapabilites().keySet());
+                    final Collection<QName> filteredQNames = Sets.difference(remoteSessionCapabilities.getModuleBasedCaps(), capabilities.getUnresolvedCapabilites().keySet());
                     capabilities.addCapabilities(filteredQNames);
                     capabilities.addNonModuleBasedCapabilities(remoteSessionCapabilities.getNonModuleCaps());
-                    handleSalInitializationSuccess(result, remoteSessionCapabilities, deviceRpc);
+                    handleSalInitializationSuccess(result, remoteSessionCapabilities, getDeviceSpecificRpc(result));
                 }
 
                 @Override
@@ -423,6 +455,10 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionPreferenc
             Futures.addCallback(schemaBuilderFuture, RecursiveSchemaBuilderCallback);
         }
 
+        private NetconfDeviceRpc getDeviceSpecificRpc(final SchemaContext result) {
+            return new NetconfDeviceRpc(result, listener, new NetconfMessageTransformer(result));
+        }
+
         private Collection<SourceIdentifier> stripMissingSource(final Collection<SourceIdentifier> requiredSources, final SourceIdentifier sIdToRemove) {
             final LinkedList<SourceIdentifier> sourceIdentifiers = Lists.newLinkedList(requiredSources);
             final boolean removed = sourceIdentifiers.remove(sIdToRemove);
@@ -430,10 +466,10 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionPreferenc
             return sourceIdentifiers;
         }
 
-        private Collection<QName> getQNameFromSourceIdentifiers(Collection<SourceIdentifier> identifiers) {
-            Collection<QName> qNames = new HashSet<>();
-            for (SourceIdentifier source : identifiers) {
-                Optional<QName> qname = getQNameFromSourceIdentifier(source);
+        private Collection<QName> getQNameFromSourceIdentifiers(final Collection<SourceIdentifier> identifiers) {
+            final Collection<QName> qNames = new HashSet<>();
+            for (final SourceIdentifier source : identifiers) {
+                final Optional<QName> qname = getQNameFromSourceIdentifier(source);
                 if (qname.isPresent()) {
                     qNames.add(qname.get());
                 }
@@ -444,8 +480,8 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionPreferenc
             return qNames;
         }
 
-        private Optional<QName> getQNameFromSourceIdentifier(SourceIdentifier identifier) {
-            for (QName qname : remoteSessionCapabilities.getModuleBasedCaps()) {
+        private Optional<QName> getQNameFromSourceIdentifier(final SourceIdentifier identifier) {
+            for (final QName qname : remoteSessionCapabilities.getModuleBasedCaps()) {
                 if (qname.getLocalName().equals(identifier.getName())
                         && qname.getFormattedRevision().equals(identifier.getRevision())) {
                     return Optional.of(qname);
index 68c1a5c..aa0897e 100644 (file)
@@ -1,16 +1,22 @@
 package org.opendaylight.controller.sal.connect.netconf;
 
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DATA_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toId;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath;
+
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
 import com.google.common.collect.Collections2;
-import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import java.net.URI;
 import java.util.Collections;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceRpc;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
@@ -20,12 +26,16 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.mon
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.Node;
-import org.opendaylight.yangtools.yang.data.api.SimpleNode;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -34,7 +44,7 @@ import org.slf4j.LoggerFactory;
  */
 public final class NetconfStateSchemas {
 
-    private static final Logger logger = LoggerFactory.getLogger(NetconfStateSchemas.class);
+    private static final Logger LOG = LoggerFactory.getLogger(NetconfStateSchemas.class);
 
     /**
      * Factory for NetconfStateSchemas
@@ -58,15 +68,12 @@ public final class NetconfStateSchemas {
 
     private static final YangInstanceIdentifier STATE_SCHEMAS_IDENTIFIER =
             YangInstanceIdentifier.builder().node(NetconfState.QNAME).node(Schemas.QNAME).build();
-    private static final YangInstanceIdentifier DATA_STATE_SCHEMAS_IDENTIFIER =
-            YangInstanceIdentifier.builder().node(NetconfMessageTransformUtil.NETCONF_DATA_QNAME)
-                    .node(NetconfState.QNAME).node(Schemas.QNAME).build();
 
-    private static final CompositeNode GET_SCHEMAS_RPC;
+    private static final ContainerNode GET_SCHEMAS_RPC;
     static {
-        final Node<?> filter = NetconfMessageTransformUtil.toFilterStructure(STATE_SCHEMAS_IDENTIFIER);
+        final DataContainerChild<?, ?> filter = NetconfMessageTransformUtil.toFilterStructure(STATE_SCHEMAS_IDENTIFIER, NetconfDevice.INIT_SCHEMA_CTX);
         GET_SCHEMAS_RPC
-                = NodeFactory.createImmutableCompositeNode(NetconfMessageTransformUtil.NETCONF_GET_QNAME, null, Lists.<Node<?>>newArrayList(filter));
+                = Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_GET_QNAME)).withChild(filter).build();
     }
 
     private final Set<RemoteYangSchema> availableYangSchemas;
@@ -93,45 +100,69 @@ public final class NetconfStateSchemas {
      */
     private static NetconfStateSchemas create(final NetconfDeviceRpc deviceRpc, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id) {
         if(remoteSessionCapabilities.isMonitoringSupported() == false) {
-            logger.warn("{}: Netconf monitoring not supported on device, cannot detect provided schemas");
+            LOG.warn("{}: Netconf monitoring not supported on device, cannot detect provided schemas");
             return EMPTY;
         }
 
-        final RpcResult<CompositeNode> schemasNodeResult;
+        final DOMRpcResult schemasNodeResult;
         try {
-            schemasNodeResult = deviceRpc.invokeRpc(NetconfMessageTransformUtil.NETCONF_GET_QNAME, GET_SCHEMAS_RPC).get();
+            schemasNodeResult = deviceRpc.invokeRpc(toPath(NETCONF_GET_QNAME), GET_SCHEMAS_RPC).get();
         } catch (final InterruptedException e) {
             Thread.currentThread().interrupt();
             throw new RuntimeException(id + ": Interrupted while waiting for response to " + STATE_SCHEMAS_IDENTIFIER, e);
         } catch (final ExecutionException e) {
-            logger.warn("{}: Unable to detect available schemas, get to {} failed", id, STATE_SCHEMAS_IDENTIFIER, e);
+            LOG.warn("{}: Unable to detect available schemas, get to {} failed", id, STATE_SCHEMAS_IDENTIFIER, e);
             return EMPTY;
         }
 
-        if(schemasNodeResult.isSuccessful() == false) {
-            logger.warn("{}: Unable to detect available schemas, get to {} failed, {}", id, STATE_SCHEMAS_IDENTIFIER, schemasNodeResult.getErrors());
+        if(schemasNodeResult.getErrors().isEmpty() == false) {
+            LOG.warn("{}: Unable to detect available schemas, get to {} failed, {}", id, STATE_SCHEMAS_IDENTIFIER, schemasNodeResult.getErrors());
             return EMPTY;
         }
 
-        final CompositeNode schemasNode =
-                (CompositeNode) NetconfMessageTransformUtil.findNode(schemasNodeResult.getResult(), DATA_STATE_SCHEMAS_IDENTIFIER);
-        if(schemasNode == null) {
-            logger.warn("{}: Unable to detect available schemas, get to {} was empty", id, STATE_SCHEMAS_IDENTIFIER);
+        final Optional<? extends NormalizedNode<?, ?>> schemasNode = findSchemasNode(schemasNodeResult.getResult());
+
+        if(schemasNode.isPresent()) {
+            Preconditions.checkState(schemasNode.get() instanceof ContainerNode,
+                    "Expecting container containing schemas, but was %s", schemasNode.get());
+            return create(id, ((ContainerNode) schemasNode.get()));
+        } else {
+            LOG.warn("{}: Unable to detect available schemas, get to {} was empty", id, STATE_SCHEMAS_IDENTIFIER);
             return EMPTY;
         }
+    }
 
-        return create(id, schemasNode);
+    private static Optional<? extends NormalizedNode<?, ?>> findSchemasNode(final NormalizedNode<?, ?> result) {
+        if(result == null) {
+            return Optional.absent();
+        }
+        final Optional<DataContainerChild<?, ?>> dataNode = ((DataContainerNode<?>) result).getChild(toId(NETCONF_DATA_QNAME));
+        if(dataNode.isPresent() == false) {
+            return Optional.absent();
+        }
+
+        final Optional<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> nStateNode =
+                ((DataContainerNode<?>) dataNode.get()).getChild(toId(NetconfState.QNAME));
+        if(nStateNode.isPresent() == false) {
+            return Optional.absent();
+        }
+
+        return ((DataContainerNode<?>) nStateNode.get()).getChild(toId(Schemas.QNAME));
     }
 
     /**
      * Parse response of get(netconf-state/schemas) to find all schemas under netconf-state/schemas
      */
     @VisibleForTesting
-    protected static NetconfStateSchemas create(final RemoteDeviceId id, final CompositeNode schemasNode) {
+    protected static NetconfStateSchemas create(final RemoteDeviceId id, final ContainerNode schemasNode) {
         final Set<RemoteYangSchema> availableYangSchemas = Sets.newHashSet();
 
-        for (final CompositeNode schemaNode : schemasNode.getCompositesByName(Schema.QNAME.withoutRevision())) {
-            final Optional<RemoteYangSchema> fromCompositeNode = RemoteYangSchema.createFromCompositeNode(id, schemaNode);
+        final Optional<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> child = schemasNode.getChild(toId(Schema.QNAME));
+        Preconditions.checkState(child.isPresent(), "Unable to find list: %s in response: %s", Schema.QNAME.withoutRevision(), schemasNode);
+        Preconditions.checkState(child.get() instanceof MapNode, "Unexpected structure for container: %s in response: %s. Expecting a list", Schema.QNAME.withoutRevision(), schemasNode);
+
+        for (final MapEntryNode schemaNode : ((MapNode) child.get()).getValue()) {
+            final Optional<RemoteYangSchema> fromCompositeNode = RemoteYangSchema.createFromNormalizedNode(id, schemaNode);
             if(fromCompositeNode.isPresent()) {
                 availableYangSchemas.add(fromCompositeNode.get());
             }
@@ -151,64 +182,70 @@ public final class NetconfStateSchemas {
             return qname;
         }
 
-        static Optional<RemoteYangSchema> createFromCompositeNode(final RemoteDeviceId id, final CompositeNode schemaNode) {
-            Preconditions.checkArgument(schemaNode.getKey().equals(Schema.QNAME.withoutRevision()), "Wrong QName %s", schemaNode.getKey());
+        static Optional<RemoteYangSchema> createFromNormalizedNode(final RemoteDeviceId id, final MapEntryNode schemaNode) {
+            Preconditions.checkArgument(schemaNode.getNodeType().equals(Schema.QNAME), "Wrong QName %s", schemaNode.getNodeType());
 
-            QName childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_FORMAT.withoutRevision();
+            QName childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_FORMAT;
 
             String formatAsString = getSingleChildNodeValue(schemaNode, childNode).get();
             //This is HotFix for situations where format statement in netconf-monitoring might be passed with prefix.
             if (formatAsString.contains(":")) {
-                String[] prefixedString = formatAsString.split(":");
+                final String[] prefixedString = formatAsString.split(":");
                 //FIXME: might be good idea to check prefix against model namespace
                 formatAsString = prefixedString[1];
             }
             if(formatAsString.equals(Yang.QNAME.getLocalName()) == false) {
-                logger.debug("{}: Ignoring schema due to unsupported format: {}", id, formatAsString);
+                LOG.debug("{}: Ignoring schema due to unsupported format: {}", id, formatAsString);
                 return Optional.absent();
             }
 
-            childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_LOCATION.withoutRevision();
+            childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_LOCATION;
             final Set<String> locationsAsString = getAllChildNodeValues(schemaNode, childNode);
             if(locationsAsString.contains(Schema.Location.Enumeration.NETCONF.toString()) == false) {
-                logger.debug("{}: Ignoring schema due to unsupported location: {}", id, locationsAsString);
+                LOG.debug("{}: Ignoring schema due to unsupported location: {}", id, locationsAsString);
                 return Optional.absent();
             }
 
-            childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_NAMESPACE.withoutRevision();
+            childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_NAMESPACE;
             final String namespaceAsString = getSingleChildNodeValue(schemaNode, childNode).get();
 
-            childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_VERSION.withoutRevision();
+            childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_VERSION;
             // Revision does not have to be filled
             final Optional<String> revisionAsString = getSingleChildNodeValue(schemaNode, childNode);
 
-            childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_IDENTIFIER.withoutRevision();
+            childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_IDENTIFIER;
             final String moduleNameAsString = getSingleChildNodeValue(schemaNode, childNode).get();
 
             final QName moduleQName = revisionAsString.isPresent()
                     ? QName.create(namespaceAsString, revisionAsString.get(), moduleNameAsString)
-                    : QName.create(URI.create(namespaceAsString), null, moduleNameAsString).withoutRevision();
+                    : QName.create(URI.create(namespaceAsString), null, moduleNameAsString);
 
             return Optional.of(new RemoteYangSchema(moduleQName));
         }
 
-        private static Set<String> getAllChildNodeValues(final CompositeNode schemaNode, final QName childNodeQName) {
+        /**
+         * Extracts all values of a leaf-list node as a set of strings
+         */
+        private static Set<String> getAllChildNodeValues(final DataContainerNode<?> schemaNode, final QName childNodeQName) {
             final Set<String> extractedValues = Sets.newHashSet();
-            for (final SimpleNode<?> childNode : schemaNode.getSimpleNodesByName(childNodeQName)) {
-                extractedValues.add(getValueOfSimpleNode(childNodeQName, childNode).get());
+            final Optional<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> child = schemaNode.getChild(toId(childNodeQName));
+            Preconditions.checkArgument(child.isPresent(), "Child nodes %s not present", childNodeQName);
+            Preconditions.checkArgument(child.get() instanceof LeafSetNode<?>, "Child nodes %s not present", childNodeQName);
+            for (final LeafSetEntryNode<?> childNode : ((LeafSetNode<?>) child.get()).getValue()) {
+                extractedValues.add(getValueOfSimpleNode(childNode).get());
             }
             return extractedValues;
         }
 
-        private static Optional<String> getSingleChildNodeValue(final CompositeNode schemaNode, final QName childNode) {
-            final SimpleNode<?> node = schemaNode.getFirstSimpleByName(childNode);
-            return getValueOfSimpleNode(childNode, node);
+        private static Optional<String> getSingleChildNodeValue(final DataContainerNode<?> schemaNode, final QName childNode) {
+            final Optional<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> node = schemaNode.getChild(toId(childNode));
+            Preconditions.checkArgument(node.isPresent(), "Child node %s not present", childNode);
+            return getValueOfSimpleNode(node.get());
         }
 
-        private static Optional<String> getValueOfSimpleNode(final QName childNode, final SimpleNode<?> node) {
-            Preconditions.checkNotNull(node, "Child node %s not present", childNode);
+        private static Optional<String> getValueOfSimpleNode(final NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?> node) {
             final Object value = node.getValue();
-            return value == null ? Optional.<String>absent() : Optional.of(value.toString().trim());
+            return value == null || Strings.isNullOrEmpty(value.toString()) ? Optional.<String>absent() : Optional.of(value.toString().trim());
         }
 
         @Override
index bc3326e..481afa5 100644 (file)
@@ -16,7 +16,8 @@ import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.opendaylight.controller.sal.connect.api.MessageTransformer;
 import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -29,14 +30,14 @@ final class NotificationHandler {
 
     private final RemoteDeviceHandler<?> salFacade;
     private final List<NetconfMessage> queue = new LinkedList<>();
-    private final MessageTransformer<NetconfMessage> messageTransformer;
     private final RemoteDeviceId id;
     private boolean passNotifications = false;
+
     private NotificationFilter filter;
+    private MessageTransformer<NetconfMessage> messageTransformer;
 
-    NotificationHandler(final RemoteDeviceHandler<?> salFacade, final MessageTransformer<NetconfMessage> messageTransformer, final RemoteDeviceId id) {
+    NotificationHandler(final RemoteDeviceHandler<?> salFacade, final RemoteDeviceId id) {
         this.salFacade = Preconditions.checkNotNull(salFacade);
-        this.messageTransformer = Preconditions.checkNotNull(messageTransformer);
         this.id = Preconditions.checkNotNull(id);
     }
 
@@ -50,8 +51,11 @@ final class NotificationHandler {
 
     /**
      * Forward all cached notifications and pass all notifications from this point directly to sal facade.
+     * @param messageTransformer
      */
-    synchronized void onRemoteSchemaUp() {
+    synchronized void onRemoteSchemaUp(final MessageTransformer<NetconfMessage> messageTransformer) {
+        this.messageTransformer = Preconditions.checkNotNull(messageTransformer);
+
         passNotifications = true;
 
         for (final NetconfMessage cachedNotification : queue) {
@@ -61,8 +65,8 @@ final class NotificationHandler {
         queue.clear();
     }
 
-    private CompositeNode transformNotification(final NetconfMessage cachedNotification) {
-        final CompositeNode parsedNotification = messageTransformer.toNotification(cachedNotification);
+    private ContainerNode transformNotification(final NetconfMessage cachedNotification) {
+        final ContainerNode parsedNotification = messageTransformer.toNotification(cachedNotification);
         Preconditions.checkNotNull(parsedNotification, "%s: Unable to parse received notification: %s", id, cachedNotification);
         return parsedNotification;
     }
@@ -78,7 +82,7 @@ final class NotificationHandler {
         queue.add(notification);
     }
 
-    private synchronized void passNotification(final CompositeNode parsedNotification) {
+    private synchronized void passNotification(final ContainerNode parsedNotification) {
         logger.debug("{}: Forwarding notification {}", id, parsedNotification);
 
         if(filter == null || filter.filterNotification(parsedNotification).isPresent()) {
@@ -93,10 +97,11 @@ final class NotificationHandler {
     synchronized void onRemoteSchemaDown() {
         queue.clear();
         passNotifications = false;
+        messageTransformer = null;
     }
 
     static interface NotificationFilter {
 
-        Optional<CompositeNode> filterNotification(CompositeNode notification);
+        Optional<NormalizedNode<?, ?>> filterNotification(NormalizedNode<?, ?> notification);
     }
 }
index 87ca11d..091c6b4 100644 (file)
@@ -10,22 +10,21 @@ package org.opendaylight.controller.sal.connect.netconf.sal;
 
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
-import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.controller.sal.connect.netconf.sal.tx.ReadOnlyTx;
 import org.opendaylight.controller.sal.connect.netconf.sal.tx.ReadWriteTx;
-import org.opendaylight.controller.sal.connect.netconf.sal.tx.WriteCandidateTx;
 import org.opendaylight.controller.sal.connect.netconf.sal.tx.WriteCandidateRunningTx;
+import org.opendaylight.controller.sal.connect.netconf.sal.tx.WriteCandidateTx;
 import org.opendaylight.controller.sal.connect.netconf.sal.tx.WriteRunningTx;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfBaseOps;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
-import org.opendaylight.controller.sal.core.api.RpcImplementation;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
@@ -34,18 +33,16 @@ final class NetconfDeviceDataBroker implements DOMDataBroker {
     private final RemoteDeviceId id;
     private final NetconfBaseOps netconfOps;
     private final NetconfSessionPreferences netconfSessionPreferences;
-    private final DataNormalizer normalizer;
 
-    public NetconfDeviceDataBroker(final RemoteDeviceId id, final RpcImplementation rpc, final SchemaContext schemaContext, final NetconfSessionPreferences netconfSessionPreferences) {
+    public NetconfDeviceDataBroker(final RemoteDeviceId id, final SchemaContext schemaContext, final DOMRpcService rpc, final NetconfSessionPreferences netconfSessionPreferences) {
         this.id = id;
-        this.netconfOps = new NetconfBaseOps(rpc);
+        this.netconfOps = new NetconfBaseOps(rpc, schemaContext);
         this.netconfSessionPreferences = netconfSessionPreferences;
-        normalizer = new DataNormalizer(schemaContext);
     }
 
     @Override
     public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
-        return new ReadOnlyTx(netconfOps, normalizer, id);
+        return new ReadOnlyTx(netconfOps, id);
     }
 
     @Override
@@ -57,12 +54,12 @@ final class NetconfDeviceDataBroker implements DOMDataBroker {
     public DOMDataWriteTransaction newWriteOnlyTransaction() {
         if(netconfSessionPreferences.isCandidateSupported()) {
             if(netconfSessionPreferences.isRunningWritable()) {
-                return new WriteCandidateRunningTx(id, netconfOps, normalizer, netconfSessionPreferences);
+                return new WriteCandidateRunningTx(id, netconfOps, netconfSessionPreferences);
             } else {
-                return new WriteCandidateTx(id, netconfOps, normalizer, netconfSessionPreferences);
+                return new WriteCandidateTx(id, netconfOps, netconfSessionPreferences);
             }
         } else {
-            return new WriteRunningTx(id, netconfOps, normalizer, netconfSessionPreferences);
+            return new WriteRunningTx(id, netconfOps, netconfSessionPreferences);
         }
     }
 
index 3715969..8590c49 100644 (file)
@@ -37,6 +37,8 @@ import org.slf4j.LoggerFactory;
  * Asynchronous (Binding-aware) adapter over datastore subtree for netconf device.
  *
  * All data changes are submitted to an ExecutorService to avoid Thread blocking while sal is waiting for schema.
+ *
+ * @deprecated Data is pushed into Topology instead if Inventory model
  */
 @Deprecated
 final class NetconfDeviceDatastoreAdapter implements AutoCloseable {
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceNotificationService.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceNotificationService.java
new file mode 100644 (file)
index 0000000..bfcb4ca
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.connect.netconf.sal;
+
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import java.util.Collection;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+class NetconfDeviceNotificationService implements DOMNotificationService {
+
+    private final Multimap<SchemaPath, DOMNotificationListener> listeners = HashMultimap.create();
+
+    // Notification publish is very simple and hijacks the thread of the caller
+    // TODO shouldnt we reuse the implementation for notification router from sal-broker-impl ?
+    public synchronized void publishNotification(final ContainerNode notification) {
+        final SchemaPath schemaPath = toPath(notification.getNodeType());
+        for (final DOMNotificationListener domNotificationListener : listeners.get(schemaPath)) {
+            domNotificationListener.onNotification(new DOMNotification() {
+                @Nonnull
+                @Override
+                public SchemaPath getType() {
+                    return schemaPath;
+                }
+
+                @Nonnull
+                @Override
+                public ContainerNode getBody() {
+                    return notification;
+                }
+            });
+        }
+    }
+
+    @Override
+    public synchronized <T extends DOMNotificationListener> ListenerRegistration<T> registerNotificationListener(@Nonnull final T listener, @Nonnull final Collection<SchemaPath> types) {
+        for (final SchemaPath type : types) {
+            listeners.put(type, listener);
+        }
+
+        // FIXME this should invoke create-subscription rpc on the remote device for a given notification
+
+        return new ListenerRegistration<T>() {
+            @Override
+            public void close() {
+                for (final SchemaPath type : types) {
+                    listeners.remove(type, listener);
+                }
+            }
+
+            @Override
+            public T getInstance() {
+                return listener;
+            }
+        };
+    }
+
+    @Override
+    public synchronized <T extends DOMNotificationListener> ListenerRegistration<T> registerNotificationListener(@Nonnull final T listener, final SchemaPath... types) {
+        return registerNotificationListener(listener, Lists.newArrayList(types));
+    }
+}
index a0453bc..108b33a 100644 (file)
 package org.opendaylight.controller.sal.connect.netconf.sal;
 
 import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Collections2;
+import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
-
-import java.util.Collections;
-import java.util.Set;
-
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Collection;
+import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
-
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcAvailabilityListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.sal.connect.api.MessageTransformer;
 import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator;
-import org.opendaylight.controller.sal.core.api.RpcImplementation;
-import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-
-import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 
 /**
  * Invokes RPC by sending netconf message via listener. Also transforms result from NetconfMessage to CompositeNode.
  */
-public final class NetconfDeviceRpc implements RpcImplementation {
+public final class NetconfDeviceRpc implements DOMRpcService {
+
+    private static final Function<RpcDefinition, DOMRpcIdentifier> RPC_TO_RPC_IDENTIFIER = new Function<RpcDefinition, DOMRpcIdentifier>() {
+        @Override
+        public DOMRpcIdentifier apply(final RpcDefinition input) {
+            // TODO add support for routed rpcs ... is it necessary in this case ?
+            return DOMRpcIdentifier.create(input.getPath());
+        }
+    };
+
     private final RemoteDeviceCommunicator<NetconfMessage> listener;
     private final MessageTransformer<NetconfMessage> transformer;
+    private final Collection<DOMRpcIdentifier> availableRpcs;
 
-    public NetconfDeviceRpc(final RemoteDeviceCommunicator<NetconfMessage> listener, final MessageTransformer<NetconfMessage> transformer) {
+    public NetconfDeviceRpc(final SchemaContext schemaContext, final RemoteDeviceCommunicator<NetconfMessage> listener, final MessageTransformer<NetconfMessage> transformer) {
         this.listener = listener;
         this.transformer = transformer;
-    }
 
-    @Override
-    public Set<QName> getSupportedRpcs() {
-        // TODO is this correct ?
-        return Collections.emptySet();
+        availableRpcs = Collections2.transform(schemaContext.getOperations(), RPC_TO_RPC_IDENTIFIER);
     }
 
-    // TODO change this to work with NormalizedNode api. Then we can loose DataNormalizer from Transactions
-
+    @Nonnull
     @Override
-    public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(final QName rpc, final CompositeNode input) {
-        final NetconfMessage message = transformRequest(rpc, input);
-        final ListenableFuture<RpcResult<NetconfMessage>> delegateFutureWithPureResult = listener.sendRequest(
-                message, rpc);
+    public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(@Nonnull final SchemaPath type, @Nullable final NormalizedNode<?, ?> input) {
+        Preconditions.checkArgument(input instanceof ContainerNode, "Epc payload has to be a %s, was %s", ContainerNode.class, input);
 
+        final NetconfMessage message = transformer.toRpcRequest(type, (ContainerNode) input);
+        final ListenableFuture<RpcResult<NetconfMessage>> delegateFutureWithPureResult = listener.sendRequest(message, type.getLastComponent());
 
-        return Futures.transform(delegateFutureWithPureResult, new Function<RpcResult<NetconfMessage>, RpcResult<CompositeNode>>() {
+        final ListenableFuture<DOMRpcResult> transformed = Futures.transform(delegateFutureWithPureResult, new Function<RpcResult<NetconfMessage>, DOMRpcResult>() {
             @Override
-            public RpcResult<CompositeNode> apply(@Nullable final RpcResult<NetconfMessage> input) {
-                return transformResult(input, rpc);
+            public DOMRpcResult apply(final RpcResult<NetconfMessage> input) {
+                if (input.isSuccessful()) {
+                    return transformer.toRpcResult(input.getResult(), type);
+                } else {
+                    // TODO check whether the listener sets errors properly
+                    return new DefaultDOMRpcResult(input.getErrors());
+                }
             }
         });
-    }
 
-    private NetconfMessage transformRequest(final QName rpc, final CompositeNode input) {
-        return transformer.toRpcRequest(rpc, input);
+        return Futures.makeChecked(transformed, new Function<Exception, DOMRpcException>() {
+            @Nullable
+            @Override
+            public DOMRpcException apply(@Nullable final Exception e) {
+                // FIXME what other possible exceptions are there ?
+                return new DOMRpcImplementationNotAvailableException(e, "Unable to invoke rpc %s", type);
+            }
+        });
     }
 
-    private RpcResult<CompositeNode> transformResult(final RpcResult<NetconfMessage> netconfMessageRpcResult,
-                                                                  final QName rpc) {
-        if (netconfMessageRpcResult.isSuccessful()) {
-            return transformer.toRpcResult(netconfMessageRpcResult.getResult(), rpc);
-        } else {
-            return RpcResultBuilder.<CompositeNode> failed()
-                                      .withRpcErrors(netconfMessageRpcResult.getErrors()).build();
-        }
-    }
+    @Nonnull
+    @Override
+    public <T extends DOMRpcAvailabilityListener> ListenerRegistration<T> registerRpcListener(@Nonnull final T listener) {
+
+        listener.onRpcAvailable(availableRpcs);
 
+        return new ListenerRegistration<T>() {
+            @Override
+            public void close() {
+                // NOOP, no rpcs appear and disappear in this implementation
+            }
+
+            @Override
+            public T getInstance() {
+                return listener;
+            }
+        };
+    }
 }
index db8a238..ad16532 100644 (file)
@@ -8,31 +8,19 @@
 package org.opendaylight.controller.sal.connect.netconf.sal;
 
 import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ExecutorService;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
 import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCapabilities;
 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.controller.sal.core.api.Broker;
-import org.opendaylight.controller.sal.core.api.RpcImplementation;
-import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
-import org.opendaylight.controller.sal.core.api.notify.NotificationListener;
-import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService;
-import org.opendaylight.controller.sal.dom.broker.impl.NotificationRouterImpl;
-import org.opendaylight.controller.sal.dom.broker.impl.SchemaAwareRpcBroker;
-import org.opendaylight.controller.sal.dom.broker.spi.NotificationRouter;
-import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
 import org.osgi.framework.BundleContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -46,9 +34,9 @@ public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDevice
 
     private final List<AutoCloseable> salRegistrations = Lists.newArrayList();
 
-    public NetconfDeviceSalFacade(final RemoteDeviceId id, final Broker domBroker, final BindingAwareBroker bindingBroker, final BundleContext bundleContext, final ExecutorService executor) {
+    public NetconfDeviceSalFacade(final RemoteDeviceId id, final Broker domBroker, final BindingAwareBroker bindingBroker, final BundleContext bundleContext) {
         this.id = id;
-        this.salProvider = new NetconfDeviceSalProvider(id, executor);
+        this.salProvider = new NetconfDeviceSalProvider(id);
         registerToSal(domBroker, bindingBroker, bundleContext);
     }
 
@@ -58,43 +46,21 @@ public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDevice
     }
 
     @Override
-    public synchronized void onNotification(final CompositeNode domNotification) {
+    public synchronized void onNotification(final ContainerNode domNotification) {
         salProvider.getMountInstance().publish(domNotification);
     }
 
     @Override
     public synchronized void onDeviceConnected(final SchemaContext schemaContext,
-                                               final NetconfSessionPreferences netconfSessionPreferences, final RpcImplementation deviceRpc) {
+                                               final NetconfSessionPreferences netconfSessionPreferences, final DOMRpcService deviceRpc) {
 
-        // TODO move SchemaAwareRpcBroker from sal-broker-impl, now we have depend on the whole sal-broker-impl
-        final RpcProvisionRegistry rpcRegistry = new SchemaAwareRpcBroker(id.getPath().toString(), new SchemaContextProvider() {
-            @Override
-            public SchemaContext getSchemaContext() {
-                return schemaContext;
-            }
-        });
-        registerRpcsToSal(schemaContext, rpcRegistry, deviceRpc);
-        final DOMDataBroker domBroker = new NetconfDeviceDataBroker(id, deviceRpc, schemaContext, netconfSessionPreferences);
-
-        // TODO NotificationPublishService and NotificationRouter have the same interface
-        final NotificationPublishService notificationService = new NotificationPublishService() {
-
-            private final NotificationRouter innerRouter = new NotificationRouterImpl();
-
-            @Override
-            public void publish(final CompositeNode notification) {
-                innerRouter.publish(notification);
-            }
+        final DOMDataBroker domBroker = new NetconfDeviceDataBroker(id, schemaContext, deviceRpc, netconfSessionPreferences);
 
-            @Override
-            public ListenerRegistration<NotificationListener> addNotificationListener(final QName notification, final NotificationListener listener) {
-                return innerRouter.addNotificationListener(notification, listener);
-            }
-        };
+        final NetconfDeviceNotificationService notificationService = new NetconfDeviceNotificationService();
 
-        salProvider.getMountInstance().onDeviceConnected(schemaContext, domBroker, rpcRegistry, notificationService);
+        salProvider.getMountInstance().onDeviceConnected(schemaContext, domBroker, deviceRpc, notificationService);
         salProvider.getDatastoreAdapter().updateDeviceState(true, netconfSessionPreferences.getModuleBasedCaps());
-        salProvider.getMountInstance().onTopologyDeviceConnected(schemaContext, domBroker, rpcRegistry, notificationService);
+        salProvider.getMountInstance().onTopologyDeviceConnected(schemaContext, domBroker, deviceRpc, notificationService);
         salProvider.getTopologyDatastoreAdapter().updateDeviceData(true, netconfSessionPreferences.getNetconfDeviceCapabilities());
     }
 
@@ -107,35 +73,12 @@ public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDevice
     }
 
     @Override
-    public void onDeviceFailed(Throwable throwable) {
+    public void onDeviceFailed(final Throwable throwable) {
         salProvider.getTopologyDatastoreAdapter().setDeviceAsFailed(throwable);
         salProvider.getMountInstance().onDeviceDisconnected();
         salProvider.getMountInstance().onTopologyDeviceDisconnected();
     }
 
-    private void registerRpcsToSal(final SchemaContext schemaContext, final RpcProvisionRegistry rpcRegistry, final RpcImplementation deviceRpc) {
-        final Map<QName, String> failedRpcs = Maps.newHashMap();
-        for (final RpcDefinition rpcDef : schemaContext.getOperations()) {
-            try {
-                salRegistrations.add(rpcRegistry.addRpcImplementation(rpcDef.getQName(), deviceRpc));
-                logger.debug("{}: Rpc {} from netconf registered successfully", id, rpcDef.getQName());
-            } catch (final Exception e) {
-                // Only debug per rpc, warn for all of them at the end to pollute log a little less (e.g. routed rpcs)
-                logger.debug("{}: Unable to register rpc {} from netconf device. This rpc will not be available", id,
-                        rpcDef.getQName(), e);
-                failedRpcs.put(rpcDef.getQName(), e.getClass() + ":" + e.getMessage());
-            }
-        }
-
-        if (failedRpcs.isEmpty() == false) {
-            if (logger.isDebugEnabled()) {
-                logger.warn("{}: Some rpcs from netconf device were not registered: {}", id, failedRpcs);
-            } else {
-                logger.warn("{}: Some rpcs from netconf device were not registered: {}", id, failedRpcs.keySet());
-            }
-        }
-    }
-
     @Override
     public void close() {
         for (final AutoCloseable reg : Lists.reverse(salRegistrations)) {
@@ -153,4 +96,5 @@ public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDevice
             }
         }
     }
+
 }
index 568ebde..3246f51 100644 (file)
@@ -10,20 +10,19 @@ package org.opendaylight.controller.sal.connect.netconf.sal;
 import com.google.common.base.Preconditions;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.concurrent.ExecutorService;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
 import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.controller.sal.core.api.Broker;
 import org.opendaylight.controller.sal.core.api.Provider;
-import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
-import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService;
 import org.opendaylight.yangtools.concepts.ObjectRegistration;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -33,15 +32,13 @@ final class NetconfDeviceSalProvider implements AutoCloseable, Provider, Binding
     private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceSalProvider.class);
 
     private final RemoteDeviceId id;
-    private final ExecutorService executor;
     private volatile NetconfDeviceDatastoreAdapter datastoreAdapter;
     private MountInstance mountInstance;
 
     private volatile NetconfDeviceTopologyAdapter topologyDatastoreAdapter;
 
-    public NetconfDeviceSalProvider(final RemoteDeviceId deviceId, final ExecutorService executor) {
+    public NetconfDeviceSalProvider(final RemoteDeviceId deviceId) {
         this.id = deviceId;
-        this.executor = executor;
     }
 
     public MountInstance getMountInstance() {
@@ -100,7 +97,7 @@ final class NetconfDeviceSalProvider implements AutoCloseable, Provider, Binding
         private DOMMountPointService mountService;
         private final RemoteDeviceId id;
         private ObjectRegistration<DOMMountPoint> registration;
-        private NotificationPublishService notificationSerivce;
+        private NetconfDeviceNotificationService notificationService;
 
         private ObjectRegistration<DOMMountPoint> topologyRegistration;
 
@@ -111,8 +108,8 @@ final class NetconfDeviceSalProvider implements AutoCloseable, Provider, Binding
 
         @Deprecated
         synchronized void onDeviceConnected(final SchemaContext initialCtx,
-                final DOMDataBroker broker, final RpcProvisionRegistry rpc,
-                final NotificationPublishService notificationSerivce) {
+                final DOMDataBroker broker, final DOMRpcService rpc,
+                final NetconfDeviceNotificationService notificationService) {
 
             Preconditions.checkNotNull(mountService, "Closed");
             Preconditions.checkState(registration == null, "Already initialized");
@@ -121,9 +118,9 @@ final class NetconfDeviceSalProvider implements AutoCloseable, Provider, Binding
             mountBuilder.addInitialSchemaContext(initialCtx);
 
             mountBuilder.addService(DOMDataBroker.class, broker);
-            mountBuilder.addService(RpcProvisionRegistry.class, rpc);
-            this.notificationSerivce = notificationSerivce;
-            mountBuilder.addService(NotificationPublishService.class, notificationSerivce);
+            mountBuilder.addService(DOMRpcService.class, rpc);
+            mountBuilder.addService(DOMNotificationService.class, notificationService);
+            this.notificationService = notificationService;
 
             registration = mountBuilder.register();
         }
@@ -145,8 +142,8 @@ final class NetconfDeviceSalProvider implements AutoCloseable, Provider, Binding
         }
 
         synchronized void onTopologyDeviceConnected(final SchemaContext initialCtx,
-                final DOMDataBroker broker, final RpcProvisionRegistry rpc,
-                final NotificationPublishService notificationSerivce) {
+                                                    final DOMDataBroker broker, final DOMRpcService rpc,
+                                                    final NetconfDeviceNotificationService notificationService) {
 
             Preconditions.checkNotNull(mountService, "Closed");
             Preconditions.checkState(topologyRegistration == null, "Already initialized");
@@ -155,9 +152,8 @@ final class NetconfDeviceSalProvider implements AutoCloseable, Provider, Binding
             mountBuilder.addInitialSchemaContext(initialCtx);
 
             mountBuilder.addService(DOMDataBroker.class, broker);
-            mountBuilder.addService(RpcProvisionRegistry.class, rpc);
-            this.notificationSerivce = notificationSerivce;
-            mountBuilder.addService(NotificationPublishService.class, notificationSerivce);
+            mountBuilder.addService(DOMRpcService.class, rpc);
+            mountBuilder.addService(DOMNotificationService.class, notificationService);
 
             topologyRegistration = mountBuilder.register();
         }
@@ -186,9 +182,9 @@ final class NetconfDeviceSalProvider implements AutoCloseable, Provider, Binding
             mountService = null;
         }
 
-        public synchronized void publish(final CompositeNode domNotification) {
-            Preconditions.checkNotNull(notificationSerivce, "Device not set up yet, cannot handle notification {}", domNotification);
-            notificationSerivce.publish(domNotification);
+        public synchronized void publish(final ContainerNode domNotification) {
+            Preconditions.checkNotNull(notificationService, "Device not set up yet, cannot handle notification {}", domNotification);
+            notificationService.publishNotification(domNotification);
         }
     }
 
index 83664e4..24b6205 100644 (file)
@@ -153,9 +153,8 @@ final class NetconfDeviceTopologyAdapter implements AutoCloseable {
                 .setUnavailableCapabilities(unavailableCapabilities);
 
         final NodeBuilder nodeBuilder = getNodeIdBuilder(id);
-        final Node node = nodeBuilder.addAugmentation(NetconfNode.class, netconfNodeBuilder.build()).build();
 
-        return node;
+        return nodeBuilder.addAugmentation(NetconfNode.class, netconfNodeBuilder.build()).build();
     }
 
     public void removeDeviceConfiguration() {
index 435ef99..ee0a774 100644 (file)
@@ -1,7 +1,5 @@
 package org.opendaylight.controller.sal.connect.netconf.sal.tx;
 
-import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.createEditConfigStructure;
-
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
@@ -11,34 +9,39 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfBaseOps;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.ModifyAction;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
 public abstract class AbstractWriteTx implements DOMDataWriteTransaction {
+
+    private static final long DEFAULT_REQUEST_TIMEOUT_MINUTES = 1L;
+
     protected final RemoteDeviceId id;
     protected final NetconfBaseOps netOps;
-    protected final DataNormalizer normalizer;
     protected final NetconfSessionPreferences netconfSessionPreferences;
     // Allow commit to be called only once
     protected boolean finished = false;
 
-    public AbstractWriteTx(final NetconfBaseOps netOps, final RemoteDeviceId id, final DataNormalizer normalizer, final NetconfSessionPreferences netconfSessionPreferences) {
+    public AbstractWriteTx(final NetconfBaseOps netOps, final RemoteDeviceId id, final NetconfSessionPreferences netconfSessionPreferences) {
         this.netOps = netOps;
         this.id = id;
-        this.normalizer = normalizer;
         this.netconfSessionPreferences = netconfSessionPreferences;
         init();
     }
 
+    static boolean isSuccess(final DOMRpcResult result) {
+        return result.getErrors().isEmpty();
+    }
+
     protected void checkNotFinished() {
         Preconditions.checkState(!isFinished(), "%s: Transaction %s already finished", id, getIdentifier());
     }
@@ -47,10 +50,10 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction {
         return finished;
     }
 
-    protected void invokeBlocking(final String msg, final Function<NetconfBaseOps, ListenableFuture<RpcResult<CompositeNode>>> op) throws NetconfDocumentedException {
+    protected void invokeBlocking(final String msg, final Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>> op) throws NetconfDocumentedException {
         try {
-            final RpcResult<CompositeNode> compositeNodeRpcResult = op.apply(netOps).get(1L, TimeUnit.MINUTES);
-            if(compositeNodeRpcResult.isSuccessful() == false) {
+            final DOMRpcResult compositeNodeRpcResult = op.apply(netOps).get(DEFAULT_REQUEST_TIMEOUT_MINUTES, TimeUnit.MINUTES);
+            if(isSuccess(compositeNodeRpcResult) == false) {
                 throw new NetconfDocumentedException(id + ": " + msg + " failed: " + compositeNodeRpcResult.getErrors(), NetconfDocumentedException.ErrorType.application,
                         NetconfDocumentedException.ErrorTag.operation_failed, NetconfDocumentedException.ErrorSeverity.warning);
             }
@@ -88,10 +91,8 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction {
         checkEditable(store);
 
         try {
-            final YangInstanceIdentifier legacyPath = ReadOnlyTx.toLegacyPath(normalizer, path, id);
-            final CompositeNode legacyData = normalizer.toLegacy(path, data);
             editConfig(
-                    createEditConfigStructure(legacyPath, Optional.of(ModifyAction.REPLACE), Optional.fromNullable(legacyData)), Optional.of(ModifyAction.NONE));
+                    netOps.createEditConfigStrcture(Optional.<NormalizedNode<?, ?>>fromNullable(data), Optional.of(ModifyAction.REPLACE), path), Optional.of(ModifyAction.NONE));
         } catch (final NetconfDocumentedException e) {
             handleEditException(path, data, e, "putting");
         }
@@ -105,10 +106,8 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction {
         checkEditable(store);
 
         try {
-            final YangInstanceIdentifier legacyPath = ReadOnlyTx.toLegacyPath(normalizer, path, id);
-            final CompositeNode legacyData = normalizer.toLegacy(path, data);
             editConfig(
-                    createEditConfigStructure(legacyPath, Optional.<ModifyAction>absent(), Optional.fromNullable(legacyData)), Optional.<ModifyAction>absent());
+                    netOps.createEditConfigStrcture(Optional.<NormalizedNode<?, ?>>fromNullable(data), Optional.<ModifyAction>absent(), path), Optional.<ModifyAction>absent());
         } catch (final NetconfDocumentedException e) {
             handleEditException(path, data, e, "merge");
         }
@@ -119,9 +118,8 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction {
         checkEditable(store);
 
         try {
-            editConfig(createEditConfigStructure(
-                    ReadOnlyTx.toLegacyPath(normalizer, path, id), Optional.of(ModifyAction.DELETE),
-                    Optional.<CompositeNode>absent()), Optional.of(ModifyAction.NONE));
+            editConfig(
+                    netOps.createEditConfigStrcture(Optional.<NormalizedNode<?, ?>>absent(), Optional.of(ModifyAction.DELETE), path), Optional.of(ModifyAction.NONE));
         } catch (final NetconfDocumentedException e) {
             handleDeleteException(path, e);
         }
@@ -142,5 +140,5 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction {
         Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "Can edit only configuration data, not %s", store);
     }
 
-    protected abstract void editConfig(CompositeNode editStructure, Optional<ModifyAction> defaultOperation) throws NetconfDocumentedException;
+    protected abstract void editConfig(DataContainerChild<?, ?> editStructure, Optional<ModifyAction> defaultOperation) throws NetconfDocumentedException;
 }
index 00bdbb6..e08731e 100644 (file)
@@ -7,8 +7,6 @@
  */
 package org.opendaylight.controller.sal.connect.netconf.sal.tx;
 
-import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DATA_QNAME;
-
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
@@ -19,17 +17,17 @@ import com.google.common.util.concurrent.ListenableFuture;
 import java.util.concurrent.ExecutionException;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
-import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
-import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfBaseOps;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.yangtools.util.concurrent.MappingCheckedFuture;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -39,19 +37,18 @@ public final class ReadOnlyTx implements DOMDataReadOnlyTransaction {
     private static final Logger LOG  = LoggerFactory.getLogger(ReadOnlyTx.class);
 
     private final NetconfBaseOps netconfOps;
-    private final DataNormalizer normalizer;
     private final RemoteDeviceId id;
-    private final FutureCallback<RpcResult<CompositeNode>> loggingCallback;
+    private final FutureCallback<DOMRpcResult> loggingCallback;
 
-    public ReadOnlyTx(final NetconfBaseOps netconfOps, final DataNormalizer normalizer, final RemoteDeviceId id) {
+    public ReadOnlyTx(final NetconfBaseOps netconfOps, final RemoteDeviceId id) {
         this.netconfOps = netconfOps;
-        this.normalizer = normalizer;
         this.id = id;
+
         // Simple logging callback to log result of read operation
-        loggingCallback = new FutureCallback<RpcResult<CompositeNode>>() {
+        loggingCallback = new FutureCallback<DOMRpcResult>() {
             @Override
-            public void onSuccess(final RpcResult<CompositeNode> result) {
-                if(result.isSuccessful()) {
+            public void onSuccess(final DOMRpcResult result) {
+                if(AbstractWriteTx.isSuccess(result)) {
                     LOG.trace("{}: Reading data successful", id);
                 } else {
                     LOG.warn("{}: Reading data unsuccessful: {}", id, result.getErrors());
@@ -68,62 +65,46 @@ public final class ReadOnlyTx implements DOMDataReadOnlyTransaction {
 
     private CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> readConfigurationData(
             final YangInstanceIdentifier path) {
-        final ListenableFuture<RpcResult<CompositeNode>> configRunning = netconfOps.getConfigRunning(loggingCallback, Optional.fromNullable(path));
-        // Find data node and normalize its content
-        final ListenableFuture<Optional<NormalizedNode<?, ?>>> transformedFuture = Futures.transform(configRunning, new Function<RpcResult<CompositeNode>, Optional<NormalizedNode<?, ?>>>() {
+        final ListenableFuture<DOMRpcResult> configRunning = netconfOps.getConfigRunning(loggingCallback, Optional.fromNullable(path));
+
+        final ListenableFuture<Optional<NormalizedNode<?, ?>>> transformedFuture = Futures.transform(configRunning, new Function<DOMRpcResult, Optional<NormalizedNode<?, ?>>>() {
             @Override
-            public Optional<NormalizedNode<?, ?>> apply(final RpcResult<CompositeNode> result) {
+            public Optional<NormalizedNode<?, ?>> apply(final DOMRpcResult result) {
                 checkReadSuccess(result, path);
 
-                final CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME);
-                final CompositeNode node = (CompositeNode) NetconfMessageTransformUtil.findNode(data, path);
-
-                return data == null ?
-                        Optional.<NormalizedNode<?, ?>>absent() :
-                        transform(path, node);
+                final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> dataNode = findDataNode(result);
+                return NormalizedNodes.findNode(dataNode, path.getPathArguments());
             }
         });
 
         return MappingCheckedFuture.create(transformedFuture, ReadFailedException.MAPPER);
     }
 
-    private void checkReadSuccess(final RpcResult<CompositeNode> result, final YangInstanceIdentifier path) {
-        try {
-            Preconditions.checkArgument(result.isSuccessful(), "%s: Unable to read data: %s, errors: %s", id, path, result.getErrors());
-        } catch (final IllegalArgumentException e) {
-            LOG.warn("{}: Unable to read data: {}, errors: {}", id, path, result.getErrors());
-            throw e;
-        }
+    private DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> findDataNode(final DOMRpcResult result) {
+        return ((ContainerNode) result.getResult()).getChild(NetconfMessageTransformUtil.toId(NetconfMessageTransformUtil.NETCONF_DATA_QNAME)).get();
     }
 
-    private Optional<NormalizedNode<?, ?>> transform(final YangInstanceIdentifier path, final CompositeNode node) {
-        if(node == null) {
-            return Optional.absent();
-        }
+    private void checkReadSuccess(final DOMRpcResult result, final YangInstanceIdentifier path) {
         try {
-            return Optional.<NormalizedNode<?, ?>>of(normalizer.toNormalized(path, node).getValue());
-        } catch (final Exception e) {
-            LOG.error("{}: Unable to normalize data for {}, data: {}", id, path, node, e);
+            Preconditions.checkArgument(AbstractWriteTx.isSuccess(result), "%s: Unable to read data: %s, errors: %s", id, path, result.getErrors());
+        } catch (final IllegalArgumentException e) {
+            LOG.warn("{}: Unable to read data: {}, errors: {}", id, path, result.getErrors());
             throw e;
         }
     }
 
     private CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> readOperationalData(
             final YangInstanceIdentifier path) {
-        final ListenableFuture<RpcResult<CompositeNode>> configCandidate = netconfOps.get(loggingCallback, Optional.fromNullable(path));
+        final ListenableFuture<DOMRpcResult> configCandidate = netconfOps.get(loggingCallback, Optional.fromNullable(path));
 
         // Find data node and normalize its content
-        final ListenableFuture<Optional<NormalizedNode<?, ?>>> transformedFuture = Futures.transform(configCandidate, new Function<RpcResult<CompositeNode>, Optional<NormalizedNode<?, ?>>>() {
+        final ListenableFuture<Optional<NormalizedNode<?, ?>>> transformedFuture = Futures.transform(configCandidate, new Function<DOMRpcResult, Optional<NormalizedNode<?, ?>>>() {
             @Override
-            public Optional<NormalizedNode<?, ?>> apply(final RpcResult<CompositeNode> result) {
+            public Optional<NormalizedNode<?, ?>> apply(final DOMRpcResult result) {
                 checkReadSuccess(result, path);
 
-                final CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME);
-                final CompositeNode node = (CompositeNode) NetconfMessageTransformUtil.findNode(data, path);
-
-                return data == null ?
-                        Optional.<NormalizedNode<?, ?>>absent() :
-                        transform(path, node);
+                final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> dataNode = findDataNode(result);
+                return NormalizedNodes.findNode(dataNode, path.getPathArguments());
             }
         });
 
@@ -138,14 +119,12 @@ public final class ReadOnlyTx implements DOMDataReadOnlyTransaction {
     @Override
     public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(
             final LogicalDatastoreType store, final YangInstanceIdentifier path) {
-        final YangInstanceIdentifier legacyPath = toLegacyPath(normalizer, path, id);
-
         switch (store) {
             case CONFIGURATION : {
-                return readConfigurationData(legacyPath);
+                return readConfigurationData(path);
             }
             case OPERATIONAL : {
-                return readOperationalData(legacyPath);
+                return readOperationalData(path);
             }
         }
 
@@ -154,8 +133,7 @@ public final class ReadOnlyTx implements DOMDataReadOnlyTransaction {
 
     @Override
     public CheckedFuture<Boolean, ReadFailedException> exists(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
-        final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException>
-            data = read(store, path);
+        final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> data = read(store, path);
 
         try {
             return Futures.immediateCheckedFuture(data.get().isPresent());
@@ -164,14 +142,6 @@ public final class ReadOnlyTx implements DOMDataReadOnlyTransaction {
         }
     }
 
-    static YangInstanceIdentifier toLegacyPath(final DataNormalizer normalizer, final YangInstanceIdentifier path, final RemoteDeviceId id) {
-        try {
-            return normalizer.toLegacy(path);
-        } catch (final DataNormalizationException e) {
-            throw new IllegalArgumentException(id + ": Cannot normalize path " + path, e);
-        }
-    }
-
     @Override
     public Object getIdentifier() {
         return this;
index 710700b..33ca5f4 100644 (file)
@@ -10,14 +10,12 @@ package org.opendaylight.controller.sal.connect.netconf.sal.tx;
 
 import com.google.common.base.Function;
 import com.google.common.util.concurrent.ListenableFuture;
-import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfBaseOps;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfRpcFutureCallback;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -32,8 +30,8 @@ public class WriteCandidateRunningTx extends WriteCandidateTx {
 
     private static final Logger LOG  = LoggerFactory.getLogger(WriteCandidateRunningTx.class);
 
-    public WriteCandidateRunningTx(final RemoteDeviceId id, final NetconfBaseOps netOps, final DataNormalizer normalizer, final NetconfSessionPreferences netconfSessionPreferences) {
-        super(id, netOps, normalizer, netconfSessionPreferences);
+    public WriteCandidateRunningTx(final RemoteDeviceId id, final NetconfBaseOps netOps, final NetconfSessionPreferences netconfSessionPreferences) {
+        super(id, netOps, netconfSessionPreferences);
     }
 
     @Override
@@ -50,9 +48,9 @@ public class WriteCandidateRunningTx extends WriteCandidateTx {
 
     private void lockRunning() {
         try {
-            invokeBlocking("Lock running", new Function<NetconfBaseOps, ListenableFuture<RpcResult<CompositeNode>>>() {
+            invokeBlocking("Lock running", new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
                 @Override
-                public ListenableFuture<RpcResult<CompositeNode>> apply(final NetconfBaseOps input) {
+                public ListenableFuture<DOMRpcResult> apply(final NetconfBaseOps input) {
                     return input.lockRunning(new NetconfRpcFutureCallback("Lock running", id));
                 }
             });
index f9bf3c7..cad65ff 100644 (file)
@@ -15,7 +15,7 @@ import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
-import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfBaseOps;
@@ -24,9 +24,9 @@ import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.yangtools.yang.common.RpcError;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.ModifyAction;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -53,10 +53,10 @@ public class WriteCandidateTx extends AbstractWriteTx {
 
     private static final Logger LOG  = LoggerFactory.getLogger(WriteCandidateTx.class);
 
-    private static final Function<RpcResult<CompositeNode>, RpcResult<TransactionStatus>> RPC_RESULT_TO_TX_STATUS = new Function<RpcResult<CompositeNode>, RpcResult<TransactionStatus>>() {
+    private static final Function<DOMRpcResult, RpcResult<TransactionStatus>> RPC_RESULT_TO_TX_STATUS = new Function<DOMRpcResult, RpcResult<TransactionStatus>>() {
         @Override
-        public RpcResult<TransactionStatus> apply(final RpcResult<CompositeNode> input) {
-            if (input.isSuccessful()) {
+        public RpcResult<TransactionStatus> apply(final DOMRpcResult input) {
+            if (isSuccess(input)) {
                 return RpcResultBuilder.success(TransactionStatus.COMMITED).build();
             } else {
                 final RpcResultBuilder<TransactionStatus> failed = RpcResultBuilder.failed();
@@ -69,8 +69,8 @@ public class WriteCandidateTx extends AbstractWriteTx {
         }
     };
 
-    public WriteCandidateTx(final RemoteDeviceId id, final NetconfBaseOps rpc, final DataNormalizer normalizer, final NetconfSessionPreferences netconfSessionPreferences) {
-        super(rpc, id, normalizer, netconfSessionPreferences);
+    public WriteCandidateTx(final RemoteDeviceId id, final NetconfBaseOps rpc, final NetconfSessionPreferences netconfSessionPreferences) {
+        super(rpc, id, netconfSessionPreferences);
     }
 
     @Override
@@ -94,9 +94,9 @@ public class WriteCandidateTx extends AbstractWriteTx {
 
     private void lock() throws NetconfDocumentedException {
         try {
-            invokeBlocking("Lock candidate", new Function<NetconfBaseOps, ListenableFuture<RpcResult<CompositeNode>>>() {
+            invokeBlocking("Lock candidate", new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
                 @Override
-                public ListenableFuture<RpcResult<CompositeNode>> apply(final NetconfBaseOps input) {
+                public ListenableFuture<DOMRpcResult> apply(final NetconfBaseOps input) {
                     return input.lockCandidate(new NetconfRpcFutureCallback("Lock candidate", id));
                 }
             });
@@ -152,16 +152,16 @@ public class WriteCandidateTx extends AbstractWriteTx {
 
     @Override
     public synchronized ListenableFuture<RpcResult<TransactionStatus>> performCommit() {
-        final ListenableFuture<RpcResult<CompositeNode>> rpcResult = netOps.commit(new NetconfRpcFutureCallback("Commit", id) {
+        final ListenableFuture<DOMRpcResult> rpcResult = netOps.commit(new NetconfRpcFutureCallback("Commit", id) {
             @Override
-            public void onSuccess(final RpcResult<CompositeNode> result) {
+            public void onSuccess(final DOMRpcResult result) {
                 super.onSuccess(result);
                 LOG.debug("{}: Write successful, transaction: {}. Unlocking", id, getIdentifier());
                 cleanupOnSuccess();
             }
 
             @Override
-            protected void onUnsuccess(final RpcResult<CompositeNode> result) {
+            protected void onUnsuccess(final DOMRpcResult result) {
                 LOG.error("{}: Write failed, transaction {}, discarding changes, unlocking: {}", id, getIdentifier(), result.getErrors());
                 cleanup();
             }
@@ -181,10 +181,10 @@ public class WriteCandidateTx extends AbstractWriteTx {
     }
 
     @Override
-    protected void editConfig(final CompositeNode editStructure, final Optional<ModifyAction> defaultOperation) throws NetconfDocumentedException {
-        invokeBlocking("Edit candidate", new Function<NetconfBaseOps, ListenableFuture<RpcResult<CompositeNode>>>() {
+    protected void editConfig(final DataContainerChild<?, ?> editStructure, final Optional<ModifyAction> defaultOperation) throws NetconfDocumentedException {
+        invokeBlocking("Edit candidate", new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
             @Override
-            public ListenableFuture<RpcResult<CompositeNode>> apply(final NetconfBaseOps input) {
+            public ListenableFuture<DOMRpcResult> apply(final NetconfBaseOps input) {
                         return defaultOperation.isPresent()
                                 ? input.editConfigCandidate(new NetconfRpcFutureCallback("Edit candidate", id), editStructure, defaultOperation.get(),
                                 netconfSessionPreferences.isRollbackSupported())
index f92e40f..0023680 100644 (file)
@@ -15,7 +15,7 @@ import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
-import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfBaseOps;
@@ -23,9 +23,9 @@ import org.opendaylight.controller.sal.connect.netconf.util.NetconfRpcFutureCall
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.ModifyAction;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -50,8 +50,8 @@ public class WriteRunningTx extends AbstractWriteTx {
     private static final Logger LOG  = LoggerFactory.getLogger(WriteRunningTx.class);
 
     public WriteRunningTx(final RemoteDeviceId id, final NetconfBaseOps netOps,
-                          final DataNormalizer normalizer, final NetconfSessionPreferences netconfSessionPreferences) {
-        super(netOps, id, normalizer, netconfSessionPreferences);
+                          final NetconfSessionPreferences netconfSessionPreferences) {
+        super(netOps, id, netconfSessionPreferences);
     }
 
     @Override
@@ -61,9 +61,9 @@ public class WriteRunningTx extends AbstractWriteTx {
 
     private void lock() {
         try {
-            invokeBlocking("Lock running", new Function<NetconfBaseOps, ListenableFuture<RpcResult<CompositeNode>>>() {
+            invokeBlocking("Lock running", new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
                 @Override
-                public ListenableFuture<RpcResult<CompositeNode>> apply(final NetconfBaseOps input) {
+                public ListenableFuture<DOMRpcResult> apply(final NetconfBaseOps input) {
                     return input.lockRunning(new NetconfRpcFutureCallback("Lock running", id));
                 }
             });
@@ -117,10 +117,10 @@ public class WriteRunningTx extends AbstractWriteTx {
     }
 
     @Override
-    protected void editConfig(final CompositeNode editStructure, final Optional<ModifyAction> defaultOperation) throws NetconfDocumentedException {
-        invokeBlocking("Edit running", new Function<NetconfBaseOps, ListenableFuture<RpcResult<CompositeNode>>>() {
+    protected void editConfig(final DataContainerChild<?, ?> editStructure, final Optional<ModifyAction> defaultOperation) throws NetconfDocumentedException {
+        invokeBlocking("Edit running", new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
             @Override
-            public ListenableFuture<RpcResult<CompositeNode>> apply(final NetconfBaseOps input) {
+            public ListenableFuture<DOMRpcResult> apply(final NetconfBaseOps input) {
                         return defaultOperation.isPresent()
                                 ? input.editConfigRunning(new NetconfRpcFutureCallback("Edit running", id), editStructure, defaultOperation.get(),
                                 netconfSessionPreferences.isRollbackSupported())
@@ -132,9 +132,9 @@ public class WriteRunningTx extends AbstractWriteTx {
 
     private void unlock() {
         try {
-            invokeBlocking("Unlocking running", new Function<NetconfBaseOps, ListenableFuture<RpcResult<CompositeNode>>>() {
+            invokeBlocking("Unlocking running", new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
                 @Override
-                public ListenableFuture<RpcResult<CompositeNode>> apply(final NetconfBaseOps input) {
+                public ListenableFuture<DOMRpcResult> apply(final NetconfBaseOps input) {
                     return input.unlockRunning(new NetconfRpcFutureCallback("Unlock running", id));
                 }
             });
index 3565ba1..fdb1d3d 100644 (file)
@@ -7,6 +7,10 @@
  */
 package org.opendaylight.controller.sal.connect.netconf.schema;
 
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.GET_SCHEMA_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DATA_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toId;
+
 import com.google.common.base.Function;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Optional;
@@ -17,16 +21,22 @@ import com.google.common.util.concurrent.ListenableFuture;
 import java.io.IOException;
 import java.io.InputStream;
 import org.apache.commons.io.IOUtils;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
-import org.opendaylight.controller.sal.core.api.RpcImplementation;
 import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.SimpleNode;
-import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
-import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
@@ -36,9 +46,6 @@ import org.slf4j.LoggerFactory;
 
 public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSourceProvider<YangTextSchemaSource> {
 
-    public static final QName GET_SCHEMA_QNAME = QName.create(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING,"get-schema");
-    public static final QName GET_DATA_QNAME = QName.create(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING, "data");
-
     private static final Logger logger = LoggerFactory.getLogger(NetconfRemoteSchemaYangSourceProvider.class);
 
     private static final ExceptionMapper<SchemaSourceException> MAPPER = new ExceptionMapper<SchemaSourceException>(
@@ -49,35 +56,55 @@ public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSource
         }
     };
 
-    private final RpcImplementation rpc;
+    private final DOMRpcService rpc;
     private final RemoteDeviceId id;
 
-    public NetconfRemoteSchemaYangSourceProvider(final RemoteDeviceId id, final RpcImplementation rpc) {
+    public NetconfRemoteSchemaYangSourceProvider(final RemoteDeviceId id, final DOMRpcService rpc) {
         this.id = id;
         this.rpc = Preconditions.checkNotNull(rpc);
     }
 
-    private ImmutableCompositeNode createGetSchemaRequest(final String moduleName, final Optional<String> revision) {
-        final CompositeNodeBuilder<ImmutableCompositeNode> request = ImmutableCompositeNode.builder();
-        request.setQName(GET_SCHEMA_QNAME).addLeaf("identifier", moduleName);
-        if (revision.isPresent()) {
-            request.addLeaf("version", revision.get());
+    public static ContainerNode createGetSchemaRequest(final String moduleName, final Optional<String> revision) {
+        final QName identifierQName = QName.cachedReference(QName.create(NetconfMessageTransformUtil.GET_SCHEMA_QNAME, "identifier"));
+        final YangInstanceIdentifier.NodeIdentifier identifierId = new YangInstanceIdentifier.NodeIdentifier(identifierQName);
+        final LeafNode<String> identifier = Builders.<String>leafBuilder().withNodeIdentifier(identifierId).withValue(moduleName).build();
+
+        final QName formatQName = QName.cachedReference(QName.create(NetconfMessageTransformUtil.GET_SCHEMA_QNAME, "format"));
+        final YangInstanceIdentifier.NodeIdentifier formatId = new YangInstanceIdentifier.NodeIdentifier(formatQName);
+        final LeafNode<String> format = Builders.<String>leafBuilder().withNodeIdentifier(formatId).withValue("yang").build();
+
+        final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> builder = Builders.containerBuilder();
+
+        builder.withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NetconfMessageTransformUtil.GET_SCHEMA_QNAME))
+        .withChild(identifier)
+        .withChild(format);
+
+        if(revision.isPresent()) {
+            final QName revisionQName = QName.cachedReference(QName.create(NetconfMessageTransformUtil.GET_SCHEMA_QNAME, "version"));
+            final YangInstanceIdentifier.NodeIdentifier revisionId = new YangInstanceIdentifier.NodeIdentifier(revisionQName);
+            final LeafNode<String> revisionNode = Builders.<String>leafBuilder().withNodeIdentifier(revisionId).withValue(revision.get()).build();
+
+            builder.withChild(revisionNode);
         }
-        request.addLeaf("format", "yang");
-        return request.toInstance();
+
+        return builder.build();
     }
 
-    private static Optional<String> getSchemaFromRpc(final RemoteDeviceId id, final CompositeNode result) {
+    private static Optional<String> getSchemaFromRpc(final RemoteDeviceId id, final NormalizedNode<?, ?> result) {
         if (result == null) {
             return Optional.absent();
         }
-        final SimpleNode<?> simpleNode = result.getFirstSimpleByName(GET_DATA_QNAME.withoutRevision());
 
-        Preconditions.checkNotNull(simpleNode,
+        final QName schemaWrapperNode = QName.cachedReference(QName.create(GET_SCHEMA_QNAME, NETCONF_DATA_QNAME.getLocalName()));
+        final Optional<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> child = ((ContainerNode) result).getChild(toId(schemaWrapperNode));
+
+        Preconditions.checkState(child.isPresent() && child.get() instanceof AnyXmlNode,
                 "%s Unexpected response to get-schema, expected response with one child %s, but was %s", id,
-                GET_DATA_QNAME.withoutRevision(), result);
+                schemaWrapperNode, result);
+
+        final Node<?> wrappedNode = (Node<?>) child.get().getValue();
+        final Object potential = wrappedNode.getValue();
 
-        final Object potential = simpleNode.getValue();
         return potential instanceof String ? Optional.of((String) potential) : Optional.<String> absent();
     }
 
@@ -88,18 +115,19 @@ public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSource
         // If formatted revision is SourceIdentifier.NOT_PRESENT_FORMATTED_REVISION, we have to omit it from request
         final String formattedRevision = sourceIdentifier.getRevision().equals(SourceIdentifier.NOT_PRESENT_FORMATTED_REVISION) ? null : sourceIdentifier.getRevision();
         final Optional<String> revision = Optional.fromNullable(formattedRevision);
-        final ImmutableCompositeNode getSchemaRequest = createGetSchemaRequest(moduleName, revision);
+        final NormalizedNode<?, ?> getSchemaRequest = createGetSchemaRequest(moduleName, revision);
 
         logger.trace("{}: Loading YANG schema source for {}:{}", id, moduleName, revision);
 
         final ListenableFuture<YangTextSchemaSource> transformed = Futures.transform(
-                rpc.invokeRpc(GET_SCHEMA_QNAME, getSchemaRequest),
+                rpc.invokeRpc(SchemaPath.create(true, NetconfMessageTransformUtil.GET_SCHEMA_QNAME), getSchemaRequest),
                 new ResultToYangSourceTransformer(id, sourceIdentifier, moduleName, revision));
 
         final CheckedFuture<YangTextSchemaSource, SchemaSourceException> checked = Futures.makeChecked(transformed, MAPPER);
 
         // / FIXME remove this get, it is only present to wait until source is retrieved
         // (goal is to limit concurrent schema download, since NetconfDevice listener does not handle concurrent messages properly)
+        // TODO retest this
         try {
             logger.trace("{}: Blocking for {}", id, sourceIdentifier);
             checked.checkedGet();
@@ -114,7 +142,7 @@ public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSource
      * Transform composite node to string schema representation and then to ASTSchemaSource
      */
     private static final class ResultToYangSourceTransformer implements
-            Function<RpcResult<CompositeNode>, YangTextSchemaSource> {
+            Function<DOMRpcResult, YangTextSchemaSource> {
 
         private final RemoteDeviceId id;
         private final SourceIdentifier sourceIdentifier;
@@ -130,9 +158,9 @@ public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSource
         }
 
         @Override
-        public YangTextSchemaSource apply(final RpcResult<CompositeNode> input) {
+        public YangTextSchemaSource apply(final DOMRpcResult input) {
 
-            if (input.isSuccessful()) {
+            if (input.getErrors().isEmpty()) {
 
                 final Optional<String> schemaString = getSchemaFromRpc(id, input.getResult());
 
@@ -140,7 +168,6 @@ public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSource
                         "%s: Unexpected response to get-schema, schema not present in message for: %s", id, sourceIdentifier);
 
                 logger.debug("{}: YANG Schema successfully retrieved for {}:{}", id, moduleName, revision);
-
                 return new NetconfYangTextSchemaSource(id, sourceIdentifier, schemaString);
             }
 
@@ -150,7 +177,6 @@ public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSource
             throw new IllegalStateException(String.format(
                     "%s: YANG schema was not successfully retrieved for %s. Errors: %s", id, sourceIdentifier,
                     input.getErrors()));
-
         }
 
     }
index 2971865..cfb302d 100644 (file)
  */
 package org.opendaylight.controller.sal.connect.netconf.schema.mapping;
 
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CONFIG_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_FILTER_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_RPC_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_TYPE_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_URI;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toId;
+
+import com.google.common.base.Function;
 import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.Set;
-import javax.activation.UnsupportedDataTypeException;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.dom.DOMResult;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.util.exception.MissingNameSpaceException;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.opendaylight.controller.sal.connect.api.MessageTransformer;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
 import org.opendaylight.controller.sal.connect.util.MessageCounter;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.edit.config.input.EditContent;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
-import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
-import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider;
-import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
-import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlUtils;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
+import org.w3c.dom.Node;
 
 public class NetconfMessageTransformer implements MessageTransformer<NetconfMessage> {
 
     public static final String MESSAGE_ID_PREFIX = "m";
 
-    private Optional<SchemaContext> schemaContext = Optional.absent();
+    private static final Logger LOG= LoggerFactory.getLogger(NetconfMessageTransformer.class);
+
+    private static final DomToNormalizedNodeParserFactory NORMALIZED_NODE_PARSER_FACTORY = DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER);
+
+    private static final Function<SchemaNode, QName> QNAME_FUNCTION = new Function<SchemaNode, QName>() {
+        @Override
+        public QName apply(final SchemaNode rpcDefinition) {
+            return rpcDefinition.getQName();
+        }
+    };
+
+    private static final Function<SchemaNode, QName> QNAME_NOREV_FUNCTION = new Function<SchemaNode, QName>() {
+        @Override
+        public QName apply(final SchemaNode notification) {
+            return QNAME_FUNCTION.apply(notification).withoutRevision();
+        }
+    };
+    private static final SchemaContext BASE_NETCONF_CTX;
+
+    static {
+        try {
+            final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create();
+            // TODO this should be used only if the base is not present
+            moduleInfoBackedContext.addModuleInfos(
+                    Lists.newArrayList(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.$YangModuleInfoImpl.getInstance()));
+            BASE_NETCONF_CTX = moduleInfoBackedContext.tryToCreateSchemaContext().get();
+        } catch (final RuntimeException e) {
+            LOG.error("Unable to prepare schema context for base netconf ops", e);
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
+    private final SchemaContext schemaContext;
     private final MessageCounter counter;
+    private final Map<QName, RpcDefinition> mappedRpcs;
+    private final Multimap<QName, NotificationDefinition> mappedNotifications;
 
-    public NetconfMessageTransformer() {
+    public NetconfMessageTransformer(final SchemaContext schemaContext) {
         this.counter = new MessageCounter();
+        this.schemaContext = schemaContext;
+
+        mappedRpcs = Maps.uniqueIndex(schemaContext.getOperations(), QNAME_FUNCTION);
+        mappedNotifications = Multimaps.index(schemaContext.getNotifications(), QNAME_NOREV_FUNCTION);
     }
 
     @Override
-    public synchronized CompositeNode toNotification(final NetconfMessage message) {
-        if(schemaContext.isPresent()) {
-            return toNotification(message, schemaContext.get());
-        } else {
-            return XmlDocumentUtils.notificationToDomNodes(message.getDocument(), Optional.<Set<NotificationDefinition>>absent());
+    public synchronized ContainerNode toNotification(final NetconfMessage message) {
+        final XmlElement stripped = stripNotification(message);
+        final QName notificationNoRev;
+        try {
+            // How to construct QName with no revision ?
+            notificationNoRev = QName.cachedReference(QName.create(stripped.getNamespace(), "0000-00-00", stripped.getName()).withoutRevision());
+        } catch (final MissingNameSpaceException e) {
+            throw new IllegalArgumentException("Unable to parse notification " + message + ", cannot find namespace", e);
         }
+
+        final Collection<NotificationDefinition> notificationDefinitions = mappedNotifications.get(notificationNoRev);
+        Preconditions.checkArgument(notificationDefinitions.size() > 0,
+                "Unable to parse notification %s, unknown notification. Available notifications: %s", notificationDefinitions, mappedNotifications.keySet());
+
+        // FIXME if multiple revisions for same notifications are present, we should pick the most recent. Or ?
+        // We should probably just put the most recent notification versions into our map. We can expect that the device sends the data according to the latest available revision of a model.
+        final NotificationDefinition next = notificationDefinitions.iterator().next();
+
+        // We wrap the notification as a container node in order to reuse the parsers and builders for container node
+        final ContainerSchemaNode notificationAsContainerSchemaNode = NetconfMessageTransformUtil.createSchemaForNotification(next);
+        return NORMALIZED_NODE_PARSER_FACTORY.getContainerNodeParser().parse(Collections.singleton(stripped.getDomElement()), notificationAsContainerSchemaNode);
     }
 
-    private static CompositeNode toNotification(final NetconfMessage message, final SchemaContext ctx) {
-        final Set<NotificationDefinition> notifications = ctx.getNotifications();
-        final Document document = message.getDocument();
-        return XmlDocumentUtils.notificationToDomNodes(document, Optional.fromNullable(notifications), ctx);
+    // FIXME move somewhere to util
+    private static XmlElement stripNotification(final NetconfMessage message) {
+        final XmlElement xmlElement = XmlElement.fromDomDocument(message.getDocument());
+        final List<XmlElement> childElements = xmlElement.getChildElements();
+        Preconditions.checkArgument(childElements.size() == 2, "Unable to parse notification %s, unexpected format", message);
+        try {
+            return Iterables.find(childElements, new Predicate<XmlElement>() {
+                @Override
+                public boolean apply(final XmlElement xmlElement) {
+                    return !xmlElement.getName().equals("eventTime");
+                }
+            });
+        } catch (final NoSuchElementException e) {
+            throw new IllegalArgumentException("Unable to parse notification " + message + ", cannot strip notification metadata", e);
+        }
     }
 
     @Override
-    public NetconfMessage toRpcRequest(final QName rpc, final CompositeNode node) {
-        final CompositeNodeTOImpl rpcPayload = NetconfMessageTransformUtil.wrap(
-                NetconfMessageTransformUtil.NETCONF_RPC_QNAME, NetconfMessageTransformUtil.flattenInput(node));
-        final Document w3cPayload;
+    public NetconfMessage toRpcRequest(SchemaPath rpc, final ContainerNode payload) {
+        // In case no input for rpc is defined, we can simply construct the payload here
+        final QName rpcQName = rpc.getLastComponent();
+        Preconditions.checkNotNull(mappedRpcs.get(rpcQName), "Unknown rpc %s, available rpcs: %s", rpcQName, mappedRpcs.keySet());
+        if(mappedRpcs.get(rpcQName).getInput() == null) {
+            final Document document = XmlUtil.newDocument();
+            final Element elementNS = document.createElementNS(rpcQName.getNamespace().toString(), rpcQName.getLocalName());
+            document.appendChild(elementNS);
+            return new NetconfMessage(document);
+        }
+
+        // Set the path to the input of rpc for the node stream writer
+        rpc = rpc.createChild(QName.cachedReference(QName.create(rpcQName, "input")));
+        final DOMResult result = prepareDomResultForRpcRequest(rpcQName);
+
+        try {
+            final SchemaContext baseNetconfCtx = schemaContext.findModuleByNamespace(NETCONF_URI).isEmpty() ? BASE_NETCONF_CTX : schemaContext;
+            if(NetconfMessageTransformUtil.isDataEditOperation(rpcQName)) {
+                writeNormalizedEdit(payload, result, rpc, baseNetconfCtx);
+            } else if(NetconfMessageTransformUtil.isDataRetrievalOperation(rpcQName)) {
+                writeNormalizedGet(payload, result, rpc, baseNetconfCtx);
+            } else {
+                writeNormalizedRpc(payload, result, rpc, schemaContext);
+            }
+        } catch (final XMLStreamException | IOException | IllegalStateException e) {
+            throw new IllegalStateException("Unable to serialize " + rpc, e);
+        }
+
+        final Document node = result.getNode().getOwnerDocument();
+
+        node.getDocumentElement().setAttribute(NetconfMessageTransformUtil.MESSAGE_ID_ATTR, counter.getNewMessageId(MESSAGE_ID_PREFIX));
+        return new NetconfMessage(node);
+    }
+
+    private DOMResult prepareDomResultForRpcRequest(final QName rpcQName) {
+        final Document document = XmlUtil.newDocument();
+        final Element rpcNS = document.createElementNS(NETCONF_RPC_QNAME.getNamespace().toString(), NETCONF_RPC_QNAME.getLocalName());
+        final Element elementNS = document.createElementNS(rpcQName.getNamespace().toString(), rpcQName.getLocalName());
+        rpcNS.appendChild(elementNS);
+        document.appendChild(rpcNS);
+        return new DOMResult(elementNS);
+    }
+
+    static final XMLOutputFactory XML_FACTORY;
+    static {
+        XML_FACTORY = XMLOutputFactory.newFactory();
+        XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);
+    }
+
+    // FIXME similar code is in netconf-notifications-impl , DRY
+    private void writeNormalizedNode(final NormalizedNode<?, ?> normalized, final DOMResult result, final SchemaPath schemaPath, final SchemaContext context)
+            throws IOException, XMLStreamException {
+        NormalizedNodeWriter normalizedNodeWriter = null;
+        NormalizedNodeStreamWriter normalizedNodeStreamWriter = null;
+        XMLStreamWriter writer = null;
+        try {
+            writer = XML_FACTORY.createXMLStreamWriter(result);
+            normalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(writer, context, schemaPath);
+            normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter);
+
+            normalizedNodeWriter.write(normalized);
+
+            normalizedNodeWriter.flush();
+        } finally {
+            try {
+                if(normalizedNodeWriter != null) {
+                    normalizedNodeWriter.close();
+                }
+                if(normalizedNodeStreamWriter != null) {
+                    normalizedNodeStreamWriter.close();
+                }
+                if(writer != null) {
+                    writer.close();
+                }
+            } catch (final Exception e) {
+                LOG.warn("Unable to close resource properly", e);
+            }
+        }
+    }
+
+    private void writeNormalizedEdit(final ContainerNode normalized, final DOMResult result, final SchemaPath schemaPath, final SchemaContext baseNetconfCtx) throws IOException, XMLStreamException {
+        final NormalizedNodeWriter normalizedNodeWriter;
+        NormalizedNodeStreamWriter normalizedNodeStreamWriter = null;
+        XMLStreamWriter writer = null;
         try {
-            final XmlCodecProvider codecProvider = XmlDocumentUtils.defaultValueCodecProvider();
-            if(schemaContext.isPresent()) {
-                if (NetconfMessageTransformUtil.isDataEditOperation(rpc)) {
-                    final DataNodeContainer schemaForEdit = NetconfMessageTransformUtil.createSchemaForEdit(schemaContext.get());
-                    w3cPayload = XmlDocumentUtils.toDocument(rpcPayload, schemaContext.get(), schemaForEdit, codecProvider);
-                } else if (NetconfMessageTransformUtil.isGetOperation(rpc)) {
-                    final DataNodeContainer schemaForGet = NetconfMessageTransformUtil.createSchemaForGet(schemaContext.get());
-                    w3cPayload = XmlDocumentUtils.toDocument(rpcPayload, schemaContext.get(), schemaForGet, codecProvider);
-                } else if (NetconfMessageTransformUtil.isGetConfigOperation(rpc)) {
-                    final DataNodeContainer schemaForGetConfig = NetconfMessageTransformUtil.createSchemaForGetConfig(schemaContext.get());
-                    w3cPayload = XmlDocumentUtils.toDocument(rpcPayload, schemaContext.get(), schemaForGetConfig, codecProvider);
+            writer = XML_FACTORY.createXMLStreamWriter(result);
+            normalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(writer, baseNetconfCtx, schemaPath);
+            normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter);
+
+            Optional<Iterable<Element>> editDataElements = Optional.absent();
+            for (final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> editElement : normalized.getValue()) {
+                if(editElement.getNodeType().getLocalName().equals(EditContent.QNAME.getLocalName())) {
+                    Preconditions.checkState(editElement instanceof ChoiceNode,
+                            "Edit content element is expected to be %s, not %s", ChoiceNode.class, editElement);
+                    final Optional<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> configContentHolder =
+                            ((ChoiceNode) editElement).getChild(toId(NETCONF_CONFIG_QNAME));
+                    // TODO The config element inside the EditContent should be AnyXml not Container, but AnyXml is based on outdated API
+                    Preconditions.checkState(configContentHolder.isPresent() && configContentHolder.get() instanceof ContainerNode,
+                            "Edit content/config element is expected to be present as a container node");
+                    normalizedNodeStreamWriter.startChoiceNode(toId(editElement.getNodeType()), 1);
+                    normalizedNodeStreamWriter.anyxmlNode(toId(NETCONF_CONFIG_QNAME), null);
+                    normalizedNodeStreamWriter.endNode();
+
+                    editDataElements = Optional.of(serializeAnyXmlAccordingToSchema(((ContainerNode) configContentHolder.get()).getValue()));
                 } else {
-                    final Optional<RpcDefinition> schemaForRpc = NetconfMessageTransformUtil.findSchemaForRpc(rpc, schemaContext.get());
-                    if(schemaForRpc.isPresent()) {
-                        final DataNodeContainer schemaForGetConfig = NetconfMessageTransformUtil.createSchemaForRpc(schemaForRpc.get());
-                        w3cPayload = XmlDocumentUtils.toDocument(rpcPayload, schemaContext.get(), schemaForGetConfig, codecProvider);
-                    } else {
-                        w3cPayload = toRpcRequestWithoutSchema(rpcPayload, codecProvider);
-                    }
+                    normalizedNodeWriter.write(editElement);
                 }
-            } else {
-                w3cPayload = toRpcRequestWithoutSchema(rpcPayload, codecProvider);
             }
-        } catch (final UnsupportedDataTypeException e) {
-            throw new IllegalArgumentException("Unable to create message", e);
+
+            normalizedNodeWriter.flush();
+
+            // FIXME this is a workaround for filter content serialization
+            // Any xml is not supported properly by the stream writer
+            if(editDataElements.isPresent()) {
+                appendEditData(result, editDataElements.get());
+            }
+        } finally {
+            try {
+                if(normalizedNodeStreamWriter != null) {
+                    normalizedNodeStreamWriter.close();
+                }
+                if(writer != null) {
+                    writer.close();
+                }
+            } catch (final Exception e) {
+                LOG.warn("Unable to close resource properly", e);
+            }
         }
-        w3cPayload.getDocumentElement().setAttribute("message-id", counter.getNewMessageId(MESSAGE_ID_PREFIX));
-        return new NetconfMessage(w3cPayload);
     }
 
-    private Document toRpcRequestWithoutSchema(final CompositeNodeTOImpl rpcPayload, final XmlCodecProvider codecProvider) {
-        return XmlDocumentUtils.toDocument(rpcPayload, codecProvider);
+    private void writeNormalizedRpc(final ContainerNode normalized, final DOMResult result, final SchemaPath schemaPath, final SchemaContext baseNetconfCtx) throws IOException, XMLStreamException {
+        final NormalizedNodeWriter normalizedNodeWriter;
+        NormalizedNodeStreamWriter normalizedNodeStreamWriter = null;
+        XMLStreamWriter writer = null;
+        try {
+            writer = XML_FACTORY.createXMLStreamWriter(result);
+            normalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(writer, baseNetconfCtx, schemaPath);
+            normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter);
+
+            for (final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> editElement : normalized.getValue()) {
+                normalizedNodeWriter.write(editElement);
+            }
+            normalizedNodeWriter.flush();
+        } finally {
+            try {
+                if(normalizedNodeStreamWriter != null) {
+                    normalizedNodeStreamWriter.close();
+                }
+                if(writer != null) {
+                    writer.close();
+                }
+            } catch (final Exception e) {
+                LOG.warn("Unable to close resource properly", e);
+            }
+        }
     }
 
-    @Override
-    public synchronized RpcResult<CompositeNode> toRpcResult(final NetconfMessage message, final QName rpc) {
-        if(schemaContext.isPresent()) {
-            return toRpcResult(message, rpc, schemaContext.get());
-        } else {
-            final CompositeNode node = (CompositeNode) XmlDocumentUtils.toDomNode(message.getDocument());
-            return RpcResultBuilder.success(node).build();
+    private void writeNormalizedGet(final ContainerNode normalized, final DOMResult result, final SchemaPath schemaPath, final SchemaContext baseNetconfCtx) throws IOException, XMLStreamException {
+        final NormalizedNodeWriter normalizedNodeWriter;
+        NormalizedNodeStreamWriter normalizedNodeStreamWriter = null;
+        XMLStreamWriter writer = null;
+        try {
+            writer = XML_FACTORY.createXMLStreamWriter(result);
+            normalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(writer, baseNetconfCtx, schemaPath);
+            normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter);
+
+            Optional<Iterable<Element>> filterElements = Optional.absent();
+
+            for (final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> editElement : normalized.getValue()) {
+                Preconditions.checkState(editElement instanceof ContainerNode);
+                if(editElement.getNodeType().getLocalName().equals(NETCONF_FILTER_QNAME.getLocalName())) {
+                    Preconditions.checkState(editElement instanceof ContainerNode,
+                            "Filter element is expected to be %s, not %s", ContainerNode.class, editElement);
+                    normalizedNodeStreamWriter.anyxmlNode(toId(editElement.getNodeType()), null);
+                    filterElements = Optional.of(serializeAnyXmlAccordingToSchema(((ContainerNode) editElement).getValue()));
+                } else {
+                    normalizedNodeWriter.write(editElement);
+                }
+            }
+
+            normalizedNodeWriter.flush();
+
+            // FIXME this is a workaround for filter content serialization
+            // Any xml is not supported properly by the stream writer
+            if(filterElements.isPresent()) {
+                appendFilter(result, filterElements.get());
+            }
+        } finally {
+            try {
+                if(normalizedNodeStreamWriter != null) {
+                    normalizedNodeStreamWriter.close();
+                }
+                if(writer != null) {
+                    writer.close();
+                }
+            } catch (final Exception e) {
+                LOG.warn("Unable to close resource properly", e);
+            }
+        }
+    }
+
+    private void appendFilter(final DOMResult result, final Iterable<Element> filterElements) {
+        final Element rpcElement = ((Element) result.getNode());
+        final Node filterParent = rpcElement.getElementsByTagNameNS(NETCONF_FILTER_QNAME.getNamespace().toString(), NETCONF_FILTER_QNAME.getLocalName()).item(0);
+        final Document ownerDocument = rpcElement.getOwnerDocument();
+        // TODO workaround, add subtree attribute, since it is not serialized by the caller of this method
+        ((Element) filterParent).setAttributeNS(NETCONF_TYPE_QNAME.getNamespace().toString(), NETCONF_TYPE_QNAME.getLocalName(), "subtree");
+        for (final Element element : filterElements) {
+            filterParent.appendChild(ownerDocument.importNode(element, true));
         }
     }
 
-    private static RpcResult<CompositeNode> toRpcResult(final NetconfMessage message, final QName rpc, final SchemaContext context) {
-        final CompositeNode compositeNode;
-        if (NetconfMessageTransformUtil.isDataRetrievalOperation(rpc)) {
+    private void appendEditData(final DOMResult result, final Iterable<Element> filterElements) {
+        final Element rpcElement = ((Element) result.getNode());
+        final Node configParent = rpcElement.getElementsByTagNameNS(NETCONF_CONFIG_QNAME.getNamespace().toString(), NETCONF_CONFIG_QNAME.getLocalName()).item(0);
+        for (final Element element : filterElements) {
+            configParent.appendChild(rpcElement.getOwnerDocument().importNode(element, true));
+        }
+    }
+
+    private Iterable<Element> serializeAnyXmlAccordingToSchema(final Iterable<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> values) throws IOException, XMLStreamException {
+        return Iterables.transform(values, new Function<DataContainerChild<? extends YangInstanceIdentifier.PathArgument,?>, Element>() {
+            @Override
+            public Element apply(final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> input) {
+                final DOMResult domResult = new DOMResult(XmlUtil.newDocument());
+                try {
+                    writeNormalizedNode(input, domResult, SchemaPath.ROOT, schemaContext);
+                } catch (IOException | XMLStreamException e) {
+                    throw new IllegalStateException(e);
+                }
+                return ((Document) domResult.getNode()).getDocumentElement();
+            }
+        });
+    }
+
+    @Override
+    public synchronized DOMRpcResult toRpcResult(final NetconfMessage message, final SchemaPath rpc) {
+        final NormalizedNode<?, ?> normalizedNode;
+        if (NetconfMessageTransformUtil.isDataRetrievalOperation(rpc.getLastComponent())) {
             final Element xmlData = NetconfMessageTransformUtil.getDataSubtree(message.getDocument());
-            final List<org.opendaylight.yangtools.yang.data.api.Node<?>> dataNodes = XmlDocumentUtils.toDomNodes(xmlData,
-                    Optional.of(context.getDataDefinitions()), context);
+            final ContainerSchemaNode schemaForDataRead = NetconfMessageTransformUtil.createSchemaForDataRead(schemaContext);
+            final ContainerNode dataNode = NORMALIZED_NODE_PARSER_FACTORY.getContainerNodeParser().parse(Collections.singleton(xmlData), schemaForDataRead);
 
-            final CompositeNodeBuilder<ImmutableCompositeNode> it = ImmutableCompositeNode.builder();
-            it.setQName(NetconfMessageTransformUtil.NETCONF_RPC_REPLY_QNAME);
-            it.add(ImmutableCompositeNode.create(NetconfMessageTransformUtil.NETCONF_DATA_QNAME, dataNodes));
-            compositeNode = it.toInstance();
+            // TODO check if the response is wrapper correctly
+            normalizedNode = Builders.containerBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NetconfMessageTransformUtil.NETCONF_RPC_REPLY_QNAME))
+                    .withChild(dataNode).build();
         } else {
-            final CompositeNode rpcReply = XmlDocumentUtils.rpcReplyToDomNodes(message.getDocument(), rpc, context);
-            if (rpcReply != null) {
-                compositeNode = rpcReply;
+            final Set<Element> documentElement = Collections.singleton(message.getDocument().getDocumentElement());
+            final RpcDefinition rpcDefinition = mappedRpcs.get(rpc.getLastComponent());
+            Preconditions.checkArgument(rpcDefinition != null, "Unable to parse response of %s, the rpc is unknown", rpc.getLastComponent());
+
+            // In case no input for rpc is defined, we can simply construct the payload here
+            if(rpcDefinition.getOutput() == null) {
+                Preconditions.checkArgument(XmlElement.fromDomDocument(message.getDocument()).getOnlyChildElementWithSameNamespaceOptionally("ok").isPresent(),
+                        "Unexpected content in response of rpc: %s, %s", rpcDefinition.getQName(), message);
+                normalizedNode = null;
             } else {
-                compositeNode = (CompositeNode) XmlDocumentUtils.toDomNode(message.getDocument());
+                normalizedNode = NORMALIZED_NODE_PARSER_FACTORY.getContainerNodeParser().parse(documentElement, rpcDefinition.getOutput());
             }
         }
-        return RpcResultBuilder.success( compositeNode ).build();
+        return new DefaultDOMRpcResult(normalizedNode);
     }
 
-    @Override
-    public synchronized void onGlobalContextUpdated(final SchemaContext schemaContext) {
-        this.schemaContext = Optional.fromNullable(schemaContext);
-    }
 }
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/InstanceIdToNodes.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/InstanceIdToNodes.java
new file mode 100644 (file)
index 0000000..cb17b35
--- /dev/null
@@ -0,0 +1,594 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.connect.netconf.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.ModifyAction;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.AttributesBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Transforms an instance of yang instance identifier to a filter like structure in normalized node format. Can be also used to nest the edit-config rpc content.
+ * For each argument of the id, a specific normalized node is created to ensure schema context conformance.
+ */
+public abstract class InstanceIdToNodes<T extends PathArgument> implements Identifiable<T> {
+
+    private final T identifier;
+
+    @Override
+    public T getIdentifier() {
+        return identifier;
+    }
+
+    protected InstanceIdToNodes(final T identifier) {
+        this.identifier = identifier;
+    }
+
+    abstract InstanceIdToNodes<?> getChild(final PathArgument child) throws DataNormalizationException;
+
+    public abstract NormalizedNode<?, ?> create(YangInstanceIdentifier legacyData, Optional<NormalizedNode<?, ?>> deepestChild, Optional<ModifyAction> operation);
+
+    private static abstract class SimpleTypeNormalization<T extends PathArgument> extends InstanceIdToNodes<T> {
+
+        protected SimpleTypeNormalization(final T identifier) {
+            super(identifier);
+        }
+
+        @Override
+        public NormalizedNode<?, ?> create(final YangInstanceIdentifier id, final Optional<NormalizedNode<?, ?>> deepestChild, final Optional<ModifyAction> operation) {
+            checkNotNull(id);
+            final PathArgument pathArgument = Iterables.get(id.getPathArguments(), 0);
+            final NormalizedNodeAttrBuilder<? extends PathArgument, Object, ? extends NormalizedNode<? extends PathArgument, Object>> builder = getBuilder(pathArgument);
+
+            if(deepestChild.isPresent()) {
+                builder.withValue(deepestChild.get().getValue());
+            }
+
+            addModifyOpIfPresent(operation, builder);
+            return builder.build();
+        }
+
+        protected abstract NormalizedNodeAttrBuilder<? extends PathArgument, Object, ? extends NormalizedNode<? extends PathArgument, Object>> getBuilder(PathArgument node);
+
+        @Override
+        public InstanceIdToNodes<?> getChild(final PathArgument child) {
+            return null;
+        }
+    }
+
+
+    public void addModifyOpIfPresent(final Optional<ModifyAction> operation, final AttributesBuilder<?> builder) {
+        if(operation.isPresent()) {
+            builder.withAttributes(Collections.singletonMap(NetconfMessageTransformUtil.NETCONF_OPERATION_QNAME, NetconfMessageTransformUtil.modifyOperationToXmlString(operation.get())));
+        }
+    }
+
+    private static final class LeafNormalization extends SimpleTypeNormalization<NodeIdentifier> {
+
+        protected LeafNormalization(final LeafSchemaNode potential) {
+            super(new NodeIdentifier(potential.getQName()));
+        }
+
+        @Override
+        protected NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> getBuilder(final PathArgument node) {
+            return Builders.leafBuilder().withNodeIdentifier(getIdentifier());
+        }
+    }
+
+    private static final class LeafListEntryNormalization extends SimpleTypeNormalization<NodeWithValue> {
+
+        public LeafListEntryNormalization(final LeafListSchemaNode potential) {
+            super(new NodeWithValue(potential.getQName(), null));
+        }
+
+        @Override
+        protected NormalizedNodeAttrBuilder<NodeWithValue, Object, LeafSetEntryNode<Object>> getBuilder(final PathArgument node) {
+            Preconditions.checkArgument(node instanceof NodeWithValue);
+            return Builders.leafSetEntryBuilder().withNodeIdentifier((NodeWithValue) node).withValue(((NodeWithValue) node).getValue());
+        }
+
+    }
+
+    private static abstract class CompositeNodeNormalizationOperation<T extends PathArgument> extends
+            InstanceIdToNodes<T> {
+
+        protected CompositeNodeNormalizationOperation(final T identifier) {
+            super(identifier);
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public final NormalizedNode<?, ?> create(final YangInstanceIdentifier id, final Optional<NormalizedNode<?, ?>> lastChild, final Optional<ModifyAction> operation) {
+            checkNotNull(id);
+            final Iterator<PathArgument> iterator = id.getPathArguments().iterator();
+            final PathArgument legacyData = iterator.next();
+
+            if (!isMixin(this) && getIdentifier().getNodeType() != null) {
+                checkArgument(getIdentifier().getNodeType().equals(legacyData.getNodeType()),
+                        "Node QName must be %s was %s", getIdentifier().getNodeType(), legacyData.getNodeType());
+            }
+            final NormalizedNodeContainerBuilder builder = createBuilder(legacyData);
+
+            if (iterator.hasNext()) {
+                final PathArgument childPath = iterator.next();
+                final InstanceIdToNodes childOp = getChildOperation(childPath);
+
+                final YangInstanceIdentifier childId = YangInstanceIdentifier.create(Iterables.skip(id.getPathArguments(), 1));
+                builder.addChild(childOp.create(childId, lastChild, operation));
+            } else if(lastChild.isPresent()) {
+                builder.withValue(Lists.newArrayList((Collection<?>) lastChild.get().getValue()));
+                if(operation.isPresent()) {
+                    Preconditions.checkArgument(builder instanceof AttributesBuilder<?>);
+                    addModifyOpIfPresent(operation, ((AttributesBuilder<?>) builder));
+                }
+            }
+
+            return builder.build();
+        }
+
+        private InstanceIdToNodes getChildOperation(final PathArgument childPath) {
+            final InstanceIdToNodes childOp;
+            try {
+                childOp = getChild(childPath);
+            } catch (final DataNormalizationException e) {
+                throw new IllegalArgumentException(String.format("Failed to process child node %s", childPath), e);
+            }
+            checkArgument(childOp != null, "Node %s is not allowed inside %s", childPath, getIdentifier());
+            return childOp;
+        }
+
+        @SuppressWarnings("rawtypes")
+        protected abstract NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode);
+    }
+
+    static boolean isMixin(final InstanceIdToNodes<?> op) {
+        return op instanceof MixinNormalizationOp;
+    }
+
+    private static abstract class DataContainerNormalizationOperation<T extends PathArgument> extends
+            CompositeNodeNormalizationOperation<T> {
+
+        private final DataNodeContainer schema;
+        private final Map<PathArgument, InstanceIdToNodes<?>> byArg;
+
+        protected DataContainerNormalizationOperation(final T identifier, final DataNodeContainer schema) {
+            super(identifier);
+            this.schema = schema;
+            this.byArg = new ConcurrentHashMap<>();
+        }
+
+        @Override
+        public InstanceIdToNodes<?> getChild(final PathArgument child) throws DataNormalizationException {
+            InstanceIdToNodes<?> potential = byArg.get(child);
+            if (potential != null) {
+                return potential;
+            }
+            potential = fromLocalSchema(child);
+            return register(potential);
+        }
+
+        private InstanceIdToNodes<?> fromLocalSchema(final PathArgument child) throws DataNormalizationException {
+            if (child instanceof AugmentationIdentifier) {
+                return fromSchemaAndQNameChecked(schema, ((AugmentationIdentifier) child).getPossibleChildNames()
+                        .iterator().next());
+            }
+            return fromSchemaAndQNameChecked(schema, child.getNodeType());
+        }
+
+        private InstanceIdToNodes<?> register(final InstanceIdToNodes<?> potential) {
+            if (potential != null) {
+                byArg.put(potential.getIdentifier(), potential);
+            }
+            return potential;
+        }
+    }
+
+    private static final class ListItemNormalization extends
+            DataContainerNormalizationOperation<NodeIdentifierWithPredicates> {
+
+        protected ListItemNormalization(final NodeIdentifierWithPredicates identifier, final ListSchemaNode schema) {
+            super(identifier, schema);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument currentArg) {
+            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = Builders
+                    .mapEntryBuilder().withNodeIdentifier((NodeIdentifierWithPredicates) currentArg);
+            for (final Entry<QName, Object> keyValue : ((NodeIdentifierWithPredicates) currentArg).getKeyValues().entrySet()) {
+                builder.addChild(Builders.leafBuilder()
+                        //
+                        .withNodeIdentifier(new NodeIdentifier(keyValue.getKey())).withValue(keyValue.getValue())
+                        .build());
+            }
+            return builder;
+        }
+
+    }
+
+    private static final class UnkeyedListItemNormalization extends DataContainerNormalizationOperation<NodeIdentifier> {
+
+        protected UnkeyedListItemNormalization(final ListSchemaNode schema) {
+            super(new NodeIdentifier(schema.getQName()), schema);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
+            return Builders.unkeyedListEntryBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+    }
+
+    private static final class ContainerTransformation extends DataContainerNormalizationOperation<NodeIdentifier> {
+
+        protected ContainerTransformation(final ContainerSchemaNode schema) {
+            super(new NodeIdentifier(schema.getQName()), schema);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
+            return Builders.containerBuilder().withNodeIdentifier(getIdentifier());
+        }
+    }
+
+    /**
+     * Marker interface for Mixin nodes normalization operations
+     */
+    private interface MixinNormalizationOp {}
+
+
+    private static final class OrderedLeafListMixinNormalization extends UnorderedLeafListMixinNormalization {
+
+
+        public OrderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
+            super(potential);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
+            return Builders.orderedLeafSetBuilder().withNodeIdentifier(getIdentifier());
+        }
+    }
+
+    private static class UnorderedLeafListMixinNormalization extends CompositeNodeNormalizationOperation<NodeIdentifier> implements MixinNormalizationOp {
+
+        private final InstanceIdToNodes<?> innerOp;
+
+        public UnorderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
+            super(new NodeIdentifier(potential.getQName()));
+            innerOp = new LeafListEntryNormalization(potential);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
+            return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        public InstanceIdToNodes<?> getChild(final PathArgument child) {
+            if (child instanceof NodeWithValue) {
+                return innerOp;
+            }
+            return null;
+        }
+    }
+
+    private static final class AugmentationNormalization extends DataContainerNormalizationOperation<AugmentationIdentifier> implements MixinNormalizationOp {
+
+        public AugmentationNormalization(final AugmentationSchema augmentation, final DataNodeContainer schema) {
+            //super();
+            super(augmentationIdentifierFrom(augmentation), augmentationProxy(augmentation, schema));
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
+            return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier());
+        }
+    }
+
+    private static class UnorderedMapMixinNormalization extends CompositeNodeNormalizationOperation<NodeIdentifier> implements MixinNormalizationOp {
+
+        private final ListItemNormalization innerNode;
+
+        public UnorderedMapMixinNormalization(final ListSchemaNode list) {
+            super(new NodeIdentifier(list.getQName()));
+            this.innerNode = new ListItemNormalization(new NodeIdentifierWithPredicates(list.getQName(),
+                    Collections.<QName, Object>emptyMap()), list);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
+            return Builders.mapBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        public InstanceIdToNodes<?> getChild(final PathArgument child) {
+            if (child.getNodeType().equals(getIdentifier().getNodeType())) {
+                return innerNode;
+            }
+            return null;
+        }
+    }
+
+    private static class UnkeyedListMixinNormalization extends CompositeNodeNormalizationOperation<NodeIdentifier> implements MixinNormalizationOp {
+
+        private final UnkeyedListItemNormalization innerNode;
+
+        public UnkeyedListMixinNormalization(final ListSchemaNode list) {
+            super(new NodeIdentifier(list.getQName()));
+            this.innerNode = new UnkeyedListItemNormalization(list);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
+            return Builders.unkeyedListBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        public InstanceIdToNodes<?> getChild(final PathArgument child) {
+            if (child.getNodeType().equals(getIdentifier().getNodeType())) {
+                return innerNode;
+            }
+            return null;
+        }
+
+    }
+
+    private static final class OrderedMapMixinNormalization extends UnorderedMapMixinNormalization {
+
+        public OrderedMapMixinNormalization(final ListSchemaNode list) {
+            super(list);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
+            return Builders.orderedMapBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+    }
+
+    private static class ChoiceNodeNormalization extends CompositeNodeNormalizationOperation<NodeIdentifier> implements MixinNormalizationOp {
+
+        private final ImmutableMap<PathArgument, InstanceIdToNodes<?>> byArg;
+
+        protected ChoiceNodeNormalization(final org.opendaylight.yangtools.yang.model.api.ChoiceNode schema) {
+            super(new NodeIdentifier(schema.getQName()));
+            final ImmutableMap.Builder<PathArgument, InstanceIdToNodes<?>> byArgBuilder = ImmutableMap.builder();
+
+            for (final ChoiceCaseNode caze : schema.getCases()) {
+                for (final DataSchemaNode cazeChild : caze.getChildNodes()) {
+                    final InstanceIdToNodes<?> childOp = fromDataSchemaNode(cazeChild);
+                    byArgBuilder.put(childOp.getIdentifier(), childOp);
+                }
+            }
+            byArg = byArgBuilder.build();
+        }
+
+        @Override
+        public InstanceIdToNodes<?> getChild(final PathArgument child) {
+            return byArg.get(child);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
+            return Builders.choiceBuilder().withNodeIdentifier(getIdentifier());
+        }
+    }
+
+    private static class AnyXmlNormalization extends InstanceIdToNodes<NodeIdentifier> {
+
+        protected AnyXmlNormalization(final AnyXmlSchemaNode schema) {
+            super(new NodeIdentifier(schema.getQName()));
+        }
+
+        @Override
+        public InstanceIdToNodes<?> getChild(final PathArgument child) throws DataNormalizationException {
+            return null;
+        }
+
+        @Override
+        public NormalizedNode<?, ?> create(final YangInstanceIdentifier legacyData, final Optional<NormalizedNode<?, ?>> deepestChild, final Optional<ModifyAction> operation) {
+            if(deepestChild.isPresent()) {
+                Preconditions.checkState(deepestChild instanceof AnyXmlNode);
+                final NormalizedNodeAttrBuilder<NodeIdentifier, Node<?>, AnyXmlNode> anyXmlBuilder =
+                        Builders.anyXmlBuilder().withNodeIdentifier(getIdentifier()).withValue(((AnyXmlNode) deepestChild).getValue());
+                addModifyOpIfPresent(operation, anyXmlBuilder);
+                return anyXmlBuilder.build();
+            }
+
+            final NormalizedNodeAttrBuilder<NodeIdentifier, Node<?>, AnyXmlNode> builder =
+                    Builders.anyXmlBuilder().withNodeIdentifier(getIdentifier());
+            return builder.build();
+        }
+
+    }
+
+    private static Optional<DataSchemaNode> findChildSchemaNode(final DataNodeContainer parent, final QName child) {
+        DataSchemaNode potential = parent.getDataChildByName(child);
+        if (potential == null) {
+            final Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices = FluentIterable.from(
+                    parent.getChildNodes()).filter(org.opendaylight.yangtools.yang.model.api.ChoiceNode.class);
+            potential = findChoice(choices, child);
+        }
+        return Optional.fromNullable(potential);
+    }
+
+    private static InstanceIdToNodes<?> fromSchemaAndQNameChecked(final DataNodeContainer schema, final QName child) throws DataNormalizationException {
+        final Optional<DataSchemaNode> potential = findChildSchemaNode(schema, child);
+        if (!potential.isPresent()) {
+            throw new DataNormalizationException(String.format("Supplied QName %s is not valid according to schema %s, potential children nodes: %s", child, schema, schema.getChildNodes()));
+        }
+
+        final DataSchemaNode result = potential.get();
+        // We try to look up if this node was added by augmentation
+        if ((schema instanceof DataSchemaNode) && result.isAugmenting()) {
+            return fromAugmentation(schema, (AugmentationTarget) schema, result);
+        }
+        return fromDataSchemaNode(result);
+    }
+
+    private static org.opendaylight.yangtools.yang.model.api.ChoiceNode findChoice(
+            final Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices, final QName child) {
+        org.opendaylight.yangtools.yang.model.api.ChoiceNode foundChoice = null;
+        choiceLoop:
+        for (final org.opendaylight.yangtools.yang.model.api.ChoiceNode choice : choices) {
+            for (final ChoiceCaseNode caze : choice.getCases()) {
+                if (findChildSchemaNode(caze, child).isPresent()) {
+                    foundChoice = choice;
+                    break choiceLoop;
+                }
+            }
+        }
+        return foundChoice;
+    }
+
+    private static AugmentationIdentifier augmentationIdentifierFrom(final AugmentationSchema augmentation) {
+        final ImmutableSet.Builder<QName> potentialChildren = ImmutableSet.builder();
+        for (final DataSchemaNode child : augmentation.getChildNodes()) {
+            potentialChildren.add(child.getQName());
+        }
+        return new AugmentationIdentifier(potentialChildren.build());
+    }
+
+    private static DataNodeContainer augmentationProxy(final AugmentationSchema augmentation, final DataNodeContainer schema) {
+        final Set<DataSchemaNode> children = new HashSet<>();
+        for (final DataSchemaNode augNode : augmentation.getChildNodes()) {
+            children.add(schema.getDataChildByName(augNode.getQName()));
+        }
+        return new NodeContainerProxy(null, children);
+    }
+
+    /**
+     * Returns a SchemaPathUtil for provided child node
+     * <p/>
+     * If supplied child is added by Augmentation this operation returns
+     * a SchemaPathUtil for augmentation,
+     * otherwise returns a SchemaPathUtil for child as
+     * call for {@link #fromDataSchemaNode(org.opendaylight.yangtools.yang.model.api.DataSchemaNode)}.
+     */
+    private static InstanceIdToNodes<?> fromAugmentation(final DataNodeContainer parent,
+                                                          final AugmentationTarget parentAug, final DataSchemaNode child) {
+        AugmentationSchema augmentation = null;
+        for (final AugmentationSchema aug : parentAug.getAvailableAugmentations()) {
+            final DataSchemaNode potential = aug.getDataChildByName(child.getQName());
+            if (potential != null) {
+                augmentation = aug;
+                break;
+            }
+
+        }
+        if (augmentation != null) {
+            return new AugmentationNormalization(augmentation, parent);
+        } else {
+            return fromDataSchemaNode(child);
+        }
+    }
+
+    private static InstanceIdToNodes<?> fromDataSchemaNode(final DataSchemaNode potential) {
+        if (potential instanceof ContainerSchemaNode) {
+            return new ContainerTransformation((ContainerSchemaNode) potential);
+        } else if (potential instanceof ListSchemaNode) {
+            return fromListSchemaNode((ListSchemaNode) potential);
+        } else if (potential instanceof LeafSchemaNode) {
+            return new LeafNormalization((LeafSchemaNode) potential);
+        } else if (potential instanceof org.opendaylight.yangtools.yang.model.api.ChoiceNode) {
+            return new ChoiceNodeNormalization((org.opendaylight.yangtools.yang.model.api.ChoiceNode) potential);
+        } else if (potential instanceof LeafListSchemaNode) {
+            return fromLeafListSchemaNode((LeafListSchemaNode) potential);
+        } else if (potential instanceof AnyXmlSchemaNode) {
+            return new AnyXmlNormalization((AnyXmlSchemaNode) potential);
+        }
+        return null;
+    }
+
+    private static InstanceIdToNodes<?> fromListSchemaNode(final ListSchemaNode potential) {
+        final List<QName> keyDefinition = potential.getKeyDefinition();
+        if (keyDefinition == null || keyDefinition.isEmpty()) {
+            return new UnkeyedListMixinNormalization(potential);
+        }
+        if (potential.isUserOrdered()) {
+            return new OrderedMapMixinNormalization(potential);
+        }
+        return new UnorderedMapMixinNormalization(potential);
+    }
+
+    private static InstanceIdToNodes<?> fromLeafListSchemaNode(final LeafListSchemaNode potential) {
+        if (potential.isUserOrdered()) {
+            return new OrderedLeafListMixinNormalization(potential);
+        }
+        return new UnorderedLeafListMixinNormalization(potential);
+    }
+
+    public static NormalizedNode<?, ?> serialize(final SchemaContext ctx, final YangInstanceIdentifier id) {
+        return serialize(ctx, id, Optional.<NormalizedNode<?, ?>>absent(), Optional.<ModifyAction>absent());
+    }
+
+    public static NormalizedNode<?, ?> serialize(final SchemaContext ctx, final YangInstanceIdentifier id, final NormalizedNode<?, ?> deepestElement) {
+        return serialize(ctx, id, Optional.<NormalizedNode<?, ?>>of(deepestElement), Optional.<ModifyAction>absent());
+    }
+
+    public static NormalizedNode<?, ?> serialize(final SchemaContext ctx, final YangInstanceIdentifier id, final Optional<NormalizedNode<?, ?>> deepestElement, final Optional<ModifyAction> operation) {
+        Preconditions.checkNotNull(ctx);
+        Preconditions.checkNotNull(id);
+        final PathArgument topLevelElement = id.getPathArguments().iterator().next();
+        final DataSchemaNode dataChildByName = ctx.getDataChildByName(topLevelElement.getNodeType());
+        Preconditions.checkNotNull(dataChildByName, "Cannot find %s node in schema context. Instance identifier has to start from root", topLevelElement);
+        try {
+            final InstanceIdToNodes<?> instanceIdToNodes = fromSchemaAndQNameChecked(ctx, topLevelElement.getNodeType());
+            return instanceIdToNodes.create(id, deepestElement, operation);
+        } catch (final DataNormalizationException e) {
+            throw new IllegalArgumentException("Unable to serialize: " + id, e);
+        }
+    }
+}
index 40e890e..7b231f9 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.controller.sal.connect.netconf.util;
 
 import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.DISCARD_CHANGES_RPC_CONTENT;
 import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME;
 import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DEFAULT_OPERATION_QNAME;
 import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME;
 import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME;
@@ -24,26 +25,28 @@ import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessag
 import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME;
 import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.ROLLBACK_ON_ERROR_OPTION;
 import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toFilterStructure;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toId;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath;
 
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
+import com.google.common.collect.Iterables;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
-import java.util.Collections;
-import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.copy.config.input.target.ConfigTarget;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.get.config.input.source.ConfigSource;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.ModifyAction;
-import org.opendaylight.yangtools.yang.data.api.Node;
-import org.opendaylight.yangtools.yang.data.api.SimpleNode;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
-import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
-import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
-import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 /**
  * Provides base operations for netconf e.g. get, get-config, edit-config, (un)lock, commit etc.
@@ -51,114 +54,117 @@ import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
  */
 public final class NetconfBaseOps {
 
-    private final RpcImplementation rpc;
+    private final DOMRpcService rpc;
+    private final SchemaContext schemaContext;
 
-    public NetconfBaseOps(final RpcImplementation rpc) {
+    public NetconfBaseOps(final DOMRpcService rpc, final SchemaContext schemaContext) {
         this.rpc = rpc;
+        this.schemaContext = schemaContext;
     }
 
-    public ListenableFuture<RpcResult<CompositeNode>> lock(final FutureCallback<RpcResult<CompositeNode>> callback, final QName datastore) {
+    public ListenableFuture<DOMRpcResult> lock(final FutureCallback<DOMRpcResult> callback, final QName datastore) {
         Preconditions.checkNotNull(callback);
         Preconditions.checkNotNull(datastore);
 
-        final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NETCONF_LOCK_QNAME, getLockContent(datastore));
+        final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NETCONF_LOCK_QNAME), getLockContent(datastore));
         Futures.addCallback(future, callback);
         return future;
     }
 
-    public ListenableFuture<RpcResult<CompositeNode>> lockCandidate(final FutureCallback<RpcResult<CompositeNode>> callback) {
-        final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NETCONF_LOCK_QNAME, getLockContent(NETCONF_CANDIDATE_QNAME));
+    public ListenableFuture<DOMRpcResult> lockCandidate(final FutureCallback<DOMRpcResult> callback) {
+        final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NETCONF_LOCK_QNAME), getLockContent(NETCONF_CANDIDATE_QNAME));
         Futures.addCallback(future, callback);
         return future;
     }
 
 
-    public ListenableFuture<RpcResult<CompositeNode>> lockRunning(final FutureCallback<RpcResult<CompositeNode>> callback) {
-        final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NETCONF_LOCK_QNAME, getLockContent(NETCONF_RUNNING_QNAME));
+    public ListenableFuture<DOMRpcResult> lockRunning(final FutureCallback<DOMRpcResult> callback) {
+        final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NETCONF_LOCK_QNAME), getLockContent(NETCONF_RUNNING_QNAME));
         Futures.addCallback(future, callback);
         return future;
     }
 
-    public ListenableFuture<RpcResult<CompositeNode>> unlock(final FutureCallback<RpcResult<CompositeNode>> callback, final QName datastore) {
+    public ListenableFuture<DOMRpcResult> unlock(final FutureCallback<DOMRpcResult> callback, final QName datastore) {
         Preconditions.checkNotNull(callback);
         Preconditions.checkNotNull(datastore);
 
-        final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NETCONF_UNLOCK_QNAME, getUnLockContent(datastore));
+        final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NETCONF_UNLOCK_QNAME), getUnLockContent(datastore));
         Futures.addCallback(future, callback);
         return future;
     }
 
-    public ListenableFuture<RpcResult<CompositeNode>> unlockRunning(final FutureCallback<RpcResult<CompositeNode>> callback) {
-        final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NETCONF_UNLOCK_QNAME, getUnLockContent(NETCONF_RUNNING_QNAME));
+    public ListenableFuture<DOMRpcResult> unlockRunning(final FutureCallback<DOMRpcResult> callback) {
+        final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NETCONF_UNLOCK_QNAME), getUnLockContent(NETCONF_RUNNING_QNAME));
         Futures.addCallback(future, callback);
         return future;
     }
 
-    public ListenableFuture<RpcResult<CompositeNode>> unlockCandidate(final FutureCallback<RpcResult<CompositeNode>> callback) {
-        final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NETCONF_UNLOCK_QNAME, getUnLockContent(NETCONF_CANDIDATE_QNAME));
+    public ListenableFuture<DOMRpcResult> unlockCandidate(final FutureCallback<DOMRpcResult> callback) {
+        final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NETCONF_UNLOCK_QNAME), getUnLockContent(NETCONF_CANDIDATE_QNAME));
         Futures.addCallback(future, callback);
         return future;
     }
 
-    public ListenableFuture<RpcResult<CompositeNode>> discardChanges(final FutureCallback<RpcResult<CompositeNode>> callback) {
+    public ListenableFuture<DOMRpcResult> discardChanges(final FutureCallback<DOMRpcResult> callback) {
         Preconditions.checkNotNull(callback);
 
-        final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NETCONF_DISCARD_CHANGES_QNAME, DISCARD_CHANGES_RPC_CONTENT);
+        final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NETCONF_DISCARD_CHANGES_QNAME), DISCARD_CHANGES_RPC_CONTENT);
         Futures.addCallback(future, callback);
         return future;
     }
 
-    public ListenableFuture<RpcResult<CompositeNode>> commit(final FutureCallback<RpcResult<CompositeNode>> callback) {
+    public ListenableFuture<DOMRpcResult> commit(final FutureCallback<DOMRpcResult> callback) {
         Preconditions.checkNotNull(callback);
 
-        final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME, NetconfMessageTransformUtil.COMMIT_RPC_CONTENT);
+        final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME), NetconfMessageTransformUtil.COMMIT_RPC_CONTENT);
         Futures.addCallback(future, callback);
         return future;
     }
 
-    public ListenableFuture<RpcResult<CompositeNode>> validate(final FutureCallback<RpcResult<CompositeNode>> callback, final QName datastore) {
+    public ListenableFuture<DOMRpcResult> validate(final FutureCallback<DOMRpcResult> callback, final QName datastore) {
         Preconditions.checkNotNull(callback);
         Preconditions.checkNotNull(datastore);
 
-        final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME, getValidateContent(datastore));
+        final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME), getValidateContent(datastore));
         Futures.addCallback(future, callback);
         return future;
     }
 
-    public ListenableFuture<RpcResult<CompositeNode>> validateCandidate(final FutureCallback<RpcResult<CompositeNode>> callback) {
+    public ListenableFuture<DOMRpcResult> validateCandidate(final FutureCallback<DOMRpcResult> callback) {
         return validate(callback, NETCONF_CANDIDATE_QNAME);
     }
 
 
-    public ListenableFuture<RpcResult<CompositeNode>> validateRunning(final FutureCallback<RpcResult<CompositeNode>> callback) {
+    public ListenableFuture<DOMRpcResult> validateRunning(final FutureCallback<DOMRpcResult> callback) {
         return validate(callback, NETCONF_RUNNING_QNAME);
     }
 
-    public ListenableFuture<RpcResult<CompositeNode>> copyConfig(final FutureCallback<RpcResult<CompositeNode>> callback, final QName source, final QName target) {
+    public ListenableFuture<DOMRpcResult> copyConfig(final FutureCallback<DOMRpcResult> callback, final QName source, final QName target) {
         Preconditions.checkNotNull(callback);
         Preconditions.checkNotNull(source);
         Preconditions.checkNotNull(target);
 
-        final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME, getCopyConfigContent(source, target));
+        final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME), getCopyConfigContent(source, target));
         Futures.addCallback(future, callback);
         return future;
     }
 
-    public ListenableFuture<RpcResult<CompositeNode>> copyRunningToCandidate(final FutureCallback<RpcResult<CompositeNode>> callback) {
+    public ListenableFuture<DOMRpcResult> copyRunningToCandidate(final FutureCallback<DOMRpcResult> callback) {
         return copyConfig(callback, NETCONF_RUNNING_QNAME, NETCONF_CANDIDATE_QNAME);
     }
 
-    public ListenableFuture<RpcResult<CompositeNode>> getConfig(final FutureCallback<RpcResult<CompositeNode>> callback, final QName datastore, final Optional<YangInstanceIdentifier> filterPath) {
+    public ListenableFuture<DOMRpcResult> getConfig(final FutureCallback<DOMRpcResult> callback, final QName datastore, final Optional<YangInstanceIdentifier> filterPath) {
         Preconditions.checkNotNull(callback);
         Preconditions.checkNotNull(datastore);
 
-        final ListenableFuture<RpcResult<CompositeNode>> future;
-        if (filterPath.isPresent()) {
-            final Node<?> node = toFilterStructure(filterPath.get());
-            future = rpc.invokeRpc(NETCONF_GET_CONFIG_QNAME,
+        final ListenableFuture<DOMRpcResult> future;
+        if (isFilterPresent(filterPath)) {
+            // FIXME the source node has to be wrapped in a choice
+            final DataContainerChild<?, ?> node = toFilterStructure(filterPath.get(), schemaContext);
+            future = rpc.invokeRpc(toPath(NETCONF_GET_CONFIG_QNAME),
                             NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_QNAME, getSourceNode(datastore), node));
         } else {
-            future = rpc.invokeRpc(NETCONF_GET_CONFIG_QNAME,
+            future = rpc.invokeRpc(toPath(NETCONF_GET_CONFIG_QNAME),
                             NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_QNAME, getSourceNode(datastore)));
         }
 
@@ -166,104 +172,117 @@ public final class NetconfBaseOps {
         return future;
     }
 
-    public ListenableFuture<RpcResult<CompositeNode>> getConfigRunning(final FutureCallback<RpcResult<CompositeNode>> callback, final Optional<YangInstanceIdentifier> filterPath) {
+    public ListenableFuture<DOMRpcResult> getConfigRunning(final FutureCallback<DOMRpcResult> callback, final Optional<YangInstanceIdentifier> filterPath) {
         return getConfig(callback, NETCONF_RUNNING_QNAME, filterPath);
     }
 
-    public ListenableFuture<RpcResult<CompositeNode>> getConfigCandidate(final FutureCallback<RpcResult<CompositeNode>> callback, final Optional<YangInstanceIdentifier> filterPath) {
+    public ListenableFuture<DOMRpcResult> getConfigCandidate(final FutureCallback<DOMRpcResult> callback, final Optional<YangInstanceIdentifier> filterPath) {
         return getConfig(callback, NETCONF_CANDIDATE_QNAME, filterPath);
     }
 
-    public ListenableFuture<RpcResult<CompositeNode>> get(final FutureCallback<RpcResult<CompositeNode>> callback, final Optional<YangInstanceIdentifier> filterPath) {
+    public ListenableFuture<DOMRpcResult> get(final FutureCallback<DOMRpcResult> callback, final Optional<YangInstanceIdentifier> filterPath) {
         Preconditions.checkNotNull(callback);
 
-        final ListenableFuture<RpcResult<CompositeNode>> future;
-        final Node<?> node = filterPath.isPresent() ? toFilterStructure(filterPath.get()) : NetconfMessageTransformUtil.GET_RPC_CONTENT;
-        future = rpc.invokeRpc(NETCONF_GET_QNAME, NetconfMessageTransformUtil.wrap(NETCONF_GET_QNAME, node));
+        final ListenableFuture<DOMRpcResult> future;
+
+        future = isFilterPresent(filterPath) ?
+                rpc.invokeRpc(toPath(NETCONF_GET_QNAME), NetconfMessageTransformUtil.wrap(NETCONF_GET_QNAME, toFilterStructure(filterPath.get(), schemaContext))) :
+                rpc.invokeRpc(toPath(NETCONF_GET_QNAME), NetconfMessageTransformUtil.GET_RPC_CONTENT);
 
         Futures.addCallback(future, callback);
         return future;
     }
 
-    public ListenableFuture<RpcResult<CompositeNode>> editConfigCandidate(final FutureCallback<? super RpcResult<CompositeNode>> callback, final CompositeNode editStructure, final ModifyAction modifyAction, final boolean rollback) {
+    private boolean isFilterPresent(final Optional<YangInstanceIdentifier> filterPath) {
+        return filterPath.isPresent() && Iterables.isEmpty(filterPath.get().getPathArguments()) == false;
+    }
+
+    public ListenableFuture<DOMRpcResult> editConfigCandidate(final FutureCallback<? super DOMRpcResult> callback, final DataContainerChild<?, ?> editStructure, final ModifyAction modifyAction, final boolean rollback) {
         return editConfig(callback, NETCONF_CANDIDATE_QNAME, editStructure, Optional.of(modifyAction), rollback);
     }
 
-    public ListenableFuture<RpcResult<CompositeNode>> editConfigCandidate(final FutureCallback<? super RpcResult<CompositeNode>> callback, final CompositeNode editStructure, final boolean rollback) {
+    public ListenableFuture<DOMRpcResult> editConfigCandidate(final FutureCallback<? super DOMRpcResult> callback, final DataContainerChild<?, ?> editStructure, final boolean rollback) {
         return editConfig(callback, NETCONF_CANDIDATE_QNAME, editStructure, Optional.<ModifyAction>absent(), rollback);
     }
 
-    public ListenableFuture<RpcResult<CompositeNode>> editConfigRunning(final FutureCallback<? super RpcResult<CompositeNode>> callback, final CompositeNode editStructure, final ModifyAction modifyAction, final boolean rollback) {
+    public ListenableFuture<DOMRpcResult> editConfigRunning(final FutureCallback<? super DOMRpcResult> callback, final DataContainerChild<?, ?> editStructure, final ModifyAction modifyAction, final boolean rollback) {
         return editConfig(callback, NETCONF_RUNNING_QNAME, editStructure, Optional.of(modifyAction), rollback);
     }
 
-    public ListenableFuture<RpcResult<CompositeNode>> editConfigRunning(final FutureCallback<? super RpcResult<CompositeNode>> callback, final CompositeNode editStructure, final boolean rollback) {
+    public ListenableFuture<DOMRpcResult> editConfigRunning(final FutureCallback<? super DOMRpcResult> callback, final DataContainerChild<?, ?> editStructure, final boolean rollback) {
         return editConfig(callback, NETCONF_RUNNING_QNAME, editStructure, Optional.<ModifyAction>absent(), rollback);
     }
 
-    public ListenableFuture<RpcResult<CompositeNode>> editConfig(final FutureCallback<? super RpcResult<CompositeNode>> callback, final QName datastore, final CompositeNode editStructure, final Optional<ModifyAction> modifyAction, final boolean rollback) {
+    public ListenableFuture<DOMRpcResult> editConfig(final FutureCallback<? super DOMRpcResult> callback, final QName datastore, final DataContainerChild<?, ?> editStructure, final Optional<ModifyAction> modifyAction, final boolean rollback) {
         Preconditions.checkNotNull(editStructure);
         Preconditions.checkNotNull(callback);
         Preconditions.checkNotNull(datastore);
 
-        final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NETCONF_EDIT_CONFIG_QNAME, getEditConfigContent(datastore, editStructure, modifyAction, rollback));
+        final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NETCONF_EDIT_CONFIG_QNAME), getEditConfigContent(datastore, editStructure, modifyAction, rollback));
 
         Futures.addCallback(future, callback);
         return future;
     }
 
-    private CompositeNode getEditConfigContent(final QName datastore, final CompositeNode editStructure, final Optional<ModifyAction> defaultOperation, final boolean rollback) {
-        final CompositeNodeBuilder<ImmutableCompositeNode> ret = ImmutableCompositeNode.builder();
+    public DataContainerChild<?, ?> createEditConfigStrcture(final Optional<NormalizedNode<?, ?>> lastChild, final Optional<ModifyAction> operation, final YangInstanceIdentifier dataPath) {
+        return NetconfMessageTransformUtil.createEditConfigStructure(schemaContext, dataPath, operation, lastChild);
+    }
+
+    private ContainerNode getEditConfigContent(final QName datastore, final DataContainerChild<?, ?> editStructure, final Optional<ModifyAction> defaultOperation, final boolean rollback) {
+        final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> editBuilder = Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_EDIT_CONFIG_QNAME));
 
         // Target
-        ret.add(getTargetNode(datastore));
+        editBuilder.withChild(getTargetNode(datastore));
 
         // Default operation
         if(defaultOperation.isPresent()) {
-            final SimpleNode<String> defOp = NodeFactory.createImmutableSimpleNode(NETCONF_DEFAULT_OPERATION_QNAME, null, NetconfMessageTransformUtil.modifyOperationToXmlString(defaultOperation.get()));
-            ret.add(defOp);
+            editBuilder.withChild(Builders.leafBuilder().withNodeIdentifier(toId(NETCONF_DEFAULT_OPERATION_QNAME)).withValue(NetconfMessageTransformUtil.modifyOperationToXmlString(defaultOperation.get())).build());
         }
 
         // Error option
         if(rollback) {
-            ret.addLeaf(NETCONF_ERROR_OPTION_QNAME, ROLLBACK_ON_ERROR_OPTION);
+            editBuilder.withChild(Builders.leafBuilder().withNodeIdentifier(toId(NETCONF_ERROR_OPTION_QNAME)).withValue(ROLLBACK_ON_ERROR_OPTION).build());
         }
 
-        ret.setQName(NETCONF_EDIT_CONFIG_QNAME);
         // Edit content
-        ret.add(editStructure);
-        return ret.toInstance();
+        editBuilder.withChild(editStructure);
+        return editBuilder.build();
     }
 
-    private static CompositeNode getSourceNode(final QName datastore) {
-        return NodeFactory.createImmutableCompositeNode(NETCONF_SOURCE_QNAME, null,
-                Collections.<Node<?>> singletonList(new SimpleNodeTOImpl<>(datastore, null, null)));
+    public static DataContainerChild<?, ?> getSourceNode(final QName datastore) {
+        return Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_SOURCE_QNAME))
+                .withChild(
+                        Builders.choiceBuilder().withNodeIdentifier(toId(ConfigSource.QNAME)).withChild(
+                                Builders.leafBuilder().withNodeIdentifier(toId(datastore)).build()).build()
+                ).build();
     }
 
-
-    public static CompositeNode getLockContent(final QName datastore) {
-        return NodeFactory.createImmutableCompositeNode(NETCONF_LOCK_QNAME, null, Collections.<Node<?>>singletonList(
-                getTargetNode(datastore)));
+    public static NormalizedNode<?, ?> getLockContent(final QName datastore) {
+        return Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_LOCK_QNAME))
+                .withChild(getTargetNode(datastore)).build();
     }
 
-    private static CompositeNode getTargetNode(final QName datastore) {
-        return NodeFactory.createImmutableCompositeNode(NETCONF_TARGET_QNAME, null, Collections.<Node<?>>singletonList(
-                NodeFactory.createImmutableSimpleNode(datastore, null, null)
-        ));
+    public static DataContainerChild<?, ?> getTargetNode(final QName datastore) {
+        return Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_TARGET_QNAME))
+                .withChild(
+                        Builders.choiceBuilder().withNodeIdentifier(toId(ConfigTarget.QNAME)).withChild(
+                                Builders.leafBuilder().withNodeIdentifier(toId(datastore)).build()).build()
+                ).build();
     }
 
-    public static CompositeNode getCopyConfigContent(final QName source, final QName target) {
-        return NodeFactory.createImmutableCompositeNode(NETCONF_LOCK_QNAME, null,
-                Lists.<Node<?>> newArrayList(getTargetNode(target), getSourceNode(source)));
+    public static NormalizedNode<?, ?> getCopyConfigContent(final QName source, final QName target) {
+        return Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_COPY_CONFIG_QNAME))
+                .withChild(getTargetNode(target)).withChild(getSourceNode(source)).build();
     }
 
-    public static CompositeNode getValidateContent(final QName source) {
-        return NodeFactory.createImmutableCompositeNode(NETCONF_VALIDATE_QNAME, null, Lists.<Node<?>> newArrayList(getSourceNode(source)));
+    public static NormalizedNode<?, ?> getValidateContent(final QName source) {
+        return Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_VALIDATE_QNAME))
+                .withChild(getSourceNode(source)).build();
     }
 
-    public static CompositeNode getUnLockContent(final QName preferedDatastore) {
-        return NodeFactory.createImmutableCompositeNode(NETCONF_UNLOCK_QNAME, null, Collections.<Node<?>>singletonList(
-                getTargetNode(preferedDatastore)));
+    public static NormalizedNode<?, ?> getUnLockContent(final QName datastore) {
+        return Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_UNLOCK_QNAME))
+                .withChild(getTargetNode(datastore)).build();
     }
 
 }
index 371907e..20085bb 100644 (file)
@@ -9,23 +9,18 @@ package org.opendaylight.controller.sal.connect.netconf.util;
 
 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.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import java.net.URI;
-import java.util.ArrayList;
 import java.util.Collections;
-import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import javax.annotation.Nullable;
 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.edit.config.input.EditContent;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.CreateSubscriptionInput;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfCapabilityChange;
@@ -33,20 +28,20 @@ import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.RpcError;
 import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.ModifyAction;
-import org.opendaylight.yangtools.yang.data.api.Node;
-import org.opendaylight.yangtools.yang.data.api.SimpleNode;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
-import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
-import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
-import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
-import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+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.NotificationDefinition;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
@@ -54,10 +49,13 @@ public class NetconfMessageTransformUtil {
 
     public static final String MESSAGE_ID_ATTR = "message-id";
     public static final QName CREATE_SUBSCRIPTION_RPC_QNAME = QName.cachedReference(QName.create(CreateSubscriptionInput.QNAME, "create-subscription"));
+    private static final String SUBTREE = "subtree";
 
     private NetconfMessageTransformUtil() {}
 
     public static final QName IETF_NETCONF_MONITORING = QName.create(NetconfState.QNAME, "ietf-netconf-monitoring");
+    public static final QName GET_DATA_QNAME = QName.create(IETF_NETCONF_MONITORING, "data");
+    public static final QName GET_SCHEMA_QNAME = QName.create(IETF_NETCONF_MONITORING, "get-schema");
     public static final QName IETF_NETCONF_MONITORING_SCHEMA_FORMAT = QName.create(IETF_NETCONF_MONITORING, "format");
     public static final QName IETF_NETCONF_MONITORING_SCHEMA_LOCATION = QName.create(IETF_NETCONF_MONITORING, "location");
     public static final QName IETF_NETCONF_MONITORING_SCHEMA_IDENTIFIER = QName.create(IETF_NETCONF_MONITORING, "identifier");
@@ -67,9 +65,10 @@ public class NetconfMessageTransformUtil {
     public static final QName IETF_NETCONF_NOTIFICATIONS = QName.create(NetconfCapabilityChange.QNAME, "ietf-netconf-notifications");
 
     public static URI NETCONF_URI = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0");
-    public static QName NETCONF_QNAME = QName.create(NETCONF_URI, null, "netconf");
+    public static QName NETCONF_QNAME = QName.create(NETCONF_URI.toString(), "2011-06-01", "netconf");
     public static QName NETCONF_DATA_QNAME = QName.create(NETCONF_QNAME, "data");
     public static QName NETCONF_RPC_REPLY_QNAME = QName.create(NETCONF_QNAME, "rpc-reply");
+    public static QName NETCONF_OK_QNAME = QName.create(NETCONF_QNAME, "ok");
     public static QName NETCONF_ERROR_OPTION_QNAME = QName.create(NETCONF_QNAME, "error-option");
     public static QName NETCONF_RUNNING_QNAME = QName.create(NETCONF_QNAME, "running");
     public static QName NETCONF_SOURCE_QNAME = QName.create(NETCONF_QNAME, "source");
@@ -106,46 +105,29 @@ public class NetconfMessageTransformUtil {
     public static QName NETCONF_UNLOCK_QNAME = QName.create(NETCONF_QNAME, "unlock");
 
     // Discard changes message
-    public static final CompositeNode DISCARD_CHANGES_RPC_CONTENT =
-            NodeFactory.createImmutableCompositeNode(NETCONF_DISCARD_CHANGES_QNAME, null, Collections.<Node<?>>emptyList());
+    public static final ContainerNode DISCARD_CHANGES_RPC_CONTENT =
+            Builders.containerBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NETCONF_DISCARD_CHANGES_QNAME)).build();
 
     // Commit changes message
-    public static final CompositeNode COMMIT_RPC_CONTENT =
-            NodeFactory.createImmutableCompositeNode(NETCONF_COMMIT_QNAME, null, Collections.<Node<?>>emptyList());
+    public static final ContainerNode COMMIT_RPC_CONTENT =
+            Builders.containerBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NETCONF_COMMIT_QNAME)).build();
 
     // Get message
-    public static final CompositeNode GET_RPC_CONTENT =
-            NodeFactory.createImmutableCompositeNode(NETCONF_GET_QNAME, null, Collections.<Node<?>>emptyList());
+    public static final ContainerNode GET_RPC_CONTENT =
+            Builders.containerBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NETCONF_GET_QNAME)).build();
 
     // Create-subscription changes message
-    public static final CompositeNode CREATE_SUBSCRIPTION_RPC_CONTENT =
-            NodeFactory.createImmutableCompositeNode(CREATE_SUBSCRIPTION_RPC_QNAME, null, Collections.<Node<?>>emptyList());
+    public static final ContainerNode CREATE_SUBSCRIPTION_RPC_CONTENT =
+            Builders.containerBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CREATE_SUBSCRIPTION_RPC_QNAME)).build();
 
-    public static Node<?> toFilterStructure(final YangInstanceIdentifier identifier) {
-        Node<?> previous = null;
-        if (Iterables.isEmpty(identifier.getPathArguments())) {
-            return null;
-        }
+    public static DataContainerChild<?, ?> toFilterStructure(final YangInstanceIdentifier identifier, final SchemaContext ctx) {
+        final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> filterBuilder = Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_FILTER_QNAME));
+        filterBuilder.withAttributes(Collections.singletonMap(NETCONF_TYPE_QNAME, SUBTREE));
 
-        for (final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument component : identifier.getReversePathArguments()) {
-            if (component instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates) {
-                previous = toNode((YangInstanceIdentifier.NodeIdentifierWithPredicates)component, previous);
-            } else {
-                previous = toNode(component, previous);
-            }
+        if (Iterables.isEmpty(identifier.getPathArguments()) == false) {
+            filterBuilder.withChild((DataContainerChild<?, ?>) InstanceIdToNodes.serialize(ctx, identifier));
         }
-        return filter("subtree", previous);
-    }
-
-    static Node<?> toNode(final YangInstanceIdentifier.NodeIdentifierWithPredicates argument, final Node<?> node) {
-        final List<Node<?>> list = new ArrayList<>();
-        for (final Map.Entry<QName, Object> arg : argument.getKeyValues().entrySet()) {
-            list.add(new SimpleNodeTOImpl(arg.getKey(), null, arg.getValue()));
-        }
-        if (node != null) {
-            list.add(node);
-        }
-        return new CompositeNodeTOImpl(argument.getNodeType(), null, list);
+        return filterBuilder.build();
     }
 
     public static void checkValidReply(final NetconfMessage input, final NetconfMessage output)
@@ -154,7 +136,7 @@ public class NetconfMessageTransformUtil {
         final String outputMsgId = output.getDocument().getDocumentElement().getAttribute(MESSAGE_ID_ATTR);
 
         if(inputMsgId.equals(outputMsgId) == false) {
-            Map<String,String> errorInfo = ImmutableMap.<String,String>builder()
+            final Map<String,String> errorInfo = ImmutableMap.<String,String>builder()
                     .put( "actual-message-id", outputMsgId )
                     .put( "expected-message-id", inputMsgId )
                     .build();
@@ -168,24 +150,22 @@ public class NetconfMessageTransformUtil {
 
     public static void checkSuccessReply(final NetconfMessage output) throws NetconfDocumentedException {
         if(NetconfMessageUtil.isErrorMessage(output)) {
-            throw NetconfDocumentedException.fromXMLDocument( output.getDocument() );
+            throw NetconfDocumentedException.fromXMLDocument(output.getDocument());
         }
     }
 
-    public static RpcError toRpcError( final NetconfDocumentedException ex )
-    {
-        StringBuilder infoBuilder = new StringBuilder();
-        Map<String, String> errorInfo = ex.getErrorInfo();
-        if( errorInfo != null )
-        {
-            for( Entry<String,String> e: errorInfo.entrySet() ) {
+    public static RpcError toRpcError( final NetconfDocumentedException ex ) {
+        final StringBuilder infoBuilder = new StringBuilder();
+        final Map<String, String> errorInfo = ex.getErrorInfo();
+        if(errorInfo != null) {
+            for( final Entry<String,String> e: errorInfo.entrySet() ) {
                 infoBuilder.append( '<' ).append( e.getKey() ).append( '>' ).append( e.getValue() )
                 .append( "</" ).append( e.getKey() ).append( '>' );
 
             }
         }
 
-        ErrorSeverity severity = toRpcErrorSeverity( ex.getErrorSeverity() );
+        final ErrorSeverity severity = toRpcErrorSeverity( ex.getErrorSeverity() );
         return severity == ErrorSeverity.ERROR ?
                 RpcResultBuilder.newError(
                         toRpcErrorType( ex.getErrorType() ), ex.getErrorTag().getTagValue(),
@@ -204,8 +184,7 @@ public class NetconfMessageTransformUtil {
         }
     }
 
-    private static RpcError.ErrorType toRpcErrorType( final NetconfDocumentedException.ErrorType type )
-    {
+    private static RpcError.ErrorType toRpcErrorType(final NetconfDocumentedException.ErrorType type) {
         switch( type ) {
         case protocol:
             return RpcError.ErrorType.PROTOCOL;
@@ -218,36 +197,12 @@ public class NetconfMessageTransformUtil {
         }
     }
 
-    public static CompositeNode flattenInput(final CompositeNode node) {
-        final QName inputQName = QName.create(node.getNodeType(), "input");
-        final CompositeNode input = node.getFirstCompositeByName(inputQName);
-        if (input == null) {
-            return node;
-        }
-        if (input instanceof CompositeNode) {
-
-            final List<Node<?>> nodes = ImmutableList.<Node<?>> builder() //
-                    .addAll(input.getValue()) //
-                    .addAll(Collections2.filter(node.getValue(), new Predicate<Node<?>>() {
-                        @Override
-                        public boolean apply(@Nullable final Node<?> input) {
-                            return !inputQName.equals(input.getNodeType());
-                        }
-                    })) //
-                    .build();
-
-            return ImmutableCompositeNode.create(node.getNodeType(), nodes);
-        }
-
-        return input;
+    public static YangInstanceIdentifier.NodeIdentifier toId(final YangInstanceIdentifier.PathArgument qname) {
+        return toId(qname.getNodeType());
     }
 
-    static Node<?> toNode(final YangInstanceIdentifier.PathArgument argument, final Node<?> node) {
-        if (node != null) {
-            return new CompositeNodeTOImpl(argument.getNodeType(), null, Collections.<Node<?>> singletonList(node));
-        } else {
-            return new SimpleNodeTOImpl<Void>(argument.getNodeType(), null, null);
-        }
+    public static YangInstanceIdentifier.NodeIdentifier toId(final QName nodeType) {
+        return new YangInstanceIdentifier.NodeIdentifier(nodeType);
     }
 
     public static Element getDataSubtree(final Document doc) {
@@ -260,14 +215,6 @@ public class NetconfMessageTransformUtil {
                         NETCONF_GET_QNAME.getLocalName()));
     }
 
-    public static boolean isGetOperation(final QName rpc) {
-        return NETCONF_URI.equals(rpc.getNamespace()) && rpc.getLocalName().equals(NETCONF_GET_QNAME.getLocalName());
-    }
-
-    public static boolean isGetConfigOperation(final QName rpc) {
-        return NETCONF_URI.equals(rpc.getNamespace()) && rpc.getLocalName().equals(NETCONF_GET_CONFIG_QNAME.getLocalName());
-    }
-
     public static boolean isDataEditOperation(final QName rpc) {
         return NETCONF_URI.equals(rpc.getNamespace())
                 && rpc.getLocalName().equals(NETCONF_EDIT_CONFIG_QNAME.getLocalName());
@@ -298,6 +245,16 @@ public class NetconfMessageTransformUtil {
         return new NodeContainerProxy(NETCONF_RPC_QNAME, Sets.<DataSchemaNode>newHashSet(editConfigProxy));
     }
 
+    public static ContainerSchemaNode createSchemaForDataRead(final SchemaContext schemaContext) {
+        final QName config = QName.create(NETCONF_EDIT_CONFIG_QNAME, "data");
+        return new NodeContainerProxy(config, schemaContext.getChildNodes());
+    }
+
+
+    public static ContainerSchemaNode createSchemaForNotification(final NotificationDefinition next) {
+        return new NodeContainerProxy(next.getQName(), next.getChildNodes(), next.getAvailableAugmentations());
+    }
+
     /**
      * Creates artificial schema node for edit-config rpc. This artificial schema looks like:
      * <pre>
@@ -348,7 +305,6 @@ public class NetconfMessageTransformUtil {
         return new NodeContainerProxy(NETCONF_RPC_QNAME, Sets.<DataSchemaNode>newHashSet(editConfigProxy));
     }
 
-
     public static Optional<RpcDefinition> findSchemaForRpc(final QName rpcName, final SchemaContext schemaContext) {
         Preconditions.checkNotNull(rpcName);
         Preconditions.checkNotNull(schemaContext);
@@ -382,94 +338,29 @@ public class NetconfMessageTransformUtil {
         return new NodeContainerProxy(NETCONF_RPC_QNAME, Sets.<DataSchemaNode>newHashSet(rpcBodyProxy));
     }
 
-    public static CompositeNodeTOImpl wrap(final QName name, final Node<?> node) {
-        if (node != null) {
-            return new CompositeNodeTOImpl(name, null, Collections.<Node<?>> singletonList(node));
-        } else {
-            return new CompositeNodeTOImpl(name, null, Collections.<Node<?>> emptyList());
-        }
+    public static ContainerNode wrap(final QName name, final DataContainerChild<?, ?>... node) {
+        return Builders.containerBuilder().withNodeIdentifier(toId(name)).withValue(Lists.newArrayList(node)).build();
     }
 
-    public static CompositeNodeTOImpl wrap(final QName name, final Node<?> additional, final Node<?> node) {
-        if (node != null) {
-            return new CompositeNodeTOImpl(name, null, ImmutableList.of(additional, node));
+    public static DataContainerChild<?, ?> createEditConfigStructure(final SchemaContext ctx, final YangInstanceIdentifier dataPath,
+                                                                     final Optional<ModifyAction> operation, final Optional<NormalizedNode<?, ?>> lastChildOverride) {
+        // TODO The config element inside the EditContent should be AnyXml not Container, but AnyXml is based on outdated API
+        if(Iterables.isEmpty(dataPath.getPathArguments())) {
+            Preconditions.checkArgument(lastChildOverride.isPresent(), "Data has to be present when creating structure for top level element");
+            Preconditions.checkArgument(lastChildOverride.get() instanceof DataContainerChild<?, ?>,
+                    "Data has to be either container or a list node when creating structure for top level element, but was: %s", lastChildOverride.get());
+            return Builders.choiceBuilder().withNodeIdentifier(toId(EditContent.QNAME)).withChild(
+                    wrap(NETCONF_CONFIG_QNAME, ((DataContainerChild<?, ?>) lastChildOverride.get()))).build();
         } else {
-            return new CompositeNodeTOImpl(name, null, ImmutableList.<Node<?>> of(additional));
+            return Builders.choiceBuilder().withNodeIdentifier(toId(EditContent.QNAME)).withChild(
+                    wrap(NETCONF_CONFIG_QNAME, (DataContainerChild<?, ?>) InstanceIdToNodes.serialize(ctx, dataPath, lastChildOverride, operation))).build();
         }
     }
 
-    static ImmutableCompositeNode filter(final String type, final Node<?> node) {
-        final CompositeNodeBuilder<ImmutableCompositeNode> it = ImmutableCompositeNode.builder(); //
-        it.setQName(NETCONF_FILTER_QNAME);
-        it.setAttribute(NETCONF_TYPE_QNAME, type);
-        if (node != null) {
-            return it.add(node).toInstance();
-        } else {
-            return it.toInstance();
-        }
-    }
-
-    public static Node<?> findNode(final CompositeNode node, final YangInstanceIdentifier identifier) {
-
-        Node<?> current = node;
-        for (final YangInstanceIdentifier.PathArgument arg : identifier.getPathArguments()) {
-            if (current instanceof SimpleNode<?>) {
-                return null;
-            } else if (current instanceof CompositeNode) {
-                final CompositeNode currentComposite = (CompositeNode) current;
-
-                current = currentComposite.getFirstCompositeByName(arg.getNodeType());
-                if (current == null) {
-                    current = currentComposite.getFirstCompositeByName(arg.getNodeType().withoutRevision());
-                }
-                if (current == null) {
-                    current = currentComposite.getFirstSimpleByName(arg.getNodeType());
-                }
-                if (current == null) {
-                    current = currentComposite.getFirstSimpleByName(arg.getNodeType().withoutRevision());
-                }
-                if (current == null) {
-                    return null;
-                }
-            }
-        }
-        return current;
-    }
-
-    public static String modifyOperationToXmlString(final ModifyAction operation) {
-        return operation.name().toLowerCase();
-    }
-
-
-    public static CompositeNode createEditConfigStructure(final YangInstanceIdentifier dataPath, final Optional<ModifyAction> operation,
-                                                    final Optional<CompositeNode> lastChildOverride) {
-        Preconditions.checkArgument(Iterables.isEmpty(dataPath.getPathArguments()) == false, "Instance identifier with empty path %s", dataPath);
-
-        List<YangInstanceIdentifier.PathArgument> reversedPath = Lists.reverse(dataPath.getPath());
-
-        // Create deepest edit element with expected edit operation
-        CompositeNode previous = getDeepestEditElement(reversedPath.get(0), operation, lastChildOverride);
-
-        // Remove already processed deepest child
-        reversedPath = Lists.newArrayList(reversedPath);
-        reversedPath.remove(0);
-
-        // Create edit structure in reversed order
-        for (final YangInstanceIdentifier.PathArgument arg : reversedPath) {
-            final CompositeNodeBuilder<ImmutableCompositeNode> builder = ImmutableCompositeNode.builder();
-            builder.setQName(arg.getNodeType());
-
-            addPredicatesToCompositeNodeBuilder(getPredicates(arg), builder);
-
-            builder.add(previous);
-            previous = builder.toInstance();
-        }
-        return ImmutableCompositeNode.create(NETCONF_CONFIG_QNAME, ImmutableList.<Node<?>>of(previous));
-    }
-
-    public static void addPredicatesToCompositeNodeBuilder(final Map<QName, Object> predicates, final CompositeNodeBuilder<ImmutableCompositeNode> builder) {
+    public static void addPredicatesToCompositeNodeBuilder(final Map<QName, Object> predicates,
+                                                           final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> builder) {
         for (final Map.Entry<QName, Object> entry : predicates.entrySet()) {
-            builder.addLeaf(entry.getKey(), entry.getValue());
+            builder.withChild(Builders.leafBuilder().withNodeIdentifier(toId(entry.getKey())).withValue(entry.getValue()).build());
         }
     }
 
@@ -481,25 +372,11 @@ public class NetconfMessageTransformUtil {
         return predicates;
     }
 
-    public static CompositeNode getDeepestEditElement(final YangInstanceIdentifier.PathArgument arg, final Optional<ModifyAction> operation, final Optional<CompositeNode> lastChildOverride) {
-        final CompositeNodeBuilder<ImmutableCompositeNode> builder = ImmutableCompositeNode.builder();
-        builder.setQName(arg.getNodeType());
-
-        final Map<QName, Object> predicates = getPredicates(arg);
-        addPredicatesToCompositeNodeBuilder(predicates, builder);
-
-        if (operation.isPresent()) {
-            builder.setAttribute(NETCONF_OPERATION_QNAME, modifyOperationToXmlString(operation.get()));
-        }
-        if (lastChildOverride.isPresent()) {
-            final List<Node<?>> children = lastChildOverride.get().getValue();
-            for(final Node<?> child : children) {
-                if(!predicates.containsKey(child.getKey())) {
-                    builder.add(child);
-                }
-            }
-        }
+    public static SchemaPath toPath(final QName rpc) {
+        return SchemaPath.create(true, rpc);
+    }
 
-        return builder.toInstance();
+    public static String modifyOperationToXmlString(final ModifyAction operation) {
+        return operation.name().toLowerCase();
     }
 }
index 6e1e9d7..83c8803 100644 (file)
@@ -9,17 +9,16 @@
 package org.opendaylight.controller.sal.connect.netconf.util;
 
 import com.google.common.util.concurrent.FutureCallback;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.sal.connect.netconf.sal.tx.WriteRunningTx;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
  * Simple Netconf rpc logging callback
  */
-public class NetconfRpcFutureCallback implements FutureCallback<RpcResult<CompositeNode>> {
+public class NetconfRpcFutureCallback implements FutureCallback<DOMRpcResult> {
     private static final Logger LOG  = LoggerFactory.getLogger(WriteRunningTx.class);
 
     private final String type;
@@ -31,15 +30,15 @@ public class NetconfRpcFutureCallback implements FutureCallback<RpcResult<Compos
     }
 
     @Override
-    public void onSuccess(final RpcResult<CompositeNode> result) {
-        if(result.isSuccessful()) {
+    public void onSuccess(final DOMRpcResult result) {
+        if(result.getErrors().isEmpty()) {
             LOG.trace("{}: " + type + " invoked successfully", id);
         } else {
             onUnsuccess(result);
         }
     }
 
-    protected void onUnsuccess(final RpcResult<CompositeNode> result) {
+    protected void onUnsuccess(final DOMRpcResult result) {
         LOG.warn("{}: " + type + " invoked unsuccessfully: {}", id, result.getErrors());
     }
 
index 1896e69..b499bd8 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.controller.sal.connect.netconf.util;
 
+import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
@@ -31,22 +32,29 @@ class NodeContainerProxy implements ContainerSchemaNode {
 
     private final Map<QName, DataSchemaNode> childNodes;
     private final QName qName;
+    private final Set<AugmentationSchema> availableAugmentations;
 
-    public NodeContainerProxy(final QName qName, final Map<QName, DataSchemaNode> childNodes) {
+    public NodeContainerProxy(final QName qName, final Map<QName, DataSchemaNode> childNodes, final Set<AugmentationSchema> availableAugmentations) {
+        this.availableAugmentations = availableAugmentations;
         this.childNodes = Preconditions.checkNotNull(childNodes, "childNodes");
-        this.qName = Preconditions.checkNotNull(qName, "qName");
+        this.qName = qName;
     }
 
     public NodeContainerProxy(final QName qName, final Collection<DataSchemaNode> childNodes) {
-        this(qName, asMap(childNodes));
+        this(qName, asMap(childNodes), Collections.<AugmentationSchema>emptySet());
+    }
+
+    public NodeContainerProxy(final QName qName, final Collection<DataSchemaNode> childNodes, final Set<AugmentationSchema> availableAugmentations) {
+        this(qName, asMap(childNodes), availableAugmentations);
     }
 
     private static Map<QName, DataSchemaNode> asMap(final Collection<DataSchemaNode> childNodes) {
-        final Map<QName, DataSchemaNode> mapped = Maps.newHashMap();
-        for (final DataSchemaNode childNode : childNodes) {
-            mapped.put(childNode.getQName(), childNode);
-        }
-        return mapped;
+        return Maps.uniqueIndex(childNodes, new Function<DataSchemaNode, QName>() {
+            @Override
+            public QName apply(final DataSchemaNode input) {
+                return input.getQName();
+            }
+        });
     }
 
     @Override
@@ -86,7 +94,7 @@ class NodeContainerProxy implements ContainerSchemaNode {
 
     @Override
     public Set<AugmentationSchema> getAvailableAugmentations() {
-        throw new UnsupportedOperationException();
+        return availableAugmentations;
     }
 
     @Override
index ec945e0..61bd73a 100644 (file)
@@ -1,10 +1,10 @@
 /*
- * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
+* Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+*
+* This program and the accompanying materials are made available under the
+* terms of the Eclipse Public License v1.0 which accompanies this distribution,
+* and is available at http://www.eclipse.org/legal/epl-v10.html
+*/
 package org.opendaylight.controller.sal.connect.netconf;
 
 import static org.mockito.Matchers.any;
@@ -34,8 +34,12 @@ import org.junit.Test;
 import org.mockito.Mockito;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.opendaylight.controller.sal.connect.api.MessageTransformer;
 import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.controller.sal.connect.api.SchemaSourceProviderFactory;
@@ -45,13 +49,11 @@ import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceRpc;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.controller.sal.core.api.RpcImplementation;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
@@ -64,20 +66,24 @@ import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 
 public class NetconfDeviceTest {
 
-    private static final NetconfMessage netconfMessage;
-    private static final CompositeNode compositeNode;
+    private static final NetconfMessage notification;
+
+    private static final ContainerNode compositeNode;
 
     static {
         try {
-            netconfMessage = mockClass(NetconfMessage.class);
-            compositeNode = mockClass(CompositeNode.class);
+            compositeNode = mockClass(ContainerNode.class);
         } catch (final Exception e) {
             throw new RuntimeException(e);
         }
+        try {
+            notification = new NetconfMessage(XmlUtil.readXmlToDocument(NetconfDeviceTest.class.getResourceAsStream("/notification-payload.xml")));
+        } catch (Exception e) {
+            throw new ExceptionInInitializerError(e);
+        }
     }
 
-    private static final  RpcResult<NetconfMessage> rpcResult = RpcResultBuilder.success(netconfMessage).build();
-    private static final  RpcResult<CompositeNode> rpcResultC = RpcResultBuilder.success(compositeNode).build();
+    private static final DOMRpcResult rpcResultC = new DefaultDOMRpcResult(compositeNode);
 
     public static final String TEST_NAMESPACE = "test:namespace";
     public static final String TEST_MODULE = "test-module";
@@ -115,7 +121,7 @@ public class NetconfDeviceTest {
 
         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
                 = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaFactory, stateSchemasResolver);
-        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), getMessageTransformer(), true);
+        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(),true);
         // Monitoring not supported
         final NetconfSessionPreferences sessionCaps = getSessionCaps(false, capList);
         device.onRemoteSessionUp(sessionCaps, listener);
@@ -147,12 +153,12 @@ public class NetconfDeviceTest {
 
         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
                 = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaFactory, stateSchemasResolver);
-        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), getMessageTransformer(), true);
+        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), true);
         // Monitoring supported
         final NetconfSessionPreferences sessionCaps = getSessionCaps(true, Lists.newArrayList(TEST_CAPABILITY, TEST_CAPABILITY2));
         device.onRemoteSessionUp(sessionCaps, listener);
 
-        Mockito.verify(facade, Mockito.timeout(5000)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(RpcImplementation.class));
+        Mockito.verify(facade, Mockito.timeout(5000)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(NetconfDeviceRpc.class));
         Mockito.verify(schemaFactory, times(2)).createSchemaContext(anyCollectionOf(SourceIdentifier.class));
     }
 
@@ -169,28 +175,26 @@ public class NetconfDeviceTest {
         final RemoteDeviceHandler<NetconfSessionPreferences> facade = getFacade();
         final NetconfDeviceCommunicator listener = getListener();
 
-        final MessageTransformer<NetconfMessage> messageTransformer = getMessageTransformer();
-
         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
                 = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), getSchemaFactory(), stateSchemasResolver);
-        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), messageTransformer, true);
+        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), true);
 
-        device.onNotification(netconfMessage);
-        device.onNotification(netconfMessage);
+        device.onNotification(notification);
+        device.onNotification(notification);
 
-        verify(facade, times(0)).onNotification(any(CompositeNode.class));
+        verify(facade, times(0)).onNotification(any(ContainerNode.class));
 
         final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
                 Lists.newArrayList(TEST_CAPABILITY));
 
-        device.onRemoteSessionUp(sessionCaps, listener);
+        final DOMRpcService deviceRpc = mock(DOMRpcService.class);
+
+        device.handleSalInitializationSuccess(NetconfToNotificationTest.getNotificationSchemaContext(getClass()), sessionCaps, deviceRpc);
 
-        verify(messageTransformer, timeout(10000).times(2)).toNotification(netconfMessage);
-        verify(facade, timeout(10000).times(2)).onNotification(compositeNode);
+        verify(facade, timeout(10000).times(2)).onNotification(any(ContainerNode.class));
 
-        device.onNotification(netconfMessage);
-        verify(messageTransformer, timeout(10000).times(3)).toNotification(netconfMessage);
-        verify(facade, timeout(10000).times(3)).onNotification(compositeNode);
+        device.onNotification(notification);
+        verify(facade, timeout(10000).times(3)).onNotification(any(ContainerNode.class));
     }
 
     @Test
@@ -199,18 +203,16 @@ public class NetconfDeviceTest {
         final NetconfDeviceCommunicator listener = getListener();
 
         final SchemaContextFactory schemaContextProviderFactory = getSchemaFactory();
-        final MessageTransformer<NetconfMessage> messageTransformer = getMessageTransformer();
 
         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
                 = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaContextProviderFactory, stateSchemasResolver);
-        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), messageTransformer, true);
+        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), true);
         final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
                 Lists.newArrayList(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
         device.onRemoteSessionUp(sessionCaps, listener);
 
         verify(schemaContextProviderFactory, timeout(5000)).createSchemaContext(any(Collection.class));
-        verify(messageTransformer, timeout(5000)).onGlobalContextUpdated(any(SchemaContext.class));
-        verify(facade, timeout(5000)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(RpcImplementation.class));
+        verify(facade, timeout(5000)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(DOMRpcService.class));
 
         device.onRemoteSessionDown();
         verify(facade, timeout(5000)).onDeviceDisconnected();
@@ -218,8 +220,7 @@ public class NetconfDeviceTest {
         device.onRemoteSessionUp(sessionCaps, listener);
 
         verify(schemaContextProviderFactory, timeout(5000).times(2)).createSchemaContext(any(Collection.class));
-        verify(messageTransformer, timeout(5000).times(3)).onGlobalContextUpdated(any(SchemaContext.class));
-        verify(facade, timeout(5000).times(2)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(RpcImplementation.class));
+        verify(facade, timeout(5000).times(2)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(DOMRpcService.class));
     }
 
     private SchemaContextFactory getSchemaFactory() {
@@ -239,9 +240,9 @@ public class NetconfDeviceTest {
 
     private RemoteDeviceHandler<NetconfSessionPreferences> getFacade() throws Exception {
         final RemoteDeviceHandler<NetconfSessionPreferences> remoteDeviceHandler = mockCloseableClass(RemoteDeviceHandler.class);
-        doNothing().when(remoteDeviceHandler).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(RpcImplementation.class));
+        doNothing().when(remoteDeviceHandler).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(NetconfDeviceRpc.class));
         doNothing().when(remoteDeviceHandler).onDeviceDisconnected();
-        doNothing().when(remoteDeviceHandler).onNotification(any(CompositeNode.class));
+        doNothing().when(remoteDeviceHandler).onNotification(any(ContainerNode.class));
         return remoteDeviceHandler;
     }
 
@@ -277,10 +278,9 @@ public class NetconfDeviceTest {
 
     public MessageTransformer<NetconfMessage> getMessageTransformer() throws Exception {
         final MessageTransformer<NetconfMessage> messageTransformer = mockClass(MessageTransformer.class);
-        doReturn(netconfMessage).when(messageTransformer).toRpcRequest(any(QName.class), any(CompositeNode.class));
-        doReturn(rpcResultC).when(messageTransformer).toRpcResult(any(NetconfMessage.class), any(QName.class));
+        doReturn(notification).when(messageTransformer).toRpcRequest(any(SchemaPath.class), any(ContainerNode.class));
+        doReturn(rpcResultC).when(messageTransformer).toRpcResult(any(NetconfMessage.class), any(SchemaPath.class));
         doReturn(compositeNode).when(messageTransformer).toNotification(any(NetconfMessage.class));
-        doNothing().when(messageTransformer).onGlobalContextUpdated(any(SchemaContext.class));
         return messageTransformer;
     }
 
@@ -301,7 +301,7 @@ public class NetconfDeviceTest {
 
     public NetconfDeviceCommunicator getListener() throws Exception {
         final NetconfDeviceCommunicator remoteDeviceCommunicator = mockCloseableClass(NetconfDeviceCommunicator.class);
-        doReturn(Futures.immediateFuture(rpcResult)).when(remoteDeviceCommunicator).sendRequest(any(NetconfMessage.class), any(QName.class));
+//        doReturn(Futures.immediateFuture(rpcResult)).when(remoteDeviceCommunicator).sendRequest(any(NetconfMessage.class), any(QName.class));
         return remoteDeviceCommunicator;
     }
 }
index ccae5d4..405579e 100644 (file)
@@ -1,25 +1,35 @@
 package org.opendaylight.controller.sal.connect.netconf;
 
+import static org.hamcrest.CoreMatchers.hasItem;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
-import static org.hamcrest.CoreMatchers.hasItem;
 
+import java.net.InetSocketAddress;
+import java.util.Collections;
 import java.util.Set;
 import org.junit.Test;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlUtils;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.ToNormalizedNodeParser;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.w3c.dom.Document;
+import org.w3c.dom.Element;
 
 public class NetconfStateSchemasTest {
 
     @Test
     public void testCreate() throws Exception {
+        final DataSchemaNode schemasNode = ((ContainerSchemaNode) NetconfDevice.INIT_SCHEMA_CTX.getDataChildByName("netconf-state")).getDataChildByName("schemas");
+
         final Document schemasXml = XmlUtil.readXmlToDocument(getClass().getResourceAsStream("/netconf-state.schemas.payload.xml"));
-        final CompositeNode compositeNodeSchemas = (CompositeNode) XmlDocumentUtils.toDomNode(schemasXml);
-        final NetconfStateSchemas schemas = NetconfStateSchemas.create(new RemoteDeviceId("device"), compositeNodeSchemas);
+        final ToNormalizedNodeParser<Element, ContainerNode, ContainerSchemaNode> containerNodeParser = DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER).getContainerNodeParser();
+        final ContainerNode compositeNodeSchemas = containerNodeParser.parse(Collections.singleton(schemasXml.getDocumentElement()), (ContainerSchemaNode) schemasNode);
+        final NetconfStateSchemas schemas = NetconfStateSchemas.create(new RemoteDeviceId("device", new InetSocketAddress(99)), compositeNodeSchemas);
 
         final Set<QName> availableYangSchemasQNames = schemas.getAvailableYangSchemasQNames();
         assertEquals(73, availableYangSchemasQNames.size());
index 5d19188..157a3b7 100644 (file)
@@ -3,6 +3,8 @@ package org.opendaylight.controller.sal.connect.netconf;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.Iterables;
 import java.io.InputStream;
 import java.util.Collections;
 import java.util.List;
@@ -13,7 +15,7 @@ import org.junit.Test;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.opendaylight.controller.sal.connect.netconf.schema.mapping.NetconfMessageTransformer;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.parser.api.YangContextParser;
@@ -32,15 +34,9 @@ public class NetconfToNotificationTest {
     @SuppressWarnings("deprecation")
     @Before
     public void setup() throws Exception {
-        final List<InputStream> modelsToParse = Collections.singletonList(getClass().getResourceAsStream("/schemas/user-notification.yang"));
-        final YangContextParser parser = new YangParserImpl();
-        final Set<Module> modules = parser.parseYangModelsFromStreams(modelsToParse);
-        assertTrue(!modules.isEmpty());
-        final SchemaContext schemaContext = parser.resolveSchemaContext(modules);
-        assertNotNull(schemaContext);
+        final SchemaContext schemaContext = getNotificationSchemaContext(getClass());
 
-        messageTransformer = new NetconfMessageTransformer();
-        messageTransformer.onGlobalContextUpdated(schemaContext);
+        messageTransformer = new NetconfMessageTransformer(schemaContext);
 
         final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
         factory.setNamespaceAware(true);
@@ -52,12 +48,21 @@ public class NetconfToNotificationTest {
         userNotification = new NetconfMessage(doc);
     }
 
+    static SchemaContext getNotificationSchemaContext(Class<?> loadClass) {
+        final List<InputStream> modelsToParse = Collections.singletonList(loadClass.getResourceAsStream("/schemas/user-notification.yang"));
+        final YangContextParser parser = new YangParserImpl();
+        final Set<Module> modules = parser.parseYangModelsFromStreams(modelsToParse);
+        assertTrue(!modules.isEmpty());
+        final SchemaContext schemaContext = parser.resolveSchemaContext(modules);
+        assertNotNull(schemaContext);
+        return schemaContext;
+    }
+
     @Test
     public void test() throws Exception {
-        final CompositeNode root = messageTransformer.toNotification(userNotification);
-
+        final ContainerNode root = messageTransformer.toNotification(userNotification);
         assertNotNull(root);
-        assertEquals(6, root.size());
-        assertEquals("user-visited-page", root.getKey().getLocalName());
+        assertEquals(6, Iterables.size(root.getValue()));
+        assertEquals("user-visited-page", root.getNodeType().getLocalName());
     }
 }
index 3425739..49f0aba 100644 (file)
@@ -3,7 +3,10 @@ package org.opendaylight.controller.sal.connect.netconf;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toId;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath;
 
+import com.google.common.collect.Sets;
 import java.io.InputStream;
 import java.util.Collections;
 import java.util.List;
@@ -13,13 +16,12 @@ import org.junit.Test;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.opendaylight.controller.sal.connect.netconf.schema.mapping.NetconfMessageTransformer;
-import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.Node;
-import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
-import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.parser.api.YangContextParser;
@@ -48,7 +50,6 @@ public class NetconfToRpcRequestTest {
     private final static QName GET_QNAME = QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "get");
     private final static QName GET_CONFIG_QNAME = QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "get-config");
 
-    static SchemaContext notifCtx;
     static SchemaContext cfgCtx;
     static NetconfMessageTransformer messageTransformer;
 
@@ -61,187 +62,29 @@ public class NetconfToRpcRequestTest {
         final Set<Module> notifModules = parser.parseYangModelsFromStreams(modelsToParse);
         assertTrue(!notifModules.isEmpty());
 
-        notifCtx = parser.resolveSchemaContext(notifModules);
-        assertNotNull(notifCtx);
-
         modelsToParse = Collections
             .singletonList(NetconfToRpcRequestTest.class.getResourceAsStream("/schemas/config-test-rpc.yang"));
         parser = new YangParserImpl();
         final Set<Module> configModules = parser.parseYangModelsFromStreams(modelsToParse);
-        cfgCtx = parser.resolveSchemaContext(configModules);
+        cfgCtx = parser.resolveSchemaContext(Sets.union(configModules, notifModules));
         assertNotNull(cfgCtx);
 
-        messageTransformer = new NetconfMessageTransformer();
-    }
-
-    @Test
-    public void testIsDataEditOperation() throws Exception {
-        messageTransformer.onGlobalContextUpdated(cfgCtx);
-
-        final CompositeNodeBuilder<ImmutableCompositeNode> rootBuilder = ImmutableCompositeNode.builder();
-        rootBuilder.setQName(EDIT_CONFIG_QNAME);
-
-        final CompositeNodeBuilder<ImmutableCompositeNode> inputBuilder = ImmutableCompositeNode.builder();
-        inputBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "input"));
-
-        final CompositeNodeBuilder<ImmutableCompositeNode> targetBuilder = ImmutableCompositeNode.builder();
-        targetBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "target"));
-        targetBuilder.addLeaf(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "running"), null);
-
-        final CompositeNodeBuilder<ImmutableCompositeNode> configBuilder = ImmutableCompositeNode.builder();
-        configBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "config"));
-
-        final CompositeNodeBuilder<ImmutableCompositeNode> anyxmlTopBuilder = ImmutableCompositeNode.builder();
-        anyxmlTopBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "top"));
-
-        final CompositeNodeBuilder<ImmutableCompositeNode> anyxmlInterfBuilder = ImmutableCompositeNode.builder();
-        anyxmlInterfBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "interface"));
-
-        anyxmlInterfBuilder.addLeaf(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "name"), "Ethernet0/0");
-        anyxmlInterfBuilder.addLeaf(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "mtu"), "1500");
-
-        anyxmlTopBuilder.add(anyxmlInterfBuilder.toInstance());
-        configBuilder.add(anyxmlTopBuilder.toInstance());
-
-        inputBuilder.add(targetBuilder.toInstance());
-        inputBuilder.add(configBuilder.toInstance());
-
-        rootBuilder.add(inputBuilder.toInstance());
-        final ImmutableCompositeNode root = rootBuilder.toInstance();
-
-        final NetconfMessage message = messageTransformer.toRpcRequest(EDIT_CONFIG_QNAME, root);
-        assertNotNull(message);
-
-        final Document xmlDoc = message.getDocument();
-        org.w3c.dom.Node rpcChild = xmlDoc.getFirstChild();
-        assertEquals(rpcChild.getLocalName(), "rpc");
-
-        final org.w3c.dom.Node editConfigNode = rpcChild.getFirstChild();
-        assertEquals(editConfigNode.getLocalName(), "edit-config");
-
-        final org.w3c.dom.Node targetNode = editConfigNode.getFirstChild();
-        assertEquals(targetNode.getLocalName(), "target");
-
-        final org.w3c.dom.Node runningNode = targetNode.getFirstChild();
-        assertEquals(runningNode.getLocalName(), "running");
-
-        final org.w3c.dom.Node configNode = targetNode.getNextSibling();
-        assertEquals(configNode.getLocalName(), "config");
-
-        final org.w3c.dom.Node topNode = configNode.getFirstChild();
-        assertEquals(topNode.getLocalName(), "top");
-
-        final org.w3c.dom.Node interfaceNode = topNode.getFirstChild();
-        assertEquals(interfaceNode.getLocalName(), "interface");
-
-        final org.w3c.dom.Node nameNode = interfaceNode.getFirstChild();
-        assertEquals(nameNode.getLocalName(), "name");
-
-        final org.w3c.dom.Node mtuNode = nameNode.getNextSibling();
-        assertEquals(mtuNode.getLocalName(), "mtu");
-    }
-
-    @Test
-    public void testIsGetOperation() throws Exception {
-        messageTransformer.onGlobalContextUpdated(cfgCtx);
-
-        final CompositeNodeBuilder<ImmutableCompositeNode> rootBuilder = ImmutableCompositeNode.builder();
-        rootBuilder.setQName(GET_QNAME);
-
-        final CompositeNodeBuilder<ImmutableCompositeNode> inputBuilder = ImmutableCompositeNode.builder();
-        inputBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "input"));
-
-        rootBuilder.add(inputBuilder.toInstance());
-        final ImmutableCompositeNode root = rootBuilder.toInstance();
-
-        final NetconfMessage message = messageTransformer.toRpcRequest(GET_QNAME, root);
-        assertNotNull(message);
-
-        final Document xmlDoc = message.getDocument();
-        final org.w3c.dom.Node rpcChild = xmlDoc.getFirstChild();
-        assertEquals(rpcChild.getLocalName(), "rpc");
-
-        final org.w3c.dom.Node get = rpcChild.getFirstChild();
-        assertEquals(get.getLocalName(), "get");
+        messageTransformer = new NetconfMessageTransformer(cfgCtx);
     }
 
-    @Test
-    public void testIsGetConfigOperation() throws Exception {
-        messageTransformer.onGlobalContextUpdated(cfgCtx);
-
-        final CompositeNodeBuilder<ImmutableCompositeNode> rootBuilder = ImmutableCompositeNode.builder();
-        rootBuilder.setQName(GET_CONFIG_QNAME);
-
-        final CompositeNodeBuilder<ImmutableCompositeNode> inputBuilder = ImmutableCompositeNode.builder();
-        inputBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "input"));
-
-        final CompositeNodeBuilder<ImmutableCompositeNode> sourceBuilder = ImmutableCompositeNode.builder();
-        sourceBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "source"));
-        sourceBuilder.addLeaf(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "running"), null);
-
-        final CompositeNodeBuilder<ImmutableCompositeNode> anyxmlFilterBuilder = ImmutableCompositeNode.builder();
-        anyxmlFilterBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "filter"));
-
-        final CompositeNodeBuilder<ImmutableCompositeNode> anyxmlTopBuilder = ImmutableCompositeNode.builder();
-        anyxmlTopBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "top"));
-        anyxmlTopBuilder.addLeaf(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "users"), null);
-
-        anyxmlFilterBuilder.add(anyxmlTopBuilder.toInstance());
-
-        inputBuilder.add(sourceBuilder.toInstance());
-        inputBuilder.add(anyxmlFilterBuilder.toInstance());
-        rootBuilder.add(inputBuilder.toInstance());
-        final ImmutableCompositeNode root = rootBuilder.toInstance();
-
-        final NetconfMessage message = messageTransformer.toRpcRequest(GET_CONFIG_QNAME, root);
-        assertNotNull(message);
-
-        final Document xmlDoc = message.getDocument();
-        final org.w3c.dom.Node rpcChild = xmlDoc.getFirstChild();
-        assertEquals(rpcChild.getLocalName(), "rpc");
-
-        final org.w3c.dom.Node getConfig = rpcChild.getFirstChild();
-        assertEquals(getConfig.getLocalName(), "get-config");
-
-        final org.w3c.dom.Node sourceNode = getConfig.getFirstChild();
-        assertEquals(sourceNode.getLocalName(), "source");
-
-        final org.w3c.dom.Node runningNode = sourceNode.getFirstChild();
-        assertEquals(runningNode.getLocalName(), "running");
-
-        final org.w3c.dom.Node filterNode = sourceNode.getNextSibling();
-        assertEquals(filterNode.getLocalName(), "filter");
-
-        final org.w3c.dom.Node topNode = filterNode.getFirstChild();
-        assertEquals(topNode.getLocalName(), "top");
-
-        final org.w3c.dom.Node usersNode = topNode.getFirstChild();
-        assertEquals(usersNode.getLocalName(), "users");
+    private LeafNode<Object> buildLeaf(final QName running, final Object value) {
+        return Builders.leafBuilder().withNodeIdentifier(toId(running)).withValue(value).build();
     }
 
     @Test
     public void testUserDefinedRpcCall() throws Exception {
-        messageTransformer.onGlobalContextUpdated(notifCtx);
-
-        final CompositeNodeBuilder<ImmutableCompositeNode> rootBuilder = ImmutableCompositeNode.builder();
-        rootBuilder.setQName(SUBSCRIBE_RPC_NAME);
-
-        final CompositeNodeBuilder<ImmutableCompositeNode> inputBuilder = ImmutableCompositeNode.builder();
-        inputBuilder.setQName(INPUT_QNAME);
-        inputBuilder.addLeaf(STREAM_NAME, "NETCONF");
+        final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> rootBuilder = Builders.containerBuilder();
+        rootBuilder.withNodeIdentifier(toId(SUBSCRIBE_RPC_NAME));
 
-        rootBuilder.add(inputBuilder.toInstance());
-        final ImmutableCompositeNode root = rootBuilder.toInstance();
+        rootBuilder.withChild(buildLeaf(STREAM_NAME, "NETCONF"));
+        final ContainerNode root = rootBuilder.build();
 
-        final CompositeNode flattenedNode = NetconfMessageTransformUtil.flattenInput(root);
-        assertNotNull(flattenedNode);
-        assertEquals(1, flattenedNode.size());
-
-        final List<CompositeNode> inputNode = flattenedNode.getCompositesByName(INPUT_QNAME);
-        assertNotNull(inputNode);
-        assertTrue(inputNode.isEmpty());
-
-        final NetconfMessage message = messageTransformer.toRpcRequest(SUBSCRIBE_RPC_NAME, root);
+        final NetconfMessage message = messageTransformer.toRpcRequest(toPath(SUBSCRIBE_RPC_NAME), root);
         assertNotNull(message);
 
         final Document xmlDoc = message.getDocument();
@@ -256,7 +99,8 @@ public class NetconfToRpcRequestTest {
 
     }
 
-    @Test
+    // The edit config defined in yang has no output
+    @Test(expected = IllegalArgumentException.class)
     public void testRpcResponse() throws Exception {
         final NetconfMessage response = new NetconfMessage(XmlUtil.readXmlToDocument(
                 "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"m-5\">\n" +
@@ -265,9 +109,8 @@ public class NetconfToRpcRequestTest {
                 "</data>\n" +
                 "</rpc-reply>\n"
         ));
-        final RpcResult<CompositeNode> compositeNodeRpcResult = messageTransformer.toRpcResult(response, SUBSCRIBE_RPC_NAME);
-        final Node<?> dataNode = compositeNodeRpcResult.getResult().getValue().get(0);
-        assertEquals("module schema", dataNode.getValue());
+
+        messageTransformer.toRpcResult(response, toPath(EDIT_CONFIG_QNAME));
     }
 
 }
index a37fade..4f6366f 100644 (file)
@@ -2,16 +2,18 @@ package org.opendaylight.controller.sal.connect.netconf.sal.tx;
 
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.same;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
 import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.DISCARD_CHANGES_RPC_CONTENT;
 import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME;
 import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_RUNNING_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath;
 
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
+import java.net.InetSocketAddress;
 import java.util.Collections;
 import org.junit.Before;
 import org.junit.Test;
@@ -20,46 +22,45 @@ import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
-import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
+import org.opendaylight.controller.sal.connect.netconf.NetconfDevice;
 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfBaseOps;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
-import org.opendaylight.controller.sal.core.api.RpcImplementation;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 
 public class NetconfDeviceWriteOnlyTxTest {
 
-    private final RemoteDeviceId id = new RemoteDeviceId("test-mount");
+    private final RemoteDeviceId id = new RemoteDeviceId("test-mount", new InetSocketAddress(99));
 
     @Mock
-    private RpcImplementation rpc;
-    @Mock
-    private DataNormalizer normalizer;
+    private DOMRpcService rpc;
     private YangInstanceIdentifier yangIId;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
-        ListenableFuture<RpcResult<CompositeNode>> successFuture = Futures.immediateFuture(RpcResultBuilder.<CompositeNode>success().build());
+        final CheckedFuture<DefaultDOMRpcResult, Exception> successFuture =
+                Futures.immediateCheckedFuture(new DefaultDOMRpcResult(((NormalizedNode<?, ?>) null)));
 
         doReturn(successFuture)
-                .doReturn(Futures.<RpcResult<CompositeNode>>immediateFailedFuture(new IllegalStateException("Failed tx")))
+                .doReturn(Futures.immediateFailedCheckedFuture(new IllegalStateException("Failed tx")))
                 .doReturn(successFuture)
-                .when(rpc).invokeRpc(any(QName.class), any(CompositeNode.class));
+                .when(rpc).invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
 
-        yangIId = YangInstanceIdentifier.builder().node(QName.create("namespace", "2012-12-12", "name")).build();
-        doReturn(yangIId).when(normalizer).toLegacy(yangIId);
+        yangIId = YangInstanceIdentifier.builder().node(NetconfState.QNAME).build();
     }
 
     @Test
     public void testDiscardChanges() {
-        final WriteCandidateTx tx = new WriteCandidateTx(id, new NetconfBaseOps(rpc), normalizer,
+        final WriteCandidateTx tx = new WriteCandidateTx(id, new NetconfBaseOps(rpc, mock(SchemaContext.class)),
                 NetconfSessionPreferences.fromStrings(Collections.<String>emptySet()));
         final CheckedFuture<Void, TransactionCommitFailedException> submitFuture = tx.submit();
         try {
@@ -67,10 +68,10 @@ public class NetconfDeviceWriteOnlyTxTest {
         } catch (final TransactionCommitFailedException e) {
             // verify discard changes was sent
             final InOrder inOrder = inOrder(rpc);
-            inOrder.verify(rpc).invokeRpc(NetconfMessageTransformUtil.NETCONF_LOCK_QNAME, NetconfBaseOps.getLockContent(NETCONF_CANDIDATE_QNAME));
-            inOrder.verify(rpc).invokeRpc(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME, NetconfMessageTransformUtil.COMMIT_RPC_CONTENT);
-            inOrder.verify(rpc).invokeRpc(NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME, DISCARD_CHANGES_RPC_CONTENT);
-            inOrder.verify(rpc).invokeRpc(NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME, NetconfBaseOps.getUnLockContent(NETCONF_CANDIDATE_QNAME));
+            inOrder.verify(rpc).invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_LOCK_QNAME), NetconfBaseOps.getLockContent(NETCONF_CANDIDATE_QNAME));
+            inOrder.verify(rpc).invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME), NetconfMessageTransformUtil.COMMIT_RPC_CONTENT);
+            inOrder.verify(rpc).invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME), DISCARD_CHANGES_RPC_CONTENT);
+            inOrder.verify(rpc).invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME), NetconfBaseOps.getUnLockContent(NETCONF_CANDIDATE_QNAME));
             return;
         }
 
@@ -79,20 +80,20 @@ public class NetconfDeviceWriteOnlyTxTest {
 
     @Test
     public void testDiscardChangesNotSentWithoutCandidate() {
-        doReturn(Futures.immediateFuture(RpcResultBuilder.<CompositeNode>success().build()))
-        .doReturn(Futures.<RpcResult<CompositeNode>>immediateFailedFuture(new IllegalStateException("Failed tx")))
-                .when(rpc).invokeRpc(any(QName.class), any(CompositeNode.class));
+        doReturn(Futures.immediateCheckedFuture(new DefaultDOMRpcResult(((NormalizedNode<?, ?>) null))))
+                .doReturn(Futures.immediateFailedCheckedFuture(new IllegalStateException("Failed tx")))
+                .when(rpc).invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
 
-        final WriteRunningTx tx = new WriteRunningTx(id, new NetconfBaseOps(rpc), normalizer,
+        final WriteRunningTx tx = new WriteRunningTx(id, new NetconfBaseOps(rpc, NetconfDevice.INIT_SCHEMA_CTX),
                 NetconfSessionPreferences.fromStrings(Collections.<String>emptySet()));
         try {
             tx.delete(LogicalDatastoreType.CONFIGURATION, yangIId);
         } catch (final Exception e) {
             // verify discard changes was sent
             final InOrder inOrder = inOrder(rpc);
-            inOrder.verify(rpc).invokeRpc(NetconfMessageTransformUtil.NETCONF_LOCK_QNAME, NetconfBaseOps.getLockContent(NETCONF_RUNNING_QNAME));
-            inOrder.verify(rpc).invokeRpc(same(NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME), any(CompositeNode.class));
-            inOrder.verify(rpc).invokeRpc(NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME, NetconfBaseOps.getUnLockContent(NETCONF_RUNNING_QNAME));
+            inOrder.verify(rpc).invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_LOCK_QNAME), NetconfBaseOps.getLockContent(NETCONF_RUNNING_QNAME));
+            inOrder.verify(rpc).invokeRpc(eq(toPath(NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME)), any(NormalizedNode.class));
+            inOrder.verify(rpc).invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME), NetconfBaseOps.getUnLockContent(NETCONF_RUNNING_QNAME));
             return;
         }
 
index 072cb18..e8587d6 100644 (file)
@@ -10,8 +10,10 @@ package org.opendaylight.controller.sal.connect.netconf.sal.tx;
 
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
+import com.google.common.util.concurrent.Futures;
 import java.net.InetSocketAddress;
 import org.junit.Before;
 import org.junit.Test;
@@ -20,43 +22,42 @@ import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
-import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfBaseOps;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
-import org.opendaylight.controller.sal.core.api.RpcImplementation;
-import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 
 public class ReadOnlyTxTest {
 
     private static final YangInstanceIdentifier path = YangInstanceIdentifier.create();
 
     @Mock
-    private RpcImplementation rpc;
+    private DOMRpcService rpc;
     @Mock
-    private DataNormalizer normalizer;
-    @Mock
-    private CompositeNode mockedNode;
+    private NormalizedNode<?, ?> mockedNode;
 
     @Before
     public void setUp() throws DataNormalizationException {
         MockitoAnnotations.initMocks(this);
-        doReturn(path).when(normalizer).toLegacy(any(YangInstanceIdentifier.class));
-        doReturn(com.google.common.util.concurrent.Futures.immediateFuture(RpcResultBuilder.success(mockedNode).build())).when(rpc).invokeRpc(any(org.opendaylight.yangtools.yang.common.QName.class), any(CompositeNode.class));
+        doReturn(Futures.immediateCheckedFuture(new DefaultDOMRpcResult(mockedNode))).when(rpc)
+                .invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
         doReturn("node").when(mockedNode).toString();
     }
 
     @Test
     public void testRead() throws Exception {
-        final NetconfBaseOps netconfOps = new NetconfBaseOps(rpc);
+        final NetconfBaseOps netconfOps = new NetconfBaseOps(rpc, mock(SchemaContext.class));
 
-        final ReadOnlyTx readOnlyTx = new ReadOnlyTx(netconfOps, normalizer, new RemoteDeviceId("a", new InetSocketAddress("localhost", 196)));
+        final ReadOnlyTx readOnlyTx = new ReadOnlyTx(netconfOps, new RemoteDeviceId("a", new InetSocketAddress("localhost", 196)));
 
         readOnlyTx.read(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create());
-        verify(rpc).invokeRpc(Mockito.same(NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME), any(CompositeNode.class));
+        verify(rpc).invokeRpc(Mockito.eq(NetconfMessageTransformUtil.toPath(NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME)), any(NormalizedNode.class));
         readOnlyTx.read(LogicalDatastoreType.OPERATIONAL, path);
-        verify(rpc).invokeRpc(Mockito.same(NetconfMessageTransformUtil.NETCONF_GET_QNAME), any(CompositeNode.class));
+        verify(rpc).invokeRpc(Mockito.eq(NetconfMessageTransformUtil.toPath(NetconfMessageTransformUtil.NETCONF_GET_QNAME)), any(NormalizedNode.class));
     }
 }
\ No newline at end of file
index 5a2c97c..ea0941e 100644 (file)
@@ -10,63 +10,272 @@ package org.opendaylight.controller.sal.connect.netconf.schema.mapping;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.GET_SCHEMA_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DATA_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_RUNNING_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.createEditConfigStructure;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toFilterStructure;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toId;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath;
 
-import java.io.InputStream;
+import com.google.common.base.Optional;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import java.io.IOException;
 import java.util.Collections;
 import java.util.List;
-import java.util.Set;
+import java.util.Map;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.XMLUnit;
 import org.hamcrest.CoreMatchers;
+import org.junit.Before;
 import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
-import org.opendaylight.controller.sal.connect.netconf.NetconfToRpcRequestTest;
+import org.opendaylight.controller.sal.connect.netconf.NetconfDevice;
+import org.opendaylight.controller.sal.connect.netconf.schema.NetconfRemoteSchemaYangSourceProvider;
+import org.opendaylight.controller.sal.connect.netconf.util.NetconfBaseOps;
+import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.$YangModuleInfoImpl;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Capabilities;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.Node;
-import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
-import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.data.api.ModifyAction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.xml.sax.SAXException;
 
 public class NetconfMessageTransformerTest {
 
-    private static final QName COMMIT_Q_NAME = QName.create("namespace", "2012-12-12", "commit");
+    private NetconfMessageTransformer netconfMessageTransformer;
+    private SchemaContext schema;
+
+    @Before
+    public void setUp() throws Exception {
+        XMLUnit.setIgnoreWhitespace(true);
+        XMLUnit.setIgnoreAttributeOrder(true);
+        XMLUnit.setIgnoreComments(true);
+
+        schema = getSchema();
+        netconfMessageTransformer = getTransformer(schema);
+
+    }
+
+    @Test
+    public void testDiscardChangesRequest() throws Exception {
+        final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(toPath(NETCONF_DISCARD_CHANGES_QNAME),
+                NetconfMessageTransformUtil.DISCARD_CHANGES_RPC_CONTENT);
+        assertThat(XmlUtil.toString(netconfMessage.getDocument()), CoreMatchers.containsString("<discard"));
+    }
+
+    @Test
+    public void tesGetSchemaRequest() throws Exception {
+        final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(toPath(GET_SCHEMA_QNAME),
+                NetconfRemoteSchemaYangSourceProvider.createGetSchemaRequest("module", Optional.of("2012-12-12")));
+        assertSimilarXml(netconfMessage, "<rpc message-id=\"m-0\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
+                "<get-schema xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n" +
+                "<format>yang</format>\n" +
+                "<identifier>module</identifier>\n" +
+                "<version>2012-12-12</version>\n" +
+                "</get-schema>\n" +
+                "</rpc>");
+    }
+
+    @Test
+    public void tesGetSchemaResponse() throws Exception {
+        final NetconfMessageTransformer netconfMessageTransformer = getTransformer(getSchema());
+        final NetconfMessage response = new NetconfMessage(XmlUtil.readXmlToDocument(
+                "<rpc-reply message-id=\"101\"\n" +
+                        "xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
+                        "<data\n" +
+                        "xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n" +
+                        "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n" +
+                        "Random YANG SCHEMA\n" +
+                        "</xs:schema>\n" +
+                        "</data>\n" +
+                        "</rpc-reply>"
+        ));
+        final DOMRpcResult compositeNodeRpcResult = netconfMessageTransformer.toRpcResult(response, toPath(GET_SCHEMA_QNAME));
+        assertTrue(compositeNodeRpcResult.getErrors().isEmpty());
+        assertNotNull(compositeNodeRpcResult.getResult());
+        final Object schemaContent = ((AnyXmlNode) ((ContainerNode) compositeNodeRpcResult.getResult()).getValue().iterator().next()).getValue().getValue();
+        assertThat(schemaContent.toString(), CoreMatchers.containsString("Random YANG SCHEMA"));
+    }
+
+    @Test
+    public void testGetConfigResponse() throws Exception {
+        final NetconfMessage response = new NetconfMessage(XmlUtil.readXmlToDocument("<rpc-reply message-id=\"101\"\n" +
+                "xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
+                "<data>\n" +
+                "<netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n" +
+                "<schemas>\n" +
+                "<schema>\n" +
+                "<identifier>module</identifier>\n" +
+                "<version>2012-12-12</version>\n" +
+                "<format>yang</format>\n" +
+                "</schema>\n" +
+                "</schemas>\n" +
+                "</netconf-state>\n" +
+                "</data>\n" +
+                "</rpc-reply>"));
+
+        final NetconfMessageTransformer netconfMessageTransformer = getTransformer(getSchema());
+        final DOMRpcResult compositeNodeRpcResult = netconfMessageTransformer.toRpcResult(response, toPath(NETCONF_GET_CONFIG_QNAME));
+        assertTrue(compositeNodeRpcResult.getErrors().isEmpty());
+        assertNotNull(compositeNodeRpcResult.getResult());
+
+        final List<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> values = Lists.newArrayList(
+                NetconfRemoteSchemaYangSourceProvider.createGetSchemaRequest("module", Optional.of("2012-12-12")).getValue());
+
+        final Map<QName, Object> keys = Maps.newHashMap();
+        for (final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> value : values) {
+            keys.put(value.getNodeType(), value.getValue());
+        }
+
+        final YangInstanceIdentifier.NodeIdentifierWithPredicates identifierWithPredicates = new YangInstanceIdentifier.NodeIdentifierWithPredicates(Schema.QNAME, keys);
+        final MapEntryNode schemaNode = Builders.mapEntryBuilder().withNodeIdentifier(identifierWithPredicates).withValue(values).build();
+
+        final ContainerNode data = (ContainerNode) ((ContainerNode) compositeNodeRpcResult.getResult()).getChild(toId(NETCONF_DATA_QNAME)).get();
+        final ContainerNode state = (ContainerNode) data.getChild(toId(NetconfState.QNAME)).get();
+        final ContainerNode schemas = (ContainerNode) state.getChild(toId(Schemas.QNAME)).get();
+        final MapNode schemaParent = (MapNode) schemas.getChild(toId(Schema.QNAME)).get();
+        assertEquals(1, Iterables.size(schemaParent.getValue()));
+
+        assertEquals(schemaNode, schemaParent.getValue().iterator().next());
+    }
+
+    @Test
+    public void testGetConfigRequest() throws Exception {
+        final DataContainerChild<?, ?> filter = toFilterStructure(
+                YangInstanceIdentifier.create(toId(NetconfState.QNAME), toId(Schemas.QNAME)), schema);
+
+        final DataContainerChild<?, ?> source = NetconfBaseOps.getSourceNode(NETCONF_RUNNING_QNAME);
+
+        final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(toPath(NETCONF_GET_CONFIG_QNAME),
+                NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_QNAME, source, filter));
+
+        assertSimilarXml(netconfMessage, "<rpc message-id=\"m-0\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
+                "<get-config xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
+                "<filter xmlns:ns0=\"urn:ietf:params:xml:ns:netconf:base:1.0\" ns0:type=\"subtree\">\n" +
+                "<netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n" +
+                "<schemas/>\n" +
+                "</netconf-state>" +
+                "</filter>\n" +
+                "<source>\n" +
+                "<running/>\n" +
+                "</source>\n" +
+                "</get-config>" +
+                "</rpc>");
+    }
+
+    @Test
+    public void testEditConfigRequest() throws Exception {
+        final List<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> values = Lists.newArrayList(
+            NetconfRemoteSchemaYangSourceProvider.createGetSchemaRequest("module", Optional.of("2012-12-12")).getValue());
+
+        final Map<QName, Object> keys = Maps.newHashMap();
+        for (final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> value : values) {
+            keys.put(value.getNodeType(), value.getValue());
+        }
+
+        final YangInstanceIdentifier.NodeIdentifierWithPredicates identifierWithPredicates = new YangInstanceIdentifier.NodeIdentifierWithPredicates(Schema.QNAME, keys);
+        final MapEntryNode schemaNode = Builders.mapEntryBuilder().withNodeIdentifier(identifierWithPredicates).withValue(values).build();
+
+        final YangInstanceIdentifier id = YangInstanceIdentifier.builder().node(NetconfState.QNAME).node(Schemas.QNAME).node(Schema.QNAME).nodeWithKey(Schema.QNAME, keys).build();
+        final DataContainerChild<?, ?> editConfigStructure = createEditConfigStructure(NetconfDevice.INIT_SCHEMA_CTX, id, Optional.of(ModifyAction.REPLACE), Optional.<NormalizedNode<?, ?>>fromNullable(schemaNode));
+
+        final DataContainerChild<?, ?> target = NetconfBaseOps.getTargetNode(NETCONF_CANDIDATE_QNAME);
+
+        final ContainerNode wrap = NetconfMessageTransformUtil.wrap(NETCONF_EDIT_CONFIG_QNAME, editConfigStructure, target);
+        final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(toPath(NETCONF_EDIT_CONFIG_QNAME), wrap);
+
+        assertSimilarXml(netconfMessage, "<rpc message-id=\"m-0\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
+                "<edit-config>\n" +
+                "<target>\n" +
+                "<candidate/>\n" +
+                "</target>\n" +
+                "<config>\n" +
+                "<netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n" +
+                "<schemas>\n" +
+                "<schema>\n" +
+                "<identifier>module</identifier>\n" +
+                "<version>2012-12-12</version>\n" +
+                "<format>yang</format>\n" +
+                "</schema>\n" +
+                "</schemas>\n" +
+                "</netconf-state>\n" +
+                "</config>\n" +
+                "</edit-config>\n" +
+                "</rpc>");
+    }
+
+    private void assertSimilarXml(final NetconfMessage netconfMessage, final String xmlContent) throws SAXException, IOException {
+        final Diff diff = XMLUnit.compareXML(netconfMessage.getDocument(), XmlUtil.readXmlToDocument(xmlContent));
+        assertTrue(diff.toString(), diff.similar());
+    }
 
     @Test
-    public void testToRpcRequestNoSchemaForRequest() throws Exception {
-        final NetconfMessageTransformer netconfMessageTransformer = getTransformer();
-        final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(COMMIT_Q_NAME,
-                NodeFactory.createImmutableCompositeNode(COMMIT_Q_NAME, null, Collections.<Node<?>>emptyList()));
-        assertThat(XmlUtil.toString(netconfMessage.getDocument()), CoreMatchers.containsString("<commit"));
+    public void testGetRequest() throws Exception {
+
+        final QName capability = QName.create(Capabilities.QNAME, "capability");
+        final DataContainerChild<?, ?> filter = toFilterStructure(
+                YangInstanceIdentifier.create(toId(NetconfState.QNAME), toId(Capabilities.QNAME), toId(capability), new YangInstanceIdentifier.NodeWithValue(capability, "a:b:c")), schema);
+
+        final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(toPath(NETCONF_GET_QNAME),
+                NetconfMessageTransformUtil.wrap(NETCONF_GET_QNAME, filter));
+
+        assertSimilarXml(netconfMessage, "<rpc message-id=\"m-0\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +
+                "<get xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
+                "<filter xmlns:ns0=\"urn:ietf:params:xml:ns:netconf:base:1.0\" ns0:type=\"subtree\">\n" +
+                "<netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n" +
+                "<capabilities>\n" +
+                "<capability>a:b:c</capability>\n" +
+                "</capabilities>\n" +
+                "</netconf-state>" +
+                "</filter>\n" +
+                "</get>" +
+                "</rpc>");
     }
 
-    private NetconfMessageTransformer getTransformer() {
-        final NetconfMessageTransformer netconfMessageTransformer = new NetconfMessageTransformer();
-        netconfMessageTransformer.onGlobalContextUpdated(getSchema());
-        return netconfMessageTransformer;
+    private NetconfMessageTransformer getTransformer(final SchemaContext schema) {
+        return new NetconfMessageTransformer(schema);
     }
 
     @Test
-    public void testToRpcResultNoSchemaForResult() throws Exception {
-        final NetconfMessageTransformer netconfMessageTransformer = getTransformer();
+    public void testCommitResponse() throws Exception {
         final NetconfMessage response = new NetconfMessage(XmlUtil.readXmlToDocument(
-                "<rpc-reply><ok/></rpc-reply>"
+                "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><ok/></rpc-reply>"
         ));
-        final RpcResult<CompositeNode> compositeNodeRpcResult = netconfMessageTransformer.toRpcResult(response, COMMIT_Q_NAME);
-        assertTrue(compositeNodeRpcResult.isSuccessful());
-        assertEquals("ok", compositeNodeRpcResult.getResult().getValue().get(0).getKey().getLocalName());
+        final DOMRpcResult compositeNodeRpcResult = netconfMessageTransformer.toRpcResult(response, toPath(NETCONF_COMMIT_QNAME));
+        assertTrue(compositeNodeRpcResult.getErrors().isEmpty());
+        assertNull(compositeNodeRpcResult.getResult());
     }
 
     public SchemaContext getSchema() {
-        final List<InputStream> modelsToParse = Collections
-                .singletonList(NetconfToRpcRequestTest.class.getResourceAsStream("/schemas/rpc-notification-subscription.yang"));
-        final YangParserImpl parser = new YangParserImpl();
-        final Set<Module> configModules = parser.parseYangModelsFromStreams(modelsToParse);
-        final SchemaContext cfgCtx = parser.resolveSchemaContext(configModules);
-        assertNotNull(cfgCtx);
-        return cfgCtx;
+        final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create();
+        moduleInfoBackedContext.addModuleInfos(Collections.singleton($YangModuleInfoImpl.getInstance()));
+        moduleInfoBackedContext.addModuleInfos(Collections.singleton(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.$YangModuleInfoImpl.getInstance()));
+        return moduleInfoBackedContext.tryToCreateSchemaContext().get();
     }
 }
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/util/InstanceIdToNodesTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/util/InstanceIdToNodesTest.java
new file mode 100644 (file)
index 0000000..39a8f42
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html.
+ */
+package org.opendaylight.controller.sal.connect.netconf.util;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.io.ByteSource;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+public class InstanceIdToNodesTest {
+
+    private static final String NS = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:normalization:test";
+    private static final String REVISION = "2014-03-13";
+    private static final QName ID = QName.create(NS, REVISION, "id");
+    private SchemaContext ctx;
+
+    private final YangInstanceIdentifier.NodeIdentifier rootContainer = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "test"));
+    private final YangInstanceIdentifier.NodeIdentifier outerContainer = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "outer-container"));
+    private final YangInstanceIdentifier.NodeIdentifier augmentedLeaf = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "augmented-leaf"));
+    private final YangInstanceIdentifier.AugmentationIdentifier augmentation = new YangInstanceIdentifier.AugmentationIdentifier(Collections.singleton(augmentedLeaf.getNodeType()));
+
+    private final YangInstanceIdentifier.NodeIdentifier outerList = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "outer-list"));
+    private final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListWithKey = new YangInstanceIdentifier.NodeIdentifierWithPredicates(QName.create(NS, REVISION, "outer-list"), ID, 1);
+    private final YangInstanceIdentifier.NodeIdentifier choice = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "outer-choice"));
+    private final YangInstanceIdentifier.NodeIdentifier leafFromCase = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "one"));
+
+    private final YangInstanceIdentifier.NodeIdentifier leafList = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "ordered-leaf-list"));
+    private final YangInstanceIdentifier.NodeWithValue leafListWithValue = new YangInstanceIdentifier.NodeWithValue(leafList.getNodeType(), "abcd");
+
+    static SchemaContext createTestContext() throws IOException, YangSyntaxErrorException {
+        final YangParserImpl parser = new YangParserImpl();
+        return parser.parseSources(Collections2.transform(Collections.singletonList("/schemas/filter-test.yang"), new Function<String, ByteSource>() {
+            @Override
+            public ByteSource apply(final String input) {
+                return new ByteSource() {
+                    @Override
+                    public InputStream openStream() throws IOException {
+                        return InstanceIdToNodesTest.class.getResourceAsStream(input);
+                    }
+                };
+            }
+        }));
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        ctx = createTestContext();
+
+    }
+
+    @Test
+    public void testInAugment() throws Exception {
+        final ContainerNode expectedFilter = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
+                Builders.containerBuilder().withNodeIdentifier(outerContainer).withChild(
+                        Builders.augmentationBuilder().withNodeIdentifier(augmentation).withChild(
+                                Builders.leafBuilder().withNodeIdentifier(augmentedLeaf).build()
+                        ).build()
+                ).build()
+        ).build();
+
+        final NormalizedNode<?, ?> filter = InstanceIdToNodes.serialize(ctx, YangInstanceIdentifier.create(rootContainer, outerContainer, augmentation, augmentedLeaf));
+        assertEquals(expectedFilter, filter);
+    }
+
+    @Test
+    public void testInAugmentLeafOverride() throws Exception {
+        final LeafNode<Object> lastLeaf = Builders.leafBuilder().withNodeIdentifier(augmentedLeaf).withValue("randomValue").build();
+
+        final ContainerNode expectedFilter = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
+                Builders.containerBuilder().withNodeIdentifier(outerContainer).withChild(
+                        Builders.augmentationBuilder().withNodeIdentifier(augmentation).withChild(
+                                lastLeaf
+                        ).build()
+                ).build()
+        ).build();
+
+        final NormalizedNode<?, ?> filter = InstanceIdToNodes.serialize(ctx, YangInstanceIdentifier.create(rootContainer, outerContainer, augmentation, augmentedLeaf), lastLeaf);
+        assertEquals(expectedFilter, filter);
+    }
+
+    @Test
+    public void testListChoice() throws Exception {
+        final ContainerNode expectedFilter = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
+                Builders.mapBuilder().withNodeIdentifier(outerList).withChild(
+                        Builders.mapEntryBuilder().withNodeIdentifier(outerListWithKey).withChild(
+                                Builders.leafBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(ID)).withValue(1).build()
+                        ).withChild(
+                                Builders.choiceBuilder().withNodeIdentifier(choice).withChild(
+                                        Builders.leafBuilder().withNodeIdentifier(leafFromCase).build()
+                                ).build()
+                        ).build()
+                ).build()
+        ).build();
+
+        final NormalizedNode<?, ?> filter = InstanceIdToNodes.serialize(ctx, YangInstanceIdentifier.create(rootContainer, outerList, outerListWithKey, choice, leafFromCase));
+        assertEquals(expectedFilter, filter);
+    }
+
+    @Test
+    public void testTopContainerLastChildOverride() throws Exception {
+        final ContainerNode expectedStructure = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
+                Builders.mapBuilder().withNodeIdentifier(outerList).withChild(
+                        Builders.mapEntryBuilder().withNodeIdentifier(outerListWithKey).withChild(
+                                Builders.leafBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(ID)).withValue(1).build()
+                        ).withChild(
+                                Builders.choiceBuilder().withNodeIdentifier(choice).withChild(
+                                        Builders.leafBuilder().withNodeIdentifier(leafFromCase).build()
+                                ).build()
+                        ).build()
+                ).build()
+        ).build();
+
+        final NormalizedNode<?, ?> filter = InstanceIdToNodes.serialize(ctx, YangInstanceIdentifier.create(rootContainer), expectedStructure);
+        assertEquals(expectedStructure, filter);
+    }
+
+    @Test
+    public void testListLastChildOverride() throws Exception {
+        final MapEntryNode outerListEntry = Builders.mapEntryBuilder().withNodeIdentifier(outerListWithKey).withChild(
+                Builders.leafBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(ID)).withValue(1).build()
+        ).build();
+        final MapNode lastChild = Builders.mapBuilder().withNodeIdentifier(this.outerList).withChild(
+                outerListEntry
+        ).build();
+        final ContainerNode expectedStructure = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
+                lastChild
+        ).build();
+
+        NormalizedNode<?, ?> filter = InstanceIdToNodes.serialize(ctx, YangInstanceIdentifier.create(rootContainer, outerList, outerListWithKey), outerListEntry);
+        assertEquals(expectedStructure, filter);
+        filter = InstanceIdToNodes.serialize(ctx, YangInstanceIdentifier.create(rootContainer, outerList, outerListWithKey));
+        assertEquals(expectedStructure, filter);
+    }
+
+    @Test
+    public void testLeafList() throws Exception {
+        final ContainerNode expectedFilter = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
+                Builders.orderedLeafSetBuilder().withNodeIdentifier(leafList).withChild(
+                        Builders.leafSetEntryBuilder().withNodeIdentifier(leafListWithValue).withValue(leafListWithValue.getValue()).build()
+                ).build()
+        ).build();
+
+        final NormalizedNode<?, ?> filter = InstanceIdToNodes.serialize(ctx, YangInstanceIdentifier.create(rootContainer, leafList, leafListWithValue));
+        assertEquals(expectedFilter, filter);
+    }
+}
\ No newline at end of file
index f67c2df..18a4ead 100644 (file)
@@ -15,7 +15,7 @@ module config-test-rpc {
     revision 2014-07-21 {
         description "Initial revision.";
     }
-    
+
     extension get-filter-element-attributes {
           description
             "If this extension is present within an 'anyxml'
@@ -25,21 +25,21 @@ module config-test-rpc {
              following unqualified XML attribute is supported
              within the <filter> element, within a <get> or
              <get-config> protocol operation:
-    
+
                type : optional attribute with allowed
                       value strings 'subtree' and 'xpath'.
                       If missing, the default value is 'subtree'.
-    
+
              If the 'xpath' feature is supported, then the
              following unqualified XML attribute is
              also supported:
-    
+
                select: optional attribute containing a
                        string representing an XPath expression.
                        The 'type' attribute must be equal to 'xpath'
                        if this attribute is present.";
     }
-    
+
     rpc edit-config {
         description "The <edit-config> operation loads all or part of a specified
              configuration to the specified target configuration.";
@@ -164,4 +164,13 @@ module config-test-rpc {
             }
         }
     }
+
+      rpc discard-changes {
+        if-feature candidate;
+
+        description
+          "Revert the candidate configuration to the current
+           running configuration.";
+        reference "RFC 6241, Section 8.3.4.2";
+      }
 }
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/filter-test.yang b/opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/filter-test.yang
new file mode 100644 (file)
index 0000000..6df5306
--- /dev/null
@@ -0,0 +1,74 @@
+module normalization-test {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:normalization:test";
+    prefix "norm-test";
+
+    revision "2014-03-13" {
+        description "Initial revision.";
+    }
+
+    grouping outer-grouping {
+    }
+
+    container test {
+        list outer-list {
+            key id;
+            leaf id {
+                type uint16;
+            }
+            choice outer-choice {
+                case one {
+                    leaf one {
+                        type string;
+                    }
+                }
+                case two-three {
+                    leaf two {
+                        type string;
+                    }
+                    leaf three {
+                        type string;
+                    }
+               }
+           }
+           list inner-list {
+                key name;
+                ordered-by user;
+
+                leaf name {
+                    type string;
+                }
+                leaf value {
+                    type string;
+                }
+            }
+        }
+
+        list unkeyed-list {
+            leaf name {
+                type string;
+            }
+        }
+
+        leaf-list unordered-leaf-list {
+            type string;
+        }
+
+        leaf-list ordered-leaf-list {
+            ordered-by user;
+            type string;
+        }
+
+        container outer-container {
+        }
+
+        anyxml any-xml-data;
+    }
+
+    augment /norm-test:test/norm-test:outer-container {
+
+        leaf augmented-leaf {
+           type string;
+        }
+    }
+}
\ No newline at end of file
index a990b5c..4899ebc 100644 (file)
@@ -16,7 +16,7 @@
 
   <modules>
     <module>netconf-api</module>
-    <module>netconf-cli</module>
+    <!--<module>netconf-cli</module>-->
     <module>netconf-config</module>
     <module>netconf-impl</module>
     <module>config-netconf-connector</module>