Merge "Add replication capability to Shard"
authorEd Warnicke <eaw@cisco.com>
Fri, 1 Aug 2014 01:26:27 +0000 (01:26 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Fri, 1 Aug 2014 01:26:27 +0000 (01:26 +0000)
41 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/config/yang/config/remote_rpc_connector/RemoteRPCBrokerModule.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/config/yang/config/remote_rpc_connector/RemoteRPCBrokerModuleFactory.java
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/RemoteRpcProvider.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProviderFactory.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/RpcListener.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcManager.java [new file with mode: 0644]
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/XmlUtils.java [deleted file]
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/ActorUtil.java [moved from opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/ActorUtil.java with 97% similarity]
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 [new file with mode: 0644]
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]
opendaylight/northbound/commons/pom.xml

index 03da7f0..f25b7d9 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 14d4f24..3f02765 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 6ee301d..ce3bfe9 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 8315bbe..9824889 100644 (file)
@@ -2,10 +2,8 @@ package org.opendaylight.controller.config.yang.config.remote_rpc_connector;
 
 import org.opendaylight.controller.remote.rpc.RemoteRpcProviderFactory;
 import org.opendaylight.controller.sal.core.api.Broker;
-import org.osgi.framework.BundleContext;
 
 public class RemoteRPCBrokerModule extends org.opendaylight.controller.config.yang.config.remote_rpc_connector.AbstractRemoteRPCBrokerModule {
-  private BundleContext bundleContext;
   public RemoteRPCBrokerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
     super(identifier, dependencyResolver);
   }
@@ -22,10 +20,6 @@ public class RemoteRPCBrokerModule extends org.opendaylight.controller.config.ya
   @Override
   public java.lang.AutoCloseable createInstance() {
     Broker broker = getDomBrokerDependency();
-    return RemoteRpcProviderFactory.createInstance(broker, bundleContext);
-  }
-
-  public void setBundleContext(final BundleContext bundleContext) {
-    this.bundleContext = bundleContext;
+    return RemoteRpcProviderFactory.createInstance(broker);
   }
 }
index e1ba46a..330845b 100644 (file)
@@ -9,27 +9,7 @@
 */
 package org.opendaylight.controller.config.yang.config.remote_rpc_connector;
 
-import org.opendaylight.controller.config.api.DependencyResolver;
-import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
-import org.opendaylight.controller.config.spi.Module;
-import org.osgi.framework.BundleContext;
 
 public class RemoteRPCBrokerModuleFactory extends org.opendaylight.controller.config.yang.config.remote_rpc_connector.AbstractRemoteRPCBrokerModuleFactory {
 
-  @Override
-  public Module createModule(String instanceName, DependencyResolver dependencyResolver, BundleContext bundleContext) {
-    RemoteRPCBrokerModule module = (RemoteRPCBrokerModule)super.createModule(instanceName,dependencyResolver,bundleContext);
-    module.setBundleContext(bundleContext);
-    return module;
-  }
-
-  @Override
-  public Module createModule(String instanceName, DependencyResolver dependencyResolver,
-                             DynamicMBeanWithInstance old, BundleContext bundleContext) throws Exception {
-    RemoteRPCBrokerModule module = (RemoteRPCBrokerModule)super.createModule(instanceName, dependencyResolver,
-        old, bundleContext);
-    module.setBundleContext(bundleContext);
-    return module;
-  }
-
 }
index 43aa5b7..d384144 100644 (file)
@@ -7,21 +7,19 @@ 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.ActorUtil;
+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 +35,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 +54,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 1bb7ea4..3df572d 100644 (file)
@@ -11,37 +11,35 @@ package org.opendaylight.controller.remote.rpc;
 
 import akka.actor.ActorRef;
 import akka.actor.ActorSystem;
+import org.opendaylight.controller.remote.rpc.messages.UpdateSchemaContext;
 import org.opendaylight.controller.remote.rpc.registry.ClusterWrapper;
 import org.opendaylight.controller.remote.rpc.registry.ClusterWrapperImpl;
-import org.opendaylight.controller.remote.rpc.registry.RpcRegistry;
 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.model.SchemaService;
-import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.Collection;
-import java.util.Set;
 
 /**
  * This is the base class which initialize all the actors, listeners and
  * default RPc implementation so remote invocation of rpcs.
  */
-public class RemoteRpcProvider implements AutoCloseable, Provider{
+public class RemoteRpcProvider implements AutoCloseable, Provider, SchemaContextListener {
 
   private static final Logger LOG = LoggerFactory.getLogger(RemoteRpcProvider.class);
 
   private final ActorSystem actorSystem;
-  private ActorRef rpcBroker;
-  private ActorRef rpcRegistry;
   private final RpcProvisionRegistry rpcProvisionRegistry;
   private Broker.ProviderSession brokerSession;
-  private RpcListener rpcListener;
-  private RoutedRpcListener routeChangeListener;
-  private RemoteRpcImplementation rpcImplementation;
+  private SchemaContext schemaContext;
+  private ActorRef rpcManager;
+
+
   public RemoteRpcProvider(ActorSystem actorSystem, RpcProvisionRegistry rpcProvisionRegistry) {
     this.actorSystem = actorSystem;
     this.rpcProvisionRegistry = rpcProvisionRegistry;
@@ -50,8 +48,6 @@ public class RemoteRpcProvider implements AutoCloseable, Provider{
   @Override
   public void close() throws Exception {
     this.actorSystem.shutdown();
-    unregisterSupportedRpcs();
-    unregisterSupportedRoutedRpcs();
   }
 
   @Override
@@ -66,64 +62,22 @@ public class RemoteRpcProvider implements AutoCloseable, Provider{
   }
 
   private void start() {
-    LOG.debug("Starting all rpc listeners.");
+    LOG.info("Starting all rpc listeners and actors.");
     // Create actor to handle and sync routing table in cluster
     ClusterWrapper clusterWrapper = new ClusterWrapperImpl(actorSystem);
-    rpcRegistry = actorSystem.actorOf(RpcRegistry.props(clusterWrapper), "rpc-registry");
-
-    // Create actor to invoke and execute rpc
     SchemaService schemaService = brokerSession.getService(SchemaService.class);
-    SchemaContext schemaContext = schemaService.getGlobalContext();
-    rpcBroker = actorSystem.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext), "rpc-broker");
-    String rpcBrokerPath = clusterWrapper.getAddress().toString() + "/user/rpc-broker";
-    rpcListener = new RpcListener(rpcRegistry, rpcBrokerPath);
-    routeChangeListener = new RoutedRpcListener(rpcRegistry, rpcBrokerPath);
-    rpcImplementation = new RemoteRpcImplementation(rpcBroker, schemaContext);
-    brokerSession.addRpcRegistrationListener(rpcListener);
-    rpcProvisionRegistry.registerRouteChangeListener(routeChangeListener);
-    rpcProvisionRegistry.setRoutedRpcDefaultDelegate(rpcImplementation);
-    announceSupportedRpcs();
-    announceSupportedRoutedRpcs();
+    schemaContext = schemaService.getGlobalContext();
 
-  }
+    rpcManager = actorSystem.actorOf(RpcManager.props(clusterWrapper, schemaContext, brokerSession, rpcProvisionRegistry), "rpc");
 
-  /**
-   * Add all the locally registered RPCs in the clustered routing table
-   */
-  private void announceSupportedRpcs(){
-    LOG.debug("Adding all supported rpcs to routing table");
-    Set<QName> currentlySupported = brokerSession.getSupportedRpcs();
-    for (QName rpc : currentlySupported) {
-      rpcListener.onRpcImplementationAdded(rpc);
-    }
+    LOG.debug("Rpc actors are created.");
   }
 
-  /**
-   * Add all the locally registered Routed RPCs in the clustered routing table
-   */
-  private void announceSupportedRoutedRpcs(){
-
-    //TODO: announce all routed RPCs as well
 
-  }
-
-  /**
-   * Un-Register all the supported RPCs from clustered routing table
-   */
-  private void unregisterSupportedRpcs(){
-    LOG.debug("removing all supported rpcs to routing table");
-    Set<QName> currentlySupported = brokerSession.getSupportedRpcs();
-    for (QName rpc : currentlySupported) {
-      rpcListener.onRpcImplementationRemoved(rpc);
-    }
-  }
-
-  /**
-   * Un-Register all the locally supported Routed RPCs from clustered routing table
-   */
-  private void unregisterSupportedRoutedRpcs(){
-
-    //TODO: remove all routed RPCs as well
+  @Override
+  public void onGlobalContextUpdated(SchemaContext schemaContext) {
+    this.schemaContext = schemaContext;
+    rpcManager.tell(new UpdateSchemaContext(schemaContext), null);
 
   }
 }
index 61dc818..4c40ca1 100644 (file)
@@ -11,13 +11,12 @@ package org.opendaylight.controller.remote.rpc;
 
 import org.opendaylight.controller.sal.core.api.Broker;
 import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
-import org.osgi.framework.BundleContext;
 
 public class RemoteRpcProviderFactory {
-    public static RemoteRpcProvider createInstance(final Broker broker, final BundleContext bundleContext){
+    public static RemoteRpcProvider createInstance(final Broker broker){
       RemoteRpcProvider rpcProvider =
           new RemoteRpcProvider(ActorSystemFactory.getInstance(), (RpcProvisionRegistry) broker);
-      broker.registerProvider(rpcProvider, bundleContext);
+      broker.registerProvider(rpcProvider);
       return rpcProvider;
     }
 }
index ea72238..85d2138 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 a0df362..a6eeac0 100644 (file)
@@ -10,13 +10,15 @@ 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.remote.rpc.utils.ActorUtil;
 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 +26,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 +83,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 3354fc3..26e8e96 100644 (file)
@@ -20,6 +20,8 @@ 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.ActorUtil;
+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;
@@ -38,7 +40,7 @@ public class RpcBroker extends AbstractUntypedActor {
   private static final Logger LOG = LoggerFactory.getLogger(RpcBroker.class);
   private final Broker.ProviderSession brokerSession;
   private final ActorRef rpcRegistry;
-  private final SchemaContext schemaContext;
+  private SchemaContext schemaContext;
 
   private RpcBroker(Broker.ProviderSession brokerSession, ActorRef rpcRegistry, SchemaContext schemaContext){
     this.brokerSession = brokerSession;
@@ -72,7 +74,7 @@ public class RpcBroker extends AbstractUntypedActor {
     try {
       RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, msg.getRpc(), msg.getIdentifier());
       GetRoutedRpc routedRpcMsg = new GetRoutedRpc(routeId);
-      GetRoutedRpcReply rpcReply = (GetRoutedRpcReply)ActorUtil.executeLocalOperation(rpcRegistry, routedRpcMsg, ActorUtil.LOCAL_ASK_DURATION, ActorUtil.LOCAL_AWAIT_DURATION);
+      GetRoutedRpcReply rpcReply = (GetRoutedRpcReply) ActorUtil.executeLocalOperation(rpcRegistry, routedRpcMsg, ActorUtil.LOCAL_ASK_DURATION, ActorUtil.LOCAL_AWAIT_DURATION);
 
       String remoteActorPath = rpcReply.getRoutePath();
       if(remoteActorPath == null) {
@@ -105,11 +107,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 +130,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 ae760fa..f614990 100644 (file)
@@ -12,6 +12,7 @@ package org.opendaylight.controller.remote.rpc;
 import akka.actor.ActorRef;
 import org.opendaylight.controller.remote.rpc.messages.AddRpc;
 import org.opendaylight.controller.remote.rpc.messages.RemoveRpc;
+import org.opendaylight.controller.remote.rpc.utils.ActorUtil;
 import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.slf4j.Logger;
diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcManager.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcManager.java
new file mode 100644 (file)
index 0000000..4925a17
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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;
+
+
+import akka.actor.ActorRef;
+import akka.actor.OneForOneStrategy;
+import akka.actor.Props;
+import akka.actor.SupervisorStrategy;
+import akka.japi.Creator;
+import akka.japi.Function;
+import org.opendaylight.controller.remote.rpc.messages.UpdateSchemaContext;
+import org.opendaylight.controller.remote.rpc.registry.ClusterWrapper;
+import org.opendaylight.controller.remote.rpc.registry.RpcRegistry;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import scala.concurrent.duration.Duration;
+import java.util.Set;
+
+/**
+ * This class acts as a supervisor, creates all the actors, resumes them, if an exception is thrown.
+ *
+ * It also starts the rpc listeners
+ */
+
+public class RpcManager extends AbstractUntypedActor {
+
+  private static final Logger LOG = LoggerFactory.getLogger(RpcManager.class);
+
+  private SchemaContext schemaContext;
+  private final ClusterWrapper clusterWrapper;
+  private ActorRef rpcBroker;
+  private ActorRef rpcRegistry;
+  private final Broker.ProviderSession brokerSession;
+  private RpcListener rpcListener;
+  private RoutedRpcListener routeChangeListener;
+  private RemoteRpcImplementation rpcImplementation;
+  private final RpcProvisionRegistry rpcProvisionRegistry;
+
+  private RpcManager(ClusterWrapper clusterWrapper, SchemaContext schemaContext,
+                     Broker.ProviderSession brokerSession, RpcProvisionRegistry rpcProvisionRegistry) {
+    this.clusterWrapper = clusterWrapper;
+    this.schemaContext = schemaContext;
+    this.brokerSession = brokerSession;
+    this.rpcProvisionRegistry = rpcProvisionRegistry;
+
+    createRpcActors();
+    startListeners();
+  }
+
+
+  public static Props props(final ClusterWrapper clusterWrapper, final SchemaContext schemaContext,
+                            final Broker.ProviderSession brokerSession, final RpcProvisionRegistry rpcProvisionRegistry) {
+    return Props.create(new Creator<RpcManager>() {
+      @Override
+      public RpcManager create() throws Exception {
+        return new RpcManager(clusterWrapper, schemaContext, brokerSession, rpcProvisionRegistry);
+      }
+    });
+  }
+
+  private void createRpcActors() {
+    LOG.debug("Create rpc registry and broker actors");
+
+    rpcRegistry = getContext().actorOf(RpcRegistry.props(clusterWrapper), "rpc-registry");
+    rpcBroker = getContext().actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext), "rpc-broker");
+  }
+
+  private void startListeners() {
+    LOG.debug("Registers rpc listeners");
+
+    String rpcBrokerPath = clusterWrapper.getAddress().toString() + "/user/rpc/rpc-broker";
+    rpcListener = new RpcListener(rpcRegistry, rpcBrokerPath);
+    routeChangeListener = new RoutedRpcListener(rpcRegistry, rpcBrokerPath);
+    rpcImplementation = new RemoteRpcImplementation(rpcBroker, schemaContext);
+
+    brokerSession.addRpcRegistrationListener(rpcListener);
+    rpcProvisionRegistry.registerRouteChangeListener(routeChangeListener);
+    rpcProvisionRegistry.setRoutedRpcDefaultDelegate(rpcImplementation);
+    announceSupportedRpcs();
+  }
+
+  /**
+   * Add all the locally registered RPCs in the clustered routing table
+   */
+  private void announceSupportedRpcs(){
+    LOG.debug("Adding all supported rpcs to routing table");
+    Set<QName> currentlySupported = brokerSession.getSupportedRpcs();
+    for (QName rpc : currentlySupported) {
+      rpcListener.onRpcImplementationAdded(rpc);
+    }
+  }
+
+
+  @Override
+  protected void handleReceive(Object message) throws Exception {
+    if(message instanceof UpdateSchemaContext) {
+      updateSchemaContext((UpdateSchemaContext) message);
+    }
+
+  }
+
+  private void updateSchemaContext(UpdateSchemaContext message) {
+    this.schemaContext = message.getSchemaContext();
+  }
+
+  @Override
+  public SupervisorStrategy supervisorStrategy() {
+    return new OneForOneStrategy(10, Duration.create("1 minute"),
+        new Function<Throwable, SupervisorStrategy.Directive>() {
+          @Override
+          public SupervisorStrategy.Directive apply(Throwable t) {
+            return SupervisorStrategy.resume();
+          }
+        }
+    );
+  }
+}
diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/XmlUtils.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/XmlUtils.java
deleted file mode 100644 (file)
index 5fb2bb8..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.remote.rpc;
-
-import com.google.common.base.Optional;
-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.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.model.api.RpcDefinition;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.controller.netconf.util.xml.XmlUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.w3c.dom.Document;
-import org.xml.sax.SAXException;
-
-import javax.activation.UnsupportedDataTypeException;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.util.Set;
-
-public class 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());
-          break;
-        }
-      }
-
-    } catch (UnsupportedDataTypeException e) {
-      LOG.error("Error during translation of CompositeNode to Document", e);
-    }
-    return domTransformer(domTree);
-  }
-
-  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());
-          break;
-        }
-      }
-
-    } catch (UnsupportedDataTypeException e) {
-      LOG.error("Error during translation of CompositeNode to Document", e);
-    }
-    return domTransformer(domTree);
-  }
-
-  private static String domTransformer(Document domTree) {
-    StringWriter writer = new StringWriter();
-    try {
-      TransformerFactory tf = TransformerFactory.newInstance();
-      Transformer transformer = tf.newTransformer();
-      transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
-      transformer.transform(new DOMSource(domTree), new StreamResult(writer));
-    } catch (TransformerException e) {
-
-      LOG.error("Error during translation of Document to OutputStream", e);
-    }
-    LOG.debug("compositeNodeToXml " + writer.toString());
-
-    return writer.toString();
-  }
-
-  public static CompositeNode xmlToCompositeNode(String xml){
-    if (xml==null || xml.length()==0) return null;
-
-    Node<?> dataTree;
-    try {
-      dataTree = XmlTreeBuilder.buildDataTree(new ByteArrayInputStream(xml.getBytes()));
-    } catch (XMLStreamException e) {
-      LOG.error("Error during building data tree from XML", e);
-      return null;
-    }
-    if (dataTree == null) {
-      LOG.error("data tree is null");
-      return null;
-    }
-    if (dataTree instanceof SimpleNode) {
-      LOG.error("RPC XML was resolved as SimpleNode");
-      return null;
-    }
-    return (CompositeNode) dataTree;
-  }
-
-  public static CompositeNode inputXmlToCompositeNode(QName rpc, String xml,  SchemaContext schemaContext){
-    if (xml==null || xml.length()==0) return null;
-
-    Node<?> dataTree = null;
-    try {
-
-      Document doc = XmlUtil.readXmlToDocument(xml);
-      LOG.debug("xmlToCompositeNode Document is " + xml );
-      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()));
-          break;
-        }
-      }
-    } catch (SAXException e) {
-      LOG.error("Error during building data tree from XML", e);
-    } catch (IOException e) {
-      LOG.error("Error during building data tree from XML", e);
-    }
-
-    LOG.debug("xmlToCompositeNode " + dataTree.toString());
-    return (CompositeNode) dataTree;
-  }
-}
index 25773bb..fd1af2b 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 eac9731..7eaa8f0 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 ef3f528..2c26243 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 030d81a..522dd44 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 b1fa410..e8d2262 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 0e15633..d426662 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 c556279..c1d4240 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 3309b98..aaf089d 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 00ef980..fd73144 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 1f4eab0..94b7fe4 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 a3aa9d1..b560b8c 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 0bfd78a..289334f 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 132fdba..c57a258 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 cbfecb1..17766f1 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..92a7fba
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * 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();
+    logger.debug("Instance identifier derserialize: splitting the text {} with Slash to find path arguments", valueTrimmed);
+    final Iterator<String> xPathParts = SLASH_SPLITTER.split(valueTrimmed).iterator();
+
+    // must be at least "/pr:node"
+    if (!xPathParts.hasNext() || !xPathParts.next().isEmpty() || !xPathParts.hasNext()) {
+      logger.debug("Instance identifier derserialize: No path argument found for element.");
+      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;
+          }
+        }
+        logger.debug("Instance identifier derserialize: finding predicates of node {}", predicateValue);
+        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) {
+    logger.debug("Instance identifier derserialize: converting the predicate vstring to object {}", 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 {
+      logger.debug("Instance identifier derserialize: converting the predicate value {{}}to correct object type {{}}", value, type);
+      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..127aa07
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.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, 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) {
+      logger.debug("toSimpleNodeWithType: found codec, deserializing text {}", text);
+      value = codec.deserialize(text);
+    }
+
+    final TypeDefinition<?> baseType = XmlUtils.resolveBaseTypeFrom(schema.getType());
+    if (baseType instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifier) {
+      logger.debug("toSimpleNodeWithType: base type of node is instance identifier, deserializing element", xmlElement);
+      value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx);
+
+    } else if(baseType instanceof IdentityrefTypeDefinition){
+      logger.debug("toSimpleNodeWithType: base type of node is IdentityrefTypeDefinition, deserializing element", xmlElement);
+      value = InstanceIdentifierForXmlCodec.toIdentity(xmlElement.getTextContent(), xmlElement, schemaCtx);
+
+    }
+
+    if (value == null) {
+      logger.debug("toSimpleNodeWithType: no type found for element, returning just the text string value of element {}", xmlElement);
+      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) {
+      logger.debug("toSimpleNodeWithType: found codec, deserializing text {}", text);
+      value = codec.deserialize(text);
+    }
+
+    if (schema.getType() instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifier) {
+      logger.debug("toSimpleNodeWithType: base type of node is instance identifier, deserializing element", xmlElement);
+      value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx);
+    }
+
+    if (value == null) {
+      logger.debug("toSimpleNodeWithType: no type found for element, returning just the text string value of element {}", xmlElement);
+      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 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..e4576c4
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * 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");
+    LOG.debug("Writing Instance identifier with Random prefix");
+    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());
+    }
+    LOG.debug("Instance identifier with Random prefix is now {}", str);
+    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<?>) {
+      LOG.debug("writeElement : node is of type 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 {
+      LOG.debug("writeElement : node is of type CompositeNode");
+      // CompositeNode
+      for (Node<?> child : ((CompositeNode) data).getValue()) {
+        DataSchemaNode childSchema = null;
+        if (schema instanceof DataNodeContainer) {
+          childSchema = SchemaUtils.findFirstSchema(child.getNodeType(), ((DataNodeContainer) schema).getChildNodes()).orNull();
+          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) {
+      LOG.debug("Writing InstanceIdentifier object {}", value);
+      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));
+    }
+  }
+}
diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlUtils.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlUtils.java
new file mode 100644 (file)
index 0000000..e07401a
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * 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.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.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.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.w3c.dom.NodeList;
+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;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+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 String BLANK = "";
+  private static final Logger LOG = LoggerFactory.getLogger(XmlUtils.class);
+
+  /**
+   * Converts the composite node to xml using rpc input schema node
+   * @param cNode
+   * @param schemaContext
+   * @return xml String
+   */
+  public static String inputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){
+    LOG.debug("Converting input composite node to xml {}", cNode);
+    if (cNode == null) return BLANK;
+
+    if(schemaContext == null) return BLANK;
+
+    Document domTree = null;
+    try {
+      Set<RpcDefinition> rpcs =  schemaContext.getOperations();
+      for(RpcDefinition rpc : rpcs) {
+        if(rpc.getQName().equals(cNode.getNodeType())){
+          LOG.debug("Found the rpc definition from schema context matching with input composite node  {}", rpc.getQName());
+
+          CompositeNode inputContainer = cNode.getFirstCompositeByName(QName.create(cNode.getNodeType(), "input"));
+          domTree = XmlDocumentUtils.toDocument(inputContainer, rpc.getInput(), XmlDocumentUtils.defaultValueCodecProvider());
+
+          LOG.debug("input composite node to document conversion complete, document is   {}", domTree);
+          break;
+        }
+      }
+
+    } catch (UnsupportedDataTypeException e) {
+      LOG.error("Error during translation of CompositeNode to Document", e);
+    }
+    return domTransformer(domTree);
+  }
+
+  /**
+   * Converts the composite node to xml String using rpc output schema node
+   * @param cNode
+   * @param schemaContext
+   * @return xml string
+   */
+  public static String outputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){
+    LOG.debug("Converting output composite node to xml {}", cNode);
+    if (cNode == null) return BLANK;
+
+    if(schemaContext == null) return BLANK;
+
+    Document domTree = null;
+    try {
+      Set<RpcDefinition> rpcs =  schemaContext.getOperations();
+      for(RpcDefinition rpc : rpcs) {
+        if(rpc.getQName().equals(cNode.getNodeType())){
+          LOG.debug("Found the rpc definition from schema context matching with output composite node  {}", rpc.getQName());
+
+          CompositeNode outputContainer = cNode.getFirstCompositeByName(QName.create(cNode.getNodeType(), "output"));
+          domTree = XmlDocumentUtils.toDocument(outputContainer, rpc.getOutput(), XmlDocumentUtils.defaultValueCodecProvider());
+
+          LOG.debug("output composite node to document conversion complete, document is   {}", domTree);
+          break;
+        }
+      }
+
+    } catch (UnsupportedDataTypeException e) {
+      LOG.error("Error during translation of CompositeNode to Document", e);
+    }
+    return domTransformer(domTree);
+  }
+
+  private static String domTransformer(Document domTree) {
+    StringWriter writer = new StringWriter();
+    try {
+      TransformerFactory tf = TransformerFactory.newInstance();
+      Transformer transformer = tf.newTransformer();
+      transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+      transformer.transform(new DOMSource(domTree), new StreamResult(writer));
+    } catch (TransformerException e) {
+
+      LOG.error("Error during translation of Document to OutputStream", e);
+    }
+    LOG.debug("Document to string conversion complete, xml string is  {} ",  writer.toString());
+
+    return writer.toString();
+  }
+
+  public static CompositeNode xmlToCompositeNode(String xml){
+    if (xml==null || xml.length()==0) return null;
+
+    Node<?> dataTree;
+    try {
+      dataTree = XmlTreeBuilder.buildDataTree(new ByteArrayInputStream(xml.getBytes()));
+    } catch (XMLStreamException e) {
+      LOG.error("Error during building data tree from XML", e);
+      return null;
+    }
+    if (dataTree == null) {
+      LOG.error("data tree is null");
+      return null;
+    }
+    if (dataTree instanceof SimpleNode) {
+      LOG.error("RPC XML was resolved as SimpleNode");
+      return null;
+    }
+    return (CompositeNode) dataTree;
+  }
+
+  /**
+   * Converts the xml to composite node using rpc input schema node
+   * @param rpc
+   * @param xml
+   * @param schemaContext
+   * @return CompositeNode object based on the input, if any of the input parameter is null, a null object is returned
+   */
+  public static CompositeNode inputXmlToCompositeNode(QName rpc, String xml,  SchemaContext schemaContext){
+    LOG.debug("Converting input xml to composite node {}", xml);
+    if (xml==null || xml.length()==0) return null;
+
+    if(rpc == null) return null;
+
+    if(schemaContext == null) return null;
+
+    CompositeNode compositeNode = null;
+    try {
+
+      Document doc = XmlUtil.readXmlToDocument(xml);
+      Set<RpcDefinition> rpcs =  schemaContext.getOperations();
+      for(RpcDefinition rpcDef : rpcs) {
+        if(rpcDef.getQName().equals(rpc)){
+          LOG.debug("found the rpc definition from schema context matching rpc  {}", rpc);
+
+          if(rpcDef.getInput() == null) {
+            LOG.warn("found rpc definition's input is null");
+            return null;
+          }
+
+          QName input = rpcDef.getInput().getQName();
+          NodeList nodeList = doc.getElementsByTagNameNS(input.getNamespace().toString(), "input");
+          if(nodeList == null || nodeList.getLength() < 1) {
+            LOG.warn("xml does not have input entry. {}", xml);
+            return null;
+          }
+          Element xmlData = (Element)nodeList.item(0);
+
+          List<Node<?>> dataNodes = XmlDocumentUtils.toDomNodes(xmlData,
+              Optional.of(rpcDef.getInput().getChildNodes()), schemaContext);
+
+          LOG.debug("Converted xml input to list of nodes  {}", dataNodes);
+
+          final CompositeNodeBuilder<ImmutableCompositeNode> it = ImmutableCompositeNode.builder();
+          it.setQName(input);
+          it.add(ImmutableCompositeNode.create(input, dataNodes));
+          compositeNode = it.toInstance();
+          break;
+        }
+      }
+    } catch (SAXException e) {
+      LOG.error("Error during building data tree from XML", e);
+    } catch (IOException e) {
+      LOG.error("Error during building data tree from XML", e);
+    }
+
+    LOG.debug("Xml to composite node conversion complete {} ", compositeNode);
+    return compositeNode;
+  }
+
+  public static TypeDefinition<?> resolveBaseTypeFrom(final @Nonnull TypeDefinition<?> type) {
+    TypeDefinition<?> superType = type;
+    while (superType.getBaseType() != null) {
+      superType = superType.getBaseType();
+    }
+    return superType;
+  }
+
+  /**
+   * This code is picked from yangtools and modified to add type of instance identifier
+   * output of instance identifier something like below for a flow ref composite node of type instance identifier,
+   * which has path arguments with predicates, whose value is of type java.lang.short
+   * <flow-ref xmlns:bgkj="urn:opendaylight:flow:inventory" xmlns:jdlk="urn:opendaylight:inventory">
+   *   /jdlk:nodes/jdlk:node[jdlk:id='openflow:205558455098190@java.lang.String']
+   *   /bgkj:table[bgkj:id='3@java.lang.Short']
+   *   /bgkj:flow[bgkj:id='156@java.lang.String']
+   * </flow-ref>
+   *
+   */
+
+  public 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 9585e9f..6088dd0 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 595d833..392c1e6 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 a57402a..129a5a5 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..a408e1d
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * 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.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.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+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.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+
+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 = "<add-flow xmlns=\"urn:opendaylight:controller:rpc:test\"><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></add-flow>";
+
+  @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 testNullInputXmlToComposite() {
+    CompositeNode node = XmlUtils.inputXmlToCompositeNode(testRpc.getQName(), null, schema);
+    Assert.assertNull(node);
+  }
+
+  @Test
+  public void testNullRpcXmlToComposite() {
+    CompositeNode node = XmlUtils.inputXmlToCompositeNode(null, XML_CONTENT, schema);
+    Assert.assertNull(node);
+  }
+
+  @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);
+  }
+
+  @Test
+  public void testInputCompositeNodeToXML() {
+    CompositeNode input = XmlUtils.inputXmlToCompositeNode(testRpc.getQName(), XML_CONTENT, schema);
+    List<Node<?>> childNodes = new ArrayList();
+    childNodes.add(input);
+    QName rpcQName = schema.getOperations().iterator().next().getQName();
+    CompositeNode node = new ImmutableCompositeNode(rpcQName, input.getValue(), ModifyAction.REPLACE);
+    String xml = XmlUtils.inputCompositeNodeToXml(node, schema);
+    Assert.assertNotNull(xml);
+    Assert.assertTrue(xml.contains("3@java.lang.Short"));
+  }
+
+  @Test
+  public void testNullCompositeNodeToXml(){
+    String xml = XmlUtils.inputCompositeNodeToXml(null, schema);
+    Assert.assertTrue(xml.isEmpty());
+  }
+
+  @Test
+  public void testNullSchemaCompositeNodeToXml(){
+    String xml = XmlUtils.inputCompositeNodeToXml(new ImmutableCompositeNode(QName.create("ns", "2013-12-09", "child1"), new ArrayList<Node<?>>(), ModifyAction.REPLACE), null);
+    Assert.assertTrue(xml.isEmpty());
+  }
+
+
+}
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
index a2d2dac..cbc1f0c 100644 (file)
@@ -91,7 +91,6 @@
               javax.ws.rs.core,
               javax.xml.bind,
               javax.xml.bind.annotation,
-              org.objectweb.asm,
               org.opendaylight.controller.sal.utils,
               org.opendaylight.controller.sal.core,
               org.opendaylight.controller.sal.authorization,

©2013 OpenDaylight, A Linux Foundation Collaborative Project. All Rights Reserved.
OpenDaylight is a registered trademark of The OpenDaylight Project, Inc.
Linux Foundation and OpenDaylight are registered trademarks of the Linux Foundation.
Linux is a registered trademark of Linus Torvalds.