Bug 7933: NPE when posting using XML 62/57362/7
authormatus.kubica <matus.kubica@pantheon.tech>
Thu, 18 May 2017 14:32:26 +0000 (16:32 +0200)
committerIvan Hrasko <ivan.hrasko@pantheon.tech>
Tue, 23 May 2017 06:55:55 +0000 (08:55 +0200)
PUT operation:
- fix issue when putting malformed XML body did not
fail with appropriate status.

Change-Id: I6bff6467f4b99bed9adf1ac39def54822899761e
Signed-off-by: matus.kubica <matus.kubica@pantheon.tech>
Signed-off-by: Ivan Hrasko <ivan.hrasko@pantheon.tech>
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/XmlNormalizedNodeBodyReader.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/jersey/providers/XmlNormalizedNodeBodyReader.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/AbstractBodyReaderTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlBodyReader.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlBodyReaderMountPoint.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/AbstractBodyReaderTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/XmlBodyReaderMountPointTest.java [new file with mode: 0644]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/XmlBodyReaderTest.java
restconf/sal-rest-connector/src/test/resources/instanceidentifier/xml/bug7933.xml [new file with mode: 0644]

index e8fabe5638f70d26f1124c7fa02a97c44e66b601..826b655d1e224c2ec4e9349d26fe5bbb2f060ea0 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.netconf.sal.rest.impl;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.Iterables;
 import java.io.IOException;
 import java.io.InputStream;
@@ -33,6 +34,7 @@ import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.restconf.utils.RestconfConstants;
 import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
 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;
@@ -145,6 +147,14 @@ public class XmlNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsPro
                     iiToDataList.add(new YangInstanceIdentifier.NodeIdentifier(schemaNode.getQName()));
                 }
             }
+        // PUT
+        } else if (!isRpc) {
+            final QName scQName = schemaNode.getQName();
+            Preconditions.checkState(
+                    docRootElm.equals(scQName.getLocalName())
+                            && docRootNamespace.equals(scQName.getNamespace().toASCIIString()),
+                    String.format("Not correct message root element \"%s\", should be \"%s\"",
+                            docRootElm, scQName));
         }
 
         NormalizedNode<?, ?> parsed = null;
index 5bba5eb2c8852c5fe7b7476044bbba7e1d7cabe6..517b11eb20234da65819bb9bf11e74937e7e702b 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.restconf.jersey.providers;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.Iterables;
 import java.io.IOException;
 import java.io.InputStream;
@@ -34,6 +35,7 @@ import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.restconf.Rfc8040;
 import org.opendaylight.restconf.utils.RestconfConstants;
 import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
 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;
@@ -133,6 +135,14 @@ public class XmlNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsPro
                     iiToDataList.add(new YangInstanceIdentifier.NodeIdentifier(schemaNode.getQName()));
                 }
             }
+        // PUT
+        } else if (!isRpc) {
+            final QName scQName = schemaNode.getQName();
+            Preconditions.checkState(
+                    docRootElm.equals(scQName.getLocalName())
+                            && docRootNamespace.equals(scQName.getNamespace().toASCIIString()),
+                    String.format("Not correct message root element \"%s\", should be \"%s\"",
+                            docRootElm, scQName));
         }
 
         NormalizedNode<?, ?> parsed = null;
index f1a6f0f497107543b08128e339dfc9b3bdd1df42..3e2a36ba4277f861eea74d0799f9a7e79339668a 100644 (file)
@@ -35,8 +35,7 @@ public abstract class AbstractBodyReaderTest {
     private static Field uriField;
     private static Field requestField;
 
-    public AbstractBodyReaderTest() throws NoSuchFieldException,
-            SecurityException {
+    public AbstractBodyReaderTest() throws NoSuchFieldException {
         uriField = AbstractIdentifierAwareJaxRsProvider.class
                 .getDeclaredField("uriInfo");
         uriField.setAccessible(true);
index dfe899b927eb403904979a1979504f51b06793cb..c1a9c567ca02711a4f8466bef0773c292162ff77 100644 (file)
@@ -20,11 +20,14 @@ import java.net.URI;
 import java.text.ParseException;
 import java.util.Collection;
 import javax.ws.rs.core.MediaType;
+import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
 import org.opendaylight.netconf.sal.rest.impl.XmlNormalizedNodeBodyReader;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
@@ -54,7 +57,7 @@ public class TestXmlBodyReader extends AbstractBodyReaderTest {
         }
     }
 
-    public TestXmlBodyReader() throws NoSuchFieldException, SecurityException {
+    public TestXmlBodyReader() throws Exception {
         super();
         this.xmlBodyReader = new XmlNormalizedNodeBodyReader();
     }
@@ -243,4 +246,24 @@ public class TestXmlBodyReader extends AbstractBodyReaderTest {
         assertEquals("Not correct container found, namespace was ignored",
                 "bar:module", returnValue.getData().getNodeType().getNamespace().toString());
     }
+
+    /**
+     * Test PUT operation when message root element is not the same as the last element in request URI.
+     * PUT operation message should always start with schema node from URI otherwise exception should be
+     * thrown.
+     */
+    @Test
+    public void wrongRootElementTest() throws Exception {
+        mockBodyReader("instance-identifier-module:cont", this.xmlBodyReader, false);
+        final InputStream inputStream = TestXmlBodyReader.class.getResourceAsStream(
+                "/instanceidentifier/xml/bug7933.xml");
+        try {
+            this.xmlBodyReader.readFrom(null, null, null, this.mediaType, null, inputStream);
+            Assert.fail("Test should fail due to malformed PUT operation message");
+        } catch (final RestconfDocumentedException exception) {
+            final RestconfError restconfError = exception.getErrors().get(0);
+            Assert.assertEquals(RestconfError.ErrorType.PROTOCOL, restconfError.getErrorType());
+            Assert.assertEquals(RestconfError.ErrorTag.MALFORMED_MESSAGE, restconfError.getErrorTag());
+        }
+    }
 }
index 932f4c942bbb269ba1cf8b4962ca1cbd1f2d5e9b..3d35a58fbb78c15c13ecc4f8ed1c1ec9666d3403 100644 (file)
@@ -22,6 +22,7 @@ import java.net.URI;
 import java.text.ParseException;
 import java.util.Collection;
 import javax.ws.rs.core.MediaType;
+import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
@@ -30,6 +31,8 @@ import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
 import org.opendaylight.netconf.sal.rest.impl.XmlNormalizedNodeBodyReader;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
@@ -58,9 +61,7 @@ public class TestXmlBodyReaderMountPoint extends AbstractBodyReaderTest {
         }
     }
 
-
-    public TestXmlBodyReaderMountPoint() throws NoSuchFieldException,
-            SecurityException {
+    public TestXmlBodyReaderMountPoint() throws Exception {
         super();
         this.xmlBodyReader = new XmlNormalizedNodeBodyReader();
     }
@@ -226,4 +227,24 @@ public class TestXmlBodyReaderMountPoint extends AbstractBodyReaderTest {
         assertEquals("Not correct container found, namespace was ignored",
                 "bar:module", returnValue.getData().getNodeType().getNamespace().toString());
     }
+
+    /**
+     * Test PUT operation when message root element is not the same as the last element in request URI.
+     * PUT operation message should always start with schema node from URI otherwise exception should be
+     * thrown.
+     */
+    @Test
+    public void wrongRootElementTest() throws Exception {
+        mockBodyReader("instance-identifier-module:cont/yang-ext:mount", this.xmlBodyReader, false);
+        final InputStream inputStream = TestXmlBodyReader.class.getResourceAsStream(
+                "/instanceidentifier/xml/bug7933.xml");
+        try {
+            this.xmlBodyReader.readFrom(null, null, null, this.mediaType, null, inputStream);
+            Assert.fail("Test should fail due to malformed PUT operation message");
+        } catch (final RestconfDocumentedException exception) {
+            final RestconfError restconfError = exception.getErrors().get(0);
+            Assert.assertEquals(RestconfError.ErrorType.PROTOCOL, restconfError.getErrorType());
+            Assert.assertEquals(RestconfError.ErrorTag.MALFORMED_MESSAGE, restconfError.getErrorTag());
+        }
+    }
 }
index c7ba67921b0fd4d7726029da976306803f0397db..d0d2ceba011ff916c621b20266c990ab1f7e4afe 100644 (file)
@@ -78,6 +78,12 @@ abstract class AbstractBodyReaderTest {
         normalizedNodeProvider.setRequest(request);
     }
 
+    protected static void checkMountPointNormalizedNodeContext(
+            final NormalizedNodeContext nnContext) {
+        checkNormalizedNodeContext(nnContext);
+        assertNotNull(nnContext.getInstanceIdentifierContext().getMountPoint());
+    }
+
     protected static void checkNormalizedNodeContext(
             final NormalizedNodeContext nnContext) {
         assertNotNull(nnContext.getData());
diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/XmlBodyReaderMountPointTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/XmlBodyReaderMountPointTest.java
new file mode 100644 (file)
index 0000000..cb3d10c
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2015 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.restconf.jersey.providers;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.common.base.Optional;
+import java.io.File;
+import java.io.InputStream;
+import java.net.URI;
+import java.text.ParseException;
+import java.util.Collection;
+import javax.ws.rs.core.MediaType;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
+import org.opendaylight.controller.sal.rest.impl.test.providers.TestXmlBodyReader;
+import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class XmlBodyReaderMountPointTest extends AbstractBodyReaderTest {
+    private final XmlNormalizedNodeBodyReader xmlBodyReader;
+    private static SchemaContext schemaContext;
+
+    private static final QNameModule INSTANCE_IDENTIFIER_MODULE_QNAME = initializeInstanceIdentifierModule();
+
+    private static QNameModule initializeInstanceIdentifierModule() {
+        try {
+            return QNameModule.create(URI.create("instance:identifier:module"),
+                SimpleDateFormatUtil.getRevisionFormat().parse("2014-01-17"));
+        } catch (final ParseException e) {
+            throw new Error(e);
+        }
+    }
+
+    public XmlBodyReaderMountPointTest() throws Exception {
+        super();
+        this.xmlBodyReader = new XmlNormalizedNodeBodyReader();
+    }
+
+    @Override
+    protected MediaType getMediaType() {
+        return new MediaType(MediaType.APPLICATION_XML, null);
+    }
+
+    @BeforeClass
+    public static void initialization() throws Exception {
+        final Collection<File> testFiles = TestRestconfUtils.loadFiles("/instanceidentifier/yang");
+        testFiles.addAll(TestRestconfUtils.loadFiles("/invoke-rpc"));
+        schemaContext = YangParserTestUtils.parseYangSources(testFiles);
+
+        final DOMMountPointService mountPointService = mock(DOMMountPointService.class);
+        final DOMMountPoint mountPoint = mock(DOMMountPoint.class);
+
+        when(MOUNT_POINT_SERVICE_HANDLER.get()).thenReturn(mountPointService);
+        when(mountPointService.getMountPoint(any(YangInstanceIdentifier.class))).thenReturn(Optional.of(mountPoint));
+        when(mountPoint.getSchemaContext()).thenReturn(schemaContext);
+
+        CONTROLLER_CONTEXT.setSchemas(schemaContext);
+    }
+
+    @Test
+    public void moduleDataTest() throws Exception {
+        final DataSchemaNode dataSchemaNode = schemaContext
+                .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
+        final String uri = "instance-identifier-module:cont/yang-ext:mount/instance-identifier-module:cont";
+        mockBodyReader(uri, this.xmlBodyReader, false);
+        final InputStream inputStream = XmlBodyReaderMountPointTest.class
+                .getResourceAsStream("/instanceidentifier/xml/xmldata.xml");
+        final NormalizedNodeContext returnValue = this.xmlBodyReader.readFrom(null,
+                null, null, this.mediaType, null, inputStream);
+        checkMountPointNormalizedNodeContext(returnValue);
+        checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue);
+    }
+
+    @Test
+    public void moduleSubContainerDataPutTest() throws Exception {
+        final DataSchemaNode dataSchemaNode = schemaContext
+                .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
+        final String uri = "instance-identifier-module:cont/yang-ext:mount/instance-identifier-module:cont/cont1";
+        mockBodyReader(uri, this.xmlBodyReader, false);
+        final InputStream inputStream = XmlBodyReaderMountPointTest.class
+                .getResourceAsStream("/instanceidentifier/xml/xml_sub_container.xml");
+        final NormalizedNodeContext returnValue = this.xmlBodyReader.readFrom(null,
+                null, null, this.mediaType, null, inputStream);
+        checkMountPointNormalizedNodeContext(returnValue);
+        checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue,
+                QName.create(dataSchemaNode.getQName(), "cont1"));
+    }
+
+    @Test
+    public void moduleSubContainerDataPostTest() throws Exception {
+        final DataSchemaNode dataSchemaNode = schemaContext
+                .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
+        final String uri = "instance-identifier-module:cont/yang-ext:mount/instance-identifier-module:cont";
+        mockBodyReader(uri, this.xmlBodyReader, true);
+        final InputStream inputStream = XmlBodyReaderMountPointTest.class
+                .getResourceAsStream("/instanceidentifier/xml/xml_sub_container.xml");
+        final NormalizedNodeContext returnValue = this.xmlBodyReader.readFrom(null,
+                null, null, this.mediaType, null, inputStream);
+        checkMountPointNormalizedNodeContext(returnValue);
+        checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue);
+    }
+
+    private void checkExpectValueNormalizeNodeContext(
+            final DataSchemaNode dataSchemaNode,
+            final NormalizedNodeContext nnContext) {
+        checkExpectValueNormalizeNodeContext(dataSchemaNode, nnContext, null);
+    }
+
+    private void checkExpectValueNormalizeNodeContext(final DataSchemaNode dataSchemaNode,
+            final NormalizedNodeContext nnContext, final QName qualifiedName) {
+        YangInstanceIdentifier dataNodeIdent = YangInstanceIdentifier
+                .of(dataSchemaNode.getQName());
+        final DOMMountPoint mountPoint = nnContext
+                .getInstanceIdentifierContext().getMountPoint();
+        final DataSchemaNode mountDataSchemaNode = mountPoint
+                .getSchemaContext().getDataChildByName(
+                        dataSchemaNode.getQName());
+        assertNotNull(mountDataSchemaNode);
+        if ((qualifiedName != null) && (dataSchemaNode instanceof DataNodeContainer)) {
+            final DataSchemaNode child = ((DataNodeContainer) dataSchemaNode)
+                    .getDataChildByName(qualifiedName);
+            dataNodeIdent = YangInstanceIdentifier.builder(dataNodeIdent)
+                    .node(child.getQName()).build();
+            assertTrue(nnContext.getInstanceIdentifierContext().getSchemaNode()
+                    .equals(child));
+        } else {
+            assertTrue(mountDataSchemaNode.equals(dataSchemaNode));
+        }
+        assertNotNull(NormalizedNodes.findNode(nnContext.getData(),
+                dataNodeIdent));
+    }
+
+    /**
+     * Test when container with the same name is placed in two modules (foo-module and bar-module). Namespace must be
+     * used to distinguish between them to find correct one. Check if container was found not only according to its name
+     * but also by correct namespace used in payload.
+     */
+    @Test
+    public void findFooContainerUsingNamespaceTest() throws Exception {
+        mockBodyReader("instance-identifier-module:cont/yang-ext:mount", this.xmlBodyReader, true);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlDataFindFooContainer.xml");
+        final NormalizedNodeContext returnValue = this.xmlBodyReader
+                .readFrom(null, null, null, this.mediaType, null, inputStream);
+
+        // check return value
+        checkMountPointNormalizedNodeContext(returnValue);
+        // check if container was found both according to its name and namespace
+        assertEquals("Not correct container found, name was ignored",
+                "foo-bar-container", returnValue.getData().getNodeType().getLocalName());
+        assertEquals("Not correct container found, namespace was ignored",
+                "foo:module", returnValue.getData().getNodeType().getNamespace().toString());
+    }
+
+    /**
+     * Test when container with the same name is placed in two modules (foo-module and bar-module). Namespace must be
+     * used to distinguish between them to find correct one. Check if container was found not only according to its name
+     * but also by correct namespace used in payload.
+     */
+    @Test
+    public void findBarContainerUsingNamespaceTest() throws Exception {
+        mockBodyReader("instance-identifier-module:cont/yang-ext:mount", this.xmlBodyReader, true);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlDataFindBarContainer.xml");
+        final NormalizedNodeContext returnValue = this.xmlBodyReader
+                .readFrom(null, null, null, this.mediaType, null, inputStream);
+
+        // check return value
+        checkMountPointNormalizedNodeContext(returnValue);
+        // check if container was found both according to its name and namespace
+        assertEquals("Not correct container found, name was ignored",
+                "foo-bar-container", returnValue.getData().getNodeType().getLocalName());
+        assertEquals("Not correct container found, namespace was ignored",
+                "bar:module", returnValue.getData().getNodeType().getNamespace().toString());
+    }
+
+    /**
+     * Test PUT operation when message root element is not the same as the last element in request URI.
+     * PUT operation message should always start with schema node from URI otherwise exception should be
+     * thrown.
+     */
+    @Test
+    public void wrongRootElementTest() throws Exception {
+        mockBodyReader("instance-identifier-module:cont/yang-ext:mount", this.xmlBodyReader, false);
+        final InputStream inputStream = TestXmlBodyReader.class.getResourceAsStream(
+                "/instanceidentifier/xml/bug7933.xml");
+        try {
+            this.xmlBodyReader.readFrom(null, null, null, this.mediaType, null, inputStream);
+            Assert.fail("Test should fail due to malformed PUT operation message");
+        } catch (final RestconfDocumentedException exception) {
+            final RestconfError restconfError = exception.getErrors().get(0);
+            Assert.assertEquals(RestconfError.ErrorType.PROTOCOL, restconfError.getErrorType());
+            Assert.assertEquals(RestconfError.ErrorTag.MALFORMED_MESSAGE, restconfError.getErrorTag());
+        }
+    }
+}
index f10b45c152a34684726b1e25b9c33e8f2d4e281b..d35c17e861bfc1aa980df5857a95aade48d05622 100644 (file)
@@ -20,11 +20,15 @@ import java.net.URI;
 import java.text.ParseException;
 import java.util.Collection;
 import javax.ws.rs.core.MediaType;
+import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
+import org.opendaylight.controller.sal.rest.impl.test.providers.TestXmlBodyReader;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
@@ -214,4 +218,25 @@ public class XmlBodyReaderTest extends AbstractBodyReaderTest {
         assertEquals("Not correct container found, namespace was ignored", "bar:module",
                 returnValue.getData().getNodeType().getNamespace().toString());
     }
+
+    /**
+     * Test PUT operation when message root element is not the same as the last element in request URI.
+     * PUT operation message should always start with schema node from URI otherwise exception should be
+     * thrown.
+     */
+    @Test
+    public void wrongRootElementTest() throws Exception {
+        mockBodyReader("instance-identifier-module:cont", this.xmlBodyReader, false);
+        final InputStream inputStream = TestXmlBodyReader.class.getResourceAsStream(
+                "/instanceidentifier/xml/bug7933.xml");
+        try {
+            this.xmlBodyReader.readFrom(null, null, null, this.mediaType, null, inputStream);
+            Assert.fail("Test should fail due to malformed PUT operation message");
+        } catch (final RestconfDocumentedException exception) {
+            final RestconfError restconfError = exception.getErrors().get(0);
+            Assert.assertEquals(RestconfError.ErrorType.PROTOCOL, restconfError.getErrorType());
+            Assert.assertEquals(RestconfError.ErrorTag.MALFORMED_MESSAGE, restconfError.getErrorTag());
+        }
+    }
+
 }
diff --git a/restconf/sal-rest-connector/src/test/resources/instanceidentifier/xml/bug7933.xml b/restconf/sal-rest-connector/src/test/resources/instanceidentifier/xml/bug7933.xml
new file mode 100644 (file)
index 0000000..aa0d79e
--- /dev/null
@@ -0,0 +1,9 @@
+<!--
+  ~ Copyright (c) 2017 Pantheon Technologies s.r.o. 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
+  -->
+
+<cont1 xmlns="instance:identifier:module"/>