- JsonReader is used for translation of XML to CompositeNode.
It creates CompositeNode and its structure with CompositeNodeWrapper and SimpleNodeWrapper
implementations. It allows to add namespaces after translation.
Change-Id: Ib0d8fef54befe791f53901785688c2dc2bd5bb8a
Signed-off-by: Martin Sunal <msunal@cisco.com>
class JsonReader {
- public CompositeNodeWrapper read(InputStream entityStream) throws UnsupportedJsonFormatException {
+ public CompositeNodeWrapper read(InputStream entityStream) throws UnsupportedFormatException {
JsonParser parser = new JsonParser();
JsonElement rootElement = parser.parse(new InputStreamReader(entityStream));
if (!rootElement.isJsonObject()) {
- throw new UnsupportedJsonFormatException("Root element of Json has to be Object");
+ throw new UnsupportedFormatException("Root element of Json has to be Object");
}
Set<Entry<String, JsonElement>> entrySetsOfRootJsonObject = rootElement.getAsJsonObject().entrySet();
if (entrySetsOfRootJsonObject.size() != 1) {
- throw new UnsupportedJsonFormatException("Json Object should contain one element");
+ throw new UnsupportedFormatException("Json Object should contain one element");
} else {
Entry<String, JsonElement> childEntry = Lists.newArrayList(entrySetsOfRootJsonObject).get(0);
String firstElementName = childEntry.getKey();
if (firstElementInArray.isJsonObject()) {
return createStructureWithRoot(firstElementName, firstElementInArray.getAsJsonObject());
}
- throw new UnsupportedJsonFormatException("Array as the first element in Json Object can have only Object element");
+ throw new UnsupportedFormatException("Array as the first element in Json Object can have only Object element");
}
}
- throw new UnsupportedJsonFormatException("First element in Json Object has to be \"Object\" or \"Array with one Object element\". Other scenarios are not supported yet.");
+ throw new UnsupportedFormatException("First element in Json Object has to be \"Object\" or \"Array with one Object element\". Other scenarios are not supported yet.");
}
}
JsonReader jsonReader = new JsonReader();
try {
return jsonReader.read(entityStream);
- } catch (UnsupportedJsonFormatException e) {
+ } catch (UnsupportedFormatException e) {
throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
.entity(e.getMessage()).build());
}
import com.google.gson.stream.JsonWriter;
@Provider
-@Produces({ API + RestconfService.JSON })
+@Produces({API+RestconfService.JSON})
public enum StructuredDataToJsonProvider implements MessageBodyWriter<StructuredData> {
INSTANCE;
--- /dev/null
+package org.opendaylight.controller.sal.rest.impl;
+
+public class UnsupportedFormatException extends Exception {
+
+ private static final long serialVersionUID = -1741388894406313402L;
+
+ public UnsupportedFormatException() {
+ super();
+ }
+
+ public UnsupportedFormatException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public UnsupportedFormatException(String message) {
+ super(message);
+ }
+
+ public UnsupportedFormatException(Throwable cause) {
+ super(cause);
+ }
+
+}
+++ /dev/null
-package org.opendaylight.controller.sal.rest.impl;
-
-public class UnsupportedJsonFormatException extends Exception {
-
- private static final long serialVersionUID = -1741388894406313402L;
-
- public UnsupportedJsonFormatException() {
- super();
- }
-
- public UnsupportedJsonFormatException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public UnsupportedJsonFormatException(String message) {
- super(message);
- }
-
- public UnsupportedJsonFormatException(Throwable cause) {
- super(cause);
- }
-
-}
--- /dev/null
+package org.opendaylight.controller.sal.rest.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Stack;
+
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.Characters;
+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.NodeWrapper;
+import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
+
+public class XmlReader {
+
+ private final static XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
+ private XMLEventReader eventReader;
+
+ public CompositeNodeWrapper read(InputStream entityStream) throws XMLStreamException, UnsupportedFormatException {
+ eventReader = xmlInputFactory.createXMLEventReader(entityStream);
+
+ if (eventReader.hasNext()) {
+ XMLEvent element = eventReader.peek();
+ if (element.isStartDocument()) {
+ eventReader.nextEvent();
+ }
+ }
+
+ if (eventReader.hasNext() && !isCompositeNodeEvent(eventReader.peek())) {
+ throw new UnsupportedFormatException("Root element of XML has to be composite element.");
+ }
+
+ final Stack<NodeWrapper<?>> processingQueue = new Stack<>();
+ CompositeNodeWrapper root = null;
+ NodeWrapper<?> element = null;
+ while (eventReader.hasNext()) {
+ final XMLEvent event = eventReader.nextEvent();
+
+ if (event.isStartElement()) {
+ final StartElement startElement = event.asStartElement();
+ CompositeNodeWrapper compParentNode = null;
+ if (!processingQueue.isEmpty() && processingQueue.peek() instanceof CompositeNodeWrapper) {
+ compParentNode = (CompositeNodeWrapper) processingQueue.peek();
+ }
+ NodeWrapper<?> newNode = null;
+ if (isCompositeNodeEvent(event)) {
+ if (root == null) {
+ root = resolveCompositeNodeFromStartElement(startElement);
+ newNode = root;
+ } else {
+ newNode = resolveCompositeNodeFromStartElement(startElement);
+ }
+ } else if (isSimpleNodeEvent(event)) {
+ if (root == null) {
+ throw new UnsupportedFormatException("Root element of XML has to be composite element.");
+ }
+ newNode = resolveSimpleNodeFromStartElement(startElement);
+ }
+
+ if (newNode != null) {
+ processingQueue.push(newNode);
+ if (compParentNode != null) {
+ compParentNode.addValue(newNode);
+ }
+ }
+ } else if (event.isEndElement()) {
+ element = processingQueue.pop();
+ }
+ }
+
+ if (!root.getLocalName().equals(element.getLocalName())) {
+ throw new UnsupportedFormatException("XML should contain only one root element");
+ }
+
+ return root;
+ }
+
+ private boolean isSimpleNodeEvent(final XMLEvent event) throws XMLStreamException {
+ checkArgument(event != null, "XML Event cannot be NULL!");
+ if (event.isStartElement()) {
+ if (eventReader.hasNext()) {
+ final XMLEvent innerEvent;
+ innerEvent = eventReader.peek();
+ if (innerEvent.isCharacters()) {
+ final Characters chars = innerEvent.asCharacters();
+ if (!chars.isWhiteSpace()) {
+ return true;
+ }
+ } else if (innerEvent.isEndElement()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean isCompositeNodeEvent(final XMLEvent event) throws XMLStreamException {
+ checkArgument(event != null, "XML Event cannot be NULL!");
+ if (event.isStartElement()) {
+ if (eventReader.hasNext()) {
+ XMLEvent innerEvent;
+ innerEvent = eventReader.peek();
+ if (innerEvent.isCharacters()) {
+ Characters chars = innerEvent.asCharacters();
+ if (chars.isWhiteSpace()) {
+ eventReader.nextEvent();
+ innerEvent = eventReader.peek();
+ }
+ }
+ if (innerEvent.isStartElement()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private SimpleNodeWrapper resolveSimpleNodeFromStartElement(final StartElement startElement) throws XMLStreamException {
+ checkArgument(startElement != null, "Start Element cannot be NULL!");
+ String data = null;
+
+ if (eventReader.hasNext()) {
+ final XMLEvent innerEvent = eventReader.peek();
+ if (innerEvent.isCharacters()) {
+ final Characters chars = innerEvent.asCharacters();
+ if (!chars.isWhiteSpace()) {
+ data = innerEvent.asCharacters().getData();
+ }
+ } else if (innerEvent.isEndElement()) {
+ data = "";
+ }
+ }
+
+ return new SimpleNodeWrapper(getNamespaceFrom(startElement), getLocalNameFrom(startElement), data);
+ }
+
+ private CompositeNodeWrapper resolveCompositeNodeFromStartElement(final StartElement startElement) {
+ checkArgument(startElement != null, "Start Element cannot be NULL!");
+ return new CompositeNodeWrapper(getNamespaceFrom(startElement), getLocalNameFrom(startElement));
+ }
+
+ private String getLocalNameFrom(StartElement startElement) {
+ return startElement.getName().getLocalPart();
+ }
+
+ private URI getNamespaceFrom(StartElement startElement) {
+ String namespaceURI = startElement.getName().getNamespaceURI();
+ return namespaceURI.isEmpty() ? null : URI.create(namespaceURI);
+ }
+
+}
import org.opendaylight.controller.sal.rest.api.RestconfService;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.Node;
-import org.opendaylight.yangtools.yang.data.api.SimpleNode;
-import org.opendaylight.yangtools.yang.data.impl.XmlTreeBuilder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
@Provider
@Consumes({API+RestconfService.XML})
public enum XmlToCompositeNodeProvider implements MessageBodyReader<CompositeNode> {
INSTANCE;
- private final static Logger logger = LoggerFactory.getLogger(XmlToCompositeNodeProvider.class);
-
@Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return true;
public CompositeNode readFrom(Class<CompositeNode> type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
throws IOException, WebApplicationException {
+ XmlReader xmlReader = new XmlReader();
try {
- Node<?> node = XmlTreeBuilder.buildDataTree(entityStream);
- if (node instanceof SimpleNode) {
- logger.info("Node is SimpleNode");
- throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
- .entity("XML should start with XML element that contains 1..N XML child elements.").build());
- }
- return (CompositeNode) node;
- } catch (XMLStreamException e) {
- logger.info("Error during translation of InputStream to Node\n" + e.getMessage());
+ return xmlReader.read(entityStream);
+ } catch (XMLStreamException | UnsupportedFormatException e) {
throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
.entity(e.getMessage()).build());
}
val List<NodeWrapper<?>> children = (nodeBuilder as CompositeNodeWrapper).getValues
for (child : children) {
addNamespaceToNodeFromSchemaRecursively(child,
- (schema as DataNodeContainer).childNodes.findFirst[n|n.QName.localName === child.localName])
+ (schema as DataNodeContainer).childNodes.findFirst[n|n.QName.localName.equals(child.localName)])
}
}
}
import java.util.Collection;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.sal.rest.api.RestconfService;
import org.opendaylight.controller.sal.rest.impl.StructuredDataToXmlProvider;
import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider;
restconfImpl.setControllerContext(controllerContext);
}
-// @Before
+ @Before
public void logs() {
List<LogRecord> loggedRecords = getLoggedRecords();
for (LogRecord l : loggedRecords) {
public void testXmlToCompositeNodeProvider() throws ParserConfigurationException, SAXException, IOException {
URI uri = null;
try {
- uri = new URI("/operations/" + URLEncoder.encode("ietf-interfaces:interfaces/interface/eth0", Charsets.US_ASCII.name()).toString());
+ uri = new URI("/config/" + URLEncoder.encode("ietf-interfaces:interfaces/interface/eth0", Charsets.US_ASCII.name()).toString());
} catch (UnsupportedEncodingException | URISyntaxException e) {
e.printStackTrace();
}
InputStream xmlStream = RestconfImplTest.class.getResourceAsStream("/parts/ietf-interfaces_interfaces.xml");
final CompositeNode loadedCompositeNode = TestUtils.loadCompositeNode(xmlStream);
- when(brokerFacade.invokeRpc(any(QName.class), any(CompositeNode.class))).thenReturn(new RpcResult<CompositeNode>() {
-
+ when(brokerFacade.commitConfigurationDataPut(any(InstanceIdentifier.class), any(CompositeNode.class))).thenReturn(new Future<RpcResult<TransactionStatus>>() {
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ return false;
+ }
@Override
- public boolean isSuccessful() {
- return true;
+ public boolean isCancelled() {
+ return false;
}
-
@Override
- public CompositeNode getResult() {
- return loadedCompositeNode;
+ public boolean isDone() {
+ return false;
}
-
@Override
- public Collection<RpcError> getErrors() {
+ public RpcResult<TransactionStatus> get() throws InterruptedException, ExecutionException {
+ return null;
+ }
+ @Override
+ public RpcResult<TransactionStatus> get(long timeout, TimeUnit unit) throws InterruptedException,
+ ExecutionException, TimeoutException {
return null;
}
});
Document doc = docBuilder.parse(xmlStream);
Response response = target(uri.toASCIIString()).request(MediaTypes.API+RestconfService.XML).post(Entity.entity(TestUtils.getDocumentInPrintableForm(doc), new MediaType("application","vnd.yang.api+xml")));
- assertEquals(200, response.getStatus());
+ assertEquals(204, response.getStatus());
}
@Test