2 * Copyright (c) 2013 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.api.messages;
10 import static java.util.Objects.requireNonNull;
12 import java.io.StringWriter;
14 import javax.xml.transform.OutputKeys;
15 import javax.xml.transform.Transformer;
16 import javax.xml.transform.TransformerConfigurationException;
17 import javax.xml.transform.TransformerException;
18 import javax.xml.transform.dom.DOMSource;
19 import javax.xml.transform.stream.StreamResult;
20 import org.eclipse.jdt.annotation.NonNull;
21 import org.opendaylight.netconf.api.DocumentedException;
22 import org.opendaylight.netconf.api.NamespaceURN;
23 import org.opendaylight.netconf.api.xml.XmlUtil;
24 import org.opendaylight.yangtools.yang.common.ErrorSeverity;
25 import org.opendaylight.yangtools.yang.common.ErrorTag;
26 import org.opendaylight.yangtools.yang.common.ErrorType;
27 import org.w3c.dom.Document;
30 * NetconfMessage represents a wrapper around {@link Document}.
32 public class NetconfMessage {
33 private static final Transformer TRANSFORMER;
38 t = XmlUtil.newIndentingTransformer();
39 } catch (TransformerConfigurationException e) {
40 throw new ExceptionInInitializerError(e);
42 t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
47 private final @NonNull Document document;
49 public NetconfMessage(final Document document) {
50 this.document = requireNonNull(document);
54 * Create a new {@link NetconfMessage} based on supplied document.
56 * @param document A {@link Document} backing the message
57 * @return A {@link NetconfMessage}
58 * @throws NullPointerException if {@code document} is {@code null}
59 * @throws DocumentedException if the {@code document} does not match a known {@link NetconfMessage}
61 public static @NonNull NetconfMessage of(final Document document) throws DocumentedException {
62 final var root = document.getDocumentElement();
63 final var rootName = root.getLocalName();
64 final var rootNs = root.getNamespaceURI();
68 case NamespaceURN.BASE:
70 case HelloMessage.ELEMENT_NAME:
71 return new HelloMessage(document);
72 case RpcMessage.ELEMENT_NAME:
73 return RpcMessage.ofChecked(document);
74 case RpcReplyMessage.ELEMENT_NAME:
75 return new RpcReplyMessage(document);
80 case NamespaceURN.NOTIFICATION:
82 case NotificationMessage.ELEMENT_NAME:
83 return NotificationMessage.ofChecked(document);
89 throw new DocumentedException("Unhandled namespace " + rootNs, ErrorType.PROTOCOL,
90 ErrorTag.UNKNOWN_NAMESPACE, ErrorSeverity.ERROR, Map.of("bad-element", rootName));
93 } else if (HelloMessage.ELEMENT_NAME.equals(rootName)) {
94 // accept even if hello has no namespace
95 return new HelloMessage(document);
97 throw new DocumentedException("Unknown element " + rootName, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT,
98 ErrorSeverity.ERROR, Map.of("bad-element", rootName));
101 public final @NonNull Document getDocument() {
106 public final String toString() {
107 final var result = new StreamResult(new StringWriter());
108 final var source = new DOMSource(document.getDocumentElement());
111 // Slight critical section is a tradeoff. This should be reasonably fast.
112 synchronized (TRANSFORMER) {
113 TRANSFORMER.transform(source, result);
115 } catch (TransformerException e) {
116 throw new IllegalStateException("Failed to encode document", e);
119 return result.getWriter().toString();