X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fsal-rest-connector%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fsal%2Frest%2Fimpl%2FXmlNormalizedNodeBodyReader.java;h=294e56a651b83294c491a639f52f674555c636da;hb=31a4027d1a9dff2e0e44e5d4cf97dfd957efa805;hp=062a4488f369075a70c4322f4d75b5a1ce24841b;hpb=458e216f175088b73c367c76264a6bbfb2d067b7;p=controller.git diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlNormalizedNodeBodyReader.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlNormalizedNodeBodyReader.java index 062a4488f3..294e56a651 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlNormalizedNodeBodyReader.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlNormalizedNodeBodyReader.java @@ -7,12 +7,16 @@ */ package org.opendaylight.controller.sal.rest.impl; -import com.google.common.base.Optional; +import com.google.common.collect.Iterables; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; +import java.util.Deque; import java.util.List; import javax.ws.rs.Consumes; import javax.ws.rs.WebApplicationException; @@ -30,12 +34,21 @@ import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext; import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException; import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag; import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlUtils; +import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils; import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory; +import org.opendaylight.yangtools.yang.model.api.AugmentationSchema; +import org.opendaylight.yangtools.yang.model.api.AugmentationTarget; +import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; +import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode; 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.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; @@ -47,11 +60,19 @@ import org.w3c.dom.Element; public class XmlNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsProvider implements MessageBodyReader { private final static Logger LOG = LoggerFactory.getLogger(XmlNormalizedNodeBodyReader.class); - private final static DomToNormalizedNodeParserFactory DOM_PARSER_FACTORY = DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER); private static final DocumentBuilderFactory BUILDERFACTORY; static { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + try { + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + factory.setFeature("http://xml.org/sax/features/external-general-entities", false); + factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + factory.setXIncludeAware(false); + factory.setExpandEntityReferences(false); + } catch (final ParserConfigurationException e) { + throw new ExceptionInInitializerError(e); + } factory.setNamespaceAware(true); factory.setCoalescing(true); factory.setIgnoringElementContentWhitespace(true); @@ -71,34 +92,141 @@ public class XmlNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsPro final MultivaluedMap httpHeaders, final InputStream entityStream) throws IOException, WebApplicationException { try { - Optional path = getIdentifierWithSchema(); + final InstanceIdentifierContext path = getInstanceIdentifierContext(); + + if (entityStream.available() < 1) { + // represent empty nopayload input + return new NormalizedNodeContext(path, null); + } final DocumentBuilder dBuilder; try { dBuilder = BUILDERFACTORY.newDocumentBuilder(); - } catch (ParserConfigurationException e) { + } catch (final ParserConfigurationException e) { throw new RuntimeException("Failed to parse XML document", e); } - Document doc = dBuilder.parse(entityStream); + final Document doc = dBuilder.parse(entityStream); - NormalizedNode result = parse(path.get(),doc); - return new NormalizedNodeContext(path.get(),result); - } catch (Exception e) { - LOG.debug("Error parsing json input", e); + return parse(path,doc); + } catch (final RestconfDocumentedException e){ + throw e; + } catch (final Exception e) { + LOG.debug("Error parsing xml input", e); throw new RestconfDocumentedException("Error parsing input: " + e.getMessage(), ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); } } - private static NormalizedNode parse(InstanceIdentifierContext pathContext,Document doc) { - List elements = Collections.singletonList(doc.getDocumentElement()); - DataSchemaNode schemaNode = pathContext.getSchemaNode(); + private NormalizedNodeContext parse(final InstanceIdentifierContext pathContext,final Document doc) { + + final List elements = Collections.singletonList(doc.getDocumentElement()); + final SchemaNode schemaNodeContext = pathContext.getSchemaNode(); + DataSchemaNode schemaNode; + boolean isRpc = false; + if (schemaNodeContext instanceof RpcDefinition) { + schemaNode = ((RpcDefinition) schemaNodeContext).getInput(); + isRpc = true; + } else if (schemaNodeContext instanceof DataSchemaNode) { + schemaNode = (DataSchemaNode) schemaNodeContext; + } else { + throw new IllegalStateException("Unknow SchemaNode"); + } + + final String docRootElm = doc.getDocumentElement().getLocalName(); + final List iiToDataList = new ArrayList<>(); + InstanceIdentifierContext outIIContext; + + + // FIXME the factory instance should be cached if the schema context is the same + final DomToNormalizedNodeParserFactory parserFactory = + DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER, pathContext.getSchemaContext()); + + if (isPost() && !isRpc) { + final Deque foundSchemaNodes = findPathToSchemaNodeByName(schemaNode, docRootElm); + if (foundSchemaNodes.isEmpty()) { + throw new IllegalStateException(String.format("Child \"%s\" was not found in parent schema node \"%s\"", + docRootElm, schemaNode.getQName())); + } + while (!foundSchemaNodes.isEmpty()) { + final Object child = foundSchemaNodes.pop(); + if (child instanceof AugmentationSchema) { + final AugmentationSchema augmentSchemaNode = (AugmentationSchema) child; + iiToDataList.add(SchemaUtils.getNodeIdentifierForAugmentation(augmentSchemaNode)); + } else if (child instanceof DataSchemaNode) { + schemaNode = (DataSchemaNode) child; + iiToDataList.add(new YangInstanceIdentifier.NodeIdentifier(schemaNode.getQName())); + } + } + } + + NormalizedNode parsed = null; + if(schemaNode instanceof ContainerSchemaNode) { - return DOM_PARSER_FACTORY.getContainerNodeParser().parse(Collections.singletonList(doc.getDocumentElement()), (ContainerSchemaNode) schemaNode); + parsed = parserFactory.getContainerNodeParser().parse(Collections.singletonList(doc.getDocumentElement()), (ContainerSchemaNode) schemaNode); } else if(schemaNode instanceof ListSchemaNode) { - ListSchemaNode casted = (ListSchemaNode) schemaNode; - return DOM_PARSER_FACTORY.getMapEntryNodeParser().parse(elements, casted); + final ListSchemaNode casted = (ListSchemaNode) schemaNode; + parsed = parserFactory.getMapEntryNodeParser().parse(elements, casted); + if (isPost()) { + iiToDataList.add(parsed.getIdentifier()); + } + } + // FIXME : add another DataSchemaNode extensions e.g. LeafSchemaNode + + final YangInstanceIdentifier fullIIToData = YangInstanceIdentifier.create(Iterables.concat( + pathContext.getInstanceIdentifier().getPathArguments(), iiToDataList)); + + outIIContext = new InstanceIdentifierContext<>(fullIIToData, pathContext.getSchemaNode(), pathContext.getMountPoint(), + pathContext.getSchemaContext()); + + return new NormalizedNodeContext(outIIContext, parsed); + } + + private static Deque findPathToSchemaNodeByName(final DataSchemaNode schemaNode, final String elementName) { + final Deque result = new ArrayDeque<>(); + final ArrayList choiceSchemaNodes = new ArrayList<>(); + final Collection children = ((DataNodeContainer) schemaNode).getChildNodes(); + for (final DataSchemaNode child : children) { + if (child instanceof ChoiceSchemaNode) { + choiceSchemaNodes.add((ChoiceSchemaNode) child); + } else if (child.getQName().getLocalName().equalsIgnoreCase(elementName)) { + result.push(child); + if (child.isAugmenting()) { + final AugmentationSchema augment = findCorrespondingAugment(schemaNode, child); + if (augment != null) { + result.push(augment); + } + } + return result; + } + } + + for (final ChoiceSchemaNode choiceNode : choiceSchemaNodes) { + for (final ChoiceCaseNode caseNode : choiceNode.getCases()) { + final Deque resultFromRecursion = findPathToSchemaNodeByName(caseNode, elementName); + if (!resultFromRecursion.isEmpty()) { + resultFromRecursion.push(choiceNode); + if (choiceNode.isAugmenting()) { + final AugmentationSchema augment = findCorrespondingAugment(schemaNode, choiceNode); + if (augment != null) { + resultFromRecursion.push(augment); + } + } + return resultFromRecursion; + } + } + } + return result; + } + + private static AugmentationSchema findCorrespondingAugment(final DataSchemaNode parent, final DataSchemaNode child) { + if (parent instanceof AugmentationTarget && !(parent instanceof ChoiceSchemaNode)) { + for (final AugmentationSchema augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) { + final DataSchemaNode childInAugmentation = augmentation.getDataChildByName(child.getQName()); + if (childInAugmentation != null) { + return augmentation; + } + } } return null; }