From: Harman Singh Date: Thu, 31 Jul 2014 01:42:26 +0000 (-0700) Subject: Adding actor supervisor, more logs and tests X-Git-Tag: release/helium~387 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=0120961d542fb1e055d4841a06c54e480ecdac3d Adding actor supervisor, more logs and tests 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 --- diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/config/yang/config/remote_rpc_connector/RemoteRPCBrokerModule.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/config/yang/config/remote_rpc_connector/RemoteRPCBrokerModule.java index 8315bbeeb3..9824889b80 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/config/yang/config/remote_rpc_connector/RemoteRPCBrokerModule.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/config/yang/config/remote_rpc_connector/RemoteRPCBrokerModule.java @@ -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); } } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/config/yang/config/remote_rpc_connector/RemoteRPCBrokerModuleFactory.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/config/yang/config/remote_rpc_connector/RemoteRPCBrokerModuleFactory.java index e1ba46ae15..330845b14f 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/config/yang/config/remote_rpc_connector/RemoteRPCBrokerModuleFactory.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/config/yang/config/remote_rpc_connector/RemoteRPCBrokerModuleFactory.java @@ -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; - } - } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementation.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementation.java index 75db0b7299..d384144f4f 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementation.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementation.java @@ -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; diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProvider.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProvider.java index 1bb7ea4514..3df572d7c2 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProvider.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProvider.java @@ -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 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 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); } } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProviderFactory.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProviderFactory.java index 61dc8183df..4c40ca1777 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProviderFactory.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProviderFactory.java @@ -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; } } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RoutedRpcListener.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RoutedRpcListener.java index dce7e20219..a6eeac0270 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RoutedRpcListener.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RoutedRpcListener.java @@ -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; diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcBroker.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcBroker.java index 412e549fac..26e8e960e3 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcBroker.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcBroker.java @@ -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) { diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcListener.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcListener.java index ae760fadc4..f614990669 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcListener.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcListener.java @@ -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 index 0000000000..4925a17c13 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcManager.java @@ -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() { + @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 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() { + @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/ActorUtil.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/ActorUtil.java similarity index 97% rename from opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/ActorUtil.java rename to opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/ActorUtil.java index 13324f996e..8f60eabf5f 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/ActorUtil.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/ActorUtil.java @@ -6,7 +6,7 @@ * and is available at http://www.eclipse.org/legal/epl-v10.html * */ -package org.opendaylight.controller.remote.rpc; +package org.opendaylight.controller.remote.rpc.utils; import akka.actor.ActorRef; import akka.actor.ActorSelection; 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 index cacacc628e..92a7fbae79 100644 --- 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 @@ -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 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 xPathParts = SLASH_SPLITTER.split(valueTrimmed).iterator(); - - // must be at least "/pr:node" - if (!xPathParts.hasNext() || !xPathParts.next().isEmpty() || !xPathParts.hasNext()) { - return null; - } - - List result = new ArrayList<>(); - while (xPathParts.hasNext()) { - String xPathPartTrimmed = xPathParts.next().trim(); + List 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 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 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 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 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 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 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 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 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; + } } 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 index 9e5d8f0cd4..127aa0732b 100644 --- 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 @@ -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> 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> 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 = 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> 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 = getModifyOperationFromAttributes(xmlElement); - return new SimpleNodeTOImpl<>(schema.getQName(), null, value, modifyAction.orNull()); + Optional 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> 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> values = toDomNodes(xmlElement, Optional.fromNullable(schema.getChildNodes()),schemaCtx); - Optional 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 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 = 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> values = toDomNodes(xmlElement, Optional.fromNullable(schema.getChildNodes()),schemaCtx); + Optional 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 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 findFirstSchema(final QName qname, final Collection dataSchemaNode) { - if (dataSchemaNode != null && !dataSchemaNode.isEmpty() && qname != null) { - for (DataSchemaNode dsn : dataSchemaNode) { - if (qname.isEqualWithoutRevision(dsn.getQName())) { - return Optional. of(dsn); - } else if (dsn instanceof ChoiceNode) { - for (ChoiceCaseNode choiceCase : ((ChoiceNode) dsn).getCases()) { - Optional 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 findFirstSchema(final QName qname, final Collection dataSchemaNode) { + if (dataSchemaNode != null && !dataSchemaNode.isEmpty() && qname != null) { + for (DataSchemaNode dsn : dataSchemaNode) { + if (qname.isEqualWithoutRevision(dsn.getQName())) { + return Optional. of(dsn); + } else if (dsn instanceof ChoiceNode) { + for (ChoiceCaseNode choiceCase : ((ChoiceNode) dsn).getCases()) { + Optional 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 List forEachChild(final NodeList nodes, final SchemaContext schemaContext, final Function> forBody) { - final int l = nodes.getLength(); - if (l == 0) { - return ImmutableList.of(); - } + private static final List forEachChild(final NodeList nodes, final SchemaContext schemaContext, final Function> forBody) { + final int l = nodes.getLength(); + if (l == 0) { + return ImmutableList.of(); + } - final List list = new ArrayList<>(l); - for (int i = 0; i < l; i++) { - org.w3c.dom.Node child = nodes.item(i); - if (child instanceof Element) { - Optional result = forBody.apply(new ElementWithSchemaContext((Element) child,schemaContext)); - if (result.isPresent()) { - list.add(result.get()); - } - } + final List list = new ArrayList<>(l); + for (int i = 0; i < l; i++) { + org.w3c.dom.Node child = nodes.item(i); + if (child instanceof Element) { + Optional 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; + } } 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 index e74a84a17e..e4576c445b 100644 --- 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 @@ -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 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 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 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 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 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 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)); } + } } 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 index 45871f5402..e07401a3e0 100644 --- 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 @@ -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> codecFor(final TypeDefinition baseType) { - return TypeDefinitionAwareCodec.from(baseType); - } - }; - - private XmlUtils() { + public static final XmlCodecProvider DEFAULT_XML_CODEC_PROVIDER = new XmlCodecProvider() { + @Override + public TypeDefinitionAwareCodec> 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 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 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 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> dataNodes = XmlDocumentUtils.toDomNodes(xmlData, Optional.of(rpcDef.getInput().getChildNodes()), schemaContext); + + LOG.debug("Converted xml input to list of nodes {}", dataNodes); + final CompositeNodeBuilder 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 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 + * + * /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'] + * + * + */ + + 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 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(); + } } 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 index 92340b63eb..a408e1d55a 100644 --- 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 @@ -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 = "" + + public static final String XML_CONTENT = "" + "flowid" + "/ltha:node/ltha:node1[ltha:id='3@java.lang.Short']" + - ""; + ""; @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> 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>(), ModifyAction.REPLACE), null); + Assert.assertTrue(xml.isEmpty()); + } + }