From: Ed Warnicke Date: Fri, 1 Aug 2014 01:26:27 +0000 (+0000) Subject: Merge "Add replication capability to Shard" X-Git-Tag: release/helium~385 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=07ba9a998f0b3c3045ed8e31afda5c96de141b3b;hp=df13d272f2f74892f2decd54ece5d8286fb1c995 Merge "Add replication capability to Shard" --- diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.xml b/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.xml index 03da7f0ccd..f25b7d91bd 100644 --- a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.xml +++ b/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.xml @@ -72,17 +72,7 @@ --> - - + prefix:inmemory-operational-datastore-provider operational-store-service @@ -157,6 +147,17 @@ + + diff --git a/opendaylight/md-sal/pom.xml b/opendaylight/md-sal/pom.xml index 14d4f24d1c..3f02765aff 100644 --- a/opendaylight/md-sal/pom.xml +++ b/opendaylight/md-sal/pom.xml @@ -71,6 +71,9 @@ sal-test-model + + + sal-remoterpc-connector diff --git a/opendaylight/md-sal/sal-remoterpc-connector/pom.xml b/opendaylight/md-sal/sal-remoterpc-connector/pom.xml index 6ee301d827..ce3bfe9a4c 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/pom.xml +++ b/opendaylight/md-sal/sal-remoterpc-connector/pom.xml @@ -55,22 +55,37 @@ org.opendaylight.controller netconf-util - + + org.opendaylight.controller + sal-core-spi + + + org.opendaylight.controller + sal-common-impl + org.opendaylight.yangtools yang-data-api + + + org.opendaylight.yangtools + yang-model-api + + org.opendaylight.yangtools yang-data-impl + org.opendaylight.yangtools yang-common + @@ -107,6 +122,17 @@ ${slf4j.version} test + + org.opendaylight.yangtools + yang-parser-impl + test + + + com.google.collections + google-collections + 1.0 + test + 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 43aa5b7e85..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,21 +7,19 @@ import org.opendaylight.controller.remote.rpc.messages.ErrorResponse; import org.opendaylight.controller.remote.rpc.messages.InvokeRoutedRpc; import org.opendaylight.controller.remote.rpc.messages.InvokeRpc; import org.opendaylight.controller.remote.rpc.messages.RpcResponse; -import org.opendaylight.controller.sal.common.util.RpcErrors; -import org.opendaylight.controller.sal.common.util.Rpcs; +import org.opendaylight.controller.remote.rpc.utils.ActorUtil; +import org.opendaylight.controller.remote.rpc.utils.XmlUtils; import org.opendaylight.controller.sal.core.api.RoutedRpcDefaultImplementation; import org.opendaylight.controller.sal.core.api.RpcImplementation; import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.Set; @@ -37,7 +35,7 @@ public class RemoteRpcImplementation implements RpcImplementation, } @Override - public ListenableFuture> invokeRpc(QName rpc, InstanceIdentifier identifier, CompositeNode input) { + public ListenableFuture> invokeRpc(QName rpc, YangInstanceIdentifier identifier, CompositeNode input) { InvokeRoutedRpc rpcMsg = new InvokeRoutedRpc(rpc, identifier, input); return executeMsg(rpcMsg); @@ -56,22 +54,33 @@ public class RemoteRpcImplementation implements RpcImplementation, } private ListenableFuture> executeMsg(Object rpcMsg) { - CompositeNode result = null; - Collection errors = errors = new ArrayList<>(); + ListenableFuture> listenableFuture = null; + try { Object response = ActorUtil.executeLocalOperation(rpcBroker, rpcMsg, ActorUtil.ASK_DURATION, ActorUtil.AWAIT_DURATION); if(response instanceof RpcResponse) { + RpcResponse rpcResponse = (RpcResponse) response; - result = XmlUtils.xmlToCompositeNode(rpcResponse.getResultCompositeNode()); + CompositeNode result = XmlUtils.xmlToCompositeNode(rpcResponse.getResultCompositeNode()); + listenableFuture = Futures.immediateFuture(RpcResultBuilder.success(result).build()); + } else if(response instanceof ErrorResponse) { + ErrorResponse errorResponse = (ErrorResponse) response; Exception e = errorResponse.getException(); - errors.add(RpcErrors.getRpcError(null, null, null, null, e.getMessage(), null, e.getCause())); + final RpcResultBuilder failed = RpcResultBuilder.failed(); + failed.withError(null, null, e.getMessage(), null, null, e.getCause()); + listenableFuture = Futures.immediateFuture(failed.build()); + } } catch (Exception e) { LOG.error("Error occurred while invoking RPC actor {}", e.toString()); - errors.add(RpcErrors.getRpcError(null, null, null, null, e.getMessage(), null, e.getCause())); + + final RpcResultBuilder failed = RpcResultBuilder.failed(); + failed.withError(null, null, e.getMessage(), null, null, e.getCause()); + listenableFuture = Futures.immediateFuture(failed.build()); } - return Futures.immediateFuture(Rpcs.getRpcResult(true, result, errors)); + + return listenableFuture; } } 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/RouteIdentifierImpl.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RouteIdentifierImpl.java index ea72238fbc..85d21381b6 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RouteIdentifierImpl.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RouteIdentifierImpl.java @@ -7,20 +7,23 @@ */ package org.opendaylight.controller.remote.rpc; +import com.google.common.base.Preconditions; import org.opendaylight.controller.sal.connector.api.RpcRouter; import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; + import java.io.Serializable; -public class RouteIdentifierImpl implements RpcRouter.RouteIdentifier,Serializable { +public class RouteIdentifierImpl implements RpcRouter.RouteIdentifier,Serializable { private static final long serialVersionUID = 1L; - private QName context; - private QName type; - private InstanceIdentifier route; + private final QName context; + private final QName type; + private final YangInstanceIdentifier route; - public RouteIdentifierImpl(QName context, QName type, InstanceIdentifier route) { + public RouteIdentifierImpl(final QName context, final QName type, final YangInstanceIdentifier route) { + Preconditions.checkNotNull(type, "Rpc type should not be null"); this.context = context; this.type = type; this.route = route; @@ -37,7 +40,7 @@ public class RouteIdentifierImpl implements RpcRouter.RouteIdentifier{ +public class RoutedRpcListener implements RouteChangeListener{ private static final Logger LOG = LoggerFactory.getLogger(RoutedRpcListener.class); private final ActorRef rpcRegistry; private final String actorPath; public RoutedRpcListener(ActorRef rpcRegistry, String actorPath) { + Preconditions.checkNotNull(rpcRegistry, "rpc registry actor should not be null"); + Preconditions.checkNotNull(actorPath, "actor path of rpc broker on current node should not be null"); + this.rpcRegistry = rpcRegistry; this.actorPath = actorPath; } @Override - public void onRouteChange(RouteChange routeChange) { - Map> announcements = routeChange.getAnnouncements(); + public void onRouteChange(RouteChange routeChange) { + Map> announcements = routeChange.getAnnouncements(); announce(getRouteIdentifiers(announcements)); - Map> removals = routeChange.getRemovals(); + Map> removals = routeChange.getRemovals(); remove(getRouteIdentifiers(removals)); } @@ -78,12 +83,12 @@ public class RoutedRpcListener implements RouteChangeListener> getRouteIdentifiers(Map> changes) { + private Set> getRouteIdentifiers(Map> changes) { RouteIdentifierImpl routeId = null; Set> routeIdSet = new HashSet<>(); for (RpcRoutingContext context : changes.keySet()){ - for (InstanceIdentifier instanceId : changes.get(context)){ + for (YangInstanceIdentifier instanceId : changes.get(context)){ routeId = new RouteIdentifierImpl(null, context.getRpc(), instanceId); routeIdSet.add(routeId); } 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 3354fc3da2..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,8 @@ import org.opendaylight.controller.remote.rpc.messages.GetRpcReply; import org.opendaylight.controller.remote.rpc.messages.InvokeRoutedRpc; import org.opendaylight.controller.remote.rpc.messages.InvokeRpc; import org.opendaylight.controller.remote.rpc.messages.RpcResponse; +import org.opendaylight.controller.remote.rpc.utils.ActorUtil; +import org.opendaylight.controller.remote.rpc.utils.XmlUtils; import org.opendaylight.controller.sal.core.api.Broker; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.CompositeNode; @@ -38,7 +40,7 @@ public class RpcBroker extends AbstractUntypedActor { private static final Logger LOG = LoggerFactory.getLogger(RpcBroker.class); private final Broker.ProviderSession brokerSession; private final ActorRef rpcRegistry; - private final SchemaContext schemaContext; + private SchemaContext schemaContext; private RpcBroker(Broker.ProviderSession brokerSession, ActorRef rpcRegistry, SchemaContext schemaContext){ this.brokerSession = brokerSession; @@ -72,7 +74,7 @@ public class RpcBroker extends AbstractUntypedActor { try { RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, msg.getRpc(), msg.getIdentifier()); GetRoutedRpc routedRpcMsg = new GetRoutedRpc(routeId); - GetRoutedRpcReply rpcReply = (GetRoutedRpcReply)ActorUtil.executeLocalOperation(rpcRegistry, routedRpcMsg, ActorUtil.LOCAL_ASK_DURATION, ActorUtil.LOCAL_AWAIT_DURATION); + GetRoutedRpcReply rpcReply = (GetRoutedRpcReply) ActorUtil.executeLocalOperation(rpcRegistry, routedRpcMsg, ActorUtil.LOCAL_ASK_DURATION, ActorUtil.LOCAL_AWAIT_DURATION); String remoteActorPath = rpcReply.getRoutePath(); if(remoteActorPath == null) { @@ -105,11 +107,12 @@ public class RpcBroker extends AbstractUntypedActor { String remoteActorPath = rpcReply.getRoutePath(); if(remoteActorPath == null) { - LOG.debug("No remote actor found for rpc execution."); + LOG.debug("No remote actor found for rpc {{}}.", msg.getRpc()); getSender().tell(new ErrorResponse( - new IllegalStateException("No remote actor found for rpc execution.")), self()); + new IllegalStateException("No remote actor found for rpc execution of : " + msg.getRpc())), self()); } else { + ExecuteRpc executeMsg = new ExecuteRpc(XmlUtils.inputCompositeNodeToXml(msg.getInput(), schemaContext), msg.getRpc()); Object operationRes = ActorUtil.executeRemoteOperation(this.context().actorSelection(remoteActorPath), executeMsg, ActorUtil.REMOTE_ASK_DURATION, ActorUtil.REMOTE_AWAIT_DURATION); @@ -127,7 +130,6 @@ public class RpcBroker extends AbstractUntypedActor { try { Future> rpc = brokerSession.rpc(msg.getRpc(), XmlUtils.inputXmlToCompositeNode(msg.getRpc(), msg.getInputCompositeNode(), schemaContext)); RpcResult rpcResult = rpc != null ? rpc.get():null; - CompositeNode result = rpcResult != null ? rpcResult.getResult() : null; getSender().tell(new RpcResponse(XmlUtils.outputCompositeNodeToXml(result, schemaContext)), self()); } catch (Exception e) { 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/XmlUtils.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/XmlUtils.java deleted file mode 100644 index 5fb2bb8772..0000000000 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/XmlUtils.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ - -package org.opendaylight.controller.remote.rpc; - -import com.google.common.base.Optional; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.data.api.Node; -import org.opendaylight.yangtools.yang.data.api.SimpleNode; -import org.opendaylight.yangtools.yang.data.impl.XmlTreeBuilder; -import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.RpcDefinition; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.controller.netconf.util.xml.XmlUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.w3c.dom.Document; -import org.xml.sax.SAXException; - -import javax.activation.UnsupportedDataTypeException; -import javax.xml.stream.XMLStreamException; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.StringWriter; -import java.util.Set; - -public class XmlUtils { - - private static final Logger LOG = LoggerFactory.getLogger(XmlUtils.class); - - public static String inputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){ - if (cNode == null) return new String(); - - //Document domTree = NodeUtils.buildShadowDomTree(cNode); - Document domTree = null; - try { - Set rpcs = schemaContext.getOperations(); - for(RpcDefinition rpc : rpcs) { - if(rpc.getQName().equals(cNode.getNodeType())){ - domTree = XmlDocumentUtils.toDocument(cNode, rpc.getInput(), XmlDocumentUtils.defaultValueCodecProvider()); - break; - } - } - - } catch (UnsupportedDataTypeException e) { - LOG.error("Error during translation of CompositeNode to Document", e); - } - return domTransformer(domTree); - } - - public static String outputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){ - if (cNode == null) return new String(); - - //Document domTree = NodeUtils.buildShadowDomTree(cNode); - Document domTree = null; - try { - Set rpcs = schemaContext.getOperations(); - for(RpcDefinition rpc : rpcs) { - if(rpc.getQName().equals(cNode.getNodeType())){ - domTree = XmlDocumentUtils.toDocument(cNode, rpc.getInput(), XmlDocumentUtils.defaultValueCodecProvider()); - break; - } - } - - } catch (UnsupportedDataTypeException e) { - LOG.error("Error during translation of CompositeNode to Document", e); - } - return domTransformer(domTree); - } - - private static String domTransformer(Document domTree) { - StringWriter writer = new StringWriter(); - try { - TransformerFactory tf = TransformerFactory.newInstance(); - Transformer transformer = tf.newTransformer(); - transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); - transformer.transform(new DOMSource(domTree), new StreamResult(writer)); - } catch (TransformerException e) { - - LOG.error("Error during translation of Document to OutputStream", e); - } - LOG.debug("compositeNodeToXml " + writer.toString()); - - return writer.toString(); - } - - public static CompositeNode xmlToCompositeNode(String xml){ - if (xml==null || xml.length()==0) return null; - - Node dataTree; - try { - dataTree = XmlTreeBuilder.buildDataTree(new ByteArrayInputStream(xml.getBytes())); - } catch (XMLStreamException e) { - LOG.error("Error during building data tree from XML", e); - return null; - } - if (dataTree == null) { - LOG.error("data tree is null"); - return null; - } - if (dataTree instanceof SimpleNode) { - LOG.error("RPC XML was resolved as SimpleNode"); - return null; - } - return (CompositeNode) dataTree; - } - - public static CompositeNode inputXmlToCompositeNode(QName rpc, String xml, SchemaContext schemaContext){ - if (xml==null || xml.length()==0) return null; - - Node dataTree = null; - try { - - Document doc = XmlUtil.readXmlToDocument(xml); - LOG.debug("xmlToCompositeNode Document is " + xml ); - Set rpcs = schemaContext.getOperations(); - for(RpcDefinition rpcDef : rpcs) { - if(rpcDef.getQName().equals(rpc)){ - dataTree = XmlDocumentUtils.toDomNode(doc.getDocumentElement(), Optional.of(rpcDef.getInput()), Optional.of(XmlDocumentUtils.defaultValueCodecProvider())); - break; - } - } - } catch (SAXException e) { - LOG.error("Error during building data tree from XML", e); - } catch (IOException e) { - LOG.error("Error during building data tree from XML", e); - } - - LOG.debug("xmlToCompositeNode " + dataTree.toString()); - return (CompositeNode) dataTree; - } -} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRoutedRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRoutedRpc.java index 25773bb8a6..fd1af2b33c 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRoutedRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRoutedRpc.java @@ -8,6 +8,7 @@ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.controller.sal.connector.api.RpcRouter; import java.io.Serializable; @@ -15,10 +16,13 @@ import java.util.Set; public class AddRoutedRpc implements Serializable { - Set> announcements; - String actorPath; + private final Set> announcements; + private final String actorPath; + + public AddRoutedRpc(final Set> announcements, final String actorPath) { + Preconditions.checkNotNull(announcements, "Route identifier should not be null"); + Preconditions.checkNotNull(actorPath, "Actor path should not be null"); - public AddRoutedRpc(Set> announcements, String actorPath) { this.announcements = announcements; this.actorPath = actorPath; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRpc.java index eac973137e..7eaa8f0618 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRpc.java @@ -8,16 +8,20 @@ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl; import java.io.Serializable; public class AddRpc implements Serializable { - RouteIdentifierImpl routeId; - String actorPath; + private final RouteIdentifierImpl routeId; + private final String actorPath; + + public AddRpc(final RouteIdentifierImpl routeId, final String actorPath) { + Preconditions.checkNotNull(routeId, "Route identifier should not be null"); + Preconditions.checkNotNull(actorPath, "Actor path should not be null"); - public AddRpc(RouteIdentifierImpl routeId, String actorPath) { this.routeId = routeId; this.actorPath = actorPath; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ErrorResponse.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ErrorResponse.java index ef3f528112..2c26243fe9 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ErrorResponse.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ErrorResponse.java @@ -7,13 +7,16 @@ */ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; + import java.io.Serializable; public class ErrorResponse implements Serializable { - Exception exception; + private final Exception exception; - public ErrorResponse(Exception e) { + public ErrorResponse(final Exception e) { + Preconditions.checkNotNull(e, "Exception should be present for error message"); this.exception = e; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ExecuteRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ExecuteRpc.java index 030d81ac7e..522dd44f5b 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ExecuteRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ExecuteRpc.java @@ -8,16 +8,20 @@ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.yangtools.yang.common.QName; import java.io.Serializable; public class ExecuteRpc implements Serializable { - private String inputCompositeNode; - private QName rpc; + private final String inputCompositeNode; + private final QName rpc; + + public ExecuteRpc(final String inputCompositeNode, final QName rpc) { + Preconditions.checkNotNull(inputCompositeNode, "Composite Node input string should be present"); + Preconditions.checkNotNull(rpc, "rpc Qname should not be null"); - public ExecuteRpc(String inputCompositeNode, QName rpc) { this.inputCompositeNode = inputCompositeNode; this.rpc = rpc; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpc.java index b1fa410e8d..e8d2262182 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpc.java @@ -9,15 +9,17 @@ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl; import java.io.Serializable; public class GetRoutedRpc implements Serializable { - RouteIdentifierImpl routeId; + private final RouteIdentifierImpl routeId; - public GetRoutedRpc(RouteIdentifierImpl routeId) { + public GetRoutedRpc(final RouteIdentifierImpl routeId) { + Preconditions.checkNotNull(routeId, "route id should not be null"); this.routeId = routeId; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpcReply.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpcReply.java index 0e1563340b..d426662192 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpcReply.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpcReply.java @@ -12,9 +12,9 @@ import java.io.Serializable; public class GetRoutedRpcReply implements Serializable { - private String routePath; + private final String routePath; - public GetRoutedRpcReply(String routePath) { + public GetRoutedRpcReply(final String routePath) { this.routePath = routePath; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpc.java index c55627961a..c1d4240dca 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpc.java @@ -7,15 +7,17 @@ */ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl; import java.io.Serializable; public class GetRpc implements Serializable { - RouteIdentifierImpl routeId; + private final RouteIdentifierImpl routeId; - public GetRpc(RouteIdentifierImpl routeId) { + public GetRpc(final RouteIdentifierImpl routeId) { + Preconditions.checkNotNull(routeId, "Route Id should not be null"); this.routeId = routeId; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpcReply.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpcReply.java index 3309b989c5..aaf089d16f 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpcReply.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpcReply.java @@ -12,9 +12,9 @@ import java.io.Serializable; public class GetRpcReply implements Serializable { - private String routePath; + private final String routePath; - public GetRpcReply(String routePath) { + public GetRpcReply(final String routePath) { this.routePath = routePath; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRoutedRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRoutedRpc.java index 00ef980bb1..fd73144e1d 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRoutedRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRoutedRpc.java @@ -7,26 +7,26 @@ */ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import java.io.Serializable; public class InvokeRoutedRpc implements Serializable { - private QName rpc; - private InstanceIdentifier identifier; - private CompositeNode input; + private final QName rpc; + private final YangInstanceIdentifier identifier; + private final CompositeNode input; - public InvokeRoutedRpc(QName rpc, InstanceIdentifier identifier, CompositeNode input) { - this.rpc = rpc; - this.identifier = identifier; - this.input = input; - } + public InvokeRoutedRpc(final QName rpc, final YangInstanceIdentifier identifier, final CompositeNode input) { + Preconditions.checkNotNull(rpc, "rpc qname should not be null"); + Preconditions.checkNotNull(identifier, "instance identifier of routed rpc should not be null"); + Preconditions.checkNotNull(input, "rpc input should not be null"); - public InvokeRoutedRpc(QName rpc, CompositeNode input) { this.rpc = rpc; + this.identifier = identifier; this.input = input; } @@ -34,7 +34,7 @@ public class InvokeRoutedRpc implements Serializable { return rpc; } - public InstanceIdentifier getIdentifier() { + public YangInstanceIdentifier getIdentifier() { return identifier; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRpc.java index 1f4eab0971..94b7fe4024 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRpc.java @@ -7,6 +7,7 @@ */ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.CompositeNode; @@ -14,10 +15,13 @@ import java.io.Serializable; public class InvokeRpc implements Serializable { - private QName rpc; - private CompositeNode input; + private final QName rpc; + private final CompositeNode input; + + public InvokeRpc(final QName rpc, final CompositeNode input) { + Preconditions.checkNotNull(rpc, "rpc qname should not be null"); + Preconditions.checkNotNull(input, "rpc input should not be null"); - public InvokeRpc(QName rpc, CompositeNode input) { this.rpc = rpc; this.input = input; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRoutedRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRoutedRpc.java index a3aa9d12fa..b560b8c8c0 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRoutedRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRoutedRpc.java @@ -8,6 +8,7 @@ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.controller.sal.connector.api.RpcRouter; import java.io.Serializable; @@ -15,10 +16,13 @@ import java.util.Set; public class RemoveRoutedRpc implements Serializable { - Set> announcements; - String actorPath; + private final Set> announcements; + private final String actorPath; + + public RemoveRoutedRpc(final Set> announcements, final String actorPath) { + Preconditions.checkNotNull(announcements, "Route identifier should not be null"); + Preconditions.checkNotNull(actorPath, "Actor path should not be null"); - public RemoveRoutedRpc(Set> announcements, String actorPath) { this.announcements = announcements; this.actorPath = actorPath; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRpc.java index 0bfd78aaae..289334fccc 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRpc.java @@ -8,15 +8,18 @@ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl; import java.io.Serializable; public class RemoveRpc implements Serializable { - RouteIdentifierImpl routeId; + private final RouteIdentifierImpl routeId; + + public RemoveRpc(final RouteIdentifierImpl routeId) { + Preconditions.checkNotNull(routeId, "Route Id should not be null"); - public RemoveRpc(RouteIdentifierImpl routeId) { this.routeId = routeId; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RoutingTableData.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RoutingTableData.java index 132fdba054..c57a258426 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RoutingTableData.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RoutingTableData.java @@ -15,11 +15,11 @@ import java.util.LinkedHashSet; import java.util.Map; public class RoutingTableData implements Serializable { - private Map, String> rpcMap; - private Map, LinkedHashSet> routedRpcMap; + private final Map, String> rpcMap; + private final Map, LinkedHashSet> routedRpcMap; - public RoutingTableData(Map, String> rpcMap, - Map, LinkedHashSet> routedRpcMap) { + public RoutingTableData(final Map, String> rpcMap, + final Map, LinkedHashSet> routedRpcMap) { this.rpcMap = rpcMap; this.routedRpcMap = routedRpcMap; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RpcResponse.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RpcResponse.java index cbfecb1918..17766f15b9 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RpcResponse.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RpcResponse.java @@ -12,9 +12,9 @@ package org.opendaylight.controller.remote.rpc.messages; import java.io.Serializable; public class RpcResponse implements Serializable { - private String resultCompositeNode; + private final String resultCompositeNode; - public RpcResponse(String resultCompositeNode) { + public RpcResponse(final String resultCompositeNode) { this.resultCompositeNode = resultCompositeNode; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/UpdateSchemaContext.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/UpdateSchemaContext.java new file mode 100644 index 0000000000..83fc7723b3 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/UpdateSchemaContext.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.remote.rpc.messages; + +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +public class UpdateSchemaContext { + + private final SchemaContext schemaContext; + + public UpdateSchemaContext(final SchemaContext schemaContext) { + this.schemaContext = schemaContext; + } + + public SchemaContext getSchemaContext() { + return schemaContext; + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/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 new file mode 100644 index 0000000000..92a7fbae79 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/InstanceIdentifierForXmlCodec.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.remote.rpc.utils; + +import com.google.common.base.Preconditions; +import com.google.common.base.Splitter; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class InstanceIdentifierForXmlCodec { + private static final Pattern PREDICATE_PATTERN = Pattern.compile("\\[(.*?)\\]"); + private static final Splitter SLASH_SPLITTER = Splitter.on('/'); + private static final Splitter COLON_SPLITTER = Splitter.on(':'); + private static final Splitter AT_SPLITTER = Splitter.on('@'); + private static final Logger logger = LoggerFactory.getLogger(InstanceIdentifierForXmlCodec.class); + + private InstanceIdentifierForXmlCodec() { + throw new UnsupportedOperationException("Utility class"); + } + + public static YangInstanceIdentifier deserialize(final Element element, final SchemaContext schemaContext) { + Preconditions.checkNotNull(element, "Value of element for deserialization can't be null"); + Preconditions.checkNotNull(schemaContext, + "Schema context for deserialization of instance identifier type can't be null"); + + final String valueTrimmed = element.getTextContent().trim(); + logger.debug("Instance identifier derserialize: splitting the text {} with Slash to find path arguments", valueTrimmed); + final Iterator xPathParts = SLASH_SPLITTER.split(valueTrimmed).iterator(); + + // must be at least "/pr:node" + if (!xPathParts.hasNext() || !xPathParts.next().isEmpty() || !xPathParts.hasNext()) { + logger.debug("Instance identifier derserialize: No path argument found for element."); + return null; + } + + List result = new ArrayList<>(); + while (xPathParts.hasNext()) { + String xPathPartTrimmed = xPathParts.next().trim(); + + PathArgument pathArgument = toPathArgument(xPathPartTrimmed, element, schemaContext); + if (pathArgument != null) { + result.add(pathArgument); + } + } + return YangInstanceIdentifier.create(result); + } + + public static Element serialize(final YangInstanceIdentifier id, final Element element) { + Preconditions.checkNotNull(id, "Variable should contain instance of instance identifier and can't be null"); + Preconditions.checkNotNull(element, "DOM element can't be null"); + + final RandomPrefix prefixes = new RandomPrefix(); + final String str = XmlUtils.encodeIdentifier(prefixes, id); + + for (Entry 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; + } + } + 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; + } + + final String prefix = it.next().trim(); + if (prefix.isEmpty()) { + return null; + } + + // it is not "prefix:value" + if (!it.hasNext()) { + return null; + } + + final String identifier = it.next().trim(); + if (identifier.isEmpty()) { + return null; + } + + URI namespace = null; + String namespaceStr = null; + try { + namespaceStr = element.getAttribute("xmlns:"+prefix); + namespace = new URI(namespaceStr); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("It wasn't possible to convert " + namespaceStr + " to URI object."); + } catch (NullPointerException e) { + throw new IllegalArgumentException("I wasn't possible to get namespace for prefix " + prefix); + } + + Module module = schemaContext.findModuleByNamespaceAndRevision(namespace, null); + return QName.create(module.getQNameModule(), identifier); + } + + private static String trimIfEndIs(final String str, final char end) { + final int l = str.length() - 1; + if (str.charAt(l) != end) { + return null; + } + + return str.substring(1, l); + } + + private static Object toPredicateValue(final String predicatedValue) { + logger.debug("Instance identifier derserialize: converting the predicate vstring to object {}", predicatedValue); + final String predicatedValueTrimmed = predicatedValue.trim(); + if (predicatedValue.isEmpty()) { + return null; + } + String updatedValue = null; + switch (predicatedValueTrimmed.charAt(0)) { + case '"': + updatedValue = trimIfEndIs(predicatedValueTrimmed, '"'); + break; + case '\'': + updatedValue = trimIfEndIs(predicatedValueTrimmed, '\''); + break; + default: + updatedValue = predicatedValueTrimmed; + } + Iterator it = AT_SPLITTER.split(updatedValue).iterator(); + // Empty string + if (!it.hasNext()) { + return null; + } + + final String value = it.next().trim(); + if (value.isEmpty()) { + return null; + } + + if (!it.hasNext()) { + return value; + } + + final String type = it.next().trim(); + if (type.isEmpty()) { + return value; + } + Object predicateObject = null; + try { + logger.debug("Instance identifier derserialize: converting the predicate value {{}}to correct object type {{}}", value, type); + predicateObject = Class.forName(type).getConstructor(String.class).newInstance(value); + } catch (Exception e) { + logger.error("Could not convert to valid type of value", e); + } + return predicateObject; + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/RandomPrefix.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/RandomPrefix.java new file mode 100644 index 0000000000..55cc8192a8 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/RandomPrefix.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.remote.rpc.utils; + +import org.opendaylight.yangtools.yang.common.QName; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ThreadLocalRandom; + +/** + * Picked from Yang tools because it was not public class + */ + +final class RandomPrefix { + final Map prefixes = new HashMap<>(); + + Iterable> getPrefixes() { + return prefixes.entrySet(); + } + + String encodeQName(final QName qname) { + String prefix = prefixes.get(qname.getNamespace()); + if (prefix == null) { + prefix = qname.getPrefix(); + if (prefix == null || prefix.isEmpty() || prefixes.containsValue(prefix)) { + final ThreadLocalRandom random = ThreadLocalRandom.current(); + do { + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 4; i++) { + sb.append((char)('a' + random.nextInt(25))); + } + + prefix = sb.toString(); + } while (prefixes.containsValue(prefix)); + } + + prefixes.put(qname.getNamespace(), prefix); + } + + return prefix + ':' + qname.getLocalName(); + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlDocumentUtils.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlDocumentUtils.java new file mode 100644 index 0000000000..127aa0732b --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlDocumentUtils.java @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.remote.rpc.utils; + +import com.google.common.base.Function; +import com.google.common.base.Objects; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.ModifyAction; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; +import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl; +import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec; +import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider; +import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; +import org.opendaylight.yangtools.yang.model.api.ChoiceNode; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import javax.activation.UnsupportedDataTypeException; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import javax.xml.transform.dom.DOMResult; +import java.net.URI; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static com.google.common.base.Preconditions.checkState; + +public class XmlDocumentUtils { + private static class ElementWithSchemaContext { + Element element; + SchemaContext schemaContext; + + ElementWithSchemaContext(final Element element,final SchemaContext schemaContext) { + this.schemaContext = schemaContext; + this.element = element; + } + + Element getElement() { + return element; + } + + SchemaContext getSchemaContext() { + return schemaContext; + } + } + + public static final QName OPERATION_ATTRIBUTE_QNAME = QName.create(URI.create("urn:ietf:params:xml:ns:netconf:base:1.0"), null, "operation"); + private static final Logger logger = LoggerFactory.getLogger(XmlDocumentUtils.class); + private static final XMLOutputFactory FACTORY = XMLOutputFactory.newFactory(); + + /** + * Converts Data DOM structure to XML Document for specified XML Codec Provider and corresponding + * Data Node Container schema. The CompositeNode data parameter enters as root of Data DOM tree and will + * be transformed to root in XML Document. Each element of Data DOM tree is compared against specified Data + * Node Container Schema and transformed accordingly. + * + * @param data Data DOM root element + * @param schema Data Node Container Schema + * @param codecProvider XML Codec Provider + * @return new instance of XML Document + * @throws javax.activation.UnsupportedDataTypeException + */ + public static Document toDocument(final CompositeNode data, final DataNodeContainer schema, final XmlCodecProvider codecProvider) + throws UnsupportedDataTypeException { + Preconditions.checkNotNull(data); + Preconditions.checkNotNull(schema); + + if (!(schema instanceof ContainerSchemaNode || schema instanceof ListSchemaNode)) { + throw new UnsupportedDataTypeException("Schema can be ContainerSchemaNode or ListSchemaNode. Other types are not supported yet."); + } + + final DOMResult result = new DOMResult(getDocument()); + try { + final XMLStreamWriter writer = FACTORY.createXMLStreamWriter(result); + XmlStreamUtils.create(codecProvider).writeDocument(writer, data, (SchemaNode)schema); + writer.close(); + return (Document)result.getNode(); + } catch (XMLStreamException e) { + logger.error("Failed to serialize data {}", data, e); + return null; + } + } + + public static Document getDocument() { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + Document doc = null; + try { + DocumentBuilder bob = dbf.newDocumentBuilder(); + doc = bob.newDocument(); + } catch (ParserConfigurationException e) { + throw new RuntimeException(e); + } + return doc; + } + + + public static QName qNameFromElement(final Element xmlElement) { + String namespace = xmlElement.getNamespaceURI(); + String localName = xmlElement.getLocalName(); + return QName.create(namespace != null ? URI.create(namespace) : null, null, localName); + } + + private static Node toNodeWithSchema(final Element xmlElement, final DataSchemaNode schema, final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) { + checkQName(xmlElement, schema.getQName()); + if (schema instanceof DataNodeContainer) { + return toCompositeNodeWithSchema(xmlElement, schema.getQName(), (DataNodeContainer) schema, schemaCtx); + } else if (schema instanceof LeafSchemaNode) { + return toSimpleNodeWithType(xmlElement, (LeafSchemaNode) schema, codecProvider,schemaCtx); + } else if (schema instanceof LeafListSchemaNode) { + return toSimpleNodeWithType(xmlElement, (LeafListSchemaNode) schema, codecProvider,schemaCtx); + } + return null; + } + + + + private static Node toSimpleNodeWithType(final Element xmlElement, final LeafSchemaNode schema, + final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) { + TypeDefinitionAwareCodec> codec = codecProvider.codecFor(schema.getType()); + String text = xmlElement.getTextContent(); + Object value = null; + if (codec != null) { + logger.debug("toSimpleNodeWithType: found codec, deserializing text {}", text); + value = codec.deserialize(text); + } + + final TypeDefinition baseType = XmlUtils.resolveBaseTypeFrom(schema.getType()); + if (baseType instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifier) { + logger.debug("toSimpleNodeWithType: base type of node is instance identifier, deserializing element", xmlElement); + value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx); + + } else if(baseType instanceof IdentityrefTypeDefinition){ + logger.debug("toSimpleNodeWithType: base type of node is IdentityrefTypeDefinition, deserializing element", xmlElement); + value = InstanceIdentifierForXmlCodec.toIdentity(xmlElement.getTextContent(), xmlElement, schemaCtx); + + } + + if (value == null) { + logger.debug("toSimpleNodeWithType: no type found for element, returning just the text string value of element {}", xmlElement); + value = xmlElement.getTextContent(); + } + + Optional modifyAction = 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); + } + + if (schema.getType() instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifier) { + logger.debug("toSimpleNodeWithType: base type of node is instance identifier, deserializing element", xmlElement); + value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx); + } + + if (value == null) { + logger.debug("toSimpleNodeWithType: no type found for element, returning just the text string value of element {}", xmlElement); + value = xmlElement.getTextContent(); + } + + Optional modifyAction = getModifyOperationFromAttributes(xmlElement); + return new SimpleNodeTOImpl<>(schema.getQName(), null, value, modifyAction.orNull()); + } + + private static Node toCompositeNodeWithSchema(final Element xmlElement, final QName qName, final DataNodeContainer schema, + final SchemaContext schemaCtx) { + List> values = toDomNodes(xmlElement, Optional.fromNullable(schema.getChildNodes()),schemaCtx); + Optional modifyAction = getModifyOperationFromAttributes(xmlElement); + return ImmutableCompositeNode.create(qName, values, modifyAction.orNull()); + } + + 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(); + } + + 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(); + } + + private static Node toDomNode(Element element) { + QName qname = qNameFromElement(element); + + ImmutableList.Builder> values = ImmutableList.> builder(); + NodeList nodes = element.getChildNodes(); + boolean isSimpleObject = true; + String value = null; + for (int i = 0; i < nodes.getLength(); i++) { + org.w3c.dom.Node child = nodes.item(i); + if (child instanceof Element) { + isSimpleObject = false; + values.add(toDomNode((Element) child)); + } + if (isSimpleObject && child instanceof org.w3c.dom.Text) { + value = element.getTextContent(); + if (!Strings.isNullOrEmpty(value)) { + isSimpleObject = true; + } + } + } + if (isSimpleObject) { + return new SimpleNodeTOImpl<>(qname, null, value); + } + return ImmutableCompositeNode.create(qname, values.build()); + } + + public static List> toDomNodes(final Element element, final Optional> context,SchemaContext schemaCtx) { + return forEachChild(element.getChildNodes(),schemaCtx, new Function>>() { + + @Override + public Optional> apply(ElementWithSchemaContext input) { + if (context.isPresent()) { + QName partialQName = qNameFromElement(input.getElement()); + Optional schemaNode = XmlDocumentUtils.findFirstSchema(partialQName, context.get()); + if (schemaNode.isPresent()) { + return Optional.> fromNullable(// + toNodeWithSchema(input.getElement(), schemaNode.get(), XmlDocumentUtils.defaultValueCodecProvider(),input.getSchemaContext())); + } + } + return Optional.> fromNullable(toDomNode(input.getElement())); + } + + }); + + } + + 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()); + } + } + } + return ImmutableList.copyOf(list); + } + + public static final XmlCodecProvider defaultValueCodecProvider() { + return XmlUtils.DEFAULT_XML_CODEC_PROVIDER; + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlStreamUtils.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlStreamUtils.java new file mode 100644 index 0000000000..e4576c445b --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlStreamUtils.java @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.remote.rpc.utils; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.AttributesContainer; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.api.SimpleNode; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec; +import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider; +import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import java.net.URI; +import java.util.Map.Entry; + +/** + * Utility class for bridging JAXP Stream and YANG Data APIs. Note that the definition of this class + * by no means final and subject to change as more functionality is centralized here. + */ +@Beta +public class XmlStreamUtils { + private static final Logger LOG = LoggerFactory.getLogger(XmlStreamUtils.class); + private final XmlCodecProvider codecProvider; + + protected XmlStreamUtils(final XmlCodecProvider codecProvider) { + this.codecProvider = Preconditions.checkNotNull(codecProvider); + } + + /** + * Create a new instance encapsulating a particular codec provider. + * + * @param codecProvider XML codec provider + * @return A new instance + */ + public static XmlStreamUtils create(final XmlCodecProvider codecProvider) { + return new XmlStreamUtils(codecProvider); + } + + /** + * Check if a particular data element can be emitted as an empty element, bypassing value encoding. This + * functionality is optional, as valid XML stream is produced even if start/end element is produced unconditionally. + * + * @param data Data node + * @return True if the data node will result in empty element body. + */ + public static boolean isEmptyElement(final Node data) { + if (data == null) { + return true; + } + + if (data instanceof CompositeNode) { + return ((CompositeNode) data).getValue().isEmpty(); + } + if (data instanceof SimpleNode) { + return data.getValue() == null; + } + + // Safe default + return false; + } + + /** + * Write an InstanceIdentifier into the output stream. Calling corresponding {@link javax.xml.stream.XMLStreamWriter#writeStartElement(String)} + * and {@link javax.xml.stream.XMLStreamWriter#writeEndElement()} is the responsibility of the caller. + * + * @param writer XML Stream writer + * @param id InstanceIdentifier + * @throws javax.xml.stream.XMLStreamException + */ + public static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull YangInstanceIdentifier id) throws XMLStreamException { + Preconditions.checkNotNull(writer, "Writer may not be null"); + Preconditions.checkNotNull(id, "Variable should contain instance of instance identifier and can't be null"); + LOG.debug("Writing Instance identifier with Random prefix"); + final RandomPrefix prefixes = new RandomPrefix(); + final String str = XmlUtils.encodeIdentifier(prefixes, id); + + for (Entry e: prefixes.getPrefixes()) { + writer.writeNamespace(e.getValue(), e.getKey().toString()); + } + LOG.debug("Instance identifier with Random prefix is now {}", str); + writer.writeCharacters(str); + } + + /** + * Write a full XML document corresponding to a CompositeNode into an XML stream writer. + * + * @param writer XML Stream writer + * @param data data node + * @param schema corresponding schema node, may be null + * @throws javax.xml.stream.XMLStreamException if an encoding problem occurs + */ + public void writeDocument(final @Nonnull XMLStreamWriter writer, final @Nonnull CompositeNode data, final @Nullable SchemaNode schema) throws XMLStreamException { + // final Boolean repairing = (Boolean) writer.getProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES); + // Preconditions.checkArgument(repairing == true, "XML Stream Writer has to be repairing namespaces"); + + writer.writeStartDocument(); + writeElement(writer, data, schema); + writer.writeEndDocument(); + writer.flush(); + } + + + /** + * Write an element into a XML stream writer. This includes the element start/end tags and + * the value of the element. + * + * @param writer XML Stream writer + * @param data data node + * @param schema Schema node + * @throws javax.xml.stream.XMLStreamException if an encoding problem occurs + */ + public void writeElement(final XMLStreamWriter writer, final @Nonnull Node data, final SchemaNode schema) throws XMLStreamException { + final QName qname = data.getNodeType(); + final String pfx = qname.getPrefix() != null ? qname.getPrefix() : ""; + final String ns = qname.getNamespace() != null ? qname.getNamespace().toString() : ""; + + if (isEmptyElement(data)) { + writer.writeEmptyElement(pfx, qname.getLocalName(), ns); + return; + } + + writer.writeStartElement(pfx, qname.getLocalName(), ns); + if (data instanceof AttributesContainer && ((AttributesContainer) data).getAttributes() != null) { + for (Entry attribute : ((AttributesContainer) data).getAttributes().entrySet()) { + writer.writeAttribute(attribute.getKey().getNamespace().toString(), attribute.getKey().getLocalName(), attribute.getValue()); + } + } + + if (data instanceof SimpleNode) { + LOG.debug("writeElement : node is of type SimpleNode"); + // Simple node + if (schema instanceof LeafListSchemaNode) { + writeValue(writer, ((LeafListSchemaNode) schema).getType(), data.getValue()); + } else if (schema instanceof LeafSchemaNode) { + writeValue(writer, ((LeafSchemaNode) schema).getType(), data.getValue()); + } else { + Object value = data.getValue(); + if (value != null) { + writer.writeCharacters(String.valueOf(value)); + } + } + } else { + LOG.debug("writeElement : node is of type CompositeNode"); + // CompositeNode + for (Node child : ((CompositeNode) data).getValue()) { + DataSchemaNode childSchema = null; + if (schema instanceof DataNodeContainer) { + childSchema = SchemaUtils.findFirstSchema(child.getNodeType(), ((DataNodeContainer) schema).getChildNodes()).orNull(); + if (childSchema == null) { + LOG.debug("Probably the data node \"{}\" does not conform to schema", child == null ? "" : child.getNodeType().getLocalName()); + } + } + + writeElement(writer, child, childSchema); + } + } + + writer.writeEndElement(); + } + + /** + * Write a value into a XML stream writer. This method assumes the start and end of element is + * emitted by the caller. + * + * @param writer XML Stream writer + * @param type type definitions + * @param value object value + * @throws javax.xml.stream.XMLStreamException if an encoding problem occurs + */ + public void writeValue(final @Nonnull XMLStreamWriter writer, final @Nonnull TypeDefinition type, final Object value) throws XMLStreamException { + if (value == null) { + LOG.debug("Value of {}:{} is null, not encoding it", type.getQName().getNamespace(), type.getQName().getLocalName()); + return; + } + + final TypeDefinition baseType = XmlUtils.resolveBaseTypeFrom(type); + if (baseType instanceof IdentityrefTypeDefinition) { + write(writer, (IdentityrefTypeDefinition) baseType, value); + } else if (baseType instanceof InstanceIdentifierTypeDefinition) { + write(writer, (InstanceIdentifierTypeDefinition) baseType, value); + } else { + final TypeDefinitionAwareCodec codec = codecProvider.codecFor(baseType); + String text; + if (codec != null) { + try { + text = codec.serialize(value); + } catch (ClassCastException e) { + LOG.error("Provided node value {} did not have type {} required by mapping. Using stream instead.", value, baseType, e); + text = String.valueOf(value); + } + } else { + LOG.error("Failed to find codec for {}, falling back to using stream", baseType); + text = String.valueOf(value); + } + writer.writeCharacters(text); + } + } + + private static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull IdentityrefTypeDefinition type, final @Nonnull Object value) throws XMLStreamException { + if (value instanceof QName) { + final QName qname = (QName) value; + final String prefix; + if (qname.getPrefix() != null && !qname.getPrefix().isEmpty()) { + prefix = qname.getPrefix(); + } else { + prefix = "x"; + } + + writer.writeNamespace(prefix, qname.getNamespace().toString()); + writer.writeCharacters(prefix + ':' + qname.getLocalName()); + } else { + LOG.debug("Value of {}:{} is not a QName but {}", type.getQName().getNamespace(), type.getQName().getLocalName(), value.getClass()); + writer.writeCharacters(String.valueOf(value)); + } + } + + private static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull InstanceIdentifierTypeDefinition type, final @Nonnull Object value) throws XMLStreamException { + if (value instanceof YangInstanceIdentifier) { + LOG.debug("Writing InstanceIdentifier object {}", value); + write(writer, (YangInstanceIdentifier)value); + } else { + LOG.debug("Value of {}:{} is not an InstanceIdentifier but {}", type.getQName().getNamespace(), type.getQName().getLocalName(), value.getClass()); + writer.writeCharacters(String.valueOf(value)); + } + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlUtils.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlUtils.java new file mode 100644 index 0000000000..e07401a3e0 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlUtils.java @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.remote.rpc.utils; + +import com.google.common.base.Optional; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.api.SimpleNode; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; +import org.opendaylight.yangtools.yang.data.impl.XmlTreeBuilder; +import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec; +import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider; +import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.activation.UnsupportedDataTypeException; +import javax.annotation.Nonnull; +import javax.xml.stream.XMLStreamException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.StringWriter; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Common XML-related utility methods, which are not specific to a particular + * JAXP API. + */ +public class XmlUtils { + + public static final XmlCodecProvider DEFAULT_XML_CODEC_PROVIDER = new XmlCodecProvider() { + @Override + public TypeDefinitionAwareCodec> codecFor(final TypeDefinition baseType) { + return TypeDefinitionAwareCodec.from(baseType); + } + }; + + private XmlUtils() { + } + + private static final String BLANK = ""; + private static final Logger LOG = LoggerFactory.getLogger(XmlUtils.class); + + /** + * Converts the composite node to xml using rpc input schema node + * @param cNode + * @param schemaContext + * @return xml String + */ + public static String inputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){ + LOG.debug("Converting input composite node to xml {}", cNode); + if (cNode == null) return BLANK; + + if(schemaContext == null) return BLANK; + + Document domTree = null; + try { + Set rpcs = schemaContext.getOperations(); + for(RpcDefinition rpc : rpcs) { + if(rpc.getQName().equals(cNode.getNodeType())){ + LOG.debug("Found the rpc definition from schema context matching with input composite node {}", rpc.getQName()); + + CompositeNode inputContainer = cNode.getFirstCompositeByName(QName.create(cNode.getNodeType(), "input")); + domTree = XmlDocumentUtils.toDocument(inputContainer, rpc.getInput(), XmlDocumentUtils.defaultValueCodecProvider()); + + LOG.debug("input composite node to document conversion complete, document is {}", domTree); + break; + } + } + + } catch (UnsupportedDataTypeException e) { + LOG.error("Error during translation of CompositeNode to Document", e); + } + return domTransformer(domTree); + } + + /** + * Converts the composite node to xml String using rpc output schema node + * @param cNode + * @param schemaContext + * @return xml string + */ + public static String outputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){ + LOG.debug("Converting output composite node to xml {}", cNode); + if (cNode == null) return BLANK; + + if(schemaContext == null) return BLANK; + + Document domTree = null; + try { + Set rpcs = schemaContext.getOperations(); + for(RpcDefinition rpc : rpcs) { + if(rpc.getQName().equals(cNode.getNodeType())){ + LOG.debug("Found the rpc definition from schema context matching with output composite node {}", rpc.getQName()); + + CompositeNode outputContainer = cNode.getFirstCompositeByName(QName.create(cNode.getNodeType(), "output")); + domTree = XmlDocumentUtils.toDocument(outputContainer, rpc.getOutput(), XmlDocumentUtils.defaultValueCodecProvider()); + + LOG.debug("output composite node to document conversion complete, document is {}", domTree); + break; + } + } + + } catch (UnsupportedDataTypeException e) { + LOG.error("Error during translation of CompositeNode to Document", e); + } + return domTransformer(domTree); + } + + private static String domTransformer(Document domTree) { + StringWriter writer = new StringWriter(); + try { + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer transformer = tf.newTransformer(); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + transformer.transform(new DOMSource(domTree), new StreamResult(writer)); + } catch (TransformerException e) { + + LOG.error("Error during translation of Document to OutputStream", e); + } + LOG.debug("Document to string conversion complete, xml string is {} ", writer.toString()); + + return writer.toString(); + } + + public static CompositeNode xmlToCompositeNode(String xml){ + if (xml==null || xml.length()==0) return null; + + Node dataTree; + try { + dataTree = XmlTreeBuilder.buildDataTree(new ByteArrayInputStream(xml.getBytes())); + } catch (XMLStreamException e) { + LOG.error("Error during building data tree from XML", e); + return null; + } + if (dataTree == null) { + LOG.error("data tree is null"); + return null; + } + if (dataTree instanceof SimpleNode) { + LOG.error("RPC XML was resolved as SimpleNode"); + return null; + } + return (CompositeNode) dataTree; + } + + /** + * Converts the xml to composite node using rpc input schema node + * @param rpc + * @param xml + * @param schemaContext + * @return CompositeNode object based on the input, if any of the input parameter is null, a null object is returned + */ + public static CompositeNode inputXmlToCompositeNode(QName rpc, String xml, SchemaContext schemaContext){ + LOG.debug("Converting input xml to composite node {}", xml); + if (xml==null || xml.length()==0) return null; + + if(rpc == null) return null; + + if(schemaContext == null) return null; + + CompositeNode compositeNode = null; + try { + + Document doc = XmlUtil.readXmlToDocument(xml); + Set rpcs = schemaContext.getOperations(); + for(RpcDefinition rpcDef : rpcs) { + if(rpcDef.getQName().equals(rpc)){ + LOG.debug("found the rpc definition from schema context matching rpc {}", rpc); + + if(rpcDef.getInput() == null) { + LOG.warn("found rpc definition's input is null"); + return null; + } + + QName input = rpcDef.getInput().getQName(); + NodeList nodeList = doc.getElementsByTagNameNS(input.getNamespace().toString(), "input"); + if(nodeList == null || nodeList.getLength() < 1) { + LOG.warn("xml does not have input entry. {}", xml); + return null; + } + Element xmlData = (Element)nodeList.item(0); + + List> 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)); + compositeNode = it.toInstance(); + break; + } + } + } catch (SAXException e) { + LOG.error("Error during building data tree from XML", e); + } catch (IOException e) { + LOG.error("Error during building data tree from XML", e); + } + + LOG.debug("Xml to composite node conversion complete {} ", compositeNode); + return compositeNode; + } + + public static TypeDefinition resolveBaseTypeFrom(final @Nonnull TypeDefinition type) { + TypeDefinition superType = type; + while (superType.getBaseType() != null) { + superType = superType.getBaseType(); + } + return superType; + } + + /** + * This code is picked from yangtools and modified to add type of instance identifier + * output of instance identifier something like below for a flow ref composite node of type instance identifier, + * which has path arguments with predicates, whose value is of type java.lang.short + * + * /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(); + + 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/main/resources/application.conf b/opendaylight/md-sal/sal-remoterpc-connector/src/main/resources/application.conf index 9585e9ffb6..6088dd0e0e 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/resources/application.conf +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/resources/application.conf @@ -7,7 +7,7 @@ odl-cluster{ remote { log-remote-lifecycle-events = off netty.tcp { - hostname = "192.168.141.142" + hostname = "192.168.141.141" port = 2551 } } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RpcBrokerTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RpcBrokerTest.java index 595d8331eb..392c1e637d 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RpcBrokerTest.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RpcBrokerTest.java @@ -35,6 +35,7 @@ import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.ModifyAction; import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; import org.opendaylight.yangtools.yang.model.api.SchemaContext; @@ -72,15 +73,16 @@ public class RpcBrokerTest { Broker.ProviderSession brokerSession = Mockito.mock(Broker.ProviderSession.class); SchemaContext schemaContext = mock(SchemaContext.class); ActorRef rpcBroker = system.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext)); - QName rpc = new QName(new URI("actor1"), "actor1"); - InvokeRpc invokeMsg = new InvokeRpc(rpc, null); + QName rpc = new QName(new URI("noactor1"), "noactor1"); + CompositeNode input = new ImmutableCompositeNode(QName.create("ns", "2013-12-09", "no child"), new ArrayList>(), ModifyAction.REPLACE); + InvokeRpc invokeMsg = new InvokeRpc(rpc, input); rpcBroker.tell(invokeMsg, getRef()); Boolean getMsg = new ExpectMsg("ErrorResponse") { protected Boolean match(Object in) { if (in instanceof ErrorResponse) { ErrorResponse reply = (ErrorResponse)in; - return "No remote actor found for rpc execution.".equals(reply.getException().getMessage()); + return reply.getException().getMessage().contains("No remote actor found for rpc execution of :"); } else { throw noMatch(); } @@ -144,7 +146,8 @@ public class RpcBrokerTest { SchemaContext schemaContext = mock(SchemaContext.class); ActorRef rpcBroker = system.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext)); QName rpc = new QName(new URI("actor1"), "actor1"); - InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, null); + CompositeNode input = new ImmutableCompositeNode(QName.create("ns", "2013-12-09", "child1"), new ArrayList>(), ModifyAction.REPLACE); + InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, YangInstanceIdentifier.create(new YangInstanceIdentifier.NodeIdentifier(rpc)), input); rpcBroker.tell(invokeMsg, getRef()); Boolean getMsg = new ExpectMsg("ErrorResponse") { @@ -176,7 +179,8 @@ public class RpcBrokerTest { ActorRef rpcBrokerRemote = system.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext), "actor2"); // Add Routed RPC in table QName rpc = new QName(new URI("actor2"), "actor2"); - RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, rpc, null); + YangInstanceIdentifier identifier = YangInstanceIdentifier.create(new YangInstanceIdentifier.NodeIdentifier(rpc)); + RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, rpc, identifier); final String route = rpcBrokerRemote.path().toString(); Set> routeIds = new HashSet<>(); routeIds.add(routeId); @@ -192,7 +196,7 @@ public class RpcBrokerTest { RpcResult result = Rpcs.getRpcResult(true, invokeRpcResult, errors); Future> rpcResult = Futures.immediateFuture(result); when(brokerSession.rpc(rpc, input)).thenReturn(rpcResult); - InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, input); + InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, identifier, input); rpcBroker.tell(invokeMsg, getRef()); //verify response msg diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RoutingTableTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RoutingTableTest.java index a57402a793..129a5a56e8 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RoutingTableTest.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RoutingTableTest.java @@ -39,7 +39,8 @@ public class RoutingTableTest { @Test public void addGlobalRouteNullRouteTest() { try { - RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, null, null); + QName type = new QName(new URI("actor1"), "actor1"); + RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, type, null); routingTable.addGlobalRoute(routeId, null); Assert.fail("Null pointer exception was not thrown."); @@ -109,7 +110,8 @@ public class RoutingTableTest { @Test public void addRoutedRpcNullRouteTest() { try { - RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, null, null); + QName type = new QName(new URI("actor1"), "actor1"); + RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, type, null); routingTable.addRoutedRpc(routeId, null); diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/utils/XmlUtilsTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/utils/XmlUtilsTest.java new file mode 100644 index 0000000000..a408e1d55a --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/utils/XmlUtilsTest.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.remote.rpc.utils; + + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.io.ByteSource; +import junit.framework.Assert; +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.ModifyAction; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.api.SimpleNode; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl; + +import javax.xml.parsers.DocumentBuilderFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + + +public class XmlUtilsTest { + + private static final DocumentBuilderFactory BUILDERFACTORY; + + static { + final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setCoalescing(true); + factory.setIgnoringElementContentWhitespace(true); + factory.setIgnoringComments(true); + BUILDERFACTORY = factory; + } + + private SchemaContext schema; + private RpcDefinition testRpc; + + public static final String XML_CONTENT = "" + + "flowid" + + "/ltha:node/ltha:node1[ltha:id='3@java.lang.Short']" + + ""; + + @Before + public void setUp() throws Exception { + final ByteSource byteSource = new ByteSource() { + @Override + public InputStream openStream() throws IOException { + return XmlUtilsTest.this.getClass().getResourceAsStream("rpcTest.yang"); + } + }; + schema = new YangParserImpl().parseSources(Lists.newArrayList(byteSource)); + final Module rpcTestModule = schema.getModules().iterator().next(); + testRpc = rpcTestModule.getRpcs().iterator().next(); + } + + @Test + public void testNullInputXmlToComposite() { + CompositeNode node = XmlUtils.inputXmlToCompositeNode(testRpc.getQName(), null, schema); + Assert.assertNull(node); + } + + @Test + public void testNullRpcXmlToComposite() { + CompositeNode node = XmlUtils.inputXmlToCompositeNode(null, XML_CONTENT, schema); + Assert.assertNull(node); + } + + @Test + public void testInputXmlToCompositeNode() { + CompositeNode node = XmlUtils.inputXmlToCompositeNode(testRpc.getQName(), XML_CONTENT, schema); + ImmutableList input = (ImmutableList)node.getValue().get(0).getValue(); + SimpleNode firstNode = input.get(0); + + Assert.assertEquals("id", firstNode.getNodeType().getLocalName()); + Assert.assertEquals("flowid", firstNode.getValue()); + + SimpleNode secondNode = input.get(1); + Assert.assertEquals("flow", secondNode.getNodeType().getLocalName()); + + YangInstanceIdentifier instance = (YangInstanceIdentifier) secondNode.getValue(); + Iterable iterable = instance.getPathArguments(); + Iterator it = iterable.iterator(); + YangInstanceIdentifier.NodeIdentifier firstPath = (YangInstanceIdentifier.NodeIdentifier) it.next(); + Assert.assertEquals("node", firstPath.getNodeType().getLocalName()); + YangInstanceIdentifier.NodeIdentifierWithPredicates secondPath = (YangInstanceIdentifier.NodeIdentifierWithPredicates)it.next(); + Short value = (Short)secondPath.getKeyValues().values().iterator().next(); + Short expected = 3; + Assert.assertEquals(expected, value); + } + + @Test + public void testInputCompositeNodeToXML() { + CompositeNode input = XmlUtils.inputXmlToCompositeNode(testRpc.getQName(), XML_CONTENT, schema); + List> 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()); + } + + +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/org/opendaylight/controller/remote/rpc/utils/rpcTest.yang b/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/org/opendaylight/controller/remote/rpc/utils/rpcTest.yang new file mode 100644 index 0000000000..5fc564fec3 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/org/opendaylight/controller/remote/rpc/utils/rpcTest.yang @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +module rpc-test { + yang-version 1; + namespace "urn:opendaylight:controller:rpc:test"; + prefix "rpct"; + + revision 2014-07-29 { + description "rpc test"; + } + + typedef flow-ref { + type instance-identifier; + } + + rpc add-flow { + input { + leaf id { + type string; + } + + leaf flow { + type flow-ref; + } + } + } +} \ No newline at end of file diff --git a/opendaylight/northbound/commons/pom.xml b/opendaylight/northbound/commons/pom.xml index a2d2dac112..cbc1f0c328 100644 --- a/opendaylight/northbound/commons/pom.xml +++ b/opendaylight/northbound/commons/pom.xml @@ -91,7 +91,6 @@ javax.ws.rs.core, javax.xml.bind, javax.xml.bind.annotation, - org.objectweb.asm, org.opendaylight.controller.sal.utils, org.opendaylight.controller.sal.core, org.opendaylight.controller.sal.authorization,