Adding actor supervisor, more logs and tests 15/9515/2
authorHarman Singh <harmasin@cisco.com>
Thu, 31 Jul 2014 01:42:26 +0000 (18:42 -0700)
committerHarman Singh <harmasin@cisco.com>
Thu, 31 Jul 2014 23:59:52 +0000 (16:59 -0700)
Actor Supervisor will start rpcRegistry and rpcBroker actors and will resume them in case of exception
More logs have been added along with some unit test cases of xmlutil

Change-Id: I30101bd3b8ac6821b58b3e1a2421f7f1af26bcd1
Signed-off-by: Harman Singh <harmasin@cisco.com>
15 files changed:
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/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/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
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlDocumentUtils.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlStreamUtils.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlUtils.java
opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/utils/XmlUtilsTest.java

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 75db0b7..d384144 100644 (file)
@@ -7,6 +7,7 @@ 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.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;
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 dce7e20..a6eeac0 100644 (file)
@@ -15,6 +15,7 @@ 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.YangInstanceIdentifier;
index 412e549..26e8e96 100644 (file)
@@ -20,6 +20,7 @@ import org.opendaylight.controller.remote.rpc.messages.GetRpcReply;
 import org.opendaylight.controller.remote.rpc.messages.InvokeRoutedRpc;
 import org.opendaylight.controller.remote.rpc.messages.InvokeRpc;
 import org.opendaylight.controller.remote.rpc.messages.RpcResponse;
+import org.opendaylight.controller.remote.rpc.utils.ActorUtil;
 import org.opendaylight.controller.remote.rpc.utils.XmlUtils;
 import org.opendaylight.controller.sal.core.api.Broker;
 import org.opendaylight.yangtools.yang.common.RpcResult;
@@ -39,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;
@@ -73,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) {
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();
+          }
+        }
+    );
+  }
+}
index cacacc6..92a7fba 100644 (file)
@@ -30,185 +30,190 @@ 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");
+  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;
     }
 
-    public static YangInstanceIdentifier deserialize(final Element element, final SchemaContext schemaContext) {
-        Preconditions.checkNotNull(element, "Value of element for deserialization can't be null");
-        Preconditions.checkNotNull(schemaContext,
-                "Schema context for deserialization of instance identifier type can't be null");
-
-        final String valueTrimmed = element.getTextContent().trim();
-        final Iterator<String> xPathParts = SLASH_SPLITTER.split(valueTrimmed).iterator();
-
-        // must be at least "/pr:node"
-        if (!xPathParts.hasNext() || !xPathParts.next().isEmpty() || !xPathParts.hasNext()) {
-            return null;
-        }
-
-        List<PathArgument> result = new ArrayList<>();
-        while (xPathParts.hasNext()) {
-            String xPathPartTrimmed = xPathParts.next().trim();
+    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);
+      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");
+  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);
+    final RandomPrefix prefixes = new RandomPrefix();
+    final String str = XmlUtils.encodeIdentifier(prefixes, id);
 
-        for (Entry<URI, String> e: prefixes.getPrefixes()) {
-            element.setAttribute("xmlns:" + e.getValue(), e.getKey().toString());
-        }
-        element.setTextContent(str);
-        return element;
-    }
-
-    private static String getIdAndPrefixAsStr(final String pathPart) {
-        int predicateStartIndex = pathPart.indexOf('[');
-        return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex);
-    }
-
-    private static PathArgument toPathArgument(final String xPathArgument, final Element element, final SchemaContext schemaContext) {
-        final QName mainQName = toIdentity(xPathArgument, element, schemaContext);
-
-        // predicates
-        final Matcher matcher = PREDICATE_PATTERN.matcher(xPathArgument);
-        final Map<QName, Object> predicates = new HashMap<>();
-        QName currentQName = mainQName;
-
-        while (matcher.find()) {
-            final String predicateStr = matcher.group(1).trim();
-            final int indexOfEqualityMark = predicateStr.indexOf('=');
-            if (indexOfEqualityMark != -1) {
-                final Object predicateValue = toPredicateValue(predicateStr.substring(indexOfEqualityMark + 1));
-                if (predicateValue == null) {
-                    return null;
-                }
-
-                if (predicateStr.charAt(0) != '.') {
-                    // target is not a leaf-list
-                    currentQName = toIdentity(predicateStr.substring(0, indexOfEqualityMark), element, schemaContext);
-                    if (currentQName == null) {
-                        return null;
-                    }
-                }
-                predicates.put(currentQName, predicateValue);
-            }
+    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 (predicates.isEmpty()) {
-            return new YangInstanceIdentifier.NodeIdentifier(mainQName);
-        } else {
-            return new YangInstanceIdentifier.NodeIdentifierWithPredicates(mainQName, predicates);
+        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;
-        }
+  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();
 
-        final String prefix = it.next().trim();
-        if (prefix.isEmpty()) {
-            return null;
-        }
+    // Empty string
+    if (!it.hasNext()) {
+      return null;
+    }
 
-        // it is not "prefix:value"
-        if (!it.hasNext()) {
-            return null;
-        }
+    final String prefix = it.next().trim();
+    if (prefix.isEmpty()) {
+      return null;
+    }
 
-        final String identifier = it.next().trim();
-        if (identifier.isEmpty()) {
-            return null;
-        }
+    // it is not "prefix:value"
+    if (!it.hasNext()) {
+      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);
-        }
+    final String identifier = it.next().trim();
+    if (identifier.isEmpty()) {
+      return null;
+    }
 
-        Module module = schemaContext.findModuleByNamespaceAndRevision(namespace, null);
-        return QName.create(module.getQNameModule(), identifier);
+    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);
     }
 
-    private static String trimIfEndIs(final String str, final char end) {
-        final int l = str.length() - 1;
-        if (str.charAt(l) != end) {
-            return null;
-        }
+    Module module = schemaContext.findModuleByNamespaceAndRevision(namespace, null);
+    return QName.create(module.getQNameModule(), identifier);
+  }
 
-        return str.substring(1, l);
+  private static String trimIfEndIs(final String str, final char end) {
+    final int l = str.length() - 1;
+    if (str.charAt(l) != end) {
+      return null;
     }
 
-    private static Object toPredicateValue(final String predicatedValue) {
-        final String predicatedValueTrimmed = predicatedValue.trim();
-        if (predicatedValue.isEmpty()) {
-            return null;
-        }
-        String updatedValue = null;
-        switch (predicatedValueTrimmed.charAt(0)) {
-        case '"':
-          updatedValue =  trimIfEndIs(predicatedValueTrimmed, '"');
-          break;
-        case '\'':
-          updatedValue =  trimIfEndIs(predicatedValueTrimmed, '\'');
-          break;
-        default:
-          updatedValue =  predicatedValueTrimmed;
-        }
-        Iterator<String> it = AT_SPLITTER.split(updatedValue).iterator();
-        // Empty string
-        if (!it.hasNext()) {
-          return null;
-        }
+    return str.substring(1, l);
+  }
 
-        final String value = it.next().trim();
-        if (value.isEmpty()) {
-          return null;
-        }
+  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;
+    }
 
-        if (!it.hasNext()) {
-          return value;
-        }
+    final String value = it.next().trim();
+    if (value.isEmpty()) {
+      return null;
+    }
 
-      final String type = it.next().trim();
-      if (type.isEmpty()) {
-        return value;
-      }
-      Object predicateObject = null;
-      try {
-        predicateObject = Class.forName(type).getConstructor(String.class).newInstance(value);
-      } catch (Exception e) {
-        logger.error("Could not convert to valid type of value", e);
-      }
-      return predicateObject;
+    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;
+  }
 }
index 9e5d8f0..127aa07 100644 (file)
@@ -56,177 +56,189 @@ import java.util.List;
 import static com.google.common.base.Preconditions.checkState;
 
 public class XmlDocumentUtils {
-    private static class ElementWithSchemaContext {
-        Element element;
-        SchemaContext schemaContext;
+  private static class ElementWithSchemaContext {
+    Element element;
+    SchemaContext schemaContext;
 
-        ElementWithSchemaContext(final Element element,final SchemaContext schemaContext) {
-            this.schemaContext = schemaContext;
-            this.element = element;
-        }
+    ElementWithSchemaContext(final Element element,final SchemaContext schemaContext) {
+      this.schemaContext = schemaContext;
+      this.element = element;
+    }
 
-        Element getElement() {
-            return element;
-        }
+    Element getElement() {
+      return element;
+    }
 
-        SchemaContext getSchemaContext() {
-            return schemaContext;
-        }
+    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.");
-        }
+  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;
-        }
+    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 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);
-    }
+  public static QName qNameFromElement(final Element xmlElement) {
+    String namespace = xmlElement.getNamespaceURI();
+    String localName = xmlElement.getLocalName();
+    return QName.create(namespace != null ? URI.create(namespace) : null, null, localName);
+  }
 
-    private static Node<?> toNodeWithSchema(final Element xmlElement, final DataSchemaNode schema, final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) {
-        checkQName(xmlElement, schema.getQName());
-        if (schema instanceof DataNodeContainer) {
-            return toCompositeNodeWithSchema(xmlElement, schema.getQName(), (DataNodeContainer) schema, codecProvider,schemaCtx);
-        } else if (schema instanceof LeafSchemaNode) {
-            return toSimpleNodeWithType(xmlElement, (LeafSchemaNode) schema, codecProvider,schemaCtx);
-        } else if (schema instanceof LeafListSchemaNode) {
-            return toSimpleNodeWithType(xmlElement, (LeafListSchemaNode) schema, codecProvider,schemaCtx);
-        }
-        return null;
+  private static Node<?> 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) {
-            value = codec.deserialize(text);
-        }
-        final TypeDefinition<?> baseType = XmlUtils.resolveBaseTypeFrom(schema.getType());
-        if (baseType instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifier) {
-            value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx);
-        } else if(baseType instanceof IdentityrefTypeDefinition){
-            value = InstanceIdentifierForXmlCodec.toIdentity(xmlElement.getTextContent(), xmlElement, schemaCtx);
-        }
+  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);
+    }
 
-        if (value == null) {
-            value = xmlElement.getTextContent();
-        }
+    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);
 
-        Optional<ModifyAction> modifyAction = getModifyOperationFromAttributes(xmlElement);
-        return new SimpleNodeTOImpl<>(schema.getQName(), null, value, modifyAction.orNull());
     }
 
-    private static Node<?> toSimpleNodeWithType(final Element xmlElement, final LeafListSchemaNode schema,
-            final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) {
-        TypeDefinitionAwareCodec<? extends Object, ? extends TypeDefinition<?>> codec = codecProvider.codecFor(schema.getType());
-        String text = xmlElement.getTextContent();
-        Object value = null;
-        if (codec != null) {
-            value = codec.deserialize(text);
-        }
-        if (schema.getType() instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifier) {
-            value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx);
-        }
-        if (value == null) {
-            value = xmlElement.getTextContent();
-        }
+    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());
+    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);
     }
 
-    private static Node<?> toCompositeNodeWithSchema(final Element xmlElement, final QName qName, final DataNodeContainer schema,
-            final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) {
-        List<Node<?>> values = toDomNodes(xmlElement, Optional.fromNullable(schema.getChildNodes()),schemaCtx);
-        Optional<ModifyAction> modifyAction = getModifyOperationFromAttributes(xmlElement);
-        return ImmutableCompositeNode.create(qName, values, modifyAction.orNull());
+    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);
     }
 
-    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();
-        }
+    if (value == null) {
+      logger.debug("toSimpleNodeWithType: no type found for element, returning just the text string value of element {}", xmlElement);
+      value = xmlElement.getTextContent();
+    }
 
-        ModifyAction action = ModifyAction.fromXmlValue(attributeNodeNS.getValue());
-        Preconditions.checkArgument(action.isOnElementPermitted(), "Unexpected operation %s on %s", action, xmlElement);
+    Optional<ModifyAction> modifyAction = getModifyOperationFromAttributes(xmlElement);
+    return new SimpleNodeTOImpl<>(schema.getQName(), null, value, modifyAction.orNull());
+  }
 
-        return Optional.of(action);
-    }
+  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 void checkQName(final Element xmlElement, final QName qName) {
-        checkState(Objects.equal(xmlElement.getNamespaceURI(), qName.getNamespace().toString()));
-        checkState(qName.getLocalName().equals(xmlElement.getLocalName()));
+  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();
     }
 
-    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;
-                        }
-                    }
-                }
+    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();
+      }
     }
+    return Optional.absent();
+  }
 
   private static Node<?> toDomNode(Element element) {
     QName qname = qNameFromElement(element);
@@ -274,26 +286,26 @@ public class XmlDocumentUtils {
 
   }
 
-    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();
-        }
+  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());
-                }
-            }
+    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);
+      }
     }
+    return ImmutableList.copyOf(list);
+  }
 
-    public static final XmlCodecProvider defaultValueCodecProvider() {
-        return XmlUtils.DEFAULT_XML_CODEC_PROVIDER;
-    }
+  public static final XmlCodecProvider defaultValueCodecProvider() {
+    return XmlUtils.DEFAULT_XML_CODEC_PROVIDER;
+  }
 }
index e74a84a..e4576c4 100644 (file)
@@ -42,206 +42,208 @@ import java.util.Map.Entry;
  */
 @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);
+  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;
     }
 
-    /**
-     * 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);
+    if (data instanceof CompositeNode) {
+      return ((CompositeNode) data).getValue().isEmpty();
     }
-
-    /**
-     * 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;
+    if (data instanceof SimpleNode) {
+      return data.getValue() == null;
     }
 
-    /**
-     * Write an InstanceIdentifier into the output stream. Calling corresponding {@link javax.xml.stream.XMLStreamWriter#writeStartElement(String)}
-     * and {@link javax.xml.stream.XMLStreamWriter#writeEndElement()} is the responsibility of the caller.
-     *
-     * @param writer XML Stream writer
-     * @param id InstanceIdentifier
-     * @throws javax.xml.stream.XMLStreamException
-     */
-    public static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull YangInstanceIdentifier id) throws XMLStreamException {
-        Preconditions.checkNotNull(writer, "Writer may not be null");
-        Preconditions.checkNotNull(id, "Variable should contain instance of instance identifier and can't be null");
-
-        final RandomPrefix prefixes = new RandomPrefix();
-        final String str = XmlUtils.encodeIdentifier(prefixes, id);
-
-        for (Entry<URI, String> e: prefixes.getPrefixes()) {
-            writer.writeNamespace(e.getValue(), e.getKey().toString());
-        }
-        writer.writeCharacters(str);
+    // 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());
     }
-
-    /**
-     * 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();
+    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());
+      }
+    }
 
-    /**
-     * 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));
         }
-
-        if (data instanceof SimpleNode<?>) {
-            // Simple node
-            if (schema instanceof LeafListSchemaNode) {
-                writeValue(writer, ((LeafListSchemaNode) schema).getType(), data.getValue());
-            } else if (schema instanceof LeafSchemaNode) {
-                writeValue(writer, ((LeafSchemaNode) schema).getType(), data.getValue());
-            } else {
-                Object value = data.getValue();
-                if (value != null) {
-                    writer.writeCharacters(String.valueOf(value));
-                }
-            }
-        } else {
-            // CompositeNode
-            for (Node<?> child : ((CompositeNode) data).getValue()) {
-                DataSchemaNode childSchema = null;
-                if (schema instanceof DataNodeContainer) {
-                    childSchema = SchemaUtils.findFirstSchema(child.getNodeType(), ((DataNodeContainer) schema).getChildNodes()).orNull();
-                    if (LOG.isDebugEnabled()) {
-                        if (childSchema == null) {
-                            LOG.debug("Probably the data node \"{}\" does not conform to schema", child == null ? "" : child.getNodeType().getLocalName());
-                        }
-                    }
-                }
-
-                writeElement(writer, child, childSchema);
-            }
+      }
+    } 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());
+          }
         }
 
-        writer.writeEndElement();
+        writeElement(writer, child, childSchema);
+      }
     }
 
-    /**
-     * 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);
-        }
+    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;
     }
 
-    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));
+    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 InstanceIdentifierTypeDefinition type, final @Nonnull Object value) throws XMLStreamException {
-        if (value instanceof YangInstanceIdentifier) {
-            write(writer, (YangInstanceIdentifier)value);
-        } else {
-            LOG.debug("Value of {}:{} is not an InstanceIdentifier but {}", type.getQName().getNamespace(), type.getQName().getLocalName(), value.getClass());
-            writer.writeCharacters(String.valueOf(value));
-        }
+  }
+
+  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));
     }
+  }
 }
index 45871f5..e07401a 100644 (file)
@@ -29,6 +29,7 @@ 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;
@@ -53,27 +54,42 @@ import java.util.Set;
  */
 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() {
+  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){
-    if (cNode == null) return new String();
+    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;
         }
       }
@@ -84,15 +100,29 @@ public class XmlUtils {
     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){
-    if (cNode == null) return new String();
+    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())){
-          domTree = XmlDocumentUtils.toDocument(cNode, rpc.getOutput(), XmlDocumentUtils.defaultValueCodecProvider());
+          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;
         }
       }
@@ -114,7 +144,7 @@ public class XmlUtils {
 
       LOG.error("Error during translation of Document to OutputStream", e);
     }
-    LOG.debug("compositeNodeToXml " + writer.toString());
+    LOG.debug("Document to string conversion complete, xml string is  {} ",  writer.toString());
 
     return writer.toString();
   }
@@ -140,21 +170,48 @@ public class XmlUtils {
     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);
-      LOG.debug("xmlToCompositeNode Document is " + 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();
-          Element xmlData = (Element) doc.getElementsByTagNameNS(input.getNamespace().toString(), "input").item(0);
+          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));
@@ -168,45 +225,57 @@ public class XmlUtils {
       LOG.error("Error during building data tree from XML", e);
     }
 
-    LOG.debug("xmlToCompositeNode " + compositeNode.toString());
+    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;
+  public static TypeDefinition<?> resolveBaseTypeFrom(final @Nonnull TypeDefinition<?> type) {
+    TypeDefinition<?> superType = type;
+    while (superType.getBaseType() != null) {
+      superType = superType.getBaseType();
     }
+    return superType;
+  }
 
-    static String encodeIdentifier(final RandomPrefix prefixes, final YangInstanceIdentifier id) {
-        StringBuilder textContent = new StringBuilder();
-        for (PathArgument pathArgument : id.getPathArguments()) {
-            textContent.append('/');
-            textContent.append(prefixes.encodeQName(pathArgument.getNodeType()));
-            if (pathArgument instanceof NodeIdentifierWithPredicates) {
-                Map<QName, Object> predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues();
-
-                for (QName keyValue : predicates.keySet()) {
-                  Object value = predicates.get(keyValue);
-                  String type = value.getClass().getName();
-                  String predicateValue = String.valueOf(value);
-                    textContent.append('[');
-                    textContent.append(prefixes.encodeQName(keyValue));
-                    textContent.append("='");
-                    textContent.append(predicateValue);
-                    textContent.append("@");
-                    textContent.append(type);
-                    textContent.append("']");
-                }
-            } else if (pathArgument instanceof NodeWithValue) {
-                textContent.append("[.='");
-                textContent.append(((NodeWithValue) pathArgument).getValue());
-                textContent.append("']");
-            }
-        }
+  /**
+   * 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();
 
-        return textContent.toString();
+        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 92340b6..a408e1d 100644 (file)
@@ -15,9 +15,13 @@ 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;
@@ -27,7 +31,9 @@ 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 {
@@ -46,10 +52,10 @@ public class XmlUtilsTest {
   private SchemaContext schema;
   private RpcDefinition testRpc;
 
-  public static final String XML_CONTENT = "<input xmlns=\"urn:opendaylight:controller:rpc:test\">" +
+  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>";
+      "</input></add-flow>";
 
   @Before
   public void setUp() throws Exception {
@@ -64,6 +70,18 @@ public class XmlUtilsTest {
     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);
@@ -87,5 +105,29 @@ public class XmlUtilsTest {
     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());
+  }
+
 
 }