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 static com.google.common.base.Preconditions.checkArgument;
12 import com.google.common.annotations.VisibleForTesting;
13 import java.io.IOException;
14 import java.util.Optional;
15 import javax.xml.stream.XMLOutputFactory;
16 import javax.xml.stream.XMLStreamException;
17 import javax.xml.stream.XMLStreamWriter;
18 import javax.xml.transform.dom.DOMResult;
19 import org.opendaylight.netconf.api.DocumentedException;
20 import org.opendaylight.netconf.api.xml.XmlElement;
21 import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
22 import org.opendaylight.netconf.mdsal.connector.CurrentSchemaContext;
23 import org.opendaylight.netconf.mdsal.connector.ops.Datastore;
24 import org.opendaylight.netconf.server.api.operations.AbstractSingletonNetconfOperation;
25 import org.opendaylight.yangtools.yang.common.ErrorSeverity;
26 import org.opendaylight.yangtools.yang.common.ErrorTag;
27 import org.opendaylight.yangtools.yang.common.ErrorType;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
32 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
33 import org.opendaylight.yangtools.yang.data.api.schema.stream.YangInstanceIdentifierWriter;
34 import org.opendaylight.yangtools.yang.data.codec.xml.XMLStreamNormalizedNodeStreamWriter;
35 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
36 import org.w3c.dom.Document;
37 import org.w3c.dom.Element;
38 import org.w3c.dom.Node;
40 // FIXME: seal when we have JDK17+
41 abstract class AbstractGet extends AbstractSingletonNetconfOperation {
42 private static final XMLOutputFactory XML_OUTPUT_FACTORY;
43 private static final String FILTER = "filter";
46 XML_OUTPUT_FACTORY = XMLOutputFactory.newFactory();
47 XML_OUTPUT_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);
50 private final CurrentSchemaContext schemaContext;
51 private final FilterContentValidator validator;
53 AbstractGet(final String netconfSessionIdForReporting, final CurrentSchemaContext schemaContext) {
54 super(netconfSessionIdForReporting);
55 this.schemaContext = schemaContext;
56 validator = new FilterContentValidator(schemaContext);
59 // FIXME: throw a DocumentedException
60 private Node transformNormalizedNode(final Document document, final NormalizedNode data,
61 final YangInstanceIdentifier dataRoot) {
62 final DOMResult result = new DOMResult(document.createElement(XmlNetconfConstants.DATA_KEY));
63 final XMLStreamWriter xmlWriter = getXmlStreamWriter(result);
64 final EffectiveModelContext currentContext = schemaContext.getCurrentContext();
66 final NormalizedNodeStreamWriter nnStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(xmlWriter,
70 if (dataRoot.isEmpty()) {
71 writeRoot(nnStreamWriter, data);
73 write(nnStreamWriter, currentContext, dataRoot.coerceParent(), data);
75 } catch (IOException e) {
76 // FIXME: throw DocumentedException
77 throw new IllegalStateException(e);
80 return result.getNode();
83 private static void write(final NormalizedNodeStreamWriter nnStreamWriter,
84 final EffectiveModelContext currentContext, final YangInstanceIdentifier parent, final NormalizedNode data)
86 try (var yiidWriter = YangInstanceIdentifierWriter.open(nnStreamWriter, currentContext, parent)) {
87 try (var nnWriter = NormalizedNodeWriter.forStreamWriter(nnStreamWriter, true)) {
93 private static void writeRoot(final NormalizedNodeStreamWriter nnStreamWriter, final NormalizedNode data)
95 checkArgument(data instanceof ContainerNode, "Unexpected root data %s", data);
97 try (var nnWriter = NormalizedNodeWriter.forStreamWriter(nnStreamWriter, true)) {
98 for (var child : ((ContainerNode) data).body()) {
99 nnWriter.write(child);
104 private static XMLStreamWriter getXmlStreamWriter(final DOMResult result) {
106 return XML_OUTPUT_FACTORY.createXMLStreamWriter(result);
107 } catch (final XMLStreamException e) {
108 // FIXME: throw DocumentedException
109 throw new IllegalStateException(e);
113 final Element serializeNodeWithParentStructure(final Document document, final YangInstanceIdentifier dataRoot,
114 final NormalizedNode node) {
115 return (Element) transformNormalizedNode(document, node, dataRoot);
119 * Obtain data root according to filter from operation element.
121 * @param operationElement operation element
122 * @return if filter is present and not empty returns Optional of the InstanceIdentifier to the read location
123 * in datastore. Empty filter returns Optional.absent() which should equal an empty <data/>
124 * container in the response. If filter is not present we want to read the entire datastore - return ROOT.
125 * @throws DocumentedException if not possible to get identifier from filter
127 final Optional<YangInstanceIdentifier> getDataRootFromFilter(final XmlElement operationElement)
128 throws DocumentedException {
129 final var optFilterElement = operationElement.getOnlyChildElementOptionally(FILTER);
130 if (optFilterElement.isEmpty()) {
131 return Optional.of(YangInstanceIdentifier.empty());
134 final var filterElement = optFilterElement.orElseThrow();
135 if (filterElement.getChildElements().isEmpty()) {
136 return Optional.empty();
138 return Optional.of(getInstanceIdentifierFromFilter(filterElement));
142 protected final YangInstanceIdentifier getInstanceIdentifierFromFilter(final XmlElement filterElement)
143 throws DocumentedException {
145 if (filterElement.getChildElements().size() != 1) {
146 throw new DocumentedException("Multiple filter roots not supported yet",
147 ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED, ErrorSeverity.ERROR);
150 final XmlElement element = filterElement.getOnlyChildElement();
151 return validator.validate(element);
154 protected static final class GetConfigExecution {
155 private final Optional<Datastore> datastore;
157 GetConfigExecution(final Optional<Datastore> datastore) {
158 this.datastore = datastore;
161 static GetConfigExecution fromXml(final XmlElement xml, final String operationName) throws DocumentedException {
163 validateInputRpc(xml, operationName);
164 } catch (final DocumentedException e) {
165 throw new DocumentedException("Incorrect RPC: " + e.getMessage(), e, e.getErrorType(), e.getErrorTag(),
166 e.getErrorSeverity(), e.getErrorInfo());
169 final Optional<Datastore> sourceDatastore;
171 sourceDatastore = parseSource(xml);
172 } catch (final DocumentedException e) {
173 throw new DocumentedException("Get-config source attribute error: " + e.getMessage(), e,
174 e.getErrorType(), e.getErrorTag(), e.getErrorSeverity(), e.getErrorInfo());
177 return new GetConfigExecution(sourceDatastore);
180 private static Optional<Datastore> parseSource(final XmlElement xml) throws DocumentedException {
181 final Optional<XmlElement> sourceElement = xml.getOnlyChildElementOptionally(XmlNetconfConstants.SOURCE_KEY,
182 XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
183 return sourceElement.isEmpty() ? Optional.empty()
184 : Optional.of(Datastore.valueOf(sourceElement.orElseThrow().getOnlyChildElement().getName()));
187 private static void validateInputRpc(final XmlElement xml, final String operationName) throws
188 DocumentedException {
189 xml.checkName(operationName);
190 xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
193 public Optional<Datastore> getDatastore() {