From fe1c9b6c6080f1d23ffaea6c03252f3c0c26184f Mon Sep 17 00:00:00 2001 From: Tony Tkacik Date: Tue, 3 Dec 2013 20:00:20 +0100 Subject: [PATCH] Fixed serialization issues with Augments and java types Change-Id: I55a522423c4bb6ac809527c175d1689897c35fb2 Signed-off-by: Tony Tkacik --- .../md-sal/sal-rest-connector/pom.xml | 5 + .../controller/sal/rest/impl/JsonReader.java | 3 +- .../controller/sal/rest/impl/XmlReader.java | 8 +- .../restconf/impl/CompositeNodeWrapper.java | 1 - .../sal/restconf/impl/ControllerContext.xtend | 6 +- .../sal/restconf/impl/EmptyNodeWrapper.java | 111 ++++++++++++++++++ .../sal/restconf/impl/RestconfImpl.xtend | 65 ++++++++-- .../sal/restconf/impl/SimpleNodeWrapper.java | 19 +-- .../impl/test/FromXmlToCompositeNodeTest.java | 4 +- 9 files changed, 193 insertions(+), 29 deletions(-) create mode 100644 opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/EmptyNodeWrapper.java diff --git a/opendaylight/md-sal/sal-rest-connector/pom.xml b/opendaylight/md-sal/sal-rest-connector/pom.xml index 3e13a584d4..5e798b6720 100644 --- a/opendaylight/md-sal/sal-rest-connector/pom.xml +++ b/opendaylight/md-sal/sal-rest-connector/pom.xml @@ -41,6 +41,11 @@ org.opendaylight.yangtools yang-data-impl ${yang.version} + + + org.opendaylight.yangtools + yang-model-util + ${yang.version} com.google.code.gson diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonReader.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonReader.java index a2ae1c9f7f..83e2d20d0e 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonReader.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonReader.java @@ -7,6 +7,7 @@ import java.util.Map.Entry; import java.util.Set; import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper; +import org.opendaylight.controller.sal.restconf.impl.EmptyNodeWrapper; import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper; import com.google.common.collect.Lists; @@ -69,7 +70,7 @@ class JsonReader { } } else if (childType.isJsonArray()) { if (childType.getAsJsonArray().size() == 1 && childType.getAsJsonArray().get(0).isJsonNull()) { - parent.addValue(new SimpleNodeWrapper(getNamespaceFrom(childName), getLocalNameFrom(childName), null)); + parent.addValue(new EmptyNodeWrapper(getNamespaceFrom(childName), getLocalNameFrom(childName))); } else { for (JsonElement childOfChildType : childType.getAsJsonArray()) { diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlReader.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlReader.java index bf7ff7d435..a53281492f 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlReader.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlReader.java @@ -14,8 +14,10 @@ import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper; +import org.opendaylight.controller.sal.restconf.impl.EmptyNodeWrapper; import org.opendaylight.controller.sal.restconf.impl.NodeWrapper; import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper; +import org.opendaylight.yangtools.yang.data.api.Node; public class XmlReader { @@ -121,7 +123,7 @@ public class XmlReader { return false; } - private SimpleNodeWrapper resolveSimpleNodeFromStartElement(final StartElement startElement) + private NodeWrapper> resolveSimpleNodeFromStartElement(final StartElement startElement) throws XMLStreamException { checkArgument(startElement != null, "Start Element cannot be NULL!"); String data = null; @@ -141,7 +143,9 @@ public class XmlReader { } } } - + if(data == null) { + return new EmptyNodeWrapper(getNamespaceFrom(startElement), getLocalNameFrom(startElement)); + } return new SimpleNodeWrapper(getNamespaceFrom(startElement), getLocalNameFrom(startElement), data); } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/CompositeNodeWrapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/CompositeNodeWrapper.java index fc146256e8..0ded60dae4 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/CompositeNodeWrapper.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/CompositeNodeWrapper.java @@ -230,5 +230,4 @@ public final class CompositeNodeWrapper implements NodeWrapper, C public Set>>> entrySet() { return unwrap().entrySet(); } - } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend index 470b4735a4..602e8b9242 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend @@ -32,6 +32,7 @@ import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil import static com.google.common.base.Preconditions.* +import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec class ControllerContext implements SchemaServiceListener { @@ -288,9 +289,10 @@ class ControllerContext implements SchemaServiceListener { private def void addKeyValue(HashMap map, DataSchemaNode node, String uriValue) { checkNotNull(uriValue); checkArgument(node instanceof LeafSchemaNode); - val decoded = URLDecoder.decode(uriValue); + val urlDecoded = URLDecoder.decode(uriValue); + val typedef = (node as LeafSchemaNode).type; + val decoded = TypeDefinitionAwareCodec.from(typedef)?.deserialize(urlDecoded) map.put(node.QName, decoded); - } private def String toModuleName(String str) { diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/EmptyNodeWrapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/EmptyNodeWrapper.java new file mode 100644 index 0000000000..c146954dce --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/EmptyNodeWrapper.java @@ -0,0 +1,111 @@ +package org.opendaylight.controller.sal.restconf.impl; + +import java.net.URI; +import java.util.Collections; + +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.MutableSimpleNode; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.api.SimpleNode; +import org.opendaylight.yangtools.yang.data.impl.NodeFactory; + +import com.google.common.base.Preconditions; + +public final class EmptyNodeWrapper implements NodeWrapper>, Node { + + private Node unwrapped; + + private String localName; + private URI namespace; + private QName name; + + private boolean composite; + + public boolean isComposite() { + return composite; + } + + public void setComposite(boolean composite) { + this.composite = composite; + } + + public EmptyNodeWrapper(URI namespace, String localName) { + this.localName = Preconditions.checkNotNull(localName); + this.namespace = namespace; + } + + @Override + public void setQname(QName name) { + Preconditions.checkState(unwrapped == null, "Cannot change the object, due to data inconsistencies."); + this.name = name; + } + + @Override + public String getLocalName() { + if (unwrapped != null) { + return unwrapped.getNodeType().getLocalName(); + } + return localName; + } + + @Override + public URI getNamespace() { + if (unwrapped != null) { + return unwrapped.getNodeType().getNamespace(); + } + return namespace; + } + + @Override + public void setNamespace(URI namespace) { + Preconditions.checkState(unwrapped == null, "Cannot change the object, due to data inconsistencies."); + this.namespace = namespace; + } + + @Override + public Node unwrap() { + if (unwrapped == null) { + if (name == null) { + Preconditions.checkNotNull(namespace); + name = new QName(namespace, localName); + } + if(composite) { + unwrapped = NodeFactory.createImmutableCompositeNode(name, null, Collections.>emptyList(),null); + } else { + unwrapped = NodeFactory.createImmutableSimpleNode(name, null, null); + } + namespace = null; + localName = null; + name = null; + } + return unwrapped; + } + + @Override + public QName getNodeType() { + return unwrap().getNodeType(); + } + + @Override + public CompositeNode getParent() { + return unwrap().getParent(); + } + + @Override + public Void getValue() { + return null; + } + + @Override + public QName getKey() { + return unwrap().getKey(); + } + + @Override + public Void setValue(Void value) { + return null; + } + +} diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend index a6a71fef55..35352e0819 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend @@ -12,6 +12,12 @@ import org.opendaylight.yangtools.yang.data.impl.NodeFactory import org.opendaylight.yangtools.yang.model.api.ChoiceNode import org.opendaylight.yangtools.yang.model.api.DataNodeContainer import org.opendaylight.yangtools.yang.model.api.DataSchemaNode +import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec +import org.opendaylight.yangtools.yang.model.api.TypeDefinition +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode +import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode +import org.opendaylight.yangtools.yang.common.QName +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode class RestconfImpl implements RestconfService { @@ -54,7 +60,7 @@ class RestconfImpl implements RestconfService { override createConfigurationData(String identifier, CompositeNode payload) { val identifierWithSchemaNode = identifier.resolveInstanceIdentifier - val value = resolveNodeNamespaceBySchema(payload, identifierWithSchemaNode.schemaNode) + val value = normalizeNode(payload, identifierWithSchemaNode.schemaNode) val status = broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier,value).get(); switch status.result { case TransactionStatus.COMMITED: Response.status(Response.Status.OK).build @@ -64,7 +70,7 @@ class RestconfImpl implements RestconfService { override updateConfigurationData(String identifier, CompositeNode payload) { val identifierWithSchemaNode = identifier.resolveInstanceIdentifier - val value = resolveNodeNamespaceBySchema(payload, identifierWithSchemaNode.schemaNode) + val value = normalizeNode(payload, identifierWithSchemaNode.schemaNode) val status = broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier,value).get(); switch status.result { case TransactionStatus.COMMITED: Response.status(Response.Status.NO_CONTENT).build @@ -77,7 +83,7 @@ class RestconfImpl implements RestconfService { if (rpc === null) { throw new ResponseException(Response.Status.NOT_FOUND, "RPC does not exist."); } - val value = resolveNodeNamespaceBySchema(payload, rpc.input) + val value = normalizeNode(payload, rpc.input) val List> input = new ArrayList input.add(value) val rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, input, null, null) @@ -113,38 +119,69 @@ class RestconfImpl implements RestconfService { return identifierWithSchemaNode } - private def CompositeNode resolveNodeNamespaceBySchema(CompositeNode node, DataSchemaNode schema) { + private def CompositeNode normalizeNode(CompositeNode node, DataSchemaNode schema) { if (node instanceof CompositeNodeWrapper) { - addNamespaceToNodeFromSchemaRecursively(node as CompositeNodeWrapper, schema) + normalizeNode(node as CompositeNodeWrapper, schema,null) return (node as CompositeNodeWrapper).unwrap() } return node } - private def void addNamespaceToNodeFromSchemaRecursively(NodeWrapper nodeBuilder, DataSchemaNode schema) { + private def void normalizeNode(NodeWrapper nodeBuilder, DataSchemaNode schema,QName previousAugment) { if (schema === null) { throw new ResponseException(Response.Status.BAD_REQUEST, "Data has bad format\n" + nodeBuilder.localName + " does not exist in yang schema."); } - val moduleName = controllerContext.findModuleByNamespace(schema.QName.namespace); - if (nodeBuilder.namespace === null || nodeBuilder.namespace == schema.QName.namespace || + var validQName = schema.QName + var currentAugment = previousAugment; + if(schema.augmenting) { + currentAugment = schema.QName + } else if(previousAugment !== null && schema.QName.namespace !== previousAugment.namespace) { + validQName = QName.create(currentAugment,schema.QName.localName); + } + val moduleName = controllerContext.findModuleByNamespace(validQName.namespace); + if (nodeBuilder.namespace === null || nodeBuilder.namespace == validQName.namespace || nodeBuilder.namespace.path == moduleName) { - nodeBuilder.qname = schema.QName + nodeBuilder.qname = validQName } else { throw new ResponseException(Response.Status.BAD_REQUEST, "Data has bad format\nIf data is in XML format then namespace for " + nodeBuilder.localName + " should be " + schema.QName.namespace + ".\n If data is in Json format then module name for " + nodeBuilder.localName + " should be " + moduleName + "."); } + if (nodeBuilder instanceof CompositeNodeWrapper) { val List> children = (nodeBuilder as CompositeNodeWrapper).getValues for (child : children) { - addNamespaceToNodeFromSchemaRecursively(child, - findFirstSchemaByLocalName(child.localName, (schema as DataNodeContainer).childNodes)) + normalizeNode(child, + findFirstSchemaByLocalName(child.localName, (schema as DataNodeContainer).childNodes),currentAugment) + } + } else if (nodeBuilder instanceof SimpleNodeWrapper) { + val simpleNode = (nodeBuilder as SimpleNodeWrapper) + val stringValue = simpleNode.value as String; + + val objectValue = TypeDefinitionAwareCodec.from(schema.typeDefinition)?.deserialize(stringValue); + simpleNode.setValue(objectValue) + } else if (nodeBuilder instanceof EmptyNodeWrapper) { + val emptyNodeBuilder = nodeBuilder as EmptyNodeWrapper + if(schema instanceof LeafSchemaNode) { + emptyNodeBuilder.setComposite(false); + } else if(schema instanceof ContainerSchemaNode) { + // FIXME: Add presence check + emptyNodeBuilder.setComposite(true); } } } + private def dispatch TypeDefinition typeDefinition(LeafSchemaNode node) { + node.type + } + + private def dispatch TypeDefinition typeDefinition(LeafListSchemaNode node) { + node.type + } + + private def DataSchemaNode findFirstSchemaByLocalName(String localName, Set schemas) { for (schema : schemas) { if (schema instanceof ChoiceNode) { @@ -155,7 +192,11 @@ class RestconfImpl implements RestconfService { } } } else { - return schemas.findFirst[n|n.QName.localName.equals(localName)] + val result = schemas.findFirst[n|n.QName.localName.equals(localName)] + if(result !== null) { + return result; + + } } } return null diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/SimpleNodeWrapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/SimpleNodeWrapper.java index c1576895fd..9f3c6e51e9 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/SimpleNodeWrapper.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/SimpleNodeWrapper.java @@ -11,16 +11,16 @@ import org.opendaylight.yangtools.yang.data.impl.NodeFactory; import com.google.common.base.Preconditions; -public final class SimpleNodeWrapper implements NodeWrapper>, SimpleNode { +public final class SimpleNodeWrapper implements NodeWrapper>, SimpleNode { - private SimpleNode simpleNode; + private SimpleNode simpleNode; private String localName; - private String value; + private Object value; private URI namespace; private QName name; - public SimpleNodeWrapper(String localName, String value) { + public SimpleNodeWrapper(String localName, Object value) { this.localName = Preconditions.checkNotNull(localName); this.value = value; } @@ -59,7 +59,7 @@ public final class SimpleNodeWrapper implements NodeWrapper>, Simp } @Override - public SimpleNode unwrap() { + public SimpleNode unwrap() { if (simpleNode == null) { if (name == null) { Preconditions.checkNotNull(namespace); @@ -72,7 +72,7 @@ public final class SimpleNodeWrapper implements NodeWrapper>, Simp localName = null; name = null; } - return simpleNode; + return (SimpleNode) simpleNode; } @Override @@ -86,7 +86,7 @@ public final class SimpleNodeWrapper implements NodeWrapper>, Simp } @Override - public String getValue() { + public Object getValue() { return unwrap().getValue(); } @@ -96,7 +96,7 @@ public final class SimpleNodeWrapper implements NodeWrapper>, Simp } @Override - public MutableSimpleNode asMutable() { + public MutableSimpleNode asMutable() { return unwrap().asMutable(); } @@ -106,9 +106,10 @@ public final class SimpleNodeWrapper implements NodeWrapper>, Simp } @Override - public String setValue(String value) { + public Object setValue(Object value) { return unwrap().setValue(value); } + } diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/FromXmlToCompositeNodeTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/FromXmlToCompositeNodeTest.java index 5aa6d0339d..a72bd60d30 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/FromXmlToCompositeNodeTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/FromXmlToCompositeNodeTest.java @@ -138,7 +138,7 @@ public class FromXmlToCompositeNodeTest { SimpleNode cont11_lf111 = (SimpleNode) cont11.getChildren().get(0); assertEquals(nameSpaceCont, cont11_lf111.getNodeType().getNamespace().toString()); assertEquals("lf111", cont11_lf111.getNodeType().getLocalName()); - assertEquals("100", cont11_lf111.getValue()); + assertEquals((short) 100, cont11_lf111.getValue()); // :lst1_2 } @@ -251,7 +251,7 @@ public class FromXmlToCompositeNodeTest { SimpleNode cont1_lf11 = (SimpleNode) cont1suf.getChildren().get(0); assertEquals(nameSpace, cont1_lf11.getNodeType().getNamespace().toString()); assertEquals("lf11" + suf, cont1_lf11.getNodeType().getLocalName()); - assertEquals("100", cont1_lf11.getValue()); + assertEquals((short) 100, cont1_lf11.getValue()); } private CompositeNode compositeContainerFromXml(String xmlPath, boolean dummyNamespaces) { -- 2.36.6