2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netconf.mdsal.connector.ops.get;
10 import com.google.common.annotations.VisibleForTesting;
11 import com.google.common.collect.Iterables;
12 import java.io.IOException;
13 import java.util.Optional;
14 import javax.xml.stream.XMLOutputFactory;
15 import javax.xml.stream.XMLStreamException;
16 import javax.xml.stream.XMLStreamWriter;
17 import javax.xml.transform.dom.DOMResult;
18 import org.opendaylight.netconf.api.DocumentedException;
19 import org.opendaylight.netconf.api.DocumentedException.ErrorTag;
20 import org.opendaylight.netconf.api.DocumentedException.ErrorType;
21 import org.opendaylight.netconf.api.xml.XmlElement;
22 import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
23 import org.opendaylight.netconf.mdsal.connector.CurrentSchemaContext;
24 import org.opendaylight.netconf.mdsal.connector.ops.Datastore;
25 import org.opendaylight.netconf.util.mapping.AbstractSingletonNetconfOperation;
26 import org.opendaylight.yangtools.yang.common.ErrorSeverity;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
29 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
31 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
35 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
36 import org.opendaylight.yangtools.yang.data.codec.xml.XMLStreamNormalizedNodeStreamWriter;
37 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
38 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
39 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
40 import org.w3c.dom.Document;
41 import org.w3c.dom.Element;
42 import org.w3c.dom.Node;
44 public abstract class AbstractGet extends AbstractSingletonNetconfOperation {
45 private static final XMLOutputFactory XML_OUTPUT_FACTORY;
46 private static final YangInstanceIdentifier ROOT = YangInstanceIdentifier.empty();
47 private static final String FILTER = "filter";
50 XML_OUTPUT_FACTORY = XMLOutputFactory.newFactory();
51 XML_OUTPUT_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);
54 protected final CurrentSchemaContext schemaContext;
55 private final FilterContentValidator validator;
57 public AbstractGet(final String netconfSessionIdForReporting, final CurrentSchemaContext schemaContext) {
58 super(netconfSessionIdForReporting);
59 this.schemaContext = schemaContext;
60 this.validator = new FilterContentValidator(schemaContext);
63 protected Node transformNormalizedNode(final Document document, final NormalizedNode data,
64 final YangInstanceIdentifier dataRoot) {
66 final DOMResult result = new DOMResult(document.createElement(XmlNetconfConstants.DATA_KEY));
68 final XMLStreamWriter xmlWriter = getXmlStreamWriter(result);
70 final NormalizedNodeStreamWriter nnStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(xmlWriter,
71 schemaContext.getCurrentContext(), getSchemaPath(dataRoot));
73 final NormalizedNodeWriter nnWriter = NormalizedNodeWriter.forStreamWriter(nnStreamWriter, true);
75 if (data instanceof ContainerNode) {
76 writeRootElement(xmlWriter, nnWriter, (ContainerNode) data);
77 } else if (data instanceof MapNode) {
78 writeRootElement(xmlWriter, nnWriter, (MapNode) data);
80 throw new IllegalArgumentException("Unable to transform node of type: " + data.getClass().toString()
81 + " offending node: " + data.toString());
84 return result.getNode();
87 private static XMLStreamWriter getXmlStreamWriter(final DOMResult result) {
89 return XML_OUTPUT_FACTORY.createXMLStreamWriter(result);
90 } catch (final XMLStreamException e) {
91 throw new RuntimeException(e);
95 private static SchemaPath getSchemaPath(final YangInstanceIdentifier dataRoot) {
96 return SchemaPath.create(
97 Iterables.transform(dataRoot.getPathArguments(), PathArgument::getNodeType), dataRoot.equals(ROOT));
100 private static void writeRootElement(final XMLStreamWriter xmlWriter, final NormalizedNodeWriter nnWriter,
101 final ContainerNode data) {
103 if (data.getIdentifier().getNodeType().equals(SchemaContext.NAME)) {
104 for (final DataContainerChild child : data.body()) {
105 nnWriter.write(child);
108 nnWriter.write(data);
112 } catch (XMLStreamException | IOException e) {
113 throw new RuntimeException(e);
117 private static void writeRootElement(final XMLStreamWriter xmlWriter, final NormalizedNodeWriter nnWriter,
118 final MapNode data) {
120 if (data.getIdentifier().getNodeType().equals(SchemaContext.NAME)) {
121 for (final MapEntryNode child : data.body()) {
122 nnWriter.write(child);
125 nnWriter.write(data);
129 } catch (XMLStreamException | IOException e) {
130 throw new RuntimeException(e);
134 protected Element serializeNodeWithParentStructure(final Document document, final YangInstanceIdentifier dataRoot,
135 final NormalizedNode node) {
136 if (!dataRoot.equals(ROOT)) {
137 return (Element) transformNormalizedNode(document,
138 ImmutableNodes.fromInstanceId(schemaContext.getCurrentContext(), dataRoot, node),
141 return (Element) transformNormalizedNode(document, node, ROOT);
145 * Obtain data root according to filter from operation element.
147 * @param operationElement operation element
148 * @return if filter is present and not empty returns Optional of the InstanceIdentifier to the read location
149 * in datastore. Empty filter returns Optional.absent() which should equal an empty <data/>
150 * container in the response. If filter is not present we want to read the entire datastore - return ROOT.
151 * @throws DocumentedException if not possible to get identifier from filter
153 protected Optional<YangInstanceIdentifier> getDataRootFromFilter(final XmlElement operationElement)
154 throws DocumentedException {
155 final Optional<XmlElement> filterElement = operationElement.getOnlyChildElementOptionally(FILTER);
156 if (filterElement.isPresent()) {
157 if (filterElement.get().getChildElements().size() == 0) {
158 return Optional.empty();
160 return Optional.of(getInstanceIdentifierFromFilter(filterElement.get()));
163 return Optional.of(ROOT);
167 protected YangInstanceIdentifier getInstanceIdentifierFromFilter(final XmlElement filterElement)
168 throws DocumentedException {
170 if (filterElement.getChildElements().size() != 1) {
171 throw new DocumentedException("Multiple filter roots not supported yet",
172 ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED, ErrorSeverity.ERROR);
175 final XmlElement element = filterElement.getOnlyChildElement();
176 return validator.validate(element);
179 protected static final class GetConfigExecution {
180 private final Optional<Datastore> datastore;
182 GetConfigExecution(final Optional<Datastore> datastore) {
183 this.datastore = datastore;
186 static GetConfigExecution fromXml(final XmlElement xml, final String operationName) throws DocumentedException {
188 validateInputRpc(xml, operationName);
189 } catch (final DocumentedException e) {
190 throw new DocumentedException("Incorrect RPC: " + e.getMessage(), e, e.getErrorType(), e.getErrorTag(),
191 e.getErrorSeverity(), e.getErrorInfo());
194 final Optional<Datastore> sourceDatastore;
196 sourceDatastore = parseSource(xml);
197 } catch (final DocumentedException e) {
198 throw new DocumentedException("Get-config source attribute error: " + e.getMessage(), e,
199 e.getErrorType(), e.getErrorTag(), e.getErrorSeverity(), e.getErrorInfo());
202 return new GetConfigExecution(sourceDatastore);
205 private static Optional<Datastore> parseSource(final XmlElement xml) throws DocumentedException {
206 final Optional<XmlElement> sourceElement = xml.getOnlyChildElementOptionally(XmlNetconfConstants.SOURCE_KEY,
207 XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
208 return sourceElement.isPresent()
209 ? Optional.of(Datastore.valueOf(sourceElement.get().getOnlyChildElement().getName()))
213 private static void validateInputRpc(final XmlElement xml, final String operationName) throws
214 DocumentedException {
215 xml.checkName(operationName);
216 xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
219 public Optional<Datastore> getDatastore() {