</schema-service>
</module>
-->
- <!-- Cluster RPC -->
- <!-- Enable the following module if you want to use remote rpc connector
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:remote-rpc-connector">prefix:remote-rpc-connector</type>
- <name>remote-rpc-connector</name>
- <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:remote-rpc-connector">
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
- <name>dom-broker</name>
- </dom-broker>
- </module>
- -->
+
<module>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:inmemory-datastore-provider">prefix:inmemory-operational-datastore-provider</type>
<name>operational-store-service</name>
</binding-mapping-service>
</binding-forwarded-data-broker>
</module>
+ <!-- Cluster RPC -->
+ <!-- Enable the following module if you want to use remote rpc connector
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:remote-rpc-connector">prefix:remote-rpc-connector</type>
+ <name>remote-rpc-connector</name>
+ <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:remote-rpc-connector">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+ <name>dom-broker</name>
+ </dom-broker>
+ </module>
+ -->
</modules>
<services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
<service>
<!-- Yang Test Models for MD-SAL -->
<module>sal-test-model</module>
+
+ <!-- Clustering -->
+ <module>sal-remoterpc-connector</module>
</modules>
<build>
<groupId>org.opendaylight.controller</groupId>
<artifactId>netconf-util</artifactId>
</dependency>
-
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-impl</artifactId>
+ </dependency>
<!-- Yang tools-->
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-data-api</artifactId>
+
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-model-api</artifactId>
+
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-data-impl</artifactId>
+
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-common</artifactId>
+
</dependency>
<version>${slf4j.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-parser-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.collections</groupId>
+ <artifactId>google-collections</artifactId>
+ <version>1.0</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
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);
}
@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);
}
}
*/
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;
- }
-
}
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;
}
@Override
- public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(QName rpc, InstanceIdentifier identifier, CompositeNode input) {
+ public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(QName rpc, YangInstanceIdentifier identifier, CompositeNode input) {
InvokeRoutedRpc rpcMsg = new InvokeRoutedRpc(rpc, identifier, input);
return executeMsg(rpcMsg);
}
private ListenableFuture<RpcResult<CompositeNode>> executeMsg(Object rpcMsg) {
- CompositeNode result = null;
- Collection<RpcError> errors = errors = new ArrayList<>();
+ ListenableFuture<RpcResult<CompositeNode>> listenableFuture = null;
+
try {
Object response = ActorUtil.executeLocalOperation(rpcBroker, rpcMsg, ActorUtil.ASK_DURATION, ActorUtil.AWAIT_DURATION);
if(response instanceof RpcResponse) {
+
RpcResponse rpcResponse = (RpcResponse) response;
- result = XmlUtils.xmlToCompositeNode(rpcResponse.getResultCompositeNode());
+ CompositeNode result = XmlUtils.xmlToCompositeNode(rpcResponse.getResultCompositeNode());
+ listenableFuture = Futures.immediateFuture(RpcResultBuilder.success(result).build());
+
} else if(response instanceof ErrorResponse) {
+
ErrorResponse errorResponse = (ErrorResponse) response;
Exception e = errorResponse.getException();
- errors.add(RpcErrors.getRpcError(null, null, null, null, e.getMessage(), null, e.getCause()));
+ final RpcResultBuilder<CompositeNode> failed = RpcResultBuilder.failed();
+ failed.withError(null, null, e.getMessage(), null, null, e.getCause());
+ listenableFuture = Futures.immediateFuture(failed.build());
+
}
} catch (Exception e) {
LOG.error("Error occurred while invoking RPC actor {}", e.toString());
- errors.add(RpcErrors.getRpcError(null, null, null, null, e.getMessage(), null, e.getCause()));
+
+ final RpcResultBuilder<CompositeNode> failed = RpcResultBuilder.failed();
+ failed.withError(null, null, e.getMessage(), null, null, e.getCause());
+ listenableFuture = Futures.immediateFuture(failed.build());
}
- return Futures.immediateFuture(Rpcs.getRpcResult(true, result, errors));
+
+ return listenableFuture;
}
}
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;
@Override
public void close() throws Exception {
this.actorSystem.shutdown();
- unregisterSupportedRpcs();
- unregisterSupportedRoutedRpcs();
}
@Override
}
private void start() {
- LOG.debug("Starting all rpc listeners.");
+ LOG.info("Starting all rpc listeners and actors.");
// Create actor to handle and sync routing table in cluster
ClusterWrapper clusterWrapper = new ClusterWrapperImpl(actorSystem);
- rpcRegistry = actorSystem.actorOf(RpcRegistry.props(clusterWrapper), "rpc-registry");
-
- // Create actor to invoke and execute rpc
SchemaService schemaService = brokerSession.getService(SchemaService.class);
- SchemaContext schemaContext = schemaService.getGlobalContext();
- rpcBroker = actorSystem.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext), "rpc-broker");
- String rpcBrokerPath = clusterWrapper.getAddress().toString() + "/user/rpc-broker";
- rpcListener = new RpcListener(rpcRegistry, rpcBrokerPath);
- routeChangeListener = new RoutedRpcListener(rpcRegistry, rpcBrokerPath);
- rpcImplementation = new RemoteRpcImplementation(rpcBroker, schemaContext);
- brokerSession.addRpcRegistrationListener(rpcListener);
- rpcProvisionRegistry.registerRouteChangeListener(routeChangeListener);
- rpcProvisionRegistry.setRoutedRpcDefaultDelegate(rpcImplementation);
- announceSupportedRpcs();
- announceSupportedRoutedRpcs();
+ schemaContext = schemaService.getGlobalContext();
- }
+ rpcManager = actorSystem.actorOf(RpcManager.props(clusterWrapper, schemaContext, brokerSession, rpcProvisionRegistry), "rpc");
- /**
- * Add all the locally registered RPCs in the clustered routing table
- */
- private void announceSupportedRpcs(){
- LOG.debug("Adding all supported rpcs to routing table");
- Set<QName> currentlySupported = brokerSession.getSupportedRpcs();
- for (QName rpc : currentlySupported) {
- rpcListener.onRpcImplementationAdded(rpc);
- }
+ LOG.debug("Rpc actors are created.");
}
- /**
- * Add all the locally registered Routed RPCs in the clustered routing table
- */
- private void announceSupportedRoutedRpcs(){
-
- //TODO: announce all routed RPCs as well
- }
-
- /**
- * Un-Register all the supported RPCs from clustered routing table
- */
- private void unregisterSupportedRpcs(){
- LOG.debug("removing all supported rpcs to routing table");
- Set<QName> currentlySupported = brokerSession.getSupportedRpcs();
- for (QName rpc : currentlySupported) {
- rpcListener.onRpcImplementationRemoved(rpc);
- }
- }
-
- /**
- * Un-Register all the locally supported Routed RPCs from clustered routing table
- */
- private void unregisterSupportedRoutedRpcs(){
-
- //TODO: remove all routed RPCs as well
+ @Override
+ public void onGlobalContextUpdated(SchemaContext schemaContext) {
+ this.schemaContext = schemaContext;
+ rpcManager.tell(new UpdateSchemaContext(schemaContext), null);
}
}
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;
}
}
*/
package org.opendaylight.controller.remote.rpc;
+import com.google.common.base.Preconditions;
import org.opendaylight.controller.sal.connector.api.RpcRouter;
import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
import java.io.Serializable;
-public class RouteIdentifierImpl implements RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier>,Serializable {
+public class RouteIdentifierImpl implements RpcRouter.RouteIdentifier<QName, QName, YangInstanceIdentifier>,Serializable {
private static final long serialVersionUID = 1L;
- private QName context;
- private QName type;
- private InstanceIdentifier route;
+ private final QName context;
+ private final QName type;
+ private final YangInstanceIdentifier route;
- public RouteIdentifierImpl(QName context, QName type, InstanceIdentifier route) {
+ public RouteIdentifierImpl(final QName context, final QName type, final YangInstanceIdentifier route) {
+ Preconditions.checkNotNull(type, "Rpc type should not be null");
this.context = context;
this.type = type;
this.route = route;
}
@Override
- public InstanceIdentifier getRoute() {
+ public YangInstanceIdentifier getRoute() {
return this.route;
}
import akka.actor.ActorRef;
+import com.google.common.base.Preconditions;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChange;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
import org.opendaylight.controller.remote.rpc.messages.AddRoutedRpc;
import org.opendaylight.controller.remote.rpc.messages.RemoveRoutedRpc;
+import org.opendaylight.controller.remote.rpc.utils.ActorUtil;
import org.opendaylight.controller.sal.connector.api.RpcRouter;
import org.opendaylight.controller.sal.core.api.RpcRoutingContext;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.Set;
-public class RoutedRpcListener implements RouteChangeListener<RpcRoutingContext, InstanceIdentifier>{
+public class RoutedRpcListener implements RouteChangeListener<RpcRoutingContext, YangInstanceIdentifier>{
private static final Logger LOG = LoggerFactory.getLogger(RoutedRpcListener.class);
private final ActorRef rpcRegistry;
private final String actorPath;
public RoutedRpcListener(ActorRef rpcRegistry, String actorPath) {
+ Preconditions.checkNotNull(rpcRegistry, "rpc registry actor should not be null");
+ Preconditions.checkNotNull(actorPath, "actor path of rpc broker on current node should not be null");
+
this.rpcRegistry = rpcRegistry;
this.actorPath = actorPath;
}
@Override
- public void onRouteChange(RouteChange<RpcRoutingContext, InstanceIdentifier> routeChange) {
- Map<RpcRoutingContext, Set<InstanceIdentifier>> announcements = routeChange.getAnnouncements();
+ public void onRouteChange(RouteChange<RpcRoutingContext, YangInstanceIdentifier> routeChange) {
+ Map<RpcRoutingContext, Set<YangInstanceIdentifier>> announcements = routeChange.getAnnouncements();
announce(getRouteIdentifiers(announcements));
- Map<RpcRoutingContext, Set<InstanceIdentifier>> removals = routeChange.getRemovals();
+ Map<RpcRoutingContext, Set<YangInstanceIdentifier>> removals = routeChange.getRemovals();
remove(getRouteIdentifiers(removals));
}
* @param changes
* @return
*/
- private Set<RpcRouter.RouteIdentifier<?, ?, ?>> getRouteIdentifiers(Map<RpcRoutingContext, Set<InstanceIdentifier>> changes) {
+ private Set<RpcRouter.RouteIdentifier<?, ?, ?>> getRouteIdentifiers(Map<RpcRoutingContext, Set<YangInstanceIdentifier>> changes) {
RouteIdentifierImpl routeId = null;
Set<RpcRouter.RouteIdentifier<?, ?, ?>> routeIdSet = new HashSet<>();
for (RpcRoutingContext context : changes.keySet()){
- for (InstanceIdentifier instanceId : changes.get(context)){
+ for (YangInstanceIdentifier instanceId : changes.get(context)){
routeId = new RouteIdentifierImpl(null, context.getRpc(), instanceId);
routeIdSet.add(routeId);
}
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;
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;
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) {
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);
try {
Future<RpcResult<CompositeNode>> rpc = brokerSession.rpc(msg.getRpc(), XmlUtils.inputXmlToCompositeNode(msg.getRpc(), msg.getInputCompositeNode(), schemaContext));
RpcResult<CompositeNode> rpcResult = rpc != null ? rpc.get():null;
-
CompositeNode result = rpcResult != null ? rpcResult.getResult() : null;
getSender().tell(new RpcResponse(XmlUtils.outputCompositeNodeToXml(result, schemaContext)), self());
} catch (Exception e) {
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;
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.remote.rpc;
+
+
+import akka.actor.ActorRef;
+import akka.actor.OneForOneStrategy;
+import akka.actor.Props;
+import akka.actor.SupervisorStrategy;
+import akka.japi.Creator;
+import akka.japi.Function;
+import org.opendaylight.controller.remote.rpc.messages.UpdateSchemaContext;
+import org.opendaylight.controller.remote.rpc.registry.ClusterWrapper;
+import org.opendaylight.controller.remote.rpc.registry.RpcRegistry;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import scala.concurrent.duration.Duration;
+import java.util.Set;
+
+/**
+ * This class acts as a supervisor, creates all the actors, resumes them, if an exception is thrown.
+ *
+ * It also starts the rpc listeners
+ */
+
+public class RpcManager extends AbstractUntypedActor {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RpcManager.class);
+
+ private SchemaContext schemaContext;
+ private final ClusterWrapper clusterWrapper;
+ private ActorRef rpcBroker;
+ private ActorRef rpcRegistry;
+ private final Broker.ProviderSession brokerSession;
+ private RpcListener rpcListener;
+ private RoutedRpcListener routeChangeListener;
+ private RemoteRpcImplementation rpcImplementation;
+ private final RpcProvisionRegistry rpcProvisionRegistry;
+
+ private RpcManager(ClusterWrapper clusterWrapper, SchemaContext schemaContext,
+ Broker.ProviderSession brokerSession, RpcProvisionRegistry rpcProvisionRegistry) {
+ this.clusterWrapper = clusterWrapper;
+ this.schemaContext = schemaContext;
+ this.brokerSession = brokerSession;
+ this.rpcProvisionRegistry = rpcProvisionRegistry;
+
+ createRpcActors();
+ startListeners();
+ }
+
+
+ public static Props props(final ClusterWrapper clusterWrapper, final SchemaContext schemaContext,
+ final Broker.ProviderSession brokerSession, final RpcProvisionRegistry rpcProvisionRegistry) {
+ return Props.create(new Creator<RpcManager>() {
+ @Override
+ public RpcManager create() throws Exception {
+ return new RpcManager(clusterWrapper, schemaContext, brokerSession, rpcProvisionRegistry);
+ }
+ });
+ }
+
+ private void createRpcActors() {
+ LOG.debug("Create rpc registry and broker actors");
+
+ rpcRegistry = getContext().actorOf(RpcRegistry.props(clusterWrapper), "rpc-registry");
+ rpcBroker = getContext().actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext), "rpc-broker");
+ }
+
+ private void startListeners() {
+ LOG.debug("Registers rpc listeners");
+
+ String rpcBrokerPath = clusterWrapper.getAddress().toString() + "/user/rpc/rpc-broker";
+ rpcListener = new RpcListener(rpcRegistry, rpcBrokerPath);
+ routeChangeListener = new RoutedRpcListener(rpcRegistry, rpcBrokerPath);
+ rpcImplementation = new RemoteRpcImplementation(rpcBroker, schemaContext);
+
+ brokerSession.addRpcRegistrationListener(rpcListener);
+ rpcProvisionRegistry.registerRouteChangeListener(routeChangeListener);
+ rpcProvisionRegistry.setRoutedRpcDefaultDelegate(rpcImplementation);
+ announceSupportedRpcs();
+ }
+
+ /**
+ * Add all the locally registered RPCs in the clustered routing table
+ */
+ private void announceSupportedRpcs(){
+ LOG.debug("Adding all supported rpcs to routing table");
+ Set<QName> currentlySupported = brokerSession.getSupportedRpcs();
+ for (QName rpc : currentlySupported) {
+ rpcListener.onRpcImplementationAdded(rpc);
+ }
+ }
+
+
+ @Override
+ protected void handleReceive(Object message) throws Exception {
+ if(message instanceof UpdateSchemaContext) {
+ updateSchemaContext((UpdateSchemaContext) message);
+ }
+
+ }
+
+ private void updateSchemaContext(UpdateSchemaContext message) {
+ this.schemaContext = message.getSchemaContext();
+ }
+
+ @Override
+ public SupervisorStrategy supervisorStrategy() {
+ return new OneForOneStrategy(10, Duration.create("1 minute"),
+ new Function<Throwable, SupervisorStrategy.Directive>() {
+ @Override
+ public SupervisorStrategy.Directive apply(Throwable t) {
+ return SupervisorStrategy.resume();
+ }
+ }
+ );
+ }
+}
+++ /dev/null
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.remote.rpc;
-
-import com.google.common.base.Optional;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.Node;
-import org.opendaylight.yangtools.yang.data.api.SimpleNode;
-import org.opendaylight.yangtools.yang.data.impl.XmlTreeBuilder;
-import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.controller.netconf.util.xml.XmlUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.w3c.dom.Document;
-import org.xml.sax.SAXException;
-
-import javax.activation.UnsupportedDataTypeException;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.util.Set;
-
-public class XmlUtils {
-
- private static final Logger LOG = LoggerFactory.getLogger(XmlUtils.class);
-
- public static String inputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){
- if (cNode == null) return new String();
-
- //Document domTree = NodeUtils.buildShadowDomTree(cNode);
- Document domTree = null;
- try {
- Set<RpcDefinition> rpcs = schemaContext.getOperations();
- for(RpcDefinition rpc : rpcs) {
- if(rpc.getQName().equals(cNode.getNodeType())){
- domTree = XmlDocumentUtils.toDocument(cNode, rpc.getInput(), XmlDocumentUtils.defaultValueCodecProvider());
- break;
- }
- }
-
- } catch (UnsupportedDataTypeException e) {
- LOG.error("Error during translation of CompositeNode to Document", e);
- }
- return domTransformer(domTree);
- }
-
- public static String outputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){
- if (cNode == null) return new String();
-
- //Document domTree = NodeUtils.buildShadowDomTree(cNode);
- Document domTree = null;
- try {
- Set<RpcDefinition> rpcs = schemaContext.getOperations();
- for(RpcDefinition rpc : rpcs) {
- if(rpc.getQName().equals(cNode.getNodeType())){
- domTree = XmlDocumentUtils.toDocument(cNode, rpc.getInput(), XmlDocumentUtils.defaultValueCodecProvider());
- break;
- }
- }
-
- } catch (UnsupportedDataTypeException e) {
- LOG.error("Error during translation of CompositeNode to Document", e);
- }
- return domTransformer(domTree);
- }
-
- private static String domTransformer(Document domTree) {
- StringWriter writer = new StringWriter();
- try {
- TransformerFactory tf = TransformerFactory.newInstance();
- Transformer transformer = tf.newTransformer();
- transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
- transformer.transform(new DOMSource(domTree), new StreamResult(writer));
- } catch (TransformerException e) {
-
- LOG.error("Error during translation of Document to OutputStream", e);
- }
- LOG.debug("compositeNodeToXml " + writer.toString());
-
- return writer.toString();
- }
-
- public static CompositeNode xmlToCompositeNode(String xml){
- if (xml==null || xml.length()==0) return null;
-
- Node<?> dataTree;
- try {
- dataTree = XmlTreeBuilder.buildDataTree(new ByteArrayInputStream(xml.getBytes()));
- } catch (XMLStreamException e) {
- LOG.error("Error during building data tree from XML", e);
- return null;
- }
- if (dataTree == null) {
- LOG.error("data tree is null");
- return null;
- }
- if (dataTree instanceof SimpleNode) {
- LOG.error("RPC XML was resolved as SimpleNode");
- return null;
- }
- return (CompositeNode) dataTree;
- }
-
- public static CompositeNode inputXmlToCompositeNode(QName rpc, String xml, SchemaContext schemaContext){
- if (xml==null || xml.length()==0) return null;
-
- Node<?> dataTree = null;
- try {
-
- Document doc = XmlUtil.readXmlToDocument(xml);
- LOG.debug("xmlToCompositeNode Document is " + xml );
- Set<RpcDefinition> rpcs = schemaContext.getOperations();
- for(RpcDefinition rpcDef : rpcs) {
- if(rpcDef.getQName().equals(rpc)){
- dataTree = XmlDocumentUtils.toDomNode(doc.getDocumentElement(), Optional.<DataSchemaNode>of(rpcDef.getInput()), Optional.of(XmlDocumentUtils.defaultValueCodecProvider()));
- break;
- }
- }
- } catch (SAXException e) {
- LOG.error("Error during building data tree from XML", e);
- } catch (IOException e) {
- LOG.error("Error during building data tree from XML", e);
- }
-
- LOG.debug("xmlToCompositeNode " + dataTree.toString());
- return (CompositeNode) dataTree;
- }
-}
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;
public class AddRoutedRpc implements Serializable {
- Set<RpcRouter.RouteIdentifier<?, ?, ?>> announcements;
- String actorPath;
+ private final Set<RpcRouter.RouteIdentifier<?, ?, ?>> announcements;
+ private final String actorPath;
+
+ public AddRoutedRpc(final Set<RpcRouter.RouteIdentifier<?, ?, ?>> announcements, final String actorPath) {
+ Preconditions.checkNotNull(announcements, "Route identifier should not be null");
+ Preconditions.checkNotNull(actorPath, "Actor path should not be null");
- public AddRoutedRpc(Set<RpcRouter.RouteIdentifier<?, ?, ?>> announcements, String actorPath) {
this.announcements = announcements;
this.actorPath = actorPath;
}
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;
}
*/
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;
}
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;
}
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;
}
public class GetRoutedRpcReply implements Serializable {
- private String routePath;
+ private final String routePath;
- public GetRoutedRpcReply(String routePath) {
+ public GetRoutedRpcReply(final String routePath) {
this.routePath = routePath;
}
*/
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;
}
public class GetRpcReply implements Serializable {
- private String routePath;
+ private final String routePath;
- public GetRpcReply(String routePath) {
+ public GetRpcReply(final String routePath) {
this.routePath = routePath;
}
*/
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;
}
return rpc;
}
- public InstanceIdentifier getIdentifier() {
+ public YangInstanceIdentifier getIdentifier() {
return identifier;
}
*/
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;
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;
}
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;
public class RemoveRoutedRpc implements Serializable {
- Set<RpcRouter.RouteIdentifier<?, ?, ?>> announcements;
- String actorPath;
+ private final Set<RpcRouter.RouteIdentifier<?, ?, ?>> announcements;
+ private final String actorPath;
+
+ public RemoveRoutedRpc(final Set<RpcRouter.RouteIdentifier<?, ?, ?>> announcements, final String actorPath) {
+ Preconditions.checkNotNull(announcements, "Route identifier should not be null");
+ Preconditions.checkNotNull(actorPath, "Actor path should not be null");
- public RemoveRoutedRpc(Set<RpcRouter.RouteIdentifier<?, ?, ?>> announcements, String actorPath) {
this.announcements = announcements;
this.actorPath = actorPath;
}
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;
}
import java.util.Map;
public class RoutingTableData implements Serializable {
- private Map<RpcRouter.RouteIdentifier<?, ?, ?>, String> rpcMap;
- private Map<RpcRouter.RouteIdentifier<?, ?, ?>, LinkedHashSet<String>> routedRpcMap;
+ private final Map<RpcRouter.RouteIdentifier<?, ?, ?>, String> rpcMap;
+ private final Map<RpcRouter.RouteIdentifier<?, ?, ?>, LinkedHashSet<String>> routedRpcMap;
- public RoutingTableData(Map<RpcRouter.RouteIdentifier<?, ?, ?>, String> rpcMap,
- Map<RpcRouter.RouteIdentifier<?, ?, ?>, LinkedHashSet<String>> routedRpcMap) {
+ public RoutingTableData(final Map<RpcRouter.RouteIdentifier<?, ?, ?>, String> rpcMap,
+ final Map<RpcRouter.RouteIdentifier<?, ?, ?>, LinkedHashSet<String>> routedRpcMap) {
this.rpcMap = rpcMap;
this.routedRpcMap = routedRpcMap;
}
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;
}
--- /dev/null
+/*
+ * 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;
+ }
+}
* 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;
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.remote.rpc.utils;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Element;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class InstanceIdentifierForXmlCodec {
+ private static final Pattern PREDICATE_PATTERN = Pattern.compile("\\[(.*?)\\]");
+ private static final Splitter SLASH_SPLITTER = Splitter.on('/');
+ private static final Splitter COLON_SPLITTER = Splitter.on(':');
+ private static final Splitter AT_SPLITTER = Splitter.on('@');
+ private static final Logger logger = LoggerFactory.getLogger(InstanceIdentifierForXmlCodec.class);
+
+ private InstanceIdentifierForXmlCodec() {
+ throw new UnsupportedOperationException("Utility class");
+ }
+
+ public static YangInstanceIdentifier deserialize(final Element element, final SchemaContext schemaContext) {
+ Preconditions.checkNotNull(element, "Value of element for deserialization can't be null");
+ Preconditions.checkNotNull(schemaContext,
+ "Schema context for deserialization of instance identifier type can't be null");
+
+ final String valueTrimmed = element.getTextContent().trim();
+ logger.debug("Instance identifier derserialize: splitting the text {} with Slash to find path arguments", valueTrimmed);
+ final Iterator<String> xPathParts = SLASH_SPLITTER.split(valueTrimmed).iterator();
+
+ // must be at least "/pr:node"
+ if (!xPathParts.hasNext() || !xPathParts.next().isEmpty() || !xPathParts.hasNext()) {
+ logger.debug("Instance identifier derserialize: No path argument found for element.");
+ return null;
+ }
+
+ List<PathArgument> result = new ArrayList<>();
+ while (xPathParts.hasNext()) {
+ String xPathPartTrimmed = xPathParts.next().trim();
+
+ PathArgument pathArgument = toPathArgument(xPathPartTrimmed, element, schemaContext);
+ if (pathArgument != null) {
+ result.add(pathArgument);
+ }
+ }
+ return YangInstanceIdentifier.create(result);
+ }
+
+ public static Element serialize(final YangInstanceIdentifier id, final Element element) {
+ Preconditions.checkNotNull(id, "Variable should contain instance of instance identifier and can't be null");
+ Preconditions.checkNotNull(element, "DOM element can't be null");
+
+ final RandomPrefix prefixes = new RandomPrefix();
+ final String str = XmlUtils.encodeIdentifier(prefixes, id);
+
+ for (Entry<URI, String> e: prefixes.getPrefixes()) {
+ element.setAttribute("xmlns:" + e.getValue(), e.getKey().toString());
+ }
+ element.setTextContent(str);
+ return element;
+ }
+
+ private static String getIdAndPrefixAsStr(final String pathPart) {
+ int predicateStartIndex = pathPart.indexOf('[');
+ return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex);
+ }
+
+ private static PathArgument toPathArgument(final String xPathArgument, final Element element, final SchemaContext schemaContext) {
+ final QName mainQName = toIdentity(xPathArgument, element, schemaContext);
+
+ // predicates
+ final Matcher matcher = PREDICATE_PATTERN.matcher(xPathArgument);
+ final Map<QName, Object> predicates = new HashMap<>();
+ QName currentQName = mainQName;
+
+ while (matcher.find()) {
+ final String predicateStr = matcher.group(1).trim();
+ final int indexOfEqualityMark = predicateStr.indexOf('=');
+ if (indexOfEqualityMark != -1) {
+ final Object predicateValue = toPredicateValue(predicateStr.substring(indexOfEqualityMark + 1));
+ if (predicateValue == null) {
+ return null;
+ }
+
+ if (predicateStr.charAt(0) != '.') {
+ // target is not a leaf-list
+ currentQName = toIdentity(predicateStr.substring(0, indexOfEqualityMark), element, schemaContext);
+ if (currentQName == null) {
+ return null;
+ }
+ }
+ logger.debug("Instance identifier derserialize: finding predicates of node {}", predicateValue);
+ predicates.put(currentQName, predicateValue);
+ }
+ }
+
+ if (predicates.isEmpty()) {
+ return new YangInstanceIdentifier.NodeIdentifier(mainQName);
+ } else {
+ return new YangInstanceIdentifier.NodeIdentifierWithPredicates(mainQName, predicates);
+ }
+
+ }
+
+ public static QName toIdentity(final String xPathArgument, final Element element, final SchemaContext schemaContext) {
+ final String xPathPartTrimmed = getIdAndPrefixAsStr(xPathArgument).trim();
+ final Iterator<String> it = COLON_SPLITTER.split(xPathPartTrimmed).iterator();
+
+ // Empty string
+ if (!it.hasNext()) {
+ return null;
+ }
+
+ final String prefix = it.next().trim();
+ if (prefix.isEmpty()) {
+ return null;
+ }
+
+ // it is not "prefix:value"
+ if (!it.hasNext()) {
+ return null;
+ }
+
+ final String identifier = it.next().trim();
+ if (identifier.isEmpty()) {
+ return null;
+ }
+
+ URI namespace = null;
+ String namespaceStr = null;
+ try {
+ namespaceStr = element.getAttribute("xmlns:"+prefix);
+ namespace = new URI(namespaceStr);
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException("It wasn't possible to convert " + namespaceStr + " to URI object.");
+ } catch (NullPointerException e) {
+ throw new IllegalArgumentException("I wasn't possible to get namespace for prefix " + prefix);
+ }
+
+ Module module = schemaContext.findModuleByNamespaceAndRevision(namespace, null);
+ return QName.create(module.getQNameModule(), identifier);
+ }
+
+ private static String trimIfEndIs(final String str, final char end) {
+ final int l = str.length() - 1;
+ if (str.charAt(l) != end) {
+ return null;
+ }
+
+ return str.substring(1, l);
+ }
+
+ private static Object toPredicateValue(final String predicatedValue) {
+ logger.debug("Instance identifier derserialize: converting the predicate vstring to object {}", predicatedValue);
+ final String predicatedValueTrimmed = predicatedValue.trim();
+ if (predicatedValue.isEmpty()) {
+ return null;
+ }
+ String updatedValue = null;
+ switch (predicatedValueTrimmed.charAt(0)) {
+ case '"':
+ updatedValue = trimIfEndIs(predicatedValueTrimmed, '"');
+ break;
+ case '\'':
+ updatedValue = trimIfEndIs(predicatedValueTrimmed, '\'');
+ break;
+ default:
+ updatedValue = predicatedValueTrimmed;
+ }
+ Iterator<String> it = AT_SPLITTER.split(updatedValue).iterator();
+ // Empty string
+ if (!it.hasNext()) {
+ return null;
+ }
+
+ final String value = it.next().trim();
+ if (value.isEmpty()) {
+ return null;
+ }
+
+ if (!it.hasNext()) {
+ return value;
+ }
+
+ final String type = it.next().trim();
+ if (type.isEmpty()) {
+ return value;
+ }
+ Object predicateObject = null;
+ try {
+ logger.debug("Instance identifier derserialize: converting the predicate value {{}}to correct object type {{}}", value, type);
+ predicateObject = Class.forName(type).getConstructor(String.class).newInstance(value);
+ } catch (Exception e) {
+ logger.error("Could not convert to valid type of value", e);
+ }
+ return predicateObject;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.remote.rpc.utils;
+
+import org.opendaylight.yangtools.yang.common.QName;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * Picked from Yang tools because it was not public class
+ */
+
+final class RandomPrefix {
+ final Map<URI, String> prefixes = new HashMap<>();
+
+ Iterable<Entry<URI, String>> getPrefixes() {
+ return prefixes.entrySet();
+ }
+
+ String encodeQName(final QName qname) {
+ String prefix = prefixes.get(qname.getNamespace());
+ if (prefix == null) {
+ prefix = qname.getPrefix();
+ if (prefix == null || prefix.isEmpty() || prefixes.containsValue(prefix)) {
+ final ThreadLocalRandom random = ThreadLocalRandom.current();
+ do {
+ final StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < 4; i++) {
+ sb.append((char)('a' + random.nextInt(25)));
+ }
+
+ prefix = sb.toString();
+ } while (prefixes.containsValue(prefix));
+ }
+
+ prefixes.put(qname.getNamespace(), prefix);
+ }
+
+ return prefix + ':' + qname.getLocalName();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.remote.rpc.utils;
+
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.ModifyAction;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import javax.activation.UnsupportedDataTypeException;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.dom.DOMResult;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkState;
+
+public class XmlDocumentUtils {
+ private static class ElementWithSchemaContext {
+ Element element;
+ SchemaContext schemaContext;
+
+ ElementWithSchemaContext(final Element element,final SchemaContext schemaContext) {
+ this.schemaContext = schemaContext;
+ this.element = element;
+ }
+
+ Element getElement() {
+ return element;
+ }
+
+ SchemaContext getSchemaContext() {
+ return schemaContext;
+ }
+ }
+
+ public static final QName OPERATION_ATTRIBUTE_QNAME = QName.create(URI.create("urn:ietf:params:xml:ns:netconf:base:1.0"), null, "operation");
+ private static final Logger logger = LoggerFactory.getLogger(XmlDocumentUtils.class);
+ private static final XMLOutputFactory FACTORY = XMLOutputFactory.newFactory();
+
+ /**
+ * Converts Data DOM structure to XML Document for specified XML Codec Provider and corresponding
+ * Data Node Container schema. The CompositeNode data parameter enters as root of Data DOM tree and will
+ * be transformed to root in XML Document. Each element of Data DOM tree is compared against specified Data
+ * Node Container Schema and transformed accordingly.
+ *
+ * @param data Data DOM root element
+ * @param schema Data Node Container Schema
+ * @param codecProvider XML Codec Provider
+ * @return new instance of XML Document
+ * @throws javax.activation.UnsupportedDataTypeException
+ */
+ public static Document toDocument(final CompositeNode data, final DataNodeContainer schema, final XmlCodecProvider codecProvider)
+ throws UnsupportedDataTypeException {
+ Preconditions.checkNotNull(data);
+ Preconditions.checkNotNull(schema);
+
+ if (!(schema instanceof ContainerSchemaNode || schema instanceof ListSchemaNode)) {
+ throw new UnsupportedDataTypeException("Schema can be ContainerSchemaNode or ListSchemaNode. Other types are not supported yet.");
+ }
+
+ final DOMResult result = new DOMResult(getDocument());
+ try {
+ final XMLStreamWriter writer = FACTORY.createXMLStreamWriter(result);
+ XmlStreamUtils.create(codecProvider).writeDocument(writer, data, (SchemaNode)schema);
+ writer.close();
+ return (Document)result.getNode();
+ } catch (XMLStreamException e) {
+ logger.error("Failed to serialize data {}", data, e);
+ return null;
+ }
+ }
+
+ public static Document getDocument() {
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ Document doc = null;
+ try {
+ DocumentBuilder bob = dbf.newDocumentBuilder();
+ doc = bob.newDocument();
+ } catch (ParserConfigurationException e) {
+ throw new RuntimeException(e);
+ }
+ return doc;
+ }
+
+
+ public static QName qNameFromElement(final Element xmlElement) {
+ String namespace = xmlElement.getNamespaceURI();
+ String localName = xmlElement.getLocalName();
+ return QName.create(namespace != null ? URI.create(namespace) : null, null, localName);
+ }
+
+ private static Node<?> toNodeWithSchema(final Element xmlElement, final DataSchemaNode schema, final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) {
+ checkQName(xmlElement, schema.getQName());
+ if (schema instanceof DataNodeContainer) {
+ return toCompositeNodeWithSchema(xmlElement, schema.getQName(), (DataNodeContainer) schema, schemaCtx);
+ } else if (schema instanceof LeafSchemaNode) {
+ return toSimpleNodeWithType(xmlElement, (LeafSchemaNode) schema, codecProvider,schemaCtx);
+ } else if (schema instanceof LeafListSchemaNode) {
+ return toSimpleNodeWithType(xmlElement, (LeafListSchemaNode) schema, codecProvider,schemaCtx);
+ }
+ return null;
+ }
+
+
+
+ private static Node<?> toSimpleNodeWithType(final Element xmlElement, final LeafSchemaNode schema,
+ final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) {
+ TypeDefinitionAwareCodec<? extends Object, ? extends TypeDefinition<?>> codec = codecProvider.codecFor(schema.getType());
+ String text = xmlElement.getTextContent();
+ Object value = null;
+ if (codec != null) {
+ logger.debug("toSimpleNodeWithType: found codec, deserializing text {}", text);
+ value = codec.deserialize(text);
+ }
+
+ final TypeDefinition<?> baseType = XmlUtils.resolveBaseTypeFrom(schema.getType());
+ if (baseType instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifier) {
+ logger.debug("toSimpleNodeWithType: base type of node is instance identifier, deserializing element", xmlElement);
+ value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx);
+
+ } else if(baseType instanceof IdentityrefTypeDefinition){
+ logger.debug("toSimpleNodeWithType: base type of node is IdentityrefTypeDefinition, deserializing element", xmlElement);
+ value = InstanceIdentifierForXmlCodec.toIdentity(xmlElement.getTextContent(), xmlElement, schemaCtx);
+
+ }
+
+ if (value == null) {
+ logger.debug("toSimpleNodeWithType: no type found for element, returning just the text string value of element {}", xmlElement);
+ value = xmlElement.getTextContent();
+ }
+
+ Optional<ModifyAction> modifyAction = getModifyOperationFromAttributes(xmlElement);
+ return new SimpleNodeTOImpl<>(schema.getQName(), null, value, modifyAction.orNull());
+ }
+
+ private static Node<?> toSimpleNodeWithType(final Element xmlElement, final LeafListSchemaNode schema,
+ final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) {
+ TypeDefinitionAwareCodec<? extends Object, ? extends TypeDefinition<?>> codec = codecProvider.codecFor(schema.getType());
+ String text = xmlElement.getTextContent();
+ Object value = null;
+ if (codec != null) {
+ logger.debug("toSimpleNodeWithType: found codec, deserializing text {}", text);
+ value = codec.deserialize(text);
+ }
+
+ if (schema.getType() instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifier) {
+ logger.debug("toSimpleNodeWithType: base type of node is instance identifier, deserializing element", xmlElement);
+ value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx);
+ }
+
+ if (value == null) {
+ logger.debug("toSimpleNodeWithType: no type found for element, returning just the text string value of element {}", xmlElement);
+ value = xmlElement.getTextContent();
+ }
+
+ Optional<ModifyAction> modifyAction = getModifyOperationFromAttributes(xmlElement);
+ return new SimpleNodeTOImpl<>(schema.getQName(), null, value, modifyAction.orNull());
+ }
+
+ private static Node<?> toCompositeNodeWithSchema(final Element xmlElement, final QName qName, final DataNodeContainer schema,
+ final SchemaContext schemaCtx) {
+ List<Node<?>> values = toDomNodes(xmlElement, Optional.fromNullable(schema.getChildNodes()),schemaCtx);
+ Optional<ModifyAction> modifyAction = getModifyOperationFromAttributes(xmlElement);
+ return ImmutableCompositeNode.create(qName, values, modifyAction.orNull());
+ }
+
+ private static Optional<ModifyAction> getModifyOperationFromAttributes(final Element xmlElement) {
+ Attr attributeNodeNS = xmlElement.getAttributeNodeNS(OPERATION_ATTRIBUTE_QNAME.getNamespace().toString(), OPERATION_ATTRIBUTE_QNAME.getLocalName());
+ if(attributeNodeNS == null) {
+ return Optional.absent();
+ }
+
+ ModifyAction action = ModifyAction.fromXmlValue(attributeNodeNS.getValue());
+ Preconditions.checkArgument(action.isOnElementPermitted(), "Unexpected operation %s on %s", action, xmlElement);
+
+ return Optional.of(action);
+ }
+
+ private static void checkQName(final Element xmlElement, final QName qName) {
+ checkState(Objects.equal(xmlElement.getNamespaceURI(), qName.getNamespace().toString()));
+ checkState(qName.getLocalName().equals(xmlElement.getLocalName()));
+ }
+
+ public static final Optional<DataSchemaNode> findFirstSchema(final QName qname, final Collection<DataSchemaNode> dataSchemaNode) {
+ if (dataSchemaNode != null && !dataSchemaNode.isEmpty() && qname != null) {
+ for (DataSchemaNode dsn : dataSchemaNode) {
+ if (qname.isEqualWithoutRevision(dsn.getQName())) {
+ return Optional.<DataSchemaNode> of(dsn);
+ } else if (dsn instanceof ChoiceNode) {
+ for (ChoiceCaseNode choiceCase : ((ChoiceNode) dsn).getCases()) {
+ Optional<DataSchemaNode> foundDsn = findFirstSchema(qname, choiceCase.getChildNodes());
+ if (foundDsn != null && foundDsn.isPresent()) {
+ return foundDsn;
+ }
+ }
+ }
+ }
+ }
+ return Optional.absent();
+ }
+
+ private static Node<?> toDomNode(Element element) {
+ QName qname = qNameFromElement(element);
+
+ ImmutableList.Builder<Node<?>> values = ImmutableList.<Node<?>> builder();
+ NodeList nodes = element.getChildNodes();
+ boolean isSimpleObject = true;
+ String value = null;
+ for (int i = 0; i < nodes.getLength(); i++) {
+ org.w3c.dom.Node child = nodes.item(i);
+ if (child instanceof Element) {
+ isSimpleObject = false;
+ values.add(toDomNode((Element) child));
+ }
+ if (isSimpleObject && child instanceof org.w3c.dom.Text) {
+ value = element.getTextContent();
+ if (!Strings.isNullOrEmpty(value)) {
+ isSimpleObject = true;
+ }
+ }
+ }
+ if (isSimpleObject) {
+ return new SimpleNodeTOImpl<>(qname, null, value);
+ }
+ return ImmutableCompositeNode.create(qname, values.build());
+ }
+
+ public static List<Node<?>> toDomNodes(final Element element, final Optional<Collection<DataSchemaNode>> context,SchemaContext schemaCtx) {
+ return forEachChild(element.getChildNodes(),schemaCtx, new Function<ElementWithSchemaContext, Optional<Node<?>>>() {
+
+ @Override
+ public Optional<Node<?>> apply(ElementWithSchemaContext input) {
+ if (context.isPresent()) {
+ QName partialQName = qNameFromElement(input.getElement());
+ Optional<DataSchemaNode> schemaNode = XmlDocumentUtils.findFirstSchema(partialQName, context.get());
+ if (schemaNode.isPresent()) {
+ return Optional.<Node<?>> fromNullable(//
+ toNodeWithSchema(input.getElement(), schemaNode.get(), XmlDocumentUtils.defaultValueCodecProvider(),input.getSchemaContext()));
+ }
+ }
+ return Optional.<Node<?>> fromNullable(toDomNode(input.getElement()));
+ }
+
+ });
+
+ }
+
+ private static final <T> List<T> forEachChild(final NodeList nodes, final SchemaContext schemaContext, final Function<ElementWithSchemaContext, Optional<T>> forBody) {
+ final int l = nodes.getLength();
+ if (l == 0) {
+ return ImmutableList.of();
+ }
+
+ final List<T> list = new ArrayList<>(l);
+ for (int i = 0; i < l; i++) {
+ org.w3c.dom.Node child = nodes.item(i);
+ if (child instanceof Element) {
+ Optional<T> result = forBody.apply(new ElementWithSchemaContext((Element) child,schemaContext));
+ if (result.isPresent()) {
+ list.add(result.get());
+ }
+ }
+ }
+ return ImmutableList.copyOf(list);
+ }
+
+ public static final XmlCodecProvider defaultValueCodecProvider() {
+ return XmlUtils.DEFAULT_XML_CODEC_PROVIDER;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.remote.rpc.utils;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.AttributesContainer;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider;
+import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import java.net.URI;
+import java.util.Map.Entry;
+
+/**
+ * Utility class for bridging JAXP Stream and YANG Data APIs. Note that the definition of this class
+ * by no means final and subject to change as more functionality is centralized here.
+ */
+@Beta
+public class XmlStreamUtils {
+ private static final Logger LOG = LoggerFactory.getLogger(XmlStreamUtils.class);
+ private final XmlCodecProvider codecProvider;
+
+ protected XmlStreamUtils(final XmlCodecProvider codecProvider) {
+ this.codecProvider = Preconditions.checkNotNull(codecProvider);
+ }
+
+ /**
+ * Create a new instance encapsulating a particular codec provider.
+ *
+ * @param codecProvider XML codec provider
+ * @return A new instance
+ */
+ public static XmlStreamUtils create(final XmlCodecProvider codecProvider) {
+ return new XmlStreamUtils(codecProvider);
+ }
+
+ /**
+ * Check if a particular data element can be emitted as an empty element, bypassing value encoding. This
+ * functionality is optional, as valid XML stream is produced even if start/end element is produced unconditionally.
+ *
+ * @param data Data node
+ * @return True if the data node will result in empty element body.
+ */
+ public static boolean isEmptyElement(final Node<?> data) {
+ if (data == null) {
+ return true;
+ }
+
+ if (data instanceof CompositeNode) {
+ return ((CompositeNode) data).getValue().isEmpty();
+ }
+ if (data instanceof SimpleNode) {
+ return data.getValue() == null;
+ }
+
+ // Safe default
+ return false;
+ }
+
+ /**
+ * Write an InstanceIdentifier into the output stream. Calling corresponding {@link javax.xml.stream.XMLStreamWriter#writeStartElement(String)}
+ * and {@link javax.xml.stream.XMLStreamWriter#writeEndElement()} is the responsibility of the caller.
+ *
+ * @param writer XML Stream writer
+ * @param id InstanceIdentifier
+ * @throws javax.xml.stream.XMLStreamException
+ */
+ public static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull YangInstanceIdentifier id) throws XMLStreamException {
+ Preconditions.checkNotNull(writer, "Writer may not be null");
+ Preconditions.checkNotNull(id, "Variable should contain instance of instance identifier and can't be null");
+ LOG.debug("Writing Instance identifier with Random prefix");
+ final RandomPrefix prefixes = new RandomPrefix();
+ final String str = XmlUtils.encodeIdentifier(prefixes, id);
+
+ for (Entry<URI, String> e: prefixes.getPrefixes()) {
+ writer.writeNamespace(e.getValue(), e.getKey().toString());
+ }
+ LOG.debug("Instance identifier with Random prefix is now {}", str);
+ writer.writeCharacters(str);
+ }
+
+ /**
+ * Write a full XML document corresponding to a CompositeNode into an XML stream writer.
+ *
+ * @param writer XML Stream writer
+ * @param data data node
+ * @param schema corresponding schema node, may be null
+ * @throws javax.xml.stream.XMLStreamException if an encoding problem occurs
+ */
+ public void writeDocument(final @Nonnull XMLStreamWriter writer, final @Nonnull CompositeNode data, final @Nullable SchemaNode schema) throws XMLStreamException {
+ // final Boolean repairing = (Boolean) writer.getProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES);
+ // Preconditions.checkArgument(repairing == true, "XML Stream Writer has to be repairing namespaces");
+
+ writer.writeStartDocument();
+ writeElement(writer, data, schema);
+ writer.writeEndDocument();
+ writer.flush();
+ }
+
+
+ /**
+ * Write an element into a XML stream writer. This includes the element start/end tags and
+ * the value of the element.
+ *
+ * @param writer XML Stream writer
+ * @param data data node
+ * @param schema Schema node
+ * @throws javax.xml.stream.XMLStreamException if an encoding problem occurs
+ */
+ public void writeElement(final XMLStreamWriter writer, final @Nonnull Node<?> data, final SchemaNode schema) throws XMLStreamException {
+ final QName qname = data.getNodeType();
+ final String pfx = qname.getPrefix() != null ? qname.getPrefix() : "";
+ final String ns = qname.getNamespace() != null ? qname.getNamespace().toString() : "";
+
+ if (isEmptyElement(data)) {
+ writer.writeEmptyElement(pfx, qname.getLocalName(), ns);
+ return;
+ }
+
+ writer.writeStartElement(pfx, qname.getLocalName(), ns);
+ if (data instanceof AttributesContainer && ((AttributesContainer) data).getAttributes() != null) {
+ for (Entry<QName, String> attribute : ((AttributesContainer) data).getAttributes().entrySet()) {
+ writer.writeAttribute(attribute.getKey().getNamespace().toString(), attribute.getKey().getLocalName(), attribute.getValue());
+ }
+ }
+
+ if (data instanceof SimpleNode<?>) {
+ LOG.debug("writeElement : node is of type SimpleNode");
+ // Simple node
+ if (schema instanceof LeafListSchemaNode) {
+ writeValue(writer, ((LeafListSchemaNode) schema).getType(), data.getValue());
+ } else if (schema instanceof LeafSchemaNode) {
+ writeValue(writer, ((LeafSchemaNode) schema).getType(), data.getValue());
+ } else {
+ Object value = data.getValue();
+ if (value != null) {
+ writer.writeCharacters(String.valueOf(value));
+ }
+ }
+ } else {
+ LOG.debug("writeElement : node is of type CompositeNode");
+ // CompositeNode
+ for (Node<?> child : ((CompositeNode) data).getValue()) {
+ DataSchemaNode childSchema = null;
+ if (schema instanceof DataNodeContainer) {
+ childSchema = SchemaUtils.findFirstSchema(child.getNodeType(), ((DataNodeContainer) schema).getChildNodes()).orNull();
+ if (childSchema == null) {
+ LOG.debug("Probably the data node \"{}\" does not conform to schema", child == null ? "" : child.getNodeType().getLocalName());
+ }
+ }
+
+ writeElement(writer, child, childSchema);
+ }
+ }
+
+ writer.writeEndElement();
+ }
+
+ /**
+ * Write a value into a XML stream writer. This method assumes the start and end of element is
+ * emitted by the caller.
+ *
+ * @param writer XML Stream writer
+ * @param type type definitions
+ * @param value object value
+ * @throws javax.xml.stream.XMLStreamException if an encoding problem occurs
+ */
+ public void writeValue(final @Nonnull XMLStreamWriter writer, final @Nonnull TypeDefinition<?> type, final Object value) throws XMLStreamException {
+ if (value == null) {
+ LOG.debug("Value of {}:{} is null, not encoding it", type.getQName().getNamespace(), type.getQName().getLocalName());
+ return;
+ }
+
+ final TypeDefinition<?> baseType = XmlUtils.resolveBaseTypeFrom(type);
+ if (baseType instanceof IdentityrefTypeDefinition) {
+ write(writer, (IdentityrefTypeDefinition) baseType, value);
+ } else if (baseType instanceof InstanceIdentifierTypeDefinition) {
+ write(writer, (InstanceIdentifierTypeDefinition) baseType, value);
+ } else {
+ final TypeDefinitionAwareCodec<Object, ?> codec = codecProvider.codecFor(baseType);
+ String text;
+ if (codec != null) {
+ try {
+ text = codec.serialize(value);
+ } catch (ClassCastException e) {
+ LOG.error("Provided node value {} did not have type {} required by mapping. Using stream instead.", value, baseType, e);
+ text = String.valueOf(value);
+ }
+ } else {
+ LOG.error("Failed to find codec for {}, falling back to using stream", baseType);
+ text = String.valueOf(value);
+ }
+ writer.writeCharacters(text);
+ }
+ }
+
+ private static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull IdentityrefTypeDefinition type, final @Nonnull Object value) throws XMLStreamException {
+ if (value instanceof QName) {
+ final QName qname = (QName) value;
+ final String prefix;
+ if (qname.getPrefix() != null && !qname.getPrefix().isEmpty()) {
+ prefix = qname.getPrefix();
+ } else {
+ prefix = "x";
+ }
+
+ writer.writeNamespace(prefix, qname.getNamespace().toString());
+ writer.writeCharacters(prefix + ':' + qname.getLocalName());
+ } else {
+ LOG.debug("Value of {}:{} is not a QName but {}", type.getQName().getNamespace(), type.getQName().getLocalName(), value.getClass());
+ writer.writeCharacters(String.valueOf(value));
+ }
+ }
+
+ private static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull InstanceIdentifierTypeDefinition type, final @Nonnull Object value) throws XMLStreamException {
+ if (value instanceof YangInstanceIdentifier) {
+ LOG.debug("Writing InstanceIdentifier object {}", value);
+ write(writer, (YangInstanceIdentifier)value);
+ } else {
+ LOG.debug("Value of {}:{} is not an InstanceIdentifier but {}", type.getQName().getNamespace(), type.getQName().getLocalName(), value.getClass());
+ writer.writeCharacters(String.valueOf(value));
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.remote.rpc.utils;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.XmlTreeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import javax.activation.UnsupportedDataTypeException;
+import javax.annotation.Nonnull;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Common XML-related utility methods, which are not specific to a particular
+ * JAXP API.
+ */
+public class XmlUtils {
+
+ public static final XmlCodecProvider DEFAULT_XML_CODEC_PROVIDER = new XmlCodecProvider() {
+ @Override
+ public TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> codecFor(final TypeDefinition<?> baseType) {
+ return TypeDefinitionAwareCodec.from(baseType);
+ }
+ };
+
+ private XmlUtils() {
+ }
+
+ private static final String BLANK = "";
+ private static final Logger LOG = LoggerFactory.getLogger(XmlUtils.class);
+
+ /**
+ * Converts the composite node to xml using rpc input schema node
+ * @param cNode
+ * @param schemaContext
+ * @return xml String
+ */
+ public static String inputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){
+ LOG.debug("Converting input composite node to xml {}", cNode);
+ if (cNode == null) return BLANK;
+
+ if(schemaContext == null) return BLANK;
+
+ Document domTree = null;
+ try {
+ Set<RpcDefinition> rpcs = schemaContext.getOperations();
+ for(RpcDefinition rpc : rpcs) {
+ if(rpc.getQName().equals(cNode.getNodeType())){
+ LOG.debug("Found the rpc definition from schema context matching with input composite node {}", rpc.getQName());
+
+ CompositeNode inputContainer = cNode.getFirstCompositeByName(QName.create(cNode.getNodeType(), "input"));
+ domTree = XmlDocumentUtils.toDocument(inputContainer, rpc.getInput(), XmlDocumentUtils.defaultValueCodecProvider());
+
+ LOG.debug("input composite node to document conversion complete, document is {}", domTree);
+ break;
+ }
+ }
+
+ } catch (UnsupportedDataTypeException e) {
+ LOG.error("Error during translation of CompositeNode to Document", e);
+ }
+ return domTransformer(domTree);
+ }
+
+ /**
+ * Converts the composite node to xml String using rpc output schema node
+ * @param cNode
+ * @param schemaContext
+ * @return xml string
+ */
+ public static String outputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){
+ LOG.debug("Converting output composite node to xml {}", cNode);
+ if (cNode == null) return BLANK;
+
+ if(schemaContext == null) return BLANK;
+
+ Document domTree = null;
+ try {
+ Set<RpcDefinition> rpcs = schemaContext.getOperations();
+ for(RpcDefinition rpc : rpcs) {
+ if(rpc.getQName().equals(cNode.getNodeType())){
+ LOG.debug("Found the rpc definition from schema context matching with output composite node {}", rpc.getQName());
+
+ CompositeNode outputContainer = cNode.getFirstCompositeByName(QName.create(cNode.getNodeType(), "output"));
+ domTree = XmlDocumentUtils.toDocument(outputContainer, rpc.getOutput(), XmlDocumentUtils.defaultValueCodecProvider());
+
+ LOG.debug("output composite node to document conversion complete, document is {}", domTree);
+ break;
+ }
+ }
+
+ } catch (UnsupportedDataTypeException e) {
+ LOG.error("Error during translation of CompositeNode to Document", e);
+ }
+ return domTransformer(domTree);
+ }
+
+ private static String domTransformer(Document domTree) {
+ StringWriter writer = new StringWriter();
+ try {
+ TransformerFactory tf = TransformerFactory.newInstance();
+ Transformer transformer = tf.newTransformer();
+ transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+ transformer.transform(new DOMSource(domTree), new StreamResult(writer));
+ } catch (TransformerException e) {
+
+ LOG.error("Error during translation of Document to OutputStream", e);
+ }
+ LOG.debug("Document to string conversion complete, xml string is {} ", writer.toString());
+
+ return writer.toString();
+ }
+
+ public static CompositeNode xmlToCompositeNode(String xml){
+ if (xml==null || xml.length()==0) return null;
+
+ Node<?> dataTree;
+ try {
+ dataTree = XmlTreeBuilder.buildDataTree(new ByteArrayInputStream(xml.getBytes()));
+ } catch (XMLStreamException e) {
+ LOG.error("Error during building data tree from XML", e);
+ return null;
+ }
+ if (dataTree == null) {
+ LOG.error("data tree is null");
+ return null;
+ }
+ if (dataTree instanceof SimpleNode) {
+ LOG.error("RPC XML was resolved as SimpleNode");
+ return null;
+ }
+ return (CompositeNode) dataTree;
+ }
+
+ /**
+ * Converts the xml to composite node using rpc input schema node
+ * @param rpc
+ * @param xml
+ * @param schemaContext
+ * @return CompositeNode object based on the input, if any of the input parameter is null, a null object is returned
+ */
+ public static CompositeNode inputXmlToCompositeNode(QName rpc, String xml, SchemaContext schemaContext){
+ LOG.debug("Converting input xml to composite node {}", xml);
+ if (xml==null || xml.length()==0) return null;
+
+ if(rpc == null) return null;
+
+ if(schemaContext == null) return null;
+
+ CompositeNode compositeNode = null;
+ try {
+
+ Document doc = XmlUtil.readXmlToDocument(xml);
+ Set<RpcDefinition> rpcs = schemaContext.getOperations();
+ for(RpcDefinition rpcDef : rpcs) {
+ if(rpcDef.getQName().equals(rpc)){
+ LOG.debug("found the rpc definition from schema context matching rpc {}", rpc);
+
+ if(rpcDef.getInput() == null) {
+ LOG.warn("found rpc definition's input is null");
+ return null;
+ }
+
+ QName input = rpcDef.getInput().getQName();
+ NodeList nodeList = doc.getElementsByTagNameNS(input.getNamespace().toString(), "input");
+ if(nodeList == null || nodeList.getLength() < 1) {
+ LOG.warn("xml does not have input entry. {}", xml);
+ return null;
+ }
+ Element xmlData = (Element)nodeList.item(0);
+
+ List<Node<?>> dataNodes = XmlDocumentUtils.toDomNodes(xmlData,
+ Optional.of(rpcDef.getInput().getChildNodes()), schemaContext);
+
+ LOG.debug("Converted xml input to list of nodes {}", dataNodes);
+
+ final CompositeNodeBuilder<ImmutableCompositeNode> it = ImmutableCompositeNode.builder();
+ it.setQName(input);
+ it.add(ImmutableCompositeNode.create(input, dataNodes));
+ compositeNode = it.toInstance();
+ break;
+ }
+ }
+ } catch (SAXException e) {
+ LOG.error("Error during building data tree from XML", e);
+ } catch (IOException e) {
+ LOG.error("Error during building data tree from XML", e);
+ }
+
+ LOG.debug("Xml to composite node conversion complete {} ", compositeNode);
+ return compositeNode;
+ }
+
+ public static TypeDefinition<?> resolveBaseTypeFrom(final @Nonnull TypeDefinition<?> type) {
+ TypeDefinition<?> superType = type;
+ while (superType.getBaseType() != null) {
+ superType = superType.getBaseType();
+ }
+ return superType;
+ }
+
+ /**
+ * This code is picked from yangtools and modified to add type of instance identifier
+ * output of instance identifier something like below for a flow ref composite node of type instance identifier,
+ * which has path arguments with predicates, whose value is of type java.lang.short
+ * <flow-ref xmlns:bgkj="urn:opendaylight:flow:inventory" xmlns:jdlk="urn:opendaylight:inventory">
+ * /jdlk:nodes/jdlk:node[jdlk:id='openflow:205558455098190@java.lang.String']
+ * /bgkj:table[bgkj:id='3@java.lang.Short']
+ * /bgkj:flow[bgkj:id='156@java.lang.String']
+ * </flow-ref>
+ *
+ */
+
+ public static String encodeIdentifier(final RandomPrefix prefixes, final YangInstanceIdentifier id) {
+ StringBuilder textContent = new StringBuilder();
+ for (PathArgument pathArgument : id.getPathArguments()) {
+ textContent.append('/');
+ textContent.append(prefixes.encodeQName(pathArgument.getNodeType()));
+ if (pathArgument instanceof NodeIdentifierWithPredicates) {
+ Map<QName, Object> predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues();
+
+ for (QName keyValue : predicates.keySet()) {
+ Object value = predicates.get(keyValue);
+ String type = value.getClass().getName();
+ String predicateValue = String.valueOf(value);
+ textContent.append('[');
+ textContent.append(prefixes.encodeQName(keyValue));
+ textContent.append("='");
+ textContent.append(predicateValue);
+ textContent.append("@");
+ textContent.append(type);
+ textContent.append("']");
+ }
+ } else if (pathArgument instanceof NodeWithValue) {
+ textContent.append("[.='");
+ textContent.append(((NodeWithValue) pathArgument).getValue());
+ textContent.append("']");
+ }
+ }
+
+ return textContent.toString();
+ }
+}
remote {
log-remote-lifecycle-events = off
netty.tcp {
- hostname = "192.168.141.142"
+ hostname = "192.168.141.141"
port = 2551
}
}
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;
Broker.ProviderSession brokerSession = Mockito.mock(Broker.ProviderSession.class);
SchemaContext schemaContext = mock(SchemaContext.class);
ActorRef rpcBroker = system.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext));
- QName rpc = new QName(new URI("actor1"), "actor1");
- InvokeRpc invokeMsg = new InvokeRpc(rpc, null);
+ QName rpc = new QName(new URI("noactor1"), "noactor1");
+ CompositeNode input = new ImmutableCompositeNode(QName.create("ns", "2013-12-09", "no child"), new ArrayList<Node<?>>(), ModifyAction.REPLACE);
+ InvokeRpc invokeMsg = new InvokeRpc(rpc, input);
rpcBroker.tell(invokeMsg, getRef());
Boolean getMsg = new ExpectMsg<Boolean>("ErrorResponse") {
protected Boolean match(Object in) {
if (in instanceof ErrorResponse) {
ErrorResponse reply = (ErrorResponse)in;
- return "No remote actor found for rpc execution.".equals(reply.getException().getMessage());
+ return reply.getException().getMessage().contains("No remote actor found for rpc execution of :");
} else {
throw noMatch();
}
SchemaContext schemaContext = mock(SchemaContext.class);
ActorRef rpcBroker = system.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext));
QName rpc = new QName(new URI("actor1"), "actor1");
- InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, null);
+ CompositeNode input = new ImmutableCompositeNode(QName.create("ns", "2013-12-09", "child1"), new ArrayList<Node<?>>(), ModifyAction.REPLACE);
+ InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, YangInstanceIdentifier.create(new YangInstanceIdentifier.NodeIdentifier(rpc)), input);
rpcBroker.tell(invokeMsg, getRef());
Boolean getMsg = new ExpectMsg<Boolean>("ErrorResponse") {
ActorRef rpcBrokerRemote = system.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext), "actor2");
// Add Routed RPC in table
QName rpc = new QName(new URI("actor2"), "actor2");
- RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, rpc, null);
+ YangInstanceIdentifier identifier = YangInstanceIdentifier.create(new YangInstanceIdentifier.NodeIdentifier(rpc));
+ RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, rpc, identifier);
final String route = rpcBrokerRemote.path().toString();
Set<RpcRouter.RouteIdentifier<?, ?, ?>> routeIds = new HashSet<>();
routeIds.add(routeId);
RpcResult<CompositeNode> result = Rpcs.getRpcResult(true, invokeRpcResult, errors);
Future<RpcResult<CompositeNode>> rpcResult = Futures.immediateFuture(result);
when(brokerSession.rpc(rpc, input)).thenReturn(rpcResult);
- InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, input);
+ InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, identifier, input);
rpcBroker.tell(invokeMsg, getRef());
//verify response msg
@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.");
@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);
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.remote.rpc.utils;
+
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.io.ByteSource;
+import junit.framework.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.ModifyAction;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+
+public class XmlUtilsTest {
+
+ private static final DocumentBuilderFactory BUILDERFACTORY;
+
+ static {
+ final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ factory.setCoalescing(true);
+ factory.setIgnoringElementContentWhitespace(true);
+ factory.setIgnoringComments(true);
+ BUILDERFACTORY = factory;
+ }
+
+ private SchemaContext schema;
+ private RpcDefinition testRpc;
+
+ public static final String XML_CONTENT = "<add-flow xmlns=\"urn:opendaylight:controller:rpc:test\"><input xmlns=\"urn:opendaylight:controller:rpc:test\">" +
+ "<id>flowid</id>" +
+ "<flow xmlns:ltha=\"urn:opendaylight:controller:rpc:test\">/ltha:node/ltha:node1[ltha:id='3@java.lang.Short']</flow>" +
+ "</input></add-flow>";
+
+ @Before
+ public void setUp() throws Exception {
+ final ByteSource byteSource = new ByteSource() {
+ @Override
+ public InputStream openStream() throws IOException {
+ return XmlUtilsTest.this.getClass().getResourceAsStream("rpcTest.yang");
+ }
+ };
+ schema = new YangParserImpl().parseSources(Lists.newArrayList(byteSource));
+ final Module rpcTestModule = schema.getModules().iterator().next();
+ testRpc = rpcTestModule.getRpcs().iterator().next();
+ }
+
+ @Test
+ public void testNullInputXmlToComposite() {
+ CompositeNode node = XmlUtils.inputXmlToCompositeNode(testRpc.getQName(), null, schema);
+ Assert.assertNull(node);
+ }
+
+ @Test
+ public void testNullRpcXmlToComposite() {
+ CompositeNode node = XmlUtils.inputXmlToCompositeNode(null, XML_CONTENT, schema);
+ Assert.assertNull(node);
+ }
+
+ @Test
+ public void testInputXmlToCompositeNode() {
+ CompositeNode node = XmlUtils.inputXmlToCompositeNode(testRpc.getQName(), XML_CONTENT, schema);
+ ImmutableList<SimpleNode> input = (ImmutableList)node.getValue().get(0).getValue();
+ SimpleNode firstNode = input.get(0);
+
+ Assert.assertEquals("id", firstNode.getNodeType().getLocalName());
+ Assert.assertEquals("flowid", firstNode.getValue());
+
+ SimpleNode secondNode = input.get(1);
+ Assert.assertEquals("flow", secondNode.getNodeType().getLocalName());
+
+ YangInstanceIdentifier instance = (YangInstanceIdentifier) secondNode.getValue();
+ Iterable<YangInstanceIdentifier.PathArgument> iterable = instance.getPathArguments();
+ Iterator it = iterable.iterator();
+ YangInstanceIdentifier.NodeIdentifier firstPath = (YangInstanceIdentifier.NodeIdentifier) it.next();
+ Assert.assertEquals("node", firstPath.getNodeType().getLocalName());
+ YangInstanceIdentifier.NodeIdentifierWithPredicates secondPath = (YangInstanceIdentifier.NodeIdentifierWithPredicates)it.next();
+ Short value = (Short)secondPath.getKeyValues().values().iterator().next();
+ Short expected = 3;
+ Assert.assertEquals(expected, value);
+ }
+
+ @Test
+ public void testInputCompositeNodeToXML() {
+ CompositeNode input = XmlUtils.inputXmlToCompositeNode(testRpc.getQName(), XML_CONTENT, schema);
+ List<Node<?>> childNodes = new ArrayList();
+ childNodes.add(input);
+ QName rpcQName = schema.getOperations().iterator().next().getQName();
+ CompositeNode node = new ImmutableCompositeNode(rpcQName, input.getValue(), ModifyAction.REPLACE);
+ String xml = XmlUtils.inputCompositeNodeToXml(node, schema);
+ Assert.assertNotNull(xml);
+ Assert.assertTrue(xml.contains("3@java.lang.Short"));
+ }
+
+ @Test
+ public void testNullCompositeNodeToXml(){
+ String xml = XmlUtils.inputCompositeNodeToXml(null, schema);
+ Assert.assertTrue(xml.isEmpty());
+ }
+
+ @Test
+ public void testNullSchemaCompositeNodeToXml(){
+ String xml = XmlUtils.inputCompositeNodeToXml(new ImmutableCompositeNode(QName.create("ns", "2013-12-09", "child1"), new ArrayList<Node<?>>(), ModifyAction.REPLACE), null);
+ Assert.assertTrue(xml.isEmpty());
+ }
+
+
+}
--- /dev/null
+/*
+ * 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
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,