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.api;
10 import static java.util.Objects.requireNonNull;
11 import static org.opendaylight.netconf.api.xml.XmlNetconfConstants.RPC_REPLY_KEY;
12 import static org.opendaylight.netconf.api.xml.XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0;
14 import java.util.Collections;
15 import java.util.HashMap;
17 import java.util.Map.Entry;
18 import javax.xml.parsers.DocumentBuilderFactory;
19 import javax.xml.parsers.ParserConfigurationException;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22 import org.w3c.dom.Document;
23 import org.w3c.dom.Node;
24 import org.w3c.dom.NodeList;
27 * Checked exception to communicate an error that needs to be sent to the
30 public class DocumentedException extends Exception {
32 public static final String RPC_ERROR = "rpc-error";
33 public static final String ERROR_TYPE = "error-type";
34 public static final String ERROR_TAG = "error-tag";
35 public static final String ERROR_SEVERITY = "error-severity";
36 public static final String ERROR_APP_TAG = "error-app-tag";
37 public static final String ERROR_PATH = "error-path";
38 public static final String ERROR_MESSAGE = "error-message";
39 public static final String ERROR_INFO = "error-info";
41 private static final long serialVersionUID = 1L;
43 private static final Logger LOG = LoggerFactory.getLogger(DocumentedException.class);
45 private static final DocumentBuilderFactory BUILDER_FACTORY;
48 BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
50 BUILDER_FACTORY.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
51 BUILDER_FACTORY.setFeature("http://xml.org/sax/features/external-general-entities", false);
52 BUILDER_FACTORY.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
53 BUILDER_FACTORY.setXIncludeAware(false);
54 BUILDER_FACTORY.setExpandEntityReferences(false);
55 } catch (final ParserConfigurationException e) {
56 throw new ExceptionInInitializerError(e);
58 BUILDER_FACTORY.setNamespaceAware(true);
59 BUILDER_FACTORY.setCoalescing(true);
60 BUILDER_FACTORY.setIgnoringElementContentWhitespace(true);
61 BUILDER_FACTORY.setIgnoringComments(true);
64 public enum ErrorType {
65 TRANSPORT("transport"), RPC("rpc"), PROTOCOL("protocol"), APPLICATION("application");
67 private final String typeValue;
69 ErrorType(final String typeValue) {
70 this.typeValue = requireNonNull(typeValue);
73 public String getTypeValue() {
74 return this.typeValue;
77 public static ErrorType from(final String text) {
78 for (ErrorType e : values()) {
79 if (e.getTypeValue().equalsIgnoreCase(text)) {
88 public enum ErrorTag {
89 ACCESS_DENIED("access-denied"),
90 BAD_ATTRIBUTE("bad-attribute"),
91 BAD_ELEMENT("bad-element"),
92 DATA_EXISTS("data-exists"),
93 DATA_MISSING("data-missing"),
95 INVALID_VALUE("invalid-value"),
96 LOCK_DENIED("lock-denied"),
97 MALFORMED_MESSAGE("malformed-message"),
98 MISSING_ATTRIBUTE("missing-attribute"),
99 MISSING_ELEMENT("missing-element"),
100 OPERATION_FAILED("operation-failed"),
101 OPERATION_NOT_SUPPORTED("operation-not-supported"),
102 RESOURCE_DENIED("resource-denied"),
103 ROLLBCK_FAILED("rollback-failed"),
105 UNKNOWN_ATTRIBUTE("unknown-attribute"),
106 UNKNOWN_ELEMENT("unknown-element"),
107 UNKNOWN_NAMESPACE("unknown-namespace");
109 private final String tagValue;
111 ErrorTag(final String tagValue) {
112 this.tagValue = tagValue;
115 public String getTagValue() {
116 return this.tagValue;
119 public static ErrorTag from(final String text) {
120 for (ErrorTag e : values()) {
121 if (e.getTagValue().equals(text)) {
126 return OPERATION_FAILED;
130 public enum ErrorSeverity {
131 ERROR("error"), WARNING("warning");
133 private final String severityValue;
135 ErrorSeverity(final String severityValue) {
136 this.severityValue = requireNonNull(severityValue);
139 public String getSeverityValue() {
140 return this.severityValue;
143 public static ErrorSeverity from(final String text) {
144 for (ErrorSeverity e : values()) {
145 if (e.getSeverityValue().equalsIgnoreCase(text)) {
154 private final ErrorType errorType;
155 private final ErrorTag errorTag;
156 private final ErrorSeverity errorSeverity;
157 private final Map<String, String> errorInfo;
159 public DocumentedException(final String message) {
160 this(message, DocumentedException.ErrorType.APPLICATION, DocumentedException.ErrorTag.INVALID_VALUE,
161 DocumentedException.ErrorSeverity.ERROR);
164 public DocumentedException(final String message, final Exception cause) {
165 this(message, cause, DocumentedException.ErrorType.APPLICATION, DocumentedException.ErrorTag.INVALID_VALUE,
166 DocumentedException.ErrorSeverity.ERROR);
169 public DocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag,
170 final ErrorSeverity errorSeverity) {
171 this(message, errorType, errorTag, errorSeverity, Collections.emptyMap());
174 public DocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag,
175 final ErrorSeverity errorSeverity, final Map<String, String> errorInfo) {
177 this.errorType = errorType;
178 this.errorTag = errorTag;
179 this.errorSeverity = errorSeverity;
180 this.errorInfo = errorInfo;
183 public DocumentedException(final String message, final Exception cause, final ErrorType errorType,
184 final ErrorTag errorTag, final ErrorSeverity errorSeverity) {
185 this(message, cause, errorType, errorTag, errorSeverity, Collections.emptyMap());
188 public DocumentedException(final String message, final Exception cause, final ErrorType errorType,
189 final ErrorTag errorTag, final ErrorSeverity errorSeverity, final Map<String, String> errorInfo) {
190 super(message, cause);
191 this.errorType = errorType;
192 this.errorTag = errorTag;
193 this.errorSeverity = errorSeverity;
194 this.errorInfo = errorInfo;
197 public static <E extends Exception> DocumentedException wrap(final E exception) throws DocumentedException {
198 final Map<String, String> errorInfo = new HashMap<>();
199 errorInfo.put(ErrorTag.OPERATION_FAILED.name(), "Exception thrown");
200 throw new DocumentedException(exception.getMessage(), exception, ErrorType.APPLICATION,
201 ErrorTag.OPERATION_FAILED, ErrorSeverity.ERROR, errorInfo);
204 public static DocumentedException fromXMLDocument(final Document fromDoc) {
206 ErrorType errorType = ErrorType.APPLICATION;
207 ErrorTag errorTag = ErrorTag.OPERATION_FAILED;
208 ErrorSeverity errorSeverity = ErrorSeverity.ERROR;
209 Map<String, String> errorInfo = null;
210 String errorMessage = "";
212 Node rpcReply = fromDoc.getDocumentElement();
214 // FIXME: BUG? - we only handle one rpc-error.
216 NodeList replyChildren = rpcReply.getChildNodes();
217 for (int i = 0; i < replyChildren.getLength(); i++) {
218 Node replyChild = replyChildren.item(i);
219 if (RPC_ERROR.equals(replyChild.getNodeName())) {
220 NodeList rpcErrorChildren = replyChild.getChildNodes();
221 for (int j = 0; j < rpcErrorChildren.getLength(); j++) {
222 Node rpcErrorChild = rpcErrorChildren.item(j);
223 if (ERROR_TYPE.equals(rpcErrorChild.getNodeName())) {
224 errorType = ErrorType.from(rpcErrorChild.getTextContent());
225 } else if (ERROR_TAG.equals(rpcErrorChild.getNodeName())) {
226 errorTag = ErrorTag.from(rpcErrorChild.getTextContent());
227 } else if (ERROR_SEVERITY.equals(rpcErrorChild.getNodeName())) {
228 errorSeverity = ErrorSeverity.from(rpcErrorChild.getTextContent());
229 } else if (ERROR_MESSAGE.equals(rpcErrorChild.getNodeName())) {
230 errorMessage = rpcErrorChild.getTextContent();
231 } else if (ERROR_INFO.equals(rpcErrorChild.getNodeName())) {
232 errorInfo = parseErrorInfo(rpcErrorChild);
240 return new DocumentedException(errorMessage, errorType, errorTag, errorSeverity, errorInfo);
243 private static Map<String, String> parseErrorInfo(final Node node) {
244 Map<String, String> infoMap = new HashMap<>();
245 NodeList children = node.getChildNodes();
246 for (int i = 0; i < children.getLength(); i++) {
247 Node child = children.item(i);
248 if (child.getNodeType() == Node.ELEMENT_NODE) {
249 infoMap.put(child.getNodeName(), child.getTextContent());
256 public ErrorType getErrorType() {
257 return this.errorType;
260 public ErrorTag getErrorTag() {
261 return this.errorTag;
264 public ErrorSeverity getErrorSeverity() {
265 return this.errorSeverity;
268 public Map<String, String> getErrorInfo() {
269 return this.errorInfo;
272 public Document toXMLDocument() {
275 doc = BUILDER_FACTORY.newDocumentBuilder().newDocument();
277 Node rpcReply = doc.createElementNS(URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, RPC_REPLY_KEY);
278 doc.appendChild(rpcReply);
280 Node rpcError = doc.createElementNS(URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, RPC_ERROR);
281 rpcReply.appendChild(rpcError);
283 rpcError.appendChild(createTextNode(doc, ERROR_TYPE, getErrorType().getTypeValue()));
284 rpcError.appendChild(createTextNode(doc, ERROR_TAG, getErrorTag().getTagValue()));
285 rpcError.appendChild(createTextNode(doc, ERROR_SEVERITY, getErrorSeverity().getSeverityValue()));
286 rpcError.appendChild(createTextNode(doc, ERROR_MESSAGE, getLocalizedMessage()));
288 Map<String, String> errorInfoMap = getErrorInfo();
289 if (errorInfoMap != null && !errorInfoMap.isEmpty()) {
291 * <error-info> <bad-attribute>message-id</bad-attribute>
292 * <bad-element>rpc</bad-element> </error-info>
295 Node errorInfoNode = doc.createElementNS(URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, ERROR_INFO);
296 errorInfoNode.setPrefix(rpcReply.getPrefix());
297 rpcError.appendChild(errorInfoNode);
299 for (Entry<String, String> entry : errorInfoMap.entrySet()) {
300 errorInfoNode.appendChild(createTextNode(doc, entry.getKey(), entry.getValue()));
303 } catch (final ParserConfigurationException e) {
304 // this shouldn't happen
305 LOG.error("Error outputting to XML document", e);
311 private Node createTextNode(final Document doc, final String tag, final String textContent) {
312 Node node = doc.createElementNS(URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, tag);
313 node.setTextContent(textContent);
318 public String toString() {
319 return "NetconfDocumentedException{" + "message=" + getMessage() + ", errorType=" + this.errorType
320 + ", errorTag=" + this.errorTag + ", errorSeverity=" + this.errorSeverity + ", errorInfo="
321 + this.errorInfo + '}';