From 5c5f9d085a0876b875ce8889dfdac2a83d5e5e97 Mon Sep 17 00:00:00 2001 From: Harman Singh Date: Tue, 29 Jul 2014 19:03:08 -0700 Subject: [PATCH] Updating cluster RPC code making it work Added XMLUtils to serialize and deserialize instance identifier objects and the values insides path arguments with proper object type. This code use Yangtools existing utility, but modify that code to make sure right type is picked up. We can think of adding it directly in yang tools also and remove it from here Added Preconditions check for pojos Change-Id: Ieb73b4ee660d2e058d0a62952799fc6f39d4c6b5 Signed-off-by: Harman Singh --- .../configuration/initial/01-md-sal.xml | 23 +- opendaylight/md-sal/pom.xml | 3 + .../md-sal/sal-remoterpc-connector/pom.xml | 28 +- .../remote/rpc/RemoteRpcImplementation.java | 34 +- .../remote/rpc/RouteIdentifierImpl.java | 17 +- .../remote/rpc/RoutedRpcListener.java | 18 +- .../controller/remote/rpc/RpcBroker.java | 7 +- .../remote/rpc/messages/AddRoutedRpc.java | 10 +- .../remote/rpc/messages/AddRpc.java | 10 +- .../remote/rpc/messages/ErrorResponse.java | 7 +- .../remote/rpc/messages/ExecuteRpc.java | 10 +- .../remote/rpc/messages/GetRoutedRpc.java | 6 +- .../rpc/messages/GetRoutedRpcReply.java | 4 +- .../remote/rpc/messages/GetRpc.java | 6 +- .../remote/rpc/messages/GetRpcReply.java | 4 +- .../remote/rpc/messages/InvokeRoutedRpc.java | 22 +- .../remote/rpc/messages/InvokeRpc.java | 10 +- .../remote/rpc/messages/RemoveRoutedRpc.java | 10 +- .../remote/rpc/messages/RemoveRpc.java | 7 +- .../remote/rpc/messages/RoutingTableData.java | 8 +- .../remote/rpc/messages/RpcResponse.java | 4 +- .../rpc/messages/UpdateSchemaContext.java | 24 ++ .../utils/InstanceIdentifierForXmlCodec.java | 214 +++++++++++++ .../remote/rpc/utils/RandomPrefix.java | 50 +++ .../remote/rpc/utils/XmlDocumentUtils.java | 299 ++++++++++++++++++ .../remote/rpc/utils/XmlStreamUtils.java | 247 +++++++++++++++ .../remote/rpc/{ => utils}/XmlUtils.java | 93 +++++- .../src/main/resources/application.conf | 2 +- .../controller/remote/rpc/RpcBrokerTest.java | 16 +- .../remote/rpc/registry/RoutingTableTest.java | 6 +- .../remote/rpc/utils/XmlUtilsTest.java | 91 ++++++ .../controller/remote/rpc/utils/rpcTest.yang | 32 ++ pom.xml | 1 - 33 files changed, 1214 insertions(+), 109 deletions(-) create mode 100644 opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/UpdateSchemaContext.java create mode 100644 opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/InstanceIdentifierForXmlCodec.java create mode 100644 opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/RandomPrefix.java create mode 100644 opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlDocumentUtils.java create mode 100644 opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlStreamUtils.java rename opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/{ => utils}/XmlUtils.java (52%) create mode 100644 opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/utils/XmlUtilsTest.java create mode 100644 opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/org/opendaylight/controller/remote/rpc/utils/rpcTest.yang diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.xml b/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.xml index 03da7f0ccd..f25b7d91bd 100644 --- a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.xml +++ b/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.xml @@ -72,17 +72,7 @@ --> - - + prefix:inmemory-operational-datastore-provider operational-store-service @@ -157,6 +147,17 @@ + + diff --git a/opendaylight/md-sal/pom.xml b/opendaylight/md-sal/pom.xml index cadd8ca87d..5e96e1cc0d 100644 --- a/opendaylight/md-sal/pom.xml +++ b/opendaylight/md-sal/pom.xml @@ -71,6 +71,9 @@ sal-test-model + + + sal-remoterpc-connector diff --git a/opendaylight/md-sal/sal-remoterpc-connector/pom.xml b/opendaylight/md-sal/sal-remoterpc-connector/pom.xml index 6ee301d827..ce3bfe9a4c 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/pom.xml +++ b/opendaylight/md-sal/sal-remoterpc-connector/pom.xml @@ -55,22 +55,37 @@ org.opendaylight.controller netconf-util - + + org.opendaylight.controller + sal-core-spi + + + org.opendaylight.controller + sal-common-impl + org.opendaylight.yangtools yang-data-api + + + org.opendaylight.yangtools + yang-model-api + + org.opendaylight.yangtools yang-data-impl + org.opendaylight.yangtools yang-common + @@ -107,6 +122,17 @@ ${slf4j.version} test + + org.opendaylight.yangtools + yang-parser-impl + test + + + com.google.collections + google-collections + 1.0 + test + diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementation.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementation.java index 43aa5b7e85..75db0b7299 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementation.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementation.java @@ -7,21 +7,18 @@ import org.opendaylight.controller.remote.rpc.messages.ErrorResponse; import org.opendaylight.controller.remote.rpc.messages.InvokeRoutedRpc; import org.opendaylight.controller.remote.rpc.messages.InvokeRpc; import org.opendaylight.controller.remote.rpc.messages.RpcResponse; -import org.opendaylight.controller.sal.common.util.RpcErrors; -import org.opendaylight.controller.sal.common.util.Rpcs; +import org.opendaylight.controller.remote.rpc.utils.XmlUtils; import org.opendaylight.controller.sal.core.api.RoutedRpcDefaultImplementation; import org.opendaylight.controller.sal.core.api.RpcImplementation; import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.Set; @@ -37,7 +34,7 @@ public class RemoteRpcImplementation implements RpcImplementation, } @Override - public ListenableFuture> invokeRpc(QName rpc, InstanceIdentifier identifier, CompositeNode input) { + public ListenableFuture> invokeRpc(QName rpc, YangInstanceIdentifier identifier, CompositeNode input) { InvokeRoutedRpc rpcMsg = new InvokeRoutedRpc(rpc, identifier, input); return executeMsg(rpcMsg); @@ -56,22 +53,33 @@ public class RemoteRpcImplementation implements RpcImplementation, } private ListenableFuture> executeMsg(Object rpcMsg) { - CompositeNode result = null; - Collection errors = errors = new ArrayList<>(); + ListenableFuture> listenableFuture = null; + try { Object response = ActorUtil.executeLocalOperation(rpcBroker, rpcMsg, ActorUtil.ASK_DURATION, ActorUtil.AWAIT_DURATION); if(response instanceof RpcResponse) { + RpcResponse rpcResponse = (RpcResponse) response; - result = XmlUtils.xmlToCompositeNode(rpcResponse.getResultCompositeNode()); + CompositeNode result = XmlUtils.xmlToCompositeNode(rpcResponse.getResultCompositeNode()); + listenableFuture = Futures.immediateFuture(RpcResultBuilder.success(result).build()); + } else if(response instanceof ErrorResponse) { + ErrorResponse errorResponse = (ErrorResponse) response; Exception e = errorResponse.getException(); - errors.add(RpcErrors.getRpcError(null, null, null, null, e.getMessage(), null, e.getCause())); + final RpcResultBuilder failed = RpcResultBuilder.failed(); + failed.withError(null, null, e.getMessage(), null, null, e.getCause()); + listenableFuture = Futures.immediateFuture(failed.build()); + } } catch (Exception e) { LOG.error("Error occurred while invoking RPC actor {}", e.toString()); - errors.add(RpcErrors.getRpcError(null, null, null, null, e.getMessage(), null, e.getCause())); + + final RpcResultBuilder failed = RpcResultBuilder.failed(); + failed.withError(null, null, e.getMessage(), null, null, e.getCause()); + listenableFuture = Futures.immediateFuture(failed.build()); } - return Futures.immediateFuture(Rpcs.getRpcResult(true, result, errors)); + + return listenableFuture; } } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RouteIdentifierImpl.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RouteIdentifierImpl.java index ea72238fbc..85d21381b6 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RouteIdentifierImpl.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RouteIdentifierImpl.java @@ -7,20 +7,23 @@ */ package org.opendaylight.controller.remote.rpc; +import com.google.common.base.Preconditions; import org.opendaylight.controller.sal.connector.api.RpcRouter; import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; + import java.io.Serializable; -public class RouteIdentifierImpl implements RpcRouter.RouteIdentifier,Serializable { +public class RouteIdentifierImpl implements RpcRouter.RouteIdentifier,Serializable { private static final long serialVersionUID = 1L; - private QName context; - private QName type; - private InstanceIdentifier route; + private final QName context; + private final QName type; + private final YangInstanceIdentifier route; - public RouteIdentifierImpl(QName context, QName type, InstanceIdentifier route) { + public RouteIdentifierImpl(final QName context, final QName type, final YangInstanceIdentifier route) { + Preconditions.checkNotNull(type, "Rpc type should not be null"); this.context = context; this.type = type; this.route = route; @@ -37,7 +40,7 @@ public class RouteIdentifierImpl implements RpcRouter.RouteIdentifier{ +public class RoutedRpcListener implements RouteChangeListener{ private static final Logger LOG = LoggerFactory.getLogger(RoutedRpcListener.class); private final ActorRef rpcRegistry; private final String actorPath; public RoutedRpcListener(ActorRef rpcRegistry, String actorPath) { + Preconditions.checkNotNull(rpcRegistry, "rpc registry actor should not be null"); + Preconditions.checkNotNull(actorPath, "actor path of rpc broker on current node should not be null"); + this.rpcRegistry = rpcRegistry; this.actorPath = actorPath; } @Override - public void onRouteChange(RouteChange routeChange) { - Map> announcements = routeChange.getAnnouncements(); + public void onRouteChange(RouteChange routeChange) { + Map> announcements = routeChange.getAnnouncements(); announce(getRouteIdentifiers(announcements)); - Map> removals = routeChange.getRemovals(); + Map> removals = routeChange.getRemovals(); remove(getRouteIdentifiers(removals)); } @@ -78,12 +82,12 @@ public class RoutedRpcListener implements RouteChangeListener> getRouteIdentifiers(Map> changes) { + private Set> getRouteIdentifiers(Map> changes) { RouteIdentifierImpl routeId = null; Set> routeIdSet = new HashSet<>(); for (RpcRoutingContext context : changes.keySet()){ - for (InstanceIdentifier instanceId : changes.get(context)){ + for (YangInstanceIdentifier instanceId : changes.get(context)){ routeId = new RouteIdentifierImpl(null, context.getRpc(), instanceId); routeIdSet.add(routeId); } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcBroker.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcBroker.java index 3354fc3da2..412e549fac 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcBroker.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcBroker.java @@ -20,6 +20,7 @@ import org.opendaylight.controller.remote.rpc.messages.GetRpcReply; import org.opendaylight.controller.remote.rpc.messages.InvokeRoutedRpc; import org.opendaylight.controller.remote.rpc.messages.InvokeRpc; import org.opendaylight.controller.remote.rpc.messages.RpcResponse; +import org.opendaylight.controller.remote.rpc.utils.XmlUtils; import org.opendaylight.controller.sal.core.api.Broker; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.CompositeNode; @@ -105,11 +106,12 @@ public class RpcBroker extends AbstractUntypedActor { String remoteActorPath = rpcReply.getRoutePath(); if(remoteActorPath == null) { - LOG.debug("No remote actor found for rpc execution."); + LOG.debug("No remote actor found for rpc {{}}.", msg.getRpc()); getSender().tell(new ErrorResponse( - new IllegalStateException("No remote actor found for rpc execution.")), self()); + new IllegalStateException("No remote actor found for rpc execution of : " + msg.getRpc())), self()); } else { + ExecuteRpc executeMsg = new ExecuteRpc(XmlUtils.inputCompositeNodeToXml(msg.getInput(), schemaContext), msg.getRpc()); Object operationRes = ActorUtil.executeRemoteOperation(this.context().actorSelection(remoteActorPath), executeMsg, ActorUtil.REMOTE_ASK_DURATION, ActorUtil.REMOTE_AWAIT_DURATION); @@ -127,7 +129,6 @@ public class RpcBroker extends AbstractUntypedActor { try { Future> rpc = brokerSession.rpc(msg.getRpc(), XmlUtils.inputXmlToCompositeNode(msg.getRpc(), msg.getInputCompositeNode(), schemaContext)); RpcResult rpcResult = rpc != null ? rpc.get():null; - CompositeNode result = rpcResult != null ? rpcResult.getResult() : null; getSender().tell(new RpcResponse(XmlUtils.outputCompositeNodeToXml(result, schemaContext)), self()); } catch (Exception e) { diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRoutedRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRoutedRpc.java index 25773bb8a6..fd1af2b33c 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRoutedRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRoutedRpc.java @@ -8,6 +8,7 @@ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.controller.sal.connector.api.RpcRouter; import java.io.Serializable; @@ -15,10 +16,13 @@ import java.util.Set; public class AddRoutedRpc implements Serializable { - Set> announcements; - String actorPath; + private final Set> announcements; + private final String actorPath; + + public AddRoutedRpc(final Set> announcements, final String actorPath) { + Preconditions.checkNotNull(announcements, "Route identifier should not be null"); + Preconditions.checkNotNull(actorPath, "Actor path should not be null"); - public AddRoutedRpc(Set> announcements, String actorPath) { this.announcements = announcements; this.actorPath = actorPath; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRpc.java index eac973137e..7eaa8f0618 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRpc.java @@ -8,16 +8,20 @@ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl; import java.io.Serializable; public class AddRpc implements Serializable { - RouteIdentifierImpl routeId; - String actorPath; + private final RouteIdentifierImpl routeId; + private final String actorPath; + + public AddRpc(final RouteIdentifierImpl routeId, final String actorPath) { + Preconditions.checkNotNull(routeId, "Route identifier should not be null"); + Preconditions.checkNotNull(actorPath, "Actor path should not be null"); - public AddRpc(RouteIdentifierImpl routeId, String actorPath) { this.routeId = routeId; this.actorPath = actorPath; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ErrorResponse.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ErrorResponse.java index ef3f528112..2c26243fe9 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ErrorResponse.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ErrorResponse.java @@ -7,13 +7,16 @@ */ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; + import java.io.Serializable; public class ErrorResponse implements Serializable { - Exception exception; + private final Exception exception; - public ErrorResponse(Exception e) { + public ErrorResponse(final Exception e) { + Preconditions.checkNotNull(e, "Exception should be present for error message"); this.exception = e; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ExecuteRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ExecuteRpc.java index 030d81ac7e..522dd44f5b 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ExecuteRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ExecuteRpc.java @@ -8,16 +8,20 @@ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.yangtools.yang.common.QName; import java.io.Serializable; public class ExecuteRpc implements Serializable { - private String inputCompositeNode; - private QName rpc; + private final String inputCompositeNode; + private final QName rpc; + + public ExecuteRpc(final String inputCompositeNode, final QName rpc) { + Preconditions.checkNotNull(inputCompositeNode, "Composite Node input string should be present"); + Preconditions.checkNotNull(rpc, "rpc Qname should not be null"); - public ExecuteRpc(String inputCompositeNode, QName rpc) { this.inputCompositeNode = inputCompositeNode; this.rpc = rpc; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpc.java index b1fa410e8d..e8d2262182 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpc.java @@ -9,15 +9,17 @@ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl; import java.io.Serializable; public class GetRoutedRpc implements Serializable { - RouteIdentifierImpl routeId; + private final RouteIdentifierImpl routeId; - public GetRoutedRpc(RouteIdentifierImpl routeId) { + public GetRoutedRpc(final RouteIdentifierImpl routeId) { + Preconditions.checkNotNull(routeId, "route id should not be null"); this.routeId = routeId; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpcReply.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpcReply.java index 0e1563340b..d426662192 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpcReply.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpcReply.java @@ -12,9 +12,9 @@ import java.io.Serializable; public class GetRoutedRpcReply implements Serializable { - private String routePath; + private final String routePath; - public GetRoutedRpcReply(String routePath) { + public GetRoutedRpcReply(final String routePath) { this.routePath = routePath; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpc.java index c55627961a..c1d4240dca 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpc.java @@ -7,15 +7,17 @@ */ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl; import java.io.Serializable; public class GetRpc implements Serializable { - RouteIdentifierImpl routeId; + private final RouteIdentifierImpl routeId; - public GetRpc(RouteIdentifierImpl routeId) { + public GetRpc(final RouteIdentifierImpl routeId) { + Preconditions.checkNotNull(routeId, "Route Id should not be null"); this.routeId = routeId; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpcReply.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpcReply.java index 3309b989c5..aaf089d16f 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpcReply.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpcReply.java @@ -12,9 +12,9 @@ import java.io.Serializable; public class GetRpcReply implements Serializable { - private String routePath; + private final String routePath; - public GetRpcReply(String routePath) { + public GetRpcReply(final String routePath) { this.routePath = routePath; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRoutedRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRoutedRpc.java index 00ef980bb1..fd73144e1d 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRoutedRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRoutedRpc.java @@ -7,26 +7,26 @@ */ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import java.io.Serializable; public class InvokeRoutedRpc implements Serializable { - private QName rpc; - private InstanceIdentifier identifier; - private CompositeNode input; + private final QName rpc; + private final YangInstanceIdentifier identifier; + private final CompositeNode input; - public InvokeRoutedRpc(QName rpc, InstanceIdentifier identifier, CompositeNode input) { - this.rpc = rpc; - this.identifier = identifier; - this.input = input; - } + public InvokeRoutedRpc(final QName rpc, final YangInstanceIdentifier identifier, final CompositeNode input) { + Preconditions.checkNotNull(rpc, "rpc qname should not be null"); + Preconditions.checkNotNull(identifier, "instance identifier of routed rpc should not be null"); + Preconditions.checkNotNull(input, "rpc input should not be null"); - public InvokeRoutedRpc(QName rpc, CompositeNode input) { this.rpc = rpc; + this.identifier = identifier; this.input = input; } @@ -34,7 +34,7 @@ public class InvokeRoutedRpc implements Serializable { return rpc; } - public InstanceIdentifier getIdentifier() { + public YangInstanceIdentifier getIdentifier() { return identifier; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRpc.java index 1f4eab0971..94b7fe4024 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRpc.java @@ -7,6 +7,7 @@ */ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.CompositeNode; @@ -14,10 +15,13 @@ import java.io.Serializable; public class InvokeRpc implements Serializable { - private QName rpc; - private CompositeNode input; + private final QName rpc; + private final CompositeNode input; + + public InvokeRpc(final QName rpc, final CompositeNode input) { + Preconditions.checkNotNull(rpc, "rpc qname should not be null"); + Preconditions.checkNotNull(input, "rpc input should not be null"); - public InvokeRpc(QName rpc, CompositeNode input) { this.rpc = rpc; this.input = input; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRoutedRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRoutedRpc.java index a3aa9d12fa..b560b8c8c0 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRoutedRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRoutedRpc.java @@ -8,6 +8,7 @@ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.controller.sal.connector.api.RpcRouter; import java.io.Serializable; @@ -15,10 +16,13 @@ import java.util.Set; public class RemoveRoutedRpc implements Serializable { - Set> announcements; - String actorPath; + private final Set> announcements; + private final String actorPath; + + public RemoveRoutedRpc(final Set> announcements, final String actorPath) { + Preconditions.checkNotNull(announcements, "Route identifier should not be null"); + Preconditions.checkNotNull(actorPath, "Actor path should not be null"); - public RemoveRoutedRpc(Set> announcements, String actorPath) { this.announcements = announcements; this.actorPath = actorPath; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRpc.java index 0bfd78aaae..289334fccc 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRpc.java @@ -8,15 +8,18 @@ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl; import java.io.Serializable; public class RemoveRpc implements Serializable { - RouteIdentifierImpl routeId; + private final RouteIdentifierImpl routeId; + + public RemoveRpc(final RouteIdentifierImpl routeId) { + Preconditions.checkNotNull(routeId, "Route Id should not be null"); - public RemoveRpc(RouteIdentifierImpl routeId) { this.routeId = routeId; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RoutingTableData.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RoutingTableData.java index 132fdba054..c57a258426 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RoutingTableData.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RoutingTableData.java @@ -15,11 +15,11 @@ import java.util.LinkedHashSet; import java.util.Map; public class RoutingTableData implements Serializable { - private Map, String> rpcMap; - private Map, LinkedHashSet> routedRpcMap; + private final Map, String> rpcMap; + private final Map, LinkedHashSet> routedRpcMap; - public RoutingTableData(Map, String> rpcMap, - Map, LinkedHashSet> routedRpcMap) { + public RoutingTableData(final Map, String> rpcMap, + final Map, LinkedHashSet> routedRpcMap) { this.rpcMap = rpcMap; this.routedRpcMap = routedRpcMap; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RpcResponse.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RpcResponse.java index cbfecb1918..17766f15b9 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RpcResponse.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RpcResponse.java @@ -12,9 +12,9 @@ package org.opendaylight.controller.remote.rpc.messages; import java.io.Serializable; public class RpcResponse implements Serializable { - private String resultCompositeNode; + private final String resultCompositeNode; - public RpcResponse(String resultCompositeNode) { + public RpcResponse(final String resultCompositeNode) { this.resultCompositeNode = resultCompositeNode; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/UpdateSchemaContext.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/UpdateSchemaContext.java new file mode 100644 index 0000000000..83fc7723b3 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/UpdateSchemaContext.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.remote.rpc.messages; + +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +public class UpdateSchemaContext { + + private final SchemaContext schemaContext; + + public UpdateSchemaContext(final SchemaContext schemaContext) { + this.schemaContext = schemaContext; + } + + public SchemaContext getSchemaContext() { + return schemaContext; + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/InstanceIdentifierForXmlCodec.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/InstanceIdentifierForXmlCodec.java new file mode 100644 index 0000000000..cacacc628e --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/InstanceIdentifierForXmlCodec.java @@ -0,0 +1,214 @@ +/* + * 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(); + final Iterator xPathParts = SLASH_SPLITTER.split(valueTrimmed).iterator(); + + // must be at least "/pr:node" + if (!xPathParts.hasNext() || !xPathParts.next().isEmpty() || !xPathParts.hasNext()) { + return null; + } + + List result = new ArrayList<>(); + while (xPathParts.hasNext()) { + String xPathPartTrimmed = xPathParts.next().trim(); + + PathArgument pathArgument = toPathArgument(xPathPartTrimmed, element, schemaContext); + if (pathArgument != null) { + result.add(pathArgument); + } + } + return YangInstanceIdentifier.create(result); + } + + public static Element serialize(final YangInstanceIdentifier id, final Element element) { + Preconditions.checkNotNull(id, "Variable should contain instance of instance identifier and can't be null"); + Preconditions.checkNotNull(element, "DOM element can't be null"); + + final RandomPrefix prefixes = new RandomPrefix(); + final String str = XmlUtils.encodeIdentifier(prefixes, id); + + for (Entry e: prefixes.getPrefixes()) { + element.setAttribute("xmlns:" + e.getValue(), e.getKey().toString()); + } + element.setTextContent(str); + return element; + } + + private static String getIdAndPrefixAsStr(final String pathPart) { + int predicateStartIndex = pathPart.indexOf('['); + return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex); + } + + private static PathArgument toPathArgument(final String xPathArgument, final Element element, final SchemaContext schemaContext) { + final QName mainQName = toIdentity(xPathArgument, element, schemaContext); + + // predicates + final Matcher matcher = PREDICATE_PATTERN.matcher(xPathArgument); + final Map predicates = new HashMap<>(); + QName currentQName = mainQName; + + while (matcher.find()) { + final String predicateStr = matcher.group(1).trim(); + final int indexOfEqualityMark = predicateStr.indexOf('='); + if (indexOfEqualityMark != -1) { + final Object predicateValue = toPredicateValue(predicateStr.substring(indexOfEqualityMark + 1)); + if (predicateValue == null) { + return null; + } + + if (predicateStr.charAt(0) != '.') { + // target is not a leaf-list + currentQName = toIdentity(predicateStr.substring(0, indexOfEqualityMark), element, schemaContext); + if (currentQName == null) { + return null; + } + } + predicates.put(currentQName, predicateValue); + } + } + + if (predicates.isEmpty()) { + return new YangInstanceIdentifier.NodeIdentifier(mainQName); + } else { + return new YangInstanceIdentifier.NodeIdentifierWithPredicates(mainQName, predicates); + } + + } + + public static QName toIdentity(final String xPathArgument, final Element element, final SchemaContext schemaContext) { + final String xPathPartTrimmed = getIdAndPrefixAsStr(xPathArgument).trim(); + final Iterator it = COLON_SPLITTER.split(xPathPartTrimmed).iterator(); + + // Empty string + if (!it.hasNext()) { + return null; + } + + final String prefix = it.next().trim(); + if (prefix.isEmpty()) { + return null; + } + + // it is not "prefix:value" + if (!it.hasNext()) { + return null; + } + + final String identifier = it.next().trim(); + if (identifier.isEmpty()) { + return null; + } + + URI namespace = null; + String namespaceStr = null; + try { + namespaceStr = element.getAttribute("xmlns:"+prefix); + namespace = new URI(namespaceStr); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("It wasn't possible to convert " + namespaceStr + " to URI object."); + } catch (NullPointerException e) { + throw new IllegalArgumentException("I wasn't possible to get namespace for prefix " + prefix); + } + + Module module = schemaContext.findModuleByNamespaceAndRevision(namespace, null); + return QName.create(module.getQNameModule(), identifier); + } + + private static String trimIfEndIs(final String str, final char end) { + final int l = str.length() - 1; + if (str.charAt(l) != end) { + return null; + } + + return str.substring(1, l); + } + + private static Object toPredicateValue(final String predicatedValue) { + final String predicatedValueTrimmed = predicatedValue.trim(); + if (predicatedValue.isEmpty()) { + return null; + } + String updatedValue = null; + switch (predicatedValueTrimmed.charAt(0)) { + case '"': + updatedValue = trimIfEndIs(predicatedValueTrimmed, '"'); + break; + case '\'': + updatedValue = trimIfEndIs(predicatedValueTrimmed, '\''); + break; + default: + updatedValue = predicatedValueTrimmed; + } + Iterator it = AT_SPLITTER.split(updatedValue).iterator(); + // Empty string + if (!it.hasNext()) { + return null; + } + + final String value = it.next().trim(); + if (value.isEmpty()) { + return null; + } + + if (!it.hasNext()) { + return value; + } + + final String type = it.next().trim(); + if (type.isEmpty()) { + return value; + } + Object predicateObject = null; + try { + predicateObject = Class.forName(type).getConstructor(String.class).newInstance(value); + } catch (Exception e) { + logger.error("Could not convert to valid type of value", e); + } + return predicateObject; + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/RandomPrefix.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/RandomPrefix.java new file mode 100644 index 0000000000..55cc8192a8 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/RandomPrefix.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.remote.rpc.utils; + +import org.opendaylight.yangtools.yang.common.QName; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ThreadLocalRandom; + +/** + * Picked from Yang tools because it was not public class + */ + +final class RandomPrefix { + final Map prefixes = new HashMap<>(); + + Iterable> getPrefixes() { + return prefixes.entrySet(); + } + + String encodeQName(final QName qname) { + String prefix = prefixes.get(qname.getNamespace()); + if (prefix == null) { + prefix = qname.getPrefix(); + if (prefix == null || prefix.isEmpty() || prefixes.containsValue(prefix)) { + final ThreadLocalRandom random = ThreadLocalRandom.current(); + do { + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 4; i++) { + sb.append((char)('a' + random.nextInt(25))); + } + + prefix = sb.toString(); + } while (prefixes.containsValue(prefix)); + } + + prefixes.put(qname.getNamespace(), prefix); + } + + return prefix + ':' + qname.getLocalName(); + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlDocumentUtils.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlDocumentUtils.java new file mode 100644 index 0000000000..9e5d8f0cd4 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlDocumentUtils.java @@ -0,0 +1,299 @@ +/* + * 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, codecProvider,schemaCtx); + } else if (schema instanceof LeafSchemaNode) { + return toSimpleNodeWithType(xmlElement, (LeafSchemaNode) schema, codecProvider,schemaCtx); + } else if (schema instanceof LeafListSchemaNode) { + return toSimpleNodeWithType(xmlElement, (LeafListSchemaNode) schema, codecProvider,schemaCtx); + } + return null; + } + + + + private static Node toSimpleNodeWithType(final Element xmlElement, final LeafSchemaNode schema, + final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) { + TypeDefinitionAwareCodec> codec = codecProvider.codecFor(schema.getType()); + String text = xmlElement.getTextContent(); + Object value = null; + if (codec != null) { + value = codec.deserialize(text); + } + final TypeDefinition baseType = XmlUtils.resolveBaseTypeFrom(schema.getType()); + if (baseType instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifier) { + value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx); + } else if(baseType instanceof IdentityrefTypeDefinition){ + value = InstanceIdentifierForXmlCodec.toIdentity(xmlElement.getTextContent(), xmlElement, schemaCtx); + } + + if (value == null) { + value = xmlElement.getTextContent(); + } + + Optional modifyAction = getModifyOperationFromAttributes(xmlElement); + return new SimpleNodeTOImpl<>(schema.getQName(), null, value, modifyAction.orNull()); + } + + private static Node toSimpleNodeWithType(final Element xmlElement, final LeafListSchemaNode schema, + final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) { + TypeDefinitionAwareCodec> codec = codecProvider.codecFor(schema.getType()); + String text = xmlElement.getTextContent(); + Object value = null; + if (codec != null) { + value = codec.deserialize(text); + } + if (schema.getType() instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifier) { + value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx); + } + if (value == null) { + value = xmlElement.getTextContent(); + } + + Optional modifyAction = getModifyOperationFromAttributes(xmlElement); + return new SimpleNodeTOImpl<>(schema.getQName(), null, value, modifyAction.orNull()); + } + + private static Node toCompositeNodeWithSchema(final Element xmlElement, final QName qName, final DataNodeContainer schema, + final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) { + List> values = toDomNodes(xmlElement, Optional.fromNullable(schema.getChildNodes()),schemaCtx); + Optional modifyAction = getModifyOperationFromAttributes(xmlElement); + return ImmutableCompositeNode.create(qName, values, modifyAction.orNull()); + } + + private static Optional getModifyOperationFromAttributes(final Element xmlElement) { + Attr attributeNodeNS = xmlElement.getAttributeNodeNS(OPERATION_ATTRIBUTE_QNAME.getNamespace().toString(), OPERATION_ATTRIBUTE_QNAME.getLocalName()); + if(attributeNodeNS == null) { + return Optional.absent(); + } + + ModifyAction action = ModifyAction.fromXmlValue(attributeNodeNS.getValue()); + Preconditions.checkArgument(action.isOnElementPermitted(), "Unexpected operation %s on %s", action, xmlElement); + + return Optional.of(action); + } + + private static void checkQName(final Element xmlElement, final QName qName) { + checkState(Objects.equal(xmlElement.getNamespaceURI(), qName.getNamespace().toString())); + checkState(qName.getLocalName().equals(xmlElement.getLocalName())); + } + + public static final Optional findFirstSchema(final QName qname, final Collection dataSchemaNode) { + if (dataSchemaNode != null && !dataSchemaNode.isEmpty() && qname != null) { + for (DataSchemaNode dsn : dataSchemaNode) { + if (qname.isEqualWithoutRevision(dsn.getQName())) { + return Optional. of(dsn); + } else if (dsn instanceof ChoiceNode) { + for (ChoiceCaseNode choiceCase : ((ChoiceNode) dsn).getCases()) { + Optional foundDsn = findFirstSchema(qname, choiceCase.getChildNodes()); + if (foundDsn != null && foundDsn.isPresent()) { + return foundDsn; + } + } + } + } + } + return Optional.absent(); + } + + private static Node toDomNode(Element element) { + QName qname = qNameFromElement(element); + + ImmutableList.Builder> values = ImmutableList.> builder(); + NodeList nodes = element.getChildNodes(); + boolean isSimpleObject = true; + String value = null; + for (int i = 0; i < nodes.getLength(); i++) { + org.w3c.dom.Node child = nodes.item(i); + if (child instanceof Element) { + isSimpleObject = false; + values.add(toDomNode((Element) child)); + } + if (isSimpleObject && child instanceof org.w3c.dom.Text) { + value = element.getTextContent(); + if (!Strings.isNullOrEmpty(value)) { + isSimpleObject = true; + } + } + } + if (isSimpleObject) { + return new SimpleNodeTOImpl<>(qname, null, value); + } + return ImmutableCompositeNode.create(qname, values.build()); + } + + public static List> toDomNodes(final Element element, final Optional> context,SchemaContext schemaCtx) { + return forEachChild(element.getChildNodes(),schemaCtx, new Function>>() { + + @Override + public Optional> apply(ElementWithSchemaContext input) { + if (context.isPresent()) { + QName partialQName = qNameFromElement(input.getElement()); + Optional schemaNode = XmlDocumentUtils.findFirstSchema(partialQName, context.get()); + if (schemaNode.isPresent()) { + return Optional.> fromNullable(// + toNodeWithSchema(input.getElement(), schemaNode.get(), XmlDocumentUtils.defaultValueCodecProvider(),input.getSchemaContext())); + } + } + return Optional.> fromNullable(toDomNode(input.getElement())); + } + + }); + + } + + private static final List forEachChild(final NodeList nodes, final SchemaContext schemaContext, final Function> forBody) { + final int l = nodes.getLength(); + if (l == 0) { + return ImmutableList.of(); + } + + final List list = new ArrayList<>(l); + for (int i = 0; i < l; i++) { + org.w3c.dom.Node child = nodes.item(i); + if (child instanceof Element) { + Optional result = forBody.apply(new ElementWithSchemaContext((Element) child,schemaContext)); + if (result.isPresent()) { + list.add(result.get()); + } + } + } + return ImmutableList.copyOf(list); + } + + public static final XmlCodecProvider defaultValueCodecProvider() { + return XmlUtils.DEFAULT_XML_CODEC_PROVIDER; + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlStreamUtils.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlStreamUtils.java new file mode 100644 index 0000000000..e74a84a17e --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlStreamUtils.java @@ -0,0 +1,247 @@ +/* + * 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"); + + final RandomPrefix prefixes = new RandomPrefix(); + final String str = XmlUtils.encodeIdentifier(prefixes, id); + + for (Entry e: prefixes.getPrefixes()) { + writer.writeNamespace(e.getValue(), e.getKey().toString()); + } + writer.writeCharacters(str); + } + + /** + * Write a full XML document corresponding to a CompositeNode into an XML stream writer. + * + * @param writer XML Stream writer + * @param data data node + * @param schema corresponding schema node, may be null + * @throws javax.xml.stream.XMLStreamException if an encoding problem occurs + */ + public void writeDocument(final @Nonnull XMLStreamWriter writer, final @Nonnull CompositeNode data, final @Nullable SchemaNode schema) throws XMLStreamException { + // final Boolean repairing = (Boolean) writer.getProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES); + // Preconditions.checkArgument(repairing == true, "XML Stream Writer has to be repairing namespaces"); + + writer.writeStartDocument(); + writeElement(writer, data, schema); + writer.writeEndDocument(); + writer.flush(); + } + + + /** + * Write an element into a XML stream writer. This includes the element start/end tags and + * the value of the element. + * + * @param writer XML Stream writer + * @param data data node + * @param schema Schema node + * @throws javax.xml.stream.XMLStreamException if an encoding problem occurs + */ + public void writeElement(final XMLStreamWriter writer, final @Nonnull Node data, final SchemaNode schema) throws XMLStreamException { + final QName qname = data.getNodeType(); + final String pfx = qname.getPrefix() != null ? qname.getPrefix() : ""; + final String ns = qname.getNamespace() != null ? qname.getNamespace().toString() : ""; + + if (isEmptyElement(data)) { + writer.writeEmptyElement(pfx, qname.getLocalName(), ns); + return; + } + + writer.writeStartElement(pfx, qname.getLocalName(), ns); + if (data instanceof AttributesContainer && ((AttributesContainer) data).getAttributes() != null) { + for (Entry attribute : ((AttributesContainer) data).getAttributes().entrySet()) { + writer.writeAttribute(attribute.getKey().getNamespace().toString(), attribute.getKey().getLocalName(), attribute.getValue()); + } + } + + if (data instanceof SimpleNode) { + // Simple node + if (schema instanceof LeafListSchemaNode) { + writeValue(writer, ((LeafListSchemaNode) schema).getType(), data.getValue()); + } else if (schema instanceof LeafSchemaNode) { + writeValue(writer, ((LeafSchemaNode) schema).getType(), data.getValue()); + } else { + Object value = data.getValue(); + if (value != null) { + writer.writeCharacters(String.valueOf(value)); + } + } + } else { + // CompositeNode + for (Node child : ((CompositeNode) data).getValue()) { + DataSchemaNode childSchema = null; + if (schema instanceof DataNodeContainer) { + childSchema = SchemaUtils.findFirstSchema(child.getNodeType(), ((DataNodeContainer) schema).getChildNodes()).orNull(); + if (LOG.isDebugEnabled()) { + if (childSchema == null) { + LOG.debug("Probably the data node \"{}\" does not conform to schema", child == null ? "" : child.getNodeType().getLocalName()); + } + } + } + + writeElement(writer, child, childSchema); + } + } + + writer.writeEndElement(); + } + + /** + * Write a value into a XML stream writer. This method assumes the start and end of element is + * emitted by the caller. + * + * @param writer XML Stream writer + * @param type type definitions + * @param value object value + * @throws javax.xml.stream.XMLStreamException if an encoding problem occurs + */ + public void writeValue(final @Nonnull XMLStreamWriter writer, final @Nonnull TypeDefinition type, final Object value) throws XMLStreamException { + if (value == null) { + LOG.debug("Value of {}:{} is null, not encoding it", type.getQName().getNamespace(), type.getQName().getLocalName()); + return; + } + + final TypeDefinition baseType = XmlUtils.resolveBaseTypeFrom(type); + if (baseType instanceof IdentityrefTypeDefinition) { + write(writer, (IdentityrefTypeDefinition) baseType, value); + } else if (baseType instanceof InstanceIdentifierTypeDefinition) { + write(writer, (InstanceIdentifierTypeDefinition) baseType, value); + } else { + final TypeDefinitionAwareCodec codec = codecProvider.codecFor(baseType); + String text; + if (codec != null) { + try { + text = codec.serialize(value); + } catch (ClassCastException e) { + LOG.error("Provided node value {} did not have type {} required by mapping. Using stream instead.", value, baseType, e); + text = String.valueOf(value); + } + } else { + LOG.error("Failed to find codec for {}, falling back to using stream", baseType); + text = String.valueOf(value); + } + writer.writeCharacters(text); + } + } + + private static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull IdentityrefTypeDefinition type, final @Nonnull Object value) throws XMLStreamException { + if (value instanceof QName) { + final QName qname = (QName) value; + final String prefix; + if (qname.getPrefix() != null && !qname.getPrefix().isEmpty()) { + prefix = qname.getPrefix(); + } else { + prefix = "x"; + } + + writer.writeNamespace(prefix, qname.getNamespace().toString()); + writer.writeCharacters(prefix + ':' + qname.getLocalName()); + } else { + LOG.debug("Value of {}:{} is not a QName but {}", type.getQName().getNamespace(), type.getQName().getLocalName(), value.getClass()); + writer.writeCharacters(String.valueOf(value)); + } + } + + private static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull InstanceIdentifierTypeDefinition type, final @Nonnull Object value) throws XMLStreamException { + if (value instanceof YangInstanceIdentifier) { + write(writer, (YangInstanceIdentifier)value); + } else { + LOG.debug("Value of {}:{} is not an InstanceIdentifier but {}", type.getQName().getNamespace(), type.getQName().getLocalName(), value.getClass()); + writer.writeCharacters(String.valueOf(value)); + } + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/XmlUtils.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlUtils.java similarity index 52% rename from opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/XmlUtils.java rename to opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlUtils.java index 5fb2bb8772..45871f5402 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/XmlUtils.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlUtils.java @@ -5,26 +5,34 @@ * 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; +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.xml.XmlDocumentUtils; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +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.controller.netconf.util.xml.XmlUtil; +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.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; @@ -35,22 +43,37 @@ import javax.xml.transform.stream.StreamResult; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.StringWriter; +import java.util.List; +import java.util.Map; import java.util.Set; +/** + * Common XML-related utility methods, which are not specific to a particular + * JAXP API. + */ public class XmlUtils { + public static final XmlCodecProvider DEFAULT_XML_CODEC_PROVIDER = new XmlCodecProvider() { + @Override + public TypeDefinitionAwareCodec> codecFor(final TypeDefinition baseType) { + return TypeDefinitionAwareCodec.from(baseType); + } + }; + + private XmlUtils() { + } private static final Logger LOG = LoggerFactory.getLogger(XmlUtils.class); public static String inputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){ if (cNode == null) return new String(); - //Document domTree = NodeUtils.buildShadowDomTree(cNode); Document domTree = null; try { Set rpcs = schemaContext.getOperations(); for(RpcDefinition rpc : rpcs) { if(rpc.getQName().equals(cNode.getNodeType())){ - domTree = XmlDocumentUtils.toDocument(cNode, rpc.getInput(), XmlDocumentUtils.defaultValueCodecProvider()); + CompositeNode inputContainer = cNode.getFirstCompositeByName(QName.create(cNode.getNodeType(), "input")); + domTree = XmlDocumentUtils.toDocument(inputContainer, rpc.getInput(), XmlDocumentUtils.defaultValueCodecProvider()); break; } } @@ -64,13 +87,12 @@ public class XmlUtils { public static String outputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){ if (cNode == null) return new String(); - //Document domTree = NodeUtils.buildShadowDomTree(cNode); Document domTree = null; try { Set rpcs = schemaContext.getOperations(); for(RpcDefinition rpc : rpcs) { if(rpc.getQName().equals(cNode.getNodeType())){ - domTree = XmlDocumentUtils.toDocument(cNode, rpc.getInput(), XmlDocumentUtils.defaultValueCodecProvider()); + domTree = XmlDocumentUtils.toDocument(cNode, rpc.getOutput(), XmlDocumentUtils.defaultValueCodecProvider()); break; } } @@ -121,7 +143,7 @@ public class XmlUtils { public static CompositeNode inputXmlToCompositeNode(QName rpc, String xml, SchemaContext schemaContext){ if (xml==null || xml.length()==0) return null; - Node dataTree = null; + CompositeNode compositeNode = null; try { Document doc = XmlUtil.readXmlToDocument(xml); @@ -129,7 +151,14 @@ public class XmlUtils { Set rpcs = schemaContext.getOperations(); for(RpcDefinition rpcDef : rpcs) { if(rpcDef.getQName().equals(rpc)){ - dataTree = XmlDocumentUtils.toDomNode(doc.getDocumentElement(), Optional.of(rpcDef.getInput()), Optional.of(XmlDocumentUtils.defaultValueCodecProvider())); + QName input = rpcDef.getInput().getQName(); + Element xmlData = (Element) doc.getElementsByTagNameNS(input.getNamespace().toString(), "input").item(0); + List> dataNodes = XmlDocumentUtils.toDomNodes(xmlData, + Optional.of(rpcDef.getInput().getChildNodes()), schemaContext); + final CompositeNodeBuilder it = ImmutableCompositeNode.builder(); + it.setQName(input); + it.add(ImmutableCompositeNode.create(input, dataNodes)); + compositeNode = it.toInstance(); break; } } @@ -139,7 +168,45 @@ public class XmlUtils { LOG.error("Error during building data tree from XML", e); } - LOG.debug("xmlToCompositeNode " + dataTree.toString()); - return (CompositeNode) dataTree; + LOG.debug("xmlToCompositeNode " + compositeNode.toString()); + return compositeNode; } + + public static TypeDefinition resolveBaseTypeFrom(final @Nonnull TypeDefinition type) { + TypeDefinition superType = type; + while (superType.getBaseType() != null) { + superType = superType.getBaseType(); + } + return superType; + } + + static String encodeIdentifier(final RandomPrefix prefixes, final YangInstanceIdentifier id) { + StringBuilder textContent = new StringBuilder(); + for (PathArgument pathArgument : id.getPathArguments()) { + textContent.append('/'); + textContent.append(prefixes.encodeQName(pathArgument.getNodeType())); + if (pathArgument instanceof NodeIdentifierWithPredicates) { + Map predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues(); + + for (QName keyValue : predicates.keySet()) { + Object value = predicates.get(keyValue); + String type = value.getClass().getName(); + String predicateValue = String.valueOf(value); + textContent.append('['); + textContent.append(prefixes.encodeQName(keyValue)); + textContent.append("='"); + textContent.append(predicateValue); + textContent.append("@"); + textContent.append(type); + textContent.append("']"); + } + } else if (pathArgument instanceof NodeWithValue) { + textContent.append("[.='"); + textContent.append(((NodeWithValue) pathArgument).getValue()); + textContent.append("']"); + } + } + + return textContent.toString(); + } } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/resources/application.conf b/opendaylight/md-sal/sal-remoterpc-connector/src/main/resources/application.conf index 9585e9ffb6..6088dd0e0e 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/resources/application.conf +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/resources/application.conf @@ -7,7 +7,7 @@ odl-cluster{ remote { log-remote-lifecycle-events = off netty.tcp { - hostname = "192.168.141.142" + hostname = "192.168.141.141" port = 2551 } } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RpcBrokerTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RpcBrokerTest.java index 595d8331eb..392c1e637d 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RpcBrokerTest.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RpcBrokerTest.java @@ -35,6 +35,7 @@ import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.ModifyAction; import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; import org.opendaylight.yangtools.yang.model.api.SchemaContext; @@ -72,15 +73,16 @@ public class RpcBrokerTest { Broker.ProviderSession brokerSession = Mockito.mock(Broker.ProviderSession.class); SchemaContext schemaContext = mock(SchemaContext.class); ActorRef rpcBroker = system.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext)); - QName rpc = new QName(new URI("actor1"), "actor1"); - InvokeRpc invokeMsg = new InvokeRpc(rpc, null); + QName rpc = new QName(new URI("noactor1"), "noactor1"); + CompositeNode input = new ImmutableCompositeNode(QName.create("ns", "2013-12-09", "no child"), new ArrayList>(), ModifyAction.REPLACE); + InvokeRpc invokeMsg = new InvokeRpc(rpc, input); rpcBroker.tell(invokeMsg, getRef()); Boolean getMsg = new ExpectMsg("ErrorResponse") { protected Boolean match(Object in) { if (in instanceof ErrorResponse) { ErrorResponse reply = (ErrorResponse)in; - return "No remote actor found for rpc execution.".equals(reply.getException().getMessage()); + return reply.getException().getMessage().contains("No remote actor found for rpc execution of :"); } else { throw noMatch(); } @@ -144,7 +146,8 @@ public class RpcBrokerTest { SchemaContext schemaContext = mock(SchemaContext.class); ActorRef rpcBroker = system.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext)); QName rpc = new QName(new URI("actor1"), "actor1"); - InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, null); + CompositeNode input = new ImmutableCompositeNode(QName.create("ns", "2013-12-09", "child1"), new ArrayList>(), ModifyAction.REPLACE); + InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, YangInstanceIdentifier.create(new YangInstanceIdentifier.NodeIdentifier(rpc)), input); rpcBroker.tell(invokeMsg, getRef()); Boolean getMsg = new ExpectMsg("ErrorResponse") { @@ -176,7 +179,8 @@ public class RpcBrokerTest { ActorRef rpcBrokerRemote = system.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext), "actor2"); // Add Routed RPC in table QName rpc = new QName(new URI("actor2"), "actor2"); - RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, rpc, null); + YangInstanceIdentifier identifier = YangInstanceIdentifier.create(new YangInstanceIdentifier.NodeIdentifier(rpc)); + RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, rpc, identifier); final String route = rpcBrokerRemote.path().toString(); Set> routeIds = new HashSet<>(); routeIds.add(routeId); @@ -192,7 +196,7 @@ public class RpcBrokerTest { RpcResult result = Rpcs.getRpcResult(true, invokeRpcResult, errors); Future> rpcResult = Futures.immediateFuture(result); when(brokerSession.rpc(rpc, input)).thenReturn(rpcResult); - InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, input); + InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, identifier, input); rpcBroker.tell(invokeMsg, getRef()); //verify response msg diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RoutingTableTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RoutingTableTest.java index a57402a793..129a5a56e8 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RoutingTableTest.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RoutingTableTest.java @@ -39,7 +39,8 @@ public class RoutingTableTest { @Test public void addGlobalRouteNullRouteTest() { try { - RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, null, null); + QName type = new QName(new URI("actor1"), "actor1"); + RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, type, null); routingTable.addGlobalRoute(routeId, null); Assert.fail("Null pointer exception was not thrown."); @@ -109,7 +110,8 @@ public class RoutingTableTest { @Test public void addRoutedRpcNullRouteTest() { try { - RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, null, null); + QName type = new QName(new URI("actor1"), "actor1"); + RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, type, null); routingTable.addRoutedRpc(routeId, null); diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/utils/XmlUtilsTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/utils/XmlUtilsTest.java new file mode 100644 index 0000000000..92340b63eb --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/utils/XmlUtilsTest.java @@ -0,0 +1,91 @@ +/* + * 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.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.SimpleNode; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +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.Iterator; + + +public class XmlUtilsTest { + + private static final DocumentBuilderFactory BUILDERFACTORY; + + static { + final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setCoalescing(true); + factory.setIgnoringElementContentWhitespace(true); + factory.setIgnoringComments(true); + BUILDERFACTORY = factory; + } + + private SchemaContext schema; + private RpcDefinition testRpc; + + public static final String XML_CONTENT = "" + + "flowid" + + "/ltha:node/ltha:node1[ltha:id='3@java.lang.Short']" + + ""; + + @Before + public void setUp() throws Exception { + final ByteSource byteSource = new ByteSource() { + @Override + public InputStream openStream() throws IOException { + return XmlUtilsTest.this.getClass().getResourceAsStream("rpcTest.yang"); + } + }; + schema = new YangParserImpl().parseSources(Lists.newArrayList(byteSource)); + final Module rpcTestModule = schema.getModules().iterator().next(); + testRpc = rpcTestModule.getRpcs().iterator().next(); + } + + @Test + public void testInputXmlToCompositeNode() { + CompositeNode node = XmlUtils.inputXmlToCompositeNode(testRpc.getQName(), XML_CONTENT, schema); + ImmutableList input = (ImmutableList)node.getValue().get(0).getValue(); + SimpleNode firstNode = input.get(0); + + Assert.assertEquals("id", firstNode.getNodeType().getLocalName()); + Assert.assertEquals("flowid", firstNode.getValue()); + + SimpleNode secondNode = input.get(1); + Assert.assertEquals("flow", secondNode.getNodeType().getLocalName()); + + YangInstanceIdentifier instance = (YangInstanceIdentifier) secondNode.getValue(); + Iterable iterable = instance.getPathArguments(); + Iterator it = iterable.iterator(); + YangInstanceIdentifier.NodeIdentifier firstPath = (YangInstanceIdentifier.NodeIdentifier) it.next(); + Assert.assertEquals("node", firstPath.getNodeType().getLocalName()); + YangInstanceIdentifier.NodeIdentifierWithPredicates secondPath = (YangInstanceIdentifier.NodeIdentifierWithPredicates)it.next(); + Short value = (Short)secondPath.getKeyValues().values().iterator().next(); + Short expected = 3; + Assert.assertEquals(expected, value); + } + + +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/org/opendaylight/controller/remote/rpc/utils/rpcTest.yang b/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/org/opendaylight/controller/remote/rpc/utils/rpcTest.yang new file mode 100644 index 0000000000..5fc564fec3 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/org/opendaylight/controller/remote/rpc/utils/rpcTest.yang @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +module rpc-test { + yang-version 1; + namespace "urn:opendaylight:controller:rpc:test"; + prefix "rpct"; + + revision 2014-07-29 { + description "rpc test"; + } + + typedef flow-ref { + type instance-identifier; + } + + rpc add-flow { + input { + leaf id { + type string; + } + + leaf flow { + type flow-ref; + } + } + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 8bebd2aa61..995917b62d 100644 --- a/pom.xml +++ b/pom.xml @@ -117,7 +117,6 @@ opendaylight/commons/concepts - opendaylight/commons/protocol-framework opendaylight/commons/httpclient opendaylight/commons/checkstyle opendaylight/commons/opendaylight -- 2.36.6