Updating cluster RPC code making it work 60/9460/3
authorHarman Singh <harmasin@cisco.com>
Wed, 30 Jul 2014 02:03:08 +0000 (19:03 -0700)
committerHarman Singh <harmasin@cisco.com>
Thu, 31 Jul 2014 23:59:51 +0000 (16:59 -0700)
Added XMLUtils to serialize and deserialize instance identifier objects and the values insides path arguments with proper object type. This code use Yangtools existing utility, but modify that code to make sure
right type is picked up. We can think of adding it directly in yang tools also and remove it from here
Added Preconditions check for pojos

Change-Id: Ieb73b4ee660d2e058d0a62952799fc6f39d4c6b5
Signed-off-by: Harman Singh <harmasin@cisco.com>
33 files changed:
opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.xml
opendaylight/md-sal/pom.xml
opendaylight/md-sal/sal-remoterpc-connector/pom.xml
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementation.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RouteIdentifierImpl.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RoutedRpcListener.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcBroker.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRoutedRpc.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRpc.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ErrorResponse.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ExecuteRpc.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpc.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpcReply.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpc.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpcReply.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRoutedRpc.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRpc.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRoutedRpc.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRpc.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RoutingTableData.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RpcResponse.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/UpdateSchemaContext.java [new file with mode: 0644]
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/InstanceIdentifierForXmlCodec.java [new file with mode: 0644]
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/RandomPrefix.java [new file with mode: 0644]
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlDocumentUtils.java [new file with mode: 0644]
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlStreamUtils.java [new file with mode: 0644]
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlUtils.java [moved from opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/XmlUtils.java with 52% similarity]
opendaylight/md-sal/sal-remoterpc-connector/src/main/resources/application.conf
opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RpcBrokerTest.java
opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RoutingTableTest.java
opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/utils/XmlUtilsTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/org/opendaylight/controller/remote/rpc/utils/rpcTest.yang [new file with mode: 0644]
pom.xml

index 03da7f0ccd6cd37a4044404e3426a8f5307f580b..f25b7d91bdb38b2f357ec5faa91283d2929e3128 100644 (file)
                     </schema-service>
                 </module>
                 -->
-                <!-- Cluster RPC -->
-                <!-- Enable the following module if you want to use remote rpc connector
-                <module>
-                    <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:remote-rpc-connector">prefix:remote-rpc-connector</type>
-                    <name>remote-rpc-connector</name>
-                    <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:remote-rpc-connector">
-                        <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
-                        <name>dom-broker</name>
-                    </dom-broker>
-                </module>
-                -->
+
                 <module>
                     <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:inmemory-datastore-provider">prefix:inmemory-operational-datastore-provider</type>
                     <name>operational-store-service</name>
                         </binding-mapping-service>
                     </binding-forwarded-data-broker>
                 </module>
+                <!-- Cluster RPC -->
+                <!-- Enable the following module if you want to use remote rpc connector
+                <module>
+                    <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:remote-rpc-connector">prefix:remote-rpc-connector</type>
+                    <name>remote-rpc-connector</name>
+                    <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:remote-rpc-connector">
+                        <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+                        <name>dom-broker</name>
+                    </dom-broker>
+                </module>
+                -->
             </modules>
             <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
                     <service>
index cadd8ca87d784095c9a9a6bc5f08b396f399691d..5e96e1cc0d8cbfebd54c05a4adc63c2e40850866 100644 (file)
@@ -71,6 +71,9 @@
 
     <!-- Yang Test Models for MD-SAL -->
     <module>sal-test-model</module>
+
+    <!-- Clustering -->
+    <module>sal-remoterpc-connector</module>
   </modules>
 
   <build>
index 6ee301d827ec3caa91f1b6f81dd13c6c21784afb..ce3bfe9a4c81a65decaf51625c7505c07cafc05e 100644 (file)
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>netconf-util</artifactId>
     </dependency>
-
+      <dependency>
+          <groupId>org.opendaylight.controller</groupId>
+          <artifactId>sal-core-spi</artifactId>
+      </dependency>
+      <dependency>
+          <groupId>org.opendaylight.controller</groupId>
+          <artifactId>sal-common-impl</artifactId>
+      </dependency>
     <!-- Yang tools-->
 
     <dependency>
       <groupId>org.opendaylight.yangtools</groupId>
       <artifactId>yang-data-api</artifactId>
+
     </dependency>
+      <dependency>
+          <groupId>org.opendaylight.yangtools</groupId>
+          <artifactId>yang-model-api</artifactId>
+
+      </dependency>
 
     <dependency>
       <groupId>org.opendaylight.yangtools</groupId>
       <artifactId>yang-data-impl</artifactId>
+
     </dependency>
 
     <dependency>
       <groupId>org.opendaylight.yangtools</groupId>
       <artifactId>yang-common</artifactId>
+
     </dependency>
 
 
       <version>${slf4j.version}</version>
       <scope>test</scope>
     </dependency>
+      <dependency>
+          <groupId>org.opendaylight.yangtools</groupId>
+          <artifactId>yang-parser-impl</artifactId>
+          <scope>test</scope>
+      </dependency>
+      <dependency>
+          <groupId>com.google.collections</groupId>
+          <artifactId>google-collections</artifactId>
+          <version>1.0</version>
+          <scope>test</scope>
+      </dependency>
 
   </dependencies>
 
index 43aa5b7e85a4a3da136de723210c5e3ae3f34cca..75db0b7299408453a3825a0fca5bebb1c62c3dbf 100644 (file)
@@ -7,21 +7,18 @@ import org.opendaylight.controller.remote.rpc.messages.ErrorResponse;
 import org.opendaylight.controller.remote.rpc.messages.InvokeRoutedRpc;
 import org.opendaylight.controller.remote.rpc.messages.InvokeRpc;
 import org.opendaylight.controller.remote.rpc.messages.RpcResponse;
-import org.opendaylight.controller.sal.common.util.RpcErrors;
-import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.controller.remote.rpc.utils.XmlUtils;
 import org.opendaylight.controller.sal.core.api.RoutedRpcDefaultImplementation;
 import org.opendaylight.controller.sal.core.api.RpcImplementation;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcError;
 import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Set;
 
@@ -37,7 +34,7 @@ public class RemoteRpcImplementation implements RpcImplementation,
   }
 
   @Override
-  public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(QName rpc, InstanceIdentifier identifier, CompositeNode input) {
+  public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(QName rpc, YangInstanceIdentifier identifier, CompositeNode input) {
     InvokeRoutedRpc rpcMsg = new InvokeRoutedRpc(rpc, identifier, input);
 
     return executeMsg(rpcMsg);
@@ -56,22 +53,33 @@ public class RemoteRpcImplementation implements RpcImplementation,
   }
 
   private ListenableFuture<RpcResult<CompositeNode>> executeMsg(Object rpcMsg) {
-    CompositeNode result = null;
-    Collection<RpcError> errors = errors = new ArrayList<>();
+    ListenableFuture<RpcResult<CompositeNode>> listenableFuture = null;
+
     try {
       Object response = ActorUtil.executeLocalOperation(rpcBroker, rpcMsg, ActorUtil.ASK_DURATION, ActorUtil.AWAIT_DURATION);
       if(response instanceof RpcResponse) {
+
         RpcResponse rpcResponse = (RpcResponse) response;
-        result = XmlUtils.xmlToCompositeNode(rpcResponse.getResultCompositeNode());
+        CompositeNode result = XmlUtils.xmlToCompositeNode(rpcResponse.getResultCompositeNode());
+        listenableFuture = Futures.immediateFuture(RpcResultBuilder.success(result).build());
+
       } else if(response instanceof ErrorResponse) {
+
         ErrorResponse errorResponse = (ErrorResponse) response;
         Exception e = errorResponse.getException();
-        errors.add(RpcErrors.getRpcError(null, null, null, null, e.getMessage(), null, e.getCause()));
+        final RpcResultBuilder<CompositeNode> failed = RpcResultBuilder.failed();
+        failed.withError(null, null, e.getMessage(), null, null, e.getCause());
+        listenableFuture = Futures.immediateFuture(failed.build());
+
       }
     } catch (Exception e) {
       LOG.error("Error occurred while invoking RPC actor {}", e.toString());
-      errors.add(RpcErrors.getRpcError(null, null, null, null, e.getMessage(), null, e.getCause()));
+
+      final RpcResultBuilder<CompositeNode> failed = RpcResultBuilder.failed();
+      failed.withError(null, null, e.getMessage(), null, null, e.getCause());
+      listenableFuture = Futures.immediateFuture(failed.build());
     }
-    return Futures.immediateFuture(Rpcs.getRpcResult(true, result, errors));
+
+    return listenableFuture;
   }
 }
index ea72238fbcb3aa6cc29621c76821d08a0eb4e158..85d21381b61bf127fb95e0e11a67275d6d25ec86 100644 (file)
@@ -7,20 +7,23 @@
  */
 package org.opendaylight.controller.remote.rpc;
 
+import com.google.common.base.Preconditions;
 import org.opendaylight.controller.sal.connector.api.RpcRouter;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
 
 import java.io.Serializable;
 
-public class RouteIdentifierImpl implements RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier>,Serializable {
+public class RouteIdentifierImpl implements RpcRouter.RouteIdentifier<QName, QName, YangInstanceIdentifier>,Serializable {
   private static final long serialVersionUID = 1L;
 
-  private QName context;
-  private QName type;
-  private InstanceIdentifier route;
+  private final QName context;
+  private final QName type;
+  private final YangInstanceIdentifier route;
 
-  public RouteIdentifierImpl(QName context, QName type, InstanceIdentifier route) {
+  public RouteIdentifierImpl(final QName context, final QName type, final YangInstanceIdentifier route) {
+    Preconditions.checkNotNull(type, "Rpc type should not be null");
     this.context = context;
     this.type = type;
     this.route = route;
@@ -37,7 +40,7 @@ public class RouteIdentifierImpl implements RpcRouter.RouteIdentifier<QName, QNa
   }
 
   @Override
-  public InstanceIdentifier getRoute() {
+  public YangInstanceIdentifier getRoute() {
     return this.route;
   }
 
index a0df3629fa521367f68ca48263d913343c189a68..dce7e20219d377a3f8d6747064c55af76cb2fcd8 100644 (file)
@@ -10,13 +10,14 @@ package org.opendaylight.controller.remote.rpc;
 
 
 import akka.actor.ActorRef;
+import com.google.common.base.Preconditions;
 import org.opendaylight.controller.md.sal.common.api.routing.RouteChange;
 import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
 import org.opendaylight.controller.remote.rpc.messages.AddRoutedRpc;
 import org.opendaylight.controller.remote.rpc.messages.RemoveRoutedRpc;
 import org.opendaylight.controller.sal.connector.api.RpcRouter;
 import org.opendaylight.controller.sal.core.api.RpcRoutingContext;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -24,22 +25,25 @@ import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
-public class RoutedRpcListener implements RouteChangeListener<RpcRoutingContext, InstanceIdentifier>{
+public class RoutedRpcListener implements RouteChangeListener<RpcRoutingContext, YangInstanceIdentifier>{
   private static final Logger LOG = LoggerFactory.getLogger(RoutedRpcListener.class);
   private final ActorRef rpcRegistry;
   private final String actorPath;
 
   public RoutedRpcListener(ActorRef rpcRegistry, String actorPath) {
+    Preconditions.checkNotNull(rpcRegistry, "rpc registry actor should not be null");
+    Preconditions.checkNotNull(actorPath, "actor path of rpc broker on current node should not be null");
+
     this.rpcRegistry = rpcRegistry;
     this.actorPath = actorPath;
   }
 
   @Override
-  public void onRouteChange(RouteChange<RpcRoutingContext, InstanceIdentifier> routeChange) {
-    Map<RpcRoutingContext, Set<InstanceIdentifier>> announcements = routeChange.getAnnouncements();
+  public void onRouteChange(RouteChange<RpcRoutingContext, YangInstanceIdentifier> routeChange) {
+    Map<RpcRoutingContext, Set<YangInstanceIdentifier>> announcements = routeChange.getAnnouncements();
     announce(getRouteIdentifiers(announcements));
 
-    Map<RpcRoutingContext, Set<InstanceIdentifier>> removals = routeChange.getRemovals();
+    Map<RpcRoutingContext, Set<YangInstanceIdentifier>> removals = routeChange.getRemovals();
     remove(getRouteIdentifiers(removals));
   }
 
@@ -78,12 +82,12 @@ public class RoutedRpcListener implements RouteChangeListener<RpcRoutingContext,
    * @param changes
    * @return
    */
-  private Set<RpcRouter.RouteIdentifier<?, ?, ?>> getRouteIdentifiers(Map<RpcRoutingContext, Set<InstanceIdentifier>> changes) {
+  private Set<RpcRouter.RouteIdentifier<?, ?, ?>> getRouteIdentifiers(Map<RpcRoutingContext, Set<YangInstanceIdentifier>> changes) {
     RouteIdentifierImpl routeId = null;
     Set<RpcRouter.RouteIdentifier<?, ?, ?>> routeIdSet = new HashSet<>();
 
     for (RpcRoutingContext context : changes.keySet()){
-      for (InstanceIdentifier instanceId : changes.get(context)){
+      for (YangInstanceIdentifier instanceId : changes.get(context)){
         routeId = new RouteIdentifierImpl(null, context.getRpc(), instanceId);
         routeIdSet.add(routeId);
       }
index 3354fc3da25e990279cacec849fa131e57e5b963..412e549facc545ea990a303dd46baa270a979a05 100644 (file)
@@ -20,6 +20,7 @@ import org.opendaylight.controller.remote.rpc.messages.GetRpcReply;
 import org.opendaylight.controller.remote.rpc.messages.InvokeRoutedRpc;
 import org.opendaylight.controller.remote.rpc.messages.InvokeRpc;
 import org.opendaylight.controller.remote.rpc.messages.RpcResponse;
+import org.opendaylight.controller.remote.rpc.utils.XmlUtils;
 import org.opendaylight.controller.sal.core.api.Broker;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
@@ -105,11 +106,12 @@ public class RpcBroker extends AbstractUntypedActor {
       String remoteActorPath = rpcReply.getRoutePath();
 
       if(remoteActorPath == null) {
-        LOG.debug("No remote actor found for rpc execution.");
+        LOG.debug("No remote actor found for rpc {{}}.", msg.getRpc());
 
         getSender().tell(new ErrorResponse(
-          new IllegalStateException("No remote actor found for rpc execution.")), self());
+          new IllegalStateException("No remote actor found for rpc execution of : " + msg.getRpc())), self());
       } else {
+
         ExecuteRpc executeMsg = new ExecuteRpc(XmlUtils.inputCompositeNodeToXml(msg.getInput(), schemaContext), msg.getRpc());
         Object operationRes = ActorUtil.executeRemoteOperation(this.context().actorSelection(remoteActorPath),
             executeMsg, ActorUtil.REMOTE_ASK_DURATION, ActorUtil.REMOTE_AWAIT_DURATION);
@@ -127,7 +129,6 @@ public class RpcBroker extends AbstractUntypedActor {
     try {
       Future<RpcResult<CompositeNode>> rpc = brokerSession.rpc(msg.getRpc(), XmlUtils.inputXmlToCompositeNode(msg.getRpc(), msg.getInputCompositeNode(), schemaContext));
       RpcResult<CompositeNode> rpcResult = rpc != null ? rpc.get():null;
-
       CompositeNode result = rpcResult != null ? rpcResult.getResult() : null;
       getSender().tell(new RpcResponse(XmlUtils.outputCompositeNodeToXml(result, schemaContext)), self());
     } catch (Exception e) {
index 25773bb8a69531b4b07f28755062671eaf4a8975..fd1af2b33ce6a9ddfc6a756644da9c51cb1e9c3d 100644 (file)
@@ -8,6 +8,7 @@
 
 package org.opendaylight.controller.remote.rpc.messages;
 
+import com.google.common.base.Preconditions;
 import org.opendaylight.controller.sal.connector.api.RpcRouter;
 
 import java.io.Serializable;
@@ -15,10 +16,13 @@ import java.util.Set;
 
 public class AddRoutedRpc implements Serializable {
 
-  Set<RpcRouter.RouteIdentifier<?, ?, ?>> announcements;
-  String actorPath;
+  private final Set<RpcRouter.RouteIdentifier<?, ?, ?>> announcements;
+  private final String actorPath;
+
+  public AddRoutedRpc(final Set<RpcRouter.RouteIdentifier<?, ?, ?>> announcements, final String actorPath) {
+    Preconditions.checkNotNull(announcements, "Route identifier should not be null");
+    Preconditions.checkNotNull(actorPath, "Actor path should not be null");
 
-  public AddRoutedRpc(Set<RpcRouter.RouteIdentifier<?, ?, ?>> announcements, String actorPath) {
     this.announcements = announcements;
     this.actorPath = actorPath;
   }
index eac973137e19592f577d3ce586d8345d18eff1ce..7eaa8f0618615ca1e0b1afc7963b737ad53ef9d8 100644 (file)
@@ -8,16 +8,20 @@
 
 package org.opendaylight.controller.remote.rpc.messages;
 
+import com.google.common.base.Preconditions;
 import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl;
 
 import java.io.Serializable;
 
 public class AddRpc implements Serializable {
 
-  RouteIdentifierImpl routeId;
-  String actorPath;
+  private final RouteIdentifierImpl routeId;
+  private final String actorPath;
+
+  public AddRpc(final RouteIdentifierImpl routeId, final String actorPath) {
+    Preconditions.checkNotNull(routeId, "Route identifier should not be null");
+    Preconditions.checkNotNull(actorPath, "Actor path should not be null");
 
-  public AddRpc(RouteIdentifierImpl routeId, String actorPath) {
     this.routeId = routeId;
     this.actorPath = actorPath;
   }
index ef3f5281122116113e1064de06e98ab6842f9782..2c26243fe990659708961c38b1bc8e1a9c828409 100644 (file)
@@ -7,13 +7,16 @@
  */
 package org.opendaylight.controller.remote.rpc.messages;
 
+import com.google.common.base.Preconditions;
+
 import java.io.Serializable;
 
 public class ErrorResponse implements Serializable {
 
-  Exception exception;
+  private final Exception exception;
 
-  public ErrorResponse(Exception e) {
+  public ErrorResponse(final Exception e) {
+    Preconditions.checkNotNull(e, "Exception should be present for error message");
     this.exception = e;
   }
 
index 030d81ac7edd1293709af83ebcf4a8f07d8c6cc2..522dd44f5bf934d95b91d40f1b5656e80f703b8e 100644 (file)
@@ -8,16 +8,20 @@
 package org.opendaylight.controller.remote.rpc.messages;
 
 
+import com.google.common.base.Preconditions;
 import org.opendaylight.yangtools.yang.common.QName;
 
 import java.io.Serializable;
 
 public class ExecuteRpc implements Serializable {
 
-  private String inputCompositeNode;
-  private QName rpc;
+  private final String inputCompositeNode;
+  private final QName rpc;
+
+  public ExecuteRpc(final String inputCompositeNode, final QName rpc) {
+    Preconditions.checkNotNull(inputCompositeNode, "Composite Node input string should be present");
+    Preconditions.checkNotNull(rpc, "rpc Qname should not be null");
 
-  public ExecuteRpc(String inputCompositeNode, QName rpc) {
     this.inputCompositeNode = inputCompositeNode;
     this.rpc = rpc;
   }
index b1fa410e8d984e4b3449c849e9c4d3eb207c05a6..e8d226218226c68c15550dd642b27c2ddc1568de 100644 (file)
@@ -9,15 +9,17 @@
 package org.opendaylight.controller.remote.rpc.messages;
 
 
+import com.google.common.base.Preconditions;
 import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl;
 
 import java.io.Serializable;
 
 public class GetRoutedRpc implements Serializable {
 
-  RouteIdentifierImpl routeId;
+  private final RouteIdentifierImpl routeId;
 
-  public GetRoutedRpc(RouteIdentifierImpl routeId) {
+  public GetRoutedRpc(final RouteIdentifierImpl routeId) {
+    Preconditions.checkNotNull(routeId, "route id should not be null");
     this.routeId = routeId;
   }
 
index 0e1563340bf44ad8f781badcc438d79ac26a05f5..d42666219289688af8075d8922b0f4bf1b4e23e9 100644 (file)
@@ -12,9 +12,9 @@ import java.io.Serializable;
 
 public class GetRoutedRpcReply implements Serializable {
 
-  private String routePath;
+  private final String routePath;
 
-  public GetRoutedRpcReply(String routePath) {
+  public GetRoutedRpcReply(final String routePath) {
     this.routePath = routePath;
   }
 
index c55627961a77683fa669ab3a09a8948886a5a276..c1d4240dca8544758adfd93c4eccb034abb02cb2 100644 (file)
@@ -7,15 +7,17 @@
  */
 package org.opendaylight.controller.remote.rpc.messages;
 
+import com.google.common.base.Preconditions;
 import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl;
 
 import java.io.Serializable;
 
 public class GetRpc implements Serializable {
 
-  RouteIdentifierImpl routeId;
+  private final RouteIdentifierImpl routeId;
 
-  public GetRpc(RouteIdentifierImpl routeId) {
+  public GetRpc(final RouteIdentifierImpl routeId) {
+    Preconditions.checkNotNull(routeId, "Route Id should not be null");
     this.routeId = routeId;
   }
 
index 3309b989c54b614b0d288aa013e388ea6a3db107..aaf089d16f2e1e85e3ca060e5214e3fc927e3993 100644 (file)
@@ -12,9 +12,9 @@ import java.io.Serializable;
 
 public class GetRpcReply implements Serializable {
 
-  private String routePath;
+  private final String routePath;
 
-  public GetRpcReply(String routePath) {
+  public GetRpcReply(final String routePath) {
     this.routePath = routePath;
   }
 
index 00ef980bb18829618dcb857ce0d1b6150ff85d63..fd73144e1d9f68378320b1c3bec347ea4f7f90fe 100644 (file)
@@ -7,26 +7,26 @@
  */
 package org.opendaylight.controller.remote.rpc.messages;
 
+import com.google.common.base.Preconditions;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 
 import java.io.Serializable;
 
 public class InvokeRoutedRpc implements Serializable {
 
-  private QName rpc;
-  private InstanceIdentifier identifier;
-  private CompositeNode input;
+  private final QName rpc;
+  private final YangInstanceIdentifier identifier;
+  private final CompositeNode input;
 
-  public InvokeRoutedRpc(QName rpc, InstanceIdentifier identifier, CompositeNode input) {
-    this.rpc = rpc;
-    this.identifier = identifier;
-    this.input = input;
-  }
+  public InvokeRoutedRpc(final QName rpc, final YangInstanceIdentifier identifier, final CompositeNode input) {
+    Preconditions.checkNotNull(rpc, "rpc qname should not be null");
+    Preconditions.checkNotNull(identifier, "instance identifier of routed rpc should not be null");
+    Preconditions.checkNotNull(input, "rpc input should not be null");
 
-  public InvokeRoutedRpc(QName rpc, CompositeNode input) {
     this.rpc = rpc;
+    this.identifier = identifier;
     this.input = input;
   }
 
@@ -34,7 +34,7 @@ public class InvokeRoutedRpc implements Serializable {
     return rpc;
   }
 
-  public InstanceIdentifier getIdentifier() {
+  public YangInstanceIdentifier getIdentifier() {
     return identifier;
   }
 
index 1f4eab0971b79452f7997da0770425639b4232bf..94b7fe40244838f6115469502af862cde4daa7cc 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.controller.remote.rpc.messages;
 
+import com.google.common.base.Preconditions;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 
@@ -14,10 +15,13 @@ import java.io.Serializable;
 
 public class InvokeRpc implements Serializable {
 
-  private QName rpc;
-  private CompositeNode input;
+  private final QName rpc;
+  private final CompositeNode input;
+
+  public InvokeRpc(final QName rpc, final CompositeNode input) {
+    Preconditions.checkNotNull(rpc, "rpc qname should not be null");
+    Preconditions.checkNotNull(input, "rpc input should not be null");
 
-  public InvokeRpc(QName rpc, CompositeNode input) {
     this.rpc = rpc;
     this.input = input;
   }
index a3aa9d12fa97e206c480c2fc0651fc9216aba60f..b560b8c8c064c9bb9c17d06121bd0e4e67b5fd70 100644 (file)
@@ -8,6 +8,7 @@
 
 package org.opendaylight.controller.remote.rpc.messages;
 
+import com.google.common.base.Preconditions;
 import org.opendaylight.controller.sal.connector.api.RpcRouter;
 
 import java.io.Serializable;
@@ -15,10 +16,13 @@ import java.util.Set;
 
 public class RemoveRoutedRpc implements Serializable {
 
-  Set<RpcRouter.RouteIdentifier<?, ?, ?>> announcements;
-  String actorPath;
+  private final Set<RpcRouter.RouteIdentifier<?, ?, ?>> announcements;
+  private final String actorPath;
+
+  public RemoveRoutedRpc(final Set<RpcRouter.RouteIdentifier<?, ?, ?>> announcements, final String actorPath) {
+    Preconditions.checkNotNull(announcements, "Route identifier should not be null");
+    Preconditions.checkNotNull(actorPath, "Actor path should not be null");
 
-  public RemoveRoutedRpc(Set<RpcRouter.RouteIdentifier<?, ?, ?>> announcements, String actorPath) {
     this.announcements = announcements;
     this.actorPath = actorPath;
   }
index 0bfd78aaae8e724eadaa8c07407a5abbcf1d7609..289334fccc9af3031fc64d9622336d6344fa411a 100644 (file)
@@ -8,15 +8,18 @@
 
 package org.opendaylight.controller.remote.rpc.messages;
 
+import com.google.common.base.Preconditions;
 import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl;
 
 import java.io.Serializable;
 
 public class RemoveRpc implements Serializable {
 
-  RouteIdentifierImpl routeId;
+  private final RouteIdentifierImpl routeId;
+
+  public RemoveRpc(final RouteIdentifierImpl routeId) {
+    Preconditions.checkNotNull(routeId, "Route Id should not be null");
 
-  public RemoveRpc(RouteIdentifierImpl routeId) {
     this.routeId = routeId;
   }
 
index 132fdba0545c95b0dcbf4ea485c1a8662bb1a781..c57a258426ec54992b158ffa37ccb77288022547 100644 (file)
@@ -15,11 +15,11 @@ import java.util.LinkedHashSet;
 import java.util.Map;
 
 public class RoutingTableData implements Serializable {
-  private Map<RpcRouter.RouteIdentifier<?, ?, ?>, String> rpcMap;
-  private Map<RpcRouter.RouteIdentifier<?, ?, ?>, LinkedHashSet<String>> routedRpcMap;
+  private final Map<RpcRouter.RouteIdentifier<?, ?, ?>, String> rpcMap;
+  private final Map<RpcRouter.RouteIdentifier<?, ?, ?>, LinkedHashSet<String>> routedRpcMap;
 
-  public RoutingTableData(Map<RpcRouter.RouteIdentifier<?, ?, ?>, String> rpcMap,
-                          Map<RpcRouter.RouteIdentifier<?, ?, ?>, LinkedHashSet<String>> routedRpcMap) {
+  public RoutingTableData(final Map<RpcRouter.RouteIdentifier<?, ?, ?>, String> rpcMap,
+                          final Map<RpcRouter.RouteIdentifier<?, ?, ?>, LinkedHashSet<String>> routedRpcMap) {
     this.rpcMap = rpcMap;
     this.routedRpcMap = routedRpcMap;
   }
index cbfecb1918abc4849a3606efa84f780399b16411..17766f15b9af7c22112852c16686c02684155924 100644 (file)
@@ -12,9 +12,9 @@ package org.opendaylight.controller.remote.rpc.messages;
 import java.io.Serializable;
 
 public class RpcResponse implements Serializable {
-  private String resultCompositeNode;
+  private final String resultCompositeNode;
 
-  public RpcResponse(String resultCompositeNode) {
+  public RpcResponse(final String resultCompositeNode) {
     this.resultCompositeNode = resultCompositeNode;
   }
 
diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/UpdateSchemaContext.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/UpdateSchemaContext.java
new file mode 100644 (file)
index 0000000..83fc772
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.remote.rpc.messages;
+
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class UpdateSchemaContext {
+
+  private final SchemaContext schemaContext;
+
+  public UpdateSchemaContext(final SchemaContext schemaContext) {
+    this.schemaContext = schemaContext;
+  }
+
+  public SchemaContext getSchemaContext() {
+    return schemaContext;
+  }
+}
diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/InstanceIdentifierForXmlCodec.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/InstanceIdentifierForXmlCodec.java
new file mode 100644 (file)
index 0000000..cacacc6
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.remote.rpc.utils;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Element;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class InstanceIdentifierForXmlCodec {
+    private static final Pattern PREDICATE_PATTERN = Pattern.compile("\\[(.*?)\\]");
+    private static final Splitter SLASH_SPLITTER = Splitter.on('/');
+    private static final Splitter COLON_SPLITTER = Splitter.on(':');
+    private static final Splitter AT_SPLITTER = Splitter.on('@');
+    private static final Logger logger = LoggerFactory.getLogger(InstanceIdentifierForXmlCodec.class);
+
+    private InstanceIdentifierForXmlCodec() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    public static YangInstanceIdentifier deserialize(final Element element, final SchemaContext schemaContext) {
+        Preconditions.checkNotNull(element, "Value of element for deserialization can't be null");
+        Preconditions.checkNotNull(schemaContext,
+                "Schema context for deserialization of instance identifier type can't be null");
+
+        final String valueTrimmed = element.getTextContent().trim();
+        final Iterator<String> xPathParts = SLASH_SPLITTER.split(valueTrimmed).iterator();
+
+        // must be at least "/pr:node"
+        if (!xPathParts.hasNext() || !xPathParts.next().isEmpty() || !xPathParts.hasNext()) {
+            return null;
+        }
+
+        List<PathArgument> result = new ArrayList<>();
+        while (xPathParts.hasNext()) {
+            String xPathPartTrimmed = xPathParts.next().trim();
+
+            PathArgument pathArgument = toPathArgument(xPathPartTrimmed, element, schemaContext);
+            if (pathArgument != null) {
+                result.add(pathArgument);
+            }
+        }
+        return YangInstanceIdentifier.create(result);
+    }
+
+    public static Element serialize(final YangInstanceIdentifier id, final Element element) {
+        Preconditions.checkNotNull(id, "Variable should contain instance of instance identifier and can't be null");
+        Preconditions.checkNotNull(element, "DOM element can't be null");
+
+        final RandomPrefix prefixes = new RandomPrefix();
+        final String str = XmlUtils.encodeIdentifier(prefixes, id);
+
+        for (Entry<URI, String> e: prefixes.getPrefixes()) {
+            element.setAttribute("xmlns:" + e.getValue(), e.getKey().toString());
+        }
+        element.setTextContent(str);
+        return element;
+    }
+
+    private static String getIdAndPrefixAsStr(final String pathPart) {
+        int predicateStartIndex = pathPart.indexOf('[');
+        return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex);
+    }
+
+    private static PathArgument toPathArgument(final String xPathArgument, final Element element, final SchemaContext schemaContext) {
+        final QName mainQName = toIdentity(xPathArgument, element, schemaContext);
+
+        // predicates
+        final Matcher matcher = PREDICATE_PATTERN.matcher(xPathArgument);
+        final Map<QName, Object> predicates = new HashMap<>();
+        QName currentQName = mainQName;
+
+        while (matcher.find()) {
+            final String predicateStr = matcher.group(1).trim();
+            final int indexOfEqualityMark = predicateStr.indexOf('=');
+            if (indexOfEqualityMark != -1) {
+                final Object predicateValue = toPredicateValue(predicateStr.substring(indexOfEqualityMark + 1));
+                if (predicateValue == null) {
+                    return null;
+                }
+
+                if (predicateStr.charAt(0) != '.') {
+                    // target is not a leaf-list
+                    currentQName = toIdentity(predicateStr.substring(0, indexOfEqualityMark), element, schemaContext);
+                    if (currentQName == null) {
+                        return null;
+                    }
+                }
+                predicates.put(currentQName, predicateValue);
+            }
+        }
+
+        if (predicates.isEmpty()) {
+            return new YangInstanceIdentifier.NodeIdentifier(mainQName);
+        } else {
+            return new YangInstanceIdentifier.NodeIdentifierWithPredicates(mainQName, predicates);
+        }
+
+    }
+
+    public static QName toIdentity(final String xPathArgument, final Element element, final SchemaContext schemaContext) {
+        final String xPathPartTrimmed = getIdAndPrefixAsStr(xPathArgument).trim();
+        final Iterator<String> it = COLON_SPLITTER.split(xPathPartTrimmed).iterator();
+
+        // Empty string
+        if (!it.hasNext()) {
+            return null;
+        }
+
+        final String prefix = it.next().trim();
+        if (prefix.isEmpty()) {
+            return null;
+        }
+
+        // it is not "prefix:value"
+        if (!it.hasNext()) {
+            return null;
+        }
+
+        final String identifier = it.next().trim();
+        if (identifier.isEmpty()) {
+            return null;
+        }
+
+        URI namespace = null;
+        String namespaceStr = null;
+        try {
+            namespaceStr = element.getAttribute("xmlns:"+prefix);
+            namespace = new URI(namespaceStr);
+        } catch (URISyntaxException e) {
+            throw new IllegalArgumentException("It wasn't possible to convert " + namespaceStr + " to URI object.");
+        } catch (NullPointerException e) {
+            throw new IllegalArgumentException("I wasn't possible to get namespace for prefix " + prefix);
+        }
+
+        Module module = schemaContext.findModuleByNamespaceAndRevision(namespace, null);
+        return QName.create(module.getQNameModule(), identifier);
+    }
+
+    private static String trimIfEndIs(final String str, final char end) {
+        final int l = str.length() - 1;
+        if (str.charAt(l) != end) {
+            return null;
+        }
+
+        return str.substring(1, l);
+    }
+
+    private static Object toPredicateValue(final String predicatedValue) {
+        final String predicatedValueTrimmed = predicatedValue.trim();
+        if (predicatedValue.isEmpty()) {
+            return null;
+        }
+        String updatedValue = null;
+        switch (predicatedValueTrimmed.charAt(0)) {
+        case '"':
+          updatedValue =  trimIfEndIs(predicatedValueTrimmed, '"');
+          break;
+        case '\'':
+          updatedValue =  trimIfEndIs(predicatedValueTrimmed, '\'');
+          break;
+        default:
+          updatedValue =  predicatedValueTrimmed;
+        }
+        Iterator<String> it = AT_SPLITTER.split(updatedValue).iterator();
+        // Empty string
+        if (!it.hasNext()) {
+          return null;
+        }
+
+        final String value = it.next().trim();
+        if (value.isEmpty()) {
+          return null;
+        }
+
+        if (!it.hasNext()) {
+          return value;
+        }
+
+      final String type = it.next().trim();
+      if (type.isEmpty()) {
+        return value;
+      }
+      Object predicateObject = null;
+      try {
+        predicateObject = Class.forName(type).getConstructor(String.class).newInstance(value);
+      } catch (Exception e) {
+        logger.error("Could not convert to valid type of value", e);
+      }
+      return predicateObject;
+    }
+}
diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/RandomPrefix.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/RandomPrefix.java
new file mode 100644 (file)
index 0000000..55cc819
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.remote.rpc.utils;
+
+import org.opendaylight.yangtools.yang.common.QName;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * Picked from Yang tools because it was not public class
+ */
+
+final class RandomPrefix {
+    final Map<URI, String> prefixes = new HashMap<>();
+
+    Iterable<Entry<URI, String>> getPrefixes() {
+        return prefixes.entrySet();
+    }
+
+    String encodeQName(final QName qname) {
+        String prefix = prefixes.get(qname.getNamespace());
+        if (prefix == null) {
+            prefix = qname.getPrefix();
+            if (prefix == null || prefix.isEmpty() || prefixes.containsValue(prefix)) {
+                final ThreadLocalRandom random = ThreadLocalRandom.current();
+                do {
+                    final StringBuilder sb = new StringBuilder();
+                    for (int i = 0; i < 4; i++) {
+                        sb.append((char)('a' + random.nextInt(25)));
+                    }
+
+                    prefix = sb.toString();
+                } while (prefixes.containsValue(prefix));
+            }
+
+            prefixes.put(qname.getNamespace(), prefix);
+        }
+
+        return prefix + ':' + qname.getLocalName();
+    }
+}
diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlDocumentUtils.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlDocumentUtils.java
new file mode 100644 (file)
index 0000000..9e5d8f0
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.remote.rpc.utils;
+
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import org.opendaylight.yangtools.yang.common.QName;
+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.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import javax.activation.UnsupportedDataTypeException;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.dom.DOMResult;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkState;
+
+public class XmlDocumentUtils {
+    private static class ElementWithSchemaContext {
+        Element element;
+        SchemaContext schemaContext;
+
+        ElementWithSchemaContext(final Element element,final SchemaContext schemaContext) {
+            this.schemaContext = schemaContext;
+            this.element = element;
+        }
+
+        Element getElement() {
+            return element;
+        }
+
+        SchemaContext getSchemaContext() {
+            return schemaContext;
+        }
+    }
+
+    public static final QName OPERATION_ATTRIBUTE_QNAME = QName.create(URI.create("urn:ietf:params:xml:ns:netconf:base:1.0"), null, "operation");
+    private static final Logger logger = LoggerFactory.getLogger(XmlDocumentUtils.class);
+    private static final XMLOutputFactory FACTORY = XMLOutputFactory.newFactory();
+
+    /**
+     * Converts Data DOM structure to XML Document for specified XML Codec Provider and corresponding
+     * Data Node Container schema. The CompositeNode data parameter enters as root of Data DOM tree and will
+     * be transformed to root in XML Document. Each element of Data DOM tree is compared against specified Data
+     * Node Container Schema and transformed accordingly.
+     *
+     * @param data Data DOM root element
+     * @param schema Data Node Container Schema
+     * @param codecProvider XML Codec Provider
+     * @return new instance of XML Document
+     * @throws javax.activation.UnsupportedDataTypeException
+     */
+    public static Document toDocument(final CompositeNode data, final DataNodeContainer schema, final XmlCodecProvider codecProvider)
+            throws UnsupportedDataTypeException {
+        Preconditions.checkNotNull(data);
+        Preconditions.checkNotNull(schema);
+
+        if (!(schema instanceof ContainerSchemaNode || schema instanceof ListSchemaNode)) {
+            throw new UnsupportedDataTypeException("Schema can be ContainerSchemaNode or ListSchemaNode. Other types are not supported yet.");
+        }
+
+        final DOMResult result = new DOMResult(getDocument());
+        try {
+            final XMLStreamWriter writer = FACTORY.createXMLStreamWriter(result);
+            XmlStreamUtils.create(codecProvider).writeDocument(writer, data, (SchemaNode)schema);
+            writer.close();
+            return (Document)result.getNode();
+        } catch (XMLStreamException e) {
+            logger.error("Failed to serialize data {}", data, e);
+            return null;
+        }
+    }
+
+    public static Document getDocument() {
+        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+        Document doc = null;
+        try {
+            DocumentBuilder bob = dbf.newDocumentBuilder();
+            doc = bob.newDocument();
+        } catch (ParserConfigurationException e) {
+            throw new RuntimeException(e);
+        }
+        return doc;
+    }
+
+
+    public static QName qNameFromElement(final Element xmlElement) {
+        String namespace = xmlElement.getNamespaceURI();
+        String localName = xmlElement.getLocalName();
+        return QName.create(namespace != null ? URI.create(namespace) : null, null, localName);
+    }
+
+    private static Node<?> toNodeWithSchema(final Element xmlElement, final DataSchemaNode schema, final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) {
+        checkQName(xmlElement, schema.getQName());
+        if (schema instanceof DataNodeContainer) {
+            return toCompositeNodeWithSchema(xmlElement, schema.getQName(), (DataNodeContainer) schema, codecProvider,schemaCtx);
+        } else if (schema instanceof LeafSchemaNode) {
+            return toSimpleNodeWithType(xmlElement, (LeafSchemaNode) schema, codecProvider,schemaCtx);
+        } else if (schema instanceof LeafListSchemaNode) {
+            return toSimpleNodeWithType(xmlElement, (LeafListSchemaNode) schema, codecProvider,schemaCtx);
+        }
+        return null;
+    }
+
+
+
+    private static Node<?> toSimpleNodeWithType(final Element xmlElement, final LeafSchemaNode schema,
+            final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) {
+        TypeDefinitionAwareCodec<? extends Object, ? extends TypeDefinition<?>> codec = codecProvider.codecFor(schema.getType());
+        String text = xmlElement.getTextContent();
+        Object value = null;
+        if (codec != null) {
+            value = codec.deserialize(text);
+        }
+        final TypeDefinition<?> baseType = XmlUtils.resolveBaseTypeFrom(schema.getType());
+        if (baseType instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifier) {
+            value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx);
+        } else if(baseType instanceof IdentityrefTypeDefinition){
+            value = InstanceIdentifierForXmlCodec.toIdentity(xmlElement.getTextContent(), xmlElement, schemaCtx);
+        }
+
+        if (value == null) {
+            value = xmlElement.getTextContent();
+        }
+
+        Optional<ModifyAction> modifyAction = getModifyOperationFromAttributes(xmlElement);
+        return new SimpleNodeTOImpl<>(schema.getQName(), null, value, modifyAction.orNull());
+    }
+
+    private static Node<?> toSimpleNodeWithType(final Element xmlElement, final LeafListSchemaNode schema,
+            final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) {
+        TypeDefinitionAwareCodec<? extends Object, ? extends TypeDefinition<?>> codec = codecProvider.codecFor(schema.getType());
+        String text = xmlElement.getTextContent();
+        Object value = null;
+        if (codec != null) {
+            value = codec.deserialize(text);
+        }
+        if (schema.getType() instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifier) {
+            value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx);
+        }
+        if (value == null) {
+            value = xmlElement.getTextContent();
+        }
+
+        Optional<ModifyAction> modifyAction = getModifyOperationFromAttributes(xmlElement);
+        return new SimpleNodeTOImpl<>(schema.getQName(), null, value, modifyAction.orNull());
+    }
+
+    private static Node<?> toCompositeNodeWithSchema(final Element xmlElement, final QName qName, final DataNodeContainer schema,
+            final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) {
+        List<Node<?>> values = toDomNodes(xmlElement, Optional.fromNullable(schema.getChildNodes()),schemaCtx);
+        Optional<ModifyAction> modifyAction = getModifyOperationFromAttributes(xmlElement);
+        return ImmutableCompositeNode.create(qName, values, modifyAction.orNull());
+    }
+
+    private static Optional<ModifyAction> getModifyOperationFromAttributes(final Element xmlElement) {
+        Attr attributeNodeNS = xmlElement.getAttributeNodeNS(OPERATION_ATTRIBUTE_QNAME.getNamespace().toString(), OPERATION_ATTRIBUTE_QNAME.getLocalName());
+        if(attributeNodeNS == null) {
+            return Optional.absent();
+        }
+
+        ModifyAction action = ModifyAction.fromXmlValue(attributeNodeNS.getValue());
+        Preconditions.checkArgument(action.isOnElementPermitted(), "Unexpected operation %s on %s", action, xmlElement);
+
+        return Optional.of(action);
+    }
+
+    private static void checkQName(final Element xmlElement, final QName qName) {
+        checkState(Objects.equal(xmlElement.getNamespaceURI(), qName.getNamespace().toString()));
+        checkState(qName.getLocalName().equals(xmlElement.getLocalName()));
+    }
+
+    public static final Optional<DataSchemaNode> findFirstSchema(final QName qname, final Collection<DataSchemaNode> dataSchemaNode) {
+        if (dataSchemaNode != null && !dataSchemaNode.isEmpty() && qname != null) {
+            for (DataSchemaNode dsn : dataSchemaNode) {
+                if (qname.isEqualWithoutRevision(dsn.getQName())) {
+                    return Optional.<DataSchemaNode> of(dsn);
+                } else if (dsn instanceof ChoiceNode) {
+                    for (ChoiceCaseNode choiceCase : ((ChoiceNode) dsn).getCases()) {
+                        Optional<DataSchemaNode> foundDsn = findFirstSchema(qname, choiceCase.getChildNodes());
+                        if (foundDsn != null && foundDsn.isPresent()) {
+                            return foundDsn;
+                        }
+                    }
+                }
+            }
+        }
+        return Optional.absent();
+    }
+
+  private static Node<?> toDomNode(Element element) {
+    QName qname = qNameFromElement(element);
+
+    ImmutableList.Builder<Node<?>> values = ImmutableList.<Node<?>> builder();
+    NodeList nodes = element.getChildNodes();
+    boolean isSimpleObject = true;
+    String value = null;
+    for (int i = 0; i < nodes.getLength(); i++) {
+      org.w3c.dom.Node child = nodes.item(i);
+      if (child instanceof Element) {
+        isSimpleObject = false;
+        values.add(toDomNode((Element) child));
+      }
+      if (isSimpleObject && child instanceof org.w3c.dom.Text) {
+        value = element.getTextContent();
+        if (!Strings.isNullOrEmpty(value)) {
+          isSimpleObject = true;
+        }
+      }
+    }
+    if (isSimpleObject) {
+      return new SimpleNodeTOImpl<>(qname, null, value);
+    }
+    return ImmutableCompositeNode.create(qname, values.build());
+  }
+
+  public static List<Node<?>> toDomNodes(final Element element, final Optional<Collection<DataSchemaNode>> context,SchemaContext schemaCtx) {
+    return forEachChild(element.getChildNodes(),schemaCtx, new Function<ElementWithSchemaContext, Optional<Node<?>>>() {
+
+      @Override
+      public Optional<Node<?>> apply(ElementWithSchemaContext input) {
+        if (context.isPresent()) {
+          QName partialQName = qNameFromElement(input.getElement());
+          Optional<DataSchemaNode> schemaNode = XmlDocumentUtils.findFirstSchema(partialQName, context.get());
+          if (schemaNode.isPresent()) {
+            return Optional.<Node<?>> fromNullable(//
+                toNodeWithSchema(input.getElement(), schemaNode.get(), XmlDocumentUtils.defaultValueCodecProvider(),input.getSchemaContext()));
+          }
+        }
+        return Optional.<Node<?>> fromNullable(toDomNode(input.getElement()));
+      }
+
+    });
+
+  }
+
+    private static final <T> List<T> forEachChild(final NodeList nodes, final SchemaContext schemaContext, final Function<ElementWithSchemaContext, Optional<T>> forBody) {
+        final int l = nodes.getLength();
+        if (l == 0) {
+            return ImmutableList.of();
+        }
+
+        final List<T> list = new ArrayList<>(l);
+        for (int i = 0; i < l; i++) {
+            org.w3c.dom.Node child = nodes.item(i);
+            if (child instanceof Element) {
+                Optional<T> result = forBody.apply(new ElementWithSchemaContext((Element) child,schemaContext));
+                if (result.isPresent()) {
+                    list.add(result.get());
+                }
+            }
+        }
+        return ImmutableList.copyOf(list);
+    }
+
+    public static final XmlCodecProvider defaultValueCodecProvider() {
+        return XmlUtils.DEFAULT_XML_CODEC_PROVIDER;
+    }
+}
diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlStreamUtils.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlStreamUtils.java
new file mode 100644 (file)
index 0000000..e74a84a
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.remote.rpc.utils;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.AttributesContainer;
+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.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider;
+import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils;
+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.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import java.net.URI;
+import java.util.Map.Entry;
+
+/**
+ * Utility class for bridging JAXP Stream and YANG Data APIs. Note that the definition of this class
+ * by no means final and subject to change as more functionality is centralized here.
+ */
+@Beta
+public class XmlStreamUtils {
+    private static final Logger LOG = LoggerFactory.getLogger(XmlStreamUtils.class);
+    private final XmlCodecProvider codecProvider;
+
+    protected XmlStreamUtils(final XmlCodecProvider codecProvider) {
+        this.codecProvider = Preconditions.checkNotNull(codecProvider);
+    }
+
+    /**
+     * Create a new instance encapsulating a particular codec provider.
+     *
+     * @param codecProvider XML codec provider
+     * @return A new instance
+     */
+    public static XmlStreamUtils create(final XmlCodecProvider codecProvider) {
+        return new XmlStreamUtils(codecProvider);
+    }
+
+    /**
+     * Check if a particular data element can be emitted as an empty element, bypassing value encoding. This
+     * functionality is optional, as valid XML stream is produced even if start/end element is produced unconditionally.
+     *
+     * @param data Data node
+     * @return True if the data node will result in empty element body.
+     */
+    public static boolean isEmptyElement(final Node<?> data) {
+        if (data == null) {
+            return true;
+        }
+
+        if (data instanceof CompositeNode) {
+            return ((CompositeNode) data).getValue().isEmpty();
+        }
+        if (data instanceof SimpleNode) {
+            return data.getValue() == null;
+        }
+
+        // Safe default
+        return false;
+    }
+
+    /**
+     * Write an InstanceIdentifier into the output stream. Calling corresponding {@link javax.xml.stream.XMLStreamWriter#writeStartElement(String)}
+     * and {@link javax.xml.stream.XMLStreamWriter#writeEndElement()} is the responsibility of the caller.
+     *
+     * @param writer XML Stream writer
+     * @param id InstanceIdentifier
+     * @throws javax.xml.stream.XMLStreamException
+     */
+    public static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull YangInstanceIdentifier id) throws XMLStreamException {
+        Preconditions.checkNotNull(writer, "Writer may not be null");
+        Preconditions.checkNotNull(id, "Variable should contain instance of instance identifier and can't be null");
+
+        final RandomPrefix prefixes = new RandomPrefix();
+        final String str = XmlUtils.encodeIdentifier(prefixes, id);
+
+        for (Entry<URI, String> e: prefixes.getPrefixes()) {
+            writer.writeNamespace(e.getValue(), e.getKey().toString());
+        }
+        writer.writeCharacters(str);
+    }
+
+    /**
+     * Write a full XML document corresponding to a CompositeNode into an XML stream writer.
+     *
+     * @param writer XML Stream writer
+     * @param data data node
+     * @param schema corresponding schema node, may be null
+     * @throws javax.xml.stream.XMLStreamException if an encoding problem occurs
+     */
+    public void writeDocument(final @Nonnull XMLStreamWriter writer, final @Nonnull CompositeNode data, final @Nullable SchemaNode schema) throws XMLStreamException {
+        // final Boolean repairing = (Boolean) writer.getProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES);
+        // Preconditions.checkArgument(repairing == true, "XML Stream Writer has to be repairing namespaces");
+
+        writer.writeStartDocument();
+        writeElement(writer, data, schema);
+        writer.writeEndDocument();
+        writer.flush();
+    }
+
+
+    /**
+     * Write an element into a XML stream writer. This includes the element start/end tags and
+     * the value of the element.
+     *
+     * @param writer XML Stream writer
+     * @param data data node
+     * @param schema Schema node
+     * @throws javax.xml.stream.XMLStreamException if an encoding problem occurs
+     */
+    public void writeElement(final XMLStreamWriter writer, final @Nonnull Node<?> data, final SchemaNode schema) throws XMLStreamException {
+        final QName qname = data.getNodeType();
+        final String pfx = qname.getPrefix() != null ? qname.getPrefix() : "";
+        final String ns = qname.getNamespace() != null ? qname.getNamespace().toString() : "";
+
+        if (isEmptyElement(data)) {
+            writer.writeEmptyElement(pfx, qname.getLocalName(), ns);
+            return;
+        }
+
+        writer.writeStartElement(pfx, qname.getLocalName(), ns);
+        if (data instanceof AttributesContainer && ((AttributesContainer) data).getAttributes() != null) {
+            for (Entry<QName, String> attribute : ((AttributesContainer) data).getAttributes().entrySet()) {
+                writer.writeAttribute(attribute.getKey().getNamespace().toString(), attribute.getKey().getLocalName(), attribute.getValue());
+            }
+        }
+
+        if (data instanceof SimpleNode<?>) {
+            // Simple node
+            if (schema instanceof LeafListSchemaNode) {
+                writeValue(writer, ((LeafListSchemaNode) schema).getType(), data.getValue());
+            } else if (schema instanceof LeafSchemaNode) {
+                writeValue(writer, ((LeafSchemaNode) schema).getType(), data.getValue());
+            } else {
+                Object value = data.getValue();
+                if (value != null) {
+                    writer.writeCharacters(String.valueOf(value));
+                }
+            }
+        } else {
+            // CompositeNode
+            for (Node<?> child : ((CompositeNode) data).getValue()) {
+                DataSchemaNode childSchema = null;
+                if (schema instanceof DataNodeContainer) {
+                    childSchema = SchemaUtils.findFirstSchema(child.getNodeType(), ((DataNodeContainer) schema).getChildNodes()).orNull();
+                    if (LOG.isDebugEnabled()) {
+                        if (childSchema == null) {
+                            LOG.debug("Probably the data node \"{}\" does not conform to schema", child == null ? "" : child.getNodeType().getLocalName());
+                        }
+                    }
+                }
+
+                writeElement(writer, child, childSchema);
+            }
+        }
+
+        writer.writeEndElement();
+    }
+
+    /**
+     * Write a value into a XML stream writer. This method assumes the start and end of element is
+     * emitted by the caller.
+     *
+     * @param writer XML Stream writer
+     * @param type type definitions
+     * @param value object value
+     * @throws javax.xml.stream.XMLStreamException if an encoding problem occurs
+     */
+    public void writeValue(final @Nonnull XMLStreamWriter writer, final @Nonnull TypeDefinition<?> type, final Object value) throws XMLStreamException {
+        if (value == null) {
+            LOG.debug("Value of {}:{} is null, not encoding it", type.getQName().getNamespace(), type.getQName().getLocalName());
+            return;
+        }
+
+        final TypeDefinition<?> baseType = XmlUtils.resolveBaseTypeFrom(type);
+        if (baseType instanceof IdentityrefTypeDefinition) {
+            write(writer, (IdentityrefTypeDefinition) baseType, value);
+        } else if (baseType instanceof InstanceIdentifierTypeDefinition) {
+            write(writer, (InstanceIdentifierTypeDefinition) baseType, value);
+        } else {
+            final TypeDefinitionAwareCodec<Object, ?> codec = codecProvider.codecFor(baseType);
+            String text;
+            if (codec != null) {
+                try {
+                    text = codec.serialize(value);
+                } catch (ClassCastException e) {
+                    LOG.error("Provided node value {} did not have type {} required by mapping. Using stream instead.", value, baseType, e);
+                    text = String.valueOf(value);
+                }
+            } else {
+                LOG.error("Failed to find codec for {}, falling back to using stream", baseType);
+                text = String.valueOf(value);
+            }
+            writer.writeCharacters(text);
+        }
+    }
+
+    private static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull IdentityrefTypeDefinition type, final @Nonnull Object value) throws XMLStreamException {
+        if (value instanceof QName) {
+            final QName qname = (QName) value;
+            final String prefix;
+            if (qname.getPrefix() != null && !qname.getPrefix().isEmpty()) {
+                prefix = qname.getPrefix();
+            } else {
+                prefix = "x";
+            }
+
+            writer.writeNamespace(prefix, qname.getNamespace().toString());
+            writer.writeCharacters(prefix + ':' + qname.getLocalName());
+        } else {
+            LOG.debug("Value of {}:{} is not a QName but {}", type.getQName().getNamespace(), type.getQName().getLocalName(), value.getClass());
+            writer.writeCharacters(String.valueOf(value));
+        }
+    }
+
+    private static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull InstanceIdentifierTypeDefinition type, final @Nonnull Object value) throws XMLStreamException {
+        if (value instanceof YangInstanceIdentifier) {
+            write(writer, (YangInstanceIdentifier)value);
+        } else {
+            LOG.debug("Value of {}:{} is not an InstanceIdentifier but {}", type.getQName().getNamespace(), type.getQName().getLocalName(), value.getClass());
+            writer.writeCharacters(String.valueOf(value));
+        }
+    }
+}
@@ -5,26 +5,34 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-
-package org.opendaylight.controller.remote.rpc;
+package org.opendaylight.controller.remote.rpc.utils;
 
 import com.google.common.base.Optional;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.opendaylight.yangtools.yang.common.QName;
 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.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.impl.ImmutableCompositeNode;
 import org.opendaylight.yangtools.yang.data.impl.XmlTreeBuilder;
-import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
+import org.w3c.dom.Element;
 import org.xml.sax.SAXException;
 
 import javax.activation.UnsupportedDataTypeException;
+import javax.annotation.Nonnull;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.transform.OutputKeys;
 import javax.xml.transform.Transformer;
@@ -35,22 +43,37 @@ import javax.xml.transform.stream.StreamResult;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.StringWriter;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
+/**
+ * Common XML-related utility methods, which are not specific to a particular
+ * JAXP API.
+ */
 public class XmlUtils {
 
+    public static final XmlCodecProvider DEFAULT_XML_CODEC_PROVIDER = new XmlCodecProvider() {
+        @Override
+        public TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> codecFor(final TypeDefinition<?> baseType) {
+            return TypeDefinitionAwareCodec.from(baseType);
+        }
+    };
+
+    private XmlUtils() {
+    }
   private static final Logger LOG = LoggerFactory.getLogger(XmlUtils.class);
 
   public static String inputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){
     if (cNode == null) return new String();
 
-    //Document domTree = NodeUtils.buildShadowDomTree(cNode);
     Document domTree = null;
     try {
       Set<RpcDefinition> rpcs =  schemaContext.getOperations();
       for(RpcDefinition rpc : rpcs) {
         if(rpc.getQName().equals(cNode.getNodeType())){
-          domTree = XmlDocumentUtils.toDocument(cNode, rpc.getInput(), XmlDocumentUtils.defaultValueCodecProvider());
+          CompositeNode inputContainer = cNode.getFirstCompositeByName(QName.create(cNode.getNodeType(), "input"));
+          domTree = XmlDocumentUtils.toDocument(inputContainer, rpc.getInput(), XmlDocumentUtils.defaultValueCodecProvider());
           break;
         }
       }
@@ -64,13 +87,12 @@ public class XmlUtils {
   public static String outputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){
     if (cNode == null) return new String();
 
-    //Document domTree = NodeUtils.buildShadowDomTree(cNode);
     Document domTree = null;
     try {
       Set<RpcDefinition> rpcs =  schemaContext.getOperations();
       for(RpcDefinition rpc : rpcs) {
         if(rpc.getQName().equals(cNode.getNodeType())){
-          domTree = XmlDocumentUtils.toDocument(cNode, rpc.getInput(), XmlDocumentUtils.defaultValueCodecProvider());
+          domTree = XmlDocumentUtils.toDocument(cNode, rpc.getOutput(), XmlDocumentUtils.defaultValueCodecProvider());
           break;
         }
       }
@@ -121,7 +143,7 @@ public class XmlUtils {
   public static CompositeNode inputXmlToCompositeNode(QName rpc, String xml,  SchemaContext schemaContext){
     if (xml==null || xml.length()==0) return null;
 
-    Node<?> dataTree = null;
+    CompositeNode compositeNode = null;
     try {
 
       Document doc = XmlUtil.readXmlToDocument(xml);
@@ -129,7 +151,14 @@ public class XmlUtils {
       Set<RpcDefinition> rpcs =  schemaContext.getOperations();
       for(RpcDefinition rpcDef : rpcs) {
         if(rpcDef.getQName().equals(rpc)){
-          dataTree = XmlDocumentUtils.toDomNode(doc.getDocumentElement(), Optional.<DataSchemaNode>of(rpcDef.getInput()), Optional.of(XmlDocumentUtils.defaultValueCodecProvider()));
+          QName input = rpcDef.getInput().getQName();
+          Element xmlData = (Element) doc.getElementsByTagNameNS(input.getNamespace().toString(), "input").item(0);
+          List<Node<?>> dataNodes = XmlDocumentUtils.toDomNodes(xmlData,
+              Optional.of(rpcDef.getInput().getChildNodes()), schemaContext);
+          final CompositeNodeBuilder<ImmutableCompositeNode> it = ImmutableCompositeNode.builder();
+          it.setQName(input);
+          it.add(ImmutableCompositeNode.create(input, dataNodes));
+          compositeNode = it.toInstance();
           break;
         }
       }
@@ -139,7 +168,45 @@ public class XmlUtils {
       LOG.error("Error during building data tree from XML", e);
     }
 
-    LOG.debug("xmlToCompositeNode " + dataTree.toString());
-    return (CompositeNode) dataTree;
+    LOG.debug("xmlToCompositeNode " + compositeNode.toString());
+    return compositeNode;
   }
+
+    public static TypeDefinition<?> resolveBaseTypeFrom(final @Nonnull TypeDefinition<?> type) {
+        TypeDefinition<?> superType = type;
+        while (superType.getBaseType() != null) {
+            superType = superType.getBaseType();
+        }
+        return superType;
+    }
+
+    static String encodeIdentifier(final RandomPrefix prefixes, final YangInstanceIdentifier id) {
+        StringBuilder textContent = new StringBuilder();
+        for (PathArgument pathArgument : id.getPathArguments()) {
+            textContent.append('/');
+            textContent.append(prefixes.encodeQName(pathArgument.getNodeType()));
+            if (pathArgument instanceof NodeIdentifierWithPredicates) {
+                Map<QName, Object> predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues();
+
+                for (QName keyValue : predicates.keySet()) {
+                  Object value = predicates.get(keyValue);
+                  String type = value.getClass().getName();
+                  String predicateValue = String.valueOf(value);
+                    textContent.append('[');
+                    textContent.append(prefixes.encodeQName(keyValue));
+                    textContent.append("='");
+                    textContent.append(predicateValue);
+                    textContent.append("@");
+                    textContent.append(type);
+                    textContent.append("']");
+                }
+            } else if (pathArgument instanceof NodeWithValue) {
+                textContent.append("[.='");
+                textContent.append(((NodeWithValue) pathArgument).getValue());
+                textContent.append("']");
+            }
+        }
+
+        return textContent.toString();
+    }
 }
index 9585e9ffb6b10a45340011f9aa9fbcc34f46b9e1..6088dd0e0ea517c27bd74e79272a92f4b5b7ef1d 100644 (file)
@@ -7,7 +7,7 @@ odl-cluster{
     remote {
       log-remote-lifecycle-events = off
       netty.tcp {
-        hostname = "192.168.141.142"
+        hostname = "192.168.141.141"
         port = 2551
       }
     }
index 595d8331eb48ffba9bbef69da7bb1e293e98fbd8..392c1e637d848e1ccc47d43b3a951011a2e25a72 100644 (file)
@@ -35,6 +35,7 @@ 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.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
@@ -72,15 +73,16 @@ public class RpcBrokerTest {
       Broker.ProviderSession brokerSession = Mockito.mock(Broker.ProviderSession.class);
       SchemaContext schemaContext = mock(SchemaContext.class);
       ActorRef rpcBroker = system.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext));
-      QName rpc = new QName(new URI("actor1"), "actor1");
-      InvokeRpc invokeMsg = new InvokeRpc(rpc, null);
+      QName rpc = new QName(new URI("noactor1"), "noactor1");
+      CompositeNode input = new ImmutableCompositeNode(QName.create("ns", "2013-12-09", "no child"), new ArrayList<Node<?>>(), ModifyAction.REPLACE);
+      InvokeRpc invokeMsg = new InvokeRpc(rpc, input);
       rpcBroker.tell(invokeMsg, getRef());
 
       Boolean getMsg = new ExpectMsg<Boolean>("ErrorResponse") {
         protected Boolean match(Object in) {
           if (in instanceof ErrorResponse) {
             ErrorResponse reply = (ErrorResponse)in;
-            return "No remote actor found for rpc execution.".equals(reply.getException().getMessage());
+            return reply.getException().getMessage().contains("No remote actor found for rpc execution of :");
           } else {
             throw noMatch();
           }
@@ -144,7 +146,8 @@ public class RpcBrokerTest {
       SchemaContext schemaContext = mock(SchemaContext.class);
       ActorRef rpcBroker = system.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext));
       QName rpc = new QName(new URI("actor1"), "actor1");
-      InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, null);
+      CompositeNode input = new ImmutableCompositeNode(QName.create("ns", "2013-12-09", "child1"), new ArrayList<Node<?>>(), ModifyAction.REPLACE);
+      InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, YangInstanceIdentifier.create(new YangInstanceIdentifier.NodeIdentifier(rpc)), input);
       rpcBroker.tell(invokeMsg, getRef());
 
       Boolean getMsg = new ExpectMsg<Boolean>("ErrorResponse") {
@@ -176,7 +179,8 @@ public class RpcBrokerTest {
       ActorRef rpcBrokerRemote = system.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext), "actor2");
       // Add Routed RPC in table
       QName rpc = new QName(new URI("actor2"), "actor2");
-      RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, rpc, null);
+      YangInstanceIdentifier identifier = YangInstanceIdentifier.create(new YangInstanceIdentifier.NodeIdentifier(rpc));
+      RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, rpc, identifier);
       final String route = rpcBrokerRemote.path().toString();
       Set<RpcRouter.RouteIdentifier<?, ?, ?>> routeIds = new HashSet<>();
       routeIds.add(routeId);
@@ -192,7 +196,7 @@ public class RpcBrokerTest {
       RpcResult<CompositeNode> result = Rpcs.getRpcResult(true, invokeRpcResult, errors);
       Future<RpcResult<CompositeNode>> rpcResult = Futures.immediateFuture(result);
       when(brokerSession.rpc(rpc, input)).thenReturn(rpcResult);
-      InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, input);
+      InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, identifier, input);
       rpcBroker.tell(invokeMsg, getRef());
 
       //verify response msg
index a57402a793ae06ca5dc4f4259c8e2d2b74b3d3aa..129a5a56e838298416716ff4e056faec7ae1370c 100644 (file)
@@ -39,7 +39,8 @@ public class RoutingTableTest {
   @Test
   public void addGlobalRouteNullRouteTest() {
     try {
-      RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, null, null);
+      QName type = new QName(new URI("actor1"), "actor1");
+      RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, type, null);
       routingTable.addGlobalRoute(routeId, null);
 
       Assert.fail("Null pointer exception was not thrown.");
@@ -109,7 +110,8 @@ public class RoutingTableTest {
   @Test
   public void addRoutedRpcNullRouteTest() {
     try {
-      RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, null, null);
+      QName type = new QName(new URI("actor1"), "actor1");
+      RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, type, null);
 
       routingTable.addRoutedRpc(routeId, null);
 
diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/utils/XmlUtilsTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/utils/XmlUtilsTest.java
new file mode 100644 (file)
index 0000000..92340b6
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.remote.rpc.utils;
+
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.io.ByteSource;
+import junit.framework.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+
+
+public class XmlUtilsTest {
+
+  private static final DocumentBuilderFactory BUILDERFACTORY;
+
+  static {
+    final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+    factory.setNamespaceAware(true);
+    factory.setCoalescing(true);
+    factory.setIgnoringElementContentWhitespace(true);
+    factory.setIgnoringComments(true);
+    BUILDERFACTORY = factory;
+  }
+
+  private SchemaContext schema;
+  private RpcDefinition testRpc;
+
+  public static final String XML_CONTENT = "<input xmlns=\"urn:opendaylight:controller:rpc:test\">" +
+      "<id>flowid</id>" +
+      "<flow xmlns:ltha=\"urn:opendaylight:controller:rpc:test\">/ltha:node/ltha:node1[ltha:id='3@java.lang.Short']</flow>" +
+      "</input>";
+
+  @Before
+  public void setUp() throws Exception {
+    final ByteSource byteSource = new ByteSource() {
+      @Override
+      public InputStream openStream() throws IOException {
+        return XmlUtilsTest.this.getClass().getResourceAsStream("rpcTest.yang");
+      }
+    };
+    schema = new YangParserImpl().parseSources(Lists.newArrayList(byteSource));
+    final Module rpcTestModule = schema.getModules().iterator().next();
+    testRpc = rpcTestModule.getRpcs().iterator().next();
+  }
+
+  @Test
+  public void testInputXmlToCompositeNode() {
+    CompositeNode node = XmlUtils.inputXmlToCompositeNode(testRpc.getQName(), XML_CONTENT, schema);
+    ImmutableList<SimpleNode> input = (ImmutableList)node.getValue().get(0).getValue();
+    SimpleNode firstNode = input.get(0);
+
+    Assert.assertEquals("id", firstNode.getNodeType().getLocalName());
+    Assert.assertEquals("flowid", firstNode.getValue());
+
+    SimpleNode secondNode = input.get(1);
+    Assert.assertEquals("flow", secondNode.getNodeType().getLocalName());
+
+    YangInstanceIdentifier instance = (YangInstanceIdentifier) secondNode.getValue();
+    Iterable<YangInstanceIdentifier.PathArgument> iterable = instance.getPathArguments();
+    Iterator it = iterable.iterator();
+    YangInstanceIdentifier.NodeIdentifier firstPath = (YangInstanceIdentifier.NodeIdentifier) it.next();
+    Assert.assertEquals("node", firstPath.getNodeType().getLocalName());
+    YangInstanceIdentifier.NodeIdentifierWithPredicates secondPath = (YangInstanceIdentifier.NodeIdentifierWithPredicates)it.next();
+    Short value = (Short)secondPath.getKeyValues().values().iterator().next();
+    Short expected = 3;
+    Assert.assertEquals(expected, value);
+  }
+
+
+}
diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/org/opendaylight/controller/remote/rpc/utils/rpcTest.yang b/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/org/opendaylight/controller/remote/rpc/utils/rpcTest.yang
new file mode 100644 (file)
index 0000000..5fc564f
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+module rpc-test {
+    yang-version 1;
+    namespace "urn:opendaylight:controller:rpc:test";
+    prefix "rpct";
+
+    revision 2014-07-29 {
+       description "rpc test";
+    }
+
+    typedef flow-ref {
+        type instance-identifier;
+    }
+
+    rpc add-flow {
+        input {
+            leaf id {
+                type string;
+            }
+
+            leaf flow {
+                type flow-ref;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 8bebd2aa61050f0d8058d82f312ac572ec44bf86..995917b62db283b6ff34d1b8c61b4a8e9f5afc81 100644 (file)
--- a/pom.xml
+++ b/pom.xml
 
     <!-- Parents -->
     <module>opendaylight/commons/concepts</module>
-    <module>opendaylight/commons/protocol-framework</module>
     <module>opendaylight/commons/httpclient</module>
     <module>opendaylight/commons/checkstyle</module>
     <module>opendaylight/commons/opendaylight</module>